Wiktionary thwiktionary https://th.wiktionary.org/wiki/%E0%B8%A7%E0%B8%B4%E0%B8%81%E0%B8%B4%E0%B8%9E%E0%B8%88%E0%B8%99%E0%B8%B2%E0%B8%99%E0%B8%B8%E0%B8%81%E0%B8%A3%E0%B8%A1:%E0%B8%AB%E0%B8%99%E0%B9%89%E0%B8%B2%E0%B8%AB%E0%B8%A5%E0%B8%B1%E0%B8%81 MediaWiki 1.47.0-wmf.24 case-sensitive สื่อ พิเศษ พูดคุย ผู้ใช้ คุยกับผู้ใช้ วิกิพจนานุกรม คุยเรื่องวิกิพจนานุกรม ไฟล์ คุยเรื่องไฟล์ มีเดียวิกิ คุยเรื่องมีเดียวิกิ แม่แบบ คุยเรื่องแม่แบบ วิธีใช้ คุยเรื่องวิธีใช้ หมวดหมู่ คุยเรื่องหมวดหมู่ ภาคผนวก คุยเรื่องภาคผนวก ดัชนี คุยเรื่องดัชนี สัมผัส คุยเรื่องสัมผัส อรรถาภิธาน คุยเรื่องอรรถาภิธาน TimedText TimedText talk มอดูล คุยเรื่องมอดูล Event Event talk ផ្សារ 0 6272 5775263 1638665 2026-10-14T09:56:04Z Octahedron80 267 5775263 wikitext text/x-wiki == ภาษาเขมร == ===รากศัพท์=== {{bor+|km|cja|ꨚꨧꩉ}}, จาก{{der|km|ms|pasar}}, จาก{{der|km|fa|بازار||ตลาด}}, จาก{{der|km|pal|𐭥𐭠𐭰𐭠𐭫|ts=wāzār|gloss=ตลาด}} === การออกเสียง === {{km-IPA}} === คำนาม === {{km-noun}} # [[ตลาด]] 8lvfob8ff0uwy7kosmy6f0fmkq1ihpm आज 0 6346 5775278 5719926 2026-10-14T10:09:38Z स्वर्ण 18519 5775278 wikitext text/x-wiki == ภาษาสันสกฤต == === คำนาม === {{sa-noun|g=m}} # [[แร้ง]] #: {{syn|hi|अद्य|इमरोज़}} #: {{hi-x|'''आज''' [[मैं]] [[विमान]] [[द्वारा]] [[प्रथम]] [[बार]] [[यात्रा]] [[करूँगा]]।|'''วันนี้''' ฉันจะเดินทางโดยเครื่องบินเป็นครั้งแรก}} {{sa-noun|g=n}} # [[เนยใส]] === คำคุณศัพท์ === {{sa-adj}} # [[ที่]][[มา]][[จาก]][[แพะ]], ที่[[เป็น]][[ของ]]แพะ == ภาษาฮินดี == === รากศัพท์ === จาก{{etyl|pra|hi}} {{m|psu|अज्ज|tr=ajja}}, จาก{{etyl|sa|hi}} {{m|sa|अद्य}} === การออกเสียง === * {{hi-IPA}} === คำกริยาวิเศษณ์ === {{hi-adv|ur=آج}} # [[วันนี้]] === คำนาม === {{hi-noun|m|ur=آج}} # [[วันนี้]] === คำเกี่ยวข้อง === * {{l|hi|आजकल||ปัจจุบัน, ทุกวันนี้}} {{topics|hi|เวลา}} qnzd1cxkkrnw40q8q97sw9pp8v392e8 बाग 0 6988 5775264 1997823 2026-10-14T09:56:21Z Octahedron80 267 5775264 wikitext text/x-wiki {{also/auto}} == ภาษามราฐี == === รากศัพท์ === {{root|mr|ine-pro|*bʰeh₂g-}} {{bor+|mr|fa|باغ|tr=bâğ}}, จาก{{der|mr|pal|𐭡𐭠𐭢||สวน (อุทยาน/ผลไม้)}}; เทียบ{{cog|gu|બાગ}}, {{cog|hi|बाग़}}, {{cog|pa|ਬਾਗ਼}} / {{m|pa|باغ}}, {{cog|bn|বাগ}} === การออกเสียง === * {{audio|mr|LL-Q1571 (mar)-नंदिनी रानडे-बाग.wav}} === คำนาม === {{mr-noun|g=m}} # [[สวน]], [[อุทยาน]] # [[สวน]][[ผลไม้]] ==== คำพ้องความ ==== * {{l|hi|उद्यान}} === อ้างอิง === * {{R:mr:Berntsen}} * {{R:mr:Molesworth}} * {{R:mr:Date}} == ภาษาฮินดี == === การออกเสียง === * {{hi-IPA|बाग़}} === คำนาม === {{hi-noun|g=m}} # {{nuqtaless form of|hi|बाग़}} ==== การผันรูป ==== {{hi-ndecl|//बाग़<M>}} q5qgbpodpaaw5obig2nb54ek35kq2rl शाह 0 7142 5775265 5685103 2026-10-14T09:57:08Z Octahedron80 267 5775265 wikitext text/x-wiki == ภาษาเนปาล == === รากศัพท์ === {{bor+|ne|fa|شاه|tr=šâh}} === การออกเสียง === * {{ne-IPA}} * {{IPA|ne|[sä̆ɦä̤]}} === คำนาม === {{ne-noun}} # ชาห์, ตำแหน่งกษัตริย์ของเปอร์เซีย/อิหร่าน == ภาษาฮินดี == {{wikipedia|lang=hi}} === รากศัพท์ === {{bor+|hi|fa|شاه|tr=šâh}}, จาก{{der|hi|pal|𐭬𐭫𐭪𐭠}}, จาก{{der|hi|peo|𐏋|tr=xšāyaθiya||กษัตริย์}}; เทียบ{{cog|pa|ਸ਼ਾਹ}} / {{m|pa|شاہ}}, {{cog|gu|શાહ}}, {{cog|mr|शहा}}, {{cog|bn|শাহ}} === การออกเสียง === * {{hi-IPA}} === คำนาม === {{hi-noun|m|ur=شاہ}} # [[กษัตริย์]], [[ราชา]] # [[ชาห์]], [[ตำแหน่ง]][[กษัตริย์]][[ของ]][[เปอร์เซีย]]/[[อิหร่าน]] # {{lb|hi|หมากรุกสากล}} [[คิง]] ==== การผันรูป ==== {{hi-ndecl|<M>}} === อ้างอิง === * {{R:hi:McGregor}} 506c5fqw67sw4t4nus5j6i5fwms03il China 0 7552 5775266 5647899 2026-10-14T09:57:40Z Octahedron80 267 5775266 wikitext text/x-wiki {{also/auto}} == ภาษากาลิเซีย == {{วิกิพีเดีย|lang=gl}} === การออกเสียง === * {{IPA|gl|/ˈt͡ʃina/}} === คำวิสามานยนาม === {{gl-proper noun|f}} # [[ประเทศจีน]] ==== คำเกี่ยวข้อง ==== * {{l|gl|chinés}} {{topics|gl|ประเทศ}} == ภาษาดัตช์ == ===การออกเสียง=== *{{IPA|nl|/ˈʃi.na/}} * พยางค์: Chi·na ==={{หน้าที่|nl|วิสามานยนาม}}=== {{pn}} # [[ประเทศจีน]] {{topics|nl|ประเทศ}} == ภาษาเยอรมัน == {{wikipedia|lang=de}} === รากศัพท์ === จาก{{der|de|fa|چینی|tr=čini||ชาวจีน; กระเบื้องเคลือบ}}, ประสมจาก {{m|fa|چین|tr=čin||ประเทศจีน}} และปัจจัย {{m|fa|ی|ـی|tr=-i}}, จาก{{der|de|pal|𐭰𐭩𐭭||ประเทศจีน}}, แรกสุดจาก{{der|de|sa|चीन}} === การออกเสียง === * {{IPA|de|[ˈçiːna]}} * {{IPA|de|[ˈkiːna]}} {{a|เยอรมนีตอนใต้|ออสเตรีย|สวิตเซอร์แลนด์}} ** {{audio|de|De-at-China.ogg}} * {{IPA|de|[ˈʃiːna]}} {{a|เยอรมนีตอนเหนือ|เยอรมนีตะวันตก}} ** {{audio|de|De-China.ogg}} * {{IPA|de|[ˈxiːna]}} {{a|สวิตเซอร์แลนด์}} * {{hyphenation|de|Chi|na}} === คำวิสามานยนาม === {{de-proper noun|toponym,,-}} # ประเทศจีน ==== การผันรูป ==== {{de-ndecl|n,s:-,-}} ==== หมายเหตุการใช้ ==== * รูปพหูพจน์ ''China'' ใช้ในชื่อของ[[สาธารณรัฐประชาชนจีน]] (เยอรมัน: [[Volksrepublik China]] (''ย่อเป็น:'' VR China), [[Festlandchina]] ([[จีนแผ่นดินใหญ่]]), [[Rotchina]] ([[จีนแดง]])) และ[[สาธารณรัฐจีน]] ({{l|de|Taiwan}}) (เยอรมัน: [[Republik China]] (auf Taiwan), [[Inselchina]]) ==== คำแผลง ==== {{col4|de |Chinaböller |Chinagras |Chinakohl |Chinakrepp |Chinaschilf |chinesisch |Chinese |Chinesin |Indochina |Rotchina}} {{topics|de|ประเทศ}} ==ภาษาสเปน== ===การออกเสียง=== {{es-pr|+<audio:LL-Q1321 (spa)-Rodelar-China.wav>}} *'ชีนา ===คำวิสามานยนาม=== {{es-proper noun|f}} # [[ประเทศจีน]] {{topics|es|ประเทศ}} == ภาษาอังกฤษ == ===การออกเสียง=== * {{a|UK|US}} {{enPR|chī'nə}}, {{IPA|en|/ˈt͡ʃaɪnə/|[ˈt͡ʃ(ʰ)aɪ̯nə]}} * {{audio|en|En-us-china.ogg|a=US}} * {{audio|en|En-us-China.ogg|a=US}} * {{rhymes|en|aɪnə|s=2}} ===คำวิสามานยนาม=== {{en-proper noun|-|Chinas}} (ไชนา) # [[ประเทศจีน]] {{topics|en|ประเทศ}} o9ofgqwo67y5de4507ebj4g7mm3vt9f बरफ 0 7760 5775267 1878907 2026-10-14T09:58:04Z Octahedron80 267 5775267 wikitext text/x-wiki {{also/auto}} == ภาษาเนปาล == === รากศัพท์ === จาก{{der|ne|fa|برف|tr=barf}}, เป็นไปได้ว่าผ่านทาง{{der|ne|hi|बरफ़}} === การออกเสียง === * {{ne-IPA}} === คำนาม === {{ne-noun}} # [[น้ำแข็ง]] == ภาษาโภชปุระ == === รากศัพท์ === จาก{{der|bho|fa|برف|tr=barf}}, จาก{{der|bho|pal|𐭥𐭯𐭫}} === คำนาม === {{bho-noun|g=m}} # [[น้ำแข็ง]] mbhyrhxs5je4m8fllmdn38xa4hr8uwm Iran 0 8107 5775268 5726108 2026-10-14T09:59:04Z Octahedron80 267 5775268 wikitext text/x-wiki {{also/auto}} {{minitoc}} == ภาษากาตาลา == === การออกเสียง === * {{ca-IPA}} * {{audio|ca|LL-Q7026 (cat)-Millars-Iran.wav|a=Valencia}} * {{rhymes|ca|an|s=2}} === คำวิสามานยนาม === {{ca-proper noun|m}} # {{tcl|ca|อิหร่าน|id=Q794}} ==== หมายเหตุการใช้ ==== * Catalan uses the [[definite article]] [[l’#Catalan|l’]] in front of Iran, but not in [[enumeration]]s (such as "Canada, China and Iran are big countries.") ==== ลูกคำ ==== * {{l|ca|iranià}} == ภาษาคอร์นวอลล์ == {{wp|kw:}} === รากศัพท์ === {{bor+|kw|en|Iran}}, จาก{{der|kw|fa-ira|ایران|tr=irân}}, จาก{{der|kw|fa-cls|ایران|tr=ērān}} === การออกเสียง === * {{IPA|kw|/iˈran/}} === คำวิสามานยนาม === {{h|kw|คำวิสามานยนาม}} # {{tcl|kw|อิหร่าน|id=Q794}} ==== Coordinate terms ==== {{list:countries in Asia/kw}} ==== คำเกี่ยวข้อง ==== {{col|kw |iranek<t:เกี่ยวกับอิหร่าน><pos:a> }} == ภาษาเซอร์โบ-โครเอเชีย == {{wp|sh:}} === การออกเสียง === * {{IPA|sh|/ǐraːn/}} * {{hyphenation|sh|I|ran}} === คำวิสามานยนาม === {{sh-propn|Ìrān|m-in}} # {{tcl|sh|อิหร่าน|id=Q794}} ==== การผันรูป ==== {{sh-decl-noun-unc |Ìrān |Irána |Iranu |Iran |Irane |Iranu |Iranom }} == ภาษาญี่ปุ่น == === การถอดเป็นอักษรโรมัน === {{ja-romaji}} # {{ja-romanization of|イラン}} == ภาษาดัตช์ == === รากศัพท์ === แรกสุดจาก{{der|nl|fa|ایران|tr=irân}}, จาก{{der|nl|pal|𐭠𐭩𐭥𐭠𐭭}} {{etystub|nl}} === การออกเสียง === * {{IPA|nl|/iˈrɑn/|/iˈraːn/|q2=somewhat dated|/ˈiː.rɑn/|q3=dated}} * {{audio|nl|Nl-Iran.ogg}} * {{hyphenation|nl|Iran}} === คำวิสามานยนาม === {{nl-proper noun|n}} # {{tcl|nl|อิหร่าน|id=Q794}} ==== คำเกี่ยวข้อง ==== * {{l|nl|Iraans}}, {{l|nl|Iraanse}} * {{l|nl|Iraniër}} === ดูเพิ่ม === * {{l|nl|Perzisch}} {{q|archaic exonym}} == ภาษาเดนมาร์ก == === คำวิสามานยนาม === {{head|da|คำวิสามานยนาม}} # {{tcl|da|อิหร่าน|id=Q794}} ==== ลูกคำ ==== * {{l|da|iraner}} * {{l|da|iransk}} == ภาษาตากาล็อก == {{wp|tl:}} === รากศัพท์ === {{bor+|tl|es|Irán||อิหร่าน}} === การออกเสียง === {{tl-pr|Irán}} === คำวิสามานยนาม === {{tl-proper noun|Irán|b=+}} # {{tcl|tl|อิหร่าน|id=Q794}} ==== Coordinate terms ==== {{list:countries in Asia/tl/Latn}} == ภาษานอร์เวย์แบบนือนอสก์ == {{wp|nn:}} === คำวิสามานยนาม === {{head|nn|คำวิสามานยนาม}} # {{tcl|nn|อิหร่าน|id=Q794}} ==== คำเกี่ยวข้อง ==== * {{l|nn|iranar}} * {{l|nn|iransk}} == ภาษานอร์เวย์แบบบุ๊กมอล == {{wp|no:}} === คำวิสามานยนาม === {{head|nb|คำวิสามานยนาม}} # {{tcl|nb|อิหร่าน|id=Q794}} ==== คำเกี่ยวข้อง ==== * {{l|nb|iraner}} * {{l|nb|iransk}} == ภาษานาวัตล์ตอนกลาง == === คำวิสามานยนาม === {{head|nhn|คำวิสามานยนาม}} # {{tcl|nhn|อิหร่าน|id=Q794}} == ภาษาบาสก์ == {{wp|eu:}} === การออกเสียง === {{eu-pr}} === คำวิสามานยนาม === {{eu-proper noun|in}} # {{tcl|eu|อิหร่าน|id=Q794}} ==== การผันรูป ==== {{eu-ndecl|in.pr.topo}} ==== ลูกคำ ==== {{col|eu|Irango Islamiar Errepublika<t:สาธารณรัฐอิสลามอิหร่าน>|irandar<t:เกี่ยวกับ/ชาวอิหร่าน>}} ==== คำเกี่ยวข้อง ==== {{col|eu|iraniar<t:เกี่ยวกับ/ชาวอิหร่าน>}} ==== ดูเพิ่ม ==== {{list:countries in Asia/eu}} == ภาษาเบรอตง == === คำวิสามานยนาม === {{head|br|คำวิสามานยนาม|g=?}} # {{tcl|br|อิหร่าน|id=Q794}} == ภาษาโปแลนด์ == === การออกเสียง === {{pl-pr|a=Pl-Iran.ogg}} === คำวิสามานยนาม === {{head|pl|คำวิสามานยนาม|g=m}} # {{tcl|pl|อิหร่าน|id=Q794}} ==== การผันรูป ==== {{pl-decl-noun-sing |Iran |Iranu |Iranowi |Iran |Iranem |Iranie |Iranie }} ==== ลูกคำ ==== {{col|pl|title=adjective|irański}} {{col|pl|title=nouns|Irańczyk|Iranka}} === อ่านเพิ่ม === * {{R:pl:WSJP}} * {{R:pl:PWN}} == ภาษาฝรั่งเศส == === การออกเสียง === * {{fr-IPA}} * {{audio|fr|LL-Q150 (fra)-Jules78120-Iran.wav}} * {{audio|fr|LL-Q150 (fra)-Mecanautes-Iran.wav|a=France}} * {{rhymes|fr|ɑ̃|s=2}} === คำวิสามานยนาม === {{fr-proper noun|m}} # {{tcl|fr|อิหร่าน|id=Q794|place_official=fr:République islamique d'Iran}} ==== คำเกี่ยวข้อง ==== * {{l|fr|iranien}}, {{l|fr|iranienne}} === ดูเพิ่ม === * {{l|fr|persane}} {{q|archaic exonym}} * {{l|fr|Perse}} {{q|archaic exonym}} === ดูเพิ่ม === {{list:countries in Asia/fr}} == ภาษาฟินแลนด์ == {{wp|fi:}} === รากศัพท์ === จาก{{der|fi|fa|ایران}} === การออกเสียง === {{fi-p}} === คำวิสามานยนาม === {{fi-proper noun}} # {{tcl|fi|อิหร่าน|id=Q794}} ==== การผันรูป ==== {{fi-decl-paperi|n=sg|Iran|a|i=0}} ==== ลูกคำ ==== {{col|fi |iranilainen }} {{col|fi|title=compounds |iraninmyyrä |iraninruohomyyrä }} === คำสลับอักษร === * {{anagrams|fi|a=ainr|Arin|arin}} == ภาษาแฟโร == === การออกเสียง === * {{IPA|fo|/ˈiːɹan/}} * {{homophones|fo|iðran}} === คำวิสามานยนาม === {{head|fo|คำวิสามานยนาม}} # {{tcl|fo|อิหร่าน|id=Q794}} ==== การผันรูป ==== {{fo-decl-proper-noun-s-indef|Iran|Iran|Irani|Irans}} ==== คำเกี่ยวข้อง ==== * {{l|fo|iranskur}} === คำสลับอักษร === * {{l|fo|Arni}} * {{l|fo|Rani}} == ภาษามอลตา == === การออกเสียง === * {{IPA|mt|/ɪˈraːn/}} * {{rhymes|mt|aːn|s=2}} * === คำวิสามานยนาม === {{mt-proper noun|head=L-Iran|g=m}} # {{tcl|mt|อิหร่าน|id=Q794|place_official=mt:Repubblika Iżlamika tal-Iran}} == ภาษามาเลเซีย == === รากศัพท์ === {{bor+|ms|en|Iran}} === การออกเสียง === {{ms-IPA}} ** {{audio|ms| LL-Q9237 (msa)-GinormousBuildings-Iran.wav |a=Malaysia}} * {{rhymes|ms|ran|an|s2=2}} * {{hyph|ms|I|ran}} === คำวิสามานยนาม === {{ms-proper noun}} # {{tcl|ms|อิหร่าน|id=Q794}} ==== ดูเพิ่ม ==== {{list:countries in Asia/ms}} == ภาษาเยอรมัน == === การออกเสียง === * {{IPA|de|/iˈʁaːn/}} * {{audio|de|De-Iran.ogg}} * {{audio|de|De-Iran2.ogg|a=<<Germany>> (<<Berlin>>)}} * {{rhymes|de|aːn|s=2}} === คำวิสามานยนาม === {{de-proper noun|toponym}} # {{tcl|de|อิหร่าน|id=Q794}} === คำวิสามานยนาม === {{de-proper noun|m,(s)}} # {{tcl|de|อิหร่าน|id=Q794}} ==== หมายเหตุการใช้ ==== * บางครั้งคำว่า ''Iran'' ใช้โดยไม่มีคำนำหน้าคำนามชี้เฉพาะ เช่น ''in Iran'' (“ในอิหร่าน”) ในกรณีนี้รูปสัมพันธการกจะเป็น ''Irans'' เสมอ เช่น ''Irans Hauptstadt'' (“เมืองหลวงของอิหร่าน”) * อย่างไรก็ตาม โดยทั่วไปจะใช้แบบมีคำนำหน้าคำนามชี้เฉพาะ กล่าวคือ ''der Iran'' เช่น ''im Iran'' (“ในอิหร่าน”) ในกรณีนี้รูปสัมพันธการกมักจะเป็น ''des Iran'' แม้ว่า ''des Irans'' ก็ถูกต้องเช่นกัน เช่น ''die Hauptstadt des Iran(s)'' (“เมืองหลวงของอิหร่าน”) ==== ลูกคำ ==== * {{l|de|Iraner}} * {{l|de|iranisch}} ==== คำเกี่ยวข้อง ==== * {{l|de|Iranier}} === ดูเพิ่ม === * {{l|de|Persien}} === อ่านเพิ่ม === * {{R:de:DWDS}} == ภาษาโรมาเนีย == === การออกเสียง === * {{audio|ro|LL-Q7913 (ron)-KlaudiuMihaila-Iran.wav}} === คำวิสามานยนาม === {{head|ro|คำวิสามานยนาม|g=n}} # {{tcl|ro|อิหร่าน|id=Q794}} == ภาษาเวลส์ == {{wp|cy:}} === การออกเสียง === * {{IPA|cy|/ɪˈran/}} * {{rhymes|cy|an|s=2}} === คำวิสามานยนาม === {{cy-proper noun|f}} # {{tcl|cy|อิหร่าน|id=Q794}} ==== Coordinate terms ==== {{list:countries in Asia/cy}} ==== ลูกคำ ==== * {{l|cy|Gweriniaeth Islamaidd Iran||สาธารณรัฐอิสลามอิหร่าน}} === การกลายรูป === {{cy-mut}} == ภาษาสโลวีเนีย == {{wp|sl:}} === การออกเสียง === * {{sl-IPA|Irân}} === คำวิสามานยนาม === {{sl-proper noun|Irân|m-in}} # {{tcl|sl|อิหร่าน|id=Q794}} ==== การผันรูป ==== {{sl-decl-noun-m|Irân|n=sg}} ==== ลูกคำ ==== * {{l|sl|Iránec}} / {{l|sl|Iránka}} * {{l|sl|iránski}} == ภาษาสวีเดน == {{wp|sv:}} === การออกเสียง === * {{audio|sv|LL-Q9027 (swe)-Moonhouse-Iran.wav}} === คำวิสามานยนาม === {{sv-proper noun|n}} # {{tcl|sv|อิหร่าน|id=Q794}} ==== ลูกคำ ==== * {{l|sv|iranier}} * {{l|sv|iransk}} * {{l|sv|iranska}} ==== ดูเพิ่ม ==== {{list:countries in Asia/sv}} === อ้างอิง === * {{R:svenska.se|saol}} * [https://mesh.kib.ki.se/term/D007492/iran Svensk MeSH] === คำสลับอักษร === * {{anagrams|sv|a=ainr|inar|rian}} == ภาษาอังกฤษ == {{wp|en:}} === รูปแบบอื่น === * {{alt|en|Irân|Irān|Îrân|Īrān||all|_|rare}} === รากศัพท์ === {{wp|+ (word),Aniran}} จาก{{der|en|fa-ira|ایران|tr=irân}}, จาก{{der|en|fa-cls|ایران|tr=ērān}}, จาก{{der|en|pal|𐭠𐭩𐭥𐭠𐭭||of the Aryans}}. See {{m|pal|𐭠𐭩𐭥𐭠𐭭}} for word formation, further etymology and cognates. The name of the political entity is a 3rd-century development that derives from the indigenous ethnolinguistic name of the Iranian peoples,<ref name="MacKenzie">MacKenzie, David Niel (1998). [https://web.archive.org/web/20170313095654/http://www.iranicaonline.org/articles/eran-eransah "Ērān, Ērānšahr"]. ''Encyclopedia Iranica'', volume 8. Costa Mesa: Mazda, page 534.</ref> i.e. the great variety of Iranian tribes that spoke an Iranian language. === การออกเสียง === * {{IPA|en|/ɪˈɹɑːn/|/ɪˈɹæn/|/iː-/|a=UK}}<ref>{{R:Lexico}}</ref> * {{IPA|en|/ɪˈɹɑn/|/ɪˈɹæn/|/aɪˈɹæn/|a=US|q3=proscribed}}<ref>{{R:Merriam-Webster Online}}</ref> * {{enPR|ĭ-răn′|ē-rän′}}<ref name="seltzer">{{R:en:Seltzer:1952|entry=Iran|page=844|col=2}}</ref><ref name="IGEA">{{R:en:IGEA:1979|entry=I·ran|page=344|col=1}}</ref> * {{audio|en|LL-Q1860 (eng)-Soundguys-Iran.wav|a=UK}} * {{audio|en|En-us-Iran-noun.ogg|a=US}} * {{audio|en|En-us-Iran.ogg|a=US}} * {{rhymes|en|ɑːn|æn|s=2}} === คำวิสามานยนาม === {{en-prop}} # {{tcl|en|อิหร่าน|id=Q794|place_official=en:Islamic Republic of Iran|place_capital=en:Tehran}} #* ''ca.'' '''1675''', Jean Chardin (Sir John Chardin), ''Travels in Persia, 1673-1677'', fasc. reprint 1988, Dover, page 126: #*: "The ''Persians'', in naming their country, make use of one word, which they indifferently pronounce ''Iroun'', and '''''Iran'''''." #* {{quote-book|en|year=1828|author=Thomas Keightley|authorlink=Thomas Keightley|title=The Fairy Mythology|volume=I|location=London|publisher=William Harrison Ainsworth|page=25|passage=Light was happiness; and the people of '''Iran''', the land of light, were the favourites of heaven; while those of [[Turan]], the gloomy region beyond the mountains to the north, were its enemies.}} #* {{quote-book|en|year=2005|editor=Massoume Price|title=Iran's Diverse Peoples|publisher=ABC-Clio|page=xiii |passage="'''Iran''' is a vast and ancient country in a strategic location in the Middle East. It borders Russia, Iraq, Turkey, Pakistan, the Gulf of Oman, and the Persian Gulf."}} #* {{quote-journal|en|author=Frederik Pleitgen; Eliza Mackintosh|title=US is ‘not in a position to obliterate Iran,’ Iranian foreign minister says|magazine=w:CNN|url=https://www.cnn.com/2019/06/26/middleeast/iran-zarif-us-trump-obliteration-intl|date=2019-06-26|passage=The US President himself accused Iran of being behind the provocation, threatening airstrikes on '''Iran''' last week – but calling them off just minutes before they were to begin.|archiveurl=https://web.archive.org/web/20260210155525/https://www.cnn.com/2019/06/26/middleeast/iran-zarif-us-trump-obliteration-intl}} # {{lb|en|literary|technical}} The regions inhabited by the Iranian peoples. #* '''1882''', James Darmesteter, ''The Zend-Avesta'', volume 1 (''SBE'', volume 31), Oxford UP, page 7, xxviii: #*: "Kavi means a king, but it is particularly used of the kings belonging to the second and most celebrated of the two mythical dynasties of '''Iran'''." #*: "One certain fact is the occurrence of geographical names [Bactria, Sogdiana, etc] in Vendidad I, which are obviously intended to describe the earliest homes of the Iranian races whose lore was the Avesta." #* {{quote-book|en|year=1898|author=A. V. W. Jackson|title=Zoroaster: The Prophet of Ancient '''Iran'''<!-- -->|publisher=Macmillan|page=10|passage="Zoroaster of '''Iran'''. &mdash; Zoroaster, it is believed, sprang up in the seventh century before the Christian era, somewhere in the land between the Indus and the Tigris."}} #* {{quote-text|en|year=1909|editor=John Huston Finley|chapter=Iran|title=Nelson's Perpetual Loose-leaf Encyclopaedia|volume=6|page=479 |passage="'''Iran'''. in early times, the name applied to the great Asiatic plateau which comprised the entire region from the Caucasus, the Caspian Sea, and Russian Turkestan on the north to the Tigris, the Persian Gulf, and the Arabian Sea on the west and south, and extending to the Indus on the east, likewise comprising the modern Afghanistan and the territory to the north of it as far as the Jaxartes River."}} #* {{quote-book|en|year=1985|author=J. M. Cooke|chapter=The Rise of the Achaemenids|title=Cambridge Historiy of Iran|volume=2|page=290 |passage="[W]e may surmise that there was a strong sense of Iranian unity lending solidarity to the eastern half of the empire. It is only in the generations after Alexander, in Eudemus and in Eratosthenes (ap. Strabo), that we find mention of the concept of a greater nation of '''Iran''' (Arianē) stretching from the Zagros to the Indus; but the sense of unity must have been there, for Herodotus tells us that the Medes were formerly called Arioi, and Darius I (followed by Xerxes) in his inscriptions proclaims himself an Iranian (Ariya) by race - he speaks of himself in ascending order as an Achaemenid, a Persian and an Iranian (Naqsh-i Rustam)."}} #* {{quote-book|en|year=1990|author=Hubert Darke|chapter=Cambridge History of Iran|title=Encyclopedia Iranica|volume=4|page=724 |passage="[The] Cambridge History of '''Iran''' [is] a survey of the history and historical geography of the land which is present-day Iran, as well as other territories inhabited by peoples of Iranian descent."}} ==== ลูกคำ ==== {{col|en |Greater Iran |Iranianize|Imperial State of Iran|Pahlavi Iran |Iranify |Iranize |Iranization |Iranianization |Iranologist |Iranology |Iranophile |Iranophobe |Iranophone }} ==== คำเกี่ยวข้อง ==== * {{l|en|Iranian}} === ดูเพิ่ม === * {{l|en|Persia}} {{lb|en|archaic exonym}} {{list:countries in Asia/en}} === อ้างอิง === <references/> === คำสลับอักษร === * {{anagrams|en|a=ainr|Rina|rani|Nair|rain|ARNI|Rain|arni|NIRA|Rian|ARIN|airn|RNAi|Rani|Arin}} == ภาษาอาฟรีกานส์ == {{wp|af:}} === การออกเสียง === * {{audio|af|LL-Q14196 (afr)-Oesjaar-Iran.wav}} === คำวิสามานยนาม === {{af-proper noun}} # {{tcl|af|อิหร่าน|id=Q794|place_capital=af:Teheran}} ==== ลูกคำ ==== * {{l|af|Iraniër}} * {{l|af|Irannees}} * {{l|af|Irans}} == ภาษาอิตาลี == {{wp|it:}} === การออกเสียง === {{it-pr|Ìran,#Iràn<r:DiPI>}} === คำวิสามานยนาม === {{it-proper noun|m}} # {{tcl|it|อิหร่าน|id=Q794}} ==== คำเกี่ยวข้อง ==== * {{l|it|iraniano}} * {{l|it|iranico}} * {{l|it|iranista}} === ดูเพิ่ม === * {{l|it|Persia}} {{q|archaic exonym}} === อ้างอิง === <references /> === คำสลับอักษร === * {{anagrams|it|a=ainr|Rina|nari}} == ภาษาอินโดนีเซีย == === รากศัพท์ === จาก{{der|id|fa|ایران|tr=irân}}, จาก{{der|id|pal|𐭠𐭩𐭥𐭠𐭭||of the Aryans}}. See {{m|pal|𐭠𐭩𐭥𐭠𐭭}} for word formation, further etymology and cognates. {{doublet|id|aria|arya|Arya|ayah}}. === การออกเสียง === {{id-pr}} === คำวิสามานยนาม === {{head|id|คำวิสามานยนาม}} # {{tcl|id|อิหร่าน|id=Q794}} === ดูเพิ่ม === {{list:countries in Asia/id}} === อ่านเพิ่ม === * {{R:KBBI Daring}} == ภาษาอินเทอร์ลิงกวา == === คำวิสามานยนาม === {{head|ia|คำวิสามานยนาม}} # {{tcl|ia|อิหร่าน|id=Q794}} == ภาษาแอลเบเนีย == {{wp|sq:+i}} === คำวิสามานยนาม === {{sq-proper noun|m|Irani}} # {{tcl|sq|อิหร่าน|id=Q794}} ==== การผันรูป ==== {{sq-noun-m-unc}} ==== ลูกคำ ==== * {{l|sq|iranian}}, {{l|sq|iraniane}} dyi8wizwx8by0achhy8ii0qvzjc1mu1 สี 0 8761 5775274 5719227 2026-10-14T10:05:19Z Octahedron80 267 5775274 wikitext text/x-wiki {{also/auto}} == ภาษาไทย == === การออกเสียง === {{th-pron}} === รากศัพท์ 1 === {{inh+|th|tai-swe-pro|*siːᴬ¹}} (Jonsson, 1991); ร่วมเชื้อสายกับ{{cog|nod|ᩈᩦ}}, {{cog|lo|ສີ}}, {{cog|khb|ᦉᦲ}}, {{cog|blt|ꪎꪲ}}, {{cog|shn|သီ}}, {{cog|tdd|ᥔᥤᥴ}}, {{cog|phk|ꩬီ}}, {{cog|aho|𑜏𑜣}} ==== คำนาม ==== {{th-noun}} # [[ชื่อ]][[เครื่อง]][[สำหรับ]][[หมุน]][[บด]][[ข้าวเปลือก]][[เพื่อ]][[ทำ]][[ให้]][[เปลือก]][[แตก]][[เป็น]][[ข้าวกล้อง]] ==== คำกริยา ==== {{th-verb}} # {{lb|th|โบ}} [[ครู่]], [[รู่]] # [[ครูด]], [[ถู]] #: {{ux|th|ช้างเอาตัวสีกับต้นไม้}} #: {{ux|th|ลมพัดแรงทำให้ลำไม้ไผ่สีกัน}} # [[ชัก]] #: {{ux|th|สีซอ}} # ทำให้เปลือก[[ออก]][[ด้วย]]เครื่อง[[อย่าง]]สี[[ข้าว]] === รากศัพท์ 2 === เป็นไปได้ว่ามาจาก{{der|th|ltc|-}} {{ltc-l|色}} ซึ่ง /-k/ หายไป; ร่วมเชื้อสายกับ{{cog|lo|ສີ}}, {{cog|khb|ᦉᦲ}}, {{cog|shn|သီ}}, {{cog|nod|ᩈᩦ}} ==== คำนาม ==== {{th-noun}} # [[ลักษณะ]][[ของ]][[แสง]][[สว่าง]] [[ปรากฏ]][[แก่]][[ตา]]ให้[[เห็น]]เป็น[[ขาว]] [[ดำ]] [[แดง]] [[เขียว]] [[เป็นต้น]] # [[สิ่ง]][[ที่]]ทำให้ตาเห็นเป็น ขาว ดำ แดง เขียว เป็นต้น #: {{ux|th|สีทาบ้าน}} #: {{ux|th|สีย้อมผ้า}} #: {{ux|th|สีวาดภาพ}} ===== คำพ้องความ ===== * {{l|th|รงค์}} ===== ลูกคำ ===== {{col4|th|สีชอล์ก|สีเทียน|สีน้ำ|สีน้ำมัน|สีผสมอาหาร|สีไม้|สีสัน}} ===== คำแปลภาษาอื่น ===== {{trans-top|คำแปลภาษาอื่น}} * กรีก: {{t+|el|χρώμα|n}} *: โบราณ: {{t|grc|χρῶμα|n}}, {{t|grc|χρώς|m}} * กรีนแลนด์: {{t+|kl|qalipaat}} * กันนฑะ: {{t+|kn|ಬಣ್ಣ}} * กาลิเซีย: {{t+|gl|cor|f}}, {{t|gl|color|f}} * เกาหลี: {{t+|ko|색깔}}, {{t+|ko|색}} * แกลิกแบบสกอต: {{t|gd|dath|m}} * เขมร: {{t+|km|ពណ៌}} * คาซัค: {{t+|kk|түс}}, {{t|kk|реңк}}, {{t|kk|рең}} * กาตาลา: {{t+|ca|color|m|f}} * คีร์กีซ: {{t+|ky|түс}} * จ้วง: {{t-needed|za}} * จอร์เจีย: {{t|ka|ფერი}} * จีน: *: กลาง: {{t+|cmn|色|tr=sè}}, {{t+|cmn|顏色}}, {{t+|cmn|颜色|tr=yánsè}} *: กวางตุ้ง: {{t|yue|色|tr=sik1}}, {{t|yue|顏色}}, {{t|yue|颜色|tr=ngaan4 sik1}} *: หมิ่นใต้: {{t|nan|色|tr=siak / sek}}, {{t|nan|顏色}}, {{t|nan|颜色|tr=gân-siak / gân-sek}} * ชวา: {{t+|jv|werna}} * เช็ก: {{t+|cs|barva|f}} * เชโรกี: {{t|chr|ᏗᎧᏃᏗ}} * เซบัวโน: {{t|ceb|kolor}} * เซอร์โบ-โครเอเชีย: * โซมาลี: {{t|so|midab}} * ญี่ปุ่น: {{t+|ja|色|tr=いろ, iro}}, {{t+|ja|色彩|tr=しきさい, shikisai}} * ดัตช์: {{t+|nl|kleur|f}} * เดนมาร์ก: {{t+|da|farve|c}} * ตากาล็อก: {{t+|tl|kulay}} * ตาตาร์: {{t+|tt|төс}} * ตุรกี: {{t+|tr|renk}} * เติร์กเมน: {{t|tk|reňk}} * ทมิฬ: {{t+|ta|நிறம்}} * ทาจิก: {{t+|tg|ранг}} * ทิเบต: {{t|bo|ཚོས་གཞི}}, {{t|bo|ཚོན་མདོག}} * นอร์เวย์: {{t+|no|farge|m}} * เนปาล: {{t|ne|रङ्ग|tr=raṅg}} * แบชเคียร์: {{t|ba|төҫ}} * บัลแกเรีย: {{t+|bg|цвят|m}} * บาสก์: {{t|eu|kolore}} * เบงกอล: {{t|bn|রং}} * เบลารุส: {{t|be|ко́лер|m}} * เปอร์เซีย: {{t+|fa|رنگ|tr=rang}}, {{t+|fa|گون|tr=gun}} * เปอร์เซียกลาง: {{t|pal|𐭫𐭭𐭢}} * โปรตุเกส: {{t+|pt|cor|f}} * โปแลนด์: {{t+|pl|kolor|m-in}}, {{t+|pl|barwa|f}} * ฝรั่งเศส: {{t+|fr|couleur|f}} * พม่า: {{t+|my|အရောင်}} * ฟริเซียนตะวันตก: {{t+|fy|kleur}} * ฟรียูลี: {{t|fur|colôr|m}} * ภาษาแฟโร: {{t|fo|litur|m}} * มลยาฬัม: {{t|ml|വര്‍ണ്ണരാജി}} * มองโกเลีย: {{t-needed|mn}} * มอลตา: {{t|mt|kulur|m}} * มาเลเซีย: {{t+|ms|warna}}, {{t+|ms|rona}} * เมารี: {{t|mi|tae}}, {{t|mi|kara}} * ยูเครน: {{t+|uk|ко́лір|m}}, {{t|uk|фа́рба|f}} * เยอรมัน: {{t+|de|Farbe|f}} * รัสเซีย: {{t+|ru|цвет|m}}, {{t+|ru|ко́лер|m}} {{qualifier|colloquial or special}} * โรมาเนีย: {{t+|ro|culoare|f}} * ละติน: {{t+|la|color|m}} * ลักเซมเบิร์ก: {{t+|lb|Faarf|f}}, {{t|lb|Fuarf|f}} * ลัตเวีย: {{t+|lv|krāsa|f}} * ลาว: {{t+|lo|ສີ}} * ลิทัวเนีย: {{t+|lt|spalva|f}} * เวลส์: {{t+|cy|lliw|f}} * โวลาปุก: {{t+|vo|köl}} * สเปน: {{t+|es|color|m}} * สโลวีเนีย: {{t+|sl|barva|f}} * สวาฮีลี: {{t+|sw|michezo}}, {{t+|sw|rangi}} * สวีเดน: {{t+|sv|färg|c}} * สันสกฤต: {{t+|sa|रङ्ग|m}} * สิงหล: {{t|si|වර්ණ}} * ออกซิตัน: {{t+|oc|color|f}} * ออกซิตันเก่า: {{t|pro|color|m}} * อังกฤษ: {{t+|en|color}}, {{t+|en|colour}} * อังกฤษเก่า: {{t|ang|blēo|n}} * อัมฮารา: {{t|am|ቀለም}} * อัสสัม: {{t|as|ৰং}}, {{t|as|বৰণ}} * อาเซอร์ไบจาน: {{t+|az|rəng}} * อาฟรีกานส์: {{t|af|kleur}} * อารบิก: {{t|ar|لَوْن|m}} * อาร์มีเนีย: {{t+|hy|գույն}} * อีโด: {{t+|io|koloro}} * อิตาลี: {{t+|it|colore|m}} * อินโตนีเซีย: {{t+|id|warna}} * อุซเบก: {{t+|uz|rang}}, {{t+|uz|tus}} * เอสโตเนีย: {{t+|et|värvus}}, {{t+|et|värv}} * เอสเปรันโต: {{t|eo|koloro}} * แอลเบเนีย: {{t+|sq|ngjyrë|f}} * ไอซ์แลนด์: {{t+|is|litur|m}} * ไอริช: {{t+|ga|dath|m}} * ฮังการี: {{t+|hu|szín}} * ฮาวาย: {{t|haw|waihoʻoluʻu}} * ฮินดี: {{t+|hi|रंग|m}} * ฮุนสริก: {{t|hrx|Forreb|f}} * ฮีบรู: {{t+|he|צֶבַע|m|tr=tséva}} {{trans-bottom}} ===== คำสืบทอด ===== * {{desc|km|ស៊ី|bor=1}} * {{desc|urk|ซี|bor=1}} {{topics|th|สี}} == ภาษาคำเมือง == === การออกเสียง === * {{IPA|nod|/siː˨˦/|a=เชียงใหม่}} === คำนาม === {{nod-noun}} # {{alternative form of|nod|ᩈᩦ}} === คำกริยา === {{nod-verb}} # {{lb|nod|สกรรม}} {{alternative form of|nod|ᩈᩦ}} 7s3x2dmavgx1f3q7tn4l147n7tvlqf8 Irán 0 11445 5775275 5714902 2026-10-14T10:05:41Z Octahedron80 267 5775275 wikitext text/x-wiki {{also/auto}} == ภาษากาลิเซีย == {{wp|gl:}} === รูปแบบอื่น === * {{alt|gl|Irám|Irão||AGAL}} === การออกเสียง === {{gl-pr}} * {{hyph|gl|I|rán}} === คำวิสามานยนาม === {{gl-proper noun|m}} # {{tcl|gl|อิหร่าน|id=Q794}} == ภาษาสเปน == {{wp|es:}} === รากศัพท์ === {{bor+|es|fa|ایران|tr=irân}}, จาก{{der|es|pal|𐭠𐭩𐭥𐭠𐭭||(land) of the Iranians}} === การออกเสียง === {{es-pr|+<audio:LL-Q1321 (spa)-Rodelar-Irán.wav<a:Spain>><audio:LL-Q1321 (spa)-Rodrigo5260-Irán.wav<a:Peru><IPA:[iˈɾãŋ]>><hmp:irán>}} === คำวิสามานยนาม === {{es-proper noun|m}} # {{tcl|es|อิหร่าน|id=Q794}} ==== หมายเหตุการใช้ ==== * The definite article is optional when referring to Iran's exonym in Spanish (i.e. '''''el''''' Irán). ==== ลูกคำ ==== {{col|es|iraní|iraniano|iranio|República Islámica de Irán}} ==== ดูเพิ่ม ==== {{list:countries in Asia/es}} == ภาษาสโลวัก == {{wp|sk:}} === การออกเสียง === * {{sk-IPA}} === คำวิสามานยนาม === {{sk-proper noun|m-in|gen=Iránu|decl=dub}} # {{tcl|sk|อิหร่าน|id=Q794}} ==== การผันรูป ==== {{sk-decl-noun-sg|Irán|Iránu|Iránu|Irán|Iráne|Iránom}} ==== ลูกคำ ==== * {{l|sk|Iránec}} * {{l|sk|Iránka}} * {{l|sk|iránsky}} === อ่านเพิ่ม === * {{R:sk:SDK}} == ภาษาอัสตูเรียส == {{wp|ast:}} === คำวิสามานยนาม === {{ast-proper noun}} # {{tcl|ast|อิหร่าน|id=Q794}} ==== คำเกี่ยวข้อง ==== * {{l|ast|iranín}} == ภาษาฮังการี == {{swp|hu:}} === การออกเสียง === * {{hu-IPA}} * {{hyphenation|hu|Irán}} * {{rhymes|hu|aːn|s=2}} === คำวิสามานยนาม === {{head|hu|คำวิสามานยนาม}} # {{tcl|hu|อิหร่าน|id=Q794|place_official=hu:Iráni Iszlám Köztársaság}} ==== การผันรูป ==== {{hu-infl-nom|Iráno|o|-|n=sg}}{{hu-pos-otok|Irán|j|jai|n=sg}} ==== ลูกคำ ==== {{col4|hu |iráni }} === ดูเพิ่ม === {{list:countries in Asia/hu}} n5p0trsk9n97ze0s2qpe7t1ekq3k8te กรัน 0 12804 5775175 1879320 2026-10-14T02:04:50Z Octahedron80 267 /* คำคุณศัพท์ */ 5775175 wikitext text/x-wiki {{also/auto}} == ภาษาไทย == === การออกเสียง === {{th-pron|กฺรัน}} === คำคุณศัพท์ === {{th-adj|-}} # {{lb|th|กลอน}} [[เล็ก]], [[แคระ]] #: {{ux|th|กรวด'''กรัน'''ขราราย}} (สุธน) === คำนาม === {{th-noun}} # [[ชื่อ]][[กล้วย]][[พันธุ์]][[หนึ่ง]][[ของ]][[ชนิด]] ''Musa sapientum'' L. [[ผล]][[สั้น]][[ป้อม]][[มี]][[เมล็ด]][[มาก]], [[กล้วยเต่า]] [[ก็]][[เรียก]] oxsc2w6175ek5p42hauutcchad6sl23 سمك 0 12977 5775241 1272946 2026-10-14T08:50:28Z Octahedron80 267 /* ภาษาอาหรับ */ เก็บกวาด 5775241 wikitext text/x-wiki == ภาษาอาหรับ == {{ar-rootbox|س م ك}} === รากศัพท์ 1 === จากราก {{ar-root|س م ك}} หมายถึง “อยู่สูงหรือสูงส่ง”, “ได้รับการค้ำจุนหรือพยุงไว้”, “ค้ำจุน”; จึงนำไปสู่ความหมาย “แหล่งค้ำจุนชีวิต”, “อาหารหลักหรืออาหารยังชีพ”; ร่วมเชื้อสายกับ{{cog|he|סָมַך|tr=samak|t=พิง, วาง, พัก, ค้ำจุน}} ซึ่งปรากฏในคัมภีร์ไบเบิลในนัยที่คล้ายกันคือ “ค้ำจุน” หรือ “หล่อเลี้ยง”<ref>[http://biblehub.com/text/genesis/27-37.htm Genesis 27:37]</ref> ==== การออกเสียง ==== {{ar-pr|سَمَك}} * {{audio|ar|Ar-سمك.ogg}} ==== คำนาม ==== {{ar-coll-noun|سَمَك|pl=أَسْماك,سِمَاك,سُمُوك|sing=+}} # [[ปลา]] #: {{uxa|ar|السَّرْدِينُ عِبَارَةٌ عَنْ سَمَكٍ مُدْهِنٍ صَغِيرٍ يُمْكِنُ أَكْلُهُ دُونَ مَضْغ.|t=ปลาซาร์ดีนคือปลาขนาดเล็กที่มีไขมันสูง ซึ่งสามารถรับประทานได้ทั้งตัว}} ===== การผันรูป ===== {{ar-decl-coll-noun|سَمَك|sing=سَمَكَة|pl=أَسْماك|pl2=سِمَاك|pl3=سُمُوك}} ===== คำสืบทอด ===== * {{desc|afb|سمچ|tr=simič, simač}} * {{desc|acm|سمچ|tr=simač}} * {{desc|gez|ሰመክ|bor=1}} * {{desctree|ota|سمك|tr=semek|bor=1}} * {{desc|sw|samaki|bor=1}} === รากศัพท์ 2 === จากรากศัพท์ {{ar-root|س م ك}} ในแง่ “หล่อเลี้ยง”, “ขุนให้อ้วนท้วนสมบูรณ์”, “ขุนปศุสัตว์ให้อ้วน” ==== คำกริยา ==== {{ar-verb|II}} # ทำให้อ้วน, ทำให้[[หนา]]ขึ้น, ทำให้[[ข้น]]ขึ้น ===== การผันรูป ===== {{ar-conj|II}} ==== คำนาม ==== {{ar-noun|سُمْك|m}} # [[ความหนา]] ===== การผันรูป ===== {{ar-decl-noun|سُمْك}} === รากศัพท์ 3 === จากรากศัพท์ {{ar-root|س م ك}} ในแง่ “อยู่สูงหรือสูงส่ง”, “ได้รับการค้ำจุนหรือพยุงไว้” ==== คำนาม ==== {{ar-noun|سَمْك|m}} # [[หลังคา]], [[เพดาน]] ===== การผันรูป ===== {{ar-decl-noun|سَمْك}} === อ้างอิง === * {{R:ar:Lane|pages=1430-1431}} * {{R:Strong's|H|5564}} {{reflist}} {{C|ar|ปลา}} iqyysgnafm1obr0onbicfn1umu5n7h5 5775242 5775241 2026-10-14T08:50:49Z Octahedron80 267 /* รากศัพท์ 2 */ 5775242 wikitext text/x-wiki == ภาษาอาหรับ == {{ar-rootbox|س م ك}} === รากศัพท์ 1 === จากราก {{ar-root|س م ك}} หมายถึง “อยู่สูงหรือสูงส่ง”, “ได้รับการค้ำจุนหรือพยุงไว้”, “ค้ำจุน”; จึงนำไปสู่ความหมาย “แหล่งค้ำจุนชีวิต”, “อาหารหลักหรืออาหารยังชีพ”; ร่วมเชื้อสายกับ{{cog|he|סָมַך|tr=samak|t=พิง, วาง, พัก, ค้ำจุน}} ซึ่งปรากฏในคัมภีร์ไบเบิลในนัยที่คล้ายกันคือ “ค้ำจุน” หรือ “หล่อเลี้ยง”<ref>[http://biblehub.com/text/genesis/27-37.htm Genesis 27:37]</ref> ==== การออกเสียง ==== {{ar-pr|سَمَك}} * {{audio|ar|Ar-سمك.ogg}} ==== คำนาม ==== {{ar-coll-noun|سَمَك|pl=أَسْماك,سِمَاك,سُمُوك|sing=+}} # [[ปลา]] #: {{uxa|ar|السَّرْدِينُ عِبَارَةٌ عَنْ سَمَكٍ مُدْهِنٍ صَغِيرٍ يُمْكِنُ أَكْلُهُ دُونَ مَضْغ.|t=ปลาซาร์ดีนคือปลาขนาดเล็กที่มีไขมันสูง ซึ่งสามารถรับประทานได้ทั้งตัว}} ===== การผันรูป ===== {{ar-decl-coll-noun|سَمَك|sing=سَمَكَة|pl=أَسْماك|pl2=سِمَاك|pl3=سُمُوك}} ===== คำสืบทอด ===== * {{desc|afb|سمچ|tr=simič, simač}} * {{desc|acm|سمچ|tr=simač}} * {{desc|gez|ሰመክ|bor=1}} * {{desctree|ota|سمك|tr=semek|bor=1}} * {{desc|sw|samaki|bor=1}} === รากศัพท์ 2 === จากราก {{ar-root|س م ك}} ในแง่ “หล่อเลี้ยง”, “ขุนให้อ้วนท้วนสมบูรณ์”, “ขุนปศุสัตว์ให้อ้วน” ==== คำกริยา ==== {{ar-verb|II}} # ทำให้อ้วน, ทำให้[[หนา]]ขึ้น, ทำให้[[ข้น]]ขึ้น ===== การผันรูป ===== {{ar-conj|II}} ==== คำนาม ==== {{ar-noun|سُمْك|m}} # [[ความหนา]] ===== การผันรูป ===== {{ar-decl-noun|سُمْك}} === รากศัพท์ 3 === จากรากศัพท์ {{ar-root|س م ك}} ในแง่ “อยู่สูงหรือสูงส่ง”, “ได้รับการค้ำจุนหรือพยุงไว้” ==== คำนาม ==== {{ar-noun|سَمْك|m}} # [[หลังคา]], [[เพดาน]] ===== การผันรูป ===== {{ar-decl-noun|سَمْك}} === อ้างอิง === * {{R:ar:Lane|pages=1430-1431}} * {{R:Strong's|H|5564}} {{reflist}} {{C|ar|ปลา}} bbp1uqej7v7vnr01bmltazufd8dsmnx 5775243 5775242 2026-10-14T08:51:06Z Octahedron80 267 /* คำกริยา */ 5775243 wikitext text/x-wiki == ภาษาอาหรับ == {{ar-rootbox|س م ك}} === รากศัพท์ 1 === จากราก {{ar-root|س م ك}} หมายถึง “อยู่สูงหรือสูงส่ง”, “ได้รับการค้ำจุนหรือพยุงไว้”, “ค้ำจุน”; จึงนำไปสู่ความหมาย “แหล่งค้ำจุนชีวิต”, “อาหารหลักหรืออาหารยังชีพ”; ร่วมเชื้อสายกับ{{cog|he|סָมַך|tr=samak|t=พิง, วาง, พัก, ค้ำจุน}} ซึ่งปรากฏในคัมภีร์ไบเบิลในนัยที่คล้ายกันคือ “ค้ำจุน” หรือ “หล่อเลี้ยง”<ref>[http://biblehub.com/text/genesis/27-37.htm Genesis 27:37]</ref> ==== การออกเสียง ==== {{ar-pr|سَمَك}} * {{audio|ar|Ar-سمك.ogg}} ==== คำนาม ==== {{ar-coll-noun|سَمَك|pl=أَسْماك,سِمَاك,سُمُوك|sing=+}} # [[ปลา]] #: {{uxa|ar|السَّرْدِينُ عِبَارَةٌ عَنْ سَمَكٍ مُدْهِنٍ صَغِيرٍ يُمْكِنُ أَكْلُهُ دُونَ مَضْغ.|t=ปลาซาร์ดีนคือปลาขนาดเล็กที่มีไขมันสูง ซึ่งสามารถรับประทานได้ทั้งตัว}} ===== การผันรูป ===== {{ar-decl-coll-noun|سَمَك|sing=سَمَكَة|pl=أَسْماك|pl2=سِمَاك|pl3=سُمُوك}} ===== คำสืบทอด ===== * {{desc|afb|سمچ|tr=simič, simač}} * {{desc|acm|سمچ|tr=simač}} * {{desc|gez|ሰመክ|bor=1}} * {{desctree|ota|سمك|tr=semek|bor=1}} * {{desc|sw|samaki|bor=1}} === รากศัพท์ 2 === จากราก {{ar-root|س م ك}} ในแง่ “หล่อเลี้ยง”, “ขุนให้อ้วนท้วนสมบูรณ์”, “ขุนปศุสัตว์ให้อ้วน” ==== คำกริยา ==== {{ar-verb|II}} # ทำให้[[อ้วน]], ทำให้[[หนา]]ขึ้น, ทำให้[[ข้น]]ขึ้น ===== การผันรูป ===== {{ar-conj|II}} ==== คำนาม ==== {{ar-noun|سُمْك|m}} # [[ความหนา]] ===== การผันรูป ===== {{ar-decl-noun|سُمْك}} === รากศัพท์ 3 === จากรากศัพท์ {{ar-root|س م ك}} ในแง่ “อยู่สูงหรือสูงส่ง”, “ได้รับการค้ำจุนหรือพยุงไว้” ==== คำนาม ==== {{ar-noun|سَمْك|m}} # [[หลังคา]], [[เพดาน]] ===== การผันรูป ===== {{ar-decl-noun|سَمْك}} === อ้างอิง === * {{R:ar:Lane|pages=1430-1431}} * {{R:Strong's|H|5564}} {{reflist}} {{C|ar|ปลา}} 4jrahathgdryzqipai8kq1s2mq7julb 5775244 5775243 2026-10-14T08:52:29Z Octahedron80 267 5775244 wikitext text/x-wiki == ภาษาอาหรับ == {{ar-rootbox|س م ك}} === รากศัพท์ 1 === จากราก {{ar-root|س م ك}} ที่หมายถึง “อยู่สูงหรือสูงส่ง”, “ได้รับการค้ำจุนหรือพยุงไว้”, “ค้ำจุน”; จึงนำไปสู่ความหมาย “แหล่งค้ำจุนชีวิต”, “อาหารหลักหรืออาหารยังชีพ”; ร่วมเชื้อสายกับ{{cog|he|סָมַך|tr=samak|t=พิง, วาง, พัก, ค้ำจุน}} ซึ่งปรากฏในคัมภีร์ไบเบิลในนัยที่คล้ายกันคือ “ค้ำจุน” หรือ “หล่อเลี้ยง”<ref>[http://biblehub.com/text/genesis/27-37.htm Genesis 27:37]</ref> ==== การออกเสียง ==== {{ar-pr|سَمَك}} * {{audio|ar|Ar-سمك.ogg}} ==== คำนาม ==== {{ar-coll-noun|سَمَك|pl=أَسْماك,سِمَاك,سُمُوك|sing=+}} # [[ปลา]] #: {{uxa|ar|السَّرْدِينُ عِبَارَةٌ عَنْ سَمَكٍ مُدْهِنٍ صَغِيرٍ يُمْكِنُ أَكْلُهُ دُونَ مَضْغ.|t=ปลาซาร์ดีนคือปลาขนาดเล็กที่มีไขมันสูง ซึ่งสามารถรับประทานได้ทั้งตัว}} ===== การผันรูป ===== {{ar-decl-coll-noun|سَمَك|sing=سَمَكَة|pl=أَسْماك|pl2=سِمَاك|pl3=سُمُوك}} ===== คำสืบทอด ===== * {{desc|afb|سمچ|tr=simič, simač}} * {{desc|acm|سمچ|tr=simač}} * {{desc|gez|ሰመክ|bor=1}} * {{desctree|ota|سمك|tr=semek|bor=1}} * {{desc|sw|samaki|bor=1}} === รากศัพท์ 2 === จากราก {{ar-root|س م ك}} ที่หมายถึง “หล่อเลี้ยง”, “ขุนให้อ้วนท้วนสมบูรณ์”, “ขุนปศุสัตว์ให้อ้วน” ==== คำกริยา ==== {{ar-verb|II}} # ทำให้[[อ้วน]], ทำให้[[หนา]]ขึ้น, ทำให้[[ข้น]]ขึ้น ===== การผันรูป ===== {{ar-conj|II}} ==== คำนาม ==== {{ar-noun|سُمْك|m}} # [[ความหนา]] ===== การผันรูป ===== {{ar-decl-noun|سُمْك}} === รากศัพท์ 3 === จากรากศัพท์ {{ar-root|س م ك}} ที่หมายถึง “อยู่สูงหรือสูงส่ง”, “ได้รับการค้ำจุนหรือพยุงไว้” ==== คำนาม ==== {{ar-noun|سَمْك|m}} # [[หลังคา]], [[เพดาน]] ===== การผันรูป ===== {{ar-decl-noun|سَمْك}} === อ้างอิง === * {{R:ar:Lane|pages=1430-1431}} * {{R:Strong's|H|5564}} {{reflist}} {{C|ar|ปลา}} f30fo5a026mi5nh0q3c5jm8l0bttvsu 5775245 5775244 2026-10-14T08:52:41Z Octahedron80 267 /* รากศัพท์ 3 */ 5775245 wikitext text/x-wiki == ภาษาอาหรับ == {{ar-rootbox|س م ك}} === รากศัพท์ 1 === จากราก {{ar-root|س م ك}} ที่หมายถึง “อยู่สูงหรือสูงส่ง”, “ได้รับการค้ำจุนหรือพยุงไว้”, “ค้ำจุน”; จึงนำไปสู่ความหมาย “แหล่งค้ำจุนชีวิต”, “อาหารหลักหรืออาหารยังชีพ”; ร่วมเชื้อสายกับ{{cog|he|סָมַך|tr=samak|t=พิง, วาง, พัก, ค้ำจุน}} ซึ่งปรากฏในคัมภีร์ไบเบิลในนัยที่คล้ายกันคือ “ค้ำจุน” หรือ “หล่อเลี้ยง”<ref>[http://biblehub.com/text/genesis/27-37.htm Genesis 27:37]</ref> ==== การออกเสียง ==== {{ar-pr|سَمَك}} * {{audio|ar|Ar-سمك.ogg}} ==== คำนาม ==== {{ar-coll-noun|سَمَك|pl=أَسْماك,سِمَاك,سُمُوك|sing=+}} # [[ปลา]] #: {{uxa|ar|السَّرْدِينُ عِبَارَةٌ عَنْ سَمَكٍ مُدْهِنٍ صَغِيرٍ يُمْكِنُ أَكْلُهُ دُونَ مَضْغ.|t=ปลาซาร์ดีนคือปลาขนาดเล็กที่มีไขมันสูง ซึ่งสามารถรับประทานได้ทั้งตัว}} ===== การผันรูป ===== {{ar-decl-coll-noun|سَمَك|sing=سَمَكَة|pl=أَسْماك|pl2=سِمَاك|pl3=سُمُوك}} ===== คำสืบทอด ===== * {{desc|afb|سمچ|tr=simič, simač}} * {{desc|acm|سمچ|tr=simač}} * {{desc|gez|ሰመክ|bor=1}} * {{desctree|ota|سمك|tr=semek|bor=1}} * {{desc|sw|samaki|bor=1}} === รากศัพท์ 2 === จากราก {{ar-root|س م ك}} ที่หมายถึง “หล่อเลี้ยง”, “ขุนให้อ้วนท้วนสมบูรณ์”, “ขุนปศุสัตว์ให้อ้วน” ==== คำกริยา ==== {{ar-verb|II}} # ทำให้[[อ้วน]], ทำให้[[หนา]]ขึ้น, ทำให้[[ข้น]]ขึ้น ===== การผันรูป ===== {{ar-conj|II}} ==== คำนาม ==== {{ar-noun|سُمْك|m}} # [[ความหนา]] ===== การผันรูป ===== {{ar-decl-noun|سُمْك}} === รากศัพท์ 3 === จากราก {{ar-root|س م ك}} ที่หมายถึง “อยู่สูงหรือสูงส่ง”, “ได้รับการค้ำจุนหรือพยุงไว้” ==== คำนาม ==== {{ar-noun|سَمْك|m}} # [[หลังคา]], [[เพดาน]] ===== การผันรูป ===== {{ar-decl-noun|سَمْك}} === อ้างอิง === * {{R:ar:Lane|pages=1430-1431}} * {{R:Strong's|H|5564}} {{reflist}} {{C|ar|ปลา}} qvnt75wqjiksz5e120zmw7u32rwg99d 5775246 5775245 2026-10-14T08:53:22Z Octahedron80 267 /* รากศัพท์ 1 */ 5775246 wikitext text/x-wiki == ภาษาอาหรับ == {{ar-rootbox|س م ك}} === รากศัพท์ 1 === จากราก {{ar-root|س م ك}} ที่หมายถึง “อยู่สูงหรือสูงส่ง”, “ได้รับการค้ำจุนหรือพยุงไว้”, “ค้ำจุน”; จึงนำไปสู่ความหมาย “แหล่งค้ำจุนชีวิต”, “อาหารหลักหรืออาหารยังชีพ”; ร่วมเชื้อสายกับ{{cog|he|סָמַך|tr=samak|t=พิง, วาง, พัก, ค้ำจุน}} ซึ่งปรากฏในคัมภีร์ไบเบิลในนัยที่คล้ายกันคือ “ค้ำจุน” หรือ “หล่อเลี้ยง”<ref>[http://biblehub.com/text/genesis/27-37.htm Genesis 27:37]</ref> ==== การออกเสียง ==== {{ar-pr|سَمَك}} * {{audio|ar|Ar-سمك.ogg}} ==== คำนาม ==== {{ar-coll-noun|سَمَك|pl=أَسْماك,سِمَاك,سُمُوك|sing=+}} # [[ปลา]] #: {{uxa|ar|السَّرْدِينُ عِبَارَةٌ عَنْ سَمَكٍ مُدْهِنٍ صَغِيرٍ يُمْكِنُ أَكْلُهُ دُونَ مَضْغ.|t=ปลาซาร์ดีนคือปลาขนาดเล็กที่มีไขมันสูง ซึ่งสามารถรับประทานได้ทั้งตัว}} ===== การผันรูป ===== {{ar-decl-coll-noun|سَمَك|sing=سَمَكَة|pl=أَسْماك|pl2=سِمَاك|pl3=سُمُوك}} ===== คำสืบทอด ===== * {{desc|afb|سمچ|tr=simič, simač}} * {{desc|acm|سمچ|tr=simač}} * {{desc|gez|ሰመክ|bor=1}} * {{desctree|ota|سمك|tr=semek|bor=1}} * {{desc|sw|samaki|bor=1}} === รากศัพท์ 2 === จากราก {{ar-root|س م ك}} ที่หมายถึง “หล่อเลี้ยง”, “ขุนให้อ้วนท้วนสมบูรณ์”, “ขุนปศุสัตว์ให้อ้วน” ==== คำกริยา ==== {{ar-verb|II}} # ทำให้[[อ้วน]], ทำให้[[หนา]]ขึ้น, ทำให้[[ข้น]]ขึ้น ===== การผันรูป ===== {{ar-conj|II}} ==== คำนาม ==== {{ar-noun|سُمْك|m}} # [[ความหนา]] ===== การผันรูป ===== {{ar-decl-noun|سُمْك}} === รากศัพท์ 3 === จากราก {{ar-root|س م ك}} ที่หมายถึง “อยู่สูงหรือสูงส่ง”, “ได้รับการค้ำจุนหรือพยุงไว้” ==== คำนาม ==== {{ar-noun|سَمْك|m}} # [[หลังคา]], [[เพดาน]] ===== การผันรูป ===== {{ar-decl-noun|سَمْك}} === อ้างอิง === * {{R:ar:Lane|pages=1430-1431}} * {{R:Strong's|H|5564}} {{reflist}} {{C|ar|ปลา}} tigl1r3q373y2994htunm1mixreuz03 5775247 5775246 2026-10-14T08:53:36Z Octahedron80 267 /* รากศัพท์ 1 */ 5775247 wikitext text/x-wiki == ภาษาอาหรับ == {{ar-rootbox|س م ك}} === รากศัพท์ 1 === จากราก {{ar-root|س م ك}} ที่หมายถึง “อยู่สูงหรือสูงส่ง”, “ได้รับการค้ำจุนหรือพยุงไว้”, “ค้ำจุน”; จึงนำไปสู่ความหมาย “แหล่งค้ำจุนชีวิต”, “อาหารหลักหรืออาหารยังชีพ”; ร่วมเชื้อสายกับ{{cog|he|סָמַך|t=พิง, วาง, พัก, ค้ำจุน}} ซึ่งปรากฏในคัมภีร์ไบเบิลในนัยที่คล้ายกันคือ “ค้ำจุน” หรือ “หล่อเลี้ยง”<ref>[http://biblehub.com/text/genesis/27-37.htm Genesis 27:37]</ref> ==== การออกเสียง ==== {{ar-pr|سَمَك}} * {{audio|ar|Ar-سمك.ogg}} ==== คำนาม ==== {{ar-coll-noun|سَمَك|pl=أَسْماك,سِمَاك,سُمُوك|sing=+}} # [[ปลา]] #: {{uxa|ar|السَّرْدِينُ عِبَارَةٌ عَنْ سَمَكٍ مُدْهِنٍ صَغِيرٍ يُمْكِنُ أَكْلُهُ دُونَ مَضْغ.|t=ปลาซาร์ดีนคือปลาขนาดเล็กที่มีไขมันสูง ซึ่งสามารถรับประทานได้ทั้งตัว}} ===== การผันรูป ===== {{ar-decl-coll-noun|سَمَك|sing=سَمَكَة|pl=أَسْماك|pl2=سِمَاك|pl3=سُمُوك}} ===== คำสืบทอด ===== * {{desc|afb|سمچ|tr=simič, simač}} * {{desc|acm|سمچ|tr=simač}} * {{desc|gez|ሰመክ|bor=1}} * {{desctree|ota|سمك|tr=semek|bor=1}} * {{desc|sw|samaki|bor=1}} === รากศัพท์ 2 === จากราก {{ar-root|س م ك}} ที่หมายถึง “หล่อเลี้ยง”, “ขุนให้อ้วนท้วนสมบูรณ์”, “ขุนปศุสัตว์ให้อ้วน” ==== คำกริยา ==== {{ar-verb|II}} # ทำให้[[อ้วน]], ทำให้[[หนา]]ขึ้น, ทำให้[[ข้น]]ขึ้น ===== การผันรูป ===== {{ar-conj|II}} ==== คำนาม ==== {{ar-noun|سُمْك|m}} # [[ความหนา]] ===== การผันรูป ===== {{ar-decl-noun|سُمْك}} === รากศัพท์ 3 === จากราก {{ar-root|س م ك}} ที่หมายถึง “อยู่สูงหรือสูงส่ง”, “ได้รับการค้ำจุนหรือพยุงไว้” ==== คำนาม ==== {{ar-noun|سَمْك|m}} # [[หลังคา]], [[เพดาน]] ===== การผันรูป ===== {{ar-decl-noun|سَمْك}} === อ้างอิง === * {{R:ar:Lane|pages=1430-1431}} * {{R:Strong's|H|5564}} {{reflist}} {{C|ar|ปลา}} lq7h90h5v6lh21r7vmmtg3dk81vvp32 after all 0 17235 5775210 1245089 2026-10-14T05:27:01Z Octahedron80 267 /* ภาษาอังกฤษ */ 5775210 wikitext text/x-wiki == ภาษาอังกฤษ == === การออกเสียง === * {{audio|en|en-au-after all.ogg|a=AU}} * {{audio|en|en-us-after all.ogg|a=Mid-Atlantic US}} === วลีบุพบท === {{en-prepphr}} # {{lb|en|สำนวน}} [[อย่างไรก็ตาม]], [[ถึงอย่างไรก็ดี]]; {{non-gloss|ระบุว่าข้อความเป็นจริงโดยไม่ต้องคำนึงถึงปัจจัยอื่น ใช้เพื่อเน้นย้ำหรืออธิบายเหตุผลของประเด็นนั้น}} #: {{ux|en|'''After all''', they never come home for Christmas.|t='''ถึงอย่างไรพวกเขาก็'''ไม่เคยกลับบ้านช่วงคริสต์มาสอยู่แล้ว}} #: {{ux|en|Of course he won't give you credit. '''After all''', his first and last concern is his company's profit margin.|t=แน่นอนว่าเขาจะไม่ให้เครดิตคุณหรอก '''ก็เพราะว่า'''สิ่งเดียวที่เขาสนใจคือกำไรของบริษัทเท่านั้น}} #* {{RQ:Austen Pride and Prejudice|volume=I|chapter=VI|page=53|passage=“What a charming amusement for young people this is, Mr. Darcy! There is nothing like dancing '''after all'''.—I consider it as one of the first refinements of polished societies.”|t=“ช่างเป็นความเพลิดเพลินที่น่ารื่นรมย์สำหรับคนหนุ่มสาวจริง ๆ นะคะ คุณดาร์ซี! '''ถึงอย่างไรแล้ว''' ก็ไม่มีสิ่งใดเทียบได้กับการเต้นรำจริง ๆ—ดิฉันถือว่ามันเป็นหนึ่งในความประณีตงดงามอันดับแรก ๆ ของสังคมผู้ดีทีเดียว”}} #* {{quote-journal|en|year=1960|month=December|title=Talking of Trains: The riding of B.R. coaches|journal=Trains Illustrated|pages=705–706|text='''After all''', it is undeniable that the B.R. standard coach scored highly in comparative trials with other European railway vehicles on the Continent a few years ago, so that B.R. civil engineers must share responsibility for any defects in its behaviour over here.|t='''ถึงอย่างไรก็ดี''' ปฏิเสธไม่ได้ว่าตู้โดยสารมาตรฐานของ B.R. ได้คะแนนสูงในการทดสอบเปรียบเทียบกับยานพาหนะระบบรางอื่น ๆ ในภาคพื้นทวีปยุโรปเมื่อไม่กี่ปีก่อน ดังนั้นวิศวกรโยธาของ B.R. จึงต้องมีส่วนรับผิดชอบต่อข้อบกพร่องใด ๆ ในสมรรถนะการทำงานที่นี่ด้วย}} #* {{quote-journal|en|date=8 Jan 2020|author=Felicity Cloake|journal=w:The Guardian|url=https://www.theguardian.com/food/2020/jan/08/how-make-perfect-gluten-free-chocolate-brownies-recipe|title=How to make the perfect gluten-free chocolate brownies – recipe|text=I’d prefer to keep things straightforward and stick in the lovely, tasty yolks, too. '''After all''', there’s no such thing as too rich when it comes to brownies.|t=ฉันชอบที่จะทำอะไรให้ตรงไปตรงมาและใส่ไข่แดงแสนอร่อยลงไปด้วย '''เพราะอย่างไรเสีย''' สำหรับบราวนี่แล้ว ไม่มีคำว่าเข้มข้นเลี่ยนเกินไปหรอก}} # {{lb|en|สำนวน}} [[ในที่สุด]], [[เอาเข้าจริง]]; {{non-gloss|ใช้อ้างถึงสิ่งที่เคยเข้าใจหรือคาดไว้แต่กลับไม่เป็นเช่นนั้น หรือผลลัพธ์ที่ไม่เป็นไปตามที่คาดหมายไว้}} #: {{ux|en|They won't be coming home for Christmas '''after all'''.|t='''เอาเข้าจริงแล้ว'''พวกเขาก็คงไม่ได้กลับบ้านช่วงคริสต์มาส}} #* {{RQ:S. Butler Way of All Flesh|chapter=XL|page=175|passage=Then the idea returned to her that, '''after all''', her son might not be innocent in the Ellen matter—and this was so interesting that she felt bound to get as near the truth as she could.|t=จากนั้นความคิดก็ย้อนกลับมาหาเธอว่า '''เอาเข้าจริงแล้ว''' ลูกชายของเธออาจจะไม่ได้บริสุทธิ์ในเรื่องของเอลเลนก็ได้—และสิ่งนี้น่าสนใจเสียจนเธอรู้สึกว่าจะต้องเข้าใกล้ความจริงให้ได้มากที่สุดเท่าที่จะทำได้}} #* {{quote-journal|en|journal=w:National Geographic|year=1989|month=May|volume=175|number=5|title=The Baltic: Arena of Power|author=Priit J. Vesilind|page=604|text=From Moscow has come the admission that, yes, '''after all''', the Soviet Union occupied these nations by force in 1940, against the will of the people.|t=มีการยอมรับจากทางมอสโกว่า ใช่แล้ว '''เอาเข้าจริงแล้ว''' สหภาพโซเวียตได้เข้ายึดครองประเทศเหล่านี้ด้วยกำลังทหารในปี 1940 โดยขัดต่อความประสงค์ของประชาชน}} # {{non-gloss|ใช้นำหน้าข้อมูลที่สนับสนุนข้อความข้างต้น:}} [[ก็...นี่นา]], [[ก็เพราะว่า]] #: {{ux|en|Of course he's coming to the party. '''After all''', he did promise.|t=แน่นอนว่าเขาจะมาร่วมงานเลี้ยง '''ก็'''เขาสัญญาไว้แล้ว'''นี่นา'''}} === สลับอักษร === * {{anagrams|en|a=aaefllrt|fallrate}} {{cln|en|hedges}} dyjy0gcft483p5fjmdwte8rank092a1 มอดูล:headword 828 36043 5775206 5721437 2026-10-14T05:19:35Z Octahedron80 267 5775206 Scribunto text/plain local export = {} -- Named constants for all modules used, to make it easier to swap out sandbox versions. local debug_track_module = "Module:debug/track" local decorations_module = "Module:decorations" local en_utilities_module = "Module:en-utilities" local th_utilities_module = "Module:th-utilities" --th local gender_and_number_module = "Module:gender and number" local headword_data_module = "Module:headword/data" local headword_page_module = "Module:headword/page" local links_module = "Module:links" local load_module = "Module:load" local pages_module = "Module:pages" local palindromes_module = "Module:palindromes" local scripts_module = "Module:scripts" local scripts_data_module = "Module:scripts/data" local script_utilities_module = "Module:script utilities" local script_utilities_data_module = "Module:script utilities/data" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local utilities_module = "Module:utilities" local concat = table.concat local dump = mw.dumpObject local insert = table.insert local ipairs = ipairs local max = math.max local new_title = mw.title.new local pairs = pairs local require = require local toNFC = mw.ustring.toNFC local toNFD = mw.ustring.toNFD local type = type local ufind = mw.ustring.find local ugmatch = mw.ustring.gmatch local ugsub = mw.ustring.gsub local umatch = mw.ustring.match --[==[ Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==] local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end local function contains(...) contains = require(table_module).contains return contains(...) end local function encode_entities(...) encode_entities = require(string_utilities_module).encode_entities return encode_entities(...) end local function extend(...) extend = require(table_module).extend return extend(...) end local function find_best_script_without_lang(...) find_best_script_without_lang = require(scripts_module).findBestScriptWithoutLang return find_best_script_without_lang(...) end local function format_categories(...) format_categories = require(utilities_module).format_categories return format_categories(...) end local function format_genders(...) format_genders = require(gender_and_number_module).format_genders return format_genders(...) end local function format_decorations(...) format_decorations = require(decorations_module).format_decorations return format_decorations(...) end local function full_link(...) full_link = require(links_module).full_link return full_link(...) end local function get_current_L2(...) get_current_L2 = require(pages_module).get_current_L2 return get_current_L2(...) end local function get_link_page(...) get_link_page = require(links_module).get_link_page return get_link_page(...) end local function get_script(...) get_script = require(scripts_module).getByCode return get_script(...) end local function is_palindrome(...) is_palindrome = require(palindromes_module).is_palindrome return is_palindrome(...) end local function language_link(...) language_link = require(links_module).language_link return language_link(...) end local function load_data(...) load_data = require(load_module).load_data return load_data(...) end local function pattern_escape(...) pattern_escape = require(string_utilities_module).pattern_escape return pattern_escape(...) end local function pluralize(...) pluralize = require(en_utilities_module).pluralize return pluralize(...) end local function process_page(...) process_page = require(headword_page_module).process_page return process_page(...) end local function remove_links(...) remove_links = require(links_module).remove_links return remove_links(...) end local function shallow_copy(...) shallow_copy = require(table_module).shallowCopy return shallow_copy(...) end local function tag_text(...) tag_text = require(script_utilities_module).tag_text return tag_text(...) end local function tag_transcription(...) tag_transcription = require(script_utilities_module).tag_transcription return tag_transcription(...) end local function tag_translit(...) tag_translit = require(script_utilities_module).tag_translit return tag_translit(...) end local function trim(...) trim = require(string_utilities_module).trim return trim(...) end local function ulen(...) ulen = require(string_utilities_module).len return ulen(...) end --[==[ Loaders for objects, which load data (or some other object) into some variable, which can then be accessed as "foo or get_foo()", where the function get_foo sets the object to "foo" and then returns it. This ensures they are only loaded when needed, and avoids the need to check for the existence of the object each time, since once "foo" has been set, "get_foo" will not be called again.]==] local m_data local function get_data() m_data = load_data(headword_data_module) return m_data end local script_data local function get_script_data() script_data = load_data(scripts_data_module) return script_data end local script_utilities_data local function get_script_utilities_data() script_utilities_data = load_data(script_utilities_data_module) return script_utilities_data end -- If set to true, categories always appear, even in non-mainspace pages local test_force_categories = false -- Add a tracking category to track entries with certain (unusually undesirable) properties. `track_id` is an identifier -- for the particular property being tracked and goes into the tracking page. Specifically, this adds a link in the -- page text to [[Wiktionary:Tracking/headword/TRACK_ID]], meaning you can find all entries with the `track_id` property -- by visiting [[Special:WhatLinksHere/Wiktionary:Tracking/headword/TRACK_ID]]. -- -- If `lang` (a language object) is given, an additional tracking page [[Wiktionary:Tracking/headword/TRACK_ID/CODE]] is -- linked to where CODE is the language code of `lang`, and you can find all entries in the combination of `track_id` -- and `lang` by visiting [[Special:WhatLinksHere/Wiktionary:Tracking/headword/TRACK_ID/CODE]]. This makes it possible to -- isolate only the entries with a specific tracking property that are in a given language. Note that if `lang` -- references at etymology-only language, both that language's code and its full parent's code are tracked. local function track(track_id, lang) local tracking_page = "headword/" .. track_id if lang and lang:hasType("etymology-only") then debug_track{tracking_page, tracking_page .. "/" .. lang:getCode(), tracking_page .. "/" .. lang:getFullCode()} elseif lang then debug_track{tracking_page, tracking_page .. "/" .. lang:getCode()} else debug_track(tracking_page) end return true end local function text_in_script(text, script_code) local sc = get_script(script_code) if not sc then error("Internal error: Bad script code " .. script_code) end local characters = sc.characters local out if characters then text = ugsub(text, "%W", "") out = ufind(text, "[" .. characters .. "]") end if out then return true else return false end end local spacingPunctuation = "[%s%p]+" --[[ List of punctuation or spacing characters that are found inside of words. Used to exclude characters from the regex above. ]] local wordPunc = "-#%%&@־׳״'.·*’་•:᠊" local notWordPunc = "[^" .. wordPunc .. "]+" --[=[ Format a term (either a head term or an inflection term) along with any decorations (left or right qualifiers, labels, references or customized separator). `part` is the object specifying the term (and `lang` the language of the term), which should optionally contain: * left qualifiers in `q`, an array of strings; * right qualifiers in `qq`, an array of strings; * left labels in `l`, an array of strings; * right labels in `ll`, an array of strings; * references in `refs`, an array either of strings (formatted reference text) or objects containing fields `text` (formatted reference text) and optionally `name` and/or `group`; * a separator in `separator`, defaulting to " <i>or</i> " if this is not the first term (j > 1), otherwise "". `formatted` is the formatted version of the term itself, and `j` is the index of the term. ]=] local function format_term_with_decorations(lang, part, formatted, j) local function part_non_empty(field) local list = part[field] if not list then return nil end if type(list) ~= "table" then error(("Internal error: Wrong type for `part.%s`=%s, should be \"table\""):format(field, dump(list))) end return list[1] end if part_non_empty("q") or part_non_empty("qq") or part_non_empty("l") or part_non_empty("ll") or part_non_empty("refs") then formatted = format_decorations { lang = lang, text = formatted, q = part.q, qq = part.qq, l = part.l, ll = part.ll, refs = part.refs, } end local separator = part.separator or j > 1 and " <i>หรือ</i> " -- use "" to request no separator if separator then formatted = separator .. formatted end return formatted end --[==[Return true if the given head is multiword according to the algorithm used in full_headword().]==] function export.head_is_multiword(head) for possibleWordBreak in ugmatch(head, spacingPunctuation) do if umatch(possibleWordBreak, notWordPunc) then return true end end return false end do local function workaround_to_exclude_chars(s) return (ugsub(s, notWordPunc, "\2%1\1")) end --[==[ Add appropriate links to `head`, correctly handling multiword terms. This is intended for multiword terms but can be used for any term if you want links added to single-word terms as well. If you want to only add links to multiword terms, first check that the term is multiword using `head_is_multiword`. If `default` is specified, this will escape colons so that they don't get interpreted as interwiki links. This should generally only be used when `head` is an actual pagename or is taken from a {{para|pagename}} parameter, not when taken from a {{para|head}} parameter. ]==] function export.add_multiword_links(head, default) head = "\1" .. ugsub(head, spacingPunctuation, workaround_to_exclude_chars) .. "\2" if default then head = head :gsub("(\1[^\2]*)\\([:#][^\2]*\2)", "%1\\\\%2") :gsub("(\1[^\2]*)([:#][^\2]*\2)", "%1\\%2") end --Escape any remaining square brackets to stop them breaking links (e.g. "[citation needed]"). head = encode_entities(head, "[]", true, true) --[=[ use this when workaround is no longer needed: head = "[[" .. ugsub(head, WORDBREAKCHARS, "]]%1[[") .. "]]" Remove any empty links, which could have been created above at the beginning or end of the string. ]=] return (head :gsub("\1\2", "") :gsub("[\1\2]", {["\1"] = "[[", ["\2"] = "]]"})) end end local function non_categorizable(full_raw_pagename) return full_raw_pagename:find("^ภาคผนวก:อากัปกิริยา/") or -- Unsupported titles with descriptive names. (full_raw_pagename:find("^ชื่อไม่รองรับ/") and not full_raw_pagename:find("`")) end local function tag_text_and_add_decorations(data, head, formatted, j) -- Add language and script wrapper. formatted = tag_text(formatted, data.lang, head.sc, "head", nil, j == 1 and data.id or nil) -- Add decorations (qualifiers, labels, references and separator). return format_term_with_decorations(data.lang, head, formatted, j) end -- Format a headword with transliterations. local function format_headword(data) -- Are there non-empty transliterations? local has_translits = false local has_manual_translits = false ------ Format the headwords. ------ local head_parts = {} local unique_head_parts = {} local has_multiple_heads = not not data.heads[2] for j, head in ipairs(data.heads) do if head.tr or head.ts then has_translits = true end if head.tr and head.tr_manual or head.ts then has_manual_translits = true end local formatted -- Apply processing to the headword, for formatting links and such. if head.term:find("[[", nil, true) and head.sc:getCode() ~= "Image" then formatted = language_link{term = head.term, lang = data.lang} else formatted = data.lang:makeDisplayText(head.term, head.sc, true) end local head_part = tag_text_and_add_decorations(data, head, formatted, j) insert(head_parts, head_part) -- If multiple heads, try to determine whether all heads display the same. To do this we need to effectively -- rerun the text tagging and addition of decorations, using 1 for all indices. if has_multiple_heads then local unique_head_part if j == 1 then unique_head_part = head_part else unique_head_part = tag_text_and_add_decorations(data, head, formatted, 1) end unique_head_parts[unique_head_part] = true end end local set_size = 0 if has_multiple_heads then for _ in pairs(unique_head_parts) do set_size = set_size + 1 end end if set_size == 1 then head_parts = head_parts[1] else head_parts = concat(head_parts) end if has_manual_translits then -- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/manual-tr]] -- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/manual-tr/LANGCODE]] track("manual-tr", data.lang) end ------ Format the transliterations and transcriptions. ------ local translits_formatted if has_translits then local translit_parts = {} for _, head in ipairs(data.heads) do if head.tr or head.ts then local this_parts = {} if head.tr then insert(this_parts, tag_translit(head.tr, data.lang:getCode(), "head", nil, head.tr_manual)) if head.ts then insert(this_parts, " ") end end if head.ts then insert(this_parts, "/" .. tag_transcription(head.ts, data.lang:getCode(), "head") .. "/") end insert(translit_parts, concat(this_parts)) end end translits_formatted = " (" .. concat(translit_parts, " <i>หรือ</i> ") .. ")" local function format_transliteration_page_link(langname) local transliteration_pagename = "การถอดอักษรภาษา" .. langname local transliteration_page = new_title(transliteration_pagename, "วิกิพจนานุกรม") if transliteration_page and transliteration_page:getContent() then return ("[[วิกิพจนานุกรม:%s|•]]"):format(transliteration_pagename) end return nil end local translit_page_link = format_transliteration_page_link(data.lang:getCanonicalName()) -- If data.lang is an etymology-only language and we didn't find a translation page for it, fall back to the -- full parent. if not translit_page_link and data.lang:hasType("etymology-only") then translit_page_link = format_transliteration_page_link(data.lang:getFullName()) end if translit_page_link then translits_formatted = " " .. translit_page_link .. translits_formatted end else translits_formatted = "" end ------ Paste heads and transliterations/transcriptions. ------ local lemma_gloss if data.gloss then lemma_gloss = ' <span class="ib-content qualifier-content">' .. data.gloss .. '</span>' else lemma_gloss = "" end return head_parts .. translits_formatted .. lemma_gloss end local function format_headword_genders(data, is_varform_only) local retval = "" if data.genders and data.genders[1] then if data.gloss then retval = "," end local pos_for_cat if not data.nogendercat and not is_varform_only then local no_gender_cat = (m_data or get_data()).no_gender_cat if not (no_gender_cat[data.lang:getCode()] or no_gender_cat[data.lang:getFullCode()]) then pos_for_cat = (m_data or get_data()).pos_for_gender_number_cat[data.pos_category:gsub("^reconstructed ", "")] end end local text, cats = format_genders(data.genders, data.lang, pos_for_cat) if cats then extend(data.categories, cats) end retval = retval .. "&nbsp;" .. text end return retval end -- Forward reference local format_inflections local function format_inflection_parts(data, parts) for j, part in ipairs(parts) do if type(part) ~= "table" then part = {term = part} end local partaccel = part.accel local face = part.face or "bold" if face ~= "bold" and face ~= "plain" and face ~= "hypothetical" then error("The face `" .. face .. "` " .. ( (script_utilities_data or get_script_utilities_data()).faces[face] and "should not be used for non-headword terms on the headword line." or "is invalid." )) end -- Here the final part 'or data.nolinkinfl' allows to have 'nolinkinfl=true' -- right into the 'data' table to disable inflection links of the entire headword -- when inflected forms aren't entry-worthy, e.g.: in Vulgar Latin local nolinkinfl = part.face == "hypothetical" or (part.nolink and track("nolink") or part.nolinkinfl) or ( data.nolink and track("nolink") or data.nolinkinfl) local formatted if part.label then -- FIXME: There should be a better way of italicizing a label. As is, this isn't customizable. formatted = "<i>" .. part.label .. "</i>" else -- Convert the term into a full link. Don't show a transliteration here unless enable_auto_translit is -- requested, either at the `parts` level (i.e. per inflection) or at the `data.inflections` level (i.e. -- specified for all inflections). This is controllable in {{head}} using autotrinfl=1 for all inflections, -- or fNautotr=1 for an individual inflection (remember that a single inflection may be associated with -- multiple terms). The reason for doing this is to avoid clutter in headword lines by default in languages -- where the script is relatively straightforward to read by learners (e.g. Greek, Russian), but allow it -- to be enabled in languages with more complex scripts (e.g. Arabic). -- -- FIXME: With nested inflections, should we also respect `enable_auto_translit` at the top level of the -- nested inflections structure? local tr = part.tr or not (parts.enable_auto_translit or data.inflections.enable_auto_translit) and "-" or nil local postprocess_annotations if part.inflections then postprocess_annotations = function(infldata) insert(infldata.annotations, {text = format_inflections(data, part.inflections)}) end end formatted = full_link( { term = not nolinkinfl and part.term or nil, alt = part.alt or (nolinkinfl and part.term or nil), lang = part.lang or data.lang, sc = part.sc or parts.sc or nil, gloss = part.gloss, pos = part.pos, ng = part.ng, lit = part.lit, id = part.id, genders = part.genders, tr = tr, ts = part.ts, accel = partaccel or parts.accel, postprocess_annotations = postprocess_annotations, }, face ) end parts[j] = format_term_with_decorations(part.lang or data.lang, part, formatted, j) end local parts_output if parts[1] then parts_output = (parts.label and " " or "") .. concat(parts) elseif parts.request then parts_output = " <small>[please provide]</small>" insert(data.categories, "Requests for inflections in " .. data.lang:getFullName() .. " entries") else parts_output = "" end local parts_label = parts.label and ("<i>" .. parts.label .. "</i>") or "" return format_term_with_decorations(data.lang, parts, parts_label .. parts_output, 1) end -- Format the inflections following the headword or nested after a given inflection. Declared local above. function format_inflections(data, inflections) if inflections and inflections[1] then -- Format each inflection individually. for key, infl in ipairs(inflections) do inflections[key] = format_inflection_parts(data, infl) end return concat(inflections, ", ") else return "" end end -- Format the top-level inflections following the headword. Currently this just adds parens around the -- formatted comma-separated inflections in `data.inflections`. local function format_top_level_inflections(data) local result = format_inflections(data, data.inflections) if result ~= "" then return " (" .. result .. ")" else return result end end -- Forward reference local check_red_link_inflections -- Check a single inflection (which consists of a label and zero or more terms, each possibly with nested inflections) -- for red links. If so, insert a red-link category based on `plpos` (the plural part of speech to insert in the -- category), stop further processing, and return true. If no red links found, return false. local function check_red_link_inflection_parts(data, parts, plpos) for _, part in ipairs(parts) do if type(part) ~= "table" then part = {term = part} end local term = part.term if term and not term:find("%[%[") then local stripped_physical_term = get_link_page(term, data.lang, part.sc or parts.sc or nil) if stripped_physical_term then local title = mw.title.new(stripped_physical_term) if title and not title:getContent() then insert(data.categories, data.lang:getFullName() .. " " .. plpos .. " with red links in their headword lines") return true end end end if part.inflections then if check_red_link_inflections(data, part.inflections, plpos) then return true end end end return false end -- Check a set of inflections (each of which describes a single inflection of the term, such as feminine or plural, and -- consists of a label and zero or more terms, each possibly with nested inflections) for red links. If so, insert a -- red-link category based on `plpos` (the plural part of speech to insert in the category), stop further processing, -- and return true. If no red links found, return false. function check_red_link_inflections(data, inflections, plpos) if inflections and inflections[1] then -- Check each inflection individually. for key, infl in ipairs(inflections) do if check_red_link_inflection_parts(data, infl, plpos) then return true end end end return false end -- Check the top-level inflections in `data.inflections`, along with any nested inflections, for red links. If so, -- insert a red-link category based on `plpos` (the plural part of speech to insert in the category), stop further -- processing, and return true. If no red links found, return false. local function check_red_link_inflections_top_level(data, plpos) return check_red_link_inflections(data, data.inflections, plpos) end --[==[ Returns the plural form of `pos`, a raw part of speech input, which could be singular or plural. Irregular plural POS are taken into account (e.g. "kanji" pluralizes to "kanji"). ]==] function export.pluralize_pos(pos) -- Make the plural form of the part of speech --[[ return (m_data or get_data()).irregular_plurals[pos] or pos:sub(-1) == "s" and pos or pluralize(pos) --]] return pos --th end --[==[ Return "lemma" if the given POS is a lemma, "non-lemma form" if a non-lemma form, or nil if unknown. The POS passed in must be in its plural form ("nouns", "prefixes", etc.). If you have a POS in its singular form, call {export.pluralize_pos()} above to pluralize it in a smart fashion that knows when to add "-s" and when to add "-es", and also takes into account any irregular plurals. If `best_guess` is given and the POS is in neither the lemma nor non-lemma list, guess based on whether it ends in " forms"; otherwise, return nil. ]==] function export.pos_lemma_or_nonlemma(plpos, best_guess) local m_headword_data = m_data or get_data() local isLemma = m_headword_data.lemmas -- Is it a lemma category? if isLemma[plpos] then return "คำหลัก" end local plpos_no_recon = plpos:gsub("สืบสร้าง", "") if isLemma[plpos_no_recon] then return "คำหลัก" end -- Is it a nonlemma category? local isNonLemma = m_headword_data.nonlemmas if isNonLemma[plpos] or isNonLemma[plpos_no_recon] then return "รูปผัน" end local plpos_no_mut = plpos:gsub("^mutated ", "") if isLemma[plpos_no_mut] or isNonLemma[plpos_no_mut] then return "รูปผัน" elseif best_guess then return plpos:find("^รูป") and "รูปผัน" or "คำหลัก" else return nil end end --[==[ Canonicalize a part of speech as specified in 2= in {{tl|head}}. This checks for POS aliases and non-lemma form aliases ending in 'f', and then pluralizes if the POS term does not have an invariable plural. ]==] function export.canonicalize_pos(pos) -- FIXME: Temporary code to throw an error for alias 'pre' (= preposition) that will go away. if pos == "pre" then -- Don't throw error on 'pref' as it's an alias for "prefix". error("POS 'pre' for 'preposition' no longer allowed as it's too ambiguous; use 'prep'") end -- Likewise for pro = pronoun. if pos == "pro" or pos == "prof" then error("POS 'pro' for 'pronoun' no longer allowed as it's too ambiguous; use 'pron'") end local m_headword_data = m_data or get_data() if m_headword_data.pos_aliases[pos] then pos = m_headword_data.pos_aliases[pos] elseif pos:sub(-1) == "f" then pos = pos:sub(1, -2) --pos = (m_headword_data.pos_aliases[pos] or pos) .. " forms" --th; making pos of non-lemma form is more complex pos = m_headword_data.pos_aliases[pos] or pos pos = require(th_utilities_module).th_pos_to_form(pos) end return export.pluralize_pos(pos) end -- Find and return the maximum index in the array `data[element]` (which may have gaps in it), and initialize it to a -- zero-length array if unspecified. Check to make sure all keys are numeric (other than "maxindex", which is set by -- [[Module:parameters]] for list parameters), all values are strings, and unless `allow_blank_string` is given, -- no blank (zero-length) strings are present. local function init_and_find_maximum_index(data, element, allow_blank_string) local maxind = 0 if not data[element] then data[element] = {} end local typ = type(data[element]) if typ ~= "table" then error(("Internal error: In full_headword(), `data.%s` must be an array but is a %s"):format(element, typ)) end for k, v in pairs(data[element]) do if k ~= "maxindex" then if type(k) ~= "number" then error(("Internal error: Unrecognized non-numeric key '%s' in `data.%s`"):format(k, element)) end if k > maxind then maxind = k end if v then if type(v) ~= "string" then error(("Internal error: For key '%s' in `data.%s`, value should be a string but is a %s"):format(k, element, type(v))) end if not allow_blank_string and v == "" then error(("Internal error: For key '%s' in `data.%s`, blank string not allowed; use 'false' for the default"):format(k, element)) end end end end return maxind end --[==[ -- Add the page to various maintenance categories for the language and the -- whole page. These are placed in the headword somewhat arbitrarily, but -- mainly because headword templates are mandatory for entries (meaning that -- in theory it provides full coverage). -- -- This is provided as an external entry point so that modules which transclude -- information from other entries (such as {{tl|ja-see}}) can take advantage -- of this feature as well, because they are used in place of a conventional -- headword template.]==] do -- Handle any manual sortkeys that have been specified in raw categories -- by tracking if they are the same or different from the automatically- -- generated sortkey, so that we can track them in maintenance -- categories. local function handle_raw_sortkeys(tbl, sortkey, page, lang, lang_cats) sortkey = sortkey or lang:makeSortKey(page.pagename) -- If there are raw categories with no sortkey, then they will be -- sorted based on the default MediaWiki sortkey, so we check against -- that. if tbl == true then if page.raw_defaultsort ~= sortkey then insert(lang_cats, lang:getFullName() .. " terms with non-redundant non-automated sortkeys") end return end local redundant, different for k in pairs(tbl) do if k == sortkey then redundant = true else different = true end end if redundant then insert(lang_cats, lang:getFullName() .. " terms with redundant sortkeys") end if different then insert(lang_cats, lang:getFullName() .. " terms with non-redundant non-automated sortkeys") end return sortkey end function export.maintenance_cats(page, lang, lang_cats, page_cats) extend(page_cats, page.cats) lang = lang:getFull() -- since we are just generating categories local canonical = lang:getCanonicalName() local tbl = page.wikitext_topic_cat[lang:getCode()] local sortkey = nil if tbl then sortkey = handle_raw_sortkeys(tbl, sortkey, page, lang, lang_cats) insert(lang_cats, canonical .. " entries with topic categories using raw markup") end tbl = page.wikitext_langname_cat[canonical] if tbl then handle_raw_sortkeys(tbl, sortkey, page, lang, lang_cats) insert(lang_cats, canonical .. " entries with language name categories using raw markup") end if get_current_L2() ~= "ภาษา" .. canonical then --insert(lang_cats, "รายการภาษา" .. canonical .. "ที่มีหัวเรื่องภาษาผิด") -- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/incorrect language header]] -- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/incorrect language header/LANGCODE]] track("incorrect language header", lang) end end end --[==[This is the primary external entry point. {{lua|full_headword(data)}} This is used by {{temp|head}} and various language-specific headword templates (e.g. {{temp|ru-adj}} for Russian adjectives, {{temp|de-noun}} for German nouns, etc.) to display an entire headword line. See [[#Further explanations for full_headword()]] ]==] function export.full_headword(data) -- Prevent data from being destructively modified. data = shallow_copy(data) ------------ 1. Basic checks for old-style (multi-arg) calling convention. ------------ if data.getCanonicalName then error("Internal error: In full_headword(), the first argument `data` needs to be a Lua object (table) of properties, not a language object") end if not data.lang or type(data.lang) ~= "table" or not data.lang.getCode then error("Internal error: In full_headword(), the first argument `data` needs to be a Lua object (table) and `data.lang` must be a language object") end if data.id and type(data.id) ~= "string" then error("Internal error: The id in the data table should be a string.") end ------------ 2. Initialize pagename etc. ------------ local langcode = data.lang:getCode() local full_langcode = data.lang:getFullCode() local langname = data.lang:getCanonicalName() local full_langname = "ภาษา" .. data.lang:getFullName() local raw_pagename = data.pagename local page local m_headword_data = m_data or get_data() if raw_pagename and raw_pagename ~= m_headword_data.pagename then -- for testing, doc pages, etc. -- data.pagename is often set on documentation and test pages through the pagename= parameter of various -- templates, to emulate running on that page. Having a large number of such test templates on a single -- page often leads to timeouts, because we fetch and parse the contents of each page in turn. However, -- we don't really need to do that and can function fine without fetching and parsing the contents of a -- given page, so turn off content fetching/parsing (and also setting the DEFAULTSORT key through a parser -- function, which is *slooooow*) in certain namespaces where test and documentation templates are likely to -- be found and where actual content does not live (User, Template, Module). local actual_namespace = m_headword_data.page.namespace local no_fetch_content = actual_namespace == "User" or actual_namespace == "Template" or actual_namespace == "Module" page = process_page(raw_pagename, no_fetch_content) else page = m_headword_data.page end local namespace = page.namespace if data.altform then -- Temporary tracking for use of old altform= track("altform", data.lang) end local noposcat = data.noposcat local is_varform_only = data.var and data.var ~= "both" local is_varform_both = data.var == "both" ------------ 3. Initialize `data.heads` table; if old-style, convert to new-style. ------------ if type(data.heads) == "table" and type(data.heads[1]) == "table" then -- new-style if data.translits or data.transcriptions then error("Internal error: In full_headword(), if `data.heads` is new-style (array of head objects), `data.translits` and `data.transcriptions` cannot be given") end else -- convert old-style `heads`, `translits` and `transcriptions` to new-style local maxind = max( init_and_find_maximum_index(data, "heads"), init_and_find_maximum_index(data, "translits", true), init_and_find_maximum_index(data, "transcriptions", true) ) for i = 1, maxind do data.heads[i] = { term = data.heads[i], tr = data.translits[i], ts = data.transcriptions[i], } end end -- Make sure there's at least one head. if not data.heads[1] then data.heads[1] = {} end ------------ 4. Initialize and validate `data.categories` and `data.whole_page_categories`, and determine `pos_category` if not given, and add basic categories. ------------ init_and_find_maximum_index(data, "categories") init_and_find_maximum_index(data, "whole_page_categories") local pos_category_already_present = false if data.categories[1] then local escaped_langname = pattern_escape(full_langname) local matches_lang_pattern = "ภาษา" .. escaped_langname .. "$" for _, cat in ipairs(data.categories) do -- Does the category begin with the language name? If not, tag it with a tracking category. if not cat:find(matches_lang_pattern) then -- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/no lang category]] -- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/no lang category/LANGCODE]] track("no lang category", data.lang) end end -- If `pos_category` not given, try to infer it from the first specified category. If this doesn't work, we -- throw an error below. if not data.pos_category and data.categories[1]:find(matches_lang_pattern) then data.pos_category = data.categories[1]:gsub(matches_lang_pattern, "") -- Optimization to avoid inserting category already present. pos_category_already_present = true end end if not data.pos_category then error("Internal error: `data.pos_category` not specified and could not be inferred from the categories given in " .. "`data.categories`. Either specify the plural part of speech in `data.pos_category` " .. "(e.g. \"proper nouns\") or ensure that the first category in `data.categories` is formed from the " .. "language's canonical name plus the plural part of speech (e.g. \"Norwegian Bokmål proper nouns\")." ) end -- Insert a category at the beginning for the part of speech unless it's already present or `data.noposcat` given. if not (pos_category_already_present or noposcat or is_varform_only) then local pos_category = data.pos_category .. full_langname --th -- FIXME: [[User:Theknightwho]] Why is this special case here? Please add an explanatory comment. if pos_category ~= "Translingual Han characters" then insert(data.categories, 1, pos_category) end end -- Try to determine whether the part of speech refers to a lemma or a non-lemma form; if we can figure this out, -- add an appropriate category. local postype = export.pos_lemma_or_nonlemma(data.pos_category) if not postype then -- We don't know what this category is, so tag it with a tracking category. -- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos]] -- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos/LANGCODE]] track("unrecognized pos", data.lang) -- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos/POS]] -- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos/POS/LANGCODE]] track("unrecognized pos/pos/" .. data.pos_category, data.lang) elseif not (noposcat or is_varform_only or data.nolemmacat) then insert(data.categories, 1, postype .. full_langname) --th end -- Categorize variant forms into 'variant lemmas' or 'variant non-lemma forms'. Originally proposed in -- [[Wiktionary:Beer parlour/2024/June#Decluttering the altform mess]] as 'alternative forms'; renamed in -- [[Wiktionary:Beer parlour/2026/July#Renaming the "alternative forms" categories]]. if (is_varform_only or is_varform_both) and postype then insert(data.categories, 1, "รูปแปร" .. full_langname) --th end ------------ 5. Create a default headword, and add links to multiword page names. ------------ -- Determine if this is an "anti-asterisk" term, i.e. an attested term in a language that must normally be -- reconstructed. local is_anti_asterisk = data.heads[1].term and data.heads[1].term:find("^!!") local lang_reconstructed = data.lang:hasType("reconstructed") if is_anti_asterisk then if not lang_reconstructed then error("Anti-asterisk feature (head= beginning with !!) can only be used with reconstructed languages") end if namespace == "การสืบสร้าง" then error("Anti-asterisk feature (head= beginning with !!) should be used in mainspace, not in Reconstruction space") end lang_reconstructed = false end -- Determine if term is reconstructed local is_reconstructed = namespace == "การสืบสร้าง" or lang_reconstructed -- Create a default headword based on the pagename, which is determined in -- advance by the data module so that it only needs to be done once. local default_head = page.pagename -- Add links to multi-word page names when appropriate if not (is_reconstructed or data.nolinkhead) then local no_links = m_headword_data.no_multiword_links if not (no_links[langcode] or no_links[full_langcode]) and export.head_is_multiword(default_head) then default_head = export.add_multiword_links(default_head, true) end end if is_reconstructed then default_head = "*" .. default_head end ------------ 6. Check the namespace against the language type. ------------ if namespace == "" then if lang_reconstructed then error("Entries in " .. langname .. " must be placed in the Reconstruction: namespace") elseif data.lang:hasType("appendix-constructed") then error("Entries in " .. langname .. " must be placed in the Appendix: namespace") end elseif namespace == "Citations" or namespace == "อรรถาภิธาน" then error("Headword templates should not be used in the " .. namespace .. ": namespace.") end ------------ 7. Fill in missing values in `data.heads`. ------------ -- True if any script among the headword scripts has spaces in it. local any_script_has_spaces = false -- True if any term has a redundant head= param. local has_redundant_head_param = false for _, head in ipairs(data.heads) do ------ 7a. If missing head, replace with default head. if not head.term then head.term = default_head elseif head.term == default_head then has_redundant_head_param = true elseif is_anti_asterisk and head.term == "!!" then -- If explicit head=!! is given, it's an anti-asterisk term and we fill in the default head. head.term = "!!" .. default_head elseif head.term:find("^[!?]$") then -- If explicit head= just consists of ! or ?, add it to the end of the default head. head.term = default_head .. head.term end head.term_no_initial_bang_bang = is_anti_asterisk and head.term:sub(3) or head.term if is_reconstructed then local head_term = head.term if head_term:find("%[%[") then head_term = remove_links(head_term) end if head_term:sub(1, 1) ~= "*" then error("The headword '" .. head_term .. "' must begin with '*' to indicate that it is reconstructed.") end end ------ 7b. Try to detect the script(s) if not provided. If a per-head script is provided, that takes precedence, ------ otherwise fall back to the overall script if given. If neither given, autodetect the script. local auto_sc = data.lang:findBestScript(head.term) if ( auto_sc:getCode() == "None" and find_best_script_without_lang(head.term):getCode() ~= "None" ) then insert(data.categories, "ศัพท์" .. full_langname .. "ที่ใช้อักษรไม่มาตรฐาน") end if not (head.sc or data.sc) then -- No script code given, so use autodetected script. head.sc = auto_sc else if not head.sc then -- Overall script code given. head.sc = data.sc end -- Track uses of sc parameter. if head.sc:getCode() == auto_sc:getCode() then track("redundant script code", data.lang) if not data.no_script_code_cat then insert(data.categories, "ศัพท์" .. full_langname .. "ที่มีรหัสอักษรซ้ำซ้อน") end else track("non-redundant manual script code", data.lang) if not data.no_script_code_cat then insert(data.categories, "ศัพท์" .. full_langname .. "ที่มีรหัสอักษรกำหนดเองและไม่ซ้ำซ้อน") end end end -- If using a discouraged character sequence, add to maintenance category. if head.sc:hasNormalizationFixes() == true then local composed_head = toNFC(head.term) if head.sc:fixDiscouragedSequences(composed_head) ~= composed_head then insert(data.whole_page_categories, "Pages using discouraged character sequences") end end any_script_has_spaces = any_script_has_spaces or head.sc:hasSpaces() ------ 7c. Create automatic transliterations for any non-Latin headwords without manual translit given ------ (provided automatic translit is available, e.g. not in Persian or Hebrew). -- Make transliterations head.tr_manual = nil -- Try to generate a transliteration if necessary if head.tr == "-" then head.tr = nil elseif langcode == "eo" then --เพิ่มเอง head.tr = require("Module:eo-translit").tr(remove_links(head.term), langcode, auto_sc:getCode()) elseif langcode == "es" then --เพิ่มเอง head.tr = require("Module:es-translit").tr(remove_links(head.term), langcode, auto_sc:getCode()) elseif langcode == "la" or langcode:find("^la%-") or langcode:find("^[CELMNRV]L%.$") then --เพิ่มเอง head.tr = require("Module:la-translit").tr(remove_links(head.term), langcode, auto_sc:getCode()) elseif langcode == "vi" then --เพิ่มเอง head.tr = require("Module:vi-translit").tr(remove_links(head.term), langcode, auto_sc:getCode()) else local notranslit = m_headword_data.notranslit if not (notranslit[langcode] or notranslit[full_langcode]) and head.sc:isTransliterated() then head.tr_manual = not not head.tr local text = head.term_no_initial_bang_bang if not data.lang:link_tr(head.sc) then text = remove_links(text) end local automated_tr = data.lang:transliterate(text, head.sc) if automated_tr then local manual_tr = head.tr if manual_tr then if remove_links(manual_tr) == remove_links(automated_tr) then insert(data.categories, "ศัพท์" .. full_langname .. "ที่มีการถอดอักษรซ้ำซ้อน") else insert(data.categories, "ศัพท์" .. full_langname .. "ที่มีการถอดอักษรกำหนดเองและไม่ซ้ำซ้อน") end end if not manual_tr then head.tr = automated_tr end end -- There is still no transliteration? -- Add the entry to a cleanup category. if not head.tr then head.tr = "<small>transliteration needed</small>" -- FIXME: No current support for 'Request for transliteration of Classical Persian terms' or similar. -- Consider adding this support in [[Module:category tree/poscatboiler/data/entry maintenance]]. insert(data.categories, "Requests for transliteration of " .. full_langname .. " terms") else -- Otherwise, trim it. head.tr = trim(head.tr) end end end -- Link to the transliteration entry for languages that require this. if head.tr and data.lang:link_tr(head.sc) then head.tr = full_link{ term = head.tr, lang = data.lang, sc = get_script("Latn"), tr = "-" } end end ------------ 8. Maybe tag the title with the appropriate script code, using the `display_title` mechanism. ------------ -- Assumes that the scripts in "toBeTagged" will never occur in the Reconstruction namespace. -- (FIXME: Don't make assumptions like this, and if you need to do so, throw an error if the assumption is violated.) -- Avoid tagging ASCII as Hani even when it is tagged as Hani in the headword, as in [[check]]. The check for ASCII -- might need to be expanded to a check for any Latin characters and whitespace or punctuation. local display_title -- Where there are multiple headwords, use the script for the first. This assumes the first headword is similar to -- the pagename, and that headwords that are in different scripts from the pagename aren't first. This seems to be -- about the best we can do (alternatively we could potentially do script detection on the pagename). local dt_script = data.heads[1].sc local dt_script_code = dt_script:getCode() local page_non_ascii = namespace == "" and not page.pagename:find("^[%z\1-\127]+$") local unsupported_pagename, unsupported = page.full_raw_pagename:gsub("^ชื่อไม่รองรับ/", "") if unsupported == 1 and page.unsupported_titles[unsupported_pagename] then display_title = 'ชื่อไม่รองรับ/<span class="' .. dt_script_code .. '">' .. page.unsupported_titles[unsupported_pagename] .. '</span>' elseif page_non_ascii and m_headword_data.toBeTagged[dt_script_code] or (dt_script_code == "Jpan" and (text_in_script(page.pagename, "Hira") or text_in_script(page.pagename, "Kana"))) or (dt_script_code == "Kore" and text_in_script(page.pagename, "Hang")) then display_title = '<span class="' .. dt_script_code .. '">' .. page.full_raw_pagename .. '</span>' -- Keep Han entries region-neutral in the display title. elseif page_non_ascii and (dt_script_code == "Hant" or dt_script_code == "Hans") then display_title = '<span class="Hani">' .. page.full_raw_pagename .. '</span>' elseif namespace == "การสืบสร้าง" then local matched display_title, matched = ugsub( page.full_raw_pagename, "^(การสืบสร้าง:[^/]+/)(.+)$", function(before, term) return before .. tag_text(term, data.lang, dt_script) end ) if matched == 0 then display_title = nil end end -- FIXME: Generalize this. -- If the current language uses Aran (Nastaliq), e.g. Urdu, and there's more than one language on the page, don't -- set the display title because we don't want Nastaliq for terms that also exist in other languages that don't -- display in Nastaliq (e.g. Arabic or Persian). Because the word "Urdu" occurs near the end of the alphabet, Urdu -- fonts tend to override the fonts of other languages. FIXME: This is checking for more than one language on the -- page but instead needs to check if there are any languages using scripts other than Aran. if dt_script_code == "Aran" and page.L2_list.n > 1 then display_title = nil end if display_title then mw.getCurrentFrame():callParserFunction( "DISPLAYTITLE", display_title ) end ------------ 9. Insert additional categories. ------------ if data.force_cat_output then -- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/force cat output]] track("force cat output") end if has_redundant_head_param then if not data.no_redundant_head_cat then -- This is not the right way to go about this; too many exceptions and problems due to language-specific headword -- handling customization. If we want this, it should be opt-in by a given language passing in the default headword. -- insert(data.categories, full_langname .. " terms with redundant head parameter") end end if namespace == "การสืบสร้าง" and not lang_reconstructed then insert(data.categories, full_langname .. " reconstructed terms") end -- If the first head is multiword (after removing links), maybe insert into "LANG multiword terms". if not data.nomultiwordcat and not is_varform_only and any_script_has_spaces and postype == "lemma" then local no_multiword_cat = m_headword_data.no_multiword_cat if not (no_multiword_cat[langcode] or no_multiword_cat[full_langcode]) then -- Check for spaces or hyphens, but exclude prefixes and suffixes. -- Use the pagename, not the head= value, because the latter may have extra -- junk in it, e.g. superscripted text that throws off the algorithm. local no_hyphen = m_headword_data.hyphen_not_multiword_sep -- Exclude hyphens if the data module states that they should for this language. local checkpattern = (no_hyphen[langcode] or no_hyphen[full_langcode]) and ".[%s፡]." or ".[%s%-፡]." local is_multiword = umatch(page.pagename, checkpattern) if is_multiword and not non_categorizable(page.full_raw_pagename) then insert(data.categories, full_langname .. " multiword terms") elseif not is_multiword then local long_word_threshold = m_headword_data.long_word_thresholds[langcode] or m_headword_data.long_word_thresholds[full_langcode] if long_word_threshold and ulen(page.pagename) >= long_word_threshold then insert(data.categories, "Long " .. full_langname .. " words") end end end end -- Determine whether to insert a category 'LANGNAME POS in SCRIPT'. If there are multiple heads, we may need to check -- each head, as the heads may (theoretically) have different scripts. local default_sccat = m_headword_data.default_sccat if data.sccat or not is_varform_only and (default_sccat[langcode] or langcode ~= full_langcode and default_sccat[full_langcode]) then local function needs_sccat(sccat_entry, sc) if sccat_entry == true or not sccat_entry then return sccat_entry end if type(sccat_entry) == "table" then local in_list = contains(sccat_entry, sc:getCode()) if sccat_entry[1] == "not" then in_list = not in_list end return in_list end return nil end for _, head in ipairs(data.heads) do -- First check the `sccat` specified at the {{head}} level. local this_needs_sccat = needs_sccat(data.sccat, head.sc) -- If that wasn't given, check the default sccat at the language level for the lang code. if this_needs_sccat == nil and not is_varform_only then this_needs_sccat = needs_sccat(default_sccat[langcode], head.sc) end -- If that wasn't found and the lang code is an etym code, check the default sccat at the parent language level. if this_needs_sccat == nil and not is_varform_only and langcode ~= full_langcode then this_needs_sccat = needs_sccat(default_sccat[full_langcode], head.sc) end if this_needs_sccat then insert(data.categories, data.pos_category .. full_langname .. "ในอักษร" .. head.sc:getDisplayForm(data.lang)) end end end -- Reconstructed terms often use weird combinations of scripts and realistically aren't spelled so much as notated. if namespace ~= "การสืบสร้าง" and not is_varform_only then -- Map from languages to a string containing the characters to ignore when considering whether a term has -- multiple written scripts in it. Typically these are Greek or Cyrillic letters used for their phonetic -- values. local characters_to_ignore = { ["aaq"] = "αάὰ", -- Penobscot (Algonquian) ["acy"] = "δθ", -- Cypriot Arabic ["aez"] = "β", -- Aeka (Trans-New Guinea) ["anc"] = "γ", -- Ngas (Chadic/Afroasiatic) ["aou"] = "χ", -- A'ou (Kra-Dai) ["art-blk"] = "ч", -- Bolak (conlang) ["awg"] = "β", -- Anguthimri (Pama-Nyungan) ["az"] = "ь", -- Azerbaijani (Turkic; Yañalif Latin spelling, c. 1928 - 1938) ["ba"] = "ь", -- Bashkir (Turkic; Yañalif Latin spelling, c. 1928 - 1938) ["bhp"] = "β", -- Bima (Austronesian) ["bjz"] = "β", -- Baruga (Trans-New Guinea) ["byk"] = "θ", -- Biao (Kra-Dai) ["cdy"] = "θ", -- Chadong (Kra-Dai) ["chp"] = "θ", -- Chipewyan (Athabaskan) ["cjh"] = "χ", -- Upper Chehalis (Salishan) ["clm"] = "χ", -- Klallam (Salishan) ["col"] = "χ", -- Colombia-Wenatchi (Salishan) ["coo"] = "χθ", -- Comox (Salishan) ["crx"] = "θ", -- Carrier (Athabaskan) ["ets"] = "θ", -- Yekhee (Edoid/Niger-Congo) ["ett"] = "χ", -- Etruscan (isolate; in romanizations) ["fla"] = "χ", -- Montana Salish (Salishan) ["grt"] = "་", -- Garo (South Asian Sino-Tibetan) ["gmw-gts"] = "χ", -- Gottscheerish (Bavarian variant spoken in Slovenia) ["hur"] = "χθ", -- Halkomelem (Salishan) ["itc-psa"] = "f", -- Pre-Samnite (Italic; normally written in Greek) ["izh"] = "ь", -- Ingrian (Finnic) ["kic"] = "θ", -- Kickapoo (Algonquian) ["kk"] = "ь", -- Kazakh (Turkic; Yañalif Latin spelling, c. 1928 - 1938) ["ky"] = "ь", -- Kyrgyz (Turkic; Yañalif Latin spelling, c. 1928 - 1938) ["lil"] = "χ", -- Lillooet (Salishan) ["lsi"] = "ꓹ", -- Lashi (Lolo-Burmese/Sino-Tibetan; represents a glottal stop) ["mhz"] = "β", -- Mor (Austronesian) ["mqn"] = "β", -- Moronene (Austronesian) ["neg"]= "ӡā", -- Negidal (Tungusic; normally in Cyrillic) ["oka"] = "χ", -- Okanagan (Salishan) ["ole"] = "θ", -- Olekha (Sino-Tibetan) ["oui"] = "γβ", -- Old Uyghur (Turkic; FIXME: others? E.g. Greek delta (δ)?) ["pox"] = "χ", -- Polabian (West Slavic) ["rif"] = "ε", -- Tarifit (Berber) ["rom"] = "Θθ", -- Romani (Indic: International Standard; two different thetas???) ["rpn"] = "β", -- Repanbitip (Austronesian) ["sah"] = "ь", -- Yakut (Turkic; 1929 - 1939 Latin spelling) ["sit-jap"] = "χ", -- Japhug (Sino-Tibetan) ["sjw"] = "θ", -- Shawnee (Algonquian) ["squ"] = "χ", -- Squamish (Salishan) ["str"] = "χθ", -- Saanich (Salishan) ["teh"] = "χ", -- Tehuelche (Chonan; spoken in Argentina) ["tep"] = "η", -- Tepecano (Uto-Aztecan) ["thp"] = "χ", -- Thompson (Salishan) ["tk"] = "ь", -- Turkmen (Turkic; Yañalif Latin spelling, c. 1928 - 1938) ["tt"] = "ь", -- Kazakh (Turkic; Yañalif Latin spelling, c. 1928 - 1938) ["twa"] = "χ", -- Twana (Salishan) ["wbl"] = "ы", -- Wakhi (Iranian) ["xbc"] = "ϸ", -- Bactrian (Iranian; represents š; normally written in Greek) ["yha"] = "θ", -- Baha (Kra-Dai) ["za"] = "зч", -- Zhuang (Tai/Kra-Dai); 1957-1982 alphabet used two Cyrillic letters (as well as some others like -- ƃ, ƅ, ƨ, ɯ and ɵ that look like Cyrillic or Greek but are actually Latin) ["zlw-slv"] = "χђћ", -- Slovincian (West Slavic; FIXME: χ is Greek, the other two are Cyrillic, but I'm not sure -- the currect characters are being chosen in the entry names) ["zng"] = "θ", -- Mang (Mon-Khmer) ["ztp"] = "θ", -- Loxicha Zapotec (Zapotecan) } -- Determine how many real scripts are found in the pagename, where we exclude symbols and such. We exclude -- scripts whose `character_category` is false as well as Zmth (mathematical notation symbols), which has a -- category of "Mathematical notation symbols". When counting scripts, we need to elide language-specific -- variants because e.g. Beng and as-Beng have slightly different characters but we don't want to consider them -- two different scripts (e.g. [[এৰ]] has two characters which are detected respectively as Beng and as-Beng). local seen_scripts = {} local num_seen_scripts = 0 local num_loops = 0 local canon_pagename = page.pagename local ch_to_ignore = characters_to_ignore[full_langcode] if ch_to_ignore then canon_pagename = ugsub(canon_pagename, "[" .. ch_to_ignore .. "]", "") end while true do if canon_pagename == "" or num_seen_scripts >= 2 or num_loops >= 10 then break end -- Make sure we don't get into a loop checking the same script over and over again; happens with e.g. [[ᠪᡳ]] num_loops = num_loops + 1 local pagename_script = find_best_script_without_lang(canon_pagename, "None only as last resort") local script_chars = pagename_script.characters if not script_chars then -- we are stuck; this happens with None break end local script_code = pagename_script:getCode() local replaced canon_pagename, replaced = ugsub(canon_pagename, "[" .. script_chars .. "]", "") if ( replaced and script_code ~= "Zmth" and (script_data or get_script_data())[script_code] and script_data[script_code].character_category ~= false ) then script_code = script_code:gsub("^.-%-", "") if not seen_scripts[script_code] then seen_scripts[script_code] = true num_seen_scripts = num_seen_scripts + 1 end end end if num_seen_scripts > 1 then insert(data.categories, "ศัพท์" .. full_langname .. "ที่เขียนด้วยอักษรหลายชนิด") end end -- Categorise for unusual characters. Takes into account combining characters, so that we can categorise for characters with diacritics that aren't encoded as atomic characters (e.g. U̠). These can be in two formats: single combining characters (i.e. character + diacritic(s)) or double combining characters (i.e. character + diacritic(s) + character). Each can have any number of diacritics. local standard = data.lang:getStandardCharacters() if not is_varform_only and standard and not non_categorizable(page.full_raw_pagename) then local function char_category(char) local specials = { ["#"] = "number sign", ["("] = "parentheses", [")"] = "parentheses", ["<"] = "angle brackets", [">"] = "angle brackets", ["["] = "square brackets", ["]"] = "square brackets", ["_"] = "underscore", ["{"] = "braces", ["|"] = "vertical line", ["}"] = "braces", ["ß"] = "ẞ", ["\205\133"] = "", -- this is UTF-8 for U+0345 ( ͅ) ["\239\191\189"] = "replacement character", } char = toNFD(char) :gsub(".[\128-\191]*", function(m) local new_m = specials[m] new_m = new_m or m:uupper() return new_m end) return toNFC(char) end if full_langcode ~= "hi" and full_langcode ~= "lo" then local standard_chars_scripts = {} for _, head in ipairs(data.heads) do standard_chars_scripts[head.sc:getCode()] = true end -- Iterate over the scripts, in case there is more than one (as they can have different sets of standard characters). for code in pairs(standard_chars_scripts) do local sc_standard = data.lang:getStandardCharacters(code) if sc_standard then if page.pagename_len > 1 then local explode_standard = {} local function explode(char) explode_standard[char] = true return "" end local sc_standard_exploded = ugsub(sc_standard, page.comb_chars.combined_double, explode) -- The following is correct; it relies on side-effecing the explode_standard[] table. ugsub(sc_standard_exploded, page.comb_chars.combined_single, explode):gsub(".[\128-\191]*", explode) local num_cat_inserted for char in pairs(page.explode_pagename) do if not explode_standard[char] then if char:find("[0-9]") then if not num_cat_inserted then insert(data.categories, "ศัพท์" .. full_langname .. "ที่สะกดด้วยตัวเลข") num_cat_inserted = true end elseif ufind(char, page.emoji_pattern) then insert(data.categories, "ศัพท์" .. full_langname .. "ที่สะกดด้วยอีโมจิ") else local upper = char_category(char) if not explode_standard[upper] then char = upper end insert(data.categories, "ศัพท์" .. full_langname .. "ที่สะกดด้วย " .. char) end end end end -- If a diacritic doesn't appear in any of the standard characters, also categorise for it generally. sc_standard = toNFD(sc_standard) for diacritic in ugmatch(page.decompose_pagename, page.comb_chars.diacritics_single) do if not umatch(sc_standard, diacritic) then insert(data.categories, "ศัพท์" .. full_langname .. "ที่สะกดด้วย ◌" .. diacritic) end end for diacritic in ugmatch(page.decompose_pagename, page.comb_chars.diacritics_double) do if not umatch(sc_standard, diacritic) then insert(data.categories, "ศัพท์" .. full_langname .. "ที่สะกดด้วย ◌" .. diacritic .. "◌") end end end end -- Ancient Greek, Hindi and Lao handled the old way for now, as their standard chars still need to be converted to the new format (because there are a lot of them). elseif ulen(page.pagename) ~= 1 then for character in ugmatch(page.pagename, "([^" .. standard .. "])") do local upper = char_category(character) if not umatch(upper, "[" .. standard .. "]") then character = upper end insert(data.categories, "ศัพท์" .. full_langname .. "ที่สะกดด้วย " .. character) end end end if not is_varform_only and data.heads[1].sc:isSystem("alphabet") then local pagename, i = page.pagename:ulower(), 2 while umatch(pagename, "(%a)" .. ("%1"):rep(i)) do i = i + 1 insert(data.categories, "ศัพท์" .. full_langname .. "ที่มีตัวอักษรเดิมติดกัน " .. i .. " ตัว") end end -- Categorise for palindromes if not is_varform_only and not data.nopalindromecat and namespace ~= "การสืบสร้าง" and ulen(page.pagename) > 2 -- FIXME: Use of first script here seems hacky. What is the clean way of doing this in the presence of -- multiple scripts? and is_palindrome(page.pagename, data.lang, data.heads[1].sc) then insert(data.categories, "พาลินโดรม" .. full_langname) end if namespace == "" and not lang_reconstructed then for _, head in ipairs(data.heads) do if page.full_raw_pagename ~= get_link_page(remove_links(head.term), data.lang, head.sc) then -- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/pagename spelling mismatch]] -- [[Special:WhatLinksHere/Wiktionary:Tracking/headword/pagename spelling mismatch/LANGCODE]] track("pagename spelling mismatch", data.lang) break end end end -- Add red link category if called for and we're not a "large" page, where such checks are disabled. if data.checkredlinks and not m_headword_data.large_pages[m_headword_data.pagename] then local plposcat = type(data.checkredlinks) == "string" and data.checkredlinks or data.pos_category check_red_link_inflections_top_level(data, plposcat) end -- Add to various maintenance categories. export.maintenance_cats(page, data.lang, data.categories, data.whole_page_categories) ------------ 10. Format and return headwords, genders, inflections and categories. ------------ -- Format and return all the gathered information. This may add more categories (e.g. gender/number categories), -- so make sure we do it before evaluating `data.categories`. local text = '<span class="headword-line">' .. format_headword(data) .. format_headword_genders(data, is_varform_only) .. format_top_level_inflections(data) .. '</span>' -- Language-specific categories. local cats = format_categories( data.categories, data.lang, data.sort_key, page.encoded_pagename, data.force_cat_output or test_force_categories, data.heads[1].sc ) -- Language-agnostic categories. local whole_page_cats = format_categories( data.whole_page_categories, nil, "-" ) return text .. cats .. whole_page_cats end return export kcr5xvf080u2bdh9faqg4gv0lld3vzo มอดูล:links 828 36044 5775179 5756373 2026-10-14T03:23:51Z Octahedron80 267 5775179 Scribunto text/plain local export = {} --[=[ [[Unsupported titles]], pages with high memory usage, extraction modules and part-of-speech names are listed at [[Module:links/data]]. Other modules used: [[Module:script utilities]] [[Module:scripts]] [[Module:languages]] and its submodules [[Module:gender and number]] [[Module:debug/track]] ]=] local anchors_module = "Module:anchors" local debug_track_module = "Module:debug/track" local decorations_module = "Module:decorations" local form_of_module = "Module:form of" local gender_and_number_module = "Module:gender and number" local languages_module = "Module:languages" local load_module = "Module:load" local memoize_module = "Module:memoize" local pages_module = "Module:pages" local roots_module = "Module:roots" local scripts_module = "Module:scripts" local script_utilities_module = "Module:script utilities" local string_encode_entities_module = "Module:string/encode entities" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local utilities_module = "Module:utilities" local concat = table.concat local find = string.find local get_current_title = mw.title.getCurrentTitle local insert = table.insert local ipairs = ipairs local match = string.match local new_title = mw.title.new local pairs = pairs local remove = table.remove local sub = string.sub local toNFC = mw.ustring.toNFC local tostring = tostring local type = type local unstrip = mw.text.unstrip local NAMESPACE = get_current_title().nsText local function anchor_encode(...) anchor_encode = require(memoize_module)(mw.uri.anchorEncode, true) return anchor_encode(...) end local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end local function decode_entities(...) decode_entities = require(string_utilities_module).decode_entities return decode_entities(...) end local function decode_uri(...) decode_uri = require(string_utilities_module).decode_uri return decode_uri(...) end -- Can't yet replace, as the [[Module:string utilities]] version no longer has automatic double-encoding prevention, which requires changes here to account for. local function encode_entities(...) encode_entities = require(string_encode_entities_module) return encode_entities(...) end local function extend(...) extend = require(table_module).extend return extend(...) end local function find_best_script_without_lang(...) find_best_script_without_lang = require(scripts_module).findBestScriptWithoutLang return find_best_script_without_lang(...) end local function format_categories(...) format_categories = require(utilities_module).format_categories return format_categories(...) end local function format_genders(...) format_genders = require(gender_and_number_module).format_genders return format_genders(...) end local function format_decorations(...) format_decorations = require(decorations_module).format_decorations return format_decorations(...) end local function get_current_L2(...) get_current_L2 = require(pages_module).get_current_L2 return get_current_L2(...) end local function get_lang(...) get_lang = require(languages_module).getByCode return get_lang(...) end local function get_script(...) get_script = require(scripts_module).getByCode return get_script(...) end local function language_anchor(...) language_anchor = require(anchors_module).language_anchor return language_anchor(...) end local function load_data(...) load_data = require(load_module).load_data return load_data(...) end local function request_script(...) request_script = require(script_utilities_module).request_script return request_script(...) end local function get_root_link_page(...) get_root_link_page = require(roots_module).get_link_page return get_root_link_page(...) end local function shallow_copy(...) shallow_copy = require(table_module).shallowCopy return shallow_copy(...) end local function split(...) split = require(string_utilities_module).split return split(...) end local function tag_text(...) tag_text = require(script_utilities_module).tag_text return tag_text(...) end local function tag_translit(...) tag_translit = require(script_utilities_module).tag_translit return tag_translit(...) end local function trim(...) trim = require(string_utilities_module).trim return trim(...) end local function u(...) u = require(string_utilities_module).char return u(...) end local function ulower(...) ulower = require(string_utilities_module).lower return ulower(...) end local function umatch(...) umatch = require(string_utilities_module).match return umatch(...) end local m_headword_data local function get_headword_data() m_headword_data = load_data("Module:headword/data") return m_headword_data end local function track(page, code) local tracking_page = "links/" .. page debug_track(tracking_page) if code then debug_track(tracking_page .. "/" .. code) end end local function field_non_empty(list, field) if not list then return nil end if type(list) ~= "table" then error(("Internal error: Wrong type for `termobj.%s`=%s, should be %s\"table\""):format( field, mw.dumpObject(list), field == "q" or field == "qq" and "\"string\" or " or "")) end return list[1] end --[=[ Add any decorations (left or right regular or accent qualifiers, labels or references) to an item. `text` is the item's text (to which to add the decorations) and `itemobj` is the object specifying the item's decorations, which should optionally contain: * left regular qualifiers in `q` (an array of strings or a single string); an empty array will be ignored; * right regular qualifiers in `qq` (an array of strings or a single string); an empty array will be ignored; * left accent qualifiers in `a` (an array of strings); an empty array will be ignored; * right accent qualifiers in `aa` (an array of strings); an empty array will be ignored; * left labels in `l` (an array of strings); an empty array will be ignored; * right labels in `ll` (an array of strings); an empty array will be ignored; * references in `refs`, an array either of strings (formatted reference text) or objects containing fields `text` (formatted reference text) and optionally `name` and/or `group`; an empty array will be ignored. `lang` is a language object and is required if any accent qualifiers or labels are given. ]=] local function add_text_decorations(text, itemobj, lang) local q = itemobj.q if type(q) == "string" then q = { q } end local qq = itemobj.qq if type(qq) == "string" then qq = { qq } end if field_non_empty(q, "q") or field_non_empty(qq, "qq") or field_non_empty(itemobj.a, "a") or field_non_empty(itemobj.aa, "aa") or field_non_empty(itemobj.l, "l") or field_non_empty(itemobj.ll, "ll") or field_non_empty(itemobj.refs, "refs") then text = format_decorations { lang = lang, text = text, q = q, qq = qq, a = itemobj.a, aa = itemobj.aa, l = itemobj.l, ll = itemobj.ll, refs = itemobj.refs, } end return text end local function selective_trim(...) -- Unconditionally trimmed charset. local always_trim = "\194\128-\194\159" .. -- U+0080-009F (C1 control characters) "\194\173" .. -- U+00AD (soft hyphen) "\226\128\170-\226\128\174" .. -- U+202A-202E (directionality formatting characters) "\226\129\166-\226\129\169" -- U+2066-2069 (directionality formatting characters) -- Standard trimmed charset. local standard_trim = "%s" .. -- (default whitespace charset) "\226\128\139-\226\128\141" .. -- U+200B-200D (zero-width spaces) always_trim -- If there are non-whitespace characters, trim all characters in `standard_trim`. -- Otherwise, only trim the characters in `always_trim`. selective_trim = function(text) if text == "" then return text end local trimmed = trim(text, standard_trim) if trimmed ~= "" then return trimmed end return trim(text, always_trim) end return selective_trim(...) end local function escape(text, str) local rep repeat text, rep = text:gsub("\\\\(\\*" .. str .. ")", "\5%1") until rep == 0 return (text:gsub("\\" .. str, "\6")) end local function unescape(text, str) return (text :gsub("\5", "\\") :gsub("\6", str)) end local function has_unescaped_carets(text) return escape(text, "^"):find("^", 1, true) ~= nil end local function assert_caret_capitalization_allowed(text, sc) if sc:forbidsCaretCapitalization() and has_unescaped_carets(text) then error(("Caret (^) cannot be used to capitalize transliterations for the %s script " .. "(see caret_capitalization in [[Module:scripts/data]]).") :format(sc:getCanonicalName()), 2) end end -- Remove bold, italics, soft hyphens, strip markers and HTML tags. local function remove_formatting(str) str = str :gsub("('*)'''(.-'*)'''", "%1%2") :gsub("('*)''(.-'*)''", "%1%2") :gsub("­", "") return (unstrip(str) :gsub("<[^<>]+>", "")) end --[==[ Split `text` on double slashes (taking account of escaping backslashes). Return a list of split items. ]==] function export.split_on_slashes(text) if text:find("\\", nil, true) then track("escaped", "split_on_slashes") end text = split(escape(text, "//"), "//", true) or {} for i, v in ipairs(text) do text[i] = unescape(v, "//") if v == "" then text[i] = false end end return text end --[==[ If `text` is a wikilink (i.e. in the form `<nowiki>[[foo]]</nowiki>` or `<nowiki>[[foo|bar]]</nowiki>`), return the link target and display text. If the wikilink is single-part, the display text will be the same as the link target. If the text is not in wikilink form, return {nil} for both link target and display text. ]==] function export.get_wikilink_parts(text) -- TODO: replace `allow_bad_target` with `allow_unsupported`, with support for links to unsupported titles, including escape sequences. if ( -- Filters out anything but "[[...]]" with no intermediate "[[" or "]]". not match(text, "^()%[%[") or -- Faster than sub(text, 1, 2) ~= "[[". find(text, "[[", 3, true) or find(text, "]]", 3, true) ~= #text - 1 ) then return nil, nil end local pipe = find(text, "|", 3, true) local title, display if pipe then title, display = sub(text, 3, pipe - 1), sub(text, pipe + 1, -3) else title = sub(text, 3, -3) display = title end return title, display end -- Does the work of export.get_fragment, but can be called directly to avoid unnecessary checks for embedded links. local function get_fragment(text) text = escape(text, "#") -- Replace numeric character references with the corresponding character (&#39; → '), -- as they contain #, which causes the numeric character reference to be -- misparsed (wa'a → wa&#39;a → pagename wa&, fragment 39;a). text = decode_entities(text) local target, fragment = text:match("^(.-)#(.+)$") target = target or text target = unescape(target, "#") fragment = fragment and unescape(fragment, "#") or nil return target, fragment end --[==[ Given a link target possibly containing a fragment (i.e. a pound sign and following anchor), return two values, the actual target (minus the fragment) and the fragment, or {nil} if there is no fragment. ]==] function export.get_fragment(text) if text:find("\\", nil, true) then track("escaped", "get_fragment") end -- If there are no embedded links, process input. local open = find(text, "[[", nil, true) if not open then return get_fragment(text) end local close = find(text, "]]", open + 2, true) if not close then return get_fragment(text) -- If there is one, but it's redundant (i.e. encloses everything with no pipe), remove and process. elseif open == 1 and close == #text - 1 and not find(text, "|", 3, true) then return get_fragment(sub(text, 3, -3)) end -- Otherwise, return the input. return text, nil end --[==[ Given a link target as passed to `full_link()`, get the actual page that the target refers to. This removes bold, italics, strip markets and HTML; calls `makeEntryName()` for the language in question; converts targets beginning with `*` to the Reconstruction namespace; and converts appendix-constructed languages to the Appendix namespace. Returns up to three values: # the actual page to link to, or {nil} to not link to anything; # how the target should be displayed as, if the user didn't explicitly specify any display text; generally the same as the original target, but minus any anti-asterisk !!; # the value `true` if the target had a backslash-escaped * in it (FIXME: explain this more clearly). FIXME: This should not be calling deprecated `makeEntryName()` and should always return the same number of values. ]==] function export.get_link_page_with_auto_display(target, lang, sc, plain) local orig_target = target if not target then return nil elseif target:find("\\", nil, true) then track("escaped", "get_link_page") end target = remove_formatting(target) if target:sub(1, 1) == ":" then track("initial colon") -- FIXME, the auto_display (second return value) should probably remove the colon return target:sub(2), orig_target end local prefix = target:match("^(.-):") -- Convert any escaped colons target = target:gsub("\\:", ":") if prefix then -- If this is an a link to another namespace or an interwiki link, ensure there's an initial colon and then -- return what we have (so that it works as a conventional link, and doesn't do anything weird like add the term -- to a category.) prefix = ulower(trim(prefix)) if prefix ~= "" and ( load_data("Module:data/namespaces")[prefix] or load_data("Module:data/interwikis")[prefix] ) then return target, orig_target end end -- Check if the term is reconstructed and remove any asterisk. Also check for anti-asterisk (!!). -- Otherwise, handle the escapes. local reconstructed, escaped, anti_asterisk if not plain then target, reconstructed = target:gsub("^%*(.)", "%1") if reconstructed == 0 then target, anti_asterisk = target:gsub("^!!(.)", "%1") if anti_asterisk == 1 then -- Remove !! from original. FIXME! We do it this way because the call to remove_formatting() above -- may cause non-initial !! to be interpreted as anti-asterisks. We should surely move the -- remove_formatting() call later. orig_target = orig_target:gsub("^!!", "") end end end target, escaped = target:gsub("^(\\-)\\%*", "%1*") if not (sc and sc:getCode() ~= "None") then sc = lang:findBestScript(target) end -- Remove carets if they are used to capitalize parts of transliterations (unless they have been escaped). if target:match("%^") then assert_caret_capitalization_allowed(target, sc) if sc:hasCaretCapitalization() then target = escape(target, "^") :gsub("%^", "") target = unescape(target, "^") end end -- Get the entry name for the language. target = lang:makeEntryName(target, sc, reconstructed == 1 or lang:hasType("appendix-constructed")) -- If the link contains unexpanded template parameters, then don't create a link. if target:match("{{{.-}}}") then -- FIXME: Should we return the original target as the default display value (second return value)? return nil end -- Link to appendix for reconstructed terms and terms in appendix-only languages. Plain links interpret * -- literally, however. if reconstructed == 1 then if lang:getFullCode() == "und" then -- Return the original target as default display value. If we don't do this, we wrongly get -- [Term?] displayed instead. return nil, orig_target end target = "การสืบสร้าง:" .. lang:getFullName() .. "/" .. target -- Reconstructed languages and substrates require an initial *. elseif anti_asterisk ~= 1 and (lang:hasType("reconstructed") or lang:getFamilyCode() == "qfa-sub") then error(("The specified language %s is unattested, while the term '%s' does not begin with '*' to indicate that it is reconstructed.") : format(lang:getCanonicalName(), orig_target)) elseif lang:hasType("appendix-constructed") then target = "ภาคผนวก:" .. lang:getFullName() .. "/" .. target else target = target end return target, orig_target, escaped > 0 end function export.get_link_page(target, lang, sc, plain) local target, auto_display, escaped = export.get_link_page_with_auto_display(target, lang, sc, plain) return target, escaped end -- Make a link from a given link's parts. local function make_link(link, lang, sc, id, isolated, cats, no_alt_ast, plain) -- Convert percent encoding to plaintext. link.target = link.target and decode_uri(link.target, "PATH") link.fragment = link.fragment and decode_uri(link.fragment, "PATH") -- Find fragments (if one isn't already set). -- Prevents {{l|en|word#Etymology 2|word}} from linking to [[word#Etymology 2#English]]. -- # can be escaped as \#. if link.target and link.fragment == nil then link.target, link.fragment = get_fragment(link.target) end -- Process the target local auto_display, escaped link.target, auto_display, escaped = export.get_link_page_with_auto_display(link.target, lang, sc, plain) -- Create a default display form. -- If the target is "" then it's a link like [[#English]], which refers to the current page. if auto_display == "" then auto_display = (m_headword_data or get_headword_data()).pagename end -- If the display is the target and the reconstruction * has been escaped, remove the escaping backslash. if escaped then auto_display = auto_display:gsub("\\([^\\]*%*)", "%1", 1) end -- Process the display form. if link.display then local orig_display = link.display link.display = lang:makeDisplayText(link.display, sc, true) if cats then auto_display = lang:makeDisplayText(auto_display, sc) -- If the alt text is the same as what would have been automatically generated, then the alt parameter is -- redundant (e.g. {{l|en|foo|foo}}, {{l|en|w:foo|foo}}, but not {{l|en|w:foo|w:foo}}). If they're -- different, but the alt text could have been entered as the term parameter without it affecting the target -- page, then the target parameter is redundant (e.g. {{l|ru|фу|фу́}}). If `no_alt_ast` is true, use pcall -- to catch the error which will be thrown if this is a reconstructed lang and the alt text doesn't have *. if link.display == auto_display then insert(cats, lang:getFullName() .. " links with redundant alt parameters") else local ok, check if no_alt_ast then ok, check = pcall(export.get_link_page, orig_display, lang, sc, plain) else ok = true check = export.get_link_page(orig_display, lang, sc, plain) end if ok and link.target == check then insert(cats, lang:getFullName() .. " links with redundant target parameters") end end end else link.display = lang:makeDisplayText(auto_display, sc) end if not link.target then return link.display end -- If the target is the same as the current page, there is no sense id -- and either the language code is "und" or the current L2 is the current -- language then return a "self-link" like the software does. if link.target == get_current_title().prefixedText then local fragment, current_L2 = link.fragment, get_current_L2() if ( fragment and fragment == current_L2 or not (id or fragment) and (lang:getFullCode() == "und" or lang:getFullName() == current_L2) ) then return tostring(mw.html.create("strong") :addClass("selflink") :wikitext(link.display)) end end -- Add fragment. Do not add a section link to "Undetermined", as such sections do not exist and are invalid. -- TabbedLanguages handles links without a section by linking to the "last visited" section, but adding -- "Undetermined" would break that feature. For localized prefixes that make syntax error, please use the -- format: ["xyz"] = true. local prefix = link.target:match("^:*([^:]+):") prefix = prefix and ulower(prefix) if prefix ~= "category" and not (prefix and load_data("Module:data/interwikis")[prefix]) then if (link.fragment or link.target:sub(-1) == "#") and not plain then track("fragment", lang:getFullCode()) if cats then insert(cats, lang:getFullName() .. " links with manual fragments") end end if not link.fragment then if id then link.fragment = lang:getFullCode() == "und" and anchor_encode(id) or language_anchor(lang, id) elseif lang:getFullCode() ~= "und" and not (link.target:match("^ภาคผนวก:") or link.target:match("^การสืบสร้าง:")) then --link.fragment = anchor_encode(lang:getFullName()) link.fragment = anchor_encode(lang:getCategoryName()) --th end end end -- Put inward-facing square brackets around a link to isolated spacing character(s). if isolated and link.display[1] and not umatch(decode_entities(link.display), "%S") then link.display = "&#x5D;" .. link.display .. "&#x5B;" end link.target = link.target:gsub("^(:?)(.*)", function(m1, m2) return m1 .. encode_entities(m2, "#%&+/:<=>@[\\]_{|}") end) link.fragment = link.fragment and encode_entities(remove_formatting(link.fragment), "#%&+/:<=>@[\\]_{|}") return "[[" .. link.target:gsub("^[^:]", ":%0") .. (link.fragment and "#" .. link.fragment or "") .. "|" .. link.display .. "]]" end -- Split a link into its parts. local function parse_link(linktext) local link = { target = linktext } local target = link.target link.target, link.display = target:match("^(..-)|(.+)$") if not link.target then link.target = target link.display = target end -- There's no point in processing these, as they aren't real links. local target_lower = link.target:lower() for _, false_positive in ipairs({ "category", "cat", "file", "image" }) do if target_lower:match("^" .. false_positive .. ":") then return nil end end link.display = decode_entities(link.display) link.target, link.fragment = get_fragment(link.target) -- So that make_link does not look for a fragment again. if not link.fragment then link.fragment = false end return link end local function check_params_ignored_when_embedded(alt, lang, id, cats) if alt then track("alt-ignored") if cats then insert(cats, lang:getFullName() .. " links with ignored alt parameters") end end if id then track("id-ignored") if cats then insert(cats, lang:getFullName() .. " links with ignored id parameters") end end end -- Find embedded links and ensure they link to the correct section. local function process_embedded_links(text, alt, lang, sc, id, cats, no_alt_ast, plain) -- Process the non-linked text. text = lang:makeDisplayText(text, sc, true) -- If the text begins with * and another character, then act as if each link begins with *. However, don't do this if the * is contained within a link at the start. E.g. `|*[[foo]]` would set all_reconstructed to true, while `|[[*foo]]` would not. local all_reconstructed = false if not plain then -- anchor_encode removes links etc. if anchor_encode(text):sub(1, 1) == "*" then all_reconstructed = true end -- Otherwise, handle any escapes. text = text:gsub("^(\\-)\\%*", "%1*") end check_params_ignored_when_embedded(alt, lang, id, cats) local function process_link(space1, linktext, space2) local capture = "[[" .. linktext .. "]]" local link = parse_link(linktext) -- Return unprocessed false positives untouched (e.g. categories). if not link then return capture end if all_reconstructed then if link.target:find("^!!") then -- Check for anti-asterisk !! at the beginning of a target, indicating that a reconstructed term -- wants a part of the term to link to a non-reconstructed term, e.g. Old English -- {{ang-noun|m|head=*[[!!Crist|Cristes]] [[!!mæsseǣfen]]}}. link.target = link.target:sub(3) -- Also remove !! from the display, which may have been copied from the target (as in mæsseǣfen in -- the example above). link.display = link.display:gsub("^!!", "") elseif not link.target:match("^%*") then link.target = "*" .. link.target end end linktext = make_link(link, lang, sc, id, false, nil, no_alt_ast, plain) :gsub("^%[%[", "\3") :gsub("%]%]$", "\4") return space1 .. linktext .. space2 end -- Use chars 1 and 2 as temporary substitutions, so that we can use charsets. These are converted to chars 3 and 4 by process_link, which means we can convert any remaining chars 1 and 2 back to square brackets (i.e. those not part of a link). text = text :gsub("%[%[", "\1") :gsub("%]%]", "\2") -- If the script uses ^ to capitalize transliterations, make sure that any carets preceding links are on the inside, so that they get processed with the following text. if text:find("^", nil, true) then assert_caret_capitalization_allowed(text, sc) if sc:hasCaretCapitalization() then text = escape(text, "^") :gsub("%^\1", "\1%^") text = unescape(text, "^") end end text = text:gsub("\1(%s*)([^\1\2]-)(%s*)\2", process_link) -- Remove the extra * at the beginning of a language link if it's immediately followed by a link whose display begins with * too. if all_reconstructed then text = text:gsub("^%*\3([^|\1-\4]+)|%*", "\3%1|*") end return (text :gsub("[\1\3]", "[[") :gsub("[\2\4]", "]]") ) end local function simple_link(term, fragment, alt, lang, sc, id, cats, no_alt_ast, suppress_redundant_wikilink_cat) local plain if lang == nil then lang, plain = get_lang("und"), true end -- Get the link target and display text. If the term is the empty string, treat the input as a link to the current page. if term == "" then term = get_current_title().prefixedText elseif term then local new_term, new_alt = export.get_wikilink_parts(term) if new_term then check_params_ignored_when_embedded(alt, lang, id, cats) -- [[|foo]] links are treated as plaintext "[[|foo]]". -- FIXME: Pipes should be handled via a proper escape sequence, as they can occur in unsupported titles. if new_term == "" then term, alt = nil, term else local title = new_title(new_term) if title then local ns = title.namespace -- File: and Category: links should be returned as-is. if ns == 6 or ns == 14 then return term end end term, alt = new_term, new_alt if cats then if not (suppress_redundant_wikilink_cat and suppress_redundant_wikilink_cat(term, alt)) then insert(cats, lang:getFullName() .. " links with redundant wikilinks") end end end end end if alt then alt = selective_trim(alt) if alt == "" then alt = nil end end -- If there's nothing to process, return nil. if not (term or alt) then return nil end -- If there is no script, get one. if not sc then sc = lang:findBestScript(alt or term) end -- Embedded wikilinks need to be processed individually. if term then local open = find(term, "[[", nil, true) if open and find(term, "]]", open + 2, true) then return process_embedded_links(term, alt, lang, sc, id, cats, no_alt_ast, plain) end term = selective_trim(term) end -- If not, make a link using the parameters. return make_link({ target = term, display = alt, fragment = fragment }, lang, sc, id, true, cats, no_alt_ast, plain) end --[==[ Create a basic link to the given term. It links to the language section (such as `==English==`), but it does not add language and script wrappers, so any code that uses this function should call `[[Module:script utilities#tag_text]]` to add such wrappers itself at some point. The first argument, `data`, may contain the following items, a subset of the items used in the `data` argument of `##full_link`. If any other items are included, they are ignored. { { term = "entry_to_link_to", alt = "link_text_or_displayed_text", lang = language_object, sc = script_object, fragment = "link_fragment", id = "sense_id", no_alt_ast = boolean, suppress_redundant_wikilink_cat = function(term, alt) -> boolean, cats = nil or {}, -- NOTE: If given, will be side-effected to return categories to add the page to. } } Specifically: * `term`: Term to turn into a link. This is generally the name of a page, possibly with extra diacritics added (e.g. length marks in Latin or Old English terms, accents in Russian terms, vowel diacritics in Arabic terms, etc.), which are stripped to determine the actual pagename. The term can contain wikilinks already embedded in it. These are processed individually just like a single link would be. The `alt` argument is ignored in this case. * `alt`: The alternative display for the link, if different from the linked page. If this is {nil}, the `term` argument is used instead (much like regular wikilinks). If `term` contains wikilinks in it, this argument is ignored and has no effect. (Links in which the alt is ignored are tracked with the tracking template {{whatlinkshere|tracking=links/alt-ignored}}.) * `lang` ('''required'''): The [[Module:languages#Language objects|language object]] for the term being linked. The link or links in `term` will normally have their fragment set to point to the canonical name (see {{tl|language data documentation}}) of `lang` (or, if it is an etymology-only language, to the canonical name of its L2 parent). (However, if `id` is defined, the fragment will point to a language-specific sense ID corresponding to this field, which in turn will be overridden by `fragment` if specified.) * `sc`: The [[Module:scripts#Script objects|script object]] for the term being linked. This rarely needs to be specified because it is autodetected based on `term` or `alt`, and the detection is usually correct. It is used to determine how to convert the term into a pagename, possibly by stripping certain diacritics from the term's text. * `fragment`: If specified, overrides the fragment in the generated link (i.e. the portion after `#`, which determines where on the page to go to when the link is clicked). If not specified, the fragment is generated from `id` (if given) or otherwise from `lang`. * `id`: Sense ID string. If this argument is defined, the link will point to a language-specific sense ID ({{ll|en|identifier|id=HTML}}) created by the template {{temp|senseid}}. The fragment for a sense ID consists of the language's canonical name, a hyphen (`-`), and the string that was supplied as the `id` argument. This is useful when a term has more than one sense in a language. If the `term` argument contains wikilinks, this argument is ignored. (Links in which the sense ID is ignored are tracked with the tracking template {{whatlinkshere|tracking=links/id-ignored}}.) * `no_alt_ast`: This is the same as `no_alt_ast` in `##full_link()`. See that function for more information. * `suppress_redundant_wikilink_cat`: This is the same as `suppress_redundant_wikilink_cat` in `##full_link()`. See that function for more information. * `cats`: This should be either {nil} or an empty list. In the latter case, tracking categories will be added to the list when appropriate. The caller can choose to add the page to those categories (as is done by ##full_link()`). The following special options are processed for each link (both simple terms and with embedded wikilinks): * The target page name will be processed by stripping certain diacritics (as mentioned above) and converting the resulting ''logical'' pagename to a ''physical'' pagename (which will be different from the logical pagename in the case of pages with unsupported characters in them and certain overly large pages, such as [[a]], that are split into parts). * If the term starts with `*`, then it is considered a reconstructed term, and a link to the `Reconstruction:` namespace will be created. If the text contains embedded wikilinks, then `*` is automatically applied to each one individually, while preserving the displayed form of each link as it was given. This allows linking to phrases containing multiple reconstructed terms, while only showing the `*` once at the beginning. * If the text starts with `:`, then the link is treated as "raw" and the above steps are skipped. This can be used in rare cases where the page name begins with `*` or if diacritics should not be stripped. For example: ** {{tl|l|en|*nix}} links to the nonexistent page [[Reconstruction:English/nix]] (`*` is interpreted as a reconstruction), but {{tl|l|en|:*nix}} links to [[*nix]]. ** {{tl|l|sl|Franche-Comté}} links to the nonexistent page [[Franche-Comte]] (`é` is converted to `e` by the diacritic-stripping process), but {{tl|l|sl|:Franche-Comté}} links to [[Franche-Comté]]. ]==] function export.language_link(data) if type(data) ~= "table" then error( "The first argument to the function language_link must be a table. See [[Module:links/documentation]] for more information.") elseif data.term and data.term:find("\\", nil, true) or data.alt and data.alt:find("\\", nil, true) then track("escaped", "language_link") end -- Categorize links to "und". local lang, cats = data.lang, data.cats if cats and lang:getCode() == "und" then insert(cats, "Undetermined language links") end return simple_link( data.term, data.fragment, data.alt, lang, data.sc, data.id, cats, data.no_alt_ast, data.suppress_redundant_wikilink_cat ) end function export.plain_link(data) if type(data) ~= "table" then error( "The first argument to the function plain_link must be a table. See Module:links/documentation for more information.") elseif data.term and data.term:find("\\", nil, true) or data.alt and data.alt:find("\\", nil, true) then track("escaped", "plain_link") end return simple_link( data.term, data.fragment, data.alt, nil, data.sc, data.id, data.cats, data.no_alt_ast, data.suppress_redundant_wikilink_cat ) end --[==[Replace any links with links to the correct section, but don't link the whole text if no embedded links are found. Returns the display text form.]==] function export.embedded_language_links(data) if type(data) ~= "table" then error( "The first argument to the function embedded_language_links must be a table. See Module:links/documentation for more information.") elseif data.term and data.term:find("\\", nil, true) or data.alt and data.alt:find("\\", nil, true) then track("escaped", "embedded_language_links") end local term, lang, sc = data.term, data.lang, data.sc -- If we don't have a script, get one. if not sc then sc = lang:findBestScript(term) end -- Do we have embedded wikilinks? If so, they need to be processed individually. local open = find(term, "[[", nil, true) if open and find(term, "]]", open + 2, true) then return process_embedded_links(term, data.alt, lang, sc, data.id, data.cats, data.no_alt_ast) end -- If not, return the display text. term = selective_trim(term) -- FIXME: Double-escape any percent-signs, because we don't want to treat non-linked text as having percent-encoded -- characters. This is a hack: percent-decoding should come out of [[Module:languages]] and only dealt with in this -- module, as it's specific to links. term = term:gsub("%%", "%%25") return lang:makeDisplayText(term, sc, true) end function export.mark(text, item_type, face, lang) local tag = { "", "" } if item_type == "gloss" then tag = { '<span class="mention-gloss-double-quote">“</span><span class="mention-gloss">', '</span><span class="mention-gloss-double-quote">”</span>' } if type(text) == "string" and text:match("^''[^'].*''$") then -- Temporary tracking for mention glosses that are entirely italicized or bolded, which is probably -- wrong. (Note that this will also find bolded mention glosses since they use triple apostrophes.) track("italicized-mention-gloss", lang and lang:getFullCode() or nil) end elseif item_type == "tr" then if face == "term" then tag = { '<span lang="' .. lang:getFullCode() .. '" class="tr mention-tr Latn">', '</span>' } else tag = { '<span lang="' .. lang:getFullCode() .. '" class="tr Latn">', '</span>' } end elseif item_type == "ts" then -- \226\129\160 = word joiner (zero-width non-breaking space) U+2060 tag = { '<span class="ts mention-ts Latn">/\226\129\160', '\226\129\160/</span>' } elseif item_type == "pos" then tag = { '<span class="ann-pos">', '</span>' } elseif item_type == "non-gloss" then tag = { '<span class="ann-non-gloss">', '</span>' } elseif item_type == "annotations" then tag = { '<span class="mention-gloss-paren annotation-paren">(</span>', '<span class="mention-gloss-paren annotation-paren">)</span>' } elseif item_type == "infl" then tag = { '<span class="ann-infl">', '</span>' } end if type(text) == "string" then return tag[1] .. text .. tag[2] else return "" end end --[=[ Implementation of `format_transliteration` and `format_transcription`. The implementation is identical except that the field containing the transliteration or transcription may vary and is specified in `field`, and the way a given transliteration or transcription is tagged may vary and is controlled by `tag_fn`, which is passed three parameters: `text` (the transliteration or transcription), `lang` (the language passed in) and `face` (the face passed in). On input, `item` is the transliteration or transcription or list of such objects; `field` is either {"tr"} or {"ts"}; `tag_fn` is a function of three parameters to tag the item, as described above; `lang` is the language object of the term whose transliteration or transcription is specified; and `face` is a string indicating how to display the item (generally only the strings {"term"} and {"default"} are recognized). ]=] local function format_transliteration_or_transcription(item, field, tag_fn, lang, item_face) if type(item) == "string" then return tag_fn(item, lang, item_face) end local formatted_items = {} for _, itemobj in ipairs(item) do local tagged_item = tag_fn(itemobj[field], lang, item_face) tagged_item = add_text_decorations(tagged_item, item, lang) insert(formatted_items, tagged_item) end if formatted_items[2] then -- FIXME: This should be customizable. return concat(formatted_items, " <i>or</i> ") else return formatted_items[1] end end --[==[ Format a transliteration string or list of transliteration objects. `tr` contains the transliteration(s), which for forward compatibility reasons can only be either a single transliteration string or a list of transliteration objects (each of which has a `tr` field holding the transliteration and optional fields `q`, `qq`, `l`, `ll` and/or `refs`). This correctly handles multiple transliterations as well as decorations (qualifiers, labels or references) attached to transliterations. ]==] function export.format_transliteration(tr, lang, face) return format_transliteration_or_transcription(tr, "tr", tag_translit, lang, face) end local function tag_transcription(ts, _lang, _face) return export.mark(ts, "ts") end --[==[ Format a transcription string or list of transcription objects. `ts` contains the transcription(s), which for forward compatibility reasons can only be either a single transcription string or a list of transcription objects (each of which has a `ts` field holding the transcription and optional decoration fields `q`, `qq`, `l`, `ll` and/or `refs`). This correctly handles multiple transcriptions as well as decorations (qualifiers, labels or references) attached to transcriptions. ]==] function export.format_transcription(ts, lang, face) return format_transliteration_or_transcription(ts, "ts", tag_transcription, lang, face) end local pos_tags --[==[ Format the annotations that are displayed with a link created by `full_link()`. Annotations are the extra bits of information that are displayed following the linked term, and include things such as gender, transliteration, gloss, etc. The first argument is a table with some or all of the following keys (all are optional): * `interwiki`: An interwiki link. This is used for links in translation tables to the corresponding term in another Wiktionary. If specified, it should be a fully formatted link and is inserted as-is at the beginning of the output. See the `interwiki()` function in [[Module:translations]]. * `genders`: Table containing a list of gender specifications in the style of [[Module:gender and number]]. If specified, these are formatted using `format_genders()` in [[Module:gender and number]] and the result inserted at the beginning of the output, following any interwiki link and (in all cases) directly after a no-break space. * `tr`: Transliteration or transliterations. Currently, this is always a one-item list. It is a list because of potential support for per-alternant transliterations due to the multiple alternants (separated by `//`) that can be specified in `term` or `alt`. The item in the list can be either a string or a list of transliteration objects (see `format_transliteration()`). * `ts`: Transcription or transcriptions. Like `tr`, this is currently always a one-item list, with the item being either a single string or a list of transcription objects, as described in `format_transcription()`. * `gloss`: Gloss that translates the term in the link. * `pos`: Part of speech of the linked term. If the given argument matches one of the aliases in `pos_aliases` in [[Module:headword/data]], or consists of a part of speech or alias followed by `f` (for a non-lemma form), expand it appropriately. Otherwise, just show the given text as it is. With `root`, the term links to the page where [[Module:roots]] keeps the language's roots, for languages that keep them outside the main namespace (`Appendix:Arabic roots/…`). * `infl`: A list of tags, each a string. Multiple tag sets may be encoded in this list by separating them with an element consisting of a semicolon. If there are multiple tag sets, they are formatted individually and separated by a semicolon + space. * `ng`: Arbitrary non-gloss descriptive text for the link. This should be used in preference to putting descriptive text in `gloss` or `pos`. * `lit`: Literal meaning of the term, if the usual meaning is figurative or idiomatic. * `postprocess_annotations`: A function to postprocess the annotations, after they have been formatted (see below). The `interwiki` and `genders` properties are formatted specially, and `postprocess_annotations` is a callback function rather than an item to display; all others are formatted (each in their own way), separated by commas and placed inside of parentheses (except that if both transliteration and transcription are present, they are separated by a space). The order of the annotations is `tr`+`ts`, `gloss`, `pos`, `infl`, `ng` and `lit`. The `postprocess_annotations` function, if supplied, is passed a single object, a table with two keys `data` (the `data` object passed into `format_link_annotations()`) and `annotations` (the formatted annotations, prior to being concatenated). It should side-effect the `annotations` list, e.g. by inserting more annotations. (It is used to handle nested inflections in [[Module:headword]]. FIXME: It should probably be generalized so that it can return the final formatted string, to allow for e.g. changing the way the annotations are formatted.) * The second argument is a string controlling the "face" that the terms are displayed in. Currently it only affects transliteration and transcription and only when the value {"term"} is passed in, in which case those annotations are displayed italicized. ]==] function export.format_link_annotations(data, face) local output = {} -- Interwiki link if data.interwiki then insert(output, data.interwiki) end -- Genders if type(data.genders) ~= "table" then data.genders = { data.genders } end if data.genders and data.genders[1] then local genders, gender_cats = format_genders(data.genders, data.lang) insert(output, "&nbsp;" .. genders) if gender_cats then local cats = data.cats if cats then extend(cats, gender_cats) end end end local annotations = {} -- Transliteration and transcription local tr = data.tr and data.tr[1] or nil local ts = data.ts and data.ts[1] or nil if tr or ts then local item_face if face == "term" then item_face = face else item_face = "default" end local formatted_tr = tr and export.format_transliteration(tr, data.lang, item_face) or nil local formatted_ts = ts and export.format_transcription(ts, data.lang, item_face) or nil if formatted_tr and formatted_ts then insert(annotations, {text = formatted_tr .. " " .. formatted_ts}) else insert(annotations, {text = formatted_tr or formatted_ts}) end end -- Gloss/translation if data.gloss then insert(annotations, {text = export.mark(data.gloss, "gloss")}) end -- Part of speech if data.pos then -- debug category for pos= containing transcriptions if data.pos:match("/[^><]-/") then data.pos = data.pos .. "[[Category:links likely containing transcriptions in pos]]" end -- Canonicalize part of speech aliases as well as non-lemma aliases like 'nf' or 'nounf' for "noun form". pos_tags = pos_tags or (m_headword_data or get_headword_data()).pos_aliases local pos = pos_tags[data.pos] if not pos and data.pos:find("f$") then local pos_form = data.pos:sub(1, -2) -- We only expand something ending in 'f' if the result is a recognized non-lemma POS. pos_form = (pos_tags[pos_form] or pos_form) .. " form" if (m_headword_data or get_headword_data()).nonlemmas[pos_form .. "s"] then pos = pos_form end end insert(annotations, {text = export.mark(pos or data.pos, "pos")}) end -- Inflection data if data.infl then local m_form_of = require(form_of_module) -- Split tag sets manually, since tagged_inflections creates a numbered list, and we do not want that. local infl_outputs = {} local tag_sets = m_form_of.split_tag_set(data.infl) for _, tag_set in ipairs(tag_sets) do table.insert(infl_outputs, m_form_of.tagged_inflections({ tags = tag_set, lang = data.lang, nocat = true, nolink = true, nowrap = true })) end insert(annotations, {text = export.mark(table.concat(infl_outputs, "; "), "infl")}) end -- Non-gloss text if data.ng then local separator local ng = data.ng if ng:find("^[;:]") then separator = "" elseif ng:find("^_") then ng = ng:sub(2) separator = " " end insert(annotations, {text = export.mark(ng, "non-gloss"), separator = separator}) end -- Literal/sum-of-parts meaning if data.lit then insert(annotations, "แปลตามตัวอักษรว่า " .. export.mark(data.lit, "gloss")) -- จาก ศบญ "literal translation" end -- Provide a hook to insert additional annotations such as nested inflections. if data.postprocess_annotations then data.postprocess_annotations { data = data, annotations = annotations } end if annotations[1] then local annparts = {} for i, annotation in ipairs(annotations) do local separator = annotation.separator if not separator then if i == 1 then separator = "" else separator = ", " end end insert(annparts, separator) insert(annparts, annotation.text) end insert(output, " ") insert(output, export.mark(concat(annparts), "annotations")) end return concat(output) end -- Encode certain characters to avoid various delimiter-related issues at various stages. We need to encode < and > -- because they end up forming part of CSS class names inside of <span ...> and will interfere with finding the end -- of the HTML tag. I first tried converting them to URL encoding, i.e. %3C and %3E; they then appear in the URL as -- %253C and %253E, which get mapped back to %3C and %3E when passed to [[Module:accel]]. But mapping them to &lt; -- and &gt; somehow works magically without any further work; they appear in the URL as < and >, and get passed to -- [[Module:accel]] as < and >. I have no idea who along the chain of calls is doing the encoding and decoding. If -- someone knows, please modify this comment appropriately! local accel_char_map local function get_accel_char_map() accel_char_map = { ["%"] = ".", [" "] = "_", ["_"] = u(0xFFF0), ["<"] = "&lt;", [">"] = "&gt;", } return accel_char_map end local function encode_accel_param_chars(param) return (param:gsub("[%% <>_]", accel_char_map or get_accel_char_map())) end local function encode_accel_param(prefix, param) if not param then return "" end if type(param) == "table" then local filled_params = {} -- There may be gaps in the sequence, especially for translit params. local maxindex = 0 for k in pairs(param) do if type(k) == "number" and k > maxindex then maxindex = k end end for i = 1, maxindex do filled_params[i] = param[i] or "" end -- [[Module:accel]] splits these up again. param = concat(filled_params, "*~!") end -- This is decoded again by [[WT:ACCEL]]. return prefix .. encode_accel_param_chars(param) end local function insert_if_not_blank(list, item) if item == "" then return end insert(list, item) end local function get_css_classes(lang, tr, accel, nowrap) if not accel and not nowrap then return "" end local classes = {} if accel then insert(classes, "form-of lang-" .. lang:getFullCode()) local form = accel.form if form then insert(classes, encode_accel_param_chars(form) .. "-form-of") end insert_if_not_blank(classes, encode_accel_param("gender-", accel.gender)) insert_if_not_blank(classes, encode_accel_param("pos-", accel.pos)) insert_if_not_blank(classes, encode_accel_param("transliteration-", accel.translit or (tr ~= "-" and tr or nil))) insert_if_not_blank(classes, encode_accel_param("target-", accel.target)) insert_if_not_blank(classes, encode_accel_param("origin-", accel.lemma)) insert_if_not_blank(classes, encode_accel_param("origin_transliteration-", accel.lemma_translit)) if accel.no_store then insert(classes, "form-of-nostore") end end if nowrap then insert(classes, nowrap) end return concat(classes, " ") end --[==[ Creates a full link, with annotations (see `##format_link_annotations`), in the style of {{tl|l}} or {{tl|m}}. The first argument, `data`, must be a table. It contains the various elements that can be supplied as parameters to {{tl|l}} or {{tl|m}}: { { -- Basic link-related fields term = "entry_to_link_to", alt = "link_text_or_displayed_text", lang = language_object, sc = script_object, fragment = "link_fragment", id = "sense_id", accel = {accelerated_creation_tags}, -- Link annotation fields interwiki = "interwiki_link", genders = {"gender1", "gender2", ...} or {{spec = "gender1", q = {"left qualifier", ...}, qq = {"right qualifier"}, ...}, ...}, tr = "transliteration" or "-" or {{tr = "transliteration", q = {"left_qualifier", ...}, qq = {"right_qualifier", ...}, ..., genders = {gender_spec, ...}}, ...}, ts = "transcription" or {{ts = "transliteration", q = {"left_qualifier", ...}, qq = {"right_qualifier", ...}, ..., genders = {gender_spec, ...}}, ...}, gloss = "gloss", pos = "part_of_speech_tag", infl = {"infl1_tag1", "infl1_tag2", ..., ";", "infl2_tag1", "infl2_tag2", ...}, ng = "non-gloss text", lit = "literal_translation", postprocess_annotations = function({data = full_link_data, annotations = {"annotation1", "annotation2", ...}}) -> nil, -- Other transliteration-related fields respect_link_tr = boolean, never_call_transliteration_module = boolean, suppress_tr = boolean, -- Decoration fields q = { "left_qualifier1", "left_qualifier2", ...} or "left_qualifier", qq = { "right_qualifier1", "right_qualifier2", ...} or "right_qualifier", l = { "left_label1", "left_label2", ...}, ll = { "right_label1", "right_label2", ...}, a = { "left_accent_qualifier1", "left_accent_qualifier2", ...}, aa = { "right_accent_qualifier1", "right_accent_qualifier2", ...}, refs = { "formatted_ref1", "formatted_ref2", ...} or { {text = "text", name = "name", group = "group"}, ... }, pretext = "text_at_beginning", posttext = "text_at_end", show_decorations = boolean, -- Fields controlling tracking categories track_sc = boolean, no_nonstandard_sc_cat = boolean, suppress_redundant_wikilink_cat = function(term, alt) -> boolean, -- Miscellaneous fields no_alt_ast = boolean, no_generate_alternants = boolean, } } Any one of the items in the `data` table (except for `lang`) may be {nil}. If none of `term`, `alt` and `tr` is present, a term request will be shown. Thus, calling {full_link{ term = term, lang = lang, sc = sc }}, where `term` is the page to link to (which may have diacritics that will be stripped and/or embedded bracketed links) and `lang` is a [[Module:languages#Language objects|language object]] from [[Module:languages]], will give a plain link similar to the one produced by the template {{tl|l}}, and calling {full_link( { term = term, lang = lang, sc = sc }, "term" )} will give a link similar to the one produced by the template {{tl|m}}. The function will: * Try to determine the script, based on the characters found in the `term` or `alt` argument, if the script was not given. If a script is given and `track_sc` is {true}, it will check whether the input script is the same as the one which would have been automatically generated and add the category ` ``lang`` terms with redundant script codes` if yes, or ` ``lang`` terms with non-redundant manual script codes` if no. This should be used when the input script object is directly determined by a template's `sc` parameter. * Call `simple_link()` on the `term` or `alt` forms, to remove diacritics in the page name, process any embedded wikilinks and create links to Reconstruction or Appendix pages when necessary. (`simple_link()` is almost exactly the same as `##language_link()`; the latter is a simple wrapper around the former that adds a bit of extra tracking.) * Call `[[Module:script utilities#tag_text]]` to add the appropriate language and script tags to the term and italicize terms written in the Latin script if necessary. Accelerated creation tags, as used by [[WT:ACCEL]], are included. * Generate a transliteration, based on the `alt` or `term` arguments, if the script is not Latin, no transliteration was provided in `tr` and the combination of the term's language and script support automatic transliteration. The transliteration itself will be linked if both `.respect_link_tr` is specified and the language of the term has the `link_tr` property set for the script of the term; but not otherwise. * Add the annotations (transliteration, gender, gloss, etc.) after the link. * If `no_alt_ast` is specified, then the `alt` text does not need to contain an asterisk if the language is reconstructed. This should only be used by modules which really need to allow links to reconstructions that don't display asterisks (e.g. number boxes). * If `suppress_redundant_wikilink_cat` is specified, it should be a function that indicates whether to suppress the generation of the ` ``lang`` links with redundant wikilinks` tracking category. It is passed two arguments, the `term` and `alt` parameters. Normally, this tracking category is added whenever the `term` argument consists entirely of a one-part or two-part embedded link, which is considered "redundant" in that the link can be rewritten into separate `term` and `alt` arguments without any embedded links. For certain wrapping templates, however, otherwise "redundant" embedded links are necessary to prevent interpretation of certain characters as delimiters. For example, {{tl|col}} and related templates use `~` as a separator, as well as `,` when not followed by a space. In these templates, embedded links are required to correctly link to terms containing those delimiters, such as [[Micros~1]] and [[1,6-Cleves acid]], but will incorrectly trigger the addition of the tracking category unless the appropriate `suppress_redundant_wikilink_cat` function is given. * If `pretext` or `posttext` is specified, this is text to (respectively) prepend or append to the output, directly before processing decorations (qualifiers, labels and references). This can be used to add arbitrary extra text inside of the decorations. * If `show_decorations` is specified, then decorations specified in `data` (i.e. left and right qualifiers, accent qualifiers, labels and references) will be displayed, otherwise they will be ignored. (This is because a fair amount of code stores decorations in these fields and displays them itself, rather than expecting {full_link()} to display them.) * ]==] function export.full_link(data, face, allow_self_link, show_qualifiers) if type(data) ~= "table" then error("The first argument to the function full_link must be a table. " .. "See Module:links/documentation for more information.") elseif data.term and data.term:find("\\", nil, true) or data.alt and data.alt:find("\\", nil, true) then track("escaped", "full_link") end if show_qualifiers then -- FIXME: Eventually remove the error code. Added 2026-09-17, remove after 2026-10-17 or so. error("Can't pass fourth parameter `show_qualifiers` any more. Set `show_decorations = true` on data.") end if data.show_qualifiers then -- FIXME: Eventually remove the error code. Added 2026-09-18, remove after 2026-10-18 or so. error("Can't set field `show_qualifiers`; use `show_decorations`") end if data.no_generate_forms then -- FIXME: Eventually remove the error code. Added 2026-09-14, remove after 2026-10-14 or so. error("Can't set field `no_generate_forms`; use `no_generate_alternants`") end -- Prevent data from being destructively modified. data = shallow_copy(data) data.cats = {} -- Categorize links to "und". local lang, cats = data.lang, data.cats if cats and lang:getCode() == "und" then insert(cats, "Undetermined language links") end -- With pos=root, link to the page where Module:roots keeps the language's roots (Appendix:Arabic roots/…) and -- display the root as given. if data.pos == "root" and type(data.term) == "string" and not data.term:find(":", nil, true) then local root_page = get_root_link_page(lang, data.term) if root_page then data.alt = data.alt or data.term data.term = root_page end end local terms = { true } -- Generate multiple alternants if applicable. for _, param in ipairs { "term", "alt" } do if type(data[param]) == "string" and data[param]:find("//", nil, true) then data[param] = export.split_on_slashes(data[param]) elseif type(data[param]) == "string" and not (type(data.term) == "string" and data.term:find("//", nil, true)) then if not data.no_generate_alternants then data[param] = lang:generateAlternants(data[param]) else data[param] = { data[param] } end else data[param] = {} end end for _, param in ipairs { "sc", "tr", "ts" } do data[param] = { data[param] } end for _, param in ipairs { "term", "alt", "sc", "tr", "ts" } do for i in pairs(data[param]) do terms[i] = true end end -- Create the link local outparts = {} local id, no_alt_ast, suppress_redundant_wikilink_cat, accel, never_call_transliteration_module = data.id, data.no_alt_ast, data.suppress_redundant_wikilink_cat, data.accel, data.never_call_transliteration_module local link_tr = data.respect_link_tr and lang:link_tr(data.sc[1]) for i in ipairs(terms) do local link -- Is there any text to show? if (data.term[i] or data.alt[i]) then -- Try to detect the script if it was not provided -- FIXME: alt is ignored if term contains embedded links, so the following isn't quite right. local display_term = data.alt[i] or data.term[i] local best = lang:findBestScript(display_term) -- no_nonstandard_sc_cat is intended for use in [[Module:interproject]] if ( not data.no_nonstandard_sc_cat and best:getCode() == "None" and find_best_script_without_lang(display_term):getCode() ~= "None" ) then insert(cats, lang:getFullName() .. " terms in nonstandard scripts") end if not data.sc[i] then data.sc[i] = best -- Track uses of sc parameter. elseif data.track_sc then if data.sc[i]:getCode() == best:getCode() then insert(cats, lang:getFullName() .. " terms with redundant script codes") else insert(cats, lang:getFullName() .. " terms with non-redundant manual script codes") end end -- If using a discouraged character sequence, add to maintenance category if data.sc[i]:hasNormalizationFixes() == true then if (data.term[i] and data.sc[i]:fixDiscouragedSequences(toNFC(data.term[i])) ~= toNFC(data.term[i])) or (data.alt[i] and data.sc[i]:fixDiscouragedSequences(toNFC(data.alt[i])) ~= toNFC(data.alt[i])) then insert(cats, "Pages using discouraged character sequences") end end link = simple_link( data.term[i], data.fragment, data.alt[i], lang, data.sc[i], id, cats, no_alt_ast, suppress_redundant_wikilink_cat ) end -- simple_link can return nil, so check if a link has been generated. if link then -- Add "nowrap" class to prefixes in order to prevent wrapping after the hyphen local nowrap -- FIXME: alt is ignored if term contains embedded links, so the following isn't quite right. local display_term = data.alt[i] or data.term[i] -- FIXME, use hyphens from [[Module:affix]] if display_term and (display_term:find("^%*?%-") or display_term:find("^%*?־")) then -- Hebrew maqqef nowrap = "nowrap" end -- Forcibly disable acceleration in the presence of embedded links, because it doesn't work correctly; -- if one of the embedded terms doesn't exist, the acceleration code will try to make a form-of for the -- embedded term pointing to the original headword (which is wrong). if link:find(".%[%[") or link:find("%]%].") then accel = nil end link = tag_text(link, lang, data.sc[i], face, get_css_classes(lang, data.tr[i], accel, nowrap)) else --[[ No term to show. Is there at least a transliteration we can work from? ]] link = request_script(lang, data.sc[i]) -- No link to show, and no transliteration either. Show a term request (unless it's a substrate, as they rarely take terms). if (link == "" or (not data.tr[i]) or data.tr[i] == "-") and lang:getFamilyCode() ~= "qfa-sub" then -- If there are multiple terms, break the loop instead. if i > 1 then remove(outparts) break elseif NAMESPACE ~= "แม่แบบ" then insert(cats, lang:getFullName() .. " term requests") end link = "<small>[Term?]</small>" end end insert(outparts, link) if i < #terms then insert(outparts, "<span class=\"Zsym mention\" style=\"font-size:100%;\">&nbsp;/ </span>") end end -- When suppress_tr is true, do not show or generate any transliteration if data.suppress_tr then data.tr[1] = nil else -- TODO: Currently only handles the first transliteration, pending consensus on how to handle multiple translits for multiple forms, as this is not always desirable (e.g. traditional/simplified Chinese). local langcode = lang:getCode() --เพิ่มเอง if data.tr[1] == "" or data.tr[1] == "-" then data.tr[1] = nil elseif langcode == "eo" then --เพิ่มเอง data.tr[1] = require("Module:eo-translit").tr( export.remove_links(data.alt[1] or data.term[1]), langcode, lang:findBestScript(data.alt[1] or data.term[1])) elseif langcode == "es" then --เพิ่มเอง data.tr[1] = require("Module:es-translit").tr( export.remove_links(data.alt[1] or data.term[1]), langcode, lang:findBestScript(data.alt[1] or data.term[1])) elseif langcode == "la" or langcode:find("^la%-") or langcode:find("^[CELMNRV]L%.$") then --เพิ่มเอง data.tr[1] = require("Module:la-translit").tr( export.remove_links(data.alt[1] or data.term[1]), langcode, lang:findBestScript(data.alt[1] or data.term[1])) elseif langcode == "vi" then --เพิ่มเอง data.tr[1] = require("Module:vi-translit").tr( export.remove_links(data.alt[1] or data.term[1]), langcode, data.lang:findBestScript(data.alt[1] or data.term[1])) else local phonetic_extraction = load_data("Module:links/data").phonetic_extraction phonetic_extraction = phonetic_extraction[lang:getCode()] or phonetic_extraction[lang:getFullCode()] if phonetic_extraction then data.tr[1] = data.tr[1] or require(phonetic_extraction).getTranslit(export.remove_links(data.alt[1] or data.term[1])) elseif (data.term[1] or data.alt[1]) and data.sc[1]:isTransliterated() then -- Track whenever there is manual translit. The categories below like 'terms with redundant transliterations' -- aren't sufficient because they only work with reference to automatic translit and won't operate at all in -- languages without any automatic translit, like Persian and Hebrew. if data.tr[1] then local full_code = lang:getFullCode() track("manual-tr", full_code) end if not never_call_transliteration_module then -- Try to generate a transliteration. local text = data.alt[1] or data.term[1] if not link_tr then text = export.remove_links(text, true) end local automated_tr = lang:transliterate(text, data.sc[1]) if automated_tr then local manual_tr = data.tr[1] if manual_tr then if export.remove_links(manual_tr) == export.remove_links(automated_tr) then insert(cats, lang:getFullName() .. " terms with redundant transliterations") else -- Prevents Arabic root categories from flooding the tracking categories. if NAMESPACE ~= "หมวดหมู่" then insert(cats, lang:getFullName() .. " terms with non-redundant manual transliterations") end end end if not manual_tr or lang:overrideManualTranslit(data.sc[1]) then data.tr[1] = automated_tr end end end end end end -- Link to the transliteration entry for languages that require this if data.tr[1] and link_tr and not data.tr[1]:match("%[%[(.-)%]%]") then data.tr[1] = simple_link( data.tr[1], nil, nil, lang, get_script("Latn"), nil, cats, no_alt_ast, suppress_redundant_wikilink_cat ) elseif data.tr[1] and not link_tr then -- Remove the pseudo-HTML tags added by remove_links. data.tr[1] = data.tr[1]:gsub("</?link>", "") end if data.tr[1] and not umatch(data.tr[1], "[^%s%p]") then data.tr[1] = nil end insert(outparts, export.format_link_annotations(data, face)) if data.pretext then insert(outparts, 1, data.pretext) end if data.posttext then insert(outparts, data.posttext) end local categories = cats[1] and format_categories(cats, lang, "-", nil, nil, data.sc) or "" local output = concat(outparts) if data.show_decorations then output = add_text_decorations(output, data, lang) end return output .. categories end --[==[ Strip links by replacing all wikilinks with their displayed text, and remove any categories. This function can be invoked either from a template or from another module. Specifically, this function deletes category links, the targets of piped links, and any double square brackets involved in links (other than file links, which are untouched). If `tag` is set, then any links removed will be given pseudo-HTML tags, which allow the substitution functions in [[Module:languages]] to properly subdivide the text in order to reduce the chance of substitution failures in modules which scrape pages like [[Module:zh-translit]]. (FIXME: This is quite hacky. We probably want this to be integrated into [[Module:languages]], but we can't do that until we know that nothing is pushing pipe linked transliterations through it for languages which don't have link_tr set.) * `<nowiki>[[page|displayed text]]</nowiki>` &rarr; `displayed text` * `<nowiki>[[page and displayed text]]</nowiki>` &rarr; `page and displayed text` * `<nowiki>[[Category:English lemmas|WORD]]</nowiki>` &rarr; ''(nothing)'' ]==] function export.remove_links(text, tag) if type(text) == "table" then text = text.args[1] end if not text or text == "" then return "" end text = text :gsub("%[%[", "\1") :gsub("%]%]", "\2") -- Parse internal links for the display text. text = text:gsub("(\1)([^\1\2]-)(\2)", function(c1, c2, c3) -- Don't remove files. for _, false_positive in ipairs({ "file", "image" }) do if c2:lower():match("^" .. false_positive .. ":") then return c1 .. c2 .. c3 end end -- Remove categories completely. for _, false_positive in ipairs({ "category", "cat" }) do if c2:lower():match("^" .. false_positive .. ":") then return "" end end -- In piped links, remove all text before the pipe, unless it's the final character (i.e. the pipe trick), in which case just remove the pipe. c2 = c2:match("^[^|]*|(.+)") or c2:match("([^|]+)|$") or c2 if tag then return "<link>" .. c2 .. "</link>" else return c2 end end) text = text :gsub("\1", "[[") :gsub("\2", "]]") return text end function export.section_link(link) if type(link) ~= "string" then error("The first argument to section_link was a " .. type(link) .. ", but it should be a string.") elseif link:find("\\", nil, true) then track("escaped", "section_link") end local target, section = get_fragment((link:gsub("_", " "))) if not section then error("No \"#\" delineating a section name") end return simple_link( target, section, target .. " §&nbsp;" .. section ) end return export ggi9iq8l06w2rd598sz9bqnsjwxk9u2 5775181 5775179 2026-10-14T03:28:15Z Octahedron80 267 5775181 Scribunto text/plain local export = {} --[=[ [[Unsupported titles]], pages with high memory usage, extraction modules and part-of-speech names are listed at [[Module:links/data]]. Other modules used: [[Module:script utilities]] [[Module:scripts]] [[Module:languages]] and its submodules [[Module:gender and number]] [[Module:debug/track]] ]=] local anchors_module = "Module:anchors" local debug_track_module = "Module:debug/track" local decorations_module = "Module:decorations" local form_of_module = "Module:form of" local gender_and_number_module = "Module:gender and number" local languages_module = "Module:languages" local load_module = "Module:load" local memoize_module = "Module:memoize" local pages_module = "Module:pages" local roots_module = "Module:roots" local scripts_module = "Module:scripts" local script_utilities_module = "Module:script utilities" local string_encode_entities_module = "Module:string/encode entities" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local utilities_module = "Module:utilities" local concat = table.concat local find = string.find local get_current_title = mw.title.getCurrentTitle local insert = table.insert local ipairs = ipairs local match = string.match local new_title = mw.title.new local pairs = pairs local remove = table.remove local sub = string.sub local toNFC = mw.ustring.toNFC local tostring = tostring local type = type local unstrip = mw.text.unstrip local NAMESPACE = get_current_title().nsText local function anchor_encode(...) anchor_encode = require(memoize_module)(mw.uri.anchorEncode, true) return anchor_encode(...) end local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end local function decode_entities(...) decode_entities = require(string_utilities_module).decode_entities return decode_entities(...) end local function decode_uri(...) decode_uri = require(string_utilities_module).decode_uri return decode_uri(...) end -- Can't yet replace, as the [[Module:string utilities]] version no longer has automatic double-encoding prevention, which requires changes here to account for. local function encode_entities(...) encode_entities = require(string_encode_entities_module) return encode_entities(...) end local function extend(...) extend = require(table_module).extend return extend(...) end local function find_best_script_without_lang(...) find_best_script_without_lang = require(scripts_module).findBestScriptWithoutLang return find_best_script_without_lang(...) end local function format_categories(...) format_categories = require(utilities_module).format_categories return format_categories(...) end local function format_genders(...) format_genders = require(gender_and_number_module).format_genders return format_genders(...) end local function format_decorations(...) format_decorations = require(decorations_module).format_decorations return format_decorations(...) end local function get_current_L2(...) get_current_L2 = require(pages_module).get_current_L2 return get_current_L2(...) end local function get_lang(...) get_lang = require(languages_module).getByCode return get_lang(...) end local function get_script(...) get_script = require(scripts_module).getByCode return get_script(...) end local function language_anchor(...) language_anchor = require(anchors_module).language_anchor return language_anchor(...) end local function load_data(...) load_data = require(load_module).load_data return load_data(...) end local function request_script(...) request_script = require(script_utilities_module).request_script return request_script(...) end local function get_root_link_page(...) get_root_link_page = require(roots_module).get_link_page return get_root_link_page(...) end local function shallow_copy(...) shallow_copy = require(table_module).shallowCopy return shallow_copy(...) end local function split(...) split = require(string_utilities_module).split return split(...) end local function tag_text(...) tag_text = require(script_utilities_module).tag_text return tag_text(...) end local function tag_translit(...) tag_translit = require(script_utilities_module).tag_translit return tag_translit(...) end local function trim(...) trim = require(string_utilities_module).trim return trim(...) end local function u(...) u = require(string_utilities_module).char return u(...) end local function ulower(...) ulower = require(string_utilities_module).lower return ulower(...) end local function umatch(...) umatch = require(string_utilities_module).match return umatch(...) end local m_headword_data local function get_headword_data() m_headword_data = load_data("Module:headword/data") return m_headword_data end local function track(page, code) local tracking_page = "links/" .. page debug_track(tracking_page) if code then debug_track(tracking_page .. "/" .. code) end end local function field_non_empty(list, field) if not list then return nil end if type(list) ~= "table" then error(("Internal error: Wrong type for `termobj.%s`=%s, should be %s\"table\""):format( field, mw.dumpObject(list), field == "q" or field == "qq" and "\"string\" or " or "")) end return list[1] end --[=[ Add any decorations (left or right regular or accent qualifiers, labels or references) to an item. `text` is the item's text (to which to add the decorations) and `itemobj` is the object specifying the item's decorations, which should optionally contain: * left regular qualifiers in `q` (an array of strings or a single string); an empty array will be ignored; * right regular qualifiers in `qq` (an array of strings or a single string); an empty array will be ignored; * left accent qualifiers in `a` (an array of strings); an empty array will be ignored; * right accent qualifiers in `aa` (an array of strings); an empty array will be ignored; * left labels in `l` (an array of strings); an empty array will be ignored; * right labels in `ll` (an array of strings); an empty array will be ignored; * references in `refs`, an array either of strings (formatted reference text) or objects containing fields `text` (formatted reference text) and optionally `name` and/or `group`; an empty array will be ignored. `lang` is a language object and is required if any accent qualifiers or labels are given. ]=] local function add_text_decorations(text, itemobj, lang) local q = itemobj.q if type(q) == "string" then q = { q } end local qq = itemobj.qq if type(qq) == "string" then qq = { qq } end if field_non_empty(q, "q") or field_non_empty(qq, "qq") or field_non_empty(itemobj.a, "a") or field_non_empty(itemobj.aa, "aa") or field_non_empty(itemobj.l, "l") or field_non_empty(itemobj.ll, "ll") or field_non_empty(itemobj.refs, "refs") then text = format_decorations { lang = lang, text = text, q = q, qq = qq, a = itemobj.a, aa = itemobj.aa, l = itemobj.l, ll = itemobj.ll, refs = itemobj.refs, } end return text end local function selective_trim(...) -- Unconditionally trimmed charset. local always_trim = "\194\128-\194\159" .. -- U+0080-009F (C1 control characters) "\194\173" .. -- U+00AD (soft hyphen) "\226\128\170-\226\128\174" .. -- U+202A-202E (directionality formatting characters) "\226\129\166-\226\129\169" -- U+2066-2069 (directionality formatting characters) -- Standard trimmed charset. local standard_trim = "%s" .. -- (default whitespace charset) "\226\128\139-\226\128\141" .. -- U+200B-200D (zero-width spaces) always_trim -- If there are non-whitespace characters, trim all characters in `standard_trim`. -- Otherwise, only trim the characters in `always_trim`. selective_trim = function(text) if text == "" then return text end local trimmed = trim(text, standard_trim) if trimmed ~= "" then return trimmed end return trim(text, always_trim) end return selective_trim(...) end local function escape(text, str) local rep repeat text, rep = text:gsub("\\\\(\\*" .. str .. ")", "\5%1") until rep == 0 return (text:gsub("\\" .. str, "\6")) end local function unescape(text, str) return (text :gsub("\5", "\\") :gsub("\6", str)) end local function has_unescaped_carets(text) return escape(text, "^"):find("^", 1, true) ~= nil end local function assert_caret_capitalization_allowed(text, sc) if sc:forbidsCaretCapitalization() and has_unescaped_carets(text) then error(("Caret (^) cannot be used to capitalize transliterations for the %s script " .. "(see caret_capitalization in [[Module:scripts/data]]).") :format(sc:getCanonicalName()), 2) end end -- Remove bold, italics, soft hyphens, strip markers and HTML tags. local function remove_formatting(str) str = str :gsub("('*)'''(.-'*)'''", "%1%2") :gsub("('*)''(.-'*)''", "%1%2") :gsub("­", "") return (unstrip(str) :gsub("<[^<>]+>", "")) end --[==[ Split `text` on double slashes (taking account of escaping backslashes). Return a list of split items. ]==] function export.split_on_slashes(text) if text:find("\\", nil, true) then track("escaped", "split_on_slashes") end text = split(escape(text, "//"), "//", true) or {} for i, v in ipairs(text) do text[i] = unescape(v, "//") if v == "" then text[i] = false end end return text end --[==[ If `text` is a wikilink (i.e. in the form `<nowiki>[[foo]]</nowiki>` or `<nowiki>[[foo|bar]]</nowiki>`), return the link target and display text. If the wikilink is single-part, the display text will be the same as the link target. If the text is not in wikilink form, return {nil} for both link target and display text. ]==] function export.get_wikilink_parts(text) -- TODO: replace `allow_bad_target` with `allow_unsupported`, with support for links to unsupported titles, including escape sequences. if ( -- Filters out anything but "[[...]]" with no intermediate "[[" or "]]". not match(text, "^()%[%[") or -- Faster than sub(text, 1, 2) ~= "[[". find(text, "[[", 3, true) or find(text, "]]", 3, true) ~= #text - 1 ) then return nil, nil end local pipe = find(text, "|", 3, true) local title, display if pipe then title, display = sub(text, 3, pipe - 1), sub(text, pipe + 1, -3) else title = sub(text, 3, -3) display = title end return title, display end -- Does the work of export.get_fragment, but can be called directly to avoid unnecessary checks for embedded links. local function get_fragment(text) text = escape(text, "#") -- Replace numeric character references with the corresponding character (&#39; → '), -- as they contain #, which causes the numeric character reference to be -- misparsed (wa'a → wa&#39;a → pagename wa&, fragment 39;a). text = decode_entities(text) local target, fragment = text:match("^(.-)#(.+)$") target = target or text target = unescape(target, "#") fragment = fragment and unescape(fragment, "#") or nil return target, fragment end --[==[ Given a link target possibly containing a fragment (i.e. a pound sign and following anchor), return two values, the actual target (minus the fragment) and the fragment, or {nil} if there is no fragment. ]==] function export.get_fragment(text) if text:find("\\", nil, true) then track("escaped", "get_fragment") end -- If there are no embedded links, process input. local open = find(text, "[[", nil, true) if not open then return get_fragment(text) end local close = find(text, "]]", open + 2, true) if not close then return get_fragment(text) -- If there is one, but it's redundant (i.e. encloses everything with no pipe), remove and process. elseif open == 1 and close == #text - 1 and not find(text, "|", 3, true) then return get_fragment(sub(text, 3, -3)) end -- Otherwise, return the input. return text, nil end --[==[ Given a link target as passed to `full_link()`, get the actual page that the target refers to. This removes bold, italics, strip markets and HTML; calls `makeEntryName()` for the language in question; converts targets beginning with `*` to the Reconstruction namespace; and converts appendix-constructed languages to the Appendix namespace. Returns up to three values: # the actual page to link to, or {nil} to not link to anything; # how the target should be displayed as, if the user didn't explicitly specify any display text; generally the same as the original target, but minus any anti-asterisk !!; # the value `true` if the target had a backslash-escaped * in it (FIXME: explain this more clearly). FIXME: This should not be calling deprecated `makeEntryName()` and should always return the same number of values. ]==] function export.get_link_page_with_auto_display(target, lang, sc, plain) local orig_target = target if not target then return nil elseif target:find("\\", nil, true) then track("escaped", "get_link_page") end target = remove_formatting(target) if target:sub(1, 1) == ":" then track("initial colon") -- FIXME, the auto_display (second return value) should probably remove the colon return target:sub(2), orig_target end local prefix = target:match("^(.-):") -- Convert any escaped colons target = target:gsub("\\:", ":") if prefix then -- If this is an a link to another namespace or an interwiki link, ensure there's an initial colon and then -- return what we have (so that it works as a conventional link, and doesn't do anything weird like add the term -- to a category.) prefix = ulower(trim(prefix)) if prefix ~= "" and ( load_data("Module:data/namespaces")[prefix] or load_data("Module:data/interwikis")[prefix] ) then return target, orig_target end end -- Check if the term is reconstructed and remove any asterisk. Also check for anti-asterisk (!!). -- Otherwise, handle the escapes. local reconstructed, escaped, anti_asterisk if not plain then target, reconstructed = target:gsub("^%*(.)", "%1") if reconstructed == 0 then target, anti_asterisk = target:gsub("^!!(.)", "%1") if anti_asterisk == 1 then -- Remove !! from original. FIXME! We do it this way because the call to remove_formatting() above -- may cause non-initial !! to be interpreted as anti-asterisks. We should surely move the -- remove_formatting() call later. orig_target = orig_target:gsub("^!!", "") end end end target, escaped = target:gsub("^(\\-)\\%*", "%1*") if not (sc and sc:getCode() ~= "None") then sc = lang:findBestScript(target) end -- Remove carets if they are used to capitalize parts of transliterations (unless they have been escaped). if target:match("%^") then assert_caret_capitalization_allowed(target, sc) if sc:hasCaretCapitalization() then target = escape(target, "^") :gsub("%^", "") target = unescape(target, "^") end end -- Get the entry name for the language. target = lang:makeEntryName(target, sc, reconstructed == 1 or lang:hasType("appendix-constructed")) -- If the link contains unexpanded template parameters, then don't create a link. if target:match("{{{.-}}}") then -- FIXME: Should we return the original target as the default display value (second return value)? return nil end -- Link to appendix for reconstructed terms and terms in appendix-only languages. Plain links interpret * -- literally, however. if reconstructed == 1 then if lang:getFullCode() == "und" then -- Return the original target as default display value. If we don't do this, we wrongly get -- [Term?] displayed instead. return nil, orig_target end target = "การสืบสร้าง:" .. lang:getFullName() .. "/" .. target -- Reconstructed languages and substrates require an initial *. elseif anti_asterisk ~= 1 and (lang:hasType("reconstructed") or lang:getFamilyCode() == "qfa-sub") then error(("The specified language %s is unattested, while the term '%s' does not begin with '*' to indicate that it is reconstructed.") : format(lang:getCanonicalName(), orig_target)) elseif lang:hasType("appendix-constructed") then target = "ภาคผนวก:" .. lang:getFullName() .. "/" .. target else target = target end return target, orig_target, escaped > 0 end function export.get_link_page(target, lang, sc, plain) local target, auto_display, escaped = export.get_link_page_with_auto_display(target, lang, sc, plain) return target, escaped end -- Make a link from a given link's parts. local function make_link(link, lang, sc, id, isolated, cats, no_alt_ast, plain) -- Convert percent encoding to plaintext. link.target = link.target and decode_uri(link.target, "PATH") link.fragment = link.fragment and decode_uri(link.fragment, "PATH") -- Find fragments (if one isn't already set). -- Prevents {{l|en|word#Etymology 2|word}} from linking to [[word#Etymology 2#English]]. -- # can be escaped as \#. if link.target and link.fragment == nil then link.target, link.fragment = get_fragment(link.target) end -- Process the target local auto_display, escaped link.target, auto_display, escaped = export.get_link_page_with_auto_display(link.target, lang, sc, plain) -- Create a default display form. -- If the target is "" then it's a link like [[#English]], which refers to the current page. if auto_display == "" then auto_display = (m_headword_data or get_headword_data()).pagename end -- If the display is the target and the reconstruction * has been escaped, remove the escaping backslash. if escaped then auto_display = auto_display:gsub("\\([^\\]*%*)", "%1", 1) end -- Process the display form. if link.display then local orig_display = link.display link.display = lang:makeDisplayText(link.display, sc, true) if cats then auto_display = lang:makeDisplayText(auto_display, sc) -- If the alt text is the same as what would have been automatically generated, then the alt parameter is -- redundant (e.g. {{l|en|foo|foo}}, {{l|en|w:foo|foo}}, but not {{l|en|w:foo|w:foo}}). If they're -- different, but the alt text could have been entered as the term parameter without it affecting the target -- page, then the target parameter is redundant (e.g. {{l|ru|фу|фу́}}). If `no_alt_ast` is true, use pcall -- to catch the error which will be thrown if this is a reconstructed lang and the alt text doesn't have *. if link.display == auto_display then insert(cats, lang:getFullName() .. " links with redundant alt parameters") else local ok, check if no_alt_ast then ok, check = pcall(export.get_link_page, orig_display, lang, sc, plain) else ok = true check = export.get_link_page(orig_display, lang, sc, plain) end if ok and link.target == check then insert(cats, lang:getFullName() .. " links with redundant target parameters") end end end else link.display = lang:makeDisplayText(auto_display, sc) end if not link.target then return link.display end -- If the target is the same as the current page, there is no sense id -- and either the language code is "und" or the current L2 is the current -- language then return a "self-link" like the software does. if link.target == get_current_title().prefixedText then local fragment, current_L2 = link.fragment, get_current_L2() if ( fragment and fragment == current_L2 or not (id or fragment) and (lang:getFullCode() == "und" or lang:getFullName() == current_L2) ) then return tostring(mw.html.create("strong") :addClass("selflink") :wikitext(link.display)) end end -- Add fragment. Do not add a section link to "Undetermined", as such sections do not exist and are invalid. -- TabbedLanguages handles links without a section by linking to the "last visited" section, but adding -- "Undetermined" would break that feature. For localized prefixes that make syntax error, please use the -- format: ["xyz"] = true. local prefix = link.target:match("^:*([^:]+):") prefix = prefix and ulower(prefix) if prefix ~= "category" and not (prefix and load_data("Module:data/interwikis")[prefix]) then if (link.fragment or link.target:sub(-1) == "#") and not plain then track("fragment", lang:getFullCode()) if cats then insert(cats, lang:getFullName() .. " links with manual fragments") end end if not link.fragment then if id then link.fragment = lang:getFullCode() == "und" and anchor_encode(id) or language_anchor(lang, id) elseif lang:getFullCode() ~= "und" and not (link.target:match("^ภาคผนวก:") or link.target:match("^การสืบสร้าง:")) then --link.fragment = anchor_encode(lang:getFullName()) link.fragment = anchor_encode(lang:getCategoryName()) --th end end end -- Put inward-facing square brackets around a link to isolated spacing character(s). if isolated and link.display[1] and not umatch(decode_entities(link.display), "%S") then link.display = "&#x5D;" .. link.display .. "&#x5B;" end link.target = link.target:gsub("^(:?)(.*)", function(m1, m2) return m1 .. encode_entities(m2, "#%&+/:<=>@[\\]_{|}") end) link.fragment = link.fragment and encode_entities(remove_formatting(link.fragment), "#%&+/:<=>@[\\]_{|}") return "[[" .. link.target:gsub("^[^:]", ":%0") .. (link.fragment and "#" .. link.fragment or "") .. "|" .. link.display .. "]]" end -- Split a link into its parts. local function parse_link(linktext) local link = { target = linktext } local target = link.target link.target, link.display = target:match("^(..-)|(.+)$") if not link.target then link.target = target link.display = target end -- There's no point in processing these, as they aren't real links. local target_lower = link.target:lower() for _, false_positive in ipairs({ "category", "cat", "file", "image" }) do if target_lower:match("^" .. false_positive .. ":") then return nil end end link.display = decode_entities(link.display) link.target, link.fragment = get_fragment(link.target) -- So that make_link does not look for a fragment again. if not link.fragment then link.fragment = false end return link end local function check_params_ignored_when_embedded(alt, lang, id, cats) if alt then track("alt-ignored") if cats then insert(cats, lang:getFullName() .. " links with ignored alt parameters") end end if id then track("id-ignored") if cats then insert(cats, lang:getFullName() .. " links with ignored id parameters") end end end -- Find embedded links and ensure they link to the correct section. local function process_embedded_links(text, alt, lang, sc, id, cats, no_alt_ast, plain) -- Process the non-linked text. text = lang:makeDisplayText(text, sc, true) -- If the text begins with * and another character, then act as if each link begins with *. However, don't do this if the * is contained within a link at the start. E.g. `|*[[foo]]` would set all_reconstructed to true, while `|[[*foo]]` would not. local all_reconstructed = false if not plain then -- anchor_encode removes links etc. if anchor_encode(text):sub(1, 1) == "*" then all_reconstructed = true end -- Otherwise, handle any escapes. text = text:gsub("^(\\-)\\%*", "%1*") end check_params_ignored_when_embedded(alt, lang, id, cats) local function process_link(space1, linktext, space2) local capture = "[[" .. linktext .. "]]" local link = parse_link(linktext) -- Return unprocessed false positives untouched (e.g. categories). if not link then return capture end if all_reconstructed then if link.target:find("^!!") then -- Check for anti-asterisk !! at the beginning of a target, indicating that a reconstructed term -- wants a part of the term to link to a non-reconstructed term, e.g. Old English -- {{ang-noun|m|head=*[[!!Crist|Cristes]] [[!!mæsseǣfen]]}}. link.target = link.target:sub(3) -- Also remove !! from the display, which may have been copied from the target (as in mæsseǣfen in -- the example above). link.display = link.display:gsub("^!!", "") elseif not link.target:match("^%*") then link.target = "*" .. link.target end end linktext = make_link(link, lang, sc, id, false, nil, no_alt_ast, plain) :gsub("^%[%[", "\3") :gsub("%]%]$", "\4") return space1 .. linktext .. space2 end -- Use chars 1 and 2 as temporary substitutions, so that we can use charsets. These are converted to chars 3 and 4 by process_link, which means we can convert any remaining chars 1 and 2 back to square brackets (i.e. those not part of a link). text = text :gsub("%[%[", "\1") :gsub("%]%]", "\2") -- If the script uses ^ to capitalize transliterations, make sure that any carets preceding links are on the inside, so that they get processed with the following text. if text:find("^", nil, true) then assert_caret_capitalization_allowed(text, sc) if sc:hasCaretCapitalization() then text = escape(text, "^") :gsub("%^\1", "\1%^") text = unescape(text, "^") end end text = text:gsub("\1(%s*)([^\1\2]-)(%s*)\2", process_link) -- Remove the extra * at the beginning of a language link if it's immediately followed by a link whose display begins with * too. if all_reconstructed then text = text:gsub("^%*\3([^|\1-\4]+)|%*", "\3%1|*") end return (text :gsub("[\1\3]", "[[") :gsub("[\2\4]", "]]") ) end local function simple_link(term, fragment, alt, lang, sc, id, cats, no_alt_ast, suppress_redundant_wikilink_cat) local plain if lang == nil then lang, plain = get_lang("und"), true end -- Get the link target and display text. If the term is the empty string, treat the input as a link to the current page. if term == "" then term = get_current_title().prefixedText elseif term then local new_term, new_alt = export.get_wikilink_parts(term) if new_term then check_params_ignored_when_embedded(alt, lang, id, cats) -- [[|foo]] links are treated as plaintext "[[|foo]]". -- FIXME: Pipes should be handled via a proper escape sequence, as they can occur in unsupported titles. if new_term == "" then term, alt = nil, term else local title = new_title(new_term) if title then local ns = title.namespace -- File: and Category: links should be returned as-is. if ns == 6 or ns == 14 then return term end end term, alt = new_term, new_alt if cats then if not (suppress_redundant_wikilink_cat and suppress_redundant_wikilink_cat(term, alt)) then insert(cats, lang:getFullName() .. " links with redundant wikilinks") end end end end end if alt then alt = selective_trim(alt) if alt == "" then alt = nil end end -- If there's nothing to process, return nil. if not (term or alt) then return nil end -- If there is no script, get one. if not sc then sc = lang:findBestScript(alt or term) end -- Embedded wikilinks need to be processed individually. if term then local open = find(term, "[[", nil, true) if open and find(term, "]]", open + 2, true) then return process_embedded_links(term, alt, lang, sc, id, cats, no_alt_ast, plain) end term = selective_trim(term) end -- If not, make a link using the parameters. return make_link({ target = term, display = alt, fragment = fragment }, lang, sc, id, true, cats, no_alt_ast, plain) end --[==[ Create a basic link to the given term. It links to the language section (such as `==English==`), but it does not add language and script wrappers, so any code that uses this function should call `[[Module:script utilities#tag_text]]` to add such wrappers itself at some point. The first argument, `data`, may contain the following items, a subset of the items used in the `data` argument of `##full_link`. If any other items are included, they are ignored. { { term = "entry_to_link_to", alt = "link_text_or_displayed_text", lang = language_object, sc = script_object, fragment = "link_fragment", id = "sense_id", no_alt_ast = boolean, suppress_redundant_wikilink_cat = function(term, alt) -> boolean, cats = nil or {}, -- NOTE: If given, will be side-effected to return categories to add the page to. } } Specifically: * `term`: Term to turn into a link. This is generally the name of a page, possibly with extra diacritics added (e.g. length marks in Latin or Old English terms, accents in Russian terms, vowel diacritics in Arabic terms, etc.), which are stripped to determine the actual pagename. The term can contain wikilinks already embedded in it. These are processed individually just like a single link would be. The `alt` argument is ignored in this case. * `alt`: The alternative display for the link, if different from the linked page. If this is {nil}, the `term` argument is used instead (much like regular wikilinks). If `term` contains wikilinks in it, this argument is ignored and has no effect. (Links in which the alt is ignored are tracked with the tracking template {{whatlinkshere|tracking=links/alt-ignored}}.) * `lang` ('''required'''): The [[Module:languages#Language objects|language object]] for the term being linked. The link or links in `term` will normally have their fragment set to point to the canonical name (see {{tl|language data documentation}}) of `lang` (or, if it is an etymology-only language, to the canonical name of its L2 parent). (However, if `id` is defined, the fragment will point to a language-specific sense ID corresponding to this field, which in turn will be overridden by `fragment` if specified.) * `sc`: The [[Module:scripts#Script objects|script object]] for the term being linked. This rarely needs to be specified because it is autodetected based on `term` or `alt`, and the detection is usually correct. It is used to determine how to convert the term into a pagename, possibly by stripping certain diacritics from the term's text. * `fragment`: If specified, overrides the fragment in the generated link (i.e. the portion after `#`, which determines where on the page to go to when the link is clicked). If not specified, the fragment is generated from `id` (if given) or otherwise from `lang`. * `id`: Sense ID string. If this argument is defined, the link will point to a language-specific sense ID ({{ll|en|identifier|id=HTML}}) created by the template {{temp|senseid}}. The fragment for a sense ID consists of the language's canonical name, a hyphen (`-`), and the string that was supplied as the `id` argument. This is useful when a term has more than one sense in a language. If the `term` argument contains wikilinks, this argument is ignored. (Links in which the sense ID is ignored are tracked with the tracking template {{whatlinkshere|tracking=links/id-ignored}}.) * `no_alt_ast`: This is the same as `no_alt_ast` in `##full_link()`. See that function for more information. * `suppress_redundant_wikilink_cat`: This is the same as `suppress_redundant_wikilink_cat` in `##full_link()`. See that function for more information. * `cats`: This should be either {nil} or an empty list. In the latter case, tracking categories will be added to the list when appropriate. The caller can choose to add the page to those categories (as is done by ##full_link()`). The following special options are processed for each link (both simple terms and with embedded wikilinks): * The target page name will be processed by stripping certain diacritics (as mentioned above) and converting the resulting ''logical'' pagename to a ''physical'' pagename (which will be different from the logical pagename in the case of pages with unsupported characters in them and certain overly large pages, such as [[a]], that are split into parts). * If the term starts with `*`, then it is considered a reconstructed term, and a link to the `Reconstruction:` namespace will be created. If the text contains embedded wikilinks, then `*` is automatically applied to each one individually, while preserving the displayed form of each link as it was given. This allows linking to phrases containing multiple reconstructed terms, while only showing the `*` once at the beginning. * If the text starts with `:`, then the link is treated as "raw" and the above steps are skipped. This can be used in rare cases where the page name begins with `*` or if diacritics should not be stripped. For example: ** {{tl|l|en|*nix}} links to the nonexistent page [[Reconstruction:English/nix]] (`*` is interpreted as a reconstruction), but {{tl|l|en|:*nix}} links to [[*nix]]. ** {{tl|l|sl|Franche-Comté}} links to the nonexistent page [[Franche-Comte]] (`é` is converted to `e` by the diacritic-stripping process), but {{tl|l|sl|:Franche-Comté}} links to [[Franche-Comté]]. ]==] function export.language_link(data) if type(data) ~= "table" then error( "The first argument to the function language_link must be a table. See [[Module:links/documentation]] for more information.") elseif data.term and data.term:find("\\", nil, true) or data.alt and data.alt:find("\\", nil, true) then track("escaped", "language_link") end -- Categorize links to "und". local lang, cats = data.lang, data.cats if cats and lang:getCode() == "und" then insert(cats, "Undetermined language links") end return simple_link( data.term, data.fragment, data.alt, lang, data.sc, data.id, cats, data.no_alt_ast, data.suppress_redundant_wikilink_cat ) end function export.plain_link(data) if type(data) ~= "table" then error( "The first argument to the function plain_link must be a table. See Module:links/documentation for more information.") elseif data.term and data.term:find("\\", nil, true) or data.alt and data.alt:find("\\", nil, true) then track("escaped", "plain_link") end return simple_link( data.term, data.fragment, data.alt, nil, data.sc, data.id, data.cats, data.no_alt_ast, data.suppress_redundant_wikilink_cat ) end --[==[Replace any links with links to the correct section, but don't link the whole text if no embedded links are found. Returns the display text form.]==] function export.embedded_language_links(data) if type(data) ~= "table" then error( "The first argument to the function embedded_language_links must be a table. See Module:links/documentation for more information.") elseif data.term and data.term:find("\\", nil, true) or data.alt and data.alt:find("\\", nil, true) then track("escaped", "embedded_language_links") end local term, lang, sc = data.term, data.lang, data.sc -- If we don't have a script, get one. if not sc then sc = lang:findBestScript(term) end -- Do we have embedded wikilinks? If so, they need to be processed individually. local open = find(term, "[[", nil, true) if open and find(term, "]]", open + 2, true) then return process_embedded_links(term, data.alt, lang, sc, data.id, data.cats, data.no_alt_ast) end -- If not, return the display text. term = selective_trim(term) -- FIXME: Double-escape any percent-signs, because we don't want to treat non-linked text as having percent-encoded -- characters. This is a hack: percent-decoding should come out of [[Module:languages]] and only dealt with in this -- module, as it's specific to links. term = term:gsub("%%", "%%25") return lang:makeDisplayText(term, sc, true) end function export.mark(text, item_type, face, lang) local tag = { "", "" } if item_type == "gloss" then tag = { '<span class="mention-gloss-double-quote">“</span><span class="mention-gloss">', '</span><span class="mention-gloss-double-quote">”</span>' } if type(text) == "string" and text:match("^''[^'].*''$") then -- Temporary tracking for mention glosses that are entirely italicized or bolded, which is probably -- wrong. (Note that this will also find bolded mention glosses since they use triple apostrophes.) track("italicized-mention-gloss", lang and lang:getFullCode() or nil) end elseif item_type == "tr" then if face == "term" then tag = { '<span lang="' .. lang:getFullCode() .. '" class="tr mention-tr Latn">', '</span>' } else tag = { '<span lang="' .. lang:getFullCode() .. '" class="tr Latn">', '</span>' } end elseif item_type == "ts" then -- \226\129\160 = word joiner (zero-width non-breaking space) U+2060 tag = { '<span class="ts mention-ts Latn">/\226\129\160', '\226\129\160/</span>' } elseif item_type == "pos" then tag = { '<span class="ann-pos">', '</span>' } elseif item_type == "non-gloss" then tag = { '<span class="ann-non-gloss">', '</span>' } elseif item_type == "annotations" then tag = { '<span class="mention-gloss-paren annotation-paren">(</span>', '<span class="mention-gloss-paren annotation-paren">)</span>' } elseif item_type == "infl" then tag = { '<span class="ann-infl">', '</span>' } end if type(text) == "string" then return tag[1] .. text .. tag[2] else return "" end end --[=[ Implementation of `format_transliteration` and `format_transcription`. The implementation is identical except that the field containing the transliteration or transcription may vary and is specified in `field`, and the way a given transliteration or transcription is tagged may vary and is controlled by `tag_fn`, which is passed three parameters: `text` (the transliteration or transcription), `lang` (the language passed in) and `face` (the face passed in). On input, `item` is the transliteration or transcription or list of such objects; `field` is either {"tr"} or {"ts"}; `tag_fn` is a function of three parameters to tag the item, as described above; `lang` is the language object of the term whose transliteration or transcription is specified; and `face` is a string indicating how to display the item (generally only the strings {"term"} and {"default"} are recognized). ]=] local function format_transliteration_or_transcription(item, field, tag_fn, lang, item_face) if type(item) == "string" then return tag_fn(item, lang, item_face) end local formatted_items = {} for _, itemobj in ipairs(item) do local tagged_item = tag_fn(itemobj[field], lang, item_face) tagged_item = add_text_decorations(tagged_item, item, lang) insert(formatted_items, tagged_item) end if formatted_items[2] then -- FIXME: This should be customizable. return concat(formatted_items, " <i>or</i> ") else return formatted_items[1] end end --[==[ Format a transliteration string or list of transliteration objects. `tr` contains the transliteration(s), which for forward compatibility reasons can only be either a single transliteration string or a list of transliteration objects (each of which has a `tr` field holding the transliteration and optional fields `q`, `qq`, `l`, `ll` and/or `refs`). This correctly handles multiple transliterations as well as decorations (qualifiers, labels or references) attached to transliterations. ]==] function export.format_transliteration(tr, lang, face) return format_transliteration_or_transcription(tr, "tr", tag_translit, lang, face) end local function tag_transcription(ts, _lang, _face) return export.mark(ts, "ts") end --[==[ Format a transcription string or list of transcription objects. `ts` contains the transcription(s), which for forward compatibility reasons can only be either a single transcription string or a list of transcription objects (each of which has a `ts` field holding the transcription and optional decoration fields `q`, `qq`, `l`, `ll` and/or `refs`). This correctly handles multiple transcriptions as well as decorations (qualifiers, labels or references) attached to transcriptions. ]==] function export.format_transcription(ts, lang, face) return format_transliteration_or_transcription(ts, "ts", tag_transcription, lang, face) end local pos_tags --[==[ Format the annotations that are displayed with a link created by `full_link()`. Annotations are the extra bits of information that are displayed following the linked term, and include things such as gender, transliteration, gloss, etc. The first argument is a table with some or all of the following keys (all are optional): * `interwiki`: An interwiki link. This is used for links in translation tables to the corresponding term in another Wiktionary. If specified, it should be a fully formatted link and is inserted as-is at the beginning of the output. See the `interwiki()` function in [[Module:translations]]. * `genders`: Table containing a list of gender specifications in the style of [[Module:gender and number]]. If specified, these are formatted using `format_genders()` in [[Module:gender and number]] and the result inserted at the beginning of the output, following any interwiki link and (in all cases) directly after a no-break space. * `tr`: Transliteration or transliterations. Currently, this is always a one-item list. It is a list because of potential support for per-alternant transliterations due to the multiple alternants (separated by `//`) that can be specified in `term` or `alt`. The item in the list can be either a string or a list of transliteration objects (see `format_transliteration()`). * `ts`: Transcription or transcriptions. Like `tr`, this is currently always a one-item list, with the item being either a single string or a list of transcription objects, as described in `format_transcription()`. * `gloss`: Gloss that translates the term in the link. * `pos`: Part of speech of the linked term. If the given argument matches one of the aliases in `pos_aliases` in [[Module:headword/data]], or consists of a part of speech or alias followed by `f` (for a non-lemma form), expand it appropriately. Otherwise, just show the given text as it is. With `root`, the term links to the page where [[Module:roots]] keeps the language's roots, for languages that keep them outside the main namespace (`Appendix:Arabic roots/…`). * `infl`: A list of tags, each a string. Multiple tag sets may be encoded in this list by separating them with an element consisting of a semicolon. If there are multiple tag sets, they are formatted individually and separated by a semicolon + space. * `ng`: Arbitrary non-gloss descriptive text for the link. This should be used in preference to putting descriptive text in `gloss` or `pos`. * `lit`: Literal meaning of the term, if the usual meaning is figurative or idiomatic. * `postprocess_annotations`: A function to postprocess the annotations, after they have been formatted (see below). The `interwiki` and `genders` properties are formatted specially, and `postprocess_annotations` is a callback function rather than an item to display; all others are formatted (each in their own way), separated by commas and placed inside of parentheses (except that if both transliteration and transcription are present, they are separated by a space). The order of the annotations is `tr`+`ts`, `gloss`, `pos`, `infl`, `ng` and `lit`. The `postprocess_annotations` function, if supplied, is passed a single object, a table with two keys `data` (the `data` object passed into `format_link_annotations()`) and `annotations` (the formatted annotations, prior to being concatenated). It should side-effect the `annotations` list, e.g. by inserting more annotations. (It is used to handle nested inflections in [[Module:headword]]. FIXME: It should probably be generalized so that it can return the final formatted string, to allow for e.g. changing the way the annotations are formatted.) * The second argument is a string controlling the "face" that the terms are displayed in. Currently it only affects transliteration and transcription and only when the value {"term"} is passed in, in which case those annotations are displayed italicized. ]==] function export.format_link_annotations(data, face) local output = {} -- Interwiki link if data.interwiki then insert(output, data.interwiki) end -- Genders if type(data.genders) ~= "table" then data.genders = { data.genders } end if data.genders and data.genders[1] then local genders, gender_cats = format_genders(data.genders, data.lang) insert(output, "&nbsp;" .. genders) if gender_cats then local cats = data.cats if cats then extend(cats, gender_cats) end end end local annotations = {} -- Transliteration and transcription local tr = data.tr and data.tr[1] or nil local ts = data.ts and data.ts[1] or nil if tr or ts then local item_face if face == "term" then item_face = face else item_face = "default" end local formatted_tr = tr and export.format_transliteration(tr, data.lang, item_face) or nil local formatted_ts = ts and export.format_transcription(ts, data.lang, item_face) or nil if formatted_tr and formatted_ts then insert(annotations, {text = formatted_tr .. " " .. formatted_ts}) else insert(annotations, {text = formatted_tr or formatted_ts}) end end -- Gloss/translation if data.gloss then insert(annotations, {text = export.mark(data.gloss, "gloss")}) end -- Part of speech if data.pos then -- debug category for pos= containing transcriptions if data.pos:match("/[^><]-/") then data.pos = data.pos .. "[[Category:links likely containing transcriptions in pos]]" end -- Canonicalize part of speech aliases as well as non-lemma aliases like 'nf' or 'nounf' for "noun form". pos_tags = pos_tags or (m_headword_data or get_headword_data()).pos_aliases local pos = pos_tags[data.pos] if not pos and data.pos:find("f$") then local pos_form = data.pos:sub(1, -2) -- We only expand something ending in 'f' if the result is a recognized non-lemma POS. pos_form = (pos_tags[pos_form] or pos_form) .. " form" if (m_headword_data or get_headword_data()).nonlemmas[pos_form .. "s"] then pos = pos_form end end insert(annotations, {text = export.mark(pos or data.pos, "pos")}) end -- Inflection data if data.infl then local m_form_of = require(form_of_module) -- Split tag sets manually, since tagged_inflections creates a numbered list, and we do not want that. local infl_outputs = {} local tag_sets = m_form_of.split_tag_set(data.infl) for _, tag_set in ipairs(tag_sets) do table.insert(infl_outputs, m_form_of.tagged_inflections({ tags = tag_set, lang = data.lang, nocat = true, nolink = true, nowrap = true })) end insert(annotations, {text = export.mark(table.concat(infl_outputs, "; "), "infl")}) end -- Non-gloss text if data.ng then local separator local ng = data.ng if ng:find("^[;:]") then separator = "" elseif ng:find("^_") then ng = ng:sub(2) separator = " " end insert(annotations, {text = export.mark(ng, "non-gloss"), separator = separator}) end -- Literal/sum-of-parts meaning if data.lit then insert(annotations, {text = "แปลตามตัวอักษรว่า " .. export.mark(data.lit, "gloss")}) -- จาก ศบญ "literal translation" end -- Provide a hook to insert additional annotations such as nested inflections. if data.postprocess_annotations then data.postprocess_annotations { data = data, annotations = annotations } end if annotations[1] then local annparts = {} for i, annotation in ipairs(annotations) do local separator = annotation.separator if not separator then if i == 1 then separator = "" else separator = ", " end end insert(annparts, separator) insert(annparts, annotation.text) end insert(output, " ") insert(output, export.mark(concat(annparts), "annotations")) end return concat(output) end -- Encode certain characters to avoid various delimiter-related issues at various stages. We need to encode < and > -- because they end up forming part of CSS class names inside of <span ...> and will interfere with finding the end -- of the HTML tag. I first tried converting them to URL encoding, i.e. %3C and %3E; they then appear in the URL as -- %253C and %253E, which get mapped back to %3C and %3E when passed to [[Module:accel]]. But mapping them to &lt; -- and &gt; somehow works magically without any further work; they appear in the URL as < and >, and get passed to -- [[Module:accel]] as < and >. I have no idea who along the chain of calls is doing the encoding and decoding. If -- someone knows, please modify this comment appropriately! local accel_char_map local function get_accel_char_map() accel_char_map = { ["%"] = ".", [" "] = "_", ["_"] = u(0xFFF0), ["<"] = "&lt;", [">"] = "&gt;", } return accel_char_map end local function encode_accel_param_chars(param) return (param:gsub("[%% <>_]", accel_char_map or get_accel_char_map())) end local function encode_accel_param(prefix, param) if not param then return "" end if type(param) == "table" then local filled_params = {} -- There may be gaps in the sequence, especially for translit params. local maxindex = 0 for k in pairs(param) do if type(k) == "number" and k > maxindex then maxindex = k end end for i = 1, maxindex do filled_params[i] = param[i] or "" end -- [[Module:accel]] splits these up again. param = concat(filled_params, "*~!") end -- This is decoded again by [[WT:ACCEL]]. return prefix .. encode_accel_param_chars(param) end local function insert_if_not_blank(list, item) if item == "" then return end insert(list, item) end local function get_css_classes(lang, tr, accel, nowrap) if not accel and not nowrap then return "" end local classes = {} if accel then insert(classes, "form-of lang-" .. lang:getFullCode()) local form = accel.form if form then insert(classes, encode_accel_param_chars(form) .. "-form-of") end insert_if_not_blank(classes, encode_accel_param("gender-", accel.gender)) insert_if_not_blank(classes, encode_accel_param("pos-", accel.pos)) insert_if_not_blank(classes, encode_accel_param("transliteration-", accel.translit or (tr ~= "-" and tr or nil))) insert_if_not_blank(classes, encode_accel_param("target-", accel.target)) insert_if_not_blank(classes, encode_accel_param("origin-", accel.lemma)) insert_if_not_blank(classes, encode_accel_param("origin_transliteration-", accel.lemma_translit)) if accel.no_store then insert(classes, "form-of-nostore") end end if nowrap then insert(classes, nowrap) end return concat(classes, " ") end --[==[ Creates a full link, with annotations (see `##format_link_annotations`), in the style of {{tl|l}} or {{tl|m}}. The first argument, `data`, must be a table. It contains the various elements that can be supplied as parameters to {{tl|l}} or {{tl|m}}: { { -- Basic link-related fields term = "entry_to_link_to", alt = "link_text_or_displayed_text", lang = language_object, sc = script_object, fragment = "link_fragment", id = "sense_id", accel = {accelerated_creation_tags}, -- Link annotation fields interwiki = "interwiki_link", genders = {"gender1", "gender2", ...} or {{spec = "gender1", q = {"left qualifier", ...}, qq = {"right qualifier"}, ...}, ...}, tr = "transliteration" or "-" or {{tr = "transliteration", q = {"left_qualifier", ...}, qq = {"right_qualifier", ...}, ..., genders = {gender_spec, ...}}, ...}, ts = "transcription" or {{ts = "transliteration", q = {"left_qualifier", ...}, qq = {"right_qualifier", ...}, ..., genders = {gender_spec, ...}}, ...}, gloss = "gloss", pos = "part_of_speech_tag", infl = {"infl1_tag1", "infl1_tag2", ..., ";", "infl2_tag1", "infl2_tag2", ...}, ng = "non-gloss text", lit = "literal_translation", postprocess_annotations = function({data = full_link_data, annotations = {"annotation1", "annotation2", ...}}) -> nil, -- Other transliteration-related fields respect_link_tr = boolean, never_call_transliteration_module = boolean, suppress_tr = boolean, -- Decoration fields q = { "left_qualifier1", "left_qualifier2", ...} or "left_qualifier", qq = { "right_qualifier1", "right_qualifier2", ...} or "right_qualifier", l = { "left_label1", "left_label2", ...}, ll = { "right_label1", "right_label2", ...}, a = { "left_accent_qualifier1", "left_accent_qualifier2", ...}, aa = { "right_accent_qualifier1", "right_accent_qualifier2", ...}, refs = { "formatted_ref1", "formatted_ref2", ...} or { {text = "text", name = "name", group = "group"}, ... }, pretext = "text_at_beginning", posttext = "text_at_end", show_decorations = boolean, -- Fields controlling tracking categories track_sc = boolean, no_nonstandard_sc_cat = boolean, suppress_redundant_wikilink_cat = function(term, alt) -> boolean, -- Miscellaneous fields no_alt_ast = boolean, no_generate_alternants = boolean, } } Any one of the items in the `data` table (except for `lang`) may be {nil}. If none of `term`, `alt` and `tr` is present, a term request will be shown. Thus, calling {full_link{ term = term, lang = lang, sc = sc }}, where `term` is the page to link to (which may have diacritics that will be stripped and/or embedded bracketed links) and `lang` is a [[Module:languages#Language objects|language object]] from [[Module:languages]], will give a plain link similar to the one produced by the template {{tl|l}}, and calling {full_link( { term = term, lang = lang, sc = sc }, "term" )} will give a link similar to the one produced by the template {{tl|m}}. The function will: * Try to determine the script, based on the characters found in the `term` or `alt` argument, if the script was not given. If a script is given and `track_sc` is {true}, it will check whether the input script is the same as the one which would have been automatically generated and add the category ` ``lang`` terms with redundant script codes` if yes, or ` ``lang`` terms with non-redundant manual script codes` if no. This should be used when the input script object is directly determined by a template's `sc` parameter. * Call `simple_link()` on the `term` or `alt` forms, to remove diacritics in the page name, process any embedded wikilinks and create links to Reconstruction or Appendix pages when necessary. (`simple_link()` is almost exactly the same as `##language_link()`; the latter is a simple wrapper around the former that adds a bit of extra tracking.) * Call `[[Module:script utilities#tag_text]]` to add the appropriate language and script tags to the term and italicize terms written in the Latin script if necessary. Accelerated creation tags, as used by [[WT:ACCEL]], are included. * Generate a transliteration, based on the `alt` or `term` arguments, if the script is not Latin, no transliteration was provided in `tr` and the combination of the term's language and script support automatic transliteration. The transliteration itself will be linked if both `.respect_link_tr` is specified and the language of the term has the `link_tr` property set for the script of the term; but not otherwise. * Add the annotations (transliteration, gender, gloss, etc.) after the link. * If `no_alt_ast` is specified, then the `alt` text does not need to contain an asterisk if the language is reconstructed. This should only be used by modules which really need to allow links to reconstructions that don't display asterisks (e.g. number boxes). * If `suppress_redundant_wikilink_cat` is specified, it should be a function that indicates whether to suppress the generation of the ` ``lang`` links with redundant wikilinks` tracking category. It is passed two arguments, the `term` and `alt` parameters. Normally, this tracking category is added whenever the `term` argument consists entirely of a one-part or two-part embedded link, which is considered "redundant" in that the link can be rewritten into separate `term` and `alt` arguments without any embedded links. For certain wrapping templates, however, otherwise "redundant" embedded links are necessary to prevent interpretation of certain characters as delimiters. For example, {{tl|col}} and related templates use `~` as a separator, as well as `,` when not followed by a space. In these templates, embedded links are required to correctly link to terms containing those delimiters, such as [[Micros~1]] and [[1,6-Cleves acid]], but will incorrectly trigger the addition of the tracking category unless the appropriate `suppress_redundant_wikilink_cat` function is given. * If `pretext` or `posttext` is specified, this is text to (respectively) prepend or append to the output, directly before processing decorations (qualifiers, labels and references). This can be used to add arbitrary extra text inside of the decorations. * If `show_decorations` is specified, then decorations specified in `data` (i.e. left and right qualifiers, accent qualifiers, labels and references) will be displayed, otherwise they will be ignored. (This is because a fair amount of code stores decorations in these fields and displays them itself, rather than expecting {full_link()} to display them.) * ]==] function export.full_link(data, face, allow_self_link, show_qualifiers) if type(data) ~= "table" then error("The first argument to the function full_link must be a table. " .. "See Module:links/documentation for more information.") elseif data.term and data.term:find("\\", nil, true) or data.alt and data.alt:find("\\", nil, true) then track("escaped", "full_link") end if show_qualifiers then -- FIXME: Eventually remove the error code. Added 2026-09-17, remove after 2026-10-17 or so. error("Can't pass fourth parameter `show_qualifiers` any more. Set `show_decorations = true` on data.") end if data.show_qualifiers then -- FIXME: Eventually remove the error code. Added 2026-09-18, remove after 2026-10-18 or so. error("Can't set field `show_qualifiers`; use `show_decorations`") end if data.no_generate_forms then -- FIXME: Eventually remove the error code. Added 2026-09-14, remove after 2026-10-14 or so. error("Can't set field `no_generate_forms`; use `no_generate_alternants`") end -- Prevent data from being destructively modified. data = shallow_copy(data) data.cats = {} -- Categorize links to "und". local lang, cats = data.lang, data.cats if cats and lang:getCode() == "und" then insert(cats, "Undetermined language links") end -- With pos=root, link to the page where Module:roots keeps the language's roots (Appendix:Arabic roots/…) and -- display the root as given. if data.pos == "root" and type(data.term) == "string" and not data.term:find(":", nil, true) then local root_page = get_root_link_page(lang, data.term) if root_page then data.alt = data.alt or data.term data.term = root_page end end local terms = { true } -- Generate multiple alternants if applicable. for _, param in ipairs { "term", "alt" } do if type(data[param]) == "string" and data[param]:find("//", nil, true) then data[param] = export.split_on_slashes(data[param]) elseif type(data[param]) == "string" and not (type(data.term) == "string" and data.term:find("//", nil, true)) then if not data.no_generate_alternants then data[param] = lang:generateAlternants(data[param]) else data[param] = { data[param] } end else data[param] = {} end end for _, param in ipairs { "sc", "tr", "ts" } do data[param] = { data[param] } end for _, param in ipairs { "term", "alt", "sc", "tr", "ts" } do for i in pairs(data[param]) do terms[i] = true end end -- Create the link local outparts = {} local id, no_alt_ast, suppress_redundant_wikilink_cat, accel, never_call_transliteration_module = data.id, data.no_alt_ast, data.suppress_redundant_wikilink_cat, data.accel, data.never_call_transliteration_module local link_tr = data.respect_link_tr and lang:link_tr(data.sc[1]) for i in ipairs(terms) do local link -- Is there any text to show? if (data.term[i] or data.alt[i]) then -- Try to detect the script if it was not provided -- FIXME: alt is ignored if term contains embedded links, so the following isn't quite right. local display_term = data.alt[i] or data.term[i] local best = lang:findBestScript(display_term) -- no_nonstandard_sc_cat is intended for use in [[Module:interproject]] if ( not data.no_nonstandard_sc_cat and best:getCode() == "None" and find_best_script_without_lang(display_term):getCode() ~= "None" ) then insert(cats, lang:getFullName() .. " terms in nonstandard scripts") end if not data.sc[i] then data.sc[i] = best -- Track uses of sc parameter. elseif data.track_sc then if data.sc[i]:getCode() == best:getCode() then insert(cats, lang:getFullName() .. " terms with redundant script codes") else insert(cats, lang:getFullName() .. " terms with non-redundant manual script codes") end end -- If using a discouraged character sequence, add to maintenance category if data.sc[i]:hasNormalizationFixes() == true then if (data.term[i] and data.sc[i]:fixDiscouragedSequences(toNFC(data.term[i])) ~= toNFC(data.term[i])) or (data.alt[i] and data.sc[i]:fixDiscouragedSequences(toNFC(data.alt[i])) ~= toNFC(data.alt[i])) then insert(cats, "Pages using discouraged character sequences") end end link = simple_link( data.term[i], data.fragment, data.alt[i], lang, data.sc[i], id, cats, no_alt_ast, suppress_redundant_wikilink_cat ) end -- simple_link can return nil, so check if a link has been generated. if link then -- Add "nowrap" class to prefixes in order to prevent wrapping after the hyphen local nowrap -- FIXME: alt is ignored if term contains embedded links, so the following isn't quite right. local display_term = data.alt[i] or data.term[i] -- FIXME, use hyphens from [[Module:affix]] if display_term and (display_term:find("^%*?%-") or display_term:find("^%*?־")) then -- Hebrew maqqef nowrap = "nowrap" end -- Forcibly disable acceleration in the presence of embedded links, because it doesn't work correctly; -- if one of the embedded terms doesn't exist, the acceleration code will try to make a form-of for the -- embedded term pointing to the original headword (which is wrong). if link:find(".%[%[") or link:find("%]%].") then accel = nil end link = tag_text(link, lang, data.sc[i], face, get_css_classes(lang, data.tr[i], accel, nowrap)) else --[[ No term to show. Is there at least a transliteration we can work from? ]] link = request_script(lang, data.sc[i]) -- No link to show, and no transliteration either. Show a term request (unless it's a substrate, as they rarely take terms). if (link == "" or (not data.tr[i]) or data.tr[i] == "-") and lang:getFamilyCode() ~= "qfa-sub" then -- If there are multiple terms, break the loop instead. if i > 1 then remove(outparts) break elseif NAMESPACE ~= "แม่แบบ" then insert(cats, lang:getFullName() .. " term requests") end link = "<small>[Term?]</small>" end end insert(outparts, link) if i < #terms then insert(outparts, "<span class=\"Zsym mention\" style=\"font-size:100%;\">&nbsp;/ </span>") end end -- When suppress_tr is true, do not show or generate any transliteration if data.suppress_tr then data.tr[1] = nil else -- TODO: Currently only handles the first transliteration, pending consensus on how to handle multiple translits for multiple forms, as this is not always desirable (e.g. traditional/simplified Chinese). local langcode = lang:getCode() --เพิ่มเอง if data.tr[1] == "" or data.tr[1] == "-" then data.tr[1] = nil elseif langcode == "eo" then --เพิ่มเอง data.tr[1] = require("Module:eo-translit").tr( export.remove_links(data.alt[1] or data.term[1]), langcode, lang:findBestScript(data.alt[1] or data.term[1])) elseif langcode == "es" then --เพิ่มเอง data.tr[1] = require("Module:es-translit").tr( export.remove_links(data.alt[1] or data.term[1]), langcode, lang:findBestScript(data.alt[1] or data.term[1])) elseif langcode == "la" or langcode:find("^la%-") or langcode:find("^[CELMNRV]L%.$") then --เพิ่มเอง data.tr[1] = require("Module:la-translit").tr( export.remove_links(data.alt[1] or data.term[1]), langcode, lang:findBestScript(data.alt[1] or data.term[1])) elseif langcode == "vi" then --เพิ่มเอง data.tr[1] = require("Module:vi-translit").tr( export.remove_links(data.alt[1] or data.term[1]), langcode, data.lang:findBestScript(data.alt[1] or data.term[1])) else local phonetic_extraction = load_data("Module:links/data").phonetic_extraction phonetic_extraction = phonetic_extraction[lang:getCode()] or phonetic_extraction[lang:getFullCode()] if phonetic_extraction then data.tr[1] = data.tr[1] or require(phonetic_extraction).getTranslit(export.remove_links(data.alt[1] or data.term[1])) elseif (data.term[1] or data.alt[1]) and data.sc[1]:isTransliterated() then -- Track whenever there is manual translit. The categories below like 'terms with redundant transliterations' -- aren't sufficient because they only work with reference to automatic translit and won't operate at all in -- languages without any automatic translit, like Persian and Hebrew. if data.tr[1] then local full_code = lang:getFullCode() track("manual-tr", full_code) end if not never_call_transliteration_module then -- Try to generate a transliteration. local text = data.alt[1] or data.term[1] if not link_tr then text = export.remove_links(text, true) end local automated_tr = lang:transliterate(text, data.sc[1]) if automated_tr then local manual_tr = data.tr[1] if manual_tr then if export.remove_links(manual_tr) == export.remove_links(automated_tr) then insert(cats, lang:getFullName() .. " terms with redundant transliterations") else -- Prevents Arabic root categories from flooding the tracking categories. if NAMESPACE ~= "หมวดหมู่" then insert(cats, lang:getFullName() .. " terms with non-redundant manual transliterations") end end end if not manual_tr or lang:overrideManualTranslit(data.sc[1]) then data.tr[1] = automated_tr end end end end end end -- Link to the transliteration entry for languages that require this if data.tr[1] and link_tr and not data.tr[1]:match("%[%[(.-)%]%]") then data.tr[1] = simple_link( data.tr[1], nil, nil, lang, get_script("Latn"), nil, cats, no_alt_ast, suppress_redundant_wikilink_cat ) elseif data.tr[1] and not link_tr then -- Remove the pseudo-HTML tags added by remove_links. data.tr[1] = data.tr[1]:gsub("</?link>", "") end if data.tr[1] and not umatch(data.tr[1], "[^%s%p]") then data.tr[1] = nil end insert(outparts, export.format_link_annotations(data, face)) if data.pretext then insert(outparts, 1, data.pretext) end if data.posttext then insert(outparts, data.posttext) end local categories = cats[1] and format_categories(cats, lang, "-", nil, nil, data.sc) or "" local output = concat(outparts) if data.show_decorations then output = add_text_decorations(output, data, lang) end return output .. categories end --[==[ Strip links by replacing all wikilinks with their displayed text, and remove any categories. This function can be invoked either from a template or from another module. Specifically, this function deletes category links, the targets of piped links, and any double square brackets involved in links (other than file links, which are untouched). If `tag` is set, then any links removed will be given pseudo-HTML tags, which allow the substitution functions in [[Module:languages]] to properly subdivide the text in order to reduce the chance of substitution failures in modules which scrape pages like [[Module:zh-translit]]. (FIXME: This is quite hacky. We probably want this to be integrated into [[Module:languages]], but we can't do that until we know that nothing is pushing pipe linked transliterations through it for languages which don't have link_tr set.) * `<nowiki>[[page|displayed text]]</nowiki>` &rarr; `displayed text` * `<nowiki>[[page and displayed text]]</nowiki>` &rarr; `page and displayed text` * `<nowiki>[[Category:English lemmas|WORD]]</nowiki>` &rarr; ''(nothing)'' ]==] function export.remove_links(text, tag) if type(text) == "table" then text = text.args[1] end if not text or text == "" then return "" end text = text :gsub("%[%[", "\1") :gsub("%]%]", "\2") -- Parse internal links for the display text. text = text:gsub("(\1)([^\1\2]-)(\2)", function(c1, c2, c3) -- Don't remove files. for _, false_positive in ipairs({ "file", "image" }) do if c2:lower():match("^" .. false_positive .. ":") then return c1 .. c2 .. c3 end end -- Remove categories completely. for _, false_positive in ipairs({ "category", "cat" }) do if c2:lower():match("^" .. false_positive .. ":") then return "" end end -- In piped links, remove all text before the pipe, unless it's the final character (i.e. the pipe trick), in which case just remove the pipe. c2 = c2:match("^[^|]*|(.+)") or c2:match("([^|]+)|$") or c2 if tag then return "<link>" .. c2 .. "</link>" else return c2 end end) text = text :gsub("\1", "[[") :gsub("\2", "]]") return text end function export.section_link(link) if type(link) ~= "string" then error("The first argument to section_link was a " .. type(link) .. ", but it should be a string.") elseif link:find("\\", nil, true) then track("escaped", "section_link") end local target, section = get_fragment((link:gsub("_", " "))) if not section then error("No \"#\" delineating a section name") end return simple_link( target, section, target .. " §&nbsp;" .. section ) end return export 928ty23ybicnijqut4244kd3u4yoaet มอดูล:script utilities 828 36045 5775190 5684442 2026-10-14T04:16:49Z Octahedron80 267 5775190 Scribunto text/plain local export = {} local anchors_module = "Module:anchors" local debug_track_module = "Module:debug/track" local links_module = "Module:links" local munge_text_module = "Module:munge text" local parameters_module = "Module:parameters" local scripts_module = "Module:scripts" local string_utilities_module = "Module:string utilities" local utilities_module = "Module:utilities" local concat = table.concat local insert = table.insert local require = require local toNFD = mw.ustring.toNFD local dump = mw.dumpObject --[==[ 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 embedded_language_links(...) embedded_language_links = require(links_module).embedded_language_links return embedded_language_links(...) end local function find_best_script_without_lang(...) find_best_script_without_lang = require(scripts_module).findBestScriptWithoutLang return find_best_script_without_lang(...) end local function format_categories(...) format_categories = require(utilities_module).format_categories return format_categories(...) end local function get_script(...) get_script = require(scripts_module).getByCode return get_script(...) end local function language_anchor(...) language_anchor = require(anchors_module).language_anchor return language_anchor(...) end local function munge_text(...) munge_text = require(munge_text_module) return munge_text(...) end local function process_params(...) process_params = require(parameters_module).process return process_params(...) end local function track(...) track = require(debug_track_module) return track(...) end local function u(...) u = require(string_utilities_module).char return u(...) end local function ugsub(...) ugsub = require(string_utilities_module).gsub return ugsub(...) end local function umatch(...) umatch = require(string_utilities_module).match return umatch(...) end --[==[ Loaders for objects, which load data (or some other object) into some variable, which can then be accessed as "foo or get_foo()", where the function get_foo sets the object to "foo" and then returns it. This ensures they are only loaded when needed, and avoids the need to check for the existence of the object each time, since once "foo" has been set, "get_foo" will not be called again.]==] local m_data local function get_data() m_data, get_data = mw.loadData("Module:script utilities/data"), nil return m_data end --[=[ Modules used: [[Module:script utilities/data]] [[Module:scripts]] [[Module:anchors]] (only when IDs present) [[Module:string utilities]] (only when hyphens in Korean text or spaces in vertical text) [[Module:languages]] [[Module:parameters]] [[Module:utilities]] [[Module:debug/track]] ]=] function export.is_Latin_script(sc) -- Latn, Latf, Latg, pjt-Latn return sc:getCode():find("Lat") and true or false end function export.is_Thai_script(sc) -- Thai return sc:getCode() == "Thai" end --[==[{{temp|#invoke:script utilities|lang_t}} This is used by {{temp|lang}} to wrap portions of text in a language tag. See there for more information.]==] do local function get_args(frame) return process_params(frame:getParent().args, { [1] = {required = true, type = "language", default = "und"}, [2] = {required = true, allow_empty = true, default = ""}, ["sc"] = {type = "script"}, ["face"] = true, ["class"] = true, }) end function export.lang_t(frame) local args = get_args(frame) local lang = args[1] local sc = args["sc"] local text = args[2] local cats = {} if sc then -- Track uses of sc parameter. if sc:getCode() == lang:findBestScript(text):getCode() then insert(cats, lang:getFullName() .. " terms with redundant script codes") else insert(cats, lang:getFullName() .. " terms with non-redundant manual script codes") end else sc = lang:findBestScript(text) end text = embedded_language_links{ term = text, lang = lang, sc = sc } cats = #cats > 0 and format_categories(cats, lang, "-", nil, nil, sc) or "" local face = args["face"] local class = args["class"] return export.tag_text(text, lang, sc, face, class) .. cats end end -- Ustring turns on the codepoint-aware string matching. The basic string function -- should be used for simple sequences of characters, Ustring function for -- sets – []. local function trackPattern(text, pattern, tracking) if pattern and umatch(text, pattern) then track("script/" .. tracking) end end local function track_text(text, lang, sc) if lang and text then local langCode = lang:getFullCode() -- [[Special:WhatLinksHere/Wiktionary:Tracking/script/ang/acute]] if langCode == "ang" then local decomposed = toNFD(text) local acute = u(0x301) trackPattern(decomposed, acute, "ang/acute") --[=[ [[Special:WhatLinksHere/Wiktionary:Tracking/script/Greek/wrong-phi]] [[Special:WhatLinksHere/Wiktionary:Tracking/script/Greek/wrong-theta]] [[Special:WhatLinksHere/Wiktionary:Tracking/script/Greek/wrong-kappa]] [[Special:WhatLinksHere/Wiktionary:Tracking/script/Greek/wrong-rho]] ϑ, ϰ, ϱ, ϕ should generally be replaced with θ, κ, ρ, φ. ]=] elseif langCode == "el" or langCode == "grc" then trackPattern(text, "ϑ", "Greek/wrong-theta") trackPattern(text, "ϰ", "Greek/wrong-kappa") trackPattern(text, "ϱ", "Greek/wrong-rho") trackPattern(text, "ϕ", "Greek/wrong-phi") --[=[ [[Special:WhatLinksHere/Wiktionary:Tracking/script/Ancient Greek/spacing-coronis]] [[Special:WhatLinksHere/Wiktionary:Tracking/script/Ancient Greek/spacing-smooth-breathing]] [[Special:WhatLinksHere/Wiktionary:Tracking/script/Ancient Greek/wrong-apostrophe]] When spacing coronis and spacing smooth breathing are used as apostrophes, they should be replaced with right single quotation marks (’). ]=] if langCode == "grc" then trackPattern(text, u(0x1FBD), "Ancient Greek/spacing-coronis") trackPattern(text, u(0x1FBF), "Ancient Greek/spacing-smooth-breathing") trackPattern(text, "[" .. u(0x1FBD) .. u(0x1FBF) .. "]", "Ancient Greek/wrong-apostrophe", true) end -- [[Special:WhatLinksHere/Wiktionary:Tracking/script/Russian/grave-accent]] elseif langCode == "ru" then local decomposed = toNFD(text) trackPattern(decomposed, u(0x300), "Russian/grave-accent") -- [[Special:WhatLinksHere/Wiktionary:Tracking/script/Chuvash/latin-homoglyph]] elseif langCode == "cv" then trackPattern(text, "[ĂăĔĕÇçŸÿ]", "Chuvash/latin-homoglyph") -- [[Special:WhatLinksHere/Wiktionary:Tracking/script/Tibetan/trailing-punctuation]] elseif langCode == "bo" then trackPattern(text, "[་།]$", "Tibetan/trailing-punctuation") trackPattern(text, "[་།]%]%]$", "Tibetan/trailing-punctuation") --[=[ [[Special:WhatLinksHere/Wiktionary:Tracking/script/Thai/broken-ae]] [[Special:WhatLinksHere/Wiktionary:Tracking/script/Thai/broken-am]] [[Special:WhatLinksHere/Wiktionary:Tracking/script/Thai/wrong-rue-lue]] ]=] elseif langCode == "th" then trackPattern(text, "เ".."เ", "Thai/broken-ae") trackPattern(text, "ํ[่้๊๋]?า", "Thai/broken-am") trackPattern(text, "[ฤฦ]า", "Thai/wrong-rue-lue") --[=[ [[Special:WhatLinksHere/Wiktionary:Tracking/script/Lao/broken-ae]] [[Special:WhatLinksHere/Wiktionary:Tracking/script/Lao/broken-am]] [[Special:WhatLinksHere/Wiktionary:Tracking/script/Lao/possible-broken-ho-no]] [[Special:WhatLinksHere/Wiktionary:Tracking/script/Lao/possible-broken-ho-mo]] [[Special:WhatLinksHere/Wiktionary:Tracking/script/Lao/possible-broken-ho-lo]] ]=] elseif langCode == "lo" then trackPattern(text, "ເ".."ເ", "Lao/broken-ae") trackPattern(text, "ໍ[່້໊໋]?າ", "Lao/broken-am") trackPattern(text, "ຫນ", "Lao/possible-broken-ho-no") trackPattern(text, "ຫມ", "Lao/possible-broken-ho-mo") trackPattern(text, "ຫລ", "Lao/possible-broken-ho-lo") --[=[ [[Special:WhatLinksHere/Wiktionary:Tracking/script/Lü/broken-ae]] [[Special:WhatLinksHere/Wiktionary:Tracking/script/Lü/possible-wrong-sequence]] ]=] elseif langCode == "khb" then trackPattern(text, "ᦵ".."ᦵ", "Lü/broken-ae") trackPattern(text, "[ᦀ-ᦫ][ᦵᦶᦷᦺ]", "Lü/possible-wrong-sequence") elseif langCode == "trk-pro" then -- Temporary tracking while we convert the transcription scheme munge_text(text, function(raw) raw = mw.ustring.toNFC(raw) -- The "old" scheme is the original, unconverted scheme. This has l/ĺ, r/ŕ, ń, ẹ/ẹ̄ (to be converted to e/ē), e/ē (to be converted to ä/ǟ). -- The "new" scheme is the original "new" scheme to which some pages have been converted. This has l/lᶴ, r/rᶻ, nʸ, e/ē (converted from ẹ/ẹ̄), ä/ǟ (converted from e/ē). -- The "new new" scheme is the "newest" scheme to which we appear to be converging. This has l₁/l₂ (or maybe l/l₂), r₁/r₂ (or maybe r/r₂), ń, and the remainder like the "new" scheme. local new_or_new_new = umatch(raw, "[äǟ]") -- ǟ is one Unicode char local unambiguous_new = raw:find("lᶴ") or raw:find("rᶻ") or raw:find("nʸ") local unambiguous_new_new = umatch(raw, "[lr][₁₂]") local unambiguous_old_dot_e = raw:find("ẹ") -- ẹ̄ is two Unicode characters local unambiguous_old_lr = umatch(raw, "[ĺŕ]") -- ĺ and ŕ are one local unambiguous_old_depending = raw:find("[lr]$") or umatch(raw, "[lr][^₁₂]") local ambiguous_old_new = umatch(raw, "[eē]") if new_or_new_new then track("script/Proto-Turkic/new-or-new-new") end if unambiguous_new then track("script/Proto-Turkic/unambiguous-new") end if unambiguous_new_new then track("script/Proto-Turkic/unambiguous-new-new") end if unambiguous_old_dot_e then track("script/Proto-Turkic/unambiguous-old-dot-e") end if unambiguous_old_lr then track("script/Proto-Turkic/unambiguous-old-lr") end if unambiguous_old_depending then track("script/Proto-Turkic/unambiguous-old-depending") end if ambiguous_old_new then track("script/Proto-Turkic/ambiguous-old-new") end if new_or_new_new or unambiguous_new or unambiguous_new_new then track("script/Proto-Turkic/any-new-or-new-new") end if unambiguous_old_dot_e or unambiguous_old_lr then track("script/Proto-Turkic/unambiguous-old-dot-e-or-lr") end return text end) end end end local function Kore_ruby(...) -- Cache character sets on the first call. local Hang_chars = get_script("Hang"):getCharacters() local Hani_chars = get_script("Hani"):getCharacters() -- Overwrite with the actual function, which is called directly on subsequent calls. function Kore_ruby(txt) return (ugsub( txt, -- Hanja part should not start with a hyphen "([" .. Hani_chars .. "][%-" .. Hani_chars .. "]*)%(([%-" .. Hang_chars .. "]+)%)", "<ruby>%1<rp>(</rp><rt>%2</rt><rp>)</rp></ruby>" )) end return Kore_ruby(...) end --[==[Wraps the given text in HTML tags with appropriate CSS classes (see [[WT:CSS]]) for the [[Module:languages#Language objects|language]] and script. This is required for all non-English text on Wiktionary. The actual tags and CSS classes that are added are determined by the <code>face</code> parameter. It can be one of the following: ; {{code|lua|"term"}} : The text is wrapped in {{code|html|2=<i class="(sc) mention" lang="(lang)">...</i>}}. ; {{code|lua|"head"}} : The text is wrapped in {{code|html|2=<strong class="(sc) headword" lang="(lang)">...</strong>}}. ; {{code|lua|"hypothetical"}} : The text is wrapped in {{code|html|2=<span class="hypothetical-star">*</span><i class="(sc) hypothetical" lang="(lang)">...</i>}}. ; {{code|lua|"bold"}} : The text is wrapped in {{code|html|2=<b class="(sc)" lang="(lang)">...</b>}}. ; {{code|lua|nil}} : The text is wrapped in {{code|html|2=<span class="(sc)" lang="(lang)">...</span>}}. The optional <code>class</code> parameter can be used to specify an additional CSS class to be added to the tag.]==] function export.tag_text(text, lang, sc, face, class, id) if not sc then if lang then sc = lang:findBestScript(text) else sc = find_best_script_without_lang(text) end end track_text(text, lang, sc) -- Replace space characters with newlines in Mongolian-script text, which is written top-to-bottom. if sc:getDirection():find("vertical", nil, true) and text:find(" ", nil, true) then text = munge_text(text, function(txt) -- having extra parentheses makes sure only the first return value gets through return (txt:gsub(" +", "<br>")) end) end -- Hack Korean script text to remove hyphens. -- FIXME: This should be handled in a more general fashion, but needs to -- be efficient by not doing anything if no hyphens are present, and currently this is the only -- language needing such processing. -- 20220221: Also convert 漢字(한자) to ruby, instead of needing [[Template:Ruby]]. if sc:getCode() == "Kore" and text:match("[%-()g]") then local title, display = require("Module:links").get_wikilink_parts(text) if title ~= nil then -- special case that the text is a single link, do not munge and preserve affix hyphens if lang and lang:getCode() == "okm" then -- Middle Korean code from [[User:Chom.kwoy]] -- Comment from [[User:Lunabunn]]: -- In Middle Korean orthography, syllable formation is phonemic as opposed to morpheme-boundary-based a la -- modern Korean. As such, for example, if you were to write nam-i, it would be rendered as na.mi so if you -- then put na-mi to indicate particle boundaries as in modern Korean, the hyphen would be misplaced. -- Previously, this was alleviated by specialcasing na--mi but [[User:Theknightwho]] made that resolve to - -- in the Hangul (previously we used to just delete all -s in Hangul processing), so it broke. -- [[User:Chom.kwoy]] implemented a different solution, which is writing -> instead using however many >s to -- shift the hyphen by that number of letters in the romanization. -- By the time we are called, > signs have been converted to &gt; by a call to encode_entities() in -- make_link() in [[Module:links]] (near the bottom of the function). -- 'g' in Middle Korean is a special sign to treat the following ㅇ sign as /G/ instead of null. display = display:gsub("&gt;", ""):gsub("g", "") end if display:find("<") then display = munge_text(display, function(txt) txt = txt:gsub("(.)%-(%-?)(.)", "%1%2%3") return Kore_ruby(txt) end) else display = display:gsub("(.)%-(%-?)(.)", "%1%2%3") display = Kore_ruby(display) end text = "[[" .. title .. "|" .. display .. "]]" else text = munge_text(text, function(txt) if lang and lang:getCode() == "okm" then txt = txt:gsub("&gt;", ""):gsub("g", "") end if txt == text then -- special case for the entire text being plain txt = txt:gsub("(.)%-(%-?)(.)", "%1%2%3") else txt = txt:gsub("%-(%-?)", "%1") end return Kore_ruby(txt) end) end end if sc:getCode() == "Image" then face = nil end if face == "hypothetical" then -- [[Special:WhatLinksHere/Wiktionary:Tracking/script-utilities/face/hypothetical]] track("script-utilities/face/hypothetical") end local data = (m_data or get_data()).faces[face or "plain"] if data == nil then error('Invalid script face "' .. face .. '".') end local tag = data.tag local opening_tag = {tag} if lang and id then insert(opening_tag, 'id="' .. language_anchor(lang, id) .. '"') end local classes = {data.class} -- if the script code is hyphenated (i.e. language code-script code, add the last component as a class as well) -- e.g. mnc-Mong adds both Mong and mnc-Mong as classes if sc:getCode():find("-", nil, true) then insert(classes, 1, (ugsub(sc:getCode(), ".+%-", ""))) insert(classes, 2, sc:getCode()) else insert(classes, 1, sc:getCode()) end if class and class ~= '' then insert(classes, class) end insert(opening_tag, 'class="' .. concat(classes, ' ') .. '"') -- FIXME: Is it OK to insert the etymology-only lang code and have it fall back to the first part of the -- lang code (by chopping off the '-...' part)? It seems the :lang() selector does this; not sure about -- [lang=...] attributes. if lang then insert(opening_tag, 'lang="' .. lang:getFullCode() .. '"') end -- Add a script wrapper return (data.prefix or "") .. "<" .. concat(opening_tag, " ") .. ">" .. text .. "</" .. tag .. ">" end --[==[Tags the transliteration for given text {translit} and language {lang}. It will add the language, script subtag (as defined in [https://www.rfc-editor.org/rfc/bcp/bcp47.txt BCP 47 2.2.3]) and [https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir dir] (directional) attributes as needed. The optional <code>kind</code> parameter can be one of the following: ; {{code|lua|"term"}} : tag transliteration for {{temp|mention}} ; {{code|lua|"usex"}} : tag transliteration for {{temp|usex}} ; {{code|lua|"head"}} : tag transliteration for {{temp|head}} ; {{code|lua|"default"}} : default The optional <code>attributes</code> parameter is used to specify additional HTML attributes for the tag.]==] function export.tag_translit(translit, lang, kind, attributes, is_manual) if type(lang) == "table" then -- FIXME: Do better support for etym languages; see https://www.rfc-editor.org/rfc/bcp/bcp47.txt lang = lang.getFullCode and lang:getFullCode() or error("Second argument to tag_translit should be a language code or language object.") end local data = (m_data or get_data()).translit[kind or "default"] local tag = data.tag local opening_tag = {tag} local class = data.class if lang == "ja" then insert(opening_tag, 'class="' .. (class and (class .. " ") or "") .. (is_manual and "manual-tr " or "") .. 'tr"') else insert(opening_tag, 'lang="' .. lang .. '"') -- removed -Latn insert(opening_tag, 'class="' .. (class and (class .. " ") or "") .. (is_manual and "manual-tr " or "") .. 'tr"') -- removed -Latn end local dir = data.dir if dir then insert(opening_tag, 'dir="' .. dir .. '"') end if attributes then track("tag_translit/attributes") insert(opening_tag, attributes) end return "<" .. concat(opening_tag, " ") .. ">" .. translit .. "</" .. tag .. ">" end function export.tag_transcription(transcription, lang, kind, attributes) if type(lang) == "table" then -- FIXME: Do better support for etym languages; see https://www.rfc-editor.org/rfc/bcp/bcp47.txt lang = lang.getFullCode and lang:getFullCode() or error("Second argument to tag_transcription should be a language code or language object.") end local data = (m_data or get_data()).transcription[kind or "default"] local tag = data.tag local opening_tag = {tag} local class = data.class if lang == "ja" then insert(opening_tag, 'class="' .. (class and (class .. " ") or "") .. 'ts"') else insert(opening_tag, 'lang="' .. lang .. '"') -- removed -Latn insert(opening_tag, 'class="' .. (class and (class .. " ") or "") .. 'ts"') -- removed -Latn end local dir = data.dir if dir then insert(opening_tag, 'dir="' .. dir .. '"') end if attributes then track("tag_transcription/attributes") insert(opening_tag, attributes) end return "<" .. concat(opening_tag, " ") .. ">" .. transcription .. "</" .. tag .. ">" end --[==[Tags {def} as a definition. The <code>def</code> parameter must be one of the following: ; {{code|lua|"gloss"}} : The text is wrapped in {{code|html|2=<span class="(mention-gloss">...</span>}}. ; {{code|lua|"non-gloss"}} : The text is wrapped in {{code|html|2=<span class="use-with-mention">...</span>}}. The optional <code>attributes</code> parameter is used to specify additional HTML attributes for the tag.]==] function export.tag_definition(def, kind, attributes) local data = (m_data or get_data()).definition[kind] if data == nil then error("Second argument to tag_definition should specify the kind of definition from the list in [[Module:script utilities/data]].") end local tag = data.tag local opening_tag = {tag} local class = data.class if class then insert(opening_tag, 'class="' .. class .. '"') end if attributes then insert(opening_tag, attributes) end return "<" .. concat(opening_tag, " ") .. ">" .. def .. "</" .. tag .. ">" end --[==[Generates a request to provide a term in its native script, if it is missing. This is used by the {{temp|rfscript}} template as well as by the functions in [[Module:links]]. The function will add entries to one of the subcategories of [[:Category:Requests for native script by language]], and do several checks on the given language and script. In particular: * If the script was given, a subcategory named "Requests for (script) script" is added, but only if the language has more than one script. Otherwise, the main "Requests for native script" category is used. * Nothing is added at all if the language has no scripts other than Latin and its varieties.]==] function export.request_script(lang, sc, usex, nocat, sort_key) local scripts = lang.getScripts and lang:getScripts() or error('The language "' .. lang:getCode() .. '" does not have the method getScripts. It may be unwritten.') -- By default, request for "native" script local cat_script = "native" local disp_script = "script" -- If the script was not specified, and the language has only one script, use that. if not sc and #scripts == 1 then sc = scripts[1] end -- Is the script known? if sc and sc:getCode() ~= "None" then -- If the script is Latin or Thai, return nothing. FOR THWIKT if export.is_Latin_script(sc) or export.is_Thai_script(sc) then return "" end if (not scripts[1]) or sc:getCode() ~= scripts[1]:getCode() then disp_script = sc:getCanonicalName() end -- The category needs to be specific to script only if there is chance of ambiguity. This occurs when when the language has multiple scripts (or with codes such as "und"). if (not scripts[1]) or scripts[2] then cat_script = sc:getCanonicalName() end else -- The script is not known. -- Does the language have at least one non-Latin script in its list? local has_nonlatin = false for _, val in ipairs(scripts) do if not (export.is_Latin_script(val) or export.is_Thai_script(val)) then --FOR THWIKT has_nonlatin = true break end end -- If there are no non-Latin scripts, return nothing. if not has_nonlatin and lang:getCode() ~= "und" then return "" end end -- Etymology languages have their own categories, whose parents are the regular language. return "<small>[" .. disp_script .. " needed]</small>" .. (nocat and "" or format_categories("Requests for " .. cat_script .. " script " .. (usex and "in" or "for") .. " " .. lang:getCanonicalName() .. " " .. (usex == "quote" and "quotations" or usex and "usage examples" or "terms"), lang, sort_key ) ) end --[==[This is used by {{temp|rfscript}}. See there for more information.]==] function export.template_rfscript(frame) local boolean = {type = "boolean"} local args = process_params(frame:getParent().args, { [1] = {required = true, type = "language", default = "und"}, ["sc"] = {type = "script"}, ["usex"] = boolean, ["quote"] = boolean, ["nocat"] = boolean, ["sort"] = true, }) local ret = export.request_script(args[1], args["sc"], args.quote and "quote" or args.usex, args.nocat, args.sort) if ret == "" then error("This language is written in the Latin alphabet. It does not need a native script.") end return ret end function export.checkScript(text, scriptCode, result) local scriptObject = get_script(scriptCode) if not scriptObject then error('The script code "' .. scriptCode .. '" is not recognized.') end local originalText = text -- Remove non-letter characters. text = ugsub(text, "%A+", "") -- Remove all characters of the script in question. text = ugsub(text, "[" .. scriptObject:getCharacters() .. "]+", "") if text ~= "" then if type(result) == "string" then error(result) else error('The text "' .. originalText .. '" contains the letters "' .. text .. '" that do not belong to the ' .. scriptObject:getDisplayForm() .. '.', 2) end end end return export 4jt4qdhzjsph3ybzuy8bo541m4j44oy มอดูล:scripts/data 828 36356 5775260 5719179 2026-10-14T09:53:04Z Octahedron80 267 5775260 Scribunto text/plain --[=[ When adding new scripts to this file, please don't forget to add style definitons for the script in [[MediaWiki:Gadget-LanguagesAndScripts.css]]. ]=] local concat = table.concat local insert = table.insert local ipairs = ipairs local next = next local remove = table.remove local select = select local sort = table.sort -- Loaded on demand, as it may not be needed (depending on the data). local function u(...) u = require("Module:string/char") return u(...) end -- We can't use mw.loadData() on [[Module:languages/chars]] because [[Module:languages/data]] itself is sometimes loaded -- using mw.loadData(), and calling mw.loadData() on [[Module:languages/chars]] will insert metatables into the -- character tables, which the second mw.loadData() will choke on. local m_chars = require("Module:languages/chars") local c = m_chars.chars local p = m_chars.puaChars local cs = m_chars.chars_substitutions ------------------------------------------------------------------------------------ -- -- Helper functions -- ------------------------------------------------------------------------------------ -- Note: a[2] > b[2] means opens are sorted before closes if otherwise equal. local function sort_ranges(a, b) return a[1] < b[1] or a[1] == b[1] and a[2] > b[2] end -- Returns the union of two or more range tables. local function union(...) local ranges = {} for i = 1, select("#", ...) do local argt = select(i, ...) for j, v in ipairs(argt) do insert(ranges, {v, j % 2 == 1 and 1 or -1}) end end sort(ranges, sort_ranges) local ret, i = {}, 0 for _, range in ipairs(ranges) do i = i + range[2] if i == 0 and range[2] == -1 then -- close insert(ret, range[1]) elseif i == 1 and range[2] == 1 then -- open if ret[#ret] and range[1] <= ret[#ret] + 1 then remove(ret) -- merge adjacent ranges else insert(ret, range[1]) end end end return ret end -- Adds the `characters` key, which is determined by a script's `ranges` table. local function process_ranges(sc) local ranges, chars = sc.ranges, {} for i = 2, #ranges, 2 do if ranges[i] == ranges[i - 1] then insert(chars, u(ranges[i])) else insert(chars, u(ranges[i - 1])) if ranges[i] > ranges[i - 1] + 1 then insert(chars, "-") end insert(chars, u(ranges[i])) end end sc.characters = concat(chars) ranges.n = #ranges return sc end local function handle_normalization_fixes(fixes) local combiningClasses = fixes.combiningClasses if combiningClasses then local chars, i = {}, 0 for char in next, combiningClasses do i = i + 1 chars[i] = char end fixes.combiningClassCharacters = concat(chars) end return fixes end ------------------------------------------------------------------------------------ -- -- Data -- ------------------------------------------------------------------------------------ local m = {} m["Adlm"] = process_ranges{ "อัดลัม", 19606346, "alphabet", aliases = {"Adlam"}, ranges = { 0x061F, 0x061F, 0x0640, 0x0640, 0x1E900, 0x1E94B, 0x1E950, 0x1E959, 0x1E95E, 0x1E95F, }, capitalized = true, direction = "rtl", } m["Afak"] = { "อาฟากา", 382019, "syllabary", aliases = {"Afaka"}, -- Not in Unicode } m["Aghb"] = process_ranges{ "Caucasian Albanian", 2495716, "alphabet", ranges = { 0x10530, 0x10563, 0x1056F, 0x1056F, }, } m["Ahom"] = process_ranges{ "อาหม", 2839633, "abugida", aliases = {"อะหม", "Ahom"}, ranges = { 0x11700, 0x1171A, 0x1171D, 0x1172B, 0x11730, 0x11746, }, } m["Arab"] = process_ranges{ "อาหรับ", 1828555, "abjad", -- more precisely, impure abjad aliases = {"อารบิก", "อาระบิก", "Arabic"}, varieties = {{"ยาวี", "Jawi"}, {"Nastaliq", "Nastaleeq"}}, ranges = { 0x0600, 0x06FF, 0x0750, 0x077F, 0x0870, 0x088E, 0x0890, 0x0891, 0x0897, 0x08E1, 0x08E3, 0x08FF, 0xFB50, 0xFBC2, 0xFBD3, 0xFD8F, 0xFD92, 0xFDC7, 0xFDCF, 0xFDCF, 0xFDF0, 0xFDFF, 0xFE70, 0xFE74, 0xFE76, 0xFEFC, 0x102E0, 0x102FB, 0x10E60, 0x10E7E, 0x10EC2, 0x10EC4, 0x10EFC, 0x10EFF, 0x1EE00, 0x1EE03, 0x1EE05, 0x1EE1F, 0x1EE21, 0x1EE22, 0x1EE24, 0x1EE24, 0x1EE27, 0x1EE27, 0x1EE29, 0x1EE32, 0x1EE34, 0x1EE37, 0x1EE39, 0x1EE39, 0x1EE3B, 0x1EE3B, 0x1EE42, 0x1EE42, 0x1EE47, 0x1EE47, 0x1EE49, 0x1EE49, 0x1EE4B, 0x1EE4B, 0x1EE4D, 0x1EE4F, 0x1EE51, 0x1EE52, 0x1EE54, 0x1EE54, 0x1EE57, 0x1EE57, 0x1EE59, 0x1EE59, 0x1EE5B, 0x1EE5B, 0x1EE5D, 0x1EE5D, 0x1EE5F, 0x1EE5F, 0x1EE61, 0x1EE62, 0x1EE64, 0x1EE64, 0x1EE67, 0x1EE6A, 0x1EE6C, 0x1EE72, 0x1EE74, 0x1EE77, 0x1EE79, 0x1EE7C, 0x1EE7E, 0x1EE7E, 0x1EE80, 0x1EE89, 0x1EE8B, 0x1EE9B, 0x1EEA1, 0x1EEA3, 0x1EEA5, 0x1EEA9, 0x1EEAB, 0x1EEBB, 0x1EEF0, 0x1EEF1, }, direction = "rtl", normalizationFixes = handle_normalization_fixes{ from = {"ٳ"}, to = {"اٟ"} }, } m["fa-Arab"] = { "อาหรับ", 744068, m["Arab"][3], aliases = m["Arab"].aliases, ranges = m["Arab"].ranges, characters = m["Arab"].characters, other_names = {"เปอร์โซ-อาหรับ", "Perso-Arabic"}, direction = "rtl", parent = "Arab", normalizationFixes = m["Arab"].normalizationFixes, } m["kk-Arab"] = { "อาหรับ", 90681452, m["Arab"][3], aliases = m["Arab"].aliases, ranges = m["Arab"].ranges, characters = m["Arab"].characters, direction = "rtl", parent = "Arab", normalizationFixes = m["Arab"].normalizationFixes, } m["ks-Arab"] = m["fa-Arab"] m["ku-Arab"] = m["fa-Arab"] m["ms-Arab"] = m["kk-Arab"] m["mzn-Arab"] = m["fa-Arab"] m["ota-Arab"] = m["fa-Arab"] m["pa-Arab"] = { "ศาหมุขี", 133800, m["Arab"][3], aliases = {"Shahmukhi"}, ranges = m["Arab"].ranges, characters = m["Arab"].characters, other_names = {"อารบิก", "อาระบิก", "Arabic"}, direction = "rtl", parent = "Arab", normalizationFixes = m["Arab"].normalizationFixes, } m["ps-Arab"] = m["fa-Arab"] m["sd-Arab"] = m["fa-Arab"] m["tt-Arab"] = m["fa-Arab"] m["ug-Arab"] = m["fa-Arab"] m["ur-Arab"] = m["fa-Arab"] -- Aran (Nastaliq) is subsumed into Arab m["Armi"] = process_ranges{ "แอราเมอิกจักรวรรดิ", 26978, "abjad", aliases = {"Imperial Aramaic"}, ranges = { 0x10840, 0x10855, 0x10857, 0x1085F, }, direction = "rtl", } m["Armn"] = process_ranges{ "อาร์มีเนีย", 11932, "alphabet", aliases = {"อาร์เมเนีย", "Armenian"}, ranges = { 0x0531, 0x0556, 0x0559, 0x058A, 0x058D, 0x058F, 0xFB13, 0xFB17, }, capitalized = true, translit = "Armn-translit", } m["Avst"] = process_ranges{ "อเวสตะ", 790681, "alphabet", aliases = {"Avestan"}, ranges = { 0x10B00, 0x10B35, 0x10B39, 0x10B3F, }, direction = "rtl", } m["pal-Avst"] = { "Pazend", 4925073, m["Avst"][3], aliases = m["Avst"].aliases, ranges = m["Avst"].ranges, characters = m["Avst"].characters, direction = "rtl", parent = "Avst", } m["Bali"] = process_ranges{ "บาหลี", 804984, "abugida", aliases = {"Balinese"}, ranges = { 0x1B00, 0x1B4C, 0x1B4E, 0x1B7F, }, } m["Bamu"] = process_ranges{ "บามุม", 806024, "syllabary", aliases = {"Bamum"}, ranges = { 0xA6A0, 0xA6F7, 0x16800, 0x16A38, }, } m["Bass"] = process_ranges{ "บัสซา", 810458, "alphabet", aliases = {"วาห์", "Bassa", "Bassa Vah", "Vah"}, ranges = { 0x16AD0, 0x16AED, 0x16AF0, 0x16AF5, }, } m["Batk"] = process_ranges{ "บาตัก", 51592, "abugida", aliases = {"Batak"}, ranges = { 0x1BC0, 0x1BF3, 0x1BFC, 0x1BFF, }, } m["Beng"] = process_ranges{ "เบงกอล", 756802, "abugida", aliases = {"เบงกาลี", "บังลา", "Bengali"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0980, 0x0983, 0x0985, 0x098C, 0x098F, 0x0990, 0x0993, 0x09A8, 0x09AA, 0x09B0, 0x09B2, 0x09B2, 0x09B6, 0x09B9, 0x09BC, 0x09C4, 0x09C7, 0x09C8, 0x09CB, 0x09CE, 0x09D7, 0x09D7, 0x09DC, 0x09DD, 0x09DF, 0x09E3, 0x09E6, 0x09EF, 0x09F2, 0x09FE, 0x1CD0, 0x1CD0, 0x1CD2, 0x1CD2, 0x1CD5, 0x1CD6, 0x1CD8, 0x1CD8, 0x1CE1, 0x1CE1, 0x1CEA, 0x1CEA, 0x1CED, 0x1CED, 0x1CF2, 0x1CF2, 0x1CF5, 0x1CF7, 0xA8F1, 0xA8F1, }, normalizationFixes = handle_normalization_fixes{ from = {"অা", "ঋৃ", "ঌৢ"}, to = {"আ", "ৠ", "ৡ"} }, } m["as-Beng"] = process_ranges{ "อัสสัม", 191272, m["Beng"][3], other_names = {"เบงกอล-อัสสัม", "เบงกาลี-อัสสัม", "นาครีตะวันออก", "Assamese", "Bengali-Assamese", "Eastern Nagari"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0980, 0x0983, 0x0985, 0x098C, 0x098F, 0x0990, 0x0993, 0x09A8, 0x09AA, 0x09AF, 0x09B2, 0x09B2, 0x09B6, 0x09B9, 0x09BC, 0x09C4, 0x09C7, 0x09C8, 0x09CB, 0x09CE, 0x09D7, 0x09D7, 0x09DC, 0x09DD, 0x09DF, 0x09E3, 0x09E6, 0x09FE, 0x1CD0, 0x1CD0, 0x1CD2, 0x1CD2, 0x1CD5, 0x1CD6, 0x1CD8, 0x1CD8, 0x1CE1, 0x1CE1, 0x1CEA, 0x1CEA, 0x1CED, 0x1CED, 0x1CF2, 0x1CF2, 0x1CF5, 0x1CF7, 0xA8F1, 0xA8F1, }, normalizationFixes = m["Beng"].normalizationFixes, } m["Bhks"] = process_ranges{ "ไภกษุกี", 17017839, "abugida", aliases = {"Bhaiksuki"}, ranges = { 0x11C00, 0x11C08, 0x11C0A, 0x11C36, 0x11C38, 0x11C45, 0x11C50, 0x11C6C, }, } m["Blis"] = { "Blissymbolic", 609817, "logography", aliases = {"Blissymbols"}, -- Not in Unicode } m["Bopo"] = process_ranges{ "จู้อิน", 198269, "semisyllabary", aliases = {"จู้อินฝูเฮ่า", "ปอพอมอฟอ", "ปัวพัวมัวฟัว", "Zhuyin", "Zhuyin Fuhao", "Bopomofo"}, ranges = { 0x02EA, 0x02EB, 0x3001, 0x3003, 0x3008, 0x3011, 0x3013, 0x301F, 0x302A, 0x302D, 0x3030, 0x3030, 0x3037, 0x3037, 0x30FB, 0x30FB, 0x3105, 0x312F, 0x31A0, 0x31BF, 0xFE45, 0xFE46, 0xFF61, 0xFF65, }, } m["Brah"] = process_ranges{ "พราหมี", 185083, "abugida", aliases = {"พราหฺมี", "Brahmi"}, ranges = { 0x11000, 0x1104D, 0x11052, 0x11075, 0x1107F, 0x1107F, }, normalizationFixes = handle_normalization_fixes{ from = {"𑀅𑀸", "𑀋𑀾", "𑀏𑁂"}, to = {"𑀆", "𑀌", "𑀐"} }, translit = "Brah-translit", } m["Brai"] = process_ranges{ "เบรลล์", 79894, "alphabet", aliases = {"Braille"}, ranges = { 0x2800, 0x28FF, }, } m["Bugi"] = process_ranges{ "ลนตารา", 1074947, "abugida", aliases = {"บูกิส", "Lontara", "Buginese"}, ranges = { 0x1A00, 0x1A1B, 0x1A1E, 0x1A1F, 0xA9CF, 0xA9CF, }, } m["Buhd"] = process_ranges{ "บูฮิด", 1002969, "abugida", aliases = {"Buhid"}, ranges = { 0x1735, 0x1736, 0x1740, 0x1751, 0x1752, 0x1753, }, } m["Cakm"] = process_ranges{ "จักมา", 1059328, "abugida", aliases = {"Chakma"}, ranges = { 0x09E6, 0x09EF, 0x1040, 0x1049, 0x11100, 0x11134, 0x11136, 0x11147, }, } m["Cans"] = process_ranges{ "Canadian syllabic", 2479183, "abugida", ranges = { 0x1400, 0x167F, 0x18B0, 0x18F5, 0x11AB0, 0x11ABF, }, } m["Cari"] = process_ranges{ "คาเรีย", 1094567, "alphabet", aliases = {"Carian"}, ranges = { 0x102A0, 0x102D0, }, } m["Cham"] = process_ranges{ "จาม", 1060381, "abugida", aliases = {"Cham"}, ranges = { 0xAA00, 0xAA36, 0xAA40, 0xAA4D, 0xAA50, 0xAA59, 0xAA5C, 0xAA5F, }, } m["Cher"] = process_ranges{ "เชโรกี", 26549, "syllabary", aliases = {"Cherokee"}, ranges = { 0x13A0, 0x13F5, 0x13F8, 0x13FD, 0xAB70, 0xABBF, }, } m["Chis"] = { "Chisoi", 123173777, "abugida", -- Not in Unicode } m["Chrs"] = process_ranges{ "Khwarezmian", 72386710, "abjad", aliases = {"Chorasmian"}, ranges = { 0x10FB0, 0x10FCB, }, direction = "rtl", } m["Copt"] = process_ranges{ "คอปติก", 321083, "alphabet", aliases = {"Coptic"}, ranges = { 0x03E2, 0x03EF, 0x2C80, 0x2CF3, 0x2CF9, 0x2CFF, 0x102E0, 0x102FB, }, capitalized = true, } m["Cpmn"] = process_ranges{ "Cypro-Minoan", 1751985, "syllabary", aliases = {"Cypro Minoan"}, ranges = { 0x10100, 0x10101, 0x12F90, 0x12FF2, }, } m["Cprt"] = process_ranges{ "ไซปรัส", 1757689, "syllabary", aliases = {"ไซเปรียท", "Cypriot"}, ranges = { 0x10100, 0x10102, 0x10107, 0x10133, 0x10137, 0x1013F, 0x10800, 0x10805, 0x10808, 0x10808, 0x1080A, 0x10835, 0x10837, 0x10838, 0x1083C, 0x1083C, 0x1083F, 0x1083F, }, direction = "rtl", } m["Cyrl"] = process_ranges{ "ซีริลลิก", 8209, "alphabet", aliases = {"Cyrillic"}, ranges = { 0x0400, 0x052F, 0x1C80, 0x1C8A, 0x1D2B, 0x1D2B, 0x1D78, 0x1D78, 0x1DF8, 0x1DF8, 0x2DE0, 0x2DFF, 0x2E43, 0x2E43, 0xA640, 0xA69F, 0xFE2E, 0xFE2F, 0x1E030, 0x1E06D, 0x1E08F, 0x1E08F, }, capitalized = true, } m["Cyrs"] = { "ซีริลลิกเก่า", 442244, m["Cyrl"][3], aliases = {"ซีริลลิกช่วงต้น", "Old Cyrillic", "Early Cyrillic"}, ranges = m["Cyrl"].ranges, characters = m["Cyrl"].characters, capitalized = m["Cyrl"].capitalized, wikipedia_article = "Early Cyrillic alphabet", normalizationFixes = handle_normalization_fixes{ from = {"Ѹ", "ѹ"}, to = {"Ꙋ", "ꙋ"} }, strip_diacritics = {remove_diacritics = cs.Cyrs_remove_diacritics}, sort_key = { remove_diacritics = cs.Cyrs_remove_diacritics, from = { "ї", "оу", -- 2 chars "[ґꙣєѕꙃꙅꙁіꙇђꙉѻꙩꙫꙭꙮꚙꚛꙋѡѿꙍѽꙑѣꙗѥꙕѧꙙѩꙝꙛѫѭѯѱѳѵҁ]" }, to = { "и" .. p[1], "у", { ["ґ"] = "г" .. p[1], ["ꙣ"] = "д" .. p[1], ["є"] = "е", ["ѕ"] = "ж" .. p[1], ["ꙃ"] = "ж" .. p[1], ["ꙅ"] = "ж" .. p[1], ["ꙁ"] = "з", ["і"] = "и" .. p[1], ["ꙇ"] = "и" .. p[1], ["ђ"] = "и" .. p[2], ["ꙉ"] = "и" .. p[2], ["ѻ"] = "о", ["ꙩ"] = "о", ["ꙫ"] = "о", ["ꙭ"] = "о", ["ꙮ"] = "о", ["ꚙ"] = "о", ["ꚛ"] = "о", ["ꙋ"] = "у", ["ѡ"] = "х" .. p[1], ["ѿ"] = "х" .. p[1], ["ꙍ"] = "х" .. p[1], ["ѽ"] = "х" .. p[1], ["ꙑ"] = "ы", ["ѣ"] = "ь" .. p[1], ["ꙗ"] = "ь" .. p[2], ["ѥ"] = "ь" .. p[3], ["ꙕ"] = "ю", ["ѧ"] = "я", ["ꙙ"] = "я", ["ѩ"] = "я" .. p[1], ["ꙝ"] = "я" .. p[1], ["ꙛ"] = "я" .. p[2], ["ѫ"] = "я" .. p[3], ["ѭ"] = "я" .. p[4], ["ѯ"] = "я" .. p[5], ["ѱ"] = "я" .. p[6], ["ѳ"] = "я" .. p[7], ["ѵ"] = "я" .. p[8], ["ҁ"] = "я" .. p[9], } }, } } m["Deva"] = process_ranges{ "เทวนาครี", 38592, "abugida", aliases = {"Devanagari"}, ranges = { 0x0900, 0x097F, 0x1CD0, 0x1CF6, 0x1CF8, 0x1CF9, 0x20F0, 0x20F0, 0xA830, 0xA839, 0xA8E0, 0xA8FF, 0x11B00, 0x11B09, }, normalizationFixes = handle_normalization_fixes{ from = {"ॆॆ", "ेे", "ाॅ", "ाॆ", "ाꣿ", "ॊॆ", "ाे", "ाै", "ोे", "ाऺ", "ॖॖ", "अॅ", "अॆ", "अा", "एॅ", "एॆ", "एे", "एꣿ", "ऎॆ", "अॉ", "आॅ", "अॊ", "आॆ", "अो", "आे", "अौ", "आै", "ओे", "अऺ", "अऻ", "आऺ", "अाꣿ", "आꣿ", "ऒॆ", "अॖ", "अॗ", "ॶॖ", "्‍?ा"}, to = {"ꣿ", "ै", "ॉ", "ॊ", "ॏ", "ॏ", "ो", "ौ", "ौ", "ऻ", "ॗ", "ॲ", "ऄ", "आ", "ऍ", "ऎ", "ऐ", "ꣾ", "ꣾ", "ऑ", "ऑ", "ऒ", "ऒ", "ओ", "ओ", "औ", "औ", "औ", "ॳ", "ॴ", "ॴ", "ॵ", "ॵ", "ॵ", "ॶ", "ॷ", "ॷ"} }, } m["Diak"] = process_ranges{ "ดิเวส อกุรุ", 3307073, "abugida", aliases = {"ดิเวหิ อกุรุ", "Dhives Akuru", "Dhivehi Akuru", "Dives Akuru", "Divehi Akuru"}, ranges = { 0x11900, 0x11906, 0x11909, 0x11909, 0x1190C, 0x11913, 0x11915, 0x11916, 0x11918, 0x11935, 0x11937, 0x11938, 0x1193B, 0x11946, 0x11950, 0x11959, }, } m["Dogr"] = process_ranges{ "โฑครี", 72402987, "abugida", aliases = {"Dogri", "Dogra"}, ranges = { 0x0964, 0x096F, 0xA830, 0xA839, 0x11800, 0x1183B, }, } m["Dsrt"] = process_ranges{ "Deseret", 1200582, "alphabet", ranges = { 0x10400, 0x1044F, }, capitalized = true, } m["Dupl"] = process_ranges{ "Duployan", 5316025, "alphabet", ranges = { 0x1BC00, 0x1BC6A, 0x1BC70, 0x1BC7C, 0x1BC80, 0x1BC88, 0x1BC90, 0x1BC99, 0x1BC9C, 0x1BCA3, }, } m["Egyd"] = { "Demotic", 188519, "abjad, logography", -- Not in Unicode } m["Egyh"] = { "Hieratic", 208111, "abjad, logography", -- Unified with Egyptian hieroglyphic in Unicode } m["Egyp"] = process_ranges{ "ไฮเออโรกลีฟอียิปต์", 132659, "abjad, logography", aliases = {"Egyptian hieroglyphic"}, ranges = { 0x13000, 0x13455, 0x13460, 0x143FA, }, varieties = {"Hieratic"}, wikipedia_article = "ไฮเออโรกลีฟอียิปต์", normalizationFixes = handle_normalization_fixes{ from = {"𓃁", "𓆖"}, to = {"𓃀𓐶𓂝", "𓆓𓐳𓐷𓏏𓐰𓇿𓐸"} }, } m["Elba"] = process_ranges{ "เอลบาซาน", 1036714, "alphabet", aliases = {"Elbasan"}, ranges = { 0x10500, 0x10527, }, } m["Elym"] = process_ranges{ "Elymaic", 60744423, "abjad", ranges = { 0x10FE0, 0x10FF6, }, direction = "rtl", } m["Ethi"] = process_ranges{ "เอธิโอเปีย", 257634, "abugida", aliases = {"กืออึซ", "Ethiopic", "Ge'ez"}, ranges = { 0x1200, 0x1248, 0x124A, 0x124D, 0x1250, 0x1256, 0x1258, 0x1258, 0x125A, 0x125D, 0x1260, 0x1288, 0x128A, 0x128D, 0x1290, 0x12B0, 0x12B2, 0x12B5, 0x12B8, 0x12BE, 0x12C0, 0x12C0, 0x12C2, 0x12C5, 0x12C8, 0x12D6, 0x12D8, 0x1310, 0x1312, 0x1315, 0x1318, 0x135A, 0x135D, 0x137C, 0x1380, 0x1399, 0x2D80, 0x2D96, 0x2DA0, 0x2DA6, 0x2DA8, 0x2DAE, 0x2DB0, 0x2DB6, 0x2DB8, 0x2DBE, 0x2DC0, 0x2DC6, 0x2DC8, 0x2DCE, 0x2DD0, 0x2DD6, 0x2DD8, 0x2DDE, 0xAB01, 0xAB06, 0xAB09, 0xAB0E, 0xAB11, 0xAB16, 0xAB20, 0xAB26, 0xAB28, 0xAB2E, 0x1E7E0, 0x1E7E6, 0x1E7E8, 0x1E7EB, 0x1E7ED, 0x1E7EE, 0x1E7F0, 0x1E7FE, }, sort_key = "Ethi-sortkey", } m["Gara"] = process_ranges{ "Garay", 3095302, "alphabet", capitalized = true, direction = "rtl", ranges = { 0x060C, 0x060C, 0x061B, 0x061B, 0x061F, 0x061F, 0x10D40, 0x10D65, 0x10D69, 0x10D85, 0x10D8E, 0x10D8F, }, } m["Geok"] = process_ranges{ "Khutsuri", 1090055, "alphabet", ranges = { -- Ⴀ-Ⴭ is Asomtavruli, ⴀ-ⴭ is Nuskhuri 0x10A0, 0x10C5, 0x10C7, 0x10C7, 0x10CD, 0x10CD, 0x10FB, 0x10FB, 0x2D00, 0x2D25, 0x2D27, 0x2D27, 0x2D2D, 0x2D2D, }, varieties = {"Nuskhuri", "Asomtavruli"}, capitalized = true, translit = "Geok-translit", } m["Geor"] = process_ranges{ "จอร์เจีย", 3317411, "alphabet", aliases = {"Georgian"}, ranges = { -- ა-ჿ is lowercase Mkhedruli; Ა-Ჿ is uppercase Mkhedruli (Mtavruli) 0x0589, 0x0589, 0x10D0, 0x10FF, 0x1C90, 0x1CBA, 0x1CBD, 0x1CBF, }, varieties = {"Mkhedruli", "Mtavruli"}, capitalized = true, translit = "Geor-translit", } m["Glag"] = process_ranges{ "กลาโกลิติก", 145625, "alphabet", aliases = {"Glagolitic"}, ranges = { 0x0484, 0x0484, 0x0487, 0x0487, 0x0589, 0x0589, 0x10FB, 0x10FB, 0x2C00, 0x2C5F, 0x2E43, 0x2E43, 0xA66F, 0xA66F, 0x1E000, 0x1E006, 0x1E008, 0x1E018, 0x1E01B, 0x1E021, 0x1E023, 0x1E024, 0x1E026, 0x1E02A, }, capitalized = true, } m["Gong"] = process_ranges{ "คุญชลาโคณฑี", 18125340, "abugida", aliases = {"Gunjala Gondi"}, ranges = { 0x0964, 0x0965, 0x11D60, 0x11D65, 0x11D67, 0x11D68, 0x11D6A, 0x11D8E, 0x11D90, 0x11D91, 0x11D93, 0x11D98, 0x11DA0, 0x11DA9, }, } m["Gonm"] = process_ranges{ "มสรามโคณฑี", 16977603, "abugida", aliases = {"Masaram Gondi"}, ranges = { 0x0964, 0x0965, 0x11D00, 0x11D06, 0x11D08, 0x11D09, 0x11D0B, 0x11D36, 0x11D3A, 0x11D3A, 0x11D3C, 0x11D3D, 0x11D3F, 0x11D47, 0x11D50, 0x11D59, }, } m["Goth"] = process_ranges{ "กอท", 467784, "alphabet", aliases = {"กอทิก", "กอทิค", "กอธิก", "กอธิค", "โกทิก", "โกทิค", "โกธิก", "โกธิค", "Gothic"}, ranges = { 0x10330, 0x1034A, }, wikipedia_article = "Gothic alphabet", } m["Gran"] = process_ranges{ "ครันถะ", 1119274, "abugida", aliases = {"Grantha"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0BE6, 0x0BF3, 0x1CD0, 0x1CD0, 0x1CD2, 0x1CD3, 0x1CF2, 0x1CF4, 0x1CF8, 0x1CF9, 0x20F0, 0x20F0, 0x11300, 0x11303, 0x11305, 0x1130C, 0x1130F, 0x11310, 0x11313, 0x11328, 0x1132A, 0x11330, 0x11332, 0x11333, 0x11335, 0x11339, 0x1133B, 0x11344, 0x11347, 0x11348, 0x1134B, 0x1134D, 0x11350, 0x11350, 0x11357, 0x11357, 0x1135D, 0x11363, 0x11366, 0x1136C, 0x11370, 0x11374, 0x11FD0, 0x11FD1, 0x11FD3, 0x11FD3, }, } m["Grek"] = process_ranges{ "กรีก", 8216, "alphabet", aliases = {"Greek"}, ranges = { 0x0341, 0x0341, 0x0374, 0x0375, 0x037E, 0x037E, 0x0384, 0x038A, 0x038C, 0x038C, 0x038E, 0x03A1, 0x03A3, 0x03D7, 0x03DA, 0x03DB, 0x03DE, 0x03E1, 0x03F0, 0x03F1, 0x03F4, 0x03F4, 0x03FC, 0x03FC, 0x1D26, 0x1D2A, 0x1D5D, 0x1D61, 0x1D66, 0x1D6A, 0x1DBF, 0x1DBF, 0x2126, 0x2127, 0x2129, 0x2129, 0x213C, 0x2140, 0xAB65, 0xAB65, 0x10140, 0x1018E, 0x101A0, 0x101A0, 0x1D200, 0x1D245, }, capitalized = true, display_text = cs["Grek-displaytext"], strip_diacritics = cs["Grek-stripdiacritics"], sort_key = { remove_diacritics = "'ʼ;·`¨´῀" .. c.grave .. c.acute .. c.diaer .. c.caron .. c.turnedcommaabove .. c.commaabove .. c.revcommaabove .. c.macron .. c.breve .. c.diaerbelow .. c.brevebelow .. c.perispomeni .. c.ypogegrammeni .. c.RSQuo .. c.prime .. c.keraia .. c.lowerkeraia .. c.tonos .. c.coronis .. c.psili .. c.dasia, from = {"ϝ", "ͷ", "ϛ", "ͱ", "ͺ", "ϳ", "ϻ", "[ϟϙ]", "[ςϲ]", "ͳ"}, to = {"ε" .. p[1], "ε" .. p[2], "ε" .. p[3], "ζ" .. p[1], "ι", "ι" .. p[1], "π" .. p[1], "π" .. p[2], "σ", "ϡ"}, }, } m["Polyt"] = process_ranges{ "กรีก", 1475332, m["Grek"][3], aliases = m["Grek"].aliases, ranges = union(m["Grek"].ranges, { 0x0340, 0x0340, 0x0342, 0x0345, 0x0370, 0x0373, 0x0376, 0x0377, 0x037A, 0x037D, 0x037F, 0x037F, 0x03D8, 0x03D9, 0x03DC, 0x03DD, 0x03F2, 0x03F3, 0x03F5, 0x03FB, 0x03FD, 0x03FF, 0x1F00, 0x1F15, 0x1F18, 0x1F1D, 0x1F20, 0x1F45, 0x1F48, 0x1F4D, 0x1F50, 0x1F57, 0x1F59, 0x1F59, 0x1F5B, 0x1F5B, 0x1F5D, 0x1F5D, 0x1F5F, 0x1F7D, 0x1F80, 0x1FB4, 0x1FB6, 0x1FC4, 0x1FC6, 0x1FD3, 0x1FD6, 0x1FDB, 0x1FDD, 0x1FEF, 0x1FF2, 0x1FF4, 0x1FF6, 0x1FFE, }), ietf_subtag = "Grek", capitalized = m["Grek"].capitalized, parent = "Grek", display_text = m["Grek"].display_text, strip_diacritics = "Polyt-stripdiacritics", sort_key = m["Grek"].sort_key, translit = "grc-translit", } m["Gujr"] = process_ranges{ "คุชราต", 733944, "abugida", aliases = {"คุชราตี", "Gujarati"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0A81, 0x0A83, 0x0A85, 0x0A8D, 0x0A8F, 0x0A91, 0x0A93, 0x0AA8, 0x0AAA, 0x0AB0, 0x0AB2, 0x0AB3, 0x0AB5, 0x0AB9, 0x0ABC, 0x0AC5, 0x0AC7, 0x0AC9, 0x0ACB, 0x0ACD, 0x0AD0, 0x0AD0, 0x0AE0, 0x0AE3, 0x0AE6, 0x0AF1, 0x0AF9, 0x0AFF, 0xA830, 0xA839, }, normalizationFixes = handle_normalization_fixes{ from = {"ઓ", "અાૈ", "અા", "અૅ", "અે", "અૈ", "અૉ", "અો", "અૌ", "આૅ", "આૈ", "ૅા"}, to = {"અાૅ", "ઔ", "આ", "ઍ", "એ", "ઐ", "ઑ", "ઓ", "ઔ", "ઓ", "ઔ", "ૉ"} }, } m["Gukh"] = process_ranges{ "Khema", 110064239, "abugida", aliases = {"Gurung Khema", "Khema Phri", "Khema Lipi"}, ranges = { 0x0965, 0x0965, 0x16100, 0x16139, }, } m["Guru"] = process_ranges{ "คุรมุขี", 689894, "abugida", aliases = {"กูร์มูคี", "เกอร์มุกห์", "Gurmukhi"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0A01, 0x0A03, 0x0A05, 0x0A0A, 0x0A0F, 0x0A10, 0x0A13, 0x0A28, 0x0A2A, 0x0A30, 0x0A32, 0x0A33, 0x0A35, 0x0A36, 0x0A38, 0x0A39, 0x0A3C, 0x0A3C, 0x0A3E, 0x0A42, 0x0A47, 0x0A48, 0x0A4B, 0x0A4D, 0x0A51, 0x0A51, 0x0A59, 0x0A5C, 0x0A5E, 0x0A5E, 0x0A66, 0x0A76, 0xA830, 0xA839, }, normalizationFixes = handle_normalization_fixes{ from = {"ਅਾ", "ਅੈ", "ਅੌ", "ੲਿ", "ੲੀ", "ੲੇ", "ੳੁ", "ੳੂ", "ੳੋ"}, to = {"ਆ", "ਐ", "ਔ", "ਇ", "ਈ", "ਏ", "ਉ", "ਊ", "ਓ"} }, } m["Hang"] = process_ranges{ "ฮันกึล", 8222, "syllabary", aliases = {"ฮังกึล", "Hangul", "Hangeul"}, ranges = { 0x1100, 0x11FF, 0x3001, 0x3003, 0x3008, 0x3011, 0x3013, 0x301F, 0x302E, 0x3030, 0x3037, 0x3037, 0x30FB, 0x30FB, 0x3131, 0x318E, 0x3200, 0x321E, 0x3260, 0x327E, 0xA960, 0xA97C, 0xAC00, 0xD7A3, 0xD7B0, 0xD7C6, 0xD7CB, 0xD7FB, 0xFE45, 0xFE46, 0xFF61, 0xFF65, 0xFFA0, 0xFFBE, 0xFFC2, 0xFFC7, 0xFFCA, 0xFFCF, 0xFFD2, 0xFFD7, 0xFFDA, 0xFFDC, }, } m["Hani"] = process_ranges{ "จีน", 8201, "logography", aliases = {"ฮั่น", "Han"}, ranges = { 0x2E80, 0x2E99, 0x2E9B, 0x2EF3, 0x2F00, 0x2FD5, 0x2FF0, 0x2FFF, 0x3001, 0x3003, 0x3005, 0x3011, 0x3013, 0x301F, 0x3021, 0x302D, 0x3030, 0x3030, 0x3037, 0x303F, 0x3190, 0x319F, 0x31C0, 0x31E5, 0x31EF, 0x31EF, 0x3220, 0x3247, 0x3280, 0x32B0, 0x32C0, 0x32CB, 0x30FB, 0x30FB, 0x32FF, 0x32FF, 0x3358, 0x3370, 0x337B, 0x337F, 0x33E0, 0x33FE, 0x3400, 0x4DBF, 0x4E00, 0x9FFF, 0xA700, 0xA707, 0xF900, 0xFA6D, 0xFA70, 0xFAD9, 0xFE45, 0xFE46, 0xFF61, 0xFF65, 0x16FE2, 0x16FE3, 0x16FF0, 0x16FF1, 0x1D360, 0x1D371, 0x1F250, 0x1F251, 0x20000, 0x2A6DF, 0x2A700, 0x2B739, 0x2B740, 0x2B81D, 0x2B820, 0x2CEA1, 0x2CEB0, 0x2EBE0, 0x2EBF0, 0x2EE5D, 0x2F800, 0x2FA1D, 0x30000, 0x3134A, 0x31350, 0x323AF, }, varieties = {{"ฮั่นจื่อ", "Hanzi"}, {"คันจิ", "Kanji"}, {"ฮันจา", "Hanja"}, {"จื๋อโนม", "Chu Nom"}}, spaces = false, } m["Hans"] = { "จีนตัวย่อ", 185614, m["Hani"][3], aliases = {"Simplified Han"}, ranges = m["Hani"].ranges, characters = m["Hani"].characters, spaces = m["Hani"].spaces, parent = "Hani", } m["Hant"] = { "จีนตัวเต็ม", 178528, m["Hani"][3], aliases = {"Traditional Han"}, ranges = m["Hani"].ranges, characters = m["Hani"].characters, spaces = m["Hani"].spaces, parent = "Hani", } m["Hano"] = process_ranges{ "ฮานูโนโอ", 1584045, "abugida", aliases = {"Hanunoo", "Hanunó'o", "Hanuno'o"}, ranges = { 0x1720, 0x1736, }, } m["Hatr"] = process_ranges{ "Hatran", 20813038, "abjad", ranges = { 0x108E0, 0x108F2, 0x108F4, 0x108F5, 0x108FB, 0x108FF, }, direction = "rtl", } m["Hebr"] = process_ranges{ "ฮีบรู", 33513, "abjad", -- more precisely, impure abjad aliases = {"Hebrew"}, ranges = { 0x0591, 0x05C7, 0x05D0, 0x05EA, 0x05EF, 0x05F4, 0x2135, 0x2138, 0xFB1D, 0xFB36, 0xFB38, 0xFB3C, 0xFB3E, 0xFB3E, 0xFB40, 0xFB41, 0xFB43, 0xFB44, 0xFB46, 0xFB4F, }, direction = "rtl", display_text = "Hebr-common", sort_key = "Hebr-common", strip_diacritics = "Hebr-common", } m["Hira"] = process_ranges{ "ฮิรางานะ", 48332, "syllabary", aliases = {"ฮิระงะนะ", "Hiragana"}, ranges = { 0x3001, 0x3003, 0x3008, 0x3011, 0x3013, 0x301F, 0x3030, 0x3035, 0x3037, 0x3037, 0x303C, 0x303D, 0x3041, 0x3096, 0x3099, 0x30A0, 0x30FB, 0x30FC, 0xFE45, 0xFE46, 0xFF61, 0xFF65, 0xFF70, 0xFF70, 0xFF9E, 0xFF9F, 0x1B001, 0x1B11F, 0x1B132, 0x1B132, 0x1B150, 0x1B152, 0x1F200, 0x1F200, }, varieties = {{"เฮ็นไตงานะ", "Hentaigana"}}, spaces = false, } m["Hluw"] = process_ranges{ "ไฮเออโรกลีฟอานาโตเลีย", 521323, "logography, syllabary", aliases = {"Anatolian Hieroglyphs"}, ranges = { 0x14400, 0x14646, }, wikipedia_article = "ไฮเออโรกลีฟอานาโตเลีย", } m["Hmng"] = process_ranges{ "Pahawh Hmong", 365954, "semisyllabary", aliases = {"ม้ง", "Hmong"}, ranges = { 0x16B00, 0x16B45, 0x16B50, 0x16B59, 0x16B5B, 0x16B61, 0x16B63, 0x16B77, 0x16B7D, 0x16B8F, }, } m["Hmnp"] = process_ranges{ "Nyiakeng Puachue Hmong", 33712499, "alphabet", ranges = { 0x1E100, 0x1E12C, 0x1E130, 0x1E13D, 0x1E140, 0x1E149, 0x1E14E, 0x1E14F, }, } m["Hung"] = process_ranges{ "ฮังการีเก่า", 446224, "alphabet", aliases = {"รูนฮังการี", "Old Hungarian", "Hungarian runic"}, ranges = { 0x10C80, 0x10CB2, 0x10CC0, 0x10CF2, 0x10CFA, 0x10CFF, }, capitalized = true, direction = "rtl", } m["Ibrnn"] = { "ไอบีเรียตะวันออกเฉียงเหนือ", 1113155, "semisyllabary", aliases = {"Northeastern Iberian"}, ietf_subtag = "Zzzz", -- Not in Unicode } m["Ibrns"] = { "ไอบีเรียตะวันออกเฉียงใต้", 2305351, "semisyllabary", aliases = {"Southeastern Iberian"}, ietf_subtag = "Zzzz", -- Not in Unicode } m["Image"] = { -- To be used to avoid any formatting or link processing "Image-rendered", 478798, -- This should not have any characters listed ietf_subtag = "Zyyy", translit = false, character_category = false, -- none } m["Inds"] = { "สินธุ", 601388, aliases = {"อินดัส", "Indus", "Harappan", "Indus Valley"}, } m["Ipach"] = { "สัทอักษรสากล", 21204, aliases = {"International Phonetic Alphabet", "IPA"}, ietf_subtag = "Latn", } m["Ital"] = process_ranges{ "อิตาลิกเก่า", 4891256, "alphabet", aliases = {"Old Italic"}, ranges = { 0x10300, 0x10323, 0x1032D, 0x1032F, }, translit = "Ital-translit", } m["Java"] = process_ranges{ "ชวา", 879704, "abugida", aliases = {"Javanese"}, ranges = { 0xA980, 0xA9CD, 0xA9CF, 0xA9D9, 0xA9DE, 0xA9DF, }, } m["Jurc"] = { "จูร์เชน", 912240, "logography", aliases = {"Jurchen"}, spaces = false, } m["Kali"] = process_ranges{ "กะยา", 4919239, "abugida", aliases = {"Kayah Li"}, ranges = { 0xA900, 0xA92F, }, } m["Kana"] = process_ranges{ "คาตากานะ", 82946, "syllabary", aliases = {"คะตะกะนะ", "Katakana"}, ranges = { 0x3001, 0x3003, 0x3008, 0x3011, 0x3013, 0x301F, 0x3030, 0x3035, 0x3037, 0x3037, 0x303C, 0x303D, 0x3099, 0x309C, 0x30A0, 0x30FF, 0x31F0, 0x31FF, 0x32D0, 0x32FE, 0x3300, 0x3357, 0xFE45, 0xFE46, 0xFF61, 0xFF9F, 0x1AFF0, 0x1AFF3, 0x1AFF5, 0x1AFFB, 0x1AFFD, 0x1AFFE, 0x1B000, 0x1B000, 0x1B120, 0x1B122, 0x1B155, 0x1B155, 0x1B164, 0x1B167, }, spaces = false, } m["Kawi"] = process_ranges{ "กวิ", 975802, "abugida", aliases = {"Kawi"}, ranges = { 0x11F00, 0x11F10, 0x11F12, 0x11F3A, 0x11F3E, 0x11F5A, }, } m["Khar"] = process_ranges{ "ขโรษฐี", 1161266, "abugida", aliases = {"Kharoshthi"}, ranges = { 0x10A00, 0x10A03, 0x10A05, 0x10A06, 0x10A0C, 0x10A13, 0x10A15, 0x10A17, 0x10A19, 0x10A35, 0x10A38, 0x10A3A, 0x10A3F, 0x10A48, 0x10A50, 0x10A58, }, direction = "rtl", } m["Khmr"] = process_ranges{ "เขมร", 1054190, "abugida", aliases = {"Khmer"}, ranges = { 0x1780, 0x17DD, 0x17E0, 0x17E9, 0x17F0, 0x17F9, 0x19E0, 0x19FF, }, spaces = false, normalizationFixes = handle_normalization_fixes{ from = {"ឣ", "ឤ"}, to = {"អ", "អា"} }, } m["Khoj"] = process_ranges{ "Khojki", 1740656, "abugida", ranges = { 0x0AE6, 0x0AEF, 0xA830, 0xA839, 0x11200, 0x11211, 0x11213, 0x11241, }, normalizationFixes = handle_normalization_fixes{ from = {"𑈀𑈬𑈱", "𑈀𑈬", "𑈀𑈱", "𑈀𑈳", "𑈁𑈱", "𑈆𑈬", "𑈬𑈰", "𑈬𑈱", "𑉀𑈮"}, to = {"𑈇", "𑈁", "𑈅", "𑈇", "𑈇", "𑈃", "𑈲", "𑈳", "𑈂"} }, } m["Khomt"] = { "ขอมไทย", 13023788, "abugida", aliases = {"Khom Thai"}, -- Not in Unicode } m["Kitl"] = { "คีตันใหญ่", 6401797, "logography", aliases = {"Khitan Large"}, spaces = false, } m["Kits"] = process_ranges{ "คีตันเล็ก", 6401800, "logography, syllabary", aliases = {"Khitan Small"}, ranges = { 0x16FE4, 0x16FE4, 0x18B00, 0x18CD5, 0x18CFF, 0x18CFF, }, spaces = false, } m["Knda"] = process_ranges{ "กันนฑะ", 839666, "abugida", aliases = {"กันนาดา", "Kannada"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0C80, 0x0C8C, 0x0C8E, 0x0C90, 0x0C92, 0x0CA8, 0x0CAA, 0x0CB3, 0x0CB5, 0x0CB9, 0x0CBC, 0x0CC4, 0x0CC6, 0x0CC8, 0x0CCA, 0x0CCD, 0x0CD5, 0x0CD6, 0x0CDD, 0x0CDE, 0x0CE0, 0x0CE3, 0x0CE6, 0x0CEF, 0x0CF1, 0x0CF3, 0x1CD0, 0x1CD0, 0x1CD2, 0x1CD3, 0x1CDA, 0x1CDA, 0x1CF2, 0x1CF2, 0x1CF4, 0x1CF4, 0xA830, 0xA835, }, normalizationFixes = handle_normalization_fixes{ from = {"ಉಾ", "ಋಾ", "ಒೌ"}, to = {"ಊ", "ೠ", "ಔ"} }, translit = "Knda-translit", } m["Kpel"] = { "Kpelle", 1586299, "syllabary", -- Not in Unicode } m["Krai"] = process_ranges{ "Kirat Rai", 123173834, "abugida", aliases = {"Rai", "Khambu Rai", "Rai Barṇamālā", "Kirat Khambu Rai"}, ranges = { 0x16D40, 0x16D79, }, } m["Kthi"] = process_ranges{ "ไกถี", 1253814, "abugida", aliases = {"Kaithi"}, ranges = { 0x0966, 0x096F, 0xA830, 0xA839, 0x11080, 0x110C2, 0x110CD, 0x110CD, }, } m["Kulit"] = { "Kulitan", 6443044, "abugida", -- Not in Unicode } m["Lana"] = process_ranges{ "ไทธรรม", 1314503, "abugida", aliases = {"ธรรม", "ตัวเมือง", "ล้านนา", "Tai Tham", "Tham", "Tua Mueang", "Lanna"}, ranges = { 0x1A20, 0x1A5E, 0x1A60, 0x1A7C, 0x1A7F, 0x1A89, 0x1A90, 0x1A99, 0x1AA0, 0x1AAD, }, spaces = false, } m["Laoo"] = process_ranges{ "ลาว", 1815229, "abugida", aliases = {"Lao"}, ranges = { 0x0E81, 0x0E82, 0x0E84, 0x0E84, 0x0E86, 0x0E8A, 0x0E8C, 0x0EA3, 0x0EA5, 0x0EA5, 0x0EA7, 0x0EBD, 0x0EC0, 0x0EC4, 0x0EC6, 0x0EC6, 0x0EC8, 0x0ECE, 0x0ED0, 0x0ED9, 0x0EDC, 0x0EDF, }, spaces = false, } m["Latn"] = process_ranges{ "ละติน", 8229, "alphabet", aliases = {"ลาติน", "โรมัน", "Latin", "Roman"}, ranges = { 0x0041, 0x005A, 0x0061, 0x007A, 0x00AA, 0x00AA, 0x00BA, 0x00BA, 0x00C0, 0x00D6, 0x00D8, 0x00F6, 0x00F8, 0x02B8, 0x02C0, 0x02C1, 0x02E0, 0x02E4, 0x0363, 0x036F, 0x0485, 0x0486, 0x0951, 0x0952, 0x10FB, 0x10FB, 0x1D00, 0x1D25, 0x1D2C, 0x1D5C, 0x1D62, 0x1D65, 0x1D6B, 0x1D77, 0x1D79, 0x1DBE, 0x1DF8, 0x1DF8, 0x1E00, 0x1EFF, 0x202F, 0x202F, 0x2071, 0x2071, 0x207F, 0x207F, 0x2090, 0x209C, 0x20F0, 0x20F0, 0x2100, 0x2125, 0x2128, 0x2128, 0x212A, 0x2134, 0x2139, 0x213B, 0x2141, 0x214E, 0x2160, 0x2188, 0x2C60, 0x2C7F, 0xA700, 0xA707, 0xA722, 0xA787, 0xA78B, 0xA7CD, 0xA7D0, 0xA7D1, 0xA7D3, 0xA7D3, 0xA7D5, 0xA7DC, 0xA7F2, 0xA7FF, 0xA92E, 0xA92E, 0xAB30, 0xAB5A, 0xAB5C, 0xAB64, 0xAB66, 0xAB69, 0xFB00, 0xFB06, 0xFF21, 0xFF3A, 0xFF41, 0xFF5A, 0x10780, 0x10785, 0x10787, 0x107B0, 0x107B2, 0x107BA, 0x1DF00, 0x1DF1E, 0x1DF25, 0x1DF2A, }, varieties = {{"รูมี", "Rumi"}, {"โรมาจิ", "โรมะจิ", "Romaji", "Rōmaji"}, "Romaja"}, capitalized = true, translit = false, } m["Latf"] = { "ฟรักทูร์", 148443, m["Latn"][3], aliases = {"Fraktur"}, ranges = m["Latn"].ranges, characters = m["Latn"].characters, other_names = {"แบล็กเลตเทอร์", "Blackletter"}, -- Blackletter is actually the parent "script" capitalized = m["Latn"].capitalized, translit = m["Latn"].translit, parent = "Latn", } m["Latg"] = { "แกลิก", 1432616, m["Latn"][3], aliases = {"Gaelic"}, ranges = m["Latn"].ranges, characters = m["Latn"].characters, other_names = {"ไอริช", "Irish"}, capitalized = m["Latn"].capitalized, translit = m["Latn"].translit, parent = "Latn", } m["pjt-Latn"] = { "ละติน", nil, m["Latn"][3], aliases = m["Latn"].aliases, ranges = m["Latn"].ranges, characters = m["Latn"].characters, capitalized = m["Latn"].capitalized, translit = m["Latn"].translit, parent = "Latn", } m["Leke"] = { "Leke", 19572613, "abugida", -- Not in Unicode } m["Lepc"] = process_ranges{ "เลปชา", 1481626, "abugida", aliases = {"Lepcha"}, ranges = { 0x1C00, 0x1C37, 0x1C3B, 0x1C49, 0x1C4D, 0x1C4F, }, } m["Limb"] = process_ranges{ "ลิมบู", 933796, "abugida", aliases = {"Limbu"}, ranges = { 0x0965, 0x0965, 0x1900, 0x191E, 0x1920, 0x192B, 0x1930, 0x193B, 0x1940, 0x1940, 0x1944, 0x194F, }, } m["Lina"] = process_ranges{ "ลิเนียร์เอ", 30972, aliases = {"Linear A"}, ranges = { 0x10107, 0x10133, 0x10600, 0x10736, 0x10740, 0x10755, 0x10760, 0x10767, }, } m["Linb"] = process_ranges{ "ลิเนียร์บี", 190102, aliases = {"Linear B"}, ranges = { 0x10000, 0x1000B, 0x1000D, 0x10026, 0x10028, 0x1003A, 0x1003C, 0x1003D, 0x1003F, 0x1004D, 0x10050, 0x1005D, 0x10080, 0x100FA, 0x10100, 0x10102, 0x10107, 0x10133, 0x10137, 0x1013F, }, } m["Lisu"] = process_ranges{ "ลีสู่", 1194621, "alphabet", aliases = {"ฟราเซอร์", "Lisu", "Fraser"}, ranges = { 0x300A, 0x300B, 0xA4D0, 0xA4FF, 0x11FB0, 0x11FB0, }, normalizationFixes = handle_normalization_fixes{ from = {"['’]", "[.ꓸ][.ꓸ]", "[.ꓸ][,ꓹ]"}, to = {"ʼ", "ꓺ", "ꓻ"} }, translit = "Lisu-translit", sort_key = { from = {"𑾰"}, to = {"ꓬ" .. p[1]} }, } m["Loma"] = { "Loma", 13023816, "syllabary", -- Not in Unicode } m["Lyci"] = process_ranges{ "ลิเชีย", 913587, "alphabet", aliases = {"Lycian"}, ranges = { 0x10280, 0x1029C, }, } m["Lydi"] = process_ranges{ "ลิเดีย", 4261300, "alphabet", aliases = {"Lydian"}, ranges = { 0x10920, 0x10939, 0x1093F, 0x1093F, }, direction = "rtl", } m["Mahj"] = process_ranges{ "มหาชนี", 6732850, "abugida", aliases = {"Mahajani"}, ranges = { 0x0964, 0x096F, 0xA830, 0xA839, 0x11150, 0x11176, }, } m["Maka"] = process_ranges{ "มากัซซาร์", 72947229, "abugida", aliases = {"มากาซาร์", "มากาซาร์เก่า", "Makasar", "Makassar", "Old Makasar"}, ranges = { 0x11EE0, 0x11EF8, }, } m["Mand"] = process_ranges{ "มันดาอิก", 1812130, aliases = {"Mandaic", "Mandaean"}, ranges = { 0x0640, 0x0640, 0x0840, 0x085B, 0x085E, 0x085E, }, direction = "rtl", } m["Mani"] = process_ranges{ "Manichaean", 3544702, "abjad", ranges = { 0x0640, 0x0640, 0x10AC0, 0x10AE6, 0x10AEB, 0x10AF6, }, direction = "rtl", translit = "Mani-translit", } m["Marc"] = process_ranges{ "Marchen", 72403709, "abugida", ranges = { 0x11C70, 0x11C8F, 0x11C92, 0x11CA7, 0x11CA9, 0x11CB6, }, } m["Maya"] = process_ranges{ "มายา", 211248, aliases = {"มายัน", "Maya", "Maya hieroglyphic", "Mayan", "Mayan hieroglyphic"}, ranges = { 0x1D2E0, 0x1D2F3, }, } m["Medf"] = process_ranges{ "Medefaidrin", 1519764, aliases = {"Oberi Okaime", "Oberi Ɔkaimɛ"}, ranges = { 0x16E40, 0x16E9A, }, capitalized = true, } m["Mend"] = process_ranges{ "Mende", 951069, aliases = {"Mende Kikakui"}, ranges = { 0x1E800, 0x1E8C4, 0x1E8C7, 0x1E8D6, }, direction = "rtl", } m["Merc"] = process_ranges{ "Meroitic cursive", 73028124, "abugida", ranges = { 0x109A0, 0x109B7, 0x109BC, 0x109CF, 0x109D2, 0x109FF, }, direction = "rtl", } m["Mero"] = process_ranges{ "Meroitic hieroglyphic", 73028623, "abugida", ranges = { 0x10980, 0x1099F, }, direction = "rtl", wikipedia_article = "Meroitic hieroglyphs", } m["Mlym"] = process_ranges{ "มลยาฬัม", 1164129, "abugida", aliases = {"มลยาลัม", "มาลายาลัม", "Malayalam"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0D00, 0x0D0C, 0x0D0E, 0x0D10, 0x0D12, 0x0D44, 0x0D46, 0x0D48, 0x0D4A, 0x0D4F, 0x0D54, 0x0D63, 0x0D66, 0x0D7F, 0x1CDA, 0x1CDA, 0x1CF2, 0x1CF2, 0xA830, 0xA832, }, normalizationFixes = handle_normalization_fixes{ from = {"ഇൗ", "ഉൗ", "എെ", "ഒാ", "ഒൗ", "ക്‍", "ണ്‍", "ന്‍റ", "ന്‍", "മ്‍", "യ്‍", "ര്‍", "ല്‍", "ള്‍", "ഴ്‍", "െെ", "ൻ്റ"}, to = {"ഈ", "ഊ", "ഐ", "ഓ", "ഔ", "ൿ", "ൺ", "ൻറ", "ൻ", "ൔ", "ൕ", "ർ", "ൽ", "ൾ", "ൖ", "ൈ", "ന്റ"} }, translit = "Mlym-translit", } m["Modi"] = process_ranges{ "โมฑี", 1703713, "abugida", aliases = {"Modi"}, ranges = { 0xA830, 0xA839, 0x11600, 0x11644, 0x11650, 0x11659, }, normalizationFixes = handle_normalization_fixes{ from = {"𑘀𑘹", "𑘀𑘺", "𑘁𑘹", "𑘁𑘺"}, to = {"𑘊", "𑘋", "𑘌", "𑘍"} }, } do local Mong_displaytext = { from = {"([ᠨ-ᡂᡸ])ᠶ([ᠨ-ᡂᡸ])", "([ᠠ-ᡂᡸ])ᠸ([^᠋ᠠ-ᠧ])", "([ᠠ-ᡂᡸ])ᠸ$"}, to = {"%1ᠢ%2", "%1ᠧ%2", "%1ᠧ"} } m["Mong"] = process_ranges{ "มองโกเลีย", 1055705, "alphabet", aliases = {"Mongolian", "Mongol bichig", "Hudum Mongol bichig"}, ranges = { 0x1800, 0x1805, 0x180A, 0x1819, 0x1820, 0x1842, 0x1878, 0x1878, 0x1880, 0x1897, 0x18A6, 0x18A6, 0x18A9, 0x18A9, 0x200C, 0x200D, 0x202F, 0x202F, 0x3001, 0x3002, 0x3008, 0x300B, 0x11660, 0x11668, }, direction = "vertical-ltr", display_text = Mong_displaytext, strip_diacritics = Mong_displaytext, translit = "Mong-translit", } m["mnc-Mong"] = process_ranges{ "แมนจู", 122888, m["Mong"][3], aliases = {"Manchu"}, ranges = { 0x1801, 0x1801, 0x1804, 0x1804, 0x1808, 0x180F, 0x1820, 0x1820, 0x1823, 0x1823, 0x1828, 0x182A, 0x182E, 0x1830, 0x1834, 0x1838, 0x183A, 0x183A, 0x185D, 0x185D, 0x185F, 0x1861, 0x1864, 0x1869, 0x186C, 0x1871, 0x1873, 0x1877, 0x1880, 0x1888, 0x188F, 0x188F, 0x189A, 0x18A5, 0x18A8, 0x18A8, 0x18AA, 0x18AA, 0x200C, 0x200D, 0x202F, 0x202F, }, direction = "vertical-ltr", parent = "Mong", translit = "mnc-translit", } m["sjo-Mong"] = process_ranges{ "ซิเบ", 113624153, m["Mong"][3], aliases = {"Xibe", "Sibe"}, ranges = { 0x1804, 0x1804, 0x1807, 0x1807, 0x180A, 0x180F, 0x1820, 0x1820, 0x1823, 0x1823, 0x1828, 0x1828, 0x182A, 0x182A, 0x182E, 0x1830, 0x1834, 0x1838, 0x183A, 0x183A, 0x185D, 0x1872, 0x200C, 0x200D, 0x202F, 0x202F, }, direction = "vertical-ltr", parent = "mnc-Mong", } m["xwo-Mong"] = process_ranges{ "Clear Script", 529085, m["Mong"][3], aliases = {"Todo", "Todo bichig"}, ranges = { 0x1800, 0x1801, 0x1804, 0x1806, 0x180A, 0x1820, 0x1828, 0x1828, 0x182F, 0x1831, 0x1834, 0x1834, 0x1837, 0x1838, 0x183A, 0x183B, 0x1840, 0x1840, 0x1843, 0x185C, 0x1880, 0x1887, 0x1889, 0x188F, 0x1894, 0x1894, 0x1896, 0x1899, 0x18A7, 0x18A7, 0x200C, 0x200D, 0x202F, 0x202F, 0x11669, 0x1166C, }, direction = "vertical-ltr", parent = "Mong", translit = "xwo-translit", } end m["Moon"] = { "Moon", 918391, "alphabet", aliases = {"Moon System of Embossed Reading", "Moon type", "Moon writing", "Moon alphabet", "Moon code"}, -- Not in Unicode } m["Morse"] = { "รหัสมอร์ส", 79897, aliases = {"Morse code"}, ietf_subtag = "Zsym", } m["Mroo"] = process_ranges{ "มรู", 75919253, aliases = {"มโร", "มรุง", "Mru", "Mro", "Mrung"}, ranges = { 0x16A40, 0x16A5E, 0x16A60, 0x16A69, 0x16A6E, 0x16A6F, }, } m["Mtei"] = process_ranges{ "มณีปุระ", 2981413, "abugida", aliases = {"ไมไตมะเยก", "มีไตมะเยก", "Manipuri", "Meitei Mayek", "Meetei Mayek"}, ranges = { 0xAAE0, 0xAAF6, 0xABC0, 0xABED, 0xABF0, 0xABF9, }, } m["Mult"] = process_ranges{ "Multani", 17047906, "abugida", ranges = { 0x0A66, 0x0A6F, 0x11280, 0x11286, 0x11288, 0x11288, 0x1128A, 0x1128D, 0x1128F, 0x1129D, 0x1129F, 0x112A9, }, } m["Music"] = process_ranges{ "สัญกรณ์ดนตรี", 233861, "pictography", aliases = {"Musical notation"}, ranges = { 0x2669, 0x266F, 0x1D100, 0x1D126, 0x1D129, 0x1D1EA, }, ietf_subtag = "Zsym", translit = false, } m["Mymr"] = process_ranges{ "พม่า", 43887939, "abugida", aliases = {"เมียนมา", "Burmese", "Myanmar"}, ranges = { 0x1000, 0x109F, 0xA92E, 0xA92E, 0xA9E0, 0xA9FE, 0xAA60, 0xAA7F, 0x116D0, 0x116E3, }, spaces = false, } m["Nagm"] = process_ranges{ "Mundari Bani", 106917274, "alphabet", aliases = {"Nag Mundari"}, ranges = { 0x1E4D0, 0x1E4F9, }, } m["Nand"] = process_ranges{ "นันทินาครี", 6963324, "abugida", aliases = {"Nandinagari"}, ranges = { 0x0964, 0x0965, 0x0CE6, 0x0CEF, 0x1CE9, 0x1CE9, 0x1CF2, 0x1CF2, 0x1CFA, 0x1CFA, 0xA830, 0xA835, 0x119A0, 0x119A7, 0x119AA, 0x119D7, 0x119DA, 0x119E4, }, } m["Narb"] = process_ranges{ "Ancient North Arabian", 1472213, "abjad", aliases = {"Old North Arabian"}, ranges = { 0x10A80, 0x10A9F, }, direction = "rtl", translit = "Narb-translit", } m["Nbat"] = process_ranges{ "Nabataean", 855624, "abjad", aliases = {"Nabatean"}, ranges = { 0x10880, 0x1089E, 0x108A7, 0x108AF, }, direction = "rtl", } m["Newa"] = process_ranges{ "Newa", 7237292, "abugida", aliases = {"Newar", "Newari", "Prachalit Nepal"}, ranges = { 0x11400, 0x1145B, 0x1145D, 0x11461, }, } m["Nkdb"] = { "Dongba", 1190953, "pictography", aliases = {"Naxi Dongba", "Nakhi Dongba", "Tomba", "Tompa", "Mo-so"}, spaces = false, -- Not in Unicode } m["Nkgb"] = { "Geba", 731189, "syllabary", aliases = {"Nakhi Geba", "Naxi Geba"}, spaces = false, -- Not in Unicode } m["Nkoo"] = process_ranges{ "N'Ko", 1062587, "alphabet", ranges = { 0x060C, 0x060C, 0x061B, 0x061B, 0x061F, 0x061F, 0x07C0, 0x07FA, 0x07FD, 0x07FF, 0xFD3E, 0xFD3F, }, direction = "rtl", } m["None"] = { "ไม่ระบุ", nil, aliases = {"unspecified"}, -- This should not have any characters listed ietf_subtag = "Zyyy", translit = false, character_category = false, -- none } m["Nshu"] = process_ranges{ "Nüshu", 56436, "syllabary", aliases = {"Nushu"}, ranges = { 0x16FE1, 0x16FE1, 0x1B170, 0x1B2FB, }, spaces = false, } m["Ogam"] = process_ranges{ "Ogham", 184661, ranges = { 0x1680, 0x169C, }, } m["Olck"] = process_ranges{ "อ็อลจิกิ", 201688, aliases = {"อ็อลเจะเม็ท", "อ็อล", "สันถาลี", "สันถัล", "Ol Chiki", "Ol Chemetʼ", "Ol", "Santali"}, ranges = { 0x1C50, 0x1C7F, }, } m["Onao"] = process_ranges{ "Ol Onal", 108607084, "alphabet", ranges = { 0x0964, 0x0965, 0x1E5D0, 0x1E5FA, 0x1E5FF, 0x1E5FF, }, } m["Orkh"] = process_ranges{ "Old Turkic", 5058305, aliases = {"Orkhon runic"}, ranges = { 0x10C00, 0x10C48, }, direction = "rtl", translit = "Orkh-translit", } m["Orya"] = process_ranges{ "โอริยา", 1760127, "abugida", aliases = {"โอเดีย", "Oriya", "Odia"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0B01, 0x0B03, 0x0B05, 0x0B0C, 0x0B0F, 0x0B10, 0x0B13, 0x0B28, 0x0B2A, 0x0B30, 0x0B32, 0x0B33, 0x0B35, 0x0B39, 0x0B3C, 0x0B44, 0x0B47, 0x0B48, 0x0B4B, 0x0B4D, 0x0B55, 0x0B57, 0x0B5C, 0x0B5D, 0x0B5F, 0x0B63, 0x0B66, 0x0B77, 0x1CDA, 0x1CDA, 0x1CF2, 0x1CF2, }, normalizationFixes = handle_normalization_fixes{ from = {"ଅା", "ଏୗ", "ଓୗ"}, to = {"ଆ", "ଐ", "ଔ"} }, } m["Osge"] = process_ranges{ "Osage", 7105529, ranges = { 0x104B0, 0x104D3, 0x104D8, 0x104FB, }, capitalized = true, } m["Osma"] = process_ranges{ "อุสมาน", 1377866, aliases = {"Osmanya"}, ranges = { 0x10480, 0x1049D, 0x104A0, 0x104A9, }, } m["Ougr"] = process_ranges{ "อุยกูร์เก่า", 1998938, "abjad, alphabet", aliases = {"Old Uyghur"}, ranges = { 0x0640, 0x0640, 0x10AF2, 0x10AF2, 0x10F70, 0x10F89, }, -- This should ideally be "vertical-ltr", but getting the CSS right is tricky because it's right-to-left horizontally, but left-to-right vertically. Currently, displaying it vertically causes it to display bottom-to-top. direction = "rtl", } m["Palm"] = process_ranges{ "Palmyrene", 17538100, ranges = { 0x10860, 0x1087F, }, direction = "rtl", } m["Pauc"] = process_ranges{ "Pau Cin Hau", 25339852, ranges = { 0x11AC0, 0x11AF8, }, } m["Pcun"] = { "Proto-Cuneiform", 1650699, "pictography", -- Not in Unicode } m["Pelm"] = { "Proto-Elamite", 56305763, "pictography", -- Not in Unicode } m["Perm"] = process_ranges{ "Old Permic", 147899, ranges = { 0x0483, 0x0483, 0x10350, 0x1037A, }, } m["Phag"] = process_ranges{ "Phags-pa", 822836, "abugida", ranges = { 0x1802, 0x1803, 0x1805, 0x1805, 0x200C, 0x200D, 0x202F, 0x202F, 0x3002, 0x3002, 0xA840, 0xA877, }, direction = "vertical-ltr", } m["Phli"] = process_ranges{ "ปาห์ลาวีจารึก", 24089793, "abjad", aliases = {"Inscriptional Pahlavi"}, ranges = { 0x10B60, 0x10B72, 0x10B78, 0x10B7F, }, direction = "rtl", } m["Phlp"] = process_ranges{ "ปาห์ลาวีเพลงสดุดี", 7253954, "abjad", aliases = {"Psalter Pahlavi"}, ranges = { 0x0640, 0x0640, 0x10B80, 0x10B91, 0x10B99, 0x10B9C, 0x10BA9, 0x10BAF, }, direction = "rtl", } m["Phlv"] = { "ปาห์ลาวีคัมภีร์", 72403118, "abjad", aliases = {"ปาห์ลาวีหนังสือ", "Book Pahlavi"}, direction = "rtl", wikipedia_article = "Pahlavi scripts#Book Pahlavi", -- Not in Unicode } m["Phnx"] = process_ranges{ "ฟินิเชีย", 26752, "abjad", aliases = {"Phoenician"}, ranges = { 0x10900, 0x1091B, 0x1091F, 0x1091F, }, direction = "rtl", translit = "Phnx-translit", } m["Plrd"] = process_ranges{ "Pollard", 601734, "abugida", aliases = {"Miao"}, ranges = { 0x16F00, 0x16F4A, 0x16F4F, 0x16F87, 0x16F8F, 0x16F9F, }, } m["Prti"] = process_ranges{ "Inscriptional Parthian", 13023804, ranges = { 0x10B40, 0x10B55, 0x10B58, 0x10B5F, }, direction = "rtl", } m["Psin"] = { "Proto-Sinaitic", 1065250, "abjad", direction = "rtl", -- Not in Unicode } m["Ranj"] = { "Ranjana", 2385276, "abugida", -- Not in Unicode } m["Rjng"] = process_ranges{ "Rejang", 2007960, "abugida", ranges = { 0xA930, 0xA953, 0xA95F, 0xA95F, }, } m["Rohg"] = process_ranges{ "ฮานิฟ", 21028705, "alphabet", aliases = {"ฮานีฟี", "Hanifi Rohingya", "Hanifi"}, ranges = { 0x060C, 0x060C, 0x061B, 0x061B, 0x061F, 0x061F, 0x0640, 0x0640, 0x06D4, 0x06D4, 0x10D00, 0x10D27, 0x10D30, 0x10D39, }, direction = "rtl", } m["Roro"] = { "Rongorongo", 209764, -- Not in Unicode } m["Rumin"] = process_ranges{ "Rumi numerals", nil, ranges = { 0x10E60, 0x10E7E, }, ietf_subtag = "Arab", } m["Runr"] = process_ranges{ "รูน", 82996, "alphabet", aliases = {"รูนิก", "Runic"}, ranges = { 0x16A0, 0x16EA, 0x16EE, 0x16F8, }, } do local Samr_stripdiacritics = { remove_diacritics = c.CGJ .. u(0x0816) .. "-" .. u(0x082D), } m["Samr"] = process_ranges{ "Samaritan", 1550930, "abjad", ranges = { 0x0800, 0x082D, 0x0830, 0x083E, }, direction = "rtl", strip_diacritics = Samr_stripdiacritics, sort_key = Samr_stripdiacritics, } end m["Sarb"] = process_ranges{ "Ancient South Arabian", 446074, "abjad", aliases = {"Old South Arabian"}, ranges = { 0x10A60, 0x10A7F, }, direction = "rtl", translit = "Sarb-translit", } m["Saur"] = process_ranges{ "Saurashtra", 3535165, "abugida", ranges = { 0xA880, 0xA8C5, 0xA8CE, 0xA8D9, }, } m["Semap"] = { "สัญญาณธง", 250796, "pictography", aliases = {"flag semaphore"}, ietf_subtag = "Zsym", } m["Sgnw"] = process_ranges{ "SignWriting", 1497335, "pictography", aliases = {"Sutton SignWriting"}, ranges = { 0x1D800, 0x1DA8B, 0x1DA9B, 0x1DA9F, 0x1DAA1, 0x1DAAF, }, translit = false, } m["Shaw"] = process_ranges{ "Shavian", 1970098, aliases = {"Shaw"}, ranges = { 0x10450, 0x1047F, }, } m["Shrd"] = process_ranges{ "ศารทา", 2047117, "abugida", aliases = {"Sharada"}, ranges = { 0x0951, 0x0951, 0x1CD7, 0x1CD7, 0x1CD9, 0x1CD9, 0x1CDC, 0x1CDD, 0x1CE0, 0x1CE0, 0xA830, 0xA835, 0xA838, 0xA838, 0x11180, 0x111DF, }, translit = "Shrd-translit", } m["Shui"] = { "Sui", 752854, "logography", spaces = false, -- Not in Unicode } m["Sidd"] = process_ranges{ "สิทธัม", 250379, "abugida", aliases = {"Siddham"}, ranges = { 0x11580, 0x115B5, 0x115B8, 0x115DD, }, translit = "Sidd-translit", } m["Sidt"] = { "Sidetic", 36659, "alphabet", direction = "rtl", -- Not in Unicode } m["Sind"] = process_ranges{ "ขุทาพาที", 6402810, "abugida", aliases = {"ขุทาวาที", "Khudabadi", "Khudawadi"}, ranges = { 0x0964, 0x0965, 0xA830, 0xA839, 0x112B0, 0x112EA, 0x112F0, 0x112F9, }, normalizationFixes = handle_normalization_fixes{ from = {"𑊰𑋠", "𑊰𑋥", "𑊰𑋦", "𑊰𑋧", "𑊰𑋨"}, to = {"𑊱", "𑊶", "𑊷", "𑊸", "𑊹"} }, } m["Sinh"] = process_ranges{ "สิงหล", 1574992, "abugida", aliases = {"Sinhalese", "Sinhala"}, ranges = { 0x0964, 0x0965, 0x0D81, 0x0D83, 0x0D85, 0x0D96, 0x0D9A, 0x0DB1, 0x0DB3, 0x0DBB, 0x0DBD, 0x0DBD, 0x0DC0, 0x0DC6, 0x0DCA, 0x0DCA, 0x0DCF, 0x0DD4, 0x0DD6, 0x0DD6, 0x0DD8, 0x0DDF, 0x0DE6, 0x0DEF, 0x0DF2, 0x0DF4, 0x1CF2, 0x1CF2, 0x111E1, 0x111F4, }, normalizationFixes = handle_normalization_fixes{ from = {"අා", "අැ", "අෑ", "උෟ", "ඍෘ", "ඏෟ", "එ්", "එෙ", "ඔෟ", "ෘෘ"}, to = {"ආ", "ඇ", "ඈ", "ඌ", "ඎ", "ඐ", "ඒ", "ඓ", "ඖ", "ෲ"} }, } m["Sogd"] = process_ranges{ "ซอกเดีย", 578359, "abjad", aliases = {"Sogdian"}, ranges = { 0x0640, 0x0640, 0x10F30, 0x10F59, }, direction = "rtl", } m["Sogo"] = process_ranges{ "ซอกเดียเก่า", 72403254, "abjad", aliases = {"Old Sogdian"}, ranges = { 0x10F00, 0x10F27, }, direction = "rtl", } m["Sora"] = process_ranges{ "Sorang Sompeng", 7563292, aliases = {"Sora Sompeng"}, ranges = { 0x110D0, 0x110E8, 0x110F0, 0x110F9, }, } m["Soyo"] = process_ranges{ "Soyombo", 8009382, "abugida", ranges = { 0x11A50, 0x11AA2, }, } m["Sund"] = process_ranges{ "ซุนดา", 51589, "abugida", aliases = {"Sundanese"}, ranges = { 0x1B80, 0x1BBF, 0x1CC0, 0x1CC7, }, } m["Sunu"] = process_ranges{ "Sunuwar", 109984965, "alphabet", ranges = { 0x11BC0, 0x11BE1, 0x11BF0, 0x11BF9, }, } m["Sylo"] = process_ranges{ "สิเลฏินาครี", 144128, "abugida", aliases = {"สิลอฏินาครี", "Syloti Nagri", "Sylheti Nagari"}, ranges = { 0x0964, 0x0965, 0x09E6, 0x09EF, 0xA800, 0xA82C, }, } m["Syrc"] = process_ranges{ "ซีรีแอก", 26567, "abjad", -- more precisely, impure abjad aliases = {"ซีเรียค", "Syriac"}, ranges = { 0x060C, 0x060C, 0x061B, 0x061C, 0x061F, 0x061F, 0x0640, 0x0640, 0x064B, 0x0655, 0x0670, 0x0670, 0x0700, 0x070D, 0x070F, 0x074A, 0x074D, 0x074F, 0x0860, 0x086A, 0x1DF8, 0x1DF8, 0x1DFA, 0x1DFA, }, direction = "rtl", } -- Syre, Syrj, Syrn are apparently subsumed into Syrc; discuss if this causes issues m["Tagb"] = process_ranges{ "ตักบันวา", 977444, "abugida", aliases = {"Tagbanwa"}, ranges = { 0x1735, 0x1736, 0x1760, 0x176C, 0x176E, 0x1770, 0x1772, 0x1773, }, } m["Takr"] = process_ranges{ "ฏากรี", 759202, "abugida", aliases = {"ตากรี", "Takri"}, ranges = { 0x0964, 0x0965, 0xA830, 0xA839, 0x11680, 0x116B9, 0x116C0, 0x116C9, }, normalizationFixes = handle_normalization_fixes{ from = {"𑚀𑚭", "𑚀𑚴", "𑚀𑚵", "𑚆𑚲"}, to = {"𑚁", "𑚈", "𑚉", "𑚇"} }, } m["Tale"] = process_ranges{ "ไทใต้คง", 2566326, "abugida", aliases = {"ไทเหนือ", "ไทเหนือใหม่", "ไตเหลอ", "Tai Nüa", "Tai Nuea", "New Tai Nüa", "New Tai Nuea", "Dehong Dai", "Tai Dehong", "Tai Le"}, ranges = { 0x1040, 0x1049, 0x1950, 0x196D, 0x1970, 0x1974, }, spaces = false, } m["Talu"] = process_ranges{ "ไทลื้อใหม่", 3498863, "abugida", aliases = {"New Tai Lue"}, ranges = { 0x1980, 0x19AB, 0x19B0, 0x19C9, 0x19D0, 0x19DA, 0x19DE, 0x19DF, }, spaces = false, } m["Taml"] = process_ranges{ "ทมิฬ", 26803, "abugida", aliases = {"Tamil"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0B82, 0x0B83, 0x0B85, 0x0B8A, 0x0B8E, 0x0B90, 0x0B92, 0x0B95, 0x0B99, 0x0B9A, 0x0B9C, 0x0B9C, 0x0B9E, 0x0B9F, 0x0BA3, 0x0BA4, 0x0BA8, 0x0BAA, 0x0BAE, 0x0BB9, 0x0BBE, 0x0BC2, 0x0BC6, 0x0BC8, 0x0BCA, 0x0BCD, 0x0BD0, 0x0BD0, 0x0BD7, 0x0BD7, 0x0BE6, 0x0BFA, 0x1CDA, 0x1CDA, 0xA8F3, 0xA8F3, 0x11301, 0x11301, 0x11303, 0x11303, 0x1133B, 0x1133C, 0x11FC0, 0x11FF1, 0x11FFF, 0x11FFF, }, normalizationFixes = handle_normalization_fixes{ from = {"அூ", "ஸ்ரீ"}, to = {"ஆ", "ஶ்ரீ"} }, } m["Tang"] = process_ranges{ "ตังกุต", 1373610, "logography, syllabary", aliases = {"Tangut"}, ranges = { 0x31EF, 0x31EF, 0x16FE0, 0x16FE0, 0x17000, 0x187F7, 0x18800, 0x18AFF, 0x18D00, 0x18D08, }, spaces = false, translit = "txg-translit", } m["Tavt"] = process_ranges{ "ไทเวียด", 11818517, "abugida", aliases = {"Tai Viet"}, ranges = { 0xAA80, 0xAAC2, 0xAADB, 0xAADF, }, spaces = false, } m["Tayo"] = process_ranges{ "ลายตัย", 16306701, "abugida", aliases = {"ญ้อ", "ไทญ้อ", "Lai Tay", "Tai Yo"}, direction = "vertical-rtl", ranges = { 0x1E6C0, 0x1E6DE, 0x1E6E0, 0x1E6F5, 0x1E6FE, 0x1E6FF, }, spaces = false, } m["Telu"] = process_ranges{ "เตลูกู", 570450, "abugida", aliases = {"Telugu"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0C00, 0x0C0C, 0x0C0E, 0x0C10, 0x0C12, 0x0C28, 0x0C2A, 0x0C39, 0x0C3C, 0x0C44, 0x0C46, 0x0C48, 0x0C4A, 0x0C4D, 0x0C55, 0x0C56, 0x0C58, 0x0C5A, 0x0C5D, 0x0C5D, 0x0C60, 0x0C63, 0x0C66, 0x0C6F, 0x0C77, 0x0C7F, 0x1CDA, 0x1CDA, 0x1CF2, 0x1CF2, }, normalizationFixes = handle_normalization_fixes{ from = {"ఒౌ", "ఒౕ", "ిౕ", "ెౕ", "ొౕ"}, to = {"ఔ", "ఓ", "ీ", "ే", "ో"} }, } m["Teng"] = { "เทงกวาร์", 473725, aliases = {"Tengwar"}, } m["Tfng"] = process_ranges{ "ทิฟินาค", 208503, "abjad, alphabet", aliases = {"Tifinagh"}, ranges = { 0x2D30, 0x2D67, 0x2D6F, 0x2D70, 0x2D7F, 0x2D7F, }, other_names = {"ลิบีโค-เบอร์เบอร์", "เบอร์เบอร์", "Libyco-Berber", "Berber"}, -- per Wikipedia, Libyco-Berber is the parent } m["Tglg"] = process_ranges{ "ไบบายิน", 812124, "abugida", aliases = {"ตากาล็อก", "Baybayin", "Tagalog"}, varieties = {"Kur-itan"}, ranges = { 0x1700, 0x1715, 0x171F, 0x171F, 0x1735, 0x1736, }, } m["Thaa"] = process_ranges{ "ทานะ", 877906, "abugida", aliases = {"Thaana"}, ranges = { 0x060C, 0x060C, 0x061B, 0x061C, 0x061F, 0x061F, 0x0660, 0x0669, 0x0780, 0x07B1, 0xFDF2, 0xFDF2, 0xFDFD, 0xFDFD, }, direction = "rtl", } m["Thai"] = process_ranges{ "ไทย", 236376, "abugida", aliases = {"Thai"}, ranges = { 0x0E01, 0x0E3A, 0x0E40, 0x0E5B, }, spaces = false, translit = false, --only for thwikt } do local Tibt_displaytext = { from = {"ༀ", "༌", "།།", "༚༚", "༚༝", "༝༚", "༝༝", "ཷ", "ཹ", "ེེ", "ོོ"}, to = {"ཨོཾ", "་", "༎", "༛", "༟", "࿎", "༞", "ྲཱྀ", "ླཱྀ", "ཻ", "ཽ"} } m["Tibt"] = process_ranges{ "ทิเบต", 46861, "abugida", aliases = {"Tibetan"}, ranges = { 0x0F00, 0x0F47, 0x0F49, 0x0F6C, 0x0F71, 0x0F97, 0x0F99, 0x0FBC, 0x0FBE, 0x0FCC, 0x0FCE, 0x0FD4, 0x0FD9, 0x0FDA, 0x3008, 0x300B, }, normalizationFixes = handle_normalization_fixes{ combiningClasses = {["༹"] = 1}, from = {"ཷ", "ཹ"}, to = {"ྲཱྀ", "ླཱྀ"} }, display_text = Tibt_displaytext, strip_diacritics = Tibt_displaytext, sort_key = "Tibt-sortkey", translit = "Tibt-translit", } m["sit-tam-Tibt"] = { "ตัมยิก", 109875213, m["Tibt"][3], -- There is no inheritance of properties currently implemented for scripts. Per [[User:Theknightwho]], this -- is because it's tricky to do since there are several types of child scripts: those that are mere display -- variants (like fa-Arab, kk-Arab), which should be eliminated in favor of CSS language selectors to -- handle the font differences; those that are genuinely different scripts that happen to share the same -- Unicode codepoints but have mostly different properties (e.g. Manchu vs. Mongolian); and those that are -- somewhere in between (like Tamyig vs. Tibetan). As a result, we currently have to manually specify -- which properties we want inherited as follows. aliases = {"Tamyig"}, ranges = m["Tibt"].ranges, characters = m["Tibt"].characters, parent = "Tibt", normalizationFixes = m["Tibt"].normalizationFixes, display_text = m["Tibt"].display_text, strip_diacritics = m["Tibt"].strip_diacritics, sort_key = m["Tibt"].sort_key, translit = m["Tibt"].translit, } end m["Tirh"] = process_ranges{ "ติรหุตา", 1765752, "abugida", aliases = {"Tirhuta"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x1CF2, 0x1CF2, 0xA830, 0xA839, 0x11480, 0x114C7, 0x114D0, 0x114D9, }, normalizationFixes = handle_normalization_fixes{ from = {"𑒁𑒰", "𑒋𑒺", "𑒍𑒺", "𑒪𑒵", "𑒪𑒶"}, to = {"𑒂", "𑒌", "𑒎", "𑒉", "𑒊"} }, } m["Tnsa"] = process_ranges{ "Tangsa", 105576311, "alphabet", ranges = { 0x16A70, 0x16ABE, 0x16AC0, 0x16AC9, }, } m["Todr"] = process_ranges{ "Todhri", 10274731, "alphabet", direction = "rtl", ranges = { 0x105C0, 0x105F3, }, } m["Tols"] = { "Tolong Siki", 4459822, "alphabet", -- Not in Unicode } m["Toto"] = process_ranges{ "Toto", 104837516, "abugida", ranges = { 0x1E290, 0x1E2AE, }, } m["Tutg"] = process_ranges{ "Tigalari", 2604990, "abugida", aliases = {"Tulu"}, ranges = { 0x1CF2, 0x1CF2, 0x1CF4, 0x1CF4, 0xA8F1, 0xA8F1, 0x11380, 0x11389, 0x1138B, 0x1138B, 0x1138E, 0x1138E, 0x11390, 0x113B5, 0x113B7, 0x113C0, 0x113C2, 0x113C2, 0x113C5, 0x113C5, 0x113C7, 0x113CA, 0x113CC, 0x113D5, 0x113D7, 0x113D8, 0x113E1, 0x113E2, }, } m["Ugar"] = process_ranges{ "ยูการิต", 332652, "abjad", aliases = {"ยูการิติก", "Ugaritic"}, ranges = { 0x10380, 0x1039D, 0x1039F, 0x1039F, }, } m["Vaii"] = process_ranges{ "ไว", 523078, "syllabary", aliases = {"Vai"}, ranges = { 0xA500, 0xA62B, }, } m["Visp"] = { "Visible Speech", 1303365, "alphabet", -- Not in Unicode } m["Vith"] = process_ranges{ "Vithkuqi", 3301993, "alphabet", ranges = { 0x10570, 0x1057A, 0x1057C, 0x1058A, 0x1058C, 0x10592, 0x10594, 0x10595, 0x10597, 0x105A1, 0x105A3, 0x105B1, 0x105B3, 0x105B9, 0x105BB, 0x105BC, }, capitalized = true, } m["Wara"] = process_ranges{ "วรังจิติ", 79199, aliases = {"วรังกษิติ", "Warang Citi", "Varang Kshiti"}, ranges = { 0x118A0, 0x118F2, 0x118FF, 0x118FF, }, capitalized = true, } m["Wcho"] = process_ranges{ "Wancho", 33713728, "alphabet", ranges = { 0x1E2C0, 0x1E2F9, 0x1E2FF, 0x1E2FF, }, } m["Wole"] = { "Woleai", 6643710, "syllabary", -- Not in Unicode } m["Xpeo"] = process_ranges{ "เปอร์เซียเก่า", 1471822, aliases = {"Old Persian"}, ranges = { 0x103A0, 0x103C3, 0x103C8, 0x103D5, }, } m["Xsux"] = process_ranges{ "คูนิฟอร์ม", 401, aliases = {"Cuneiform", "Sumero-Akkadian Cuneiform"}, ranges = { 0x12000, 0x12399, 0x12400, 0x1246E, 0x12470, 0x12474, 0x12480, 0x12543, }, } m["Yezi"] = process_ranges{ "Yezidi", 13175481, "alphabet", ranges = { 0x060C, 0x060C, 0x061B, 0x061B, 0x061F, 0x061F, 0x0660, 0x0669, 0x10E80, 0x10EA9, 0x10EAB, 0x10EAD, 0x10EB0, 0x10EB1, }, direction = "rtl", } m["Yiii"] = process_ranges{ "อี๋", 1197646, "syllabary", aliases = {"Yi"}, ranges = { 0x3001, 0x3002, 0x3008, 0x3011, 0x3014, 0x301B, 0x30FB, 0x30FB, 0xA000, 0xA48C, 0xA490, 0xA4C6, 0xFF61, 0xFF65, }, } m["Zanb"] = process_ranges{ "Zanabazar Square", 50809208, "abugida", ranges = { 0x11A00, 0x11A47, }, } m["Zmth"] = process_ranges{ "mathematical notation", 1140046, ranges = { 0x00AC, 0x00AC, 0x00B1, 0x00B1, 0x00D7, 0x00D7, 0x00F7, 0x00F7, 0x03D0, 0x03D2, 0x03D5, 0x03D5, 0x03F0, 0x03F1, 0x03F4, 0x03F6, 0x0606, 0x0608, 0x2016, 0x2016, 0x2032, 0x2034, 0x2040, 0x2040, 0x2044, 0x2044, 0x2052, 0x2052, 0x205F, 0x205F, 0x2061, 0x2064, 0x207A, 0x207E, 0x208A, 0x208E, 0x20D0, 0x20DC, 0x20E1, 0x20E1, 0x20E5, 0x20E6, 0x20EB, 0x20EF, 0x2102, 0x2102, 0x2107, 0x2107, 0x210A, 0x2113, 0x2115, 0x2115, 0x2118, 0x211D, 0x2124, 0x2124, 0x2128, 0x2129, 0x212C, 0x212D, 0x212F, 0x2131, 0x2133, 0x2138, 0x213C, 0x2149, 0x214B, 0x214B, 0x2190, 0x21A7, 0x21A9, 0x21AE, 0x21B0, 0x21B1, 0x21B6, 0x21B7, 0x21BC, 0x21DB, 0x21DD, 0x21DD, 0x21E4, 0x21E5, 0x21F4, 0x22FF, 0x2308, 0x230B, 0x2320, 0x2321, 0x237C, 0x237C, 0x239B, 0x23B5, 0x23B7, 0x23B7, 0x23D0, 0x23D0, 0x23DC, 0x23E2, 0x25A0, 0x25A1, 0x25AE, 0x25B7, 0x25BC, 0x25C1, 0x25C6, 0x25C7, 0x25CA, 0x25CB, 0x25CF, 0x25D3, 0x25E2, 0x25E2, 0x25E4, 0x25E4, 0x25E7, 0x25EC, 0x25F8, 0x25FF, 0x2605, 0x2606, 0x2640, 0x2640, 0x2642, 0x2642, 0x2660, 0x2663, 0x266D, 0x266F, 0x27C0, 0x27FF, 0x2900, 0x2AFF, 0x2B30, 0x2B44, 0x2B47, 0x2B4C, 0xFB29, 0xFB29, 0xFE61, 0xFE66, 0xFE68, 0xFE68, 0xFF0B, 0xFF0B, 0xFF1C, 0xFF1E, 0xFF3C, 0xFF3C, 0xFF3E, 0xFF3E, 0xFF5C, 0xFF5C, 0xFF5E, 0xFF5E, 0xFFE2, 0xFFE2, 0xFFE9, 0xFFEC, 0x1D400, 0x1D454, 0x1D456, 0x1D49C, 0x1D49E, 0x1D49F, 0x1D4A2, 0x1D4A2, 0x1D4A5, 0x1D4A6, 0x1D4A9, 0x1D4AC, 0x1D4AE, 0x1D4B9, 0x1D4BB, 0x1D4BB, 0x1D4BD, 0x1D4C3, 0x1D4C5, 0x1D505, 0x1D507, 0x1D50A, 0x1D50D, 0x1D514, 0x1D516, 0x1D51C, 0x1D51E, 0x1D539, 0x1D53B, 0x1D53E, 0x1D540, 0x1D544, 0x1D546, 0x1D546, 0x1D54A, 0x1D550, 0x1D552, 0x1D6A5, 0x1D6A8, 0x1D7CB, 0x1D7CE, 0x1D7FF, 0x1EE00, 0x1EE03, 0x1EE05, 0x1EE1F, 0x1EE21, 0x1EE22, 0x1EE24, 0x1EE24, 0x1EE27, 0x1EE27, 0x1EE29, 0x1EE32, 0x1EE34, 0x1EE37, 0x1EE39, 0x1EE39, 0x1EE3B, 0x1EE3B, 0x1EE42, 0x1EE42, 0x1EE47, 0x1EE47, 0x1EE49, 0x1EE49, 0x1EE4B, 0x1EE4B, 0x1EE4D, 0x1EE4F, 0x1EE51, 0x1EE52, 0x1EE54, 0x1EE54, 0x1EE57, 0x1EE57, 0x1EE59, 0x1EE59, 0x1EE5B, 0x1EE5B, 0x1EE5D, 0x1EE5D, 0x1EE5F, 0x1EE5F, 0x1EE61, 0x1EE62, 0x1EE64, 0x1EE64, 0x1EE67, 0x1EE6A, 0x1EE6C, 0x1EE72, 0x1EE74, 0x1EE77, 0x1EE79, 0x1EE7C, 0x1EE7E, 0x1EE7E, 0x1EE80, 0x1EE89, 0x1EE8B, 0x1EE9B, 0x1EEA1, 0x1EEA3, 0x1EEA5, 0x1EEA9, 0x1EEAB, 0x1EEBB, 0x1EEF0, 0x1EEF1, }, translit = false, } m["Zname"] = process_ranges{ "Znamenny musical notation", 965834, "pictography", ranges = { 0x1CF00, 0x1CF2D, 0x1CF30, 0x1CF46, 0x1CF50, 0x1CFC3, }, ietf_subtag = "Zsym", translit = false, } m["Zsym"] = process_ranges{ "สัญลักษณ์", 80071, "pictography", aliases = {"symbolic"}, ranges = { 0x20DD, 0x20E0, 0x20E2, 0x20E4, 0x20E7, 0x20EA, 0x20F0, 0x20F0, 0x2100, 0x2101, 0x2103, 0x2106, 0x2108, 0x2109, 0x2114, 0x2114, 0x2116, 0x2117, 0x211E, 0x2123, 0x2125, 0x2127, 0x212A, 0x212B, 0x212E, 0x212E, 0x2132, 0x2132, 0x2139, 0x213B, 0x214A, 0x214A, 0x214C, 0x214F, 0x21A8, 0x21A8, 0x21AF, 0x21AF, 0x21B2, 0x21B5, 0x21B8, 0x21BB, 0x21DC, 0x21DC, 0x21DE, 0x21E3, 0x21E6, 0x21F3, 0x2300, 0x2307, 0x230C, 0x231F, 0x2322, 0x237B, 0x237D, 0x239A, 0x23B6, 0x23B6, 0x23B8, 0x23CF, 0x23D1, 0x23DB, 0x23E3, 0x23FF, 0x2500, 0x259F, 0x25A2, 0x25AD, 0x25B8, 0x25BB, 0x25C2, 0x25C5, 0x25C8, 0x25C9, 0x25CC, 0x25CE, 0x25D4, 0x25E1, 0x25E3, 0x25E3, 0x25E5, 0x25E6, 0x25ED, 0x25F7, 0x2600, 0x2604, 0x2607, 0x263F, 0x2641, 0x2641, 0x2643, 0x265F, 0x2664, 0x266C, 0x2670, 0x27BF, 0x2B00, 0x2B2F, 0x2B45, 0x2B46, 0x2B4D, 0x2B73, 0x2B76, 0x2B95, 0x2B97, 0x2BFF, 0x4DC0, 0x4DFF, 0x1F000, 0x1F02B, 0x1F030, 0x1F093, 0x1F0A0, 0x1F0AE, 0x1F0B1, 0x1F0BF, 0x1F0C1, 0x1F0CF, 0x1F0D1, 0x1F0F5, 0x1F300, 0x1F6D7, 0x1F6DC, 0x1F6EC, 0x1F6F0, 0x1F6FC, 0x1F700, 0x1F776, 0x1F77B, 0x1F7D9, 0x1F7E0, 0x1F7EB, 0x1F7F0, 0x1F7F0, 0x1F800, 0x1F80B, 0x1F810, 0x1F847, 0x1F850, 0x1F859, 0x1F860, 0x1F887, 0x1F890, 0x1F8AD, 0x1F8B0, 0x1F8B1, 0x1F900, 0x1FA53, 0x1FA60, 0x1FA6D, 0x1FA70, 0x1FA7C, 0x1FA80, 0x1FA88, 0x1FA90, 0x1FABD, 0x1FABF, 0x1FAC5, 0x1FACE, 0x1FADB, 0x1FAE0, 0x1FAE8, 0x1FAF0, 0x1FAF8, 0x1FB00, 0x1FB92, 0x1FB94, 0x1FBCA, 0x1FBF0, 0x1FBF9, }, translit = false, character_category = false, -- none } m["Zyyy"] = { "ยังไม่กำหนด", 104839687, aliases = {"undetermined"}, -- This should not have any characters listed, probably translit = false, character_category = false, -- none } m["Zzzz"] = { "ยังไม่มีรหัส", 104839675, aliases = {"uncoded"}, -- This should not have any characters listed translit = false, character_category = false, -- none } -- These should be defined after the scripts they are composed of. m["Hrkt"] = process_ranges{ "คานะ", 187659, "syllabary", aliases = {"Kana", "Japanese syllabaries"}, ranges = union( m["Hira"].ranges, m["Kana"].ranges ), spaces = false, } m["Jpan"] = process_ranges{ "ญี่ปุ่น", 190502, "logography, syllabary", aliases = {"Japanese"}, ranges = union( m["Hrkt"].ranges, m["Hani"].ranges, m["Latn"].ranges ), spaces = false, sort_by_scraping = true, } m["Kore"] = process_ranges{ "เกาหลี", 711797, "logography, syllabary", aliases = {"Korean"}, ranges = union( m["Hang"].ranges, m["Hani"].ranges, m["Latn"].ranges ), -- `漢字(한자)`→`漢字` -- `가-나-다`→`가나다`, `가--나--다`→`가-나-다` -- `온돌(溫突/溫堗)`→`온돌` ([[ondol]]) strip_diacritics = { remove_diacritics = u(0x302E) .. u(0x302F), from = {"([" .. m["Hani"].characters .. "])%(.-%)", "^%-", "%-$", "%-(%-?)", "\1", "%([" .. m["Hani"].characters .. "/]+%)"}, to = {"%1", "\1", "\1", "%1", "-"} } } return require("Module:languages").finalizeData(m, "script") kmuv12bkzpkwys0edke4gjm44zr91oo 5775290 5775260 2026-10-14T10:20:23Z Octahedron80 267 5775290 Scribunto text/plain --[=[ When adding new scripts to this file, please don't forget to add style definitons for the script in [[MediaWiki:Gadget-LanguagesAndScripts.css]]. ]=] local concat = table.concat local insert = table.insert local ipairs = ipairs local next = next local remove = table.remove local select = select local sort = table.sort -- Loaded on demand, as it may not be needed (depending on the data). local function u(...) u = require("Module:string/char") return u(...) end -- We can't use mw.loadData() on [[Module:languages/chars]] because [[Module:languages/data]] itself is sometimes loaded -- using mw.loadData(), and calling mw.loadData() on [[Module:languages/chars]] will insert metatables into the -- character tables, which the second mw.loadData() will choke on. local m_chars = require("Module:languages/chars") local c = m_chars.chars local p = m_chars.puaChars local cs = m_chars.chars_substitutions ------------------------------------------------------------------------------------ -- -- Helper functions -- ------------------------------------------------------------------------------------ -- Note: a[2] > b[2] means opens are sorted before closes if otherwise equal. local function sort_ranges(a, b) return a[1] < b[1] or a[1] == b[1] and a[2] > b[2] end -- Returns the union of two or more range tables. local function union(...) local ranges = {} for i = 1, select("#", ...) do local argt = select(i, ...) for j, v in ipairs(argt) do insert(ranges, {v, j % 2 == 1 and 1 or -1}) end end sort(ranges, sort_ranges) local ret, i = {}, 0 for _, range in ipairs(ranges) do i = i + range[2] if i == 0 and range[2] == -1 then -- close insert(ret, range[1]) elseif i == 1 and range[2] == 1 then -- open if ret[#ret] and range[1] <= ret[#ret] + 1 then remove(ret) -- merge adjacent ranges else insert(ret, range[1]) end end end return ret end -- Adds the `characters` key, which is determined by a script's `ranges` table. local function process_ranges(sc) local ranges, chars = sc.ranges, {} for i = 2, #ranges, 2 do if ranges[i] == ranges[i - 1] then insert(chars, u(ranges[i])) else insert(chars, u(ranges[i - 1])) if ranges[i] > ranges[i - 1] + 1 then insert(chars, "-") end insert(chars, u(ranges[i])) end end sc.characters = concat(chars) ranges.n = #ranges return sc end local function handle_normalization_fixes(fixes) local combiningClasses = fixes.combiningClasses if combiningClasses then local chars, i = {}, 0 for char in next, combiningClasses do i = i + 1 chars[i] = char end fixes.combiningClassCharacters = concat(chars) end return fixes end ------------------------------------------------------------------------------------ -- -- Data -- ------------------------------------------------------------------------------------ local m = {} m["Adlm"] = process_ranges{ "อัดลัม", 19606346, "alphabet", aliases = {"Adlam"}, ranges = { 0x061F, 0x061F, 0x0640, 0x0640, 0x1E900, 0x1E94B, 0x1E950, 0x1E959, 0x1E95E, 0x1E95F, }, capitalized = true, direction = "rtl", } m["Afak"] = { "อาฟากา", 382019, "syllabary", aliases = {"Afaka"}, -- Not in Unicode } m["Aghb"] = process_ranges{ "Caucasian Albanian", 2495716, "alphabet", ranges = { 0x10530, 0x10563, 0x1056F, 0x1056F, }, } m["Ahom"] = process_ranges{ "อาหม", 2839633, "abugida", aliases = {"อะหม", "Ahom"}, ranges = { 0x11700, 0x1171A, 0x1171D, 0x1172B, 0x11730, 0x11746, }, } m["Arab"] = process_ranges{ "อาหรับ", 1828555, "abjad", -- more precisely, impure abjad aliases = {"อารบิก", "อาระบิก", "Arabic"}, varieties = {{"ยาวี", "Jawi"}, {"Nastaliq", "Nastaleeq"}}, ranges = { 0x0600, 0x06FF, 0x0750, 0x077F, 0x0870, 0x088E, 0x0890, 0x0891, 0x0897, 0x08E1, 0x08E3, 0x08FF, 0xFB50, 0xFBC2, 0xFBD3, 0xFD8F, 0xFD92, 0xFDC7, 0xFDCF, 0xFDCF, 0xFDF0, 0xFDFF, 0xFE70, 0xFE74, 0xFE76, 0xFEFC, 0x102E0, 0x102FB, 0x10E60, 0x10E7E, 0x10EC2, 0x10EC4, 0x10EFC, 0x10EFF, 0x1EE00, 0x1EE03, 0x1EE05, 0x1EE1F, 0x1EE21, 0x1EE22, 0x1EE24, 0x1EE24, 0x1EE27, 0x1EE27, 0x1EE29, 0x1EE32, 0x1EE34, 0x1EE37, 0x1EE39, 0x1EE39, 0x1EE3B, 0x1EE3B, 0x1EE42, 0x1EE42, 0x1EE47, 0x1EE47, 0x1EE49, 0x1EE49, 0x1EE4B, 0x1EE4B, 0x1EE4D, 0x1EE4F, 0x1EE51, 0x1EE52, 0x1EE54, 0x1EE54, 0x1EE57, 0x1EE57, 0x1EE59, 0x1EE59, 0x1EE5B, 0x1EE5B, 0x1EE5D, 0x1EE5D, 0x1EE5F, 0x1EE5F, 0x1EE61, 0x1EE62, 0x1EE64, 0x1EE64, 0x1EE67, 0x1EE6A, 0x1EE6C, 0x1EE72, 0x1EE74, 0x1EE77, 0x1EE79, 0x1EE7C, 0x1EE7E, 0x1EE7E, 0x1EE80, 0x1EE89, 0x1EE8B, 0x1EE9B, 0x1EEA1, 0x1EEA3, 0x1EEA5, 0x1EEA9, 0x1EEAB, 0x1EEBB, 0x1EEF0, 0x1EEF1, }, direction = "rtl", normalizationFixes = handle_normalization_fixes{ from = {"ٳ"}, to = {"اٟ"} }, } m["fa-Arab"] = { "อาหรับ", 744068, m["Arab"][3], aliases = m["Arab"].aliases, ranges = m["Arab"].ranges, characters = m["Arab"].characters, other_names = {"เปอร์โซ-อาหรับ", "Perso-Arabic"}, direction = "rtl", parent = "Arab", normalizationFixes = m["Arab"].normalizationFixes, } m["kk-Arab"] = { "อาหรับ", 90681452, m["Arab"][3], aliases = m["Arab"].aliases, ranges = m["Arab"].ranges, characters = m["Arab"].characters, direction = "rtl", parent = "Arab", normalizationFixes = m["Arab"].normalizationFixes, } m["ks-Arab"] = m["fa-Arab"] m["ku-Arab"] = m["fa-Arab"] m["ms-Arab"] = m["kk-Arab"] m["mzn-Arab"] = m["fa-Arab"] m["ota-Arab"] = m["fa-Arab"] m["pa-Arab"] = { "ศาหมุขี", 133800, m["Arab"][3], aliases = {"Shahmukhi"}, ranges = m["Arab"].ranges, characters = m["Arab"].characters, other_names = {"อารบิก", "อาระบิก", "Arabic"}, direction = "rtl", parent = "Arab", normalizationFixes = m["Arab"].normalizationFixes, } m["ps-Arab"] = m["fa-Arab"] m["sd-Arab"] = m["fa-Arab"] m["tt-Arab"] = m["fa-Arab"] m["ug-Arab"] = m["fa-Arab"] m["ur-Arab"] = m["fa-Arab"] -- Aran (Nastaliq) is subsumed into Arab m["Armi"] = process_ranges{ "แอราเมอิกจักรวรรดิ", 26978, "abjad", aliases = {"Imperial Aramaic"}, ranges = { 0x10840, 0x10855, 0x10857, 0x1085F, }, direction = "rtl", } m["Armn"] = process_ranges{ "อาร์มีเนีย", 11932, "alphabet", aliases = {"อาร์เมเนีย", "Armenian"}, ranges = { 0x0531, 0x0556, 0x0559, 0x058A, 0x058D, 0x058F, 0xFB13, 0xFB17, }, capitalized = true, translit = "Armn-translit", } m["Avst"] = process_ranges{ "อเวสตะ", 790681, "alphabet", aliases = {"Avestan"}, ranges = { 0x10B00, 0x10B35, 0x10B39, 0x10B3F, }, direction = "rtl", } m["pal-Avst"] = { "Pazend", 4925073, m["Avst"][3], aliases = m["Avst"].aliases, ranges = m["Avst"].ranges, characters = m["Avst"].characters, direction = "rtl", parent = "Avst", } m["Bali"] = process_ranges{ "บาหลี", 804984, "abugida", aliases = {"Balinese"}, ranges = { 0x1B00, 0x1B4C, 0x1B4E, 0x1B7F, }, } m["Bamu"] = process_ranges{ "บามุม", 806024, "syllabary", aliases = {"Bamum"}, ranges = { 0xA6A0, 0xA6F7, 0x16800, 0x16A38, }, } m["Bass"] = process_ranges{ "บัสซา", 810458, "alphabet", aliases = {"วาห์", "Bassa", "Bassa Vah", "Vah"}, ranges = { 0x16AD0, 0x16AED, 0x16AF0, 0x16AF5, }, } m["Batk"] = process_ranges{ "บาตัก", 51592, "abugida", aliases = {"Batak"}, ranges = { 0x1BC0, 0x1BF3, 0x1BFC, 0x1BFF, }, } m["Beng"] = process_ranges{ "เบงกอล", 756802, "abugida", aliases = {"เบงกาลี", "บังลา", "Bengali"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0980, 0x0983, 0x0985, 0x098C, 0x098F, 0x0990, 0x0993, 0x09A8, 0x09AA, 0x09B0, 0x09B2, 0x09B2, 0x09B6, 0x09B9, 0x09BC, 0x09C4, 0x09C7, 0x09C8, 0x09CB, 0x09CE, 0x09D7, 0x09D7, 0x09DC, 0x09DD, 0x09DF, 0x09E3, 0x09E6, 0x09EF, 0x09F2, 0x09FE, 0x1CD0, 0x1CD0, 0x1CD2, 0x1CD2, 0x1CD5, 0x1CD6, 0x1CD8, 0x1CD8, 0x1CE1, 0x1CE1, 0x1CEA, 0x1CEA, 0x1CED, 0x1CED, 0x1CF2, 0x1CF2, 0x1CF5, 0x1CF7, 0xA8F1, 0xA8F1, }, normalizationFixes = handle_normalization_fixes{ from = {"অা", "ঋৃ", "ঌৢ"}, to = {"আ", "ৠ", "ৡ"} }, } m["as-Beng"] = process_ranges{ "อัสสัม", 191272, m["Beng"][3], other_names = {"เบงกอล-อัสสัม", "เบงกาลี-อัสสัม", "นาครีตะวันออก", "Assamese", "Bengali-Assamese", "Eastern Nagari"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0980, 0x0983, 0x0985, 0x098C, 0x098F, 0x0990, 0x0993, 0x09A8, 0x09AA, 0x09AF, 0x09B2, 0x09B2, 0x09B6, 0x09B9, 0x09BC, 0x09C4, 0x09C7, 0x09C8, 0x09CB, 0x09CE, 0x09D7, 0x09D7, 0x09DC, 0x09DD, 0x09DF, 0x09E3, 0x09E6, 0x09FE, 0x1CD0, 0x1CD0, 0x1CD2, 0x1CD2, 0x1CD5, 0x1CD6, 0x1CD8, 0x1CD8, 0x1CE1, 0x1CE1, 0x1CEA, 0x1CEA, 0x1CED, 0x1CED, 0x1CF2, 0x1CF2, 0x1CF5, 0x1CF7, 0xA8F1, 0xA8F1, }, normalizationFixes = m["Beng"].normalizationFixes, } m["Bhks"] = process_ranges{ "ไภกษุกี", 17017839, "abugida", aliases = {"Bhaiksuki"}, ranges = { 0x11C00, 0x11C08, 0x11C0A, 0x11C36, 0x11C38, 0x11C45, 0x11C50, 0x11C6C, }, } m["Blis"] = { "Blissymbolic", 609817, "logography", aliases = {"Blissymbols"}, -- Not in Unicode } m["Bopo"] = process_ranges{ "จู้อิน", 198269, "semisyllabary", aliases = {"จู้อินฝูเฮ่า", "ปอพอมอฟอ", "ปัวพัวมัวฟัว", "Zhuyin", "Zhuyin Fuhao", "Bopomofo"}, ranges = { 0x02EA, 0x02EB, 0x3001, 0x3003, 0x3008, 0x3011, 0x3013, 0x301F, 0x302A, 0x302D, 0x3030, 0x3030, 0x3037, 0x3037, 0x30FB, 0x30FB, 0x3105, 0x312F, 0x31A0, 0x31BF, 0xFE45, 0xFE46, 0xFF61, 0xFF65, }, } m["Brah"] = process_ranges{ "พราหมี", 185083, "abugida", aliases = {"พราหฺมี", "Brahmi"}, ranges = { 0x11000, 0x1104D, 0x11052, 0x11075, 0x1107F, 0x1107F, }, normalizationFixes = handle_normalization_fixes{ from = {"𑀅𑀸", "𑀋𑀾", "𑀏𑁂"}, to = {"𑀆", "𑀌", "𑀐"} }, translit = "Brah-translit", } m["Brai"] = process_ranges{ "เบรลล์", 79894, "alphabet", aliases = {"Braille"}, ranges = { 0x2800, 0x28FF, }, } m["Bugi"] = process_ranges{ "ลนตารา", 1074947, "abugida", aliases = {"บูกิส", "Lontara", "Buginese"}, ranges = { 0x1A00, 0x1A1B, 0x1A1E, 0x1A1F, 0xA9CF, 0xA9CF, }, } m["Buhd"] = process_ranges{ "บูฮิด", 1002969, "abugida", aliases = {"Buhid"}, ranges = { 0x1735, 0x1736, 0x1740, 0x1751, 0x1752, 0x1753, }, } m["Cakm"] = process_ranges{ "จักมา", 1059328, "abugida", aliases = {"Chakma"}, ranges = { 0x09E6, 0x09EF, 0x1040, 0x1049, 0x11100, 0x11134, 0x11136, 0x11147, }, } m["Cans"] = process_ranges{ "Canadian syllabic", 2479183, "abugida", ranges = { 0x1400, 0x167F, 0x18B0, 0x18F5, 0x11AB0, 0x11ABF, }, } m["Cari"] = process_ranges{ "คาเรีย", 1094567, "alphabet", aliases = {"Carian"}, ranges = { 0x102A0, 0x102D0, }, } m["Cham"] = process_ranges{ "จาม", 1060381, "abugida", aliases = {"Cham"}, ranges = { 0xAA00, 0xAA36, 0xAA40, 0xAA4D, 0xAA50, 0xAA59, 0xAA5C, 0xAA5F, }, } m["Cher"] = process_ranges{ "เชโรกี", 26549, "syllabary", aliases = {"Cherokee"}, ranges = { 0x13A0, 0x13F5, 0x13F8, 0x13FD, 0xAB70, 0xABBF, }, } m["Chis"] = { "Chisoi", 123173777, "abugida", -- Not in Unicode } m["Chrs"] = process_ranges{ "Khwarezmian", 72386710, "abjad", aliases = {"Chorasmian"}, ranges = { 0x10FB0, 0x10FCB, }, direction = "rtl", } m["Copt"] = process_ranges{ "คอปติก", 321083, "alphabet", aliases = {"Coptic"}, ranges = { 0x03E2, 0x03EF, 0x2C80, 0x2CF3, 0x2CF9, 0x2CFF, 0x102E0, 0x102FB, }, capitalized = true, } m["Cpmn"] = process_ranges{ "Cypro-Minoan", 1751985, "syllabary", aliases = {"Cypro Minoan"}, ranges = { 0x10100, 0x10101, 0x12F90, 0x12FF2, }, } m["Cprt"] = process_ranges{ "ไซปรัส", 1757689, "syllabary", aliases = {"ไซเปรียท", "Cypriot"}, ranges = { 0x10100, 0x10102, 0x10107, 0x10133, 0x10137, 0x1013F, 0x10800, 0x10805, 0x10808, 0x10808, 0x1080A, 0x10835, 0x10837, 0x10838, 0x1083C, 0x1083C, 0x1083F, 0x1083F, }, direction = "rtl", } m["Cyrl"] = process_ranges{ "ซีริลลิก", 8209, "alphabet", aliases = {"Cyrillic"}, ranges = { 0x0400, 0x052F, 0x1C80, 0x1C8A, 0x1D2B, 0x1D2B, 0x1D78, 0x1D78, 0x1DF8, 0x1DF8, 0x2DE0, 0x2DFF, 0x2E43, 0x2E43, 0xA640, 0xA69F, 0xFE2E, 0xFE2F, 0x1E030, 0x1E06D, 0x1E08F, 0x1E08F, }, capitalized = true, } m["Cyrs"] = { "ซีริลลิกเก่า", 442244, m["Cyrl"][3], aliases = {"ซีริลลิกช่วงต้น", "Old Cyrillic", "Early Cyrillic"}, ranges = m["Cyrl"].ranges, characters = m["Cyrl"].characters, capitalized = m["Cyrl"].capitalized, wikipedia_article = "Early Cyrillic alphabet", normalizationFixes = handle_normalization_fixes{ from = {"Ѹ", "ѹ"}, to = {"Ꙋ", "ꙋ"} }, strip_diacritics = {remove_diacritics = cs.Cyrs_remove_diacritics}, sort_key = { remove_diacritics = cs.Cyrs_remove_diacritics, from = { "ї", "оу", -- 2 chars "[ґꙣєѕꙃꙅꙁіꙇђꙉѻꙩꙫꙭꙮꚙꚛꙋѡѿꙍѽꙑѣꙗѥꙕѧꙙѩꙝꙛѫѭѯѱѳѵҁ]" }, to = { "и" .. p[1], "у", { ["ґ"] = "г" .. p[1], ["ꙣ"] = "д" .. p[1], ["є"] = "е", ["ѕ"] = "ж" .. p[1], ["ꙃ"] = "ж" .. p[1], ["ꙅ"] = "ж" .. p[1], ["ꙁ"] = "з", ["і"] = "и" .. p[1], ["ꙇ"] = "и" .. p[1], ["ђ"] = "и" .. p[2], ["ꙉ"] = "и" .. p[2], ["ѻ"] = "о", ["ꙩ"] = "о", ["ꙫ"] = "о", ["ꙭ"] = "о", ["ꙮ"] = "о", ["ꚙ"] = "о", ["ꚛ"] = "о", ["ꙋ"] = "у", ["ѡ"] = "х" .. p[1], ["ѿ"] = "х" .. p[1], ["ꙍ"] = "х" .. p[1], ["ѽ"] = "х" .. p[1], ["ꙑ"] = "ы", ["ѣ"] = "ь" .. p[1], ["ꙗ"] = "ь" .. p[2], ["ѥ"] = "ь" .. p[3], ["ꙕ"] = "ю", ["ѧ"] = "я", ["ꙙ"] = "я", ["ѩ"] = "я" .. p[1], ["ꙝ"] = "я" .. p[1], ["ꙛ"] = "я" .. p[2], ["ѫ"] = "я" .. p[3], ["ѭ"] = "я" .. p[4], ["ѯ"] = "я" .. p[5], ["ѱ"] = "я" .. p[6], ["ѳ"] = "я" .. p[7], ["ѵ"] = "я" .. p[8], ["ҁ"] = "я" .. p[9], } }, } } m["Deva"] = process_ranges{ "เทวนาครี", 38592, "abugida", aliases = {"Devanagari"}, ranges = { 0x0900, 0x097F, 0x1CD0, 0x1CF6, 0x1CF8, 0x1CF9, 0x20F0, 0x20F0, 0xA830, 0xA839, 0xA8E0, 0xA8FF, 0x11B00, 0x11B09, }, normalizationFixes = handle_normalization_fixes{ from = {"ॆॆ", "ेे", "ाॅ", "ाॆ", "ाꣿ", "ॊॆ", "ाे", "ाै", "ोे", "ाऺ", "ॖॖ", "अॅ", "अॆ", "अा", "एॅ", "एॆ", "एे", "एꣿ", "ऎॆ", "अॉ", "आॅ", "अॊ", "आॆ", "अो", "आे", "अौ", "आै", "ओे", "अऺ", "अऻ", "आऺ", "अाꣿ", "आꣿ", "ऒॆ", "अॖ", "अॗ", "ॶॖ", "्‍?ा"}, to = {"ꣿ", "ै", "ॉ", "ॊ", "ॏ", "ॏ", "ो", "ौ", "ौ", "ऻ", "ॗ", "ॲ", "ऄ", "आ", "ऍ", "ऎ", "ऐ", "ꣾ", "ꣾ", "ऑ", "ऑ", "ऒ", "ऒ", "ओ", "ओ", "औ", "औ", "औ", "ॳ", "ॴ", "ॴ", "ॵ", "ॵ", "ॵ", "ॶ", "ॷ", "ॷ"} }, } m["Diak"] = process_ranges{ "ดิเวส อกุรุ", 3307073, "abugida", aliases = {"ดิเวหิ อกุรุ", "Dhives Akuru", "Dhivehi Akuru", "Dives Akuru", "Divehi Akuru"}, ranges = { 0x11900, 0x11906, 0x11909, 0x11909, 0x1190C, 0x11913, 0x11915, 0x11916, 0x11918, 0x11935, 0x11937, 0x11938, 0x1193B, 0x11946, 0x11950, 0x11959, }, } m["Dogr"] = process_ranges{ "โฑครี", 72402987, "abugida", aliases = {"Dogri", "Dogra"}, ranges = { 0x0964, 0x096F, 0xA830, 0xA839, 0x11800, 0x1183B, }, } m["Dsrt"] = process_ranges{ "Deseret", 1200582, "alphabet", ranges = { 0x10400, 0x1044F, }, capitalized = true, } m["Dupl"] = process_ranges{ "Duployan", 5316025, "alphabet", ranges = { 0x1BC00, 0x1BC6A, 0x1BC70, 0x1BC7C, 0x1BC80, 0x1BC88, 0x1BC90, 0x1BC99, 0x1BC9C, 0x1BCA3, }, } m["Egyd"] = { "Demotic", 188519, "abjad, logography", -- Not in Unicode } m["Egyh"] = { "Hieratic", 208111, "abjad, logography", -- Unified with Egyptian hieroglyphic in Unicode } m["Egyp"] = process_ranges{ "ไฮเออโรกลีฟอียิปต์", 132659, "abjad, logography", aliases = {"Egyptian hieroglyphic"}, ranges = { 0x13000, 0x13455, 0x13460, 0x143FA, }, varieties = {"Hieratic"}, wikipedia_article = "ไฮเออโรกลีฟอียิปต์", normalizationFixes = handle_normalization_fixes{ from = {"𓃁", "𓆖"}, to = {"𓃀𓐶𓂝", "𓆓𓐳𓐷𓏏𓐰𓇿𓐸"} }, } m["Elba"] = process_ranges{ "เอลบาซาน", 1036714, "alphabet", aliases = {"Elbasan"}, ranges = { 0x10500, 0x10527, }, } m["Elym"] = process_ranges{ "Elymaic", 60744423, "abjad", ranges = { 0x10FE0, 0x10FF6, }, direction = "rtl", } m["Ethi"] = process_ranges{ "เอธิโอเปีย", 257634, "abugida", aliases = {"กืออึซ", "Ethiopic", "Ge'ez"}, ranges = { 0x1200, 0x1248, 0x124A, 0x124D, 0x1250, 0x1256, 0x1258, 0x1258, 0x125A, 0x125D, 0x1260, 0x1288, 0x128A, 0x128D, 0x1290, 0x12B0, 0x12B2, 0x12B5, 0x12B8, 0x12BE, 0x12C0, 0x12C0, 0x12C2, 0x12C5, 0x12C8, 0x12D6, 0x12D8, 0x1310, 0x1312, 0x1315, 0x1318, 0x135A, 0x135D, 0x137C, 0x1380, 0x1399, 0x2D80, 0x2D96, 0x2DA0, 0x2DA6, 0x2DA8, 0x2DAE, 0x2DB0, 0x2DB6, 0x2DB8, 0x2DBE, 0x2DC0, 0x2DC6, 0x2DC8, 0x2DCE, 0x2DD0, 0x2DD6, 0x2DD8, 0x2DDE, 0xAB01, 0xAB06, 0xAB09, 0xAB0E, 0xAB11, 0xAB16, 0xAB20, 0xAB26, 0xAB28, 0xAB2E, 0x1E7E0, 0x1E7E6, 0x1E7E8, 0x1E7EB, 0x1E7ED, 0x1E7EE, 0x1E7F0, 0x1E7FE, }, sort_key = "Ethi-sortkey", } m["Gara"] = process_ranges{ "Garay", 3095302, "alphabet", capitalized = true, direction = "rtl", ranges = { 0x060C, 0x060C, 0x061B, 0x061B, 0x061F, 0x061F, 0x10D40, 0x10D65, 0x10D69, 0x10D85, 0x10D8E, 0x10D8F, }, } m["Geok"] = process_ranges{ "Khutsuri", 1090055, "alphabet", ranges = { -- Ⴀ-Ⴭ is Asomtavruli, ⴀ-ⴭ is Nuskhuri 0x10A0, 0x10C5, 0x10C7, 0x10C7, 0x10CD, 0x10CD, 0x10FB, 0x10FB, 0x2D00, 0x2D25, 0x2D27, 0x2D27, 0x2D2D, 0x2D2D, }, varieties = {"Nuskhuri", "Asomtavruli"}, capitalized = true, translit = "Geok-translit", } m["Geor"] = process_ranges{ "จอร์เจีย", 3317411, "alphabet", aliases = {"Georgian"}, ranges = { -- ა-ჿ is lowercase Mkhedruli; Ა-Ჿ is uppercase Mkhedruli (Mtavruli) 0x0589, 0x0589, 0x10D0, 0x10FF, 0x1C90, 0x1CBA, 0x1CBD, 0x1CBF, }, varieties = {"Mkhedruli", "Mtavruli"}, capitalized = true, translit = "Geor-translit", } m["Glag"] = process_ranges{ "กลาโกลิติก", 145625, "alphabet", aliases = {"Glagolitic"}, ranges = { 0x0484, 0x0484, 0x0487, 0x0487, 0x0589, 0x0589, 0x10FB, 0x10FB, 0x2C00, 0x2C5F, 0x2E43, 0x2E43, 0xA66F, 0xA66F, 0x1E000, 0x1E006, 0x1E008, 0x1E018, 0x1E01B, 0x1E021, 0x1E023, 0x1E024, 0x1E026, 0x1E02A, }, capitalized = true, } m["Gong"] = process_ranges{ "คุญชลาโคณฑี", 18125340, "abugida", aliases = {"Gunjala Gondi"}, ranges = { 0x0964, 0x0965, 0x11D60, 0x11D65, 0x11D67, 0x11D68, 0x11D6A, 0x11D8E, 0x11D90, 0x11D91, 0x11D93, 0x11D98, 0x11DA0, 0x11DA9, }, } m["Gonm"] = process_ranges{ "มสรามโคณฑี", 16977603, "abugida", aliases = {"Masaram Gondi"}, ranges = { 0x0964, 0x0965, 0x11D00, 0x11D06, 0x11D08, 0x11D09, 0x11D0B, 0x11D36, 0x11D3A, 0x11D3A, 0x11D3C, 0x11D3D, 0x11D3F, 0x11D47, 0x11D50, 0x11D59, }, } m["Goth"] = process_ranges{ "กอท", 467784, "alphabet", aliases = {"กอทิก", "กอทิค", "กอธิก", "กอธิค", "โกทิก", "โกทิค", "โกธิก", "โกธิค", "Gothic"}, ranges = { 0x10330, 0x1034A, }, wikipedia_article = "Gothic alphabet", } m["Gran"] = process_ranges{ "ครันถะ", 1119274, "abugida", aliases = {"Grantha"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0BE6, 0x0BF3, 0x1CD0, 0x1CD0, 0x1CD2, 0x1CD3, 0x1CF2, 0x1CF4, 0x1CF8, 0x1CF9, 0x20F0, 0x20F0, 0x11300, 0x11303, 0x11305, 0x1130C, 0x1130F, 0x11310, 0x11313, 0x11328, 0x1132A, 0x11330, 0x11332, 0x11333, 0x11335, 0x11339, 0x1133B, 0x11344, 0x11347, 0x11348, 0x1134B, 0x1134D, 0x11350, 0x11350, 0x11357, 0x11357, 0x1135D, 0x11363, 0x11366, 0x1136C, 0x11370, 0x11374, 0x11FD0, 0x11FD1, 0x11FD3, 0x11FD3, }, } m["Grek"] = process_ranges{ "กรีก", 8216, "alphabet", aliases = {"Greek"}, ranges = { 0x0341, 0x0341, 0x0374, 0x0375, 0x037E, 0x037E, 0x0384, 0x038A, 0x038C, 0x038C, 0x038E, 0x03A1, 0x03A3, 0x03D7, 0x03DA, 0x03DB, 0x03DE, 0x03E1, 0x03F0, 0x03F1, 0x03F4, 0x03F4, 0x03FC, 0x03FC, 0x1D26, 0x1D2A, 0x1D5D, 0x1D61, 0x1D66, 0x1D6A, 0x1DBF, 0x1DBF, 0x2126, 0x2127, 0x2129, 0x2129, 0x213C, 0x2140, 0xAB65, 0xAB65, 0x10140, 0x1018E, 0x101A0, 0x101A0, 0x1D200, 0x1D245, }, capitalized = true, display_text = cs["Grek-displaytext"], strip_diacritics = cs["Grek-stripdiacritics"], sort_key = { remove_diacritics = "'ʼ;·`¨´῀" .. c.grave .. c.acute .. c.diaer .. c.caron .. c.turnedcommaabove .. c.commaabove .. c.revcommaabove .. c.macron .. c.breve .. c.diaerbelow .. c.brevebelow .. c.perispomeni .. c.ypogegrammeni .. c.RSQuo .. c.prime .. c.keraia .. c.lowerkeraia .. c.tonos .. c.coronis .. c.psili .. c.dasia, from = {"ϝ", "ͷ", "ϛ", "ͱ", "ͺ", "ϳ", "ϻ", "[ϟϙ]", "[ςϲ]", "ͳ"}, to = {"ε" .. p[1], "ε" .. p[2], "ε" .. p[3], "ζ" .. p[1], "ι", "ι" .. p[1], "π" .. p[1], "π" .. p[2], "σ", "ϡ"}, }, } m["Polyt"] = process_ranges{ "กรีก", 1475332, m["Grek"][3], aliases = m["Grek"].aliases, ranges = union(m["Grek"].ranges, { 0x0340, 0x0340, 0x0342, 0x0345, 0x0370, 0x0373, 0x0376, 0x0377, 0x037A, 0x037D, 0x037F, 0x037F, 0x03D8, 0x03D9, 0x03DC, 0x03DD, 0x03F2, 0x03F3, 0x03F5, 0x03FB, 0x03FD, 0x03FF, 0x1F00, 0x1F15, 0x1F18, 0x1F1D, 0x1F20, 0x1F45, 0x1F48, 0x1F4D, 0x1F50, 0x1F57, 0x1F59, 0x1F59, 0x1F5B, 0x1F5B, 0x1F5D, 0x1F5D, 0x1F5F, 0x1F7D, 0x1F80, 0x1FB4, 0x1FB6, 0x1FC4, 0x1FC6, 0x1FD3, 0x1FD6, 0x1FDB, 0x1FDD, 0x1FEF, 0x1FF2, 0x1FF4, 0x1FF6, 0x1FFE, }), ietf_subtag = "Grek", capitalized = m["Grek"].capitalized, parent = "Grek", display_text = m["Grek"].display_text, strip_diacritics = "Polyt-stripdiacritics", sort_key = m["Grek"].sort_key, translit = "grc-translit", } m["Gujr"] = process_ranges{ "คุชราต", 733944, "abugida", aliases = {"คุชราตี", "Gujarati"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0A81, 0x0A83, 0x0A85, 0x0A8D, 0x0A8F, 0x0A91, 0x0A93, 0x0AA8, 0x0AAA, 0x0AB0, 0x0AB2, 0x0AB3, 0x0AB5, 0x0AB9, 0x0ABC, 0x0AC5, 0x0AC7, 0x0AC9, 0x0ACB, 0x0ACD, 0x0AD0, 0x0AD0, 0x0AE0, 0x0AE3, 0x0AE6, 0x0AF1, 0x0AF9, 0x0AFF, 0xA830, 0xA839, }, normalizationFixes = handle_normalization_fixes{ from = {"ઓ", "અાૈ", "અા", "અૅ", "અે", "અૈ", "અૉ", "અો", "અૌ", "આૅ", "આૈ", "ૅા"}, to = {"અાૅ", "ઔ", "આ", "ઍ", "એ", "ઐ", "ઑ", "ઓ", "ઔ", "ઓ", "ઔ", "ૉ"} }, } m["Gukh"] = process_ranges{ "Khema", 110064239, "abugida", aliases = {"Gurung Khema", "Khema Phri", "Khema Lipi"}, ranges = { 0x0965, 0x0965, 0x16100, 0x16139, }, } m["Guru"] = process_ranges{ "คุรมุขี", 689894, "abugida", aliases = {"กูร์มูคี", "เกอร์มุกห์", "Gurmukhi"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0A01, 0x0A03, 0x0A05, 0x0A0A, 0x0A0F, 0x0A10, 0x0A13, 0x0A28, 0x0A2A, 0x0A30, 0x0A32, 0x0A33, 0x0A35, 0x0A36, 0x0A38, 0x0A39, 0x0A3C, 0x0A3C, 0x0A3E, 0x0A42, 0x0A47, 0x0A48, 0x0A4B, 0x0A4D, 0x0A51, 0x0A51, 0x0A59, 0x0A5C, 0x0A5E, 0x0A5E, 0x0A66, 0x0A76, 0xA830, 0xA839, }, normalizationFixes = handle_normalization_fixes{ from = {"ਅਾ", "ਅੈ", "ਅੌ", "ੲਿ", "ੲੀ", "ੲੇ", "ੳੁ", "ੳੂ", "ੳੋ"}, to = {"ਆ", "ਐ", "ਔ", "ਇ", "ਈ", "ਏ", "ਉ", "ਊ", "ਓ"} }, } m["Hang"] = process_ranges{ "ฮันกึล", 8222, "syllabary", aliases = {"ฮังกึล", "Hangul", "Hangeul"}, ranges = { 0x1100, 0x11FF, 0x3001, 0x3003, 0x3008, 0x3011, 0x3013, 0x301F, 0x302E, 0x3030, 0x3037, 0x3037, 0x30FB, 0x30FB, 0x3131, 0x318E, 0x3200, 0x321E, 0x3260, 0x327E, 0xA960, 0xA97C, 0xAC00, 0xD7A3, 0xD7B0, 0xD7C6, 0xD7CB, 0xD7FB, 0xFE45, 0xFE46, 0xFF61, 0xFF65, 0xFFA0, 0xFFBE, 0xFFC2, 0xFFC7, 0xFFCA, 0xFFCF, 0xFFD2, 0xFFD7, 0xFFDA, 0xFFDC, }, } m["Hani"] = process_ranges{ "จีน", 8201, "logography", aliases = {"ฮั่น", "Han"}, ranges = { 0x2E80, 0x2E99, 0x2E9B, 0x2EF3, 0x2F00, 0x2FD5, 0x2FF0, 0x2FFF, 0x3001, 0x3003, 0x3005, 0x3011, 0x3013, 0x301F, 0x3021, 0x302D, 0x3030, 0x3030, 0x3037, 0x303F, 0x3190, 0x319F, 0x31C0, 0x31E5, 0x31EF, 0x31EF, 0x3220, 0x3247, 0x3280, 0x32B0, 0x32C0, 0x32CB, 0x30FB, 0x30FB, 0x32FF, 0x32FF, 0x3358, 0x3370, 0x337B, 0x337F, 0x33E0, 0x33FE, 0x3400, 0x4DBF, 0x4E00, 0x9FFF, 0xA700, 0xA707, 0xF900, 0xFA6D, 0xFA70, 0xFAD9, 0xFE45, 0xFE46, 0xFF61, 0xFF65, 0x16FE2, 0x16FE3, 0x16FF0, 0x16FF1, 0x1D360, 0x1D371, 0x1F250, 0x1F251, 0x20000, 0x2A6DF, 0x2A700, 0x2B739, 0x2B740, 0x2B81D, 0x2B820, 0x2CEA1, 0x2CEB0, 0x2EBE0, 0x2EBF0, 0x2EE5D, 0x2F800, 0x2FA1D, 0x30000, 0x3134A, 0x31350, 0x323AF, }, varieties = {{"ฮั่นจื่อ", "Hanzi"}, {"คันจิ", "Kanji"}, {"ฮันจา", "Hanja"}, {"จื๋อโนม", "Chu Nom"}}, spaces = false, } m["Hans"] = { "จีนตัวย่อ", 185614, m["Hani"][3], aliases = {"Simplified Han"}, ranges = m["Hani"].ranges, characters = m["Hani"].characters, spaces = m["Hani"].spaces, parent = "Hani", } m["Hant"] = { "จีนตัวเต็ม", 178528, m["Hani"][3], aliases = {"Traditional Han"}, ranges = m["Hani"].ranges, characters = m["Hani"].characters, spaces = m["Hani"].spaces, parent = "Hani", } m["Hano"] = process_ranges{ "ฮานูโนโอ", 1584045, "abugida", aliases = {"Hanunoo", "Hanunó'o", "Hanuno'o"}, ranges = { 0x1720, 0x1736, }, } m["Hatr"] = process_ranges{ "Hatran", 20813038, "abjad", ranges = { 0x108E0, 0x108F2, 0x108F4, 0x108F5, 0x108FB, 0x108FF, }, direction = "rtl", } m["Hebr"] = process_ranges{ "ฮีบรู", 33513, "abjad", -- more precisely, impure abjad aliases = {"Hebrew"}, ranges = { 0x0591, 0x05C7, 0x05D0, 0x05EA, 0x05EF, 0x05F4, 0x2135, 0x2138, 0xFB1D, 0xFB36, 0xFB38, 0xFB3C, 0xFB3E, 0xFB3E, 0xFB40, 0xFB41, 0xFB43, 0xFB44, 0xFB46, 0xFB4F, }, direction = "rtl", display_text = "Hebr-common", sort_key = "Hebr-common", strip_diacritics = "Hebr-common", } m["Hira"] = process_ranges{ "ฮิรางานะ", 48332, "syllabary", aliases = {"ฮิระงะนะ", "Hiragana"}, ranges = { 0x3001, 0x3003, 0x3008, 0x3011, 0x3013, 0x301F, 0x3030, 0x3035, 0x3037, 0x3037, 0x303C, 0x303D, 0x3041, 0x3096, 0x3099, 0x30A0, 0x30FB, 0x30FC, 0xFE45, 0xFE46, 0xFF61, 0xFF65, 0xFF70, 0xFF70, 0xFF9E, 0xFF9F, 0x1B001, 0x1B11F, 0x1B132, 0x1B132, 0x1B150, 0x1B152, 0x1F200, 0x1F200, }, varieties = {{"เฮ็นไตงานะ", "Hentaigana"}}, spaces = false, } m["Hluw"] = process_ranges{ "ไฮเออโรกลีฟอานาโตเลีย", 521323, "logography, syllabary", aliases = {"Anatolian Hieroglyphs"}, ranges = { 0x14400, 0x14646, }, wikipedia_article = "ไฮเออโรกลีฟอานาโตเลีย", } m["Hmng"] = process_ranges{ "Pahawh Hmong", 365954, "semisyllabary", aliases = {"ม้ง", "Hmong"}, ranges = { 0x16B00, 0x16B45, 0x16B50, 0x16B59, 0x16B5B, 0x16B61, 0x16B63, 0x16B77, 0x16B7D, 0x16B8F, }, } m["Hmnp"] = process_ranges{ "Nyiakeng Puachue Hmong", 33712499, "alphabet", ranges = { 0x1E100, 0x1E12C, 0x1E130, 0x1E13D, 0x1E140, 0x1E149, 0x1E14E, 0x1E14F, }, } m["Hung"] = process_ranges{ "ฮังการีเก่า", 446224, "alphabet", aliases = {"รูนฮังการี", "Old Hungarian", "Hungarian runic"}, ranges = { 0x10C80, 0x10CB2, 0x10CC0, 0x10CF2, 0x10CFA, 0x10CFF, }, capitalized = true, direction = "rtl", } m["Ibrnn"] = { "ไอบีเรียตะวันออกเฉียงเหนือ", 1113155, "semisyllabary", aliases = {"Northeastern Iberian"}, ietf_subtag = "Zzzz", -- Not in Unicode } m["Ibrns"] = { "ไอบีเรียตะวันออกเฉียงใต้", 2305351, "semisyllabary", aliases = {"Southeastern Iberian"}, ietf_subtag = "Zzzz", -- Not in Unicode } m["Image"] = { -- To be used to avoid any formatting or link processing "Image-rendered", 478798, -- This should not have any characters listed ietf_subtag = "Zyyy", translit = false, character_category = false, -- none } m["Inds"] = { "สินธุ", 601388, aliases = {"อินดัส", "Indus", "Harappan", "Indus Valley"}, } m["Ipach"] = { "สัทอักษรสากล", 21204, aliases = {"International Phonetic Alphabet", "IPA"}, ietf_subtag = "Latn", } m["Ital"] = process_ranges{ "อิตาลิกเก่า", 4891256, "alphabet", aliases = {"Old Italic"}, ranges = { 0x10300, 0x10323, 0x1032D, 0x1032F, }, translit = "Ital-translit", } m["Java"] = process_ranges{ "ชวา", 879704, "abugida", aliases = {"Javanese"}, ranges = { 0xA980, 0xA9CD, 0xA9CF, 0xA9D9, 0xA9DE, 0xA9DF, }, } m["Jurc"] = { "จูร์เชน", 912240, "logography", aliases = {"Jurchen"}, spaces = false, } m["Kali"] = process_ranges{ "กะยา", 4919239, "abugida", aliases = {"Kayah Li"}, ranges = { 0xA900, 0xA92F, }, } m["Kana"] = process_ranges{ "คาตากานะ", 82946, "syllabary", aliases = {"คะตะกะนะ", "Katakana"}, ranges = { 0x3001, 0x3003, 0x3008, 0x3011, 0x3013, 0x301F, 0x3030, 0x3035, 0x3037, 0x3037, 0x303C, 0x303D, 0x3099, 0x309C, 0x30A0, 0x30FF, 0x31F0, 0x31FF, 0x32D0, 0x32FE, 0x3300, 0x3357, 0xFE45, 0xFE46, 0xFF61, 0xFF9F, 0x1AFF0, 0x1AFF3, 0x1AFF5, 0x1AFFB, 0x1AFFD, 0x1AFFE, 0x1B000, 0x1B000, 0x1B120, 0x1B122, 0x1B155, 0x1B155, 0x1B164, 0x1B167, }, spaces = false, } m["Kawi"] = process_ranges{ "กวิ", 975802, "abugida", aliases = {"Kawi"}, ranges = { 0x11F00, 0x11F10, 0x11F12, 0x11F3A, 0x11F3E, 0x11F5A, }, } m["Khar"] = process_ranges{ "ขโรษฐี", 1161266, "abugida", aliases = {"Kharoshthi"}, ranges = { 0x10A00, 0x10A03, 0x10A05, 0x10A06, 0x10A0C, 0x10A13, 0x10A15, 0x10A17, 0x10A19, 0x10A35, 0x10A38, 0x10A3A, 0x10A3F, 0x10A48, 0x10A50, 0x10A58, }, direction = "rtl", } m["Khmr"] = process_ranges{ "เขมร", 1054190, "abugida", aliases = {"Khmer"}, ranges = { 0x1780, 0x17DD, 0x17E0, 0x17E9, 0x17F0, 0x17F9, 0x19E0, 0x19FF, }, spaces = false, normalizationFixes = handle_normalization_fixes{ from = {"ឣ", "ឤ"}, to = {"អ", "អា"} }, } m["Khoj"] = process_ranges{ "Khojki", 1740656, "abugida", ranges = { 0x0AE6, 0x0AEF, 0xA830, 0xA839, 0x11200, 0x11211, 0x11213, 0x11241, }, normalizationFixes = handle_normalization_fixes{ from = {"𑈀𑈬𑈱", "𑈀𑈬", "𑈀𑈱", "𑈀𑈳", "𑈁𑈱", "𑈆𑈬", "𑈬𑈰", "𑈬𑈱", "𑉀𑈮"}, to = {"𑈇", "𑈁", "𑈅", "𑈇", "𑈇", "𑈃", "𑈲", "𑈳", "𑈂"} }, } m["Khomt"] = { "ขอมไทย", 13023788, "abugida", aliases = {"Khom Thai"}, -- Not in Unicode } m["Kitl"] = { "คีตันใหญ่", 6401797, "logography", aliases = {"Khitan Large"}, spaces = false, } m["Kits"] = process_ranges{ "คีตันเล็ก", 6401800, "logography, syllabary", aliases = {"Khitan Small"}, ranges = { 0x16FE4, 0x16FE4, 0x18B00, 0x18CD5, 0x18CFF, 0x18CFF, }, spaces = false, } m["Knda"] = process_ranges{ "กันนฑะ", 839666, "abugida", aliases = {"กันนาดา", "Kannada"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0C80, 0x0C8C, 0x0C8E, 0x0C90, 0x0C92, 0x0CA8, 0x0CAA, 0x0CB3, 0x0CB5, 0x0CB9, 0x0CBC, 0x0CC4, 0x0CC6, 0x0CC8, 0x0CCA, 0x0CCD, 0x0CD5, 0x0CD6, 0x0CDD, 0x0CDE, 0x0CE0, 0x0CE3, 0x0CE6, 0x0CEF, 0x0CF1, 0x0CF3, 0x1CD0, 0x1CD0, 0x1CD2, 0x1CD3, 0x1CDA, 0x1CDA, 0x1CF2, 0x1CF2, 0x1CF4, 0x1CF4, 0xA830, 0xA835, }, normalizationFixes = handle_normalization_fixes{ from = {"ಉಾ", "ಋಾ", "ಒೌ"}, to = {"ಊ", "ೠ", "ಔ"} }, translit = "Knda-translit", } m["Kpel"] = { "Kpelle", 1586299, "syllabary", -- Not in Unicode } m["Krai"] = process_ranges{ "Kirat Rai", 123173834, "abugida", aliases = {"Rai", "Khambu Rai", "Rai Barṇamālā", "Kirat Khambu Rai"}, ranges = { 0x16D40, 0x16D79, }, } m["Kthi"] = process_ranges{ "ไกถี", 1253814, "abugida", aliases = {"Kaithi"}, ranges = { 0x0966, 0x096F, 0xA830, 0xA839, 0x11080, 0x110C2, 0x110CD, 0x110CD, }, } m["Kulit"] = { "Kulitan", 6443044, "abugida", -- Not in Unicode } m["Lana"] = process_ranges{ "ไทธรรม", 1314503, "abugida", aliases = {"ธรรม", "ตัวเมือง", "ล้านนา", "Tai Tham", "Tham", "Tua Mueang", "Lanna"}, ranges = { 0x1A20, 0x1A5E, 0x1A60, 0x1A7C, 0x1A7F, 0x1A89, 0x1A90, 0x1A99, 0x1AA0, 0x1AAD, }, spaces = false, } m["Laoo"] = process_ranges{ "ลาว", 1815229, "abugida", aliases = {"Lao"}, ranges = { 0x0E81, 0x0E82, 0x0E84, 0x0E84, 0x0E86, 0x0E8A, 0x0E8C, 0x0EA3, 0x0EA5, 0x0EA5, 0x0EA7, 0x0EBD, 0x0EC0, 0x0EC4, 0x0EC6, 0x0EC6, 0x0EC8, 0x0ECE, 0x0ED0, 0x0ED9, 0x0EDC, 0x0EDF, }, spaces = false, } m["Latn"] = process_ranges{ "ละติน", 8229, "alphabet", aliases = {"ลาติน", "โรมัน", "Latin", "Roman"}, ranges = { 0x0041, 0x005A, 0x0061, 0x007A, 0x00AA, 0x00AA, 0x00BA, 0x00BA, 0x00C0, 0x00D6, 0x00D8, 0x00F6, 0x00F8, 0x02B8, 0x02C0, 0x02C1, 0x02E0, 0x02E4, 0x0363, 0x036F, 0x0485, 0x0486, 0x0951, 0x0952, 0x10FB, 0x10FB, 0x1D00, 0x1D25, 0x1D2C, 0x1D5C, 0x1D62, 0x1D65, 0x1D6B, 0x1D77, 0x1D79, 0x1DBE, 0x1DF8, 0x1DF8, 0x1E00, 0x1EFF, 0x202F, 0x202F, 0x2071, 0x2071, 0x207F, 0x207F, 0x2090, 0x209C, 0x20F0, 0x20F0, 0x2100, 0x2125, 0x2128, 0x2128, 0x212A, 0x2134, 0x2139, 0x213B, 0x2141, 0x214E, 0x2160, 0x2188, 0x2C60, 0x2C7F, 0xA700, 0xA707, 0xA722, 0xA787, 0xA78B, 0xA7CD, 0xA7D0, 0xA7D1, 0xA7D3, 0xA7D3, 0xA7D5, 0xA7DC, 0xA7F2, 0xA7FF, 0xA92E, 0xA92E, 0xAB30, 0xAB5A, 0xAB5C, 0xAB64, 0xAB66, 0xAB69, 0xFB00, 0xFB06, 0xFF21, 0xFF3A, 0xFF41, 0xFF5A, 0x10780, 0x10785, 0x10787, 0x107B0, 0x107B2, 0x107BA, 0x1DF00, 0x1DF1E, 0x1DF25, 0x1DF2A, }, varieties = {{"รูมี", "Rumi"}, {"โรมาจิ", "โรมะจิ", "Romaji", "Rōmaji"}, "Romaja"}, capitalized = true, translit = false, } m["Latf"] = { "ฟรักทูร์", 148443, m["Latn"][3], aliases = {"Fraktur"}, ranges = m["Latn"].ranges, characters = m["Latn"].characters, other_names = {"แบล็กเลตเทอร์", "Blackletter"}, -- Blackletter is actually the parent "script" capitalized = m["Latn"].capitalized, translit = m["Latn"].translit, parent = "Latn", } m["Latg"] = { "แกลิก", 1432616, m["Latn"][3], aliases = {"Gaelic"}, ranges = m["Latn"].ranges, characters = m["Latn"].characters, other_names = {"ไอริช", "Irish"}, capitalized = m["Latn"].capitalized, translit = m["Latn"].translit, parent = "Latn", } m["pjt-Latn"] = { "ละติน", nil, m["Latn"][3], aliases = m["Latn"].aliases, ranges = m["Latn"].ranges, characters = m["Latn"].characters, capitalized = m["Latn"].capitalized, translit = m["Latn"].translit, parent = "Latn", } m["Leke"] = { "Leke", 19572613, "abugida", -- Not in Unicode } m["Lepc"] = process_ranges{ "เลปชา", 1481626, "abugida", aliases = {"Lepcha"}, ranges = { 0x1C00, 0x1C37, 0x1C3B, 0x1C49, 0x1C4D, 0x1C4F, }, } m["Limb"] = process_ranges{ "ลิมบู", 933796, "abugida", aliases = {"Limbu"}, ranges = { 0x0965, 0x0965, 0x1900, 0x191E, 0x1920, 0x192B, 0x1930, 0x193B, 0x1940, 0x1940, 0x1944, 0x194F, }, } m["Lina"] = process_ranges{ "ลิเนียร์เอ", 30972, aliases = {"Linear A"}, ranges = { 0x10107, 0x10133, 0x10600, 0x10736, 0x10740, 0x10755, 0x10760, 0x10767, }, } m["Linb"] = process_ranges{ "ลิเนียร์บี", 190102, aliases = {"Linear B"}, ranges = { 0x10000, 0x1000B, 0x1000D, 0x10026, 0x10028, 0x1003A, 0x1003C, 0x1003D, 0x1003F, 0x1004D, 0x10050, 0x1005D, 0x10080, 0x100FA, 0x10100, 0x10102, 0x10107, 0x10133, 0x10137, 0x1013F, }, } m["Lisu"] = process_ranges{ "ลีสู่", 1194621, "alphabet", aliases = {"ฟราเซอร์", "Lisu", "Fraser"}, ranges = { 0x300A, 0x300B, 0xA4D0, 0xA4FF, 0x11FB0, 0x11FB0, }, normalizationFixes = handle_normalization_fixes{ from = {"['’]", "[.ꓸ][.ꓸ]", "[.ꓸ][,ꓹ]"}, to = {"ʼ", "ꓺ", "ꓻ"} }, translit = "Lisu-translit", sort_key = { from = {"𑾰"}, to = {"ꓬ" .. p[1]} }, } m["Loma"] = { "Loma", 13023816, "syllabary", -- Not in Unicode } m["Lyci"] = process_ranges{ "ลิเชีย", 913587, "alphabet", aliases = {"Lycian"}, ranges = { 0x10280, 0x1029C, }, } m["Lydi"] = process_ranges{ "ลิเดีย", 4261300, "alphabet", aliases = {"Lydian"}, ranges = { 0x10920, 0x10939, 0x1093F, 0x1093F, }, direction = "rtl", } m["Mahj"] = process_ranges{ "มหาชนี", 6732850, "abugida", aliases = {"Mahajani"}, ranges = { 0x0964, 0x096F, 0xA830, 0xA839, 0x11150, 0x11176, }, } m["Maka"] = process_ranges{ "มากัซซาร์", 72947229, "abugida", aliases = {"มากาซาร์", "มากาซาร์เก่า", "Makasar", "Makassar", "Old Makasar"}, ranges = { 0x11EE0, 0x11EF8, }, } m["Mand"] = process_ranges{ "มันดาอิก", 1812130, aliases = {"Mandaic", "Mandaean"}, ranges = { 0x0640, 0x0640, 0x0840, 0x085B, 0x085E, 0x085E, }, direction = "rtl", } m["Mani"] = process_ranges{ "Manichaean", 3544702, "abjad", ranges = { 0x0640, 0x0640, 0x10AC0, 0x10AE6, 0x10AEB, 0x10AF6, }, direction = "rtl", translit = "Mani-translit", } m["Marc"] = process_ranges{ "Marchen", 72403709, "abugida", ranges = { 0x11C70, 0x11C8F, 0x11C92, 0x11CA7, 0x11CA9, 0x11CB6, }, } m["Maya"] = process_ranges{ "มายา", 211248, aliases = {"มายัน", "Maya", "Maya hieroglyphic", "Mayan", "Mayan hieroglyphic"}, ranges = { 0x1D2E0, 0x1D2F3, }, } m["Medf"] = process_ranges{ "Medefaidrin", 1519764, aliases = {"Oberi Okaime", "Oberi Ɔkaimɛ"}, ranges = { 0x16E40, 0x16E9A, }, capitalized = true, } m["Mend"] = process_ranges{ "Mende", 951069, aliases = {"Mende Kikakui"}, ranges = { 0x1E800, 0x1E8C4, 0x1E8C7, 0x1E8D6, }, direction = "rtl", } m["Merc"] = process_ranges{ "Meroitic cursive", 73028124, "abugida", ranges = { 0x109A0, 0x109B7, 0x109BC, 0x109CF, 0x109D2, 0x109FF, }, direction = "rtl", } m["Mero"] = process_ranges{ "Meroitic hieroglyphic", 73028623, "abugida", ranges = { 0x10980, 0x1099F, }, direction = "rtl", wikipedia_article = "Meroitic hieroglyphs", } m["Mlym"] = process_ranges{ "มลยาฬัม", 1164129, "abugida", aliases = {"มลยาลัม", "มาลายาลัม", "Malayalam"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0D00, 0x0D0C, 0x0D0E, 0x0D10, 0x0D12, 0x0D44, 0x0D46, 0x0D48, 0x0D4A, 0x0D4F, 0x0D54, 0x0D63, 0x0D66, 0x0D7F, 0x1CDA, 0x1CDA, 0x1CF2, 0x1CF2, 0xA830, 0xA832, }, normalizationFixes = handle_normalization_fixes{ from = {"ഇൗ", "ഉൗ", "എെ", "ഒാ", "ഒൗ", "ക്‍", "ണ്‍", "ന്‍റ", "ന്‍", "മ്‍", "യ്‍", "ര്‍", "ല്‍", "ള്‍", "ഴ്‍", "െെ", "ൻ്റ"}, to = {"ഈ", "ഊ", "ഐ", "ഓ", "ഔ", "ൿ", "ൺ", "ൻറ", "ൻ", "ൔ", "ൕ", "ർ", "ൽ", "ൾ", "ൖ", "ൈ", "ന്റ"} }, translit = "Mlym-translit", } m["Modi"] = process_ranges{ "โมฑี", 1703713, "abugida", aliases = {"Modi"}, ranges = { 0xA830, 0xA839, 0x11600, 0x11644, 0x11650, 0x11659, }, normalizationFixes = handle_normalization_fixes{ from = {"𑘀𑘹", "𑘀𑘺", "𑘁𑘹", "𑘁𑘺"}, to = {"𑘊", "𑘋", "𑘌", "𑘍"} }, } do local Mong_displaytext = { from = {"([ᠨ-ᡂᡸ])ᠶ([ᠨ-ᡂᡸ])", "([ᠠ-ᡂᡸ])ᠸ([^᠋ᠠ-ᠧ])", "([ᠠ-ᡂᡸ])ᠸ$"}, to = {"%1ᠢ%2", "%1ᠧ%2", "%1ᠧ"} } m["Mong"] = process_ranges{ "มองโกเลีย", 1055705, "alphabet", aliases = {"Mongolian", "Mongol bichig", "Hudum Mongol bichig"}, ranges = { 0x1800, 0x1805, 0x180A, 0x1819, 0x1820, 0x1842, 0x1878, 0x1878, 0x1880, 0x1897, 0x18A6, 0x18A6, 0x18A9, 0x18A9, 0x200C, 0x200D, 0x202F, 0x202F, 0x3001, 0x3002, 0x3008, 0x300B, 0x11660, 0x11668, }, direction = "vertical-ltr", display_text = Mong_displaytext, strip_diacritics = Mong_displaytext, translit = "Mong-translit", } m["mnc-Mong"] = process_ranges{ "แมนจู", 122888, m["Mong"][3], aliases = {"Manchu"}, ranges = { 0x1801, 0x1801, 0x1804, 0x1804, 0x1808, 0x180F, 0x1820, 0x1820, 0x1823, 0x1823, 0x1828, 0x182A, 0x182E, 0x1830, 0x1834, 0x1838, 0x183A, 0x183A, 0x185D, 0x185D, 0x185F, 0x1861, 0x1864, 0x1869, 0x186C, 0x1871, 0x1873, 0x1877, 0x1880, 0x1888, 0x188F, 0x188F, 0x189A, 0x18A5, 0x18A8, 0x18A8, 0x18AA, 0x18AA, 0x200C, 0x200D, 0x202F, 0x202F, }, direction = "vertical-ltr", parent = "Mong", translit = "mnc-translit", } m["sjo-Mong"] = process_ranges{ "ซิเบ", 113624153, m["Mong"][3], aliases = {"Xibe", "Sibe"}, ranges = { 0x1804, 0x1804, 0x1807, 0x1807, 0x180A, 0x180F, 0x1820, 0x1820, 0x1823, 0x1823, 0x1828, 0x1828, 0x182A, 0x182A, 0x182E, 0x1830, 0x1834, 0x1838, 0x183A, 0x183A, 0x185D, 0x1872, 0x200C, 0x200D, 0x202F, 0x202F, }, direction = "vertical-ltr", parent = "mnc-Mong", } m["xwo-Mong"] = process_ranges{ "Clear Script", 529085, m["Mong"][3], aliases = {"Todo", "Todo bichig"}, ranges = { 0x1800, 0x1801, 0x1804, 0x1806, 0x180A, 0x1820, 0x1828, 0x1828, 0x182F, 0x1831, 0x1834, 0x1834, 0x1837, 0x1838, 0x183A, 0x183B, 0x1840, 0x1840, 0x1843, 0x185C, 0x1880, 0x1887, 0x1889, 0x188F, 0x1894, 0x1894, 0x1896, 0x1899, 0x18A7, 0x18A7, 0x200C, 0x200D, 0x202F, 0x202F, 0x11669, 0x1166C, }, direction = "vertical-ltr", parent = "Mong", translit = "xwo-translit", } end m["Moon"] = { "Moon", 918391, "alphabet", aliases = {"Moon System of Embossed Reading", "Moon type", "Moon writing", "Moon alphabet", "Moon code"}, -- Not in Unicode } m["Morse"] = { "รหัสมอร์ส", 79897, aliases = {"Morse code"}, ietf_subtag = "Zsym", } m["Mroo"] = process_ranges{ "มรู", 75919253, aliases = {"มโร", "มรุง", "Mru", "Mro", "Mrung"}, ranges = { 0x16A40, 0x16A5E, 0x16A60, 0x16A69, 0x16A6E, 0x16A6F, }, } m["Mtei"] = process_ranges{ "มณีปุระ", 2981413, "abugida", aliases = {"ไมไตมะเยก", "มีไตมะเยก", "Manipuri", "Meitei Mayek", "Meetei Mayek"}, ranges = { 0xAAE0, 0xAAF6, 0xABC0, 0xABED, 0xABF0, 0xABF9, }, } m["Mult"] = process_ranges{ "Multani", 17047906, "abugida", ranges = { 0x0A66, 0x0A6F, 0x11280, 0x11286, 0x11288, 0x11288, 0x1128A, 0x1128D, 0x1128F, 0x1129D, 0x1129F, 0x112A9, }, } m["Music"] = process_ranges{ "สัญกรณ์ดนตรี", 233861, "pictography", aliases = {"Musical notation"}, ranges = { 0x2669, 0x266F, 0x1D100, 0x1D126, 0x1D129, 0x1D1EA, }, ietf_subtag = "Zsym", translit = false, } m["Mymr"] = process_ranges{ "พม่า", 43887939, "abugida", aliases = {"เมียนมา", "Burmese", "Myanmar"}, ranges = { 0x1000, 0x109F, 0xA92E, 0xA92E, 0xA9E0, 0xA9FE, 0xAA60, 0xAA7F, 0x116D0, 0x116E3, }, spaces = false, } m["Nagm"] = process_ranges{ "Mundari Bani", 106917274, "alphabet", aliases = {"Nag Mundari"}, ranges = { 0x1E4D0, 0x1E4F9, }, } m["Nand"] = process_ranges{ "นันทินาครี", 6963324, "abugida", aliases = {"Nandinagari"}, ranges = { 0x0964, 0x0965, 0x0CE6, 0x0CEF, 0x1CE9, 0x1CE9, 0x1CF2, 0x1CF2, 0x1CFA, 0x1CFA, 0xA830, 0xA835, 0x119A0, 0x119A7, 0x119AA, 0x119D7, 0x119DA, 0x119E4, }, } m["Narb"] = process_ranges{ "Ancient North Arabian", 1472213, "abjad", aliases = {"Old North Arabian"}, ranges = { 0x10A80, 0x10A9F, }, direction = "rtl", translit = "Narb-translit", } m["Nbat"] = process_ranges{ "Nabataean", 855624, "abjad", aliases = {"Nabatean"}, ranges = { 0x10880, 0x1089E, 0x108A7, 0x108AF, }, direction = "rtl", } m["Newa"] = process_ranges{ "Newa", 7237292, "abugida", aliases = {"Newar", "Newari", "Prachalit Nepal"}, ranges = { 0x11400, 0x1145B, 0x1145D, 0x11461, }, } m["Nkdb"] = { "Dongba", 1190953, "pictography", aliases = {"Naxi Dongba", "Nakhi Dongba", "Tomba", "Tompa", "Mo-so"}, spaces = false, -- Not in Unicode } m["Nkgb"] = { "Geba", 731189, "syllabary", aliases = {"Nakhi Geba", "Naxi Geba"}, spaces = false, -- Not in Unicode } m["Nkoo"] = process_ranges{ "N'Ko", 1062587, "alphabet", ranges = { 0x060C, 0x060C, 0x061B, 0x061B, 0x061F, 0x061F, 0x07C0, 0x07FA, 0x07FD, 0x07FF, 0xFD3E, 0xFD3F, }, direction = "rtl", } m["None"] = { "ไม่ระบุ", nil, aliases = {"unspecified"}, -- This should not have any characters listed ietf_subtag = "Zyyy", translit = false, character_category = false, -- none } m["Nshu"] = process_ranges{ "Nüshu", 56436, "syllabary", aliases = {"Nushu"}, ranges = { 0x16FE1, 0x16FE1, 0x1B170, 0x1B2FB, }, spaces = false, } m["Ogam"] = process_ranges{ "Ogham", 184661, ranges = { 0x1680, 0x169C, }, } m["Olck"] = process_ranges{ "อ็อลจิกิ", 201688, aliases = {"อ็อลเจะเม็ท", "อ็อล", "สันถาลี", "สันถัล", "Ol Chiki", "Ol Chemetʼ", "Ol", "Santali"}, ranges = { 0x1C50, 0x1C7F, }, } m["Onao"] = process_ranges{ "Ol Onal", 108607084, "alphabet", ranges = { 0x0964, 0x0965, 0x1E5D0, 0x1E5FA, 0x1E5FF, 0x1E5FF, }, } m["Orkh"] = process_ranges{ "Old Turkic", 5058305, aliases = {"Orkhon runic"}, ranges = { 0x10C00, 0x10C48, }, direction = "rtl", translit = "Orkh-translit", } m["Orya"] = process_ranges{ "โอริยา", 1760127, "abugida", aliases = {"โอเดีย", "Oriya", "Odia"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0B01, 0x0B03, 0x0B05, 0x0B0C, 0x0B0F, 0x0B10, 0x0B13, 0x0B28, 0x0B2A, 0x0B30, 0x0B32, 0x0B33, 0x0B35, 0x0B39, 0x0B3C, 0x0B44, 0x0B47, 0x0B48, 0x0B4B, 0x0B4D, 0x0B55, 0x0B57, 0x0B5C, 0x0B5D, 0x0B5F, 0x0B63, 0x0B66, 0x0B77, 0x1CDA, 0x1CDA, 0x1CF2, 0x1CF2, }, normalizationFixes = handle_normalization_fixes{ from = {"ଅା", "ଏୗ", "ଓୗ"}, to = {"ଆ", "ଐ", "ଔ"} }, } m["Osge"] = process_ranges{ "Osage", 7105529, ranges = { 0x104B0, 0x104D3, 0x104D8, 0x104FB, }, capitalized = true, } m["Osma"] = process_ranges{ "อุสมาน", 1377866, aliases = {"Osmanya"}, ranges = { 0x10480, 0x1049D, 0x104A0, 0x104A9, }, } m["Ougr"] = process_ranges{ "อุยกูร์เก่า", 1998938, "abjad, alphabet", aliases = {"Old Uyghur"}, ranges = { 0x0640, 0x0640, 0x10AF2, 0x10AF2, 0x10F70, 0x10F89, }, -- This should ideally be "vertical-ltr", but getting the CSS right is tricky because it's right-to-left horizontally, but left-to-right vertically. Currently, displaying it vertically causes it to display bottom-to-top. direction = "rtl", } m["Palm"] = process_ranges{ "Palmyrene", 17538100, ranges = { 0x10860, 0x1087F, }, direction = "rtl", } m["Pauc"] = process_ranges{ "Pau Cin Hau", 25339852, ranges = { 0x11AC0, 0x11AF8, }, } m["Pcun"] = { "Proto-Cuneiform", 1650699, "pictography", -- Not in Unicode } m["Pelm"] = { "Proto-Elamite", 56305763, "pictography", -- Not in Unicode } m["Perm"] = process_ranges{ "Old Permic", 147899, ranges = { 0x0483, 0x0483, 0x10350, 0x1037A, }, } m["Phag"] = process_ranges{ "Phags-pa", 822836, "abugida", ranges = { 0x1802, 0x1803, 0x1805, 0x1805, 0x200C, 0x200D, 0x202F, 0x202F, 0x3002, 0x3002, 0xA840, 0xA877, }, direction = "vertical-ltr", } m["Phli"] = process_ranges{ "ปาห์ลาวีจารึก", 24089793, "abjad", aliases = {"Inscriptional Pahlavi"}, ranges = { 0x10B60, 0x10B72, 0x10B78, 0x10B7F, }, direction = "rtl", } m["Phlp"] = process_ranges{ "ปาห์ลาวีเพลงสดุดี", 7253954, "abjad", aliases = {"Psalter Pahlavi"}, ranges = { 0x0640, 0x0640, 0x10B80, 0x10B91, 0x10B99, 0x10B9C, 0x10BA9, 0x10BAF, }, direction = "rtl", } m["Phlv"] = { "ปาห์ลาวีคัมภีร์", 72403118, "abjad", aliases = {"ปาห์ลาวีหนังสือ", "Book Pahlavi"}, direction = "rtl", wikipedia_article = "Pahlavi scripts#Book Pahlavi", -- Not in Unicode } m["Phnx"] = process_ranges{ "ฟินิเชีย", 26752, "abjad", aliases = {"Phoenician"}, ranges = { 0x10900, 0x1091B, 0x1091F, 0x1091F, }, direction = "rtl", translit = "Phnx-translit", } m["Plrd"] = process_ranges{ "Pollard", 601734, "abugida", aliases = {"Miao"}, ranges = { 0x16F00, 0x16F4A, 0x16F4F, 0x16F87, 0x16F8F, 0x16F9F, }, } m["Prti"] = process_ranges{ "พาร์เทียจารึก", 13023804, aliases = {"Inscriptional Parthian"}, ranges = { 0x10B40, 0x10B55, 0x10B58, 0x10B5F, }, direction = "rtl", } m["Psin"] = { "Proto-Sinaitic", 1065250, "abjad", direction = "rtl", -- Not in Unicode } m["Ranj"] = { "Ranjana", 2385276, "abugida", -- Not in Unicode } m["Rjng"] = process_ranges{ "Rejang", 2007960, "abugida", ranges = { 0xA930, 0xA953, 0xA95F, 0xA95F, }, } m["Rohg"] = process_ranges{ "ฮานิฟ", 21028705, "alphabet", aliases = {"ฮานีฟี", "Hanifi Rohingya", "Hanifi"}, ranges = { 0x060C, 0x060C, 0x061B, 0x061B, 0x061F, 0x061F, 0x0640, 0x0640, 0x06D4, 0x06D4, 0x10D00, 0x10D27, 0x10D30, 0x10D39, }, direction = "rtl", } m["Roro"] = { "Rongorongo", 209764, -- Not in Unicode } m["Rumin"] = process_ranges{ "Rumi numerals", nil, ranges = { 0x10E60, 0x10E7E, }, ietf_subtag = "Arab", } m["Runr"] = process_ranges{ "รูน", 82996, "alphabet", aliases = {"รูนิก", "Runic"}, ranges = { 0x16A0, 0x16EA, 0x16EE, 0x16F8, }, } do local Samr_stripdiacritics = { remove_diacritics = c.CGJ .. u(0x0816) .. "-" .. u(0x082D), } m["Samr"] = process_ranges{ "Samaritan", 1550930, "abjad", ranges = { 0x0800, 0x082D, 0x0830, 0x083E, }, direction = "rtl", strip_diacritics = Samr_stripdiacritics, sort_key = Samr_stripdiacritics, } end m["Sarb"] = process_ranges{ "Ancient South Arabian", 446074, "abjad", aliases = {"Old South Arabian"}, ranges = { 0x10A60, 0x10A7F, }, direction = "rtl", translit = "Sarb-translit", } m["Saur"] = process_ranges{ "Saurashtra", 3535165, "abugida", ranges = { 0xA880, 0xA8C5, 0xA8CE, 0xA8D9, }, } m["Semap"] = { "สัญญาณธง", 250796, "pictography", aliases = {"flag semaphore"}, ietf_subtag = "Zsym", } m["Sgnw"] = process_ranges{ "SignWriting", 1497335, "pictography", aliases = {"Sutton SignWriting"}, ranges = { 0x1D800, 0x1DA8B, 0x1DA9B, 0x1DA9F, 0x1DAA1, 0x1DAAF, }, translit = false, } m["Shaw"] = process_ranges{ "Shavian", 1970098, aliases = {"Shaw"}, ranges = { 0x10450, 0x1047F, }, } m["Shrd"] = process_ranges{ "ศารทา", 2047117, "abugida", aliases = {"Sharada"}, ranges = { 0x0951, 0x0951, 0x1CD7, 0x1CD7, 0x1CD9, 0x1CD9, 0x1CDC, 0x1CDD, 0x1CE0, 0x1CE0, 0xA830, 0xA835, 0xA838, 0xA838, 0x11180, 0x111DF, }, translit = "Shrd-translit", } m["Shui"] = { "Sui", 752854, "logography", spaces = false, -- Not in Unicode } m["Sidd"] = process_ranges{ "สิทธัม", 250379, "abugida", aliases = {"Siddham"}, ranges = { 0x11580, 0x115B5, 0x115B8, 0x115DD, }, translit = "Sidd-translit", } m["Sidt"] = { "Sidetic", 36659, "alphabet", direction = "rtl", -- Not in Unicode } m["Sind"] = process_ranges{ "ขุทาพาที", 6402810, "abugida", aliases = {"ขุทาวาที", "Khudabadi", "Khudawadi"}, ranges = { 0x0964, 0x0965, 0xA830, 0xA839, 0x112B0, 0x112EA, 0x112F0, 0x112F9, }, normalizationFixes = handle_normalization_fixes{ from = {"𑊰𑋠", "𑊰𑋥", "𑊰𑋦", "𑊰𑋧", "𑊰𑋨"}, to = {"𑊱", "𑊶", "𑊷", "𑊸", "𑊹"} }, } m["Sinh"] = process_ranges{ "สิงหล", 1574992, "abugida", aliases = {"Sinhalese", "Sinhala"}, ranges = { 0x0964, 0x0965, 0x0D81, 0x0D83, 0x0D85, 0x0D96, 0x0D9A, 0x0DB1, 0x0DB3, 0x0DBB, 0x0DBD, 0x0DBD, 0x0DC0, 0x0DC6, 0x0DCA, 0x0DCA, 0x0DCF, 0x0DD4, 0x0DD6, 0x0DD6, 0x0DD8, 0x0DDF, 0x0DE6, 0x0DEF, 0x0DF2, 0x0DF4, 0x1CF2, 0x1CF2, 0x111E1, 0x111F4, }, normalizationFixes = handle_normalization_fixes{ from = {"අා", "අැ", "අෑ", "උෟ", "ඍෘ", "ඏෟ", "එ්", "එෙ", "ඔෟ", "ෘෘ"}, to = {"ආ", "ඇ", "ඈ", "ඌ", "ඎ", "ඐ", "ඒ", "ඓ", "ඖ", "ෲ"} }, } m["Sogd"] = process_ranges{ "ซอกเดีย", 578359, "abjad", aliases = {"Sogdian"}, ranges = { 0x0640, 0x0640, 0x10F30, 0x10F59, }, direction = "rtl", } m["Sogo"] = process_ranges{ "ซอกเดียเก่า", 72403254, "abjad", aliases = {"Old Sogdian"}, ranges = { 0x10F00, 0x10F27, }, direction = "rtl", } m["Sora"] = process_ranges{ "Sorang Sompeng", 7563292, aliases = {"Sora Sompeng"}, ranges = { 0x110D0, 0x110E8, 0x110F0, 0x110F9, }, } m["Soyo"] = process_ranges{ "Soyombo", 8009382, "abugida", ranges = { 0x11A50, 0x11AA2, }, } m["Sund"] = process_ranges{ "ซุนดา", 51589, "abugida", aliases = {"Sundanese"}, ranges = { 0x1B80, 0x1BBF, 0x1CC0, 0x1CC7, }, } m["Sunu"] = process_ranges{ "Sunuwar", 109984965, "alphabet", ranges = { 0x11BC0, 0x11BE1, 0x11BF0, 0x11BF9, }, } m["Sylo"] = process_ranges{ "สิเลฏินาครี", 144128, "abugida", aliases = {"สิลอฏินาครี", "Syloti Nagri", "Sylheti Nagari"}, ranges = { 0x0964, 0x0965, 0x09E6, 0x09EF, 0xA800, 0xA82C, }, } m["Syrc"] = process_ranges{ "ซีรีแอก", 26567, "abjad", -- more precisely, impure abjad aliases = {"ซีเรียค", "Syriac"}, ranges = { 0x060C, 0x060C, 0x061B, 0x061C, 0x061F, 0x061F, 0x0640, 0x0640, 0x064B, 0x0655, 0x0670, 0x0670, 0x0700, 0x070D, 0x070F, 0x074A, 0x074D, 0x074F, 0x0860, 0x086A, 0x1DF8, 0x1DF8, 0x1DFA, 0x1DFA, }, direction = "rtl", } -- Syre, Syrj, Syrn are apparently subsumed into Syrc; discuss if this causes issues m["Tagb"] = process_ranges{ "ตักบันวา", 977444, "abugida", aliases = {"Tagbanwa"}, ranges = { 0x1735, 0x1736, 0x1760, 0x176C, 0x176E, 0x1770, 0x1772, 0x1773, }, } m["Takr"] = process_ranges{ "ฏากรี", 759202, "abugida", aliases = {"ตากรี", "Takri"}, ranges = { 0x0964, 0x0965, 0xA830, 0xA839, 0x11680, 0x116B9, 0x116C0, 0x116C9, }, normalizationFixes = handle_normalization_fixes{ from = {"𑚀𑚭", "𑚀𑚴", "𑚀𑚵", "𑚆𑚲"}, to = {"𑚁", "𑚈", "𑚉", "𑚇"} }, } m["Tale"] = process_ranges{ "ไทใต้คง", 2566326, "abugida", aliases = {"ไทเหนือ", "ไทเหนือใหม่", "ไตเหลอ", "Tai Nüa", "Tai Nuea", "New Tai Nüa", "New Tai Nuea", "Dehong Dai", "Tai Dehong", "Tai Le"}, ranges = { 0x1040, 0x1049, 0x1950, 0x196D, 0x1970, 0x1974, }, spaces = false, } m["Talu"] = process_ranges{ "ไทลื้อใหม่", 3498863, "abugida", aliases = {"New Tai Lue"}, ranges = { 0x1980, 0x19AB, 0x19B0, 0x19C9, 0x19D0, 0x19DA, 0x19DE, 0x19DF, }, spaces = false, } m["Taml"] = process_ranges{ "ทมิฬ", 26803, "abugida", aliases = {"Tamil"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0B82, 0x0B83, 0x0B85, 0x0B8A, 0x0B8E, 0x0B90, 0x0B92, 0x0B95, 0x0B99, 0x0B9A, 0x0B9C, 0x0B9C, 0x0B9E, 0x0B9F, 0x0BA3, 0x0BA4, 0x0BA8, 0x0BAA, 0x0BAE, 0x0BB9, 0x0BBE, 0x0BC2, 0x0BC6, 0x0BC8, 0x0BCA, 0x0BCD, 0x0BD0, 0x0BD0, 0x0BD7, 0x0BD7, 0x0BE6, 0x0BFA, 0x1CDA, 0x1CDA, 0xA8F3, 0xA8F3, 0x11301, 0x11301, 0x11303, 0x11303, 0x1133B, 0x1133C, 0x11FC0, 0x11FF1, 0x11FFF, 0x11FFF, }, normalizationFixes = handle_normalization_fixes{ from = {"அூ", "ஸ்ரீ"}, to = {"ஆ", "ஶ்ரீ"} }, } m["Tang"] = process_ranges{ "ตังกุต", 1373610, "logography, syllabary", aliases = {"Tangut"}, ranges = { 0x31EF, 0x31EF, 0x16FE0, 0x16FE0, 0x17000, 0x187F7, 0x18800, 0x18AFF, 0x18D00, 0x18D08, }, spaces = false, translit = "txg-translit", } m["Tavt"] = process_ranges{ "ไทเวียด", 11818517, "abugida", aliases = {"Tai Viet"}, ranges = { 0xAA80, 0xAAC2, 0xAADB, 0xAADF, }, spaces = false, } m["Tayo"] = process_ranges{ "ลายตัย", 16306701, "abugida", aliases = {"ญ้อ", "ไทญ้อ", "Lai Tay", "Tai Yo"}, direction = "vertical-rtl", ranges = { 0x1E6C0, 0x1E6DE, 0x1E6E0, 0x1E6F5, 0x1E6FE, 0x1E6FF, }, spaces = false, } m["Telu"] = process_ranges{ "เตลูกู", 570450, "abugida", aliases = {"Telugu"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0C00, 0x0C0C, 0x0C0E, 0x0C10, 0x0C12, 0x0C28, 0x0C2A, 0x0C39, 0x0C3C, 0x0C44, 0x0C46, 0x0C48, 0x0C4A, 0x0C4D, 0x0C55, 0x0C56, 0x0C58, 0x0C5A, 0x0C5D, 0x0C5D, 0x0C60, 0x0C63, 0x0C66, 0x0C6F, 0x0C77, 0x0C7F, 0x1CDA, 0x1CDA, 0x1CF2, 0x1CF2, }, normalizationFixes = handle_normalization_fixes{ from = {"ఒౌ", "ఒౕ", "ిౕ", "ెౕ", "ొౕ"}, to = {"ఔ", "ఓ", "ీ", "ే", "ో"} }, } m["Teng"] = { "เทงกวาร์", 473725, aliases = {"Tengwar"}, } m["Tfng"] = process_ranges{ "ทิฟินาค", 208503, "abjad, alphabet", aliases = {"Tifinagh"}, ranges = { 0x2D30, 0x2D67, 0x2D6F, 0x2D70, 0x2D7F, 0x2D7F, }, other_names = {"ลิบีโค-เบอร์เบอร์", "เบอร์เบอร์", "Libyco-Berber", "Berber"}, -- per Wikipedia, Libyco-Berber is the parent } m["Tglg"] = process_ranges{ "ไบบายิน", 812124, "abugida", aliases = {"ตากาล็อก", "Baybayin", "Tagalog"}, varieties = {"Kur-itan"}, ranges = { 0x1700, 0x1715, 0x171F, 0x171F, 0x1735, 0x1736, }, } m["Thaa"] = process_ranges{ "ทานะ", 877906, "abugida", aliases = {"Thaana"}, ranges = { 0x060C, 0x060C, 0x061B, 0x061C, 0x061F, 0x061F, 0x0660, 0x0669, 0x0780, 0x07B1, 0xFDF2, 0xFDF2, 0xFDFD, 0xFDFD, }, direction = "rtl", } m["Thai"] = process_ranges{ "ไทย", 236376, "abugida", aliases = {"Thai"}, ranges = { 0x0E01, 0x0E3A, 0x0E40, 0x0E5B, }, spaces = false, translit = false, --only for thwikt } do local Tibt_displaytext = { from = {"ༀ", "༌", "།།", "༚༚", "༚༝", "༝༚", "༝༝", "ཷ", "ཹ", "ེེ", "ོོ"}, to = {"ཨོཾ", "་", "༎", "༛", "༟", "࿎", "༞", "ྲཱྀ", "ླཱྀ", "ཻ", "ཽ"} } m["Tibt"] = process_ranges{ "ทิเบต", 46861, "abugida", aliases = {"Tibetan"}, ranges = { 0x0F00, 0x0F47, 0x0F49, 0x0F6C, 0x0F71, 0x0F97, 0x0F99, 0x0FBC, 0x0FBE, 0x0FCC, 0x0FCE, 0x0FD4, 0x0FD9, 0x0FDA, 0x3008, 0x300B, }, normalizationFixes = handle_normalization_fixes{ combiningClasses = {["༹"] = 1}, from = {"ཷ", "ཹ"}, to = {"ྲཱྀ", "ླཱྀ"} }, display_text = Tibt_displaytext, strip_diacritics = Tibt_displaytext, sort_key = "Tibt-sortkey", translit = "Tibt-translit", } m["sit-tam-Tibt"] = { "ตัมยิก", 109875213, m["Tibt"][3], -- There is no inheritance of properties currently implemented for scripts. Per [[User:Theknightwho]], this -- is because it's tricky to do since there are several types of child scripts: those that are mere display -- variants (like fa-Arab, kk-Arab), which should be eliminated in favor of CSS language selectors to -- handle the font differences; those that are genuinely different scripts that happen to share the same -- Unicode codepoints but have mostly different properties (e.g. Manchu vs. Mongolian); and those that are -- somewhere in between (like Tamyig vs. Tibetan). As a result, we currently have to manually specify -- which properties we want inherited as follows. aliases = {"Tamyig"}, ranges = m["Tibt"].ranges, characters = m["Tibt"].characters, parent = "Tibt", normalizationFixes = m["Tibt"].normalizationFixes, display_text = m["Tibt"].display_text, strip_diacritics = m["Tibt"].strip_diacritics, sort_key = m["Tibt"].sort_key, translit = m["Tibt"].translit, } end m["Tirh"] = process_ranges{ "ติรหุตา", 1765752, "abugida", aliases = {"Tirhuta"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x1CF2, 0x1CF2, 0xA830, 0xA839, 0x11480, 0x114C7, 0x114D0, 0x114D9, }, normalizationFixes = handle_normalization_fixes{ from = {"𑒁𑒰", "𑒋𑒺", "𑒍𑒺", "𑒪𑒵", "𑒪𑒶"}, to = {"𑒂", "𑒌", "𑒎", "𑒉", "𑒊"} }, } m["Tnsa"] = process_ranges{ "Tangsa", 105576311, "alphabet", ranges = { 0x16A70, 0x16ABE, 0x16AC0, 0x16AC9, }, } m["Todr"] = process_ranges{ "Todhri", 10274731, "alphabet", direction = "rtl", ranges = { 0x105C0, 0x105F3, }, } m["Tols"] = { "Tolong Siki", 4459822, "alphabet", -- Not in Unicode } m["Toto"] = process_ranges{ "Toto", 104837516, "abugida", ranges = { 0x1E290, 0x1E2AE, }, } m["Tutg"] = process_ranges{ "Tigalari", 2604990, "abugida", aliases = {"Tulu"}, ranges = { 0x1CF2, 0x1CF2, 0x1CF4, 0x1CF4, 0xA8F1, 0xA8F1, 0x11380, 0x11389, 0x1138B, 0x1138B, 0x1138E, 0x1138E, 0x11390, 0x113B5, 0x113B7, 0x113C0, 0x113C2, 0x113C2, 0x113C5, 0x113C5, 0x113C7, 0x113CA, 0x113CC, 0x113D5, 0x113D7, 0x113D8, 0x113E1, 0x113E2, }, } m["Ugar"] = process_ranges{ "ยูการิต", 332652, "abjad", aliases = {"ยูการิติก", "Ugaritic"}, ranges = { 0x10380, 0x1039D, 0x1039F, 0x1039F, }, } m["Vaii"] = process_ranges{ "ไว", 523078, "syllabary", aliases = {"Vai"}, ranges = { 0xA500, 0xA62B, }, } m["Visp"] = { "Visible Speech", 1303365, "alphabet", -- Not in Unicode } m["Vith"] = process_ranges{ "Vithkuqi", 3301993, "alphabet", ranges = { 0x10570, 0x1057A, 0x1057C, 0x1058A, 0x1058C, 0x10592, 0x10594, 0x10595, 0x10597, 0x105A1, 0x105A3, 0x105B1, 0x105B3, 0x105B9, 0x105BB, 0x105BC, }, capitalized = true, } m["Wara"] = process_ranges{ "วรังจิติ", 79199, aliases = {"วรังกษิติ", "Warang Citi", "Varang Kshiti"}, ranges = { 0x118A0, 0x118F2, 0x118FF, 0x118FF, }, capitalized = true, } m["Wcho"] = process_ranges{ "Wancho", 33713728, "alphabet", ranges = { 0x1E2C0, 0x1E2F9, 0x1E2FF, 0x1E2FF, }, } m["Wole"] = { "Woleai", 6643710, "syllabary", -- Not in Unicode } m["Xpeo"] = process_ranges{ "เปอร์เซียเก่า", 1471822, aliases = {"Old Persian"}, ranges = { 0x103A0, 0x103C3, 0x103C8, 0x103D5, }, } m["Xsux"] = process_ranges{ "คูนิฟอร์ม", 401, aliases = {"Cuneiform", "Sumero-Akkadian Cuneiform"}, ranges = { 0x12000, 0x12399, 0x12400, 0x1246E, 0x12470, 0x12474, 0x12480, 0x12543, }, } m["Yezi"] = process_ranges{ "Yezidi", 13175481, "alphabet", ranges = { 0x060C, 0x060C, 0x061B, 0x061B, 0x061F, 0x061F, 0x0660, 0x0669, 0x10E80, 0x10EA9, 0x10EAB, 0x10EAD, 0x10EB0, 0x10EB1, }, direction = "rtl", } m["Yiii"] = process_ranges{ "อี๋", 1197646, "syllabary", aliases = {"Yi"}, ranges = { 0x3001, 0x3002, 0x3008, 0x3011, 0x3014, 0x301B, 0x30FB, 0x30FB, 0xA000, 0xA48C, 0xA490, 0xA4C6, 0xFF61, 0xFF65, }, } m["Zanb"] = process_ranges{ "Zanabazar Square", 50809208, "abugida", ranges = { 0x11A00, 0x11A47, }, } m["Zmth"] = process_ranges{ "mathematical notation", 1140046, ranges = { 0x00AC, 0x00AC, 0x00B1, 0x00B1, 0x00D7, 0x00D7, 0x00F7, 0x00F7, 0x03D0, 0x03D2, 0x03D5, 0x03D5, 0x03F0, 0x03F1, 0x03F4, 0x03F6, 0x0606, 0x0608, 0x2016, 0x2016, 0x2032, 0x2034, 0x2040, 0x2040, 0x2044, 0x2044, 0x2052, 0x2052, 0x205F, 0x205F, 0x2061, 0x2064, 0x207A, 0x207E, 0x208A, 0x208E, 0x20D0, 0x20DC, 0x20E1, 0x20E1, 0x20E5, 0x20E6, 0x20EB, 0x20EF, 0x2102, 0x2102, 0x2107, 0x2107, 0x210A, 0x2113, 0x2115, 0x2115, 0x2118, 0x211D, 0x2124, 0x2124, 0x2128, 0x2129, 0x212C, 0x212D, 0x212F, 0x2131, 0x2133, 0x2138, 0x213C, 0x2149, 0x214B, 0x214B, 0x2190, 0x21A7, 0x21A9, 0x21AE, 0x21B0, 0x21B1, 0x21B6, 0x21B7, 0x21BC, 0x21DB, 0x21DD, 0x21DD, 0x21E4, 0x21E5, 0x21F4, 0x22FF, 0x2308, 0x230B, 0x2320, 0x2321, 0x237C, 0x237C, 0x239B, 0x23B5, 0x23B7, 0x23B7, 0x23D0, 0x23D0, 0x23DC, 0x23E2, 0x25A0, 0x25A1, 0x25AE, 0x25B7, 0x25BC, 0x25C1, 0x25C6, 0x25C7, 0x25CA, 0x25CB, 0x25CF, 0x25D3, 0x25E2, 0x25E2, 0x25E4, 0x25E4, 0x25E7, 0x25EC, 0x25F8, 0x25FF, 0x2605, 0x2606, 0x2640, 0x2640, 0x2642, 0x2642, 0x2660, 0x2663, 0x266D, 0x266F, 0x27C0, 0x27FF, 0x2900, 0x2AFF, 0x2B30, 0x2B44, 0x2B47, 0x2B4C, 0xFB29, 0xFB29, 0xFE61, 0xFE66, 0xFE68, 0xFE68, 0xFF0B, 0xFF0B, 0xFF1C, 0xFF1E, 0xFF3C, 0xFF3C, 0xFF3E, 0xFF3E, 0xFF5C, 0xFF5C, 0xFF5E, 0xFF5E, 0xFFE2, 0xFFE2, 0xFFE9, 0xFFEC, 0x1D400, 0x1D454, 0x1D456, 0x1D49C, 0x1D49E, 0x1D49F, 0x1D4A2, 0x1D4A2, 0x1D4A5, 0x1D4A6, 0x1D4A9, 0x1D4AC, 0x1D4AE, 0x1D4B9, 0x1D4BB, 0x1D4BB, 0x1D4BD, 0x1D4C3, 0x1D4C5, 0x1D505, 0x1D507, 0x1D50A, 0x1D50D, 0x1D514, 0x1D516, 0x1D51C, 0x1D51E, 0x1D539, 0x1D53B, 0x1D53E, 0x1D540, 0x1D544, 0x1D546, 0x1D546, 0x1D54A, 0x1D550, 0x1D552, 0x1D6A5, 0x1D6A8, 0x1D7CB, 0x1D7CE, 0x1D7FF, 0x1EE00, 0x1EE03, 0x1EE05, 0x1EE1F, 0x1EE21, 0x1EE22, 0x1EE24, 0x1EE24, 0x1EE27, 0x1EE27, 0x1EE29, 0x1EE32, 0x1EE34, 0x1EE37, 0x1EE39, 0x1EE39, 0x1EE3B, 0x1EE3B, 0x1EE42, 0x1EE42, 0x1EE47, 0x1EE47, 0x1EE49, 0x1EE49, 0x1EE4B, 0x1EE4B, 0x1EE4D, 0x1EE4F, 0x1EE51, 0x1EE52, 0x1EE54, 0x1EE54, 0x1EE57, 0x1EE57, 0x1EE59, 0x1EE59, 0x1EE5B, 0x1EE5B, 0x1EE5D, 0x1EE5D, 0x1EE5F, 0x1EE5F, 0x1EE61, 0x1EE62, 0x1EE64, 0x1EE64, 0x1EE67, 0x1EE6A, 0x1EE6C, 0x1EE72, 0x1EE74, 0x1EE77, 0x1EE79, 0x1EE7C, 0x1EE7E, 0x1EE7E, 0x1EE80, 0x1EE89, 0x1EE8B, 0x1EE9B, 0x1EEA1, 0x1EEA3, 0x1EEA5, 0x1EEA9, 0x1EEAB, 0x1EEBB, 0x1EEF0, 0x1EEF1, }, translit = false, } m["Zname"] = process_ranges{ "Znamenny musical notation", 965834, "pictography", ranges = { 0x1CF00, 0x1CF2D, 0x1CF30, 0x1CF46, 0x1CF50, 0x1CFC3, }, ietf_subtag = "Zsym", translit = false, } m["Zsym"] = process_ranges{ "สัญลักษณ์", 80071, "pictography", aliases = {"symbolic"}, ranges = { 0x20DD, 0x20E0, 0x20E2, 0x20E4, 0x20E7, 0x20EA, 0x20F0, 0x20F0, 0x2100, 0x2101, 0x2103, 0x2106, 0x2108, 0x2109, 0x2114, 0x2114, 0x2116, 0x2117, 0x211E, 0x2123, 0x2125, 0x2127, 0x212A, 0x212B, 0x212E, 0x212E, 0x2132, 0x2132, 0x2139, 0x213B, 0x214A, 0x214A, 0x214C, 0x214F, 0x21A8, 0x21A8, 0x21AF, 0x21AF, 0x21B2, 0x21B5, 0x21B8, 0x21BB, 0x21DC, 0x21DC, 0x21DE, 0x21E3, 0x21E6, 0x21F3, 0x2300, 0x2307, 0x230C, 0x231F, 0x2322, 0x237B, 0x237D, 0x239A, 0x23B6, 0x23B6, 0x23B8, 0x23CF, 0x23D1, 0x23DB, 0x23E3, 0x23FF, 0x2500, 0x259F, 0x25A2, 0x25AD, 0x25B8, 0x25BB, 0x25C2, 0x25C5, 0x25C8, 0x25C9, 0x25CC, 0x25CE, 0x25D4, 0x25E1, 0x25E3, 0x25E3, 0x25E5, 0x25E6, 0x25ED, 0x25F7, 0x2600, 0x2604, 0x2607, 0x263F, 0x2641, 0x2641, 0x2643, 0x265F, 0x2664, 0x266C, 0x2670, 0x27BF, 0x2B00, 0x2B2F, 0x2B45, 0x2B46, 0x2B4D, 0x2B73, 0x2B76, 0x2B95, 0x2B97, 0x2BFF, 0x4DC0, 0x4DFF, 0x1F000, 0x1F02B, 0x1F030, 0x1F093, 0x1F0A0, 0x1F0AE, 0x1F0B1, 0x1F0BF, 0x1F0C1, 0x1F0CF, 0x1F0D1, 0x1F0F5, 0x1F300, 0x1F6D7, 0x1F6DC, 0x1F6EC, 0x1F6F0, 0x1F6FC, 0x1F700, 0x1F776, 0x1F77B, 0x1F7D9, 0x1F7E0, 0x1F7EB, 0x1F7F0, 0x1F7F0, 0x1F800, 0x1F80B, 0x1F810, 0x1F847, 0x1F850, 0x1F859, 0x1F860, 0x1F887, 0x1F890, 0x1F8AD, 0x1F8B0, 0x1F8B1, 0x1F900, 0x1FA53, 0x1FA60, 0x1FA6D, 0x1FA70, 0x1FA7C, 0x1FA80, 0x1FA88, 0x1FA90, 0x1FABD, 0x1FABF, 0x1FAC5, 0x1FACE, 0x1FADB, 0x1FAE0, 0x1FAE8, 0x1FAF0, 0x1FAF8, 0x1FB00, 0x1FB92, 0x1FB94, 0x1FBCA, 0x1FBF0, 0x1FBF9, }, translit = false, character_category = false, -- none } m["Zyyy"] = { "ยังไม่กำหนด", 104839687, aliases = {"undetermined"}, -- This should not have any characters listed, probably translit = false, character_category = false, -- none } m["Zzzz"] = { "ยังไม่มีรหัส", 104839675, aliases = {"uncoded"}, -- This should not have any characters listed translit = false, character_category = false, -- none } -- These should be defined after the scripts they are composed of. m["Hrkt"] = process_ranges{ "คานะ", 187659, "syllabary", aliases = {"Kana", "Japanese syllabaries"}, ranges = union( m["Hira"].ranges, m["Kana"].ranges ), spaces = false, } m["Jpan"] = process_ranges{ "ญี่ปุ่น", 190502, "logography, syllabary", aliases = {"Japanese"}, ranges = union( m["Hrkt"].ranges, m["Hani"].ranges, m["Latn"].ranges ), spaces = false, sort_by_scraping = true, } m["Kore"] = process_ranges{ "เกาหลี", 711797, "logography, syllabary", aliases = {"Korean"}, ranges = union( m["Hang"].ranges, m["Hani"].ranges, m["Latn"].ranges ), -- `漢字(한자)`→`漢字` -- `가-나-다`→`가나다`, `가--나--다`→`가-나-다` -- `온돌(溫突/溫堗)`→`온돌` ([[ondol]]) strip_diacritics = { remove_diacritics = u(0x302E) .. u(0x302F), from = {"([" .. m["Hani"].characters .. "])%(.-%)", "^%-", "%-$", "%-(%-?)", "\1", "%([" .. m["Hani"].characters .. "/]+%)"}, to = {"%1", "\1", "\1", "%1", "-"} } } return require("Module:languages").finalizeData(m, "script") 6lrj3m4fxfjcg0b4zpm035jujz111qu 5775295 5775290 2026-10-14T10:31:42Z Octahedron80 267 5775295 Scribunto text/plain --[=[ When adding new scripts to this file, please don't forget to add style definitons for the script in [[MediaWiki:Gadget-LanguagesAndScripts.css]]. ]=] local concat = table.concat local insert = table.insert local ipairs = ipairs local next = next local remove = table.remove local select = select local sort = table.sort -- Loaded on demand, as it may not be needed (depending on the data). local function u(...) u = require("Module:string/char") return u(...) end -- We can't use mw.loadData() on [[Module:languages/chars]] because [[Module:languages/data]] itself is sometimes loaded -- using mw.loadData(), and calling mw.loadData() on [[Module:languages/chars]] will insert metatables into the -- character tables, which the second mw.loadData() will choke on. local m_chars = require("Module:languages/chars") local c = m_chars.chars local p = m_chars.puaChars local cs = m_chars.chars_substitutions ------------------------------------------------------------------------------------ -- -- Helper functions -- ------------------------------------------------------------------------------------ -- Note: a[2] > b[2] means opens are sorted before closes if otherwise equal. local function sort_ranges(a, b) return a[1] < b[1] or a[1] == b[1] and a[2] > b[2] end -- Returns the union of two or more range tables. local function union(...) local ranges = {} for i = 1, select("#", ...) do local argt = select(i, ...) for j, v in ipairs(argt) do insert(ranges, {v, j % 2 == 1 and 1 or -1}) end end sort(ranges, sort_ranges) local ret, i = {}, 0 for _, range in ipairs(ranges) do i = i + range[2] if i == 0 and range[2] == -1 then -- close insert(ret, range[1]) elseif i == 1 and range[2] == 1 then -- open if ret[#ret] and range[1] <= ret[#ret] + 1 then remove(ret) -- merge adjacent ranges else insert(ret, range[1]) end end end return ret end -- Adds the `characters` key, which is determined by a script's `ranges` table. local function process_ranges(sc) local ranges, chars = sc.ranges, {} for i = 2, #ranges, 2 do if ranges[i] == ranges[i - 1] then insert(chars, u(ranges[i])) else insert(chars, u(ranges[i - 1])) if ranges[i] > ranges[i - 1] + 1 then insert(chars, "-") end insert(chars, u(ranges[i])) end end sc.characters = concat(chars) ranges.n = #ranges return sc end local function handle_normalization_fixes(fixes) local combiningClasses = fixes.combiningClasses if combiningClasses then local chars, i = {}, 0 for char in next, combiningClasses do i = i + 1 chars[i] = char end fixes.combiningClassCharacters = concat(chars) end return fixes end ------------------------------------------------------------------------------------ -- -- Data -- ------------------------------------------------------------------------------------ local m = {} m["Adlm"] = process_ranges{ "อัดลัม", 19606346, "alphabet", aliases = {"Adlam"}, ranges = { 0x061F, 0x061F, 0x0640, 0x0640, 0x1E900, 0x1E94B, 0x1E950, 0x1E959, 0x1E95E, 0x1E95F, }, capitalized = true, direction = "rtl", } m["Afak"] = { "อาฟากา", 382019, "syllabary", aliases = {"Afaka"}, -- Not in Unicode } m["Aghb"] = process_ranges{ "Caucasian Albanian", 2495716, "alphabet", ranges = { 0x10530, 0x10563, 0x1056F, 0x1056F, }, } m["Ahom"] = process_ranges{ "อาหม", 2839633, "abugida", aliases = {"อะหม", "Ahom"}, ranges = { 0x11700, 0x1171A, 0x1171D, 0x1172B, 0x11730, 0x11746, }, } m["Arab"] = process_ranges{ "อาหรับ", 1828555, "abjad", -- more precisely, impure abjad aliases = {"อารบิก", "อาระบิก", "Arabic"}, varieties = {{"ยาวี", "Jawi"}, {"Nastaliq", "Nastaleeq"}}, ranges = { 0x0600, 0x06FF, 0x0750, 0x077F, 0x0870, 0x088E, 0x0890, 0x0891, 0x0897, 0x08E1, 0x08E3, 0x08FF, 0xFB50, 0xFBC2, 0xFBD3, 0xFD8F, 0xFD92, 0xFDC7, 0xFDCF, 0xFDCF, 0xFDF0, 0xFDFF, 0xFE70, 0xFE74, 0xFE76, 0xFEFC, 0x102E0, 0x102FB, 0x10E60, 0x10E7E, 0x10EC2, 0x10EC4, 0x10EFC, 0x10EFF, 0x1EE00, 0x1EE03, 0x1EE05, 0x1EE1F, 0x1EE21, 0x1EE22, 0x1EE24, 0x1EE24, 0x1EE27, 0x1EE27, 0x1EE29, 0x1EE32, 0x1EE34, 0x1EE37, 0x1EE39, 0x1EE39, 0x1EE3B, 0x1EE3B, 0x1EE42, 0x1EE42, 0x1EE47, 0x1EE47, 0x1EE49, 0x1EE49, 0x1EE4B, 0x1EE4B, 0x1EE4D, 0x1EE4F, 0x1EE51, 0x1EE52, 0x1EE54, 0x1EE54, 0x1EE57, 0x1EE57, 0x1EE59, 0x1EE59, 0x1EE5B, 0x1EE5B, 0x1EE5D, 0x1EE5D, 0x1EE5F, 0x1EE5F, 0x1EE61, 0x1EE62, 0x1EE64, 0x1EE64, 0x1EE67, 0x1EE6A, 0x1EE6C, 0x1EE72, 0x1EE74, 0x1EE77, 0x1EE79, 0x1EE7C, 0x1EE7E, 0x1EE7E, 0x1EE80, 0x1EE89, 0x1EE8B, 0x1EE9B, 0x1EEA1, 0x1EEA3, 0x1EEA5, 0x1EEA9, 0x1EEAB, 0x1EEBB, 0x1EEF0, 0x1EEF1, }, direction = "rtl", normalizationFixes = handle_normalization_fixes{ from = {"ٳ"}, to = {"اٟ"} }, } m["fa-Arab"] = { "อาหรับ", 744068, m["Arab"][3], aliases = m["Arab"].aliases, ranges = m["Arab"].ranges, characters = m["Arab"].characters, other_names = {"เปอร์โซ-อาหรับ", "Perso-Arabic"}, direction = "rtl", parent = "Arab", normalizationFixes = m["Arab"].normalizationFixes, } m["kk-Arab"] = { "อาหรับ", 90681452, m["Arab"][3], aliases = m["Arab"].aliases, ranges = m["Arab"].ranges, characters = m["Arab"].characters, direction = "rtl", parent = "Arab", normalizationFixes = m["Arab"].normalizationFixes, } m["ks-Arab"] = m["fa-Arab"] m["ku-Arab"] = m["fa-Arab"] m["ms-Arab"] = m["kk-Arab"] m["mzn-Arab"] = m["fa-Arab"] m["ota-Arab"] = m["fa-Arab"] m["pa-Arab"] = { "ศาหมุขี", 133800, m["Arab"][3], aliases = {"Shahmukhi"}, ranges = m["Arab"].ranges, characters = m["Arab"].characters, other_names = {"อารบิก", "อาระบิก", "Arabic"}, direction = "rtl", parent = "Arab", normalizationFixes = m["Arab"].normalizationFixes, } m["ps-Arab"] = m["fa-Arab"] m["sd-Arab"] = m["fa-Arab"] m["tt-Arab"] = m["fa-Arab"] m["ug-Arab"] = m["fa-Arab"] m["ur-Arab"] = m["fa-Arab"] -- Aran (Nastaliq) is subsumed into Arab m["Armi"] = process_ranges{ "แอราเมอิกจักรวรรดิ", 26978, "abjad", aliases = {"Imperial Aramaic"}, ranges = { 0x10840, 0x10855, 0x10857, 0x1085F, }, direction = "rtl", } m["Armn"] = process_ranges{ "อาร์มีเนีย", 11932, "alphabet", aliases = {"อาร์เมเนีย", "Armenian"}, ranges = { 0x0531, 0x0556, 0x0559, 0x058A, 0x058D, 0x058F, 0xFB13, 0xFB17, }, capitalized = true, translit = "Armn-translit", } m["Avst"] = process_ranges{ "อเวสตะ", 790681, "alphabet", aliases = {"Avestan"}, ranges = { 0x10B00, 0x10B35, 0x10B39, 0x10B3F, }, direction = "rtl", } m["pal-Avst"] = { "Pazend", 4925073, m["Avst"][3], aliases = m["Avst"].aliases, ranges = m["Avst"].ranges, characters = m["Avst"].characters, direction = "rtl", parent = "Avst", } m["Bali"] = process_ranges{ "บาหลี", 804984, "abugida", aliases = {"Balinese"}, ranges = { 0x1B00, 0x1B4C, 0x1B4E, 0x1B7F, }, } m["Bamu"] = process_ranges{ "บามุม", 806024, "syllabary", aliases = {"Bamum"}, ranges = { 0xA6A0, 0xA6F7, 0x16800, 0x16A38, }, } m["Bass"] = process_ranges{ "บัสซา", 810458, "alphabet", aliases = {"วาห์", "Bassa", "Bassa Vah", "Vah"}, ranges = { 0x16AD0, 0x16AED, 0x16AF0, 0x16AF5, }, } m["Batk"] = process_ranges{ "บาตัก", 51592, "abugida", aliases = {"Batak"}, ranges = { 0x1BC0, 0x1BF3, 0x1BFC, 0x1BFF, }, } m["Beng"] = process_ranges{ "เบงกอล", 756802, "abugida", aliases = {"เบงกาลี", "บังลา", "Bengali"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0980, 0x0983, 0x0985, 0x098C, 0x098F, 0x0990, 0x0993, 0x09A8, 0x09AA, 0x09B0, 0x09B2, 0x09B2, 0x09B6, 0x09B9, 0x09BC, 0x09C4, 0x09C7, 0x09C8, 0x09CB, 0x09CE, 0x09D7, 0x09D7, 0x09DC, 0x09DD, 0x09DF, 0x09E3, 0x09E6, 0x09EF, 0x09F2, 0x09FE, 0x1CD0, 0x1CD0, 0x1CD2, 0x1CD2, 0x1CD5, 0x1CD6, 0x1CD8, 0x1CD8, 0x1CE1, 0x1CE1, 0x1CEA, 0x1CEA, 0x1CED, 0x1CED, 0x1CF2, 0x1CF2, 0x1CF5, 0x1CF7, 0xA8F1, 0xA8F1, }, normalizationFixes = handle_normalization_fixes{ from = {"অা", "ঋৃ", "ঌৢ"}, to = {"আ", "ৠ", "ৡ"} }, } m["as-Beng"] = process_ranges{ "อัสสัม", 191272, m["Beng"][3], other_names = {"เบงกอล-อัสสัม", "เบงกาลี-อัสสัม", "นาครีตะวันออก", "Assamese", "Bengali-Assamese", "Eastern Nagari"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0980, 0x0983, 0x0985, 0x098C, 0x098F, 0x0990, 0x0993, 0x09A8, 0x09AA, 0x09AF, 0x09B2, 0x09B2, 0x09B6, 0x09B9, 0x09BC, 0x09C4, 0x09C7, 0x09C8, 0x09CB, 0x09CE, 0x09D7, 0x09D7, 0x09DC, 0x09DD, 0x09DF, 0x09E3, 0x09E6, 0x09FE, 0x1CD0, 0x1CD0, 0x1CD2, 0x1CD2, 0x1CD5, 0x1CD6, 0x1CD8, 0x1CD8, 0x1CE1, 0x1CE1, 0x1CEA, 0x1CEA, 0x1CED, 0x1CED, 0x1CF2, 0x1CF2, 0x1CF5, 0x1CF7, 0xA8F1, 0xA8F1, }, normalizationFixes = m["Beng"].normalizationFixes, } m["Bhks"] = process_ranges{ "ไภกษุกี", 17017839, "abugida", aliases = {"Bhaiksuki"}, ranges = { 0x11C00, 0x11C08, 0x11C0A, 0x11C36, 0x11C38, 0x11C45, 0x11C50, 0x11C6C, }, } m["Blis"] = { "Blissymbolic", 609817, "logography", aliases = {"Blissymbols"}, -- Not in Unicode } m["Bopo"] = process_ranges{ "จู้อิน", 198269, "semisyllabary", aliases = {"จู้อินฝูเฮ่า", "ปอพอมอฟอ", "ปัวพัวมัวฟัว", "Zhuyin", "Zhuyin Fuhao", "Bopomofo"}, ranges = { 0x02EA, 0x02EB, 0x3001, 0x3003, 0x3008, 0x3011, 0x3013, 0x301F, 0x302A, 0x302D, 0x3030, 0x3030, 0x3037, 0x3037, 0x30FB, 0x30FB, 0x3105, 0x312F, 0x31A0, 0x31BF, 0xFE45, 0xFE46, 0xFF61, 0xFF65, }, } m["Brah"] = process_ranges{ "พราหมี", 185083, "abugida", aliases = {"พราหฺมี", "Brahmi"}, ranges = { 0x11000, 0x1104D, 0x11052, 0x11075, 0x1107F, 0x1107F, }, normalizationFixes = handle_normalization_fixes{ from = {"𑀅𑀸", "𑀋𑀾", "𑀏𑁂"}, to = {"𑀆", "𑀌", "𑀐"} }, translit = "Brah-translit", } m["Brai"] = process_ranges{ "เบรลล์", 79894, "alphabet", aliases = {"Braille"}, ranges = { 0x2800, 0x28FF, }, } m["Bugi"] = process_ranges{ "ลนตารา", 1074947, "abugida", aliases = {"บูกิส", "Lontara", "Buginese"}, ranges = { 0x1A00, 0x1A1B, 0x1A1E, 0x1A1F, 0xA9CF, 0xA9CF, }, } m["Buhd"] = process_ranges{ "บูฮิด", 1002969, "abugida", aliases = {"Buhid"}, ranges = { 0x1735, 0x1736, 0x1740, 0x1751, 0x1752, 0x1753, }, } m["Cakm"] = process_ranges{ "จักมา", 1059328, "abugida", aliases = {"Chakma"}, ranges = { 0x09E6, 0x09EF, 0x1040, 0x1049, 0x11100, 0x11134, 0x11136, 0x11147, }, } m["Cans"] = process_ranges{ "Canadian syllabic", 2479183, "abugida", ranges = { 0x1400, 0x167F, 0x18B0, 0x18F5, 0x11AB0, 0x11ABF, }, } m["Cari"] = process_ranges{ "คาเรีย", 1094567, "alphabet", aliases = {"Carian"}, ranges = { 0x102A0, 0x102D0, }, } m["Cham"] = process_ranges{ "จาม", 1060381, "abugida", aliases = {"Cham"}, ranges = { 0xAA00, 0xAA36, 0xAA40, 0xAA4D, 0xAA50, 0xAA59, 0xAA5C, 0xAA5F, }, } m["Cher"] = process_ranges{ "เชโรกี", 26549, "syllabary", aliases = {"Cherokee"}, ranges = { 0x13A0, 0x13F5, 0x13F8, 0x13FD, 0xAB70, 0xABBF, }, } m["Chis"] = { "Chisoi", 123173777, "abugida", -- Not in Unicode } m["Chrs"] = process_ranges{ "ฆวารัซม์", 72386710, "abjad", aliases = {"โคแรสเมีย", "Khwarezmian", "Chorasmian"}, ranges = { 0x10FB0, 0x10FCB, }, direction = "rtl", } m["Copt"] = process_ranges{ "คอปติก", 321083, "alphabet", aliases = {"Coptic"}, ranges = { 0x03E2, 0x03EF, 0x2C80, 0x2CF3, 0x2CF9, 0x2CFF, 0x102E0, 0x102FB, }, capitalized = true, } m["Cpmn"] = process_ranges{ "Cypro-Minoan", 1751985, "syllabary", aliases = {"Cypro Minoan"}, ranges = { 0x10100, 0x10101, 0x12F90, 0x12FF2, }, } m["Cprt"] = process_ranges{ "ไซปรัส", 1757689, "syllabary", aliases = {"ไซเปรียท", "Cypriot"}, ranges = { 0x10100, 0x10102, 0x10107, 0x10133, 0x10137, 0x1013F, 0x10800, 0x10805, 0x10808, 0x10808, 0x1080A, 0x10835, 0x10837, 0x10838, 0x1083C, 0x1083C, 0x1083F, 0x1083F, }, direction = "rtl", } m["Cyrl"] = process_ranges{ "ซีริลลิก", 8209, "alphabet", aliases = {"Cyrillic"}, ranges = { 0x0400, 0x052F, 0x1C80, 0x1C8A, 0x1D2B, 0x1D2B, 0x1D78, 0x1D78, 0x1DF8, 0x1DF8, 0x2DE0, 0x2DFF, 0x2E43, 0x2E43, 0xA640, 0xA69F, 0xFE2E, 0xFE2F, 0x1E030, 0x1E06D, 0x1E08F, 0x1E08F, }, capitalized = true, } m["Cyrs"] = { "ซีริลลิกเก่า", 442244, m["Cyrl"][3], aliases = {"ซีริลลิกช่วงต้น", "Old Cyrillic", "Early Cyrillic"}, ranges = m["Cyrl"].ranges, characters = m["Cyrl"].characters, capitalized = m["Cyrl"].capitalized, wikipedia_article = "Early Cyrillic alphabet", normalizationFixes = handle_normalization_fixes{ from = {"Ѹ", "ѹ"}, to = {"Ꙋ", "ꙋ"} }, strip_diacritics = {remove_diacritics = cs.Cyrs_remove_diacritics}, sort_key = { remove_diacritics = cs.Cyrs_remove_diacritics, from = { "ї", "оу", -- 2 chars "[ґꙣєѕꙃꙅꙁіꙇђꙉѻꙩꙫꙭꙮꚙꚛꙋѡѿꙍѽꙑѣꙗѥꙕѧꙙѩꙝꙛѫѭѯѱѳѵҁ]" }, to = { "и" .. p[1], "у", { ["ґ"] = "г" .. p[1], ["ꙣ"] = "д" .. p[1], ["є"] = "е", ["ѕ"] = "ж" .. p[1], ["ꙃ"] = "ж" .. p[1], ["ꙅ"] = "ж" .. p[1], ["ꙁ"] = "з", ["і"] = "и" .. p[1], ["ꙇ"] = "и" .. p[1], ["ђ"] = "и" .. p[2], ["ꙉ"] = "и" .. p[2], ["ѻ"] = "о", ["ꙩ"] = "о", ["ꙫ"] = "о", ["ꙭ"] = "о", ["ꙮ"] = "о", ["ꚙ"] = "о", ["ꚛ"] = "о", ["ꙋ"] = "у", ["ѡ"] = "х" .. p[1], ["ѿ"] = "х" .. p[1], ["ꙍ"] = "х" .. p[1], ["ѽ"] = "х" .. p[1], ["ꙑ"] = "ы", ["ѣ"] = "ь" .. p[1], ["ꙗ"] = "ь" .. p[2], ["ѥ"] = "ь" .. p[3], ["ꙕ"] = "ю", ["ѧ"] = "я", ["ꙙ"] = "я", ["ѩ"] = "я" .. p[1], ["ꙝ"] = "я" .. p[1], ["ꙛ"] = "я" .. p[2], ["ѫ"] = "я" .. p[3], ["ѭ"] = "я" .. p[4], ["ѯ"] = "я" .. p[5], ["ѱ"] = "я" .. p[6], ["ѳ"] = "я" .. p[7], ["ѵ"] = "я" .. p[8], ["ҁ"] = "я" .. p[9], } }, } } m["Deva"] = process_ranges{ "เทวนาครี", 38592, "abugida", aliases = {"Devanagari"}, ranges = { 0x0900, 0x097F, 0x1CD0, 0x1CF6, 0x1CF8, 0x1CF9, 0x20F0, 0x20F0, 0xA830, 0xA839, 0xA8E0, 0xA8FF, 0x11B00, 0x11B09, }, normalizationFixes = handle_normalization_fixes{ from = {"ॆॆ", "ेे", "ाॅ", "ाॆ", "ाꣿ", "ॊॆ", "ाे", "ाै", "ोे", "ाऺ", "ॖॖ", "अॅ", "अॆ", "अा", "एॅ", "एॆ", "एे", "एꣿ", "ऎॆ", "अॉ", "आॅ", "अॊ", "आॆ", "अो", "आे", "अौ", "आै", "ओे", "अऺ", "अऻ", "आऺ", "अाꣿ", "आꣿ", "ऒॆ", "अॖ", "अॗ", "ॶॖ", "्‍?ा"}, to = {"ꣿ", "ै", "ॉ", "ॊ", "ॏ", "ॏ", "ो", "ौ", "ौ", "ऻ", "ॗ", "ॲ", "ऄ", "आ", "ऍ", "ऎ", "ऐ", "ꣾ", "ꣾ", "ऑ", "ऑ", "ऒ", "ऒ", "ओ", "ओ", "औ", "औ", "औ", "ॳ", "ॴ", "ॴ", "ॵ", "ॵ", "ॵ", "ॶ", "ॷ", "ॷ"} }, } m["Diak"] = process_ranges{ "ดิเวส อกุรุ", 3307073, "abugida", aliases = {"ดิเวหิ อกุรุ", "Dhives Akuru", "Dhivehi Akuru", "Dives Akuru", "Divehi Akuru"}, ranges = { 0x11900, 0x11906, 0x11909, 0x11909, 0x1190C, 0x11913, 0x11915, 0x11916, 0x11918, 0x11935, 0x11937, 0x11938, 0x1193B, 0x11946, 0x11950, 0x11959, }, } m["Dogr"] = process_ranges{ "โฑครี", 72402987, "abugida", aliases = {"Dogri", "Dogra"}, ranges = { 0x0964, 0x096F, 0xA830, 0xA839, 0x11800, 0x1183B, }, } m["Dsrt"] = process_ranges{ "Deseret", 1200582, "alphabet", ranges = { 0x10400, 0x1044F, }, capitalized = true, } m["Dupl"] = process_ranges{ "Duployan", 5316025, "alphabet", ranges = { 0x1BC00, 0x1BC6A, 0x1BC70, 0x1BC7C, 0x1BC80, 0x1BC88, 0x1BC90, 0x1BC99, 0x1BC9C, 0x1BCA3, }, } m["Egyd"] = { "Demotic", 188519, "abjad, logography", -- Not in Unicode } m["Egyh"] = { "Hieratic", 208111, "abjad, logography", -- Unified with Egyptian hieroglyphic in Unicode } m["Egyp"] = process_ranges{ "ไฮเออโรกลีฟอียิปต์", 132659, "abjad, logography", aliases = {"Egyptian hieroglyphic"}, ranges = { 0x13000, 0x13455, 0x13460, 0x143FA, }, varieties = {"Hieratic"}, wikipedia_article = "ไฮเออโรกลีฟอียิปต์", normalizationFixes = handle_normalization_fixes{ from = {"𓃁", "𓆖"}, to = {"𓃀𓐶𓂝", "𓆓𓐳𓐷𓏏𓐰𓇿𓐸"} }, } m["Elba"] = process_ranges{ "เอลบาซาน", 1036714, "alphabet", aliases = {"Elbasan"}, ranges = { 0x10500, 0x10527, }, } m["Elym"] = process_ranges{ "Elymaic", 60744423, "abjad", ranges = { 0x10FE0, 0x10FF6, }, direction = "rtl", } m["Ethi"] = process_ranges{ "เอธิโอเปีย", 257634, "abugida", aliases = {"กืออึซ", "Ethiopic", "Ge'ez"}, ranges = { 0x1200, 0x1248, 0x124A, 0x124D, 0x1250, 0x1256, 0x1258, 0x1258, 0x125A, 0x125D, 0x1260, 0x1288, 0x128A, 0x128D, 0x1290, 0x12B0, 0x12B2, 0x12B5, 0x12B8, 0x12BE, 0x12C0, 0x12C0, 0x12C2, 0x12C5, 0x12C8, 0x12D6, 0x12D8, 0x1310, 0x1312, 0x1315, 0x1318, 0x135A, 0x135D, 0x137C, 0x1380, 0x1399, 0x2D80, 0x2D96, 0x2DA0, 0x2DA6, 0x2DA8, 0x2DAE, 0x2DB0, 0x2DB6, 0x2DB8, 0x2DBE, 0x2DC0, 0x2DC6, 0x2DC8, 0x2DCE, 0x2DD0, 0x2DD6, 0x2DD8, 0x2DDE, 0xAB01, 0xAB06, 0xAB09, 0xAB0E, 0xAB11, 0xAB16, 0xAB20, 0xAB26, 0xAB28, 0xAB2E, 0x1E7E0, 0x1E7E6, 0x1E7E8, 0x1E7EB, 0x1E7ED, 0x1E7EE, 0x1E7F0, 0x1E7FE, }, sort_key = "Ethi-sortkey", } m["Gara"] = process_ranges{ "Garay", 3095302, "alphabet", capitalized = true, direction = "rtl", ranges = { 0x060C, 0x060C, 0x061B, 0x061B, 0x061F, 0x061F, 0x10D40, 0x10D65, 0x10D69, 0x10D85, 0x10D8E, 0x10D8F, }, } m["Geok"] = process_ranges{ "Khutsuri", 1090055, "alphabet", ranges = { -- Ⴀ-Ⴭ is Asomtavruli, ⴀ-ⴭ is Nuskhuri 0x10A0, 0x10C5, 0x10C7, 0x10C7, 0x10CD, 0x10CD, 0x10FB, 0x10FB, 0x2D00, 0x2D25, 0x2D27, 0x2D27, 0x2D2D, 0x2D2D, }, varieties = {"Nuskhuri", "Asomtavruli"}, capitalized = true, translit = "Geok-translit", } m["Geor"] = process_ranges{ "จอร์เจีย", 3317411, "alphabet", aliases = {"Georgian"}, ranges = { -- ა-ჿ is lowercase Mkhedruli; Ა-Ჿ is uppercase Mkhedruli (Mtavruli) 0x0589, 0x0589, 0x10D0, 0x10FF, 0x1C90, 0x1CBA, 0x1CBD, 0x1CBF, }, varieties = {"Mkhedruli", "Mtavruli"}, capitalized = true, translit = "Geor-translit", } m["Glag"] = process_ranges{ "กลาโกลิติก", 145625, "alphabet", aliases = {"Glagolitic"}, ranges = { 0x0484, 0x0484, 0x0487, 0x0487, 0x0589, 0x0589, 0x10FB, 0x10FB, 0x2C00, 0x2C5F, 0x2E43, 0x2E43, 0xA66F, 0xA66F, 0x1E000, 0x1E006, 0x1E008, 0x1E018, 0x1E01B, 0x1E021, 0x1E023, 0x1E024, 0x1E026, 0x1E02A, }, capitalized = true, } m["Gong"] = process_ranges{ "คุญชลาโคณฑี", 18125340, "abugida", aliases = {"Gunjala Gondi"}, ranges = { 0x0964, 0x0965, 0x11D60, 0x11D65, 0x11D67, 0x11D68, 0x11D6A, 0x11D8E, 0x11D90, 0x11D91, 0x11D93, 0x11D98, 0x11DA0, 0x11DA9, }, } m["Gonm"] = process_ranges{ "มสรามโคณฑี", 16977603, "abugida", aliases = {"Masaram Gondi"}, ranges = { 0x0964, 0x0965, 0x11D00, 0x11D06, 0x11D08, 0x11D09, 0x11D0B, 0x11D36, 0x11D3A, 0x11D3A, 0x11D3C, 0x11D3D, 0x11D3F, 0x11D47, 0x11D50, 0x11D59, }, } m["Goth"] = process_ranges{ "กอท", 467784, "alphabet", aliases = {"กอทิก", "กอทิค", "กอธิก", "กอธิค", "โกทิก", "โกทิค", "โกธิก", "โกธิค", "Gothic"}, ranges = { 0x10330, 0x1034A, }, wikipedia_article = "Gothic alphabet", } m["Gran"] = process_ranges{ "ครันถะ", 1119274, "abugida", aliases = {"Grantha"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0BE6, 0x0BF3, 0x1CD0, 0x1CD0, 0x1CD2, 0x1CD3, 0x1CF2, 0x1CF4, 0x1CF8, 0x1CF9, 0x20F0, 0x20F0, 0x11300, 0x11303, 0x11305, 0x1130C, 0x1130F, 0x11310, 0x11313, 0x11328, 0x1132A, 0x11330, 0x11332, 0x11333, 0x11335, 0x11339, 0x1133B, 0x11344, 0x11347, 0x11348, 0x1134B, 0x1134D, 0x11350, 0x11350, 0x11357, 0x11357, 0x1135D, 0x11363, 0x11366, 0x1136C, 0x11370, 0x11374, 0x11FD0, 0x11FD1, 0x11FD3, 0x11FD3, }, } m["Grek"] = process_ranges{ "กรีก", 8216, "alphabet", aliases = {"Greek"}, ranges = { 0x0341, 0x0341, 0x0374, 0x0375, 0x037E, 0x037E, 0x0384, 0x038A, 0x038C, 0x038C, 0x038E, 0x03A1, 0x03A3, 0x03D7, 0x03DA, 0x03DB, 0x03DE, 0x03E1, 0x03F0, 0x03F1, 0x03F4, 0x03F4, 0x03FC, 0x03FC, 0x1D26, 0x1D2A, 0x1D5D, 0x1D61, 0x1D66, 0x1D6A, 0x1DBF, 0x1DBF, 0x2126, 0x2127, 0x2129, 0x2129, 0x213C, 0x2140, 0xAB65, 0xAB65, 0x10140, 0x1018E, 0x101A0, 0x101A0, 0x1D200, 0x1D245, }, capitalized = true, display_text = cs["Grek-displaytext"], strip_diacritics = cs["Grek-stripdiacritics"], sort_key = { remove_diacritics = "'ʼ;·`¨´῀" .. c.grave .. c.acute .. c.diaer .. c.caron .. c.turnedcommaabove .. c.commaabove .. c.revcommaabove .. c.macron .. c.breve .. c.diaerbelow .. c.brevebelow .. c.perispomeni .. c.ypogegrammeni .. c.RSQuo .. c.prime .. c.keraia .. c.lowerkeraia .. c.tonos .. c.coronis .. c.psili .. c.dasia, from = {"ϝ", "ͷ", "ϛ", "ͱ", "ͺ", "ϳ", "ϻ", "[ϟϙ]", "[ςϲ]", "ͳ"}, to = {"ε" .. p[1], "ε" .. p[2], "ε" .. p[3], "ζ" .. p[1], "ι", "ι" .. p[1], "π" .. p[1], "π" .. p[2], "σ", "ϡ"}, }, } m["Polyt"] = process_ranges{ "กรีก", 1475332, m["Grek"][3], aliases = m["Grek"].aliases, ranges = union(m["Grek"].ranges, { 0x0340, 0x0340, 0x0342, 0x0345, 0x0370, 0x0373, 0x0376, 0x0377, 0x037A, 0x037D, 0x037F, 0x037F, 0x03D8, 0x03D9, 0x03DC, 0x03DD, 0x03F2, 0x03F3, 0x03F5, 0x03FB, 0x03FD, 0x03FF, 0x1F00, 0x1F15, 0x1F18, 0x1F1D, 0x1F20, 0x1F45, 0x1F48, 0x1F4D, 0x1F50, 0x1F57, 0x1F59, 0x1F59, 0x1F5B, 0x1F5B, 0x1F5D, 0x1F5D, 0x1F5F, 0x1F7D, 0x1F80, 0x1FB4, 0x1FB6, 0x1FC4, 0x1FC6, 0x1FD3, 0x1FD6, 0x1FDB, 0x1FDD, 0x1FEF, 0x1FF2, 0x1FF4, 0x1FF6, 0x1FFE, }), ietf_subtag = "Grek", capitalized = m["Grek"].capitalized, parent = "Grek", display_text = m["Grek"].display_text, strip_diacritics = "Polyt-stripdiacritics", sort_key = m["Grek"].sort_key, translit = "grc-translit", } m["Gujr"] = process_ranges{ "คุชราต", 733944, "abugida", aliases = {"คุชราตี", "Gujarati"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0A81, 0x0A83, 0x0A85, 0x0A8D, 0x0A8F, 0x0A91, 0x0A93, 0x0AA8, 0x0AAA, 0x0AB0, 0x0AB2, 0x0AB3, 0x0AB5, 0x0AB9, 0x0ABC, 0x0AC5, 0x0AC7, 0x0AC9, 0x0ACB, 0x0ACD, 0x0AD0, 0x0AD0, 0x0AE0, 0x0AE3, 0x0AE6, 0x0AF1, 0x0AF9, 0x0AFF, 0xA830, 0xA839, }, normalizationFixes = handle_normalization_fixes{ from = {"ઓ", "અાૈ", "અા", "અૅ", "અે", "અૈ", "અૉ", "અો", "અૌ", "આૅ", "આૈ", "ૅા"}, to = {"અાૅ", "ઔ", "આ", "ઍ", "એ", "ઐ", "ઑ", "ઓ", "ઔ", "ઓ", "ઔ", "ૉ"} }, } m["Gukh"] = process_ranges{ "Khema", 110064239, "abugida", aliases = {"Gurung Khema", "Khema Phri", "Khema Lipi"}, ranges = { 0x0965, 0x0965, 0x16100, 0x16139, }, } m["Guru"] = process_ranges{ "คุรมุขี", 689894, "abugida", aliases = {"กูร์มูคี", "เกอร์มุกห์", "Gurmukhi"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0A01, 0x0A03, 0x0A05, 0x0A0A, 0x0A0F, 0x0A10, 0x0A13, 0x0A28, 0x0A2A, 0x0A30, 0x0A32, 0x0A33, 0x0A35, 0x0A36, 0x0A38, 0x0A39, 0x0A3C, 0x0A3C, 0x0A3E, 0x0A42, 0x0A47, 0x0A48, 0x0A4B, 0x0A4D, 0x0A51, 0x0A51, 0x0A59, 0x0A5C, 0x0A5E, 0x0A5E, 0x0A66, 0x0A76, 0xA830, 0xA839, }, normalizationFixes = handle_normalization_fixes{ from = {"ਅਾ", "ਅੈ", "ਅੌ", "ੲਿ", "ੲੀ", "ੲੇ", "ੳੁ", "ੳੂ", "ੳੋ"}, to = {"ਆ", "ਐ", "ਔ", "ਇ", "ਈ", "ਏ", "ਉ", "ਊ", "ਓ"} }, } m["Hang"] = process_ranges{ "ฮันกึล", 8222, "syllabary", aliases = {"ฮังกึล", "Hangul", "Hangeul"}, ranges = { 0x1100, 0x11FF, 0x3001, 0x3003, 0x3008, 0x3011, 0x3013, 0x301F, 0x302E, 0x3030, 0x3037, 0x3037, 0x30FB, 0x30FB, 0x3131, 0x318E, 0x3200, 0x321E, 0x3260, 0x327E, 0xA960, 0xA97C, 0xAC00, 0xD7A3, 0xD7B0, 0xD7C6, 0xD7CB, 0xD7FB, 0xFE45, 0xFE46, 0xFF61, 0xFF65, 0xFFA0, 0xFFBE, 0xFFC2, 0xFFC7, 0xFFCA, 0xFFCF, 0xFFD2, 0xFFD7, 0xFFDA, 0xFFDC, }, } m["Hani"] = process_ranges{ "จีน", 8201, "logography", aliases = {"ฮั่น", "Han"}, ranges = { 0x2E80, 0x2E99, 0x2E9B, 0x2EF3, 0x2F00, 0x2FD5, 0x2FF0, 0x2FFF, 0x3001, 0x3003, 0x3005, 0x3011, 0x3013, 0x301F, 0x3021, 0x302D, 0x3030, 0x3030, 0x3037, 0x303F, 0x3190, 0x319F, 0x31C0, 0x31E5, 0x31EF, 0x31EF, 0x3220, 0x3247, 0x3280, 0x32B0, 0x32C0, 0x32CB, 0x30FB, 0x30FB, 0x32FF, 0x32FF, 0x3358, 0x3370, 0x337B, 0x337F, 0x33E0, 0x33FE, 0x3400, 0x4DBF, 0x4E00, 0x9FFF, 0xA700, 0xA707, 0xF900, 0xFA6D, 0xFA70, 0xFAD9, 0xFE45, 0xFE46, 0xFF61, 0xFF65, 0x16FE2, 0x16FE3, 0x16FF0, 0x16FF1, 0x1D360, 0x1D371, 0x1F250, 0x1F251, 0x20000, 0x2A6DF, 0x2A700, 0x2B739, 0x2B740, 0x2B81D, 0x2B820, 0x2CEA1, 0x2CEB0, 0x2EBE0, 0x2EBF0, 0x2EE5D, 0x2F800, 0x2FA1D, 0x30000, 0x3134A, 0x31350, 0x323AF, }, varieties = {{"ฮั่นจื่อ", "Hanzi"}, {"คันจิ", "Kanji"}, {"ฮันจา", "Hanja"}, {"จื๋อโนม", "Chu Nom"}}, spaces = false, } m["Hans"] = { "จีนตัวย่อ", 185614, m["Hani"][3], aliases = {"Simplified Han"}, ranges = m["Hani"].ranges, characters = m["Hani"].characters, spaces = m["Hani"].spaces, parent = "Hani", } m["Hant"] = { "จีนตัวเต็ม", 178528, m["Hani"][3], aliases = {"Traditional Han"}, ranges = m["Hani"].ranges, characters = m["Hani"].characters, spaces = m["Hani"].spaces, parent = "Hani", } m["Hano"] = process_ranges{ "ฮานูโนโอ", 1584045, "abugida", aliases = {"Hanunoo", "Hanunó'o", "Hanuno'o"}, ranges = { 0x1720, 0x1736, }, } m["Hatr"] = process_ranges{ "Hatran", 20813038, "abjad", ranges = { 0x108E0, 0x108F2, 0x108F4, 0x108F5, 0x108FB, 0x108FF, }, direction = "rtl", } m["Hebr"] = process_ranges{ "ฮีบรู", 33513, "abjad", -- more precisely, impure abjad aliases = {"Hebrew"}, ranges = { 0x0591, 0x05C7, 0x05D0, 0x05EA, 0x05EF, 0x05F4, 0x2135, 0x2138, 0xFB1D, 0xFB36, 0xFB38, 0xFB3C, 0xFB3E, 0xFB3E, 0xFB40, 0xFB41, 0xFB43, 0xFB44, 0xFB46, 0xFB4F, }, direction = "rtl", display_text = "Hebr-common", sort_key = "Hebr-common", strip_diacritics = "Hebr-common", } m["Hira"] = process_ranges{ "ฮิรางานะ", 48332, "syllabary", aliases = {"ฮิระงะนะ", "Hiragana"}, ranges = { 0x3001, 0x3003, 0x3008, 0x3011, 0x3013, 0x301F, 0x3030, 0x3035, 0x3037, 0x3037, 0x303C, 0x303D, 0x3041, 0x3096, 0x3099, 0x30A0, 0x30FB, 0x30FC, 0xFE45, 0xFE46, 0xFF61, 0xFF65, 0xFF70, 0xFF70, 0xFF9E, 0xFF9F, 0x1B001, 0x1B11F, 0x1B132, 0x1B132, 0x1B150, 0x1B152, 0x1F200, 0x1F200, }, varieties = {{"เฮ็นไตงานะ", "Hentaigana"}}, spaces = false, } m["Hluw"] = process_ranges{ "ไฮเออโรกลีฟอานาโตเลีย", 521323, "logography, syllabary", aliases = {"Anatolian Hieroglyphs"}, ranges = { 0x14400, 0x14646, }, wikipedia_article = "ไฮเออโรกลีฟอานาโตเลีย", } m["Hmng"] = process_ranges{ "Pahawh Hmong", 365954, "semisyllabary", aliases = {"ม้ง", "Hmong"}, ranges = { 0x16B00, 0x16B45, 0x16B50, 0x16B59, 0x16B5B, 0x16B61, 0x16B63, 0x16B77, 0x16B7D, 0x16B8F, }, } m["Hmnp"] = process_ranges{ "Nyiakeng Puachue Hmong", 33712499, "alphabet", ranges = { 0x1E100, 0x1E12C, 0x1E130, 0x1E13D, 0x1E140, 0x1E149, 0x1E14E, 0x1E14F, }, } m["Hung"] = process_ranges{ "ฮังการีเก่า", 446224, "alphabet", aliases = {"รูนฮังการี", "Old Hungarian", "Hungarian runic"}, ranges = { 0x10C80, 0x10CB2, 0x10CC0, 0x10CF2, 0x10CFA, 0x10CFF, }, capitalized = true, direction = "rtl", } m["Ibrnn"] = { "ไอบีเรียตะวันออกเฉียงเหนือ", 1113155, "semisyllabary", aliases = {"Northeastern Iberian"}, ietf_subtag = "Zzzz", -- Not in Unicode } m["Ibrns"] = { "ไอบีเรียตะวันออกเฉียงใต้", 2305351, "semisyllabary", aliases = {"Southeastern Iberian"}, ietf_subtag = "Zzzz", -- Not in Unicode } m["Image"] = { -- To be used to avoid any formatting or link processing "Image-rendered", 478798, -- This should not have any characters listed ietf_subtag = "Zyyy", translit = false, character_category = false, -- none } m["Inds"] = { "สินธุ", 601388, aliases = {"อินดัส", "Indus", "Harappan", "Indus Valley"}, } m["Ipach"] = { "สัทอักษรสากล", 21204, aliases = {"International Phonetic Alphabet", "IPA"}, ietf_subtag = "Latn", } m["Ital"] = process_ranges{ "อิตาลิกเก่า", 4891256, "alphabet", aliases = {"Old Italic"}, ranges = { 0x10300, 0x10323, 0x1032D, 0x1032F, }, translit = "Ital-translit", } m["Java"] = process_ranges{ "ชวา", 879704, "abugida", aliases = {"Javanese"}, ranges = { 0xA980, 0xA9CD, 0xA9CF, 0xA9D9, 0xA9DE, 0xA9DF, }, } m["Jurc"] = { "จูร์เชน", 912240, "logography", aliases = {"Jurchen"}, spaces = false, } m["Kali"] = process_ranges{ "กะยา", 4919239, "abugida", aliases = {"Kayah Li"}, ranges = { 0xA900, 0xA92F, }, } m["Kana"] = process_ranges{ "คาตากานะ", 82946, "syllabary", aliases = {"คะตะกะนะ", "Katakana"}, ranges = { 0x3001, 0x3003, 0x3008, 0x3011, 0x3013, 0x301F, 0x3030, 0x3035, 0x3037, 0x3037, 0x303C, 0x303D, 0x3099, 0x309C, 0x30A0, 0x30FF, 0x31F0, 0x31FF, 0x32D0, 0x32FE, 0x3300, 0x3357, 0xFE45, 0xFE46, 0xFF61, 0xFF9F, 0x1AFF0, 0x1AFF3, 0x1AFF5, 0x1AFFB, 0x1AFFD, 0x1AFFE, 0x1B000, 0x1B000, 0x1B120, 0x1B122, 0x1B155, 0x1B155, 0x1B164, 0x1B167, }, spaces = false, } m["Kawi"] = process_ranges{ "กวิ", 975802, "abugida", aliases = {"Kawi"}, ranges = { 0x11F00, 0x11F10, 0x11F12, 0x11F3A, 0x11F3E, 0x11F5A, }, } m["Khar"] = process_ranges{ "ขโรษฐี", 1161266, "abugida", aliases = {"Kharoshthi"}, ranges = { 0x10A00, 0x10A03, 0x10A05, 0x10A06, 0x10A0C, 0x10A13, 0x10A15, 0x10A17, 0x10A19, 0x10A35, 0x10A38, 0x10A3A, 0x10A3F, 0x10A48, 0x10A50, 0x10A58, }, direction = "rtl", } m["Khmr"] = process_ranges{ "เขมร", 1054190, "abugida", aliases = {"Khmer"}, ranges = { 0x1780, 0x17DD, 0x17E0, 0x17E9, 0x17F0, 0x17F9, 0x19E0, 0x19FF, }, spaces = false, normalizationFixes = handle_normalization_fixes{ from = {"ឣ", "ឤ"}, to = {"អ", "អា"} }, } m["Khoj"] = process_ranges{ "Khojki", 1740656, "abugida", ranges = { 0x0AE6, 0x0AEF, 0xA830, 0xA839, 0x11200, 0x11211, 0x11213, 0x11241, }, normalizationFixes = handle_normalization_fixes{ from = {"𑈀𑈬𑈱", "𑈀𑈬", "𑈀𑈱", "𑈀𑈳", "𑈁𑈱", "𑈆𑈬", "𑈬𑈰", "𑈬𑈱", "𑉀𑈮"}, to = {"𑈇", "𑈁", "𑈅", "𑈇", "𑈇", "𑈃", "𑈲", "𑈳", "𑈂"} }, } m["Khomt"] = { "ขอมไทย", 13023788, "abugida", aliases = {"Khom Thai"}, -- Not in Unicode } m["Kitl"] = { "คีตันใหญ่", 6401797, "logography", aliases = {"Khitan Large"}, spaces = false, } m["Kits"] = process_ranges{ "คีตันเล็ก", 6401800, "logography, syllabary", aliases = {"Khitan Small"}, ranges = { 0x16FE4, 0x16FE4, 0x18B00, 0x18CD5, 0x18CFF, 0x18CFF, }, spaces = false, } m["Knda"] = process_ranges{ "กันนฑะ", 839666, "abugida", aliases = {"กันนาดา", "Kannada"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0C80, 0x0C8C, 0x0C8E, 0x0C90, 0x0C92, 0x0CA8, 0x0CAA, 0x0CB3, 0x0CB5, 0x0CB9, 0x0CBC, 0x0CC4, 0x0CC6, 0x0CC8, 0x0CCA, 0x0CCD, 0x0CD5, 0x0CD6, 0x0CDD, 0x0CDE, 0x0CE0, 0x0CE3, 0x0CE6, 0x0CEF, 0x0CF1, 0x0CF3, 0x1CD0, 0x1CD0, 0x1CD2, 0x1CD3, 0x1CDA, 0x1CDA, 0x1CF2, 0x1CF2, 0x1CF4, 0x1CF4, 0xA830, 0xA835, }, normalizationFixes = handle_normalization_fixes{ from = {"ಉಾ", "ಋಾ", "ಒೌ"}, to = {"ಊ", "ೠ", "ಔ"} }, translit = "Knda-translit", } m["Kpel"] = { "Kpelle", 1586299, "syllabary", -- Not in Unicode } m["Krai"] = process_ranges{ "Kirat Rai", 123173834, "abugida", aliases = {"Rai", "Khambu Rai", "Rai Barṇamālā", "Kirat Khambu Rai"}, ranges = { 0x16D40, 0x16D79, }, } m["Kthi"] = process_ranges{ "ไกถี", 1253814, "abugida", aliases = {"Kaithi"}, ranges = { 0x0966, 0x096F, 0xA830, 0xA839, 0x11080, 0x110C2, 0x110CD, 0x110CD, }, } m["Kulit"] = { "Kulitan", 6443044, "abugida", -- Not in Unicode } m["Lana"] = process_ranges{ "ไทธรรม", 1314503, "abugida", aliases = {"ธรรม", "ตัวเมือง", "ล้านนา", "Tai Tham", "Tham", "Tua Mueang", "Lanna"}, ranges = { 0x1A20, 0x1A5E, 0x1A60, 0x1A7C, 0x1A7F, 0x1A89, 0x1A90, 0x1A99, 0x1AA0, 0x1AAD, }, spaces = false, } m["Laoo"] = process_ranges{ "ลาว", 1815229, "abugida", aliases = {"Lao"}, ranges = { 0x0E81, 0x0E82, 0x0E84, 0x0E84, 0x0E86, 0x0E8A, 0x0E8C, 0x0EA3, 0x0EA5, 0x0EA5, 0x0EA7, 0x0EBD, 0x0EC0, 0x0EC4, 0x0EC6, 0x0EC6, 0x0EC8, 0x0ECE, 0x0ED0, 0x0ED9, 0x0EDC, 0x0EDF, }, spaces = false, } m["Latn"] = process_ranges{ "ละติน", 8229, "alphabet", aliases = {"ลาติน", "โรมัน", "Latin", "Roman"}, ranges = { 0x0041, 0x005A, 0x0061, 0x007A, 0x00AA, 0x00AA, 0x00BA, 0x00BA, 0x00C0, 0x00D6, 0x00D8, 0x00F6, 0x00F8, 0x02B8, 0x02C0, 0x02C1, 0x02E0, 0x02E4, 0x0363, 0x036F, 0x0485, 0x0486, 0x0951, 0x0952, 0x10FB, 0x10FB, 0x1D00, 0x1D25, 0x1D2C, 0x1D5C, 0x1D62, 0x1D65, 0x1D6B, 0x1D77, 0x1D79, 0x1DBE, 0x1DF8, 0x1DF8, 0x1E00, 0x1EFF, 0x202F, 0x202F, 0x2071, 0x2071, 0x207F, 0x207F, 0x2090, 0x209C, 0x20F0, 0x20F0, 0x2100, 0x2125, 0x2128, 0x2128, 0x212A, 0x2134, 0x2139, 0x213B, 0x2141, 0x214E, 0x2160, 0x2188, 0x2C60, 0x2C7F, 0xA700, 0xA707, 0xA722, 0xA787, 0xA78B, 0xA7CD, 0xA7D0, 0xA7D1, 0xA7D3, 0xA7D3, 0xA7D5, 0xA7DC, 0xA7F2, 0xA7FF, 0xA92E, 0xA92E, 0xAB30, 0xAB5A, 0xAB5C, 0xAB64, 0xAB66, 0xAB69, 0xFB00, 0xFB06, 0xFF21, 0xFF3A, 0xFF41, 0xFF5A, 0x10780, 0x10785, 0x10787, 0x107B0, 0x107B2, 0x107BA, 0x1DF00, 0x1DF1E, 0x1DF25, 0x1DF2A, }, varieties = {{"รูมี", "Rumi"}, {"โรมาจิ", "โรมะจิ", "Romaji", "Rōmaji"}, "Romaja"}, capitalized = true, translit = false, } m["Latf"] = { "ฟรักทูร์", 148443, m["Latn"][3], aliases = {"Fraktur"}, ranges = m["Latn"].ranges, characters = m["Latn"].characters, other_names = {"แบล็กเลตเทอร์", "Blackletter"}, -- Blackletter is actually the parent "script" capitalized = m["Latn"].capitalized, translit = m["Latn"].translit, parent = "Latn", } m["Latg"] = { "แกลิก", 1432616, m["Latn"][3], aliases = {"Gaelic"}, ranges = m["Latn"].ranges, characters = m["Latn"].characters, other_names = {"ไอริช", "Irish"}, capitalized = m["Latn"].capitalized, translit = m["Latn"].translit, parent = "Latn", } m["pjt-Latn"] = { "ละติน", nil, m["Latn"][3], aliases = m["Latn"].aliases, ranges = m["Latn"].ranges, characters = m["Latn"].characters, capitalized = m["Latn"].capitalized, translit = m["Latn"].translit, parent = "Latn", } m["Leke"] = { "Leke", 19572613, "abugida", -- Not in Unicode } m["Lepc"] = process_ranges{ "เลปชา", 1481626, "abugida", aliases = {"Lepcha"}, ranges = { 0x1C00, 0x1C37, 0x1C3B, 0x1C49, 0x1C4D, 0x1C4F, }, } m["Limb"] = process_ranges{ "ลิมบู", 933796, "abugida", aliases = {"Limbu"}, ranges = { 0x0965, 0x0965, 0x1900, 0x191E, 0x1920, 0x192B, 0x1930, 0x193B, 0x1940, 0x1940, 0x1944, 0x194F, }, } m["Lina"] = process_ranges{ "ลิเนียร์เอ", 30972, aliases = {"Linear A"}, ranges = { 0x10107, 0x10133, 0x10600, 0x10736, 0x10740, 0x10755, 0x10760, 0x10767, }, } m["Linb"] = process_ranges{ "ลิเนียร์บี", 190102, aliases = {"Linear B"}, ranges = { 0x10000, 0x1000B, 0x1000D, 0x10026, 0x10028, 0x1003A, 0x1003C, 0x1003D, 0x1003F, 0x1004D, 0x10050, 0x1005D, 0x10080, 0x100FA, 0x10100, 0x10102, 0x10107, 0x10133, 0x10137, 0x1013F, }, } m["Lisu"] = process_ranges{ "ลีสู่", 1194621, "alphabet", aliases = {"ฟราเซอร์", "Lisu", "Fraser"}, ranges = { 0x300A, 0x300B, 0xA4D0, 0xA4FF, 0x11FB0, 0x11FB0, }, normalizationFixes = handle_normalization_fixes{ from = {"['’]", "[.ꓸ][.ꓸ]", "[.ꓸ][,ꓹ]"}, to = {"ʼ", "ꓺ", "ꓻ"} }, translit = "Lisu-translit", sort_key = { from = {"𑾰"}, to = {"ꓬ" .. p[1]} }, } m["Loma"] = { "Loma", 13023816, "syllabary", -- Not in Unicode } m["Lyci"] = process_ranges{ "ลิเชีย", 913587, "alphabet", aliases = {"Lycian"}, ranges = { 0x10280, 0x1029C, }, } m["Lydi"] = process_ranges{ "ลิเดีย", 4261300, "alphabet", aliases = {"Lydian"}, ranges = { 0x10920, 0x10939, 0x1093F, 0x1093F, }, direction = "rtl", } m["Mahj"] = process_ranges{ "มหาชนี", 6732850, "abugida", aliases = {"Mahajani"}, ranges = { 0x0964, 0x096F, 0xA830, 0xA839, 0x11150, 0x11176, }, } m["Maka"] = process_ranges{ "มากัซซาร์", 72947229, "abugida", aliases = {"มากาซาร์", "มากาซาร์เก่า", "Makasar", "Makassar", "Old Makasar"}, ranges = { 0x11EE0, 0x11EF8, }, } m["Mand"] = process_ranges{ "มันดาอิก", 1812130, aliases = {"Mandaic", "Mandaean"}, ranges = { 0x0640, 0x0640, 0x0840, 0x085B, 0x085E, 0x085E, }, direction = "rtl", } m["Mani"] = process_ranges{ "Manichaean", 3544702, "abjad", ranges = { 0x0640, 0x0640, 0x10AC0, 0x10AE6, 0x10AEB, 0x10AF6, }, direction = "rtl", translit = "Mani-translit", } m["Marc"] = process_ranges{ "Marchen", 72403709, "abugida", ranges = { 0x11C70, 0x11C8F, 0x11C92, 0x11CA7, 0x11CA9, 0x11CB6, }, } m["Maya"] = process_ranges{ "มายา", 211248, aliases = {"มายัน", "Maya", "Maya hieroglyphic", "Mayan", "Mayan hieroglyphic"}, ranges = { 0x1D2E0, 0x1D2F3, }, } m["Medf"] = process_ranges{ "Medefaidrin", 1519764, aliases = {"Oberi Okaime", "Oberi Ɔkaimɛ"}, ranges = { 0x16E40, 0x16E9A, }, capitalized = true, } m["Mend"] = process_ranges{ "Mende", 951069, aliases = {"Mende Kikakui"}, ranges = { 0x1E800, 0x1E8C4, 0x1E8C7, 0x1E8D6, }, direction = "rtl", } m["Merc"] = process_ranges{ "Meroitic cursive", 73028124, "abugida", ranges = { 0x109A0, 0x109B7, 0x109BC, 0x109CF, 0x109D2, 0x109FF, }, direction = "rtl", } m["Mero"] = process_ranges{ "Meroitic hieroglyphic", 73028623, "abugida", ranges = { 0x10980, 0x1099F, }, direction = "rtl", wikipedia_article = "Meroitic hieroglyphs", } m["Mlym"] = process_ranges{ "มลยาฬัม", 1164129, "abugida", aliases = {"มลยาลัม", "มาลายาลัม", "Malayalam"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0D00, 0x0D0C, 0x0D0E, 0x0D10, 0x0D12, 0x0D44, 0x0D46, 0x0D48, 0x0D4A, 0x0D4F, 0x0D54, 0x0D63, 0x0D66, 0x0D7F, 0x1CDA, 0x1CDA, 0x1CF2, 0x1CF2, 0xA830, 0xA832, }, normalizationFixes = handle_normalization_fixes{ from = {"ഇൗ", "ഉൗ", "എെ", "ഒാ", "ഒൗ", "ക്‍", "ണ്‍", "ന്‍റ", "ന്‍", "മ്‍", "യ്‍", "ര്‍", "ല്‍", "ള്‍", "ഴ്‍", "െെ", "ൻ്റ"}, to = {"ഈ", "ഊ", "ഐ", "ഓ", "ഔ", "ൿ", "ൺ", "ൻറ", "ൻ", "ൔ", "ൕ", "ർ", "ൽ", "ൾ", "ൖ", "ൈ", "ന്റ"} }, translit = "Mlym-translit", } m["Modi"] = process_ranges{ "โมฑี", 1703713, "abugida", aliases = {"Modi"}, ranges = { 0xA830, 0xA839, 0x11600, 0x11644, 0x11650, 0x11659, }, normalizationFixes = handle_normalization_fixes{ from = {"𑘀𑘹", "𑘀𑘺", "𑘁𑘹", "𑘁𑘺"}, to = {"𑘊", "𑘋", "𑘌", "𑘍"} }, } do local Mong_displaytext = { from = {"([ᠨ-ᡂᡸ])ᠶ([ᠨ-ᡂᡸ])", "([ᠠ-ᡂᡸ])ᠸ([^᠋ᠠ-ᠧ])", "([ᠠ-ᡂᡸ])ᠸ$"}, to = {"%1ᠢ%2", "%1ᠧ%2", "%1ᠧ"} } m["Mong"] = process_ranges{ "มองโกเลีย", 1055705, "alphabet", aliases = {"Mongolian", "Mongol bichig", "Hudum Mongol bichig"}, ranges = { 0x1800, 0x1805, 0x180A, 0x1819, 0x1820, 0x1842, 0x1878, 0x1878, 0x1880, 0x1897, 0x18A6, 0x18A6, 0x18A9, 0x18A9, 0x200C, 0x200D, 0x202F, 0x202F, 0x3001, 0x3002, 0x3008, 0x300B, 0x11660, 0x11668, }, direction = "vertical-ltr", display_text = Mong_displaytext, strip_diacritics = Mong_displaytext, translit = "Mong-translit", } m["mnc-Mong"] = process_ranges{ "แมนจู", 122888, m["Mong"][3], aliases = {"Manchu"}, ranges = { 0x1801, 0x1801, 0x1804, 0x1804, 0x1808, 0x180F, 0x1820, 0x1820, 0x1823, 0x1823, 0x1828, 0x182A, 0x182E, 0x1830, 0x1834, 0x1838, 0x183A, 0x183A, 0x185D, 0x185D, 0x185F, 0x1861, 0x1864, 0x1869, 0x186C, 0x1871, 0x1873, 0x1877, 0x1880, 0x1888, 0x188F, 0x188F, 0x189A, 0x18A5, 0x18A8, 0x18A8, 0x18AA, 0x18AA, 0x200C, 0x200D, 0x202F, 0x202F, }, direction = "vertical-ltr", parent = "Mong", translit = "mnc-translit", } m["sjo-Mong"] = process_ranges{ "ซิเบ", 113624153, m["Mong"][3], aliases = {"Xibe", "Sibe"}, ranges = { 0x1804, 0x1804, 0x1807, 0x1807, 0x180A, 0x180F, 0x1820, 0x1820, 0x1823, 0x1823, 0x1828, 0x1828, 0x182A, 0x182A, 0x182E, 0x1830, 0x1834, 0x1838, 0x183A, 0x183A, 0x185D, 0x1872, 0x200C, 0x200D, 0x202F, 0x202F, }, direction = "vertical-ltr", parent = "mnc-Mong", } m["xwo-Mong"] = process_ranges{ "Clear Script", 529085, m["Mong"][3], aliases = {"Todo", "Todo bichig"}, ranges = { 0x1800, 0x1801, 0x1804, 0x1806, 0x180A, 0x1820, 0x1828, 0x1828, 0x182F, 0x1831, 0x1834, 0x1834, 0x1837, 0x1838, 0x183A, 0x183B, 0x1840, 0x1840, 0x1843, 0x185C, 0x1880, 0x1887, 0x1889, 0x188F, 0x1894, 0x1894, 0x1896, 0x1899, 0x18A7, 0x18A7, 0x200C, 0x200D, 0x202F, 0x202F, 0x11669, 0x1166C, }, direction = "vertical-ltr", parent = "Mong", translit = "xwo-translit", } end m["Moon"] = { "Moon", 918391, "alphabet", aliases = {"Moon System of Embossed Reading", "Moon type", "Moon writing", "Moon alphabet", "Moon code"}, -- Not in Unicode } m["Morse"] = { "รหัสมอร์ส", 79897, aliases = {"Morse code"}, ietf_subtag = "Zsym", } m["Mroo"] = process_ranges{ "มรู", 75919253, aliases = {"มโร", "มรุง", "Mru", "Mro", "Mrung"}, ranges = { 0x16A40, 0x16A5E, 0x16A60, 0x16A69, 0x16A6E, 0x16A6F, }, } m["Mtei"] = process_ranges{ "มณีปุระ", 2981413, "abugida", aliases = {"ไมไตมะเยก", "มีไตมะเยก", "Manipuri", "Meitei Mayek", "Meetei Mayek"}, ranges = { 0xAAE0, 0xAAF6, 0xABC0, 0xABED, 0xABF0, 0xABF9, }, } m["Mult"] = process_ranges{ "Multani", 17047906, "abugida", ranges = { 0x0A66, 0x0A6F, 0x11280, 0x11286, 0x11288, 0x11288, 0x1128A, 0x1128D, 0x1128F, 0x1129D, 0x1129F, 0x112A9, }, } m["Music"] = process_ranges{ "สัญกรณ์ดนตรี", 233861, "pictography", aliases = {"Musical notation"}, ranges = { 0x2669, 0x266F, 0x1D100, 0x1D126, 0x1D129, 0x1D1EA, }, ietf_subtag = "Zsym", translit = false, } m["Mymr"] = process_ranges{ "พม่า", 43887939, "abugida", aliases = {"เมียนมา", "Burmese", "Myanmar"}, ranges = { 0x1000, 0x109F, 0xA92E, 0xA92E, 0xA9E0, 0xA9FE, 0xAA60, 0xAA7F, 0x116D0, 0x116E3, }, spaces = false, } m["Nagm"] = process_ranges{ "Mundari Bani", 106917274, "alphabet", aliases = {"Nag Mundari"}, ranges = { 0x1E4D0, 0x1E4F9, }, } m["Nand"] = process_ranges{ "นันทินาครี", 6963324, "abugida", aliases = {"Nandinagari"}, ranges = { 0x0964, 0x0965, 0x0CE6, 0x0CEF, 0x1CE9, 0x1CE9, 0x1CF2, 0x1CF2, 0x1CFA, 0x1CFA, 0xA830, 0xA835, 0x119A0, 0x119A7, 0x119AA, 0x119D7, 0x119DA, 0x119E4, }, } m["Narb"] = process_ranges{ "Ancient North Arabian", 1472213, "abjad", aliases = {"Old North Arabian"}, ranges = { 0x10A80, 0x10A9F, }, direction = "rtl", translit = "Narb-translit", } m["Nbat"] = process_ranges{ "Nabataean", 855624, "abjad", aliases = {"Nabatean"}, ranges = { 0x10880, 0x1089E, 0x108A7, 0x108AF, }, direction = "rtl", } m["Newa"] = process_ranges{ "Newa", 7237292, "abugida", aliases = {"Newar", "Newari", "Prachalit Nepal"}, ranges = { 0x11400, 0x1145B, 0x1145D, 0x11461, }, } m["Nkdb"] = { "Dongba", 1190953, "pictography", aliases = {"Naxi Dongba", "Nakhi Dongba", "Tomba", "Tompa", "Mo-so"}, spaces = false, -- Not in Unicode } m["Nkgb"] = { "Geba", 731189, "syllabary", aliases = {"Nakhi Geba", "Naxi Geba"}, spaces = false, -- Not in Unicode } m["Nkoo"] = process_ranges{ "N'Ko", 1062587, "alphabet", ranges = { 0x060C, 0x060C, 0x061B, 0x061B, 0x061F, 0x061F, 0x07C0, 0x07FA, 0x07FD, 0x07FF, 0xFD3E, 0xFD3F, }, direction = "rtl", } m["None"] = { "ไม่ระบุ", nil, aliases = {"unspecified"}, -- This should not have any characters listed ietf_subtag = "Zyyy", translit = false, character_category = false, -- none } m["Nshu"] = process_ranges{ "Nüshu", 56436, "syllabary", aliases = {"Nushu"}, ranges = { 0x16FE1, 0x16FE1, 0x1B170, 0x1B2FB, }, spaces = false, } m["Ogam"] = process_ranges{ "Ogham", 184661, ranges = { 0x1680, 0x169C, }, } m["Olck"] = process_ranges{ "อ็อลจิกิ", 201688, aliases = {"อ็อลเจะเม็ท", "อ็อล", "สันถาลี", "สันถัล", "Ol Chiki", "Ol Chemetʼ", "Ol", "Santali"}, ranges = { 0x1C50, 0x1C7F, }, } m["Onao"] = process_ranges{ "Ol Onal", 108607084, "alphabet", ranges = { 0x0964, 0x0965, 0x1E5D0, 0x1E5FA, 0x1E5FF, 0x1E5FF, }, } m["Orkh"] = process_ranges{ "Old Turkic", 5058305, aliases = {"Orkhon runic"}, ranges = { 0x10C00, 0x10C48, }, direction = "rtl", translit = "Orkh-translit", } m["Orya"] = process_ranges{ "โอริยา", 1760127, "abugida", aliases = {"โอเดีย", "Oriya", "Odia"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0B01, 0x0B03, 0x0B05, 0x0B0C, 0x0B0F, 0x0B10, 0x0B13, 0x0B28, 0x0B2A, 0x0B30, 0x0B32, 0x0B33, 0x0B35, 0x0B39, 0x0B3C, 0x0B44, 0x0B47, 0x0B48, 0x0B4B, 0x0B4D, 0x0B55, 0x0B57, 0x0B5C, 0x0B5D, 0x0B5F, 0x0B63, 0x0B66, 0x0B77, 0x1CDA, 0x1CDA, 0x1CF2, 0x1CF2, }, normalizationFixes = handle_normalization_fixes{ from = {"ଅା", "ଏୗ", "ଓୗ"}, to = {"ଆ", "ଐ", "ଔ"} }, } m["Osge"] = process_ranges{ "Osage", 7105529, ranges = { 0x104B0, 0x104D3, 0x104D8, 0x104FB, }, capitalized = true, } m["Osma"] = process_ranges{ "อุสมาน", 1377866, aliases = {"Osmanya"}, ranges = { 0x10480, 0x1049D, 0x104A0, 0x104A9, }, } m["Ougr"] = process_ranges{ "อุยกูร์เก่า", 1998938, "abjad, alphabet", aliases = {"Old Uyghur"}, ranges = { 0x0640, 0x0640, 0x10AF2, 0x10AF2, 0x10F70, 0x10F89, }, -- This should ideally be "vertical-ltr", but getting the CSS right is tricky because it's right-to-left horizontally, but left-to-right vertically. Currently, displaying it vertically causes it to display bottom-to-top. direction = "rtl", } m["Palm"] = process_ranges{ "Palmyrene", 17538100, ranges = { 0x10860, 0x1087F, }, direction = "rtl", } m["Pauc"] = process_ranges{ "Pau Cin Hau", 25339852, ranges = { 0x11AC0, 0x11AF8, }, } m["Pcun"] = { "Proto-Cuneiform", 1650699, "pictography", -- Not in Unicode } m["Pelm"] = { "Proto-Elamite", 56305763, "pictography", -- Not in Unicode } m["Perm"] = process_ranges{ "Old Permic", 147899, ranges = { 0x0483, 0x0483, 0x10350, 0x1037A, }, } m["Phag"] = process_ranges{ "Phags-pa", 822836, "abugida", ranges = { 0x1802, 0x1803, 0x1805, 0x1805, 0x200C, 0x200D, 0x202F, 0x202F, 0x3002, 0x3002, 0xA840, 0xA877, }, direction = "vertical-ltr", } m["Phli"] = process_ranges{ "ปาห์ลาวีจารึก", 24089793, "abjad", aliases = {"Inscriptional Pahlavi"}, ranges = { 0x10B60, 0x10B72, 0x10B78, 0x10B7F, }, direction = "rtl", } m["Phlp"] = process_ranges{ "ปาห์ลาวีเพลงสดุดี", 7253954, "abjad", aliases = {"Psalter Pahlavi"}, ranges = { 0x0640, 0x0640, 0x10B80, 0x10B91, 0x10B99, 0x10B9C, 0x10BA9, 0x10BAF, }, direction = "rtl", } m["Phlv"] = { "ปาห์ลาวีคัมภีร์", 72403118, "abjad", aliases = {"ปาห์ลาวีหนังสือ", "Book Pahlavi"}, direction = "rtl", wikipedia_article = "Pahlavi scripts#Book Pahlavi", -- Not in Unicode } m["Phnx"] = process_ranges{ "ฟินิเชีย", 26752, "abjad", aliases = {"Phoenician"}, ranges = { 0x10900, 0x1091B, 0x1091F, 0x1091F, }, direction = "rtl", translit = "Phnx-translit", } m["Plrd"] = process_ranges{ "Pollard", 601734, "abugida", aliases = {"Miao"}, ranges = { 0x16F00, 0x16F4A, 0x16F4F, 0x16F87, 0x16F8F, 0x16F9F, }, } m["Prti"] = process_ranges{ "พาร์เทียจารึก", 13023804, aliases = {"Inscriptional Parthian"}, ranges = { 0x10B40, 0x10B55, 0x10B58, 0x10B5F, }, direction = "rtl", } m["Psin"] = { "Proto-Sinaitic", 1065250, "abjad", direction = "rtl", -- Not in Unicode } m["Ranj"] = { "Ranjana", 2385276, "abugida", -- Not in Unicode } m["Rjng"] = process_ranges{ "Rejang", 2007960, "abugida", ranges = { 0xA930, 0xA953, 0xA95F, 0xA95F, }, } m["Rohg"] = process_ranges{ "ฮานิฟ", 21028705, "alphabet", aliases = {"ฮานีฟี", "Hanifi Rohingya", "Hanifi"}, ranges = { 0x060C, 0x060C, 0x061B, 0x061B, 0x061F, 0x061F, 0x0640, 0x0640, 0x06D4, 0x06D4, 0x10D00, 0x10D27, 0x10D30, 0x10D39, }, direction = "rtl", } m["Roro"] = { "Rongorongo", 209764, -- Not in Unicode } m["Rumin"] = process_ranges{ "Rumi numerals", nil, ranges = { 0x10E60, 0x10E7E, }, ietf_subtag = "Arab", } m["Runr"] = process_ranges{ "รูน", 82996, "alphabet", aliases = {"รูนิก", "Runic"}, ranges = { 0x16A0, 0x16EA, 0x16EE, 0x16F8, }, } do local Samr_stripdiacritics = { remove_diacritics = c.CGJ .. u(0x0816) .. "-" .. u(0x082D), } m["Samr"] = process_ranges{ "Samaritan", 1550930, "abjad", ranges = { 0x0800, 0x082D, 0x0830, 0x083E, }, direction = "rtl", strip_diacritics = Samr_stripdiacritics, sort_key = Samr_stripdiacritics, } end m["Sarb"] = process_ranges{ "Ancient South Arabian", 446074, "abjad", aliases = {"Old South Arabian"}, ranges = { 0x10A60, 0x10A7F, }, direction = "rtl", translit = "Sarb-translit", } m["Saur"] = process_ranges{ "Saurashtra", 3535165, "abugida", ranges = { 0xA880, 0xA8C5, 0xA8CE, 0xA8D9, }, } m["Semap"] = { "สัญญาณธง", 250796, "pictography", aliases = {"flag semaphore"}, ietf_subtag = "Zsym", } m["Sgnw"] = process_ranges{ "SignWriting", 1497335, "pictography", aliases = {"Sutton SignWriting"}, ranges = { 0x1D800, 0x1DA8B, 0x1DA9B, 0x1DA9F, 0x1DAA1, 0x1DAAF, }, translit = false, } m["Shaw"] = process_ranges{ "Shavian", 1970098, aliases = {"Shaw"}, ranges = { 0x10450, 0x1047F, }, } m["Shrd"] = process_ranges{ "ศารทา", 2047117, "abugida", aliases = {"Sharada"}, ranges = { 0x0951, 0x0951, 0x1CD7, 0x1CD7, 0x1CD9, 0x1CD9, 0x1CDC, 0x1CDD, 0x1CE0, 0x1CE0, 0xA830, 0xA835, 0xA838, 0xA838, 0x11180, 0x111DF, }, translit = "Shrd-translit", } m["Shui"] = { "Sui", 752854, "logography", spaces = false, -- Not in Unicode } m["Sidd"] = process_ranges{ "สิทธัม", 250379, "abugida", aliases = {"Siddham"}, ranges = { 0x11580, 0x115B5, 0x115B8, 0x115DD, }, translit = "Sidd-translit", } m["Sidt"] = { "Sidetic", 36659, "alphabet", direction = "rtl", -- Not in Unicode } m["Sind"] = process_ranges{ "ขุทาพาที", 6402810, "abugida", aliases = {"ขุทาวาที", "Khudabadi", "Khudawadi"}, ranges = { 0x0964, 0x0965, 0xA830, 0xA839, 0x112B0, 0x112EA, 0x112F0, 0x112F9, }, normalizationFixes = handle_normalization_fixes{ from = {"𑊰𑋠", "𑊰𑋥", "𑊰𑋦", "𑊰𑋧", "𑊰𑋨"}, to = {"𑊱", "𑊶", "𑊷", "𑊸", "𑊹"} }, } m["Sinh"] = process_ranges{ "สิงหล", 1574992, "abugida", aliases = {"Sinhalese", "Sinhala"}, ranges = { 0x0964, 0x0965, 0x0D81, 0x0D83, 0x0D85, 0x0D96, 0x0D9A, 0x0DB1, 0x0DB3, 0x0DBB, 0x0DBD, 0x0DBD, 0x0DC0, 0x0DC6, 0x0DCA, 0x0DCA, 0x0DCF, 0x0DD4, 0x0DD6, 0x0DD6, 0x0DD8, 0x0DDF, 0x0DE6, 0x0DEF, 0x0DF2, 0x0DF4, 0x1CF2, 0x1CF2, 0x111E1, 0x111F4, }, normalizationFixes = handle_normalization_fixes{ from = {"අා", "අැ", "අෑ", "උෟ", "ඍෘ", "ඏෟ", "එ්", "එෙ", "ඔෟ", "ෘෘ"}, to = {"ආ", "ඇ", "ඈ", "ඌ", "ඎ", "ඐ", "ඒ", "ඓ", "ඖ", "ෲ"} }, } m["Sogd"] = process_ranges{ "ซอกเดีย", 578359, "abjad", aliases = {"Sogdian"}, ranges = { 0x0640, 0x0640, 0x10F30, 0x10F59, }, direction = "rtl", } m["Sogo"] = process_ranges{ "ซอกเดียเก่า", 72403254, "abjad", aliases = {"Old Sogdian"}, ranges = { 0x10F00, 0x10F27, }, direction = "rtl", } m["Sora"] = process_ranges{ "Sorang Sompeng", 7563292, aliases = {"Sora Sompeng"}, ranges = { 0x110D0, 0x110E8, 0x110F0, 0x110F9, }, } m["Soyo"] = process_ranges{ "Soyombo", 8009382, "abugida", ranges = { 0x11A50, 0x11AA2, }, } m["Sund"] = process_ranges{ "ซุนดา", 51589, "abugida", aliases = {"Sundanese"}, ranges = { 0x1B80, 0x1BBF, 0x1CC0, 0x1CC7, }, } m["Sunu"] = process_ranges{ "Sunuwar", 109984965, "alphabet", ranges = { 0x11BC0, 0x11BE1, 0x11BF0, 0x11BF9, }, } m["Sylo"] = process_ranges{ "สิเลฏินาครี", 144128, "abugida", aliases = {"สิลอฏินาครี", "Syloti Nagri", "Sylheti Nagari"}, ranges = { 0x0964, 0x0965, 0x09E6, 0x09EF, 0xA800, 0xA82C, }, } m["Syrc"] = process_ranges{ "ซีรีแอก", 26567, "abjad", -- more precisely, impure abjad aliases = {"ซีเรียค", "Syriac"}, ranges = { 0x060C, 0x060C, 0x061B, 0x061C, 0x061F, 0x061F, 0x0640, 0x0640, 0x064B, 0x0655, 0x0670, 0x0670, 0x0700, 0x070D, 0x070F, 0x074A, 0x074D, 0x074F, 0x0860, 0x086A, 0x1DF8, 0x1DF8, 0x1DFA, 0x1DFA, }, direction = "rtl", } -- Syre, Syrj, Syrn are apparently subsumed into Syrc; discuss if this causes issues m["Tagb"] = process_ranges{ "ตักบันวา", 977444, "abugida", aliases = {"Tagbanwa"}, ranges = { 0x1735, 0x1736, 0x1760, 0x176C, 0x176E, 0x1770, 0x1772, 0x1773, }, } m["Takr"] = process_ranges{ "ฏากรี", 759202, "abugida", aliases = {"ตากรี", "Takri"}, ranges = { 0x0964, 0x0965, 0xA830, 0xA839, 0x11680, 0x116B9, 0x116C0, 0x116C9, }, normalizationFixes = handle_normalization_fixes{ from = {"𑚀𑚭", "𑚀𑚴", "𑚀𑚵", "𑚆𑚲"}, to = {"𑚁", "𑚈", "𑚉", "𑚇"} }, } m["Tale"] = process_ranges{ "ไทใต้คง", 2566326, "abugida", aliases = {"ไทเหนือ", "ไทเหนือใหม่", "ไตเหลอ", "Tai Nüa", "Tai Nuea", "New Tai Nüa", "New Tai Nuea", "Dehong Dai", "Tai Dehong", "Tai Le"}, ranges = { 0x1040, 0x1049, 0x1950, 0x196D, 0x1970, 0x1974, }, spaces = false, } m["Talu"] = process_ranges{ "ไทลื้อใหม่", 3498863, "abugida", aliases = {"New Tai Lue"}, ranges = { 0x1980, 0x19AB, 0x19B0, 0x19C9, 0x19D0, 0x19DA, 0x19DE, 0x19DF, }, spaces = false, } m["Taml"] = process_ranges{ "ทมิฬ", 26803, "abugida", aliases = {"Tamil"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0B82, 0x0B83, 0x0B85, 0x0B8A, 0x0B8E, 0x0B90, 0x0B92, 0x0B95, 0x0B99, 0x0B9A, 0x0B9C, 0x0B9C, 0x0B9E, 0x0B9F, 0x0BA3, 0x0BA4, 0x0BA8, 0x0BAA, 0x0BAE, 0x0BB9, 0x0BBE, 0x0BC2, 0x0BC6, 0x0BC8, 0x0BCA, 0x0BCD, 0x0BD0, 0x0BD0, 0x0BD7, 0x0BD7, 0x0BE6, 0x0BFA, 0x1CDA, 0x1CDA, 0xA8F3, 0xA8F3, 0x11301, 0x11301, 0x11303, 0x11303, 0x1133B, 0x1133C, 0x11FC0, 0x11FF1, 0x11FFF, 0x11FFF, }, normalizationFixes = handle_normalization_fixes{ from = {"அூ", "ஸ்ரீ"}, to = {"ஆ", "ஶ்ரீ"} }, } m["Tang"] = process_ranges{ "ตังกุต", 1373610, "logography, syllabary", aliases = {"Tangut"}, ranges = { 0x31EF, 0x31EF, 0x16FE0, 0x16FE0, 0x17000, 0x187F7, 0x18800, 0x18AFF, 0x18D00, 0x18D08, }, spaces = false, translit = "txg-translit", } m["Tavt"] = process_ranges{ "ไทเวียด", 11818517, "abugida", aliases = {"Tai Viet"}, ranges = { 0xAA80, 0xAAC2, 0xAADB, 0xAADF, }, spaces = false, } m["Tayo"] = process_ranges{ "ลายตัย", 16306701, "abugida", aliases = {"ญ้อ", "ไทญ้อ", "Lai Tay", "Tai Yo"}, direction = "vertical-rtl", ranges = { 0x1E6C0, 0x1E6DE, 0x1E6E0, 0x1E6F5, 0x1E6FE, 0x1E6FF, }, spaces = false, } m["Telu"] = process_ranges{ "เตลูกู", 570450, "abugida", aliases = {"Telugu"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x0C00, 0x0C0C, 0x0C0E, 0x0C10, 0x0C12, 0x0C28, 0x0C2A, 0x0C39, 0x0C3C, 0x0C44, 0x0C46, 0x0C48, 0x0C4A, 0x0C4D, 0x0C55, 0x0C56, 0x0C58, 0x0C5A, 0x0C5D, 0x0C5D, 0x0C60, 0x0C63, 0x0C66, 0x0C6F, 0x0C77, 0x0C7F, 0x1CDA, 0x1CDA, 0x1CF2, 0x1CF2, }, normalizationFixes = handle_normalization_fixes{ from = {"ఒౌ", "ఒౕ", "ిౕ", "ెౕ", "ొౕ"}, to = {"ఔ", "ఓ", "ీ", "ే", "ో"} }, } m["Teng"] = { "เทงกวาร์", 473725, aliases = {"Tengwar"}, } m["Tfng"] = process_ranges{ "ทิฟินาค", 208503, "abjad, alphabet", aliases = {"Tifinagh"}, ranges = { 0x2D30, 0x2D67, 0x2D6F, 0x2D70, 0x2D7F, 0x2D7F, }, other_names = {"ลิบีโค-เบอร์เบอร์", "เบอร์เบอร์", "Libyco-Berber", "Berber"}, -- per Wikipedia, Libyco-Berber is the parent } m["Tglg"] = process_ranges{ "ไบบายิน", 812124, "abugida", aliases = {"ตากาล็อก", "Baybayin", "Tagalog"}, varieties = {"Kur-itan"}, ranges = { 0x1700, 0x1715, 0x171F, 0x171F, 0x1735, 0x1736, }, } m["Thaa"] = process_ranges{ "ทานะ", 877906, "abugida", aliases = {"Thaana"}, ranges = { 0x060C, 0x060C, 0x061B, 0x061C, 0x061F, 0x061F, 0x0660, 0x0669, 0x0780, 0x07B1, 0xFDF2, 0xFDF2, 0xFDFD, 0xFDFD, }, direction = "rtl", } m["Thai"] = process_ranges{ "ไทย", 236376, "abugida", aliases = {"Thai"}, ranges = { 0x0E01, 0x0E3A, 0x0E40, 0x0E5B, }, spaces = false, translit = false, --only for thwikt } do local Tibt_displaytext = { from = {"ༀ", "༌", "།།", "༚༚", "༚༝", "༝༚", "༝༝", "ཷ", "ཹ", "ེེ", "ོོ"}, to = {"ཨོཾ", "་", "༎", "༛", "༟", "࿎", "༞", "ྲཱྀ", "ླཱྀ", "ཻ", "ཽ"} } m["Tibt"] = process_ranges{ "ทิเบต", 46861, "abugida", aliases = {"Tibetan"}, ranges = { 0x0F00, 0x0F47, 0x0F49, 0x0F6C, 0x0F71, 0x0F97, 0x0F99, 0x0FBC, 0x0FBE, 0x0FCC, 0x0FCE, 0x0FD4, 0x0FD9, 0x0FDA, 0x3008, 0x300B, }, normalizationFixes = handle_normalization_fixes{ combiningClasses = {["༹"] = 1}, from = {"ཷ", "ཹ"}, to = {"ྲཱྀ", "ླཱྀ"} }, display_text = Tibt_displaytext, strip_diacritics = Tibt_displaytext, sort_key = "Tibt-sortkey", translit = "Tibt-translit", } m["sit-tam-Tibt"] = { "ตัมยิก", 109875213, m["Tibt"][3], -- There is no inheritance of properties currently implemented for scripts. Per [[User:Theknightwho]], this -- is because it's tricky to do since there are several types of child scripts: those that are mere display -- variants (like fa-Arab, kk-Arab), which should be eliminated in favor of CSS language selectors to -- handle the font differences; those that are genuinely different scripts that happen to share the same -- Unicode codepoints but have mostly different properties (e.g. Manchu vs. Mongolian); and those that are -- somewhere in between (like Tamyig vs. Tibetan). As a result, we currently have to manually specify -- which properties we want inherited as follows. aliases = {"Tamyig"}, ranges = m["Tibt"].ranges, characters = m["Tibt"].characters, parent = "Tibt", normalizationFixes = m["Tibt"].normalizationFixes, display_text = m["Tibt"].display_text, strip_diacritics = m["Tibt"].strip_diacritics, sort_key = m["Tibt"].sort_key, translit = m["Tibt"].translit, } end m["Tirh"] = process_ranges{ "ติรหุตา", 1765752, "abugida", aliases = {"Tirhuta"}, ranges = { 0x0951, 0x0952, 0x0964, 0x0965, 0x1CF2, 0x1CF2, 0xA830, 0xA839, 0x11480, 0x114C7, 0x114D0, 0x114D9, }, normalizationFixes = handle_normalization_fixes{ from = {"𑒁𑒰", "𑒋𑒺", "𑒍𑒺", "𑒪𑒵", "𑒪𑒶"}, to = {"𑒂", "𑒌", "𑒎", "𑒉", "𑒊"} }, } m["Tnsa"] = process_ranges{ "Tangsa", 105576311, "alphabet", ranges = { 0x16A70, 0x16ABE, 0x16AC0, 0x16AC9, }, } m["Todr"] = process_ranges{ "Todhri", 10274731, "alphabet", direction = "rtl", ranges = { 0x105C0, 0x105F3, }, } m["Tols"] = { "Tolong Siki", 4459822, "alphabet", -- Not in Unicode } m["Toto"] = process_ranges{ "Toto", 104837516, "abugida", ranges = { 0x1E290, 0x1E2AE, }, } m["Tutg"] = process_ranges{ "Tigalari", 2604990, "abugida", aliases = {"Tulu"}, ranges = { 0x1CF2, 0x1CF2, 0x1CF4, 0x1CF4, 0xA8F1, 0xA8F1, 0x11380, 0x11389, 0x1138B, 0x1138B, 0x1138E, 0x1138E, 0x11390, 0x113B5, 0x113B7, 0x113C0, 0x113C2, 0x113C2, 0x113C5, 0x113C5, 0x113C7, 0x113CA, 0x113CC, 0x113D5, 0x113D7, 0x113D8, 0x113E1, 0x113E2, }, } m["Ugar"] = process_ranges{ "ยูการิต", 332652, "abjad", aliases = {"ยูการิติก", "Ugaritic"}, ranges = { 0x10380, 0x1039D, 0x1039F, 0x1039F, }, } m["Vaii"] = process_ranges{ "ไว", 523078, "syllabary", aliases = {"Vai"}, ranges = { 0xA500, 0xA62B, }, } m["Visp"] = { "Visible Speech", 1303365, "alphabet", -- Not in Unicode } m["Vith"] = process_ranges{ "Vithkuqi", 3301993, "alphabet", ranges = { 0x10570, 0x1057A, 0x1057C, 0x1058A, 0x1058C, 0x10592, 0x10594, 0x10595, 0x10597, 0x105A1, 0x105A3, 0x105B1, 0x105B3, 0x105B9, 0x105BB, 0x105BC, }, capitalized = true, } m["Wara"] = process_ranges{ "วรังจิติ", 79199, aliases = {"วรังกษิติ", "Warang Citi", "Varang Kshiti"}, ranges = { 0x118A0, 0x118F2, 0x118FF, 0x118FF, }, capitalized = true, } m["Wcho"] = process_ranges{ "Wancho", 33713728, "alphabet", ranges = { 0x1E2C0, 0x1E2F9, 0x1E2FF, 0x1E2FF, }, } m["Wole"] = { "Woleai", 6643710, "syllabary", -- Not in Unicode } m["Xpeo"] = process_ranges{ "เปอร์เซียเก่า", 1471822, aliases = {"Old Persian"}, ranges = { 0x103A0, 0x103C3, 0x103C8, 0x103D5, }, } m["Xsux"] = process_ranges{ "คูนิฟอร์ม", 401, aliases = {"Cuneiform", "Sumero-Akkadian Cuneiform"}, ranges = { 0x12000, 0x12399, 0x12400, 0x1246E, 0x12470, 0x12474, 0x12480, 0x12543, }, } m["Yezi"] = process_ranges{ "Yezidi", 13175481, "alphabet", ranges = { 0x060C, 0x060C, 0x061B, 0x061B, 0x061F, 0x061F, 0x0660, 0x0669, 0x10E80, 0x10EA9, 0x10EAB, 0x10EAD, 0x10EB0, 0x10EB1, }, direction = "rtl", } m["Yiii"] = process_ranges{ "อี๋", 1197646, "syllabary", aliases = {"Yi"}, ranges = { 0x3001, 0x3002, 0x3008, 0x3011, 0x3014, 0x301B, 0x30FB, 0x30FB, 0xA000, 0xA48C, 0xA490, 0xA4C6, 0xFF61, 0xFF65, }, } m["Zanb"] = process_ranges{ "Zanabazar Square", 50809208, "abugida", ranges = { 0x11A00, 0x11A47, }, } m["Zmth"] = process_ranges{ "mathematical notation", 1140046, ranges = { 0x00AC, 0x00AC, 0x00B1, 0x00B1, 0x00D7, 0x00D7, 0x00F7, 0x00F7, 0x03D0, 0x03D2, 0x03D5, 0x03D5, 0x03F0, 0x03F1, 0x03F4, 0x03F6, 0x0606, 0x0608, 0x2016, 0x2016, 0x2032, 0x2034, 0x2040, 0x2040, 0x2044, 0x2044, 0x2052, 0x2052, 0x205F, 0x205F, 0x2061, 0x2064, 0x207A, 0x207E, 0x208A, 0x208E, 0x20D0, 0x20DC, 0x20E1, 0x20E1, 0x20E5, 0x20E6, 0x20EB, 0x20EF, 0x2102, 0x2102, 0x2107, 0x2107, 0x210A, 0x2113, 0x2115, 0x2115, 0x2118, 0x211D, 0x2124, 0x2124, 0x2128, 0x2129, 0x212C, 0x212D, 0x212F, 0x2131, 0x2133, 0x2138, 0x213C, 0x2149, 0x214B, 0x214B, 0x2190, 0x21A7, 0x21A9, 0x21AE, 0x21B0, 0x21B1, 0x21B6, 0x21B7, 0x21BC, 0x21DB, 0x21DD, 0x21DD, 0x21E4, 0x21E5, 0x21F4, 0x22FF, 0x2308, 0x230B, 0x2320, 0x2321, 0x237C, 0x237C, 0x239B, 0x23B5, 0x23B7, 0x23B7, 0x23D0, 0x23D0, 0x23DC, 0x23E2, 0x25A0, 0x25A1, 0x25AE, 0x25B7, 0x25BC, 0x25C1, 0x25C6, 0x25C7, 0x25CA, 0x25CB, 0x25CF, 0x25D3, 0x25E2, 0x25E2, 0x25E4, 0x25E4, 0x25E7, 0x25EC, 0x25F8, 0x25FF, 0x2605, 0x2606, 0x2640, 0x2640, 0x2642, 0x2642, 0x2660, 0x2663, 0x266D, 0x266F, 0x27C0, 0x27FF, 0x2900, 0x2AFF, 0x2B30, 0x2B44, 0x2B47, 0x2B4C, 0xFB29, 0xFB29, 0xFE61, 0xFE66, 0xFE68, 0xFE68, 0xFF0B, 0xFF0B, 0xFF1C, 0xFF1E, 0xFF3C, 0xFF3C, 0xFF3E, 0xFF3E, 0xFF5C, 0xFF5C, 0xFF5E, 0xFF5E, 0xFFE2, 0xFFE2, 0xFFE9, 0xFFEC, 0x1D400, 0x1D454, 0x1D456, 0x1D49C, 0x1D49E, 0x1D49F, 0x1D4A2, 0x1D4A2, 0x1D4A5, 0x1D4A6, 0x1D4A9, 0x1D4AC, 0x1D4AE, 0x1D4B9, 0x1D4BB, 0x1D4BB, 0x1D4BD, 0x1D4C3, 0x1D4C5, 0x1D505, 0x1D507, 0x1D50A, 0x1D50D, 0x1D514, 0x1D516, 0x1D51C, 0x1D51E, 0x1D539, 0x1D53B, 0x1D53E, 0x1D540, 0x1D544, 0x1D546, 0x1D546, 0x1D54A, 0x1D550, 0x1D552, 0x1D6A5, 0x1D6A8, 0x1D7CB, 0x1D7CE, 0x1D7FF, 0x1EE00, 0x1EE03, 0x1EE05, 0x1EE1F, 0x1EE21, 0x1EE22, 0x1EE24, 0x1EE24, 0x1EE27, 0x1EE27, 0x1EE29, 0x1EE32, 0x1EE34, 0x1EE37, 0x1EE39, 0x1EE39, 0x1EE3B, 0x1EE3B, 0x1EE42, 0x1EE42, 0x1EE47, 0x1EE47, 0x1EE49, 0x1EE49, 0x1EE4B, 0x1EE4B, 0x1EE4D, 0x1EE4F, 0x1EE51, 0x1EE52, 0x1EE54, 0x1EE54, 0x1EE57, 0x1EE57, 0x1EE59, 0x1EE59, 0x1EE5B, 0x1EE5B, 0x1EE5D, 0x1EE5D, 0x1EE5F, 0x1EE5F, 0x1EE61, 0x1EE62, 0x1EE64, 0x1EE64, 0x1EE67, 0x1EE6A, 0x1EE6C, 0x1EE72, 0x1EE74, 0x1EE77, 0x1EE79, 0x1EE7C, 0x1EE7E, 0x1EE7E, 0x1EE80, 0x1EE89, 0x1EE8B, 0x1EE9B, 0x1EEA1, 0x1EEA3, 0x1EEA5, 0x1EEA9, 0x1EEAB, 0x1EEBB, 0x1EEF0, 0x1EEF1, }, translit = false, } m["Zname"] = process_ranges{ "Znamenny musical notation", 965834, "pictography", ranges = { 0x1CF00, 0x1CF2D, 0x1CF30, 0x1CF46, 0x1CF50, 0x1CFC3, }, ietf_subtag = "Zsym", translit = false, } m["Zsym"] = process_ranges{ "สัญลักษณ์", 80071, "pictography", aliases = {"symbolic"}, ranges = { 0x20DD, 0x20E0, 0x20E2, 0x20E4, 0x20E7, 0x20EA, 0x20F0, 0x20F0, 0x2100, 0x2101, 0x2103, 0x2106, 0x2108, 0x2109, 0x2114, 0x2114, 0x2116, 0x2117, 0x211E, 0x2123, 0x2125, 0x2127, 0x212A, 0x212B, 0x212E, 0x212E, 0x2132, 0x2132, 0x2139, 0x213B, 0x214A, 0x214A, 0x214C, 0x214F, 0x21A8, 0x21A8, 0x21AF, 0x21AF, 0x21B2, 0x21B5, 0x21B8, 0x21BB, 0x21DC, 0x21DC, 0x21DE, 0x21E3, 0x21E6, 0x21F3, 0x2300, 0x2307, 0x230C, 0x231F, 0x2322, 0x237B, 0x237D, 0x239A, 0x23B6, 0x23B6, 0x23B8, 0x23CF, 0x23D1, 0x23DB, 0x23E3, 0x23FF, 0x2500, 0x259F, 0x25A2, 0x25AD, 0x25B8, 0x25BB, 0x25C2, 0x25C5, 0x25C8, 0x25C9, 0x25CC, 0x25CE, 0x25D4, 0x25E1, 0x25E3, 0x25E3, 0x25E5, 0x25E6, 0x25ED, 0x25F7, 0x2600, 0x2604, 0x2607, 0x263F, 0x2641, 0x2641, 0x2643, 0x265F, 0x2664, 0x266C, 0x2670, 0x27BF, 0x2B00, 0x2B2F, 0x2B45, 0x2B46, 0x2B4D, 0x2B73, 0x2B76, 0x2B95, 0x2B97, 0x2BFF, 0x4DC0, 0x4DFF, 0x1F000, 0x1F02B, 0x1F030, 0x1F093, 0x1F0A0, 0x1F0AE, 0x1F0B1, 0x1F0BF, 0x1F0C1, 0x1F0CF, 0x1F0D1, 0x1F0F5, 0x1F300, 0x1F6D7, 0x1F6DC, 0x1F6EC, 0x1F6F0, 0x1F6FC, 0x1F700, 0x1F776, 0x1F77B, 0x1F7D9, 0x1F7E0, 0x1F7EB, 0x1F7F0, 0x1F7F0, 0x1F800, 0x1F80B, 0x1F810, 0x1F847, 0x1F850, 0x1F859, 0x1F860, 0x1F887, 0x1F890, 0x1F8AD, 0x1F8B0, 0x1F8B1, 0x1F900, 0x1FA53, 0x1FA60, 0x1FA6D, 0x1FA70, 0x1FA7C, 0x1FA80, 0x1FA88, 0x1FA90, 0x1FABD, 0x1FABF, 0x1FAC5, 0x1FACE, 0x1FADB, 0x1FAE0, 0x1FAE8, 0x1FAF0, 0x1FAF8, 0x1FB00, 0x1FB92, 0x1FB94, 0x1FBCA, 0x1FBF0, 0x1FBF9, }, translit = false, character_category = false, -- none } m["Zyyy"] = { "ยังไม่กำหนด", 104839687, aliases = {"undetermined"}, -- This should not have any characters listed, probably translit = false, character_category = false, -- none } m["Zzzz"] = { "ยังไม่มีรหัส", 104839675, aliases = {"uncoded"}, -- This should not have any characters listed translit = false, character_category = false, -- none } -- These should be defined after the scripts they are composed of. m["Hrkt"] = process_ranges{ "คานะ", 187659, "syllabary", aliases = {"Kana", "Japanese syllabaries"}, ranges = union( m["Hira"].ranges, m["Kana"].ranges ), spaces = false, } m["Jpan"] = process_ranges{ "ญี่ปุ่น", 190502, "logography, syllabary", aliases = {"Japanese"}, ranges = union( m["Hrkt"].ranges, m["Hani"].ranges, m["Latn"].ranges ), spaces = false, sort_by_scraping = true, } m["Kore"] = process_ranges{ "เกาหลี", 711797, "logography, syllabary", aliases = {"Korean"}, ranges = union( m["Hang"].ranges, m["Hani"].ranges, m["Latn"].ranges ), -- `漢字(한자)`→`漢字` -- `가-나-다`→`가나다`, `가--나--다`→`가-나-다` -- `온돌(溫突/溫堗)`→`온돌` ([[ondol]]) strip_diacritics = { remove_diacritics = u(0x302E) .. u(0x302F), from = {"([" .. m["Hani"].characters .. "])%(.-%)", "^%-", "%-$", "%-(%-?)", "\1", "%([" .. m["Hani"].characters .. "/]+%)"}, to = {"%1", "\1", "\1", "%1", "-"} } } return require("Module:languages").finalizeData(m, "script") 9io8pgm7fkrm2293ob1qve96tnq19bq มอดูล:languages/data/3/x 828 36363 5775298 5715034 2026-10-14T10:33:16Z Octahedron80 267 5775298 Scribunto text/plain local m_langdata = require("Module:languages/data") -- Loaded on demand, as it may not be needed (depending on the data). local function u(...) u = require("Module:string utilities").char return u(...) end local c = m_langdata.chars local p = m_langdata.puaChars local s = m_langdata.shared local m = {} m["xaa"] = { "อาหรับแบบอันดะลุส", 1137945, "sem-arb", "Arab, Latn", strip_diacritics = { Arab = { remove_diacritics = c.kashida .. c.fathatan .. c.dammatan .. c.kasratan .. c.fatha .. c.damma .. c.kasra .. c.shadda .. c.sukun .. c.superalef, from = {u(0x0671)}, to = {u(0x0627)} }, }, } m["xab"] = { "Sambe", 36265, "nic-alu", "Latn", } m["xac"] = { "Kachari", 3442442, "tbq-bdg", } m["xad"] = { "Adai", 346744, nil, "Latn", } m["xae"] = { "Aequian", 930579, "itc", "Ital, Latn", -- Ital translit in [[Module:scripts/data]] display_text = { Latn = s["itc-Latn-displaytext"] }, strip_diacritics = { Latn = s["itc-Latn-stripdiacritics"] }, sort_key = { Latn = s["itc-Latn-sortkey"] }, } m["xag"] = { "Aghwan", 34931, "cau-esm", "Aghb", translit = "Aghb-translit", override_translit = true, } m["xai"] = { "Kaimbé", 6348017, } m["xaj"] = { "Ararandewára", 63339949, "tup-gua", "Latn", } m["xak"] = { "Maku", 2032882, nil, "Latn", } m["xal"] = { "คัลมึค", 33634, "xgn-cen", "Cyrl, xwo-Mong", ancestors = "xwo", translit = { Cyrl = "xal-translit", -- xwo-Mong translit in [[Module:scripts/data]]. NOTE: Formerly used xal-translit for transliterating xwo-Mong -- but that only handles Cyrillic; it has code to transliterate xwo-Mong but it's broken so I've replaced it -- with the default xwo-translit. }, override_translit = true, sort_key = { Cyrl = "xal-sortkey", }, } m["xam"] = { "ǀXam", 2086145, "khi-tuu", "Latn", } m["xan"] = { "Xamtanga", 56527, "cus-cen", "Ethi", } m["xao"] = { "Khao", 3196077, "mkh-pal", } m["xap"] = { "Apalachee", 686501, "nai-mus", "Latn", } m["xaq"] = { "Aquitanian", 500522, "euq", "Latn", } m["xar"] = { "Karami", 11732281, "qfa-unc", -- extinct; "It is attested from only a short word list, which include many loans from Foia Foia" } m["xas"] = { "Kamassian", 35991, translit = "xas-translit", "syd", "Cyrl", } m["xat"] = { "Katawixi", 3440512, "sai-ktk", } m["xau"] = { "Kauwera", 6378983, "paa-tkw", } m["xav"] = { "Xavante", 36962, "sai-cje", "Latn", } m["xaw"] = { "Kawaiisu", 56338, "azc-num", "Latn", } m["xay"] = { "Kayan Mahakam", 25337171, } m["xbb"] = { "Lower Burdekin", 6693353, } m["xbc"] = { "แบกเตรีย", 756651, "ira-sbc", "Polyt, Mani", -- Polyt translit, display_text, strip_diacritics, sort_key in [[Module:scripts/data]] -- Mani translit in [[Module:scripts/data]] (NOTE: not present before, presumably an accidental omission) } m["xbd"] = { "Bindal", 4913975, } m["xbe"] = { "Bigambal", 16841801, "aus-pam", --unclassified within "Latn", } m["xbg"] = { "Bunganditj", 4997615, } m["xbi"] = { "Kombio", 6428259, "paa-tor", "Latn", } m["xbj"] = { "Birrpayi", 63339969, "aus-yuk", "Latn", } m["xbm"] = { "เบรอตงกลาง", 787610, "cel-brs", "Latn", ancestors = "obt", } m["xbn"] = { "Kenaboi", 6388752, "qfa-unc", -- extinct; two dialects seem to exist, either an isolate or Austroasiatic } m["xbo"] = { "Bulgar", 36880, "trk-ogr", "Arab, Polyt, Cyrs", ancestors = "trk-bul-pro", translit = { Cyrs = "Cyrs-translit", -- FIXME: formerly no translit specified for Polyt; unclear if the default [[Module:grc-translit]] is -- acceptable, so we disable it for now Polyt = false, }, strip_diacritics = { Arab = "ar-stripdiacritics", }, -- Polyt display_text, strip_diacritics, sort_key in [[Module:scripts/data]] -- Cyrs strip_diacritics, sort_key in [[Module:scripts/data]] } m["xbp"] = { "Bibbulman", 22918391, } m["xbr"] = { "กัมเบอรา", 3053279, "poz-cet", "Latn", } m["xbw"] = { "Kambiwá", 9006744, } m["xby"] = { "Butchulla", 31752631, } m["xcb"] = { "Cumbric", 35965, "cel-brw", } m["xcc"] = { "Camunic", 489011, nil, "Ital", -- Ital translit in [[Module:scripts/data]] } m["xce"] = { "Celtiberian", 37012, "cel-his", "Latn", } m["xch"] = { "Chemakum", 56397, "chi", "Latn", } m["xcl"] = { "อาร์มีเนียเก่า", 181074, "hyx", "Armn", -- Armn translit in [[Module:scripts/data]] override_translit = true, strip_diacritics = { remove_diacritics = "՞՜՛՟", from = {"եւ", "ՙ", "՚"}, to = {"և", "ʻ", "’"} }, } m["xcm"] = { "Comecrudo", 609808, "nai-pak", } m["xcn"] = { "Cotoname", 56889, "nai-pak", } m["xco"] = { "ฆวารัซม์", 33138, "ira-sbc", "Arab, Armi, Chrs, Phlv, Sogd", translit = { Chrs = "Chrs-translit" }, } m["xcr"] = { "Carian", 35929, "ine-luw", "Cari", } m["xct"] = { "ทิเบตคลาสสิก", 5128314, "sit-tib", "Tibt, Hani, Marc, Mong, mnc-Mong, xwo-Mong, Phag, Tang, Zanb", override_translit = true, -- Mong translit, display_text and strip_diacritics in [[Module:scripts/data]] -- Tibt translit, display_text, strip_diacritics, sort_key in [[Module:scripts/data]] -- mnc-Mong, xwo-Mong, Tang translit in [[Module:scripts/data]] sort_key = { Hani = "Hani-sortkey", }, } m["xcu"] = { "Curonian", 35857, "bat", "Latn", } m["xcv"] = { "Chuvan", 3516641, "qfa-yuk", "Cyrl", translit = "xcv-translit" } m["xcw"] = { "Coahuilteco", 2008062, "nai-pak", } m["xcy"] = { "Cayuse", 2472016, } m["xda"] = { "Darkinjung", 5223660, "aus-yuk", "Latn", } m["xdc"] = { "Dacian", 682547, "ine", "Latn, Polyt", -- Polyt translit, display_text, strip_diacritics, sort_key in [[Module:scripts/data]] } m["xdk"] = { "Dharug", 1166814, "aus-yuk", "Latn", } m["xdm"] = { "เอโดม", 2363529, "sem-can", "Phnx", -- Phnx translit in [[Module:scripts/data]] } m["xdq"] = { "Kaitag", 1990659, "cau-drg", "Cyrl", translit = {Cyrl = "dar-translit"}, override_translit = true, display_text = {Cyrl = s["cau-Cyrl-displaytext"]}, strip_diacritics = { Cyrl = s["cau-Cyrl-stripdiacritics"], Latn = s["cau-Latn-stripdiacritics"], }, sort_key = { Cyrl = { from = { "къкъ", "хьхь", -- 4 chars "гъ", "гь", "гӏ", "ё", "къ", "кь", "кӏ", "пп", "пӏ", "сс", "тт", "тӏ", "хх", "хъ", "хь", "хӏ", "цц", "цӏ", "чч", "чӏ" -- 2 chars }, to = { "к" .. p[2], "х" .. p[4], "г" .. p[1], "г" .. p[2], "г" .. p[3], "е" .. p[1], "к" .. p[1], "к" .. p[3], "к" .. p[4], "п" .. p[1], "п" .. p[2], "с" .. p[1], "т" .. p[1], "т" .. p[2], "х" .. p[1], "х" .. p[2], "х" .. p[3], "х" .. p[5], "ц" .. p[1], "ц" .. p[2], "ч" .. p[1], "ч" .. p[2] } }, }, } m["xdy"] = { "Malayic Dayak", 3514892, } m["xeb"] = { "Eblaite", 35345, "sem-eas", "Xsux", } m["xed"] = { "Hdi", 56246, "cdc-cbm", "Latn", } m["xeg"] = { "ǁXegwi", 3509732, "khi-tuu", "Latn", } m["xel"] = { "Kelo", 6386412, "sdv-eje", } m["xem"] = { "Kembayan", 6386874, } m["xep"] = { "Epi-Olmec", 124257204, } m["xer"] = { "Xerénte", 3073436, "sai-cje", "Latn", } m["xes"] = { "Kesawai", 6394907, "ngf-eva", "Latn", } m["xet"] = { "Xetá", 2980404, "tup-gua", "Latn", } m["xeu"] = { "Keoru-Ahia", 11732313, "paa-wel", "Latn", } m["xfa"] = { "ฟาลิสกุส", --Faliscus 35669, "itc-laf", "Ital, Latn", -- Ital translit in [[Module:scripts/data]] display_text = { Latn = s["itc-Latn-displaytext"] }, strip_diacritics = { Latn = s["itc-Latn-stripdiacritics"] }, sort_key = { Latn = s["itc-Latn-sortkey"] }, } m["xga"] = { "Galatian", 27403, "cel", "Latn, Polyt", ancestors = "cel-gau", -- Polyt translit, display_text, strip_diacritics, sort_key in [[Module:scripts/data]] } m["xgb"] = { "Gbin", 16934745, "dmn-mse", "Latn", } m["xgd"] = { "Gudang", 5614528, } m["xgf"] = { "Gabrielino-Fernandeño", 56387, "azc-tak", "Latn", } m["xgg"] = { "Goreng", 50934914, } m["xgi"] = { "Garingbal", 52008172, } m["xgl"] = { "Galindian", 1190494, "bat-wes", "Latn", } m["xgm"] = { "Darumbal", 16954400, } m["xgr"] = { "Garza", 3098656, "nai-pak", } m["xgu"] = { "Unggumi", 62000004, "aus-wor", "Latn", } m["xgw"] = { "Guwa", 5621992, } m["xha"] = { "Harami", 41506724, nil, "Sarb", -- Sarb translit in [[Module:scripts/data]] } m["xhc"] = { "Hunnic", 35959, } m["xhd"] = { "Hadrami", 1032453, "sem-osa", "Sarb", -- Sarb translit in [[Module:scripts/data]] } m["xhe"] = { "Khetrani", 2614111, "inc-pan", ancestors = "lah", } m["xhm"] = { "เขมรกลาง", 25226861, "mkh-kmr", "Latn, Khmr", --and also Khom ancestors = "okz", translit = { Khmr = "Khmr-translit", }, } m["xhr"] = { "Hernican", 5908773, "itc-sbl", "Ital, Latn", -- Ital translit in [[Module:scripts/data]] display_text = { Latn = s["itc-Latn-displaytext"] }, strip_diacritics = { Latn = s["itc-Latn-stripdiacritics"] }, sort_key = { Latn = s["itc-Latn-sortkey"] }, } m["xht"] = { "Hattic", 31107, "qfa-iso", "Xsux", } m["xhu"] = { "ฮูร์เรีย", 35740, "qfa-hur", "Xsux, Ugar", } m["xhv"] = { "Khua", 22970290, "mkh-kat", } m["xib"] = { "Iberian", 855215, "qfa-iso", "Latn, Ibrnn, Ibrns", } m["xii"] = { "Xiri", 36876, } m["xin"] = { "Xinca", 1546494, "nai-xin", "Latn", } m["xil"] = { "Illyrian", 35976, "ine", type = "reconstructed", } m["xir"] = { "Xiriâna", 2028772, "awd", "Latn", } m["xis"] = { "Kisan", 63339998, "dra-kml", ancestors = "kru", } m["xiv"] = { "Harappan", 3428279, "qfa-unc", "Inds", } m["xiy"] = { "Xipaya", 13226, "tup", } m["xjb"] = { "Minjungbal", 16334334, "aus-pam", "Latn", } m["xka"] = { "Kalkoti", 3877551, "inc-dng", "ur-Arab,Latn", } m["xkb"] = { "Manigri-Kambolé Ede Nago", 36042, "alv-ede", "Latn", } m["xkc"] = { "Khoini", 6401919, "xme-ttc", ancestors = "xme-ttc-wes", } m["xkd"] = { "Mendalam Kayan", 12952597, } m["xke"] = { "Kereho", 6437086, "poz", "Latn", } m["xkf"] = { "Khengkha", 3695207, "sit-ebo", "Tibt", override_translit = true, -- Tibt translit, display_text, strip_diacritics, sort_key in [[Module:scripts/data]] } m["xkg"] = { "Kagoro", 11159524, "dmn-wmn", } m["xki"] = { "Kenyan Sign Language", 6392859, "sgn", } m["xkj"] = { "Kajali", 14916876, "xme-ttc", ancestors = "xme-ttc-cen", } m["xkk"] = { "Kaco'", 6344767, "mkh", } m["xkl"] = { "Bakung", 6736761, "poz-swa", "Latn", } m["xkn"] = { "Kayan River Kayan", 12473395, "poz", "Latn", } m["xko"] = { "Kiorr", 6414519, "mkh-pal", } m["xkp"] = { "Kabatei", 34165, "xme-ttc", ancestors = "xme-ttc-cen", } m["xkq"] = { "Koroni", 3199000, "poz-btk", } m["xkr"] = { "Xakriabá", 3073441, "sai-cje", "Latn", } m["xks"] = { "Kumbewaha", 6443722, } m["xkt"] = { "Kantosi", 35651, "nic-dag", } m["xku"] = { "Kaamba", 11042324, "bnt-kng", } m["xkv"] = { "Kgalagadi", 2088743, "bnt-sts", "Latn", } m["xkw"] = { "Kembra", 12953627, "paa-pau", } m["xkx"] = { "Karore", 6373260, "poz-ocw", } m["xky"] = { "Uma' Lasan", 7881227, "poz-swa", } m["xkz"] = { "Kurtöp", 3695193, "sit-ebo", "Tibt, Latn", -- Tibt translit, display_text, strip_diacritics, sort_key in [[Module:scripts/data]] } m["xla"] = { "Kamula", 10957277, "paa-kae", "Latn", } m["xlb"] = { "Loup B", 13108281, "alg-eas", "Latn", } m["xlc"] = { "Lycian", 35969, "ine-luw", "Lyci", translit = "Lyci-translit", } m["xld"] = { "Lydian", 36095, "ine-ana", "Lydi", translit = "Lydi-translit", } m["xle"] = { "Lemnian", 36203, "qfa-tyn", "Ital", -- Ital translit in [[Module:scripts/data]] } m["xlg"] = { "Ancient Ligurian", 36104, "ine", "Latn", } m["xli"] = { "Liburnian", 35835, "ine", } m["xln"] = { "Alanic", 3658580, "xsc-sar", "Polyt, Latn", -- Polyt translit, display_text, strip_diacritics, sort_key in [[Module:scripts/data]] } m["xlo"] = { "Loup A", 27921265, "alg-eas", "Latn", } m["xlp"] = { "Lepontic", 35993, "cel", "Ital, Latn", -- Ital translit in [[Module:scripts/data]] } m["xls"] = { "Lusitanian", 35960, "ine", "Latn", } m["xlu"] = { "Luwian", 12634577, "ine-luw", "Xsux, Hluw", } m["xly"] = { "Elymian", 35329, "ine", "Polyt", -- Polyt translit, display_text, strip_diacritics, sort_key in [[Module:scripts/data]] } m["xmb"] = { "Mbonga", 36064, "nic-jrn", "Latn", } m["xmc"] = { "Makhuwa-Marrevone", 11127231, "bnt-mak", ancestors = "vmw", } m["xmd"] = { "Mbudum", 6799790, "cdc-cbm", "Latn", } m["xmf"] = { "มิงเกรเลีย", 13359, "ccs-zan", "Geor", -- Geor translit in [[Module:scripts/data]] override_translit = true, } m["xmg"] = { "Mengaka", 36017, "bai", "Latn", } m["xmh"] = { "Kugu-Muminh", 10549849, "aus-pmn", "Latn", } m["xmj"] = { "Majera", 6737666, "cdc-cbm", "Latn", } m["xmk"] = { "Ancient Macedonian", 35974, "grk", "Polyt", -- Polyt translit, display_text, strip_diacritics, sort_key in [[Module:scripts/data]] } m["xml"] = { "Malaysian Sign Language", 33420, "sgn", } m["xmm"] = { "มลายูแบบมานาโด", 1068112, "crp", "Latn", } m["xmo"] = { "Morerebi", 12953749, "tup", "Latn", } m["xmp"] = { "Kuku-Mu'inh", 10549852, nil, "Latn", } m["xmq"] = { "Kuku-Mangk", 10549851, "aus-pam", "Latn", } m["xmr"] = { "Meroitic", 13366, "afa", "Mero, Merc, Latn", -- we have entries in Latn translit = { Mero = "xmr-translit", Merc = "xmr-translit", }, } m["xms"] = { "Moroccan Sign Language", 6913107, "sgn", } m["xmt"] = { "Matbat", 6786187, "poz-hce", "Latn", } m["xmu"] = { "Kamu", 6359779, } m["xmx"] = { "Maden", 12952756, "poz-hce", } m["xmy"] = { "Mayaguduna", 3436736, } m["xmz"] = { "Mori Bawah", 3324069, "poz-btk", "Latn", } m["xna"] = { "Ancient North Arabian", 1472213, "sem-cen", "Narb", -- Narb translit in [[Module:scripts/data]] } m["xnb"] = { "Kanakanabu", 172244, "map", "Latn", } m["xng"] = { "มองโกเลียกลาง", 2582455, "xgn", "Mong, Phag, Hani, Arab, Armn", translit = { -- Mong translit in [[Module:scripts/data]] -- Armn translit in [[Module:scripts/data]] (NOTE: formerly not present, probably an accidental omission) }, -- Mong display_text and strip_diacritics in [[Module:scripts/data]] sort_key = { Hani = "Hani-sortkey" }, } m["xnh"] = { "Kuanhua", 6441084, "mkh-pal", } m["xni"] = { "Ngarigu", 7022072, "aus-yuk", } m["xnk"] = { "Nganakarti", 33087049, } m["xnn"] = { "กันกานาอือเหนือ", 12953609, "phi", "Latn", } -- "xno" is treated as "fro", see [[WT:LT]] m["xnr"] = { "Kangri", 2331560, "him", "Deva, Takr, fa-Arab", ancestors = "doi", translit = { Deva = "Deva-translit", Takr = "Takr-translit", }, } m["xns"] = { "Kanashi", 6360672, "sit-whm", } m["xnt"] = { "Narragansett", 3336118, "alg-eas", "Latn, Hebr", strip_diacritics = { Latn = {remove_diacritics = c.grave .. c.acute .. c.tilde .. c.macron .. c.breve}, }, -- Hebr display_text, strip_diacritics, sort_key in [[Module:scripts/data]] } m["xnu"] = { "Nukunul", 7068904, } m["xny"] = { "Nyiyaparli", 16919427, "aus-nga", "Latn", } m["xoc"] = { "O'chi'chi'", 3813833, "nic-cde", "Latn", } m["xod"] = { "Kokoda", 6426734, "ngf-sbh", "Latn", } m["xog"] = { "Soga", 33784, "bnt-nyg", "Latn", } m["xoi"] = { "Kominimung", 6428352, "paa-ram", "Latn", } m["xok"] = { "Xokleng", 3027930, "sai-sje", } m["xom"] = { "Komo", 56681, "ssa-kom", "Latn", } m["xon"] = { "Konkomba", 35674, "nic-grm", "Latn", } m["xoo"] = { -- contrast kzw, sai-kat, sai-xoc "Xukurú", 9096758, } m["xop"] = { "Kopar", 11732346, "paa-lsp", "Latn", } m["xor"] = { "Korubo", 3199022, } m["xow"] = { "Kowaki", 6434920, "ngf-tib", "Latn", } m["xpa"] = { "Pirriya", 16978087, } m["xpb"] = { "Pyemmairre", 7262964, "qfa-unc", "Latn", } m["xpc"] = { "Pecheneg", 877881, "trk", } m["xpd"] = { "Paredarerme", 7136678, "qfa-unc", "Latn", } m["xpe"] = { "Liberia Kpelle", 20527226, "dmn-msw", "Latn, Kpel", ancestors = "kpe", } m["xpf"] = { "Southeast Tasmanian", 7068421, "qfa-unc", "Latn", } m["xpg"] = { "Phrygian", 36751, "ine", "Polyt", -- Polyt translit, display_text, strip_diacritics, sort_key in [[Module:scripts/data]] } m["xph"] = { "Tyerrernotepanner", 7859815, "qfa-unc", "Latn", } m["xpi"] = { "Pictish", 856383, "cel-brw", "Ogam, Latn", } m["xpj"] = { "Mpalitjanh", 6928192, "aus-pam", } m["xpk"] = { "Kulina", 6443027, "sai-pan", } m["xpl"] = { "Port Sorell", 7230944, "qfa-unc", "Latn", } m["xpm"] = { "Pumpokol", 2991985, "qfa-ypm", "Latn, Cyrl", } m["xpn"] = { "Kapinawá", 6366667, } m["xpo"] = { "Pochutec", 2427341, "azc-nah", "Latn", } m["xpp"] = { "Puyo-Paekche", 65455887, } m["xpq"] = { "Mohegan-Pequot", 3319130, "alg-eas", "Latn", } m["xpr"] = { "พาร์เทีย", 25953, "ira-mpr", "Prti, Mani, Phlv", translit = { Prti = "Prti-translit", -- Mani translit in [[Module:scripts/data]] }, } m["xps"] = { "Pisidian", 36580, "ine-luw", } m["xpu"] = { "Punic", 535958, "sem-can", "Phnx, Latn, Polyt", ancestors = "phn", -- Polyt translit, display_text, strip_diacritics, sort_key in [[Module:scripts/data]] -- Phnx translit in [[Module:scripts/data]] } m["xpv"] = { "Tommeginne", 7819095, "qfa-unc", "Latn", } m["xpw"] = { "Peerapper", 7160431, "qfa-unc", "Latn", } m["xpx"] = { "Toogee", 7824008, "qfa-unc", "Latn", } m["xpy"] = { "Buyeo", 5003359, "qfa-kor", "Hani", sort_key = "Hani-sortkey", } m["xpz"] = { "Bruny Island", 4979601, "qfa-unc", "Latn", } m["xqa"] = { "Karakhanid", 4494885, "trk-kar", "Arab, Ougr", strip_diacritics = "ar-stripdiacritics", } m["xqt"] = { "Qatabanian", 384101, "sem-osa", "Sarb", -- Sarb translit in [[Module:scripts/data]] } m["xra"] = { "Krahô", 3199549, "sai-nje", "Latn", } m["xrb"] = { "Eastern Karaboro", 35716, "alv-krb", "Latn", } m["xrd"] = { "Gundungurra", 60611652, nil, "Latn", } m["xre"] = { "Kreye", 3199686, "sai-nje", } m["xrg"] = { "Minang", 22893424, "aus-pam", } m["xri"] = { "Krikati-Timbira", 3199710, } m["xrm"] = { "Armazic", 7599646, } m["xrn"] = { "Arin", 34088, "qfa-yrn", "Latn, Cyrl", } m["xrq"] = { "Karranga", 6373349, nil, "Latn", } m["xrr"] = { "Raetic", 36689, "qfa-tyn", "Ital", -- Ital translit in [[Module:scripts/data]] } m["xrt"] = { "Aranama-Tamique", 2859505, } m["xru"] = { "Marriammu", 10577724, "aus-dal", } m["xrw"] = { "Karawa", 6368857, "paa-spk", "Latn", } m["xsa"] = { "Sabaean", 1070391, "sem-osa", "Sarb", -- Sarb translit in [[Module:scripts/data]] } m["xsb"] = { "ซัมบัล", 2592378, "phi", "Latn", } m["xsd"] = { "Sidetic", 36659, "ine-luw", "Sidt", } m["xse"] = { "Sempan", 3504358, "ngf-ask", "Latn", } m["xsh"] = { "Shamang", 3914876, "nic-plc", } m["xsi"] = { "Sio", 3485100, "poz-ocw", } m["xsj"] = { "Subi", 7631298, "bnt-haj", } m["xsl"] = { "South Slavey", 28552, "den", "Latn", } m["xsm"] = { "Kasem", 35552, "nic-gnn", "Latn", } m["xsn"] = { "Sanga (Nigeria)", 3915334, "nic-jer", "Latn", } m["xso"] = { "Solano", 2474492, "qfa-unc", -- extinct, poorly classified (only 21 words known), possibly an isolate "Latn", } m["xsp"] = { "Silopi", 7515533, "ngf-usi", "Latn", } m["xsq"] = { "Makhuwa-Saka", 11008159, "bnt-mak", ancestors = "vmw", } m["xsr"] = { "เศรปา", 36612, "sit-tib", "Tibt, Deva", ancestors = "xct", translit = { Deva = "Deva-translit", }, override_translit = true, -- Tibt translit, display_text, strip_diacritics, sort_key in [[Module:scripts/data]] } m["xss"] = { "Assan", 34089, "qfa-yko", "Latn, Cyrl", } m["xsu"] = { "Sanumá", 251728, "sai-ynm", "Latn", } m["xsv"] = { "ซูโดเวีย", 35603, "bat-wes", "Latn", } m["xsy"] = { "Saisiyat", 716695, "map", "Latn", } m["xta"] = { "Alcozauca Mixtec", 25559587, "omq-mxt", "Latn", } m["xtb"] = { "Chazumba Mixtec", 12182838, "omq-mxt", "Latn", } m["xtc"] = { "Kadugli", 3407136, "qfa-kad", "Latn", } m["xtd"] = { "Diuxi-Tilantongo Mixtec", 7802048, "omq-mxt", "Latn", } m["xte"] = { "Ketengban", 10990152, "ngf-mek", "Latn", } m["xth"] = { "Yitha Yitha", 8053879, } m["xti"] = { "Sinicahua Mixtec", 12953733, "omq-mxt", "Latn", } m["xtj"] = { "San Juan Teita Mixtec", 32093049, "omq-mxt", "Latn", } m["xtl"] = { "Tijaltepec Mixtec", 12953738, "omq-mxt", "Latn", } m["xtm"] = { "Magdalena Peñasco Mixtec", 7179700, "omq-mxt", "Latn", } m["xtn"] = { "Northern Tlaxiaco Mixtec", 25559585, "omq-mxt", "Latn", } m["xto"] = { "โทแคเรียนเอ", 2827041, "ine-toc", "Latn", } m["xtp"] = { "San Miguel Piedras Mixtec", 7414970, "omq-mxt", "Latn", } m["xtq"] = { "ตุมซุก", 65455890, "xsc-sak", "Brah, Khar", -- Brah translit in [[Module:scripts/data]] } m["xtr"] = { "Early Tripuri", 65455891, } m["xts"] = { "Sindihui Mixtec", 13583581, "omq-mxt", "Latn", } m["xtt"] = { "Tacahua Mixtec", 7673668, "omq-mxt", "Latn", } m["xtu"] = { "Cuyamecalco Mixtec", 12953726, "omq-mxt", "Latn", } m["xtv"] = { "Thawa", 7711494, } m["xtw"] = { "Tawandê", 63340139, "sai-nmk", "Latn", } m["xty"] = { "Yoloxochitl Mixtec", 8054817, "omq-mxt", "Latn", } m["xua"] = { "Alu Kurumba", 12952679, "dra-kan", "Knda", -- Knda translit in [[Module:scripts/data]] } m["xub"] = { "Betta Kurumba", 16841033, "dra-kan", "Knda, Mlym, Taml", translit = { Taml = "Taml-translit", }, -- Knda translit in [[Module:scripts/data]] -- Mlym translit in [[Module:scripts/data]] (NOTE: not present before, presumably an accidental omission) } m["xud"] = { "Umiida", 61999874, "aus-wor", "Latn", } m["xug"] = { "คูนิงามิ", 56558, "jpx-nry", "Jpan", translit = s["jpx-translit"], display_text = s["jpx-displaytext"], strip_diacritics = s["jpx-stripdiacritics"], sort_key = s["jpx-sortkey"], } m["xuj"] = { "Jennu Kurumba", 21282543, "dra-kan", "Knda", -- Knda translit in [[Module:scripts/data]] } m["xul"] = { "Ngunawal", 7022712, "aus-yuk", "Latn", } m["xum"] = { "อุมเบรีย", 36957, "itc-sbl", "Ital, Latn", -- Ital translit in [[Module:scripts/data]] display_text = { Latn = s["itc-Latn-displaytext"] }, strip_diacritics = { Latn = s["itc-Latn-stripdiacritics"] }, sort_key = { Latn = s["itc-Latn-sortkey"] }, } m["xun"] = { "Unggaranggu", 61999823, "aus-wor", "Latn", } m["xuo"] = { "Kuo", 6445233, "alv-mbm", } m["xup"] = { "Upper Umpqua", 20607, "ath-pco", "Latn", } m["xur"] = { "Urartian", 36934, "qfa-hur", "Xsux", } m["xut"] = { "Kuthant", 6448417, } m["xuu"] = { "Khwe", 28305, "khi-kal", "Latn", } m["xve"] = { "Venetic", 36871, "ine", "Ital", -- Ital translit in [[Module:scripts/data]] } -- m["xvi"] = { "Kamviri", 1193495, "nur-nor", Arab } moved to etym-only code m["xvn"] = { "วันดัล", 36835, "gme", "Latn", } m["xvo"] = { "Volscian", 622110, "itc-sbl", "Ital, Latn", -- Ital translit in [[Module:scripts/data]] display_text = { Latn = s["itc-Latn-displaytext"] }, strip_diacritics = { Latn = s["itc-Latn-stripdiacritics"] }, sort_key = { Latn = s["itc-Latn-sortkey"] }, } m["xvs"] = { "Vestinian", 2576407, "itc", "Ital, Latn", -- Ital translit in [[Module:scripts/data]] display_text = { Latn = s["itc-Latn-displaytext"] }, strip_diacritics = { Latn = s["itc-Latn-stripdiacritics"] }, sort_key = { Latn = s["itc-Latn-sortkey"] }, } m["xwa"] = { "Kwaza", 3200839, "qfa-unc", -- insufficient research; probably an isolate "Latn", } m["xwc"] = { "Woccon", 3569569, "nai-cat", "Latn", } m["xwd"] = { "Wadi Wadi", 7959249, } m["xwe"] = { "Xwela Gbe", 36887, "alv-pph", } m["xwg"] = { "Kwegu", 56723, "sdv", } m["xwj"] = { "Wajuk", 33110188, } m["xwk"] = { "Wangkumara", 7967891, "aus-pam", "Latn", } m["xwl"] = { "Western Xwla Gbe", 36924, "alv-pph", "Latn", } m["xwo"] = { "Written Oirat", 56959, "xgn-cen", "xwo-Mong", -- xwo-Mong translit in [[Module:scripts/data]] } m["xwr"] = { "Kwerba Mamberamo", 6450325, "paa-tkw", } m["xww"] = { "Wemba-Wemba", 18472819, "aus-pam", "Latn", } m["xxb"] = { "Boro", 16844787, nil, "Latn", } m["xxk"] = { "Ke'o", 3195346, } m["xxm"] = { "Minkin", 6867836, } m["xxr"] = { "Koropó", 6432560, } m["xxt"] = { "Tambora", 36711, "qfa-unc", -- Papuan; extinct, poorly attested, probable isolate per Wikipedia "Latn", } m["xya"] = { "Yaygir", 8050525, "aus-pam", "Latn", } m["xyb"] = { "Yandjibara", 63340189, nil, "Latn", } m["xyl"] = { "Yalakalore", 12645352, "sai-nmk", "Latn", } m["xyt"] = { "Mayi-Thakurti", 47004719, "aus-pam", "Latn", } m["xyy"] = { "Yorta Yorta", 8055849, "aus-pam", "Latn", } m["xzh"] = { "Zhang-Zhung", 3437292, "sit-alm", "Marc, Tibt", -- Tibt translit, display_text, strip_diacritics, sort_key in [[Module:scripts/data]] } m["xzm"] = { "Semigallian", 47631, "bat-eas", } m["xzp"] = { "Ancient Zapotec", 65455892, } return require("Module:languages").finalizeData(m, "language") dgsbkxvd46zraojqykzpo47fywb9gpu มอดูล:languages 828 36388 5775180 5723327 2026-10-14T03:26:39Z Octahedron80 267 5775180 Scribunto text/plain --[==[ intro: This module implements fetching of language-specific information and processing text in a given language. ===Types of languages=== There are two types of languages: full languages and etymology-only languages. The essential difference is that only full languages appear in L2 headings in vocabulary entries, and hence categories like [[:Category:French nouns]] exist only for full languages. Etymology-only languages have either a full language or another etymology-only language as their parent (in the parent-child inheritance sense), and for etymology-only languages with another etymology-only language as their parent, a full language can always be derived by following the parent links upwards. For example, "Canadian French", code `fr-CA`, is an etymology-only language whose parent is the full language "French", code `fr`. An example of an etymology-only language with another etymology-only parent is "Northumbrian Old English", code `ang-nor`, which has "Anglian Old English", code `ang-ang` as its parent; this is an etymology-only language whose parent is "Old English", code `ang`, which is a full language. (This is because Northumbrian Old English is considered a variety of Anglian Old English.) Sometimes the parent is the "Undetermined" language, code `und`; this is the case, for example, for "substrate" languages such as "Pre-Greek", code `qsb-grc`, and "the BMAC substrate", code `qsb-bma`. It is important to distinguish language ''parents'' from language ''ancestors''. The parent-child relationship is one of containment, i.e. if X is a child of Y, X is considered a variety of Y. On the other hand, the ancestor-descendant relationship is one of descent in time. For example, "Classical Latin", code `la-cla`, and "Late Latin", code `la-lat`, are both etymology-only languages with "Latin", code `la`, as their parents, because both of the former are varieties of Latin. However, Late Latin does *NOT* have Classical Latin as its parent because Late Latin is *not* a variety of Classical Latin; rather, it is a descendant. There is in fact a separate `ancestors` field that is used to express the ancestor-descendant relationship, and Late Latin's ancestor is given as Classical Latin. It is also important to note that sometimes an etymology-only language is actually the conceptual ancestor of its parent language. This happens, for example, with "Old Italian" (code `roa-oit`), which is an etymology-only variant of full language "Italian" (code `it`), and with "Old Latin" (code `itc-ola`), which is an etymology-only variant of Latin. In both cases, the full language has the etymology-only variant listed as an ancestor. This allows a Latin term to inherit from Old Latin using the {{tl|inh}} template (where in this template, "inheritance" refers to ancestral inheritance, i.e. inheritance in time, rather than in the parent-child sense); likewise for Italian and Old Italian. Full languages come in three subtypes: * {regular}: This indicates a full language that is attested according to [[WT:CFI]] and therefore permitted in the main namespace. There may also be reconstructed terms for the language, which are placed in the {Reconstruction} namespace and must be prefixed with * to indicate a reconstruction. Most full languages are natural (not constructed) languages, but a few constructed languages (e.g. Esperanto and Volapük, among others) are also allowed in the mainspace and considered regular languages. * {reconstructed}: This language is not attested according to [[WT:CFI]], and therefore is allowed only in the {Reconstruction} namespace. All terms in this language are reconstructed, and must be prefixed with *. Languages such as Proto-Indo-European and Proto-Germanic are in this category. * {appendix-constructed}: This language is attested but does not meet the additional requirements set out for constructed languages ([[WT:CFI#Constructed languages]]). Its entries must therefore be in the Appendix namespace, but they are not reconstructed and therefore should not have * prefixed in links. Most constructed languages are of this subtype. Both full languages and etymology-only languages have a {Language} object associated with them, which is fetched using the {getByCode} function in [[Module:languages]] to convert a language code to a {Language} object. Depending on the options supplied to this function, etymology-only languages may or may not be accepted, and family codes may be accepted (returning a {Family} object as described in [[Module:families]]). There are also separate {getByCanonicalName} functions in [[Module:languages]] and [[Module:etymology languages]] to convert a language's canonical name to a {Language} object (depending on whether the canonical name refers to a full or etymology-only language). ===Textual representations=== Textual strings belonging to a given language come in several different ''text variants'': # The ''input text'' is what the user supplies in wikitext, in the parameters to {{tl|m}}, {{tl|l}}, {{tl|ux}}, {{tl|t}}, {{tl|lang}} and the like. # The ''corrected input text'' is the input text with some corrections and/or normalizations applied, such as bad-character replacements for certain languages, like replacing `l` or `1` to [[palochka]] in some languages written in Cyrillic. (FIXME: This currently goes under the name ''display text'' but that will be repurposed below. Also, [[User:Surjection]] suggests renaming this to ''normalized input text'', but "normalized" is used in a different sense in [[Module:usex]].) # The ''display text'' is the text in the form as it will be displayed to the user. This is what appears in headwords, in usexes, in displayed internal links, etc. This can include accent marks that are removed to form the stripped display text (see below), as well as embedded bracketed links that are variously processed further. The display text is generated from the corrected input text by applying language-specific transformations; for most languages, there will be no such transformations. The general reason for having a difference between input and display text is to allow for extra information in the input text that is not displayed to the user but is sent to the transliteration module. Note that having different display and input text is only supported currently through special-casing but will be generalized. Examples of transformations are: (1) Removing the {{cd|^}} that is used in certain East Asian (and possibly other unicameral) languages to indicate capitalization of the transliteration (which is currently special-cased); (2) for Korean, removing or otherwise processing hyphens (which is currently special-cased); (3) for Arabic, removing a ''sukūn'' diacritic placed over a ''tāʔ marbūṭa'' (like this: ةْ) to indicate that the ''tāʔ marbūṭa'' is pronounced and transliterated as /t/ instead of being silent [NOTE, NOT IMPLEMENTED YET]; (4) for Thai and Khmer, converting space-separated words to bracketed words and resolving respelling substitutions such as `[กรีน/กฺรีน]`, which indicate how to transliterate given words [NOTE, NOT IMPLEMENTED YET except in language-specific templates like {{tl|th-usex}}]. ## The ''right-resolved display text'' is the result of removing brackets around one-part embedded links and resolving two-part embedded links into their right-hand components (i.e. converting two-part links into the displayed form). The process of right-resolution is what happens when you call {{cd|remove_links()}} in [[Module:links]] on some text. When applied to the display text, it produces exactly what the user sees, without any link markup. # The ''stripped display text'' is the result of applying diacritic-stripping to the display text. ## The ''left-resolved stripped display text'' [NEED BETTER NAME] is the result of applying left-resolution to the stripped display text, i.e. similar to right-resolution but resolving two-part embedded links into their left-hand components (i.e. the linked-to page). If the display text refers to a single page, the resulting of applying diacritic stripping and left-resolution produces the ''logical pagename''. # The ''physical pagename text'' is the result of converting the stripped display text into physical page links. If the stripped display text contains embedded links, the left side of those links is converted into physical page links; otherwise, the entire text is considered a pagename and converted in the same fashion. The conversion does three things: (1) converts characters not allowed in pagenames into their "unsupported title" representation, e.g. {{cd|Unsupported titles/`gt`}} in place of the logical name {{cd|>}}; (2) handles certain special-cased unsupported-title logical pagenames, such as {{cd|Unsupported titles/Space}} in place of {{cd|[space]}} and {{cd|Unsupported titles/Ancient Greek dish}} in place of a very long Greek name for a gourmet dish as found in Aristophanes; (3) converts "mammoth" pagenames such as [[a]] into their appropriate split component, e.g. [[a/languages A to L]]. # The ''source translit text'' is the text as supplied to the language-specific {{cd|transliterate()}} method. The form of the source translit text may need to be language-specific, e.g Thai and Khmer will need the corrected input text, whereas other languages may need to work off the display text. [FIXME: It's still unclear to me how embedded bracketed links are handled in the existing code.] In general, embedded links need to be right-resolved (see above), but when this happens is unclear to me [FIXME]. Some languages have a chop-up-and-paste-together scheme that sends parts of the text through the transliterate mechanism, and for others (those listed with "cont" in {{cd|substitution}} in [[Module:languages/data]]) they receive the full input text, but preprocessed in certain ways. (The wisdom of this is still unclear to me.) # The ''transliterated text'' (or ''transliteration'') is the result of transliterating the source translit text. Unlike for all the other text variants except the transcribed text, it is always in the Latin script. # The ''transcribed text'' (or ''transcription'') is the result of transcribing the source translit text, where "transcription" here means a close approximation to the phonetic form of the language in languages (e.g. Akkadian, Sumerian, Ancient Egyptian, maybe Tibetan) that have a wide difference between the written letters and spoken form. Unlike for all the other text variants other than the transliterated text, it is always in the Latin script. Currently, the transcribed text is always supplied manually be the user; there is no such thing as a {{cd|transcribe()}} method on language objects. # The ''sort key'' is the text used in sort keys for determining the placing of pages in categories they belong to. The sort key is generated from the pagename or a specified ''sort base'' by lowercasing, doing language-specific transformations and then uppercasing the result. If the sort base is supplied and is generated from input text, it needs to be converted to display text, have embedded links removed through right-resolution and have diacritic-stripping applied. # There are other text variants that occur in usexes (specifically, there are normalized variants of several of the above text variants), but we can skip them for now. The following methods exist on {Language} objects to convert between different text variants: # {correctInputText} (currently called {makeDisplayText}): This converts input text to corrected input text. # {stripDiacritics}: This converts to stripped display text. [FIXME: This needs some rethinking. In particular, {stripDiacritics} is sometimes called on input text, corrected input text or display text (in various paths inside of [[Module:links]], and, in the case of input text, usually from other modules). We need to make sure we don't try to convert input text to display text twice, but at the same time we need to support calling it directly on input text since so many modules do this. This means we need to add a parameter indicating whether the passed-in text is input, corrected input, or display text; if the former two, we call {correctInputText} ourselves.] # {logicalToPhysical}: This converts logical pagenames to physical pagenames. # {transliterate}: This appears to convert input text with embedded brackets removed into a transliteration. [FIXME: This needs some rethinking. In particular, it calls {processDisplayText} on its input, which won't work for Thai and Khmer, so we may need language-specific flags indicating whether to pass the input text directly to the language transliterate method. In addition, I'm not sure how embedded links are handled in the existing translit code; a lot of callers remove the links themselves before calling {transliterate()}, which I assume is wrong.] # {makeSortKey}: This converts display text (?) to a sort key. [FIXME: Clarify this.] ]==] local export = {} local debug_track_module = "Module:debug/track" local etymology_languages_data_module = "Module:etymology languages/data" local families_module = "Module:families" local headword_page_module = "Module:headword/page" local json_module = "Module:JSON" local language_like_module = "Module:language-like" local languages_data_module = "Module:languages/data" local languages_data_patterns_module = "Module:languages/data/patterns" local links_data_module = "Module:links/data" local load_module = "Module:load" local scripts_module = "Module:scripts" local scripts_data_module = "Module:scripts/data" local string_encode_entities_module = "Module:string/encode entities" local string_pattern_escape_module = "Module:string/patternEscape" local string_replacement_escape_module = "Module:string/replacementEscape" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local utilities_module = "Module:utilities" local wikimedia_languages_module = "Module:wikimedia languages" local mw = mw local string = string local table = table local char = string.char local concat = table.concat local find = string.find local floor = math.floor local get_by_code -- Defined below. local get_data_module_name -- Defined below. local get_extra_data_module_name -- Defined below. local getmetatable = getmetatable local gmatch = string.gmatch local gsub = string.gsub local insert = table.insert local ipairs = ipairs local is_known_language_tag = mw.language.isKnownLanguageTag local make_object -- Defined below. local match = string.match local next = next local pairs = pairs local remove = table.remove local require = require local select = select local setmetatable = setmetatable local sub = string.sub local type = type local unstrip = mw.text.unstrip -- Loaded as needed by findBestScript. local Hans_chars local Hant_chars local function check_object(...) check_object = require(utilities_module).check_object return check_object(...) end local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end local function decode_entities(...) decode_entities = require(string_utilities_module).decode_entities return decode_entities(...) end local function decode_uri(...) decode_uri = require(string_utilities_module).decode_uri return decode_uri(...) end local function deep_copy(...) deep_copy = require(table_module).deepCopy return deep_copy(...) end local function encode_entities(...) encode_entities = require(string_encode_entities_module) return encode_entities(...) end local function get_L2_sort_key(...) get_L2_sort_key = require(headword_page_module).get_L2_sort_key return get_L2_sort_key(...) end local function get_script(...) get_script = require(scripts_module).getByCode return get_script(...) end local function find_best_script_without_lang(...) find_best_script_without_lang = require(scripts_module).findBestScriptWithoutLang return find_best_script_without_lang(...) end local function get_family(...) get_family = require(families_module).getByCode return get_family(...) end local function get_plaintext(...) get_plaintext = require(utilities_module).get_plaintext return get_plaintext(...) end local function get_wikimedia_lang(...) get_wikimedia_lang = require(wikimedia_languages_module).getByCode return get_wikimedia_lang(...) end local function keys_to_list(...) keys_to_list = require(table_module).keysToList return keys_to_list(...) end local function list_to_set(...) list_to_set = require(table_module).listToSet return list_to_set(...) end local function load_data(...) load_data = require(load_module).load_data return load_data(...) end local function make_family_object(...) make_family_object = require(families_module).makeObject return make_family_object(...) end local function pattern_escape(...) pattern_escape = require(string_pattern_escape_module) return pattern_escape(...) end local function replacement_escape(...) replacement_escape = require(string_replacement_escape_module) return replacement_escape(...) end local function safe_require(...) safe_require = require(load_module).safe_require return safe_require(...) end local function shallow_copy(...) shallow_copy = require(table_module).shallowCopy return shallow_copy(...) end local function split(...) split = require(string_utilities_module).split return split(...) end local function to_json(...) to_json = require(json_module).toJSON return to_json(...) end local function u(...) u = require(string_utilities_module).char return u(...) end local function ugsub(...) ugsub = require(string_utilities_module).gsub return ugsub(...) end local function ulen(...) ulen = require(string_utilities_module).len return ulen(...) end local function ulower(...) ulower = require(string_utilities_module).lower return ulower(...) end local function umatch(...) umatch = require(string_utilities_module).match return umatch(...) end local function uupper(...) uupper = require(string_utilities_module).upper return uupper(...) end local function track(page) debug_track("languages/" .. page) return true end local function normalize_code(code) return load_data(languages_data_module).aliases[code] or code end local function check_inputs(self, check, default, ...) local n = select("#", ...) if n == 0 then return false end local ret = check(self, (...)) if ret ~= nil then return ret elseif n > 1 then local inputs = {...} for i = 2, n do ret = check(self, inputs[i]) if ret ~= nil then return ret end end end return default end local function make_link(self, target, display) local prefix, main if self:getFamilyCode() == "qfa-sub" then prefix, main = display:match("^(the )(.*)") if not prefix then prefix, main = display:match("^(a )(.*)") end end return (prefix or "") .. "[[" .. target .. "|" .. (main or display) .. "]]" end -- Convert risky characters to HTML entities, which minimizes interference once returned (e.g. for "sms:a", "<!-- -->" etc.). local function escape_risky_characters(text) -- Spacing characters in isolation generally need to be escaped in order to be properly processed by the MediaWiki -- software. if umatch(text, "^%s*$") then return encode_entities(text, text) end return encode_entities(text, "!#%&*+/:;<=>?@[\\]_{|}") end -- Temporarily convert various formatting characters to PUA to prevent them from being disrupted by the substitution process. local function doTempSubstitutions(text, subbedChars, keepCarets, noTrim) -- Clone so that we don't insert any extra patterns into the table in package.loaded. For some reason, using require -- seems to keep memory use down; probably because the table is always cloned. local patterns = shallow_copy(require(languages_data_patterns_module)) if keepCarets then insert(patterns, "((\\+)%^)") insert(patterns, "((%^))") end -- Ensure any whitespace at the beginning and end is temp substituted, to prevent it from being accidentally -- trimmed. We only want to trim any final spaces added during the substitution process (e.g. by a module), which -- means we only do this during the first round of temp substitutions. if not noTrim then insert(patterns, "^([\128-\191\244]*(%s+))") insert(patterns, "((%s+)[\128-\191\244]*)$") end -- Pre-substitution, of "[[" and "]]", which makes pattern matching more accurate. text = gsub(text, "%f[%[]%[%[", "\1"):gsub("%f[%]]%]%]", "\2") local i = #subbedChars for _, pattern in ipairs(patterns) do -- Patterns ending in \0 stand are for things like "[[" or "]]"), so the inserted PUA are treated as breaks -- between terms by modules that scrape info from pages. local term_divider pattern = gsub(pattern, "%z$", function(divider) term_divider = divider == "\0" return "" end) text = gsub(text, pattern, function(...) local m = {...} local m1New = m[1] for k = 2, #m do local n = i + k - 1 subbedChars[n] = m[k] local byte2 = floor(n / 4096) % 64 + (term_divider and 128 or 136) local byte3 = floor(n / 64) % 64 + 128 local byte4 = n % 64 + 128 m1New = gsub(m1New, pattern_escape(m[k]), "\244" .. char(byte2) .. char(byte3) .. char(byte4), 1) end i = i + #m - 1 return m1New end) end text = gsub(text, "\1", "%[%["):gsub("\2", "%]%]") return text, subbedChars end -- Reinsert any formatting that was temporarily substituted. local function undoTempSubstitutions(text, subbedChars) for i = 1, #subbedChars do local byte2 = floor(i / 4096) % 64 + 128 local byte3 = floor(i / 64) % 64 + 128 local byte4 = i % 64 + 128 text = gsub(text, "\244[" .. char(byte2) .. char(byte2+8) .. "]" .. char(byte3) .. char(byte4), replacement_escape(subbedChars[i])) end text = gsub(text, "\1", "%[%["):gsub("\2", "%]%]") return text end -- Check if the raw text is an unsupported title, and if so return that. Otherwise, remove HTML entities. We do the -- pre-conversion to avoid loading the unsupported title list unnecessarily. local function checkNoEntities(text) local textNoEnc = decode_entities(text) if textNoEnc ~= text and load_data(links_data_module).unsupported_titles[text] then return text else return textNoEnc end end -- If no script object is provided (or if it's invalid or None), get one. local function checkScript(text, self, sc) if not check_object("script", true, sc) or sc:getCode() == "None" then return self:findBestScript(text) end return sc end local function normalize(text, sc) text = sc:fixDiscouragedSequences(text) return sc:toFixedNFD(text) end --[=[ Subfunction of iterateSectionSubstitutions(). Process an individual chunk of text according to the specifications in `substitution_data`. The input parameters are all as in the documentation of iterateSectionSubstitutions() except for `recursed`, which is set to true if we called ourselves recursively to process a script-specific setting or script-wide fallback. Returns two values: the processed text and the actual substitution data used to do the substitutions (same as the `actual_substitution_data` return value to iterateSectionSubstitutions()). ]=] local function doSubstitutions(self, text, sc, substitution_data, data_field, function_name, recursed) -- BE CAREFUL in this function because the value at any level can be `false`, which causes no processing to be done -- and blocks any further fallback processing. local actual_substitution_data = substitution_data -- If there are language-specific substitutes given in the data module, use those. if type(substitution_data) == "table" then -- If a script is specified, run this function with the script-specific data before continuing. local sc_code = sc:getCode() local has_substitution_data = false if substitution_data[sc_code] ~= nil then has_substitution_data = true if substitution_data[sc_code] then text, actual_substitution_data = doSubstitutions(self, text, sc, substitution_data[sc_code], data_field, function_name, true) end -- Hant, Hans and Hani are usually treated the same, so add a special case to avoid having to specify each one -- separately. elseif sc_code:match("^Han") and substitution_data.Hani ~= nil then has_substitution_data = true if substitution_data.Hani then text, actual_substitution_data = doSubstitutions(self, text, sc, substitution_data.Hani, data_field, function_name, true) end -- Substitution data with key 1 in the outer table may be given as a fallback. elseif substitution_data[1] ~= nil then has_substitution_data = true if substitution_data[1] then text, actual_substitution_data = doSubstitutions(self, text, sc, substitution_data[1], data_field, function_name, true) end end -- Iterate over all strings in the "from" subtable, and gsub with the corresponding string in "to". We work with -- the NFD decomposed forms, as this simplifies many substitutions. if substitution_data.from then has_substitution_data = true for i, from in ipairs(substitution_data.from) do -- Normalize each loop, to ensure multi-stage substitutions work correctly. text = sc:toFixedNFD(text) text = ugsub(text, sc:toFixedNFD(from), substitution_data.to[i] or "") end end if substitution_data.remove_diacritics then has_substitution_data = true text = sc:toFixedNFD(text) -- Convert exceptions to PUA. local remove_exceptions, substitutes = substitution_data.remove_exceptions if remove_exceptions then substitutes = {} local i = 0 for _, exception in ipairs(remove_exceptions) do exception = sc:toFixedNFD(exception) text = ugsub(text, exception, function(m) i = i + 1 local subst = u(0x80000 + i) substitutes[subst] = m return subst end) end end -- Strip diacritics. text = ugsub(text, "[" .. substitution_data.remove_diacritics .. "]", "") -- Convert exceptions back. if remove_exceptions then text = text:gsub("\242[\128-\191]*", substitutes) end end if not has_substitution_data and sc._data[data_field] then -- If language-specific sort key (etc.) is nil, fall back to script-wide sort key (etc.). text, actual_substitution_data = doSubstitutions(self, text, sc, sc._data[data_field], data_field, function_name, true) end elseif type(substitution_data) == "string" then -- If there is a dedicated function module, use that. local module = safe_require("Module:" .. substitution_data) if module then -- TODO: translit functions should take objects, not codes. -- TODO: translit functions should be called with form NFD. if function_name == "tr" then if not module[function_name] then error(("Internal error: Module [[%s]] has no function named 'tr'"):format(substitution_data)) end text = module[function_name](text, self._code, sc:getCode()) elseif function_name == "stripDiacritics" then -- FIXME, get rid of this arm after renaming makeEntryName -> stripDiacritics. if module[function_name] then text = module[function_name](sc:toFixedNFD(text), self, sc) elseif module.makeEntryName then text = module.makeEntryName(sc:toFixedNFD(text), self, sc) else error(("Internal error: Module [[%s]] has no function named 'stripDiacritics' or 'makeEntryName'" ):format(substitution_data)) end else if not module[function_name] then error(("Internal error: Module [[%s]] has no function named '%s'"):format( substitution_data, function_name)) end text = module[function_name](sc:toFixedNFD(text), self, sc) end else error("Substitution data '" .. substitution_data .. "' does not match an existing module.") end elseif substitution_data == nil and sc._data[data_field] then -- If language-specific sort key (etc.) is nil, fall back to script-wide sort key (etc.). text, actual_substitution_data = doSubstitutions(self, text, sc, sc._data[data_field], data_field, function_name, true) end -- Don't normalize to NFC if this is the inner loop or if a module returned nil. if recursed or not text then return text, actual_substitution_data end -- Fix any discouraged sequences created during the substitution process, and normalize into the final form. return sc:toFixedNFC(sc:fixDiscouragedSequences(text)), actual_substitution_data end --[=[ Split the text into sections, based on the presence of temporarily substituted formatting characters, then iterate over each section to apply substitutions (e.g. transliteration or diacritic stripping). This avoids putting PUA (Private Use Area) characters through language-specific modules, which may be unequipped for them. This function is passed the following values: * `self` (the Language object); * `text` (the text to process); * `sc` (the script of the text, which must be specified; callers should call checkScript() as needed to autodetect the script of the text if not given explicitly by the user); * `subbedChars` (an array of the same length as the text, indicating which characters have been substituted and by what, or {nil} if no substitutions are to happen); * `keepCarets` (DOCUMENT ME); * `substitution_data` (the data indicating which substitutions to apply, taken directly from `data_field` in the language's data structure in a submodule of [[Module:languages/data]]); * `data_field` (the field from which `substitution_data` was fetched, such as {"sort_key"} or {"strip_diacritics"}); * `function_name` (the name of the function to call to do the substitution, in case `substitution_data` specifies a module to do the substitution); * `notrim` (don't trim whitespace at the edges of `text`; set when computing the sort key, because whitespace at the beginning of a sort key is significant and causes the resulting page to be sorted at the beginning of the category it's in). Return three values: # the processed text; # the value of `subbedChars` that was passed in, possibly modified with additional character substitutions; will be {nil} if {nil} was passed in; # the actual substitution data that was used to apply substitutions to `text`; this may be different from the value of `substitution_data` passed in if that value recursively specified script-specific substitutions or if no substitution data could be found in the language-specific data (e.g. {nil} was passed in or a structure was passed in that had no setting for the script given in `sc`), but a script-wide fallback value was set; currently it is only used by {makeSortKey()}. ]=] local function iterateSectionSubstitutions(self, text, sc, subbedChars, keepCarets, substitution_data, data_field, function_name, notrim) local sections -- See [[Module:languages/data]]. if not find(text, "\244") or load_data(languages_data_module).substitution[self._code] == "cont" then sections = {text} else sections = split(text, "\244[\128-\143][\128-\191]*", true) end local actual_substitution_data for _, section in ipairs(sections) do -- Don't bother processing empty strings or whitespace (which may also not be handled well by dedicated -- modules). if gsub(section, "%s+", "") ~= "" then local sub, this_actual_substitution_data = doSubstitutions(self, section, sc, substitution_data, data_field, function_name) actual_substitution_data = this_actual_substitution_data -- Second round of temporary substitutions, in case any formatting was added by the main substitution -- process. However, don't do this if the section contains formatting already (as it would have had to have -- been escaped to reach this stage, and therefore should be given as raw text). if sub and subbedChars then local noSub for _, pattern in ipairs(require(languages_data_patterns_module)) do if match(section, pattern .. "%z?") then noSub = true end end if not noSub then sub, subbedChars = doTempSubstitutions(sub, subbedChars, keepCarets, true) end end if not sub then text = sub break end text = sub and gsub(text, pattern_escape(section), replacement_escape(sub), 1) or text end end if not notrim then -- Trim, unless there are only spacing characters, while ignoring any final formatting characters. -- Do not trim sort keys because spaces at the beginning are significant. text = text and text:gsub("^([\128-\191\244]*)%s+(%S)", "%1%2"):gsub("(%S)%s+([\128-\191\244]*)$", "%1%2") or nil end return text, subbedChars, actual_substitution_data end -- Process carets (and any escapes). Default to simple removal, if no pattern/replacement is given. local function processCarets(text, pattern, repl) local rep repeat text, rep = gsub(text, "\\\\(\\*^)", "\3%1") until rep == 0 return (text:gsub("\\^", "\4") :gsub(pattern or "%^", repl or "") :gsub("\3", "\\") :gsub("\4", "^")) end local function hasUnescapedCarets(text) return processCarets(text, "%^", "\1"):find("\1", 1, true) ~= nil end local function assertCaretCapitalizationAllowed(text, sc) if sc:forbidsCaretCapitalization() and hasUnescapedCarets(text) then error(("Caret (^) cannot be used to capitalize transliterations for the %s script " .. "(see caret_capitalization in [[Module:scripts/data]]).") :format(sc:getCanonicalName()), 2) end end -- Remove carets if they are used to capitalize parts of transliterations (unless they have been escaped). local function removeCarets(text, sc) if text:find("^", 1, true) then assertCaretCapitalizationAllowed(text, sc) if sc:hasCaretCapitalization() then return processCarets(text) end end return text end local Language = {} --[==[ Return the language code of the language. Example: {fr"} for French. ]==] function Language:getCode() return self._code end --[==[ Return the canonical name of the language. This is the name used to represent that language on Wiktionary, and is guaranteed to be unique to that language alone. Example: {"French"} for French. ]==] function Language:getCanonicalName() local name = self._name if name == nil then name = self._data[1] self._name = name end return name end --[==[ Return the display form of the language. The display form of a language, family or script is the form it takes when appearing as the <code><var>source</var></code> in categories such as <code>English terms derived from <var>source</var></code> or <code>English given names from <var>source</var></code>, and is also the displayed text in `##makeCategoryLink()` links. For full and etymology-only languages, this is the same as the canonical name, but for families, it reads <code>"<var>name</var> languages"</code> (e.g. {"Indo-Iranian languages"}), and for scripts, it reads <code>"<var>name</var> script"</code> (e.g. {"Arabic script"}). ]==] function Language:getDisplayForm() local form = self._displayForm if form == nil then form = self:getCanonicalName() -- Add article and " substrate" to substrates that lack them. if self:getFamilyCode() == "qfa-sub" then if not (sub(form, 1, 4) == "the " or sub(form, 1, 2) == "a ") then form = "a " .. form end if not match(form, " [Ss]ubstrate") then form = form .. " substrate" end end self._displayForm = form end return form end --[==[ Return the value which should be used in the HTML `lang=` attribute for tagged text in the language. ]==] function Language:getHTMLAttribute(sc, region) local code = self._code if not find(code, "-", 1, true) then return code .. "-" .. sc:getCode() .. (region and "-" .. region or "") end local parent = self:getParent() region = region or match(code, "%f[%u][%u-]+%f[%U]") if parent then return parent:getHTMLAttribute(sc, region) end -- TODO: ISO family codes can also be used. return "mis-" .. sc:getCode() .. (region and "-" .. region or "") end --[==[ Return a list of the aliases that the language is known by, excluding the canonical name. Aliases are synonyms for the language in question. The names are not guaranteed to be unique, in that sometimes more than one language is known by the same name. Example: { {"High German", "New High German", "Deutsch"}} for {{lnl|de}}. ]==] function Language:getAliases() self:loadInExtraData() return require(language_like_module).getAliases(self) end --[==[ Return a list of the known subvarieties of a given language, excluding subvarieties that have been given explicit etymology-only language codes. The names are not guaranteed to be unique, in that sometimes a given name refers to a subvariety of more than one language. Example: { {"Southern Aymara", "Central Aymara"}} for {{lnl|ay}}. Note that the returned value can have nested tables in it, when a subvariety goes by more than one name. Example: { {"North Azerbaijani", "South Azerbaijani", {"Afshar", "Afshari", "Afshar Azerbaijani", "Afchar"}, {"Qashqa'i", "Qashqai", "Kashkay"}, "Sonqor"}} for {{lnl|az}}. Here, for example, Afshar, Afshari, Afshar Azerbaijani and Afchar all refer to the same subvariety, whose preferred name is Afshar (the one listed first). To avoid a return value with nested tables in it, specify a non-{nil} value for the `flatten` parameter; in that case, the return value would be { {"North Azerbaijani", "South Azerbaijani", "Afshar", "Afshari", "Afshar Azerbaijani", "Afchar", "Qashqa'i", "Qashqai", "Kashkay", "Sonqor"}}. ]==] function Language:getVarieties(flatten) self:loadInExtraData() return require(language_like_module).getVarieties(self, flatten) end --[==[ Return a table of the "other names" that the language is known by, which are listed in the `other_names` field in the language's extra-data module. It should be noted that the `other_names` field itself is deprecated, and entries listed there should eventually be moved to either `aliases` or `varieties`, or removed if they refer to larger entities which the language in question is actually a part of. ]==] function Language:getOtherNames() -- To be eventually removed, once there are no more uses of the `other_names` field. self:loadInExtraData() return require(language_like_module).getOtherNames(self) end --[==[ Return a combined table of the canonical name, aliases, varieties and other names of a given language. ]==] function Language:getAllNames() self:loadInExtraData() return require(language_like_module).getAllNames(self) end --[==[ Return a table of types as a set (with the types as keys). The possible types are * {language}: This is a language, either full or etymology-only. * {full}: This is a "full" (not etymology-only) language, i.e. the union of {regular}, {reconstructed} and {appendix-constructed}. Note that the types {full} and {etymology-only} also exist for families, so if you want to check specifically for a full language and you have an object that might be a family, you should use {hasType("language", "full")} and not simply {hasType("full")}. * {etymology-only}: This is an etymology-only (not full) language, whose parent is another etymology-only language or a full language. Note that the types {full} and {etymology-only} also exist for families, so if you want to check specifically for an etymology-only language and you have an object that might be a family, you should use {hasType("language", "etymology-only")} and not simply {hasType("etymology-only")}. * {regular}: This indicates a full language that is attested according to [[WT:CFI]] and therefore permitted in the main namespace. There may also be reconstructed terms for the language, which are placed in the {Reconstruction} namespace and must be prefixed with `*` to indicate a reconstruction. Most full languages are natural (not constructed) languages, but a few constructed languages (e.g. Esperanto and Volapük, among others) are also allowed in the mainspace and considered regular languages. * {reconstructed}: This language is not attested according to [[WT:CFI]], and therefore is allowed only in the {Reconstruction} namespace. All terms in this language are reconstructed, and must be prefixed with `*`. Languages such as Proto-Indo-European and Proto-Germanic are in this category. '''Exception:''' Reconstructed languages can have entries in the mainspace using the ''anti-asterisk'' feature. This requires that both the headword for the mainspace term (as specified using {{para|head}}) and links to the term are prefixed with the anti-asterisk indicator `!!`. * {appendix-constructed}: This language is attested but does not meet the additional requirements set out for constructed languages ([[WT:CFI#Constructed languages]]). Its entries must therefore be in the Appendix namespace, but they are not reconstructed and therefore should not have * prefixed in links. ]==] function Language:getTypes() local types = self._types if types == nil then types = {language = true} if self:getFullCode() == self._code then types.full = true else types["etymology-only"] = true end for t in gmatch(self._data.type, "[^,]+") do types[t] = true end self._types = types end return types end --[==[ Given a list of types as strings, return {true} if the language has all of them. ]==] function Language:hasType(...) Language.hasType = require(language_like_module).hasType return self:hasType(...) end --[==[ Return a list containing `WikimediaLanguage` objects (see [[Module:wikimedia languages]]), which represent languages and their codes as they are used in Wikimedia projects for interwiki linking and such. More than one object may be returned, as a single Wiktionary language may correspond to multiple Wikimedia languages. For example, Wiktionary's single code `sh` (Serbo-Croatian) maps to four Wikimedia codes: `sh` (Serbo-Croatian), `bs` (Bosnian), `hr` (Croatian) and `sr` (Serbian). The code for the Wikimedia language is retrieved from the `wikimedia_codes` property in the data modules. If that property is not present, the code of the current language is used. If none of the available codes is actually a valid Wikimedia code, an empty list is returned. ]==] function Language:getWikimediaLanguages() local wm_langs = self._wikimediaLanguageObjects if wm_langs == nil then local codes = self:getWikimediaLanguageCodes() wm_langs = {} for i = 1, #codes do wm_langs[i] = get_wikimedia_lang(codes[i]) end self._wikimediaLanguageObjects = wm_langs end return wm_langs end function Language:getWikimediaLanguageCodes() local wm_langs = self._wikimediaLanguageCodes if wm_langs == nil then wm_langs = self._data.wikimedia_codes if wm_langs then wm_langs = split(wm_langs, ",", true, true) else local code = self._code if is_known_language_tag(code) then wm_langs = {code} else -- Inherit, but only if no codes are specified in the data *and* -- the language code isn't a valid Wikimedia language code. local parent = self:getParent() wm_langs = parent and parent:getWikimediaLanguageCodes() or {} end end self._wikimediaLanguageCodes = wm_langs end return wm_langs end --[==[ Return the name of the Wikipedia article for the language. `project` specifies the language and project to retrieve the article from, defaulting to {"enwiki"} for the English Wikipedia. Normally if specified it should be the project code for a specific-language Wikipedia e.g. "zhwiki" for the Chinese Wikipedia, but it can be any project, including non-Wikipedia ones. If the project is the English Wikipedia and the property {wikipedia_article} is present in the data module it will be used first. In all other cases, a sitelink will be generated from {:getWikidataItem} (if set). The resulting value (or lack of value) is cached so that subsequent calls are fast. If no value could be determined, and `noCategoryFallback` is {false}, `##Language:getCategoryName` is used as fallback; otherwise, {nil} is returned. Note that if `noCategoryFallback` is {nil} or omitted, it defaults to {false} if the project is the English Wikipedia, otherwise to {true}. In other words, under normal circumstances, if the English Wikipedia article couldn't be retrieved, the return value will fall back to a link to the language's category, but this won't normally happen for any other project. ]==] function Language:getWikipediaArticle(noCategoryFallback, project) Language.getWikipediaArticle = require(language_like_module).getWikipediaArticle return self:getWikipediaArticle(noCategoryFallback, project) end function Language:makeWikipediaLink() return make_link(self, "w:" .. self:getWikipediaArticle(), self:getCanonicalName()) end --[==[ Return the name of the Wikimedia Commons category page for the language. ]==] function Language:getCommonsCategory() Language.getCommonsCategory = require(language_like_module).getCommonsCategory return self:getCommonsCategory() end --[==[ Return the Wikidata item ID for the language or {nil}. This corresponds to the the second field in the data modules. ]==] function Language:getWikidataItem() Language.getWikidataItem = require(language_like_module).getWikidataItem return self:getWikidataItem() end --[==[ Return a list of `Script` objects for all scripts that the language is written in. See [[Module:scripts]]. ]==] function Language:getScripts() local scripts = self._scriptObjects if scripts == nil then local codes = self:getScriptCodes() if codes[1] == "All" then scripts = load_data(scripts_data_module) else scripts = {} for i = 1, #codes do scripts[i] = get_script(codes[i]) end end self._scriptObjects = scripts end return scripts end --[==[ Return the list of script codes in the language's data file. ]==] function Language:getScriptCodes() local scripts = self._scriptCodes if scripts == nil then scripts = self._data[4] if scripts then local codes, n = {}, 0 for code in gmatch(scripts, "[^,]+") do n = n + 1 -- Special handling of "Hants", which represents "Hani", "Hant" and "Hans" collectively. if code == "Hants" then codes[n] = "Hani" codes[n + 1] = "Hant" codes[n + 2] = "Hans" n = n + 2 else codes[n] = code end end scripts = codes else scripts = {"None"} end self._scriptCodes = scripts end return scripts end --[==[ Given some text, iterate through the scripts of a given language trying to find the script that best matches the text. Return a {Script} object representing the script. If no match is found at all, it returns the {None} script object. ]==] function Language:findBestScript(text, forceDetect) if not text or text == "" or text == "-" then return get_script("None") end -- Differs from table returned by getScriptCodes, as Hants is not normalized into its constituents. local codes = self._bestScriptCodes if codes == nil then codes = self._data[4] codes = codes and split(codes, ",", true, true) or {"None"} self._bestScriptCodes = codes end local first_sc = codes[1] if first_sc == "All" then return find_best_script_without_lang(text) end local codes_len = #codes if not (forceDetect or first_sc == "Hants" or codes_len > 1) then first_sc = get_script(first_sc) local charset = first_sc.characters return charset and umatch(text, "[" .. charset .. "]") and first_sc or get_script("None") end -- Remove all formatting characters. text = get_plaintext(text) -- Remove all spaces and any ASCII punctuation. Some non-ASCII punctuation is script-specific, so can't be removed. text = ugsub(text, "[%s!\"#%%&'()*,%-./:;?@[\\%]_{}]+", "") if #text == 0 then return get_script("None") end -- Try to match every script against the text, -- and return the one with the most matching characters. local bestcount, bestscript, length = 0 for i = 1, codes_len do local sc = codes[i] -- Special case for "Hants", which is a special code that represents whichever of "Hant" or "Hans" best matches, -- or "Hani" if they match equally. This avoids having to list all three. In addition, "Hants" will be treated -- as the best match if there is at least one matching character, under the assumption that a Han script is -- desirable in terms that contain a mix of Han and other scripts (not counting those which use Jpan or Kore). if sc == "Hants" then local Hani = get_script("Hani") if not Hant_chars then Hant_chars = load_data("Module:zh/data/ts") Hans_chars = load_data("Module:zh/data/st") end local t, s, found = 0, 0 -- This is faster than using mw.ustring.gmatch directly. for ch in gmatch((ugsub(text, "[" .. Hani.characters .. "]", "\255%0")), "\255(.[\128-\191]*)") do found = true if Hant_chars[ch] then t = t + 1 if Hans_chars[ch] then s = s + 1 end elseif Hans_chars[ch] then s = s + 1 else t, s = t + 1, s + 1 end end if found then if t == s then return Hani end return get_script(t > s and "Hant" or "Hans") end else sc = get_script(sc) if not length then length = ulen(text) end -- Count characters by removing everything in the script's charset and comparing to the original length. local charset = sc.characters local count = charset and length - ulen((ugsub(text, "[" .. charset .. "]+", ""))) or 0 if count >= length then return sc elseif count > bestcount then bestcount = count bestscript = sc end end end -- Return best matching script, or otherwise None. return bestscript or get_script("None") end --[==[ Return a `Family` object for the language family that the language belongs to. See [[Module:families]]. ]==] function Language:getFamily() local family = self._familyObject if family == nil then family = self:getFamilyCode() -- If the value is nil, it's cached as false. family = family and get_family(family) or false self._familyObject = family end return family or nil end --[==[ Return the family code in the language's data file. ]==] function Language:getFamilyCode() local family = self._familyCode if family == nil then -- If the value is nil, it's cached as false. family = self._data[3] or false self._familyCode = family end return family or nil end function Language:getFamilyName() local family = self._familyName if family == nil then family = self:getFamily() -- If the value is nil, it's cached as false. family = family and family:getCanonicalName() or false self._familyName = family end return family or nil end do local function check_family(self, family) if type(family) == "table" then family = family:getCode() end if self:getFamilyCode() == family then return true end local self_family = self:getFamily() if self_family:inFamily(family) then return true -- If the family isn't a real family (e.g. creoles) check any ancestors. elseif self_family:inFamily("qfa-not") then local ancestors = self:getAncestors() for _, ancestor in ipairs(ancestors) do if ancestor:inFamily(family) then return true end end end end --[==[ Check whether the language belongs to `family` (which can be a family code or object). A list of objects can be given in place of `family`; in that case, return true if the language belongs to any of the specified families. Note that some languages (in particular, certain creoles) can have multiple immediate ancestors potentially belonging to different families; in that case, return true if the language belongs to any of the specified families. ]==] function Language:inFamily(...) if self:getFamilyCode() == nil then return false end return check_inputs(self, check_family, false, ...) end end function Language:getParent() local parent = self._parentObject if parent == nil then parent = self:getParentCode() -- If the value is nil, it's cached as false. parent = parent and get_by_code(parent, nil, true, true) or false self._parentObject = parent end return parent or nil end function Language:getParentCode() local parent = self._parentCode if parent == nil then -- If the value is nil, it's cached as false. parent = self._data.parent or false self._parentCode = parent end return parent or nil end function Language:getParentName() local parent = self._parentName if parent == nil then parent = self:getParent() -- If the value is nil, it's cached as false. parent = parent and parent:getCanonicalName() or false self._parentName = parent end return parent or nil end function Language:getParentChain() local chain = self._parentChain if chain == nil then chain = {} local parent, n = self:getParent(), 0 while parent do n = n + 1 chain[n] = parent parent = parent:getParent() end self._parentChain = chain end return chain end --[==[ Return a list of the pseudo-families (if any) that a language belongs to. The returned value is a list of Family objects. Pseudo-families are groupings of languages that are not clades but share some geographic, ethnic or other characteristic (e.g. [[:Category:Papuan languages]], [[:Category:Jewish languages]]). ]==] function Language:getPseudoFamilies() self:loadInExtraData() return require(language_like_module).getPseudoFamilies(self) end --[==[ Return a list of the pseudo-family codes (if any) that a language belongs to. The returned value is a list of strings (family codes). See `##Language:getPseudoFamilies`. ]==] function Language:getPseudoFamilyCodes() local codes = {} for _, family in ipairs(self:getPseudoFamilies()) do insert(codes, family:getCode()) end return codes end do local function check_lang(self, lang) for _, parent in ipairs(self:getParentChain()) do if (type(lang) == "string" and lang or lang:getCode()) == parent:getCode() then return true end end end function Language:hasParent(...) return check_inputs(self, check_lang, false, ...) end end --[==[ If the language is etymology-only, iterate through parents until a full language or family is found, and the corresponding object is returned. If the language is a full language, return that language. ]==] function Language:getFull() local full = self._fullObject if full == nil then full = self:getFullCode() full = full == self._code and self or get_by_code(full) self._fullObject = full end return full end --[==[ If the language is an etymology-only language, iterate through parents until a full language or family is found, and the corresponding code is returned. If the language is a full language, return that language's code. ]==] function Language:getFullCode() return self._fullCode or self._code end --[==[ If the language is an etymology-only language, iterate through parents until a full language or family is found, and the corresponding canonical name is returned. If the language is a full language, return that language's canonical name. ]==] function Language:getFullName() local full = self._fullName if full == nil then full = self:getFull():getCanonicalName() self._fullName = full end return full end --[==[ Return a list of {Language} objects for all languages that this language is directly descended from. Generally this is only a single language, but creoles, pidgins and mixed languages can have multiple ancestors. ]==] function Language:getAncestors() local ancestors = self._ancestorObjects if ancestors == nil then ancestors = {} local ancestor_codes = self:getAncestorCodes() if #ancestor_codes > 0 then for _, ancestor in ipairs(ancestor_codes) do insert(ancestors, get_by_code(ancestor, nil, true)) end else local fam = self:getFamily() local protoLang = fam and fam:getProtoLanguage() or nil -- For the cases where the current language is the proto-language -- of its family, or an etymology-only language that is ancestral to that -- proto-language, we need to step up a level higher right from the -- start. if protoLang and ( protoLang:getCode() == self._code or (self:hasType("etymology-only") and protoLang:hasAncestor(self)) ) then fam = fam:getFamily() protoLang = fam and fam:getProtoLanguage() or nil end while not protoLang and not (not fam or fam:getCode() == "qfa-not") do fam = fam:getFamily() protoLang = fam and fam:getProtoLanguage() or nil end insert(ancestors, protoLang) end self._ancestorObjects = ancestors end return ancestors end do -- Avoid a language being its own ancestor via class inheritance. We only need to check for this if the language has -- inherited an ancestor table from its parent, because we never want to drop ancestors that have been explicitly -- set in the data. Recursively iterate over ancestors until we either find self or run out. If self is found, -- return true. local function check_ancestor(self, lang) local codes = lang:getAncestorCodes() if not codes then return nil end for i = 1, #codes do local code = codes[i] if code == self._code then return true end local anc = get_by_code(code, nil, true) if check_ancestor(self, anc) then return true end end end --[==[ Return a list of `Language` codes for all languages that this language is directly descended from. Generally this is only a single language, but creoles, pidgins and mixed languages can have multiple ancestors. ]==] function Language:getAncestorCodes() if self._ancestorCodes then return self._ancestorCodes end local data = self._data local codes = data.ancestors if codes == nil then codes = {} self._ancestorCodes = codes return codes end codes = split(codes, ",", true, true) self._ancestorCodes = codes -- If there are no codes or the ancestors weren't inherited data, there's nothing left to check. if #codes == 0 or self:getData(false, "raw").ancestors ~= nil then return codes end local i = 1 while i <= #codes do if check_ancestor(self, self) then remove(codes, i) else i = i + 1 end end return codes end end --[==[ Given a list of language objects or codes, return true if at least one of them is an ancestor. This includes any etymology-only children of that ancestor. If the language's ancestor(s) are etymology-only languages, it will also return true for those language parent(s) (e.g. if Vulgar Latin is the ancestor, it will also return true for its parent, Latin). However, a parent is excluded from this if the ancestor is also ancestral to that parent (e.g. if Classical Persian is the ancestor, Persian would return {false}, because Classical Persian is also ancestral to Persian). ]==] function Language:hasAncestor(...) local function iterateOverAncestorTree(node, func, parent_check) local ancestors = node:getAncestors() local ancestorsParents = {} for _, ancestor in ipairs(ancestors) do -- When checking the parents of the other language, and the ancestor is also a parent, skip to the next -- ancestor, so that we exclude any etymology-only children of that parent that are not directly related -- (see below). local ret = (parent_check or not node:hasParent(ancestor)) and func(ancestor) or iterateOverAncestorTree(ancestor, func, parent_check) if ret then return ret end end -- Check the parents of any ancestors. We don't do this if checking the parents of the other language, so that -- we exclude any etymology-only children of those parents that are not directly related (e.g. if the ancestor -- is Vulgar Latin and we are checking New Latin, we want it to return false because they are on different -- ancestral branches. As such, if we're already checking the parent of New Latin (Latin) we don't want to -- compare it to the parent of the ancestor (Latin), as this would be a false positive; it should be one or the -- other). if not parent_check then return nil end for _, ancestor in ipairs(ancestors) do local ancestorParents = ancestor:getParentChain() for _, ancestorParent in ipairs(ancestorParents) do if ancestorParent:getCode() == self._code or ancestorParent:hasAncestor(ancestor) then break else insert(ancestorsParents, ancestorParent) end end end for _, ancestorParent in ipairs(ancestorsParents) do local ret = func(ancestorParent) if ret then return ret end end end local function do_iteration(otherlang, parent_check) -- otherlang can't be self if (type(otherlang) == "string" and otherlang or otherlang:getCode()) == self._code then return false end repeat if iterateOverAncestorTree( self, function(ancestor) return ancestor:getCode() == (type(otherlang) == "string" and otherlang or otherlang:getCode()) end, parent_check ) then return true elseif type(otherlang) == "string" then otherlang = get_by_code(otherlang, nil, true) end otherlang = otherlang:getParent() parent_check = false until not otherlang end local parent_check = true for _, otherlang in ipairs{...} do local ret = do_iteration(otherlang, parent_check) if ret then return true end end return false end do local function construct_node(lang, memo) local branch, ancestors = {lang = lang:getCode()} memo[lang:getCode()] = branch for _, ancestor in ipairs(lang:getAncestors()) do if ancestors == nil then ancestors = {} end insert(ancestors, memo[ancestor:getCode()] or construct_node(ancestor, memo)) end branch.ancestors = ancestors return branch end function Language:getAncestorChain() local chain = self._ancestorChain if chain == nil then chain = construct_node(self, {}) self._ancestorChain = chain end return chain end end function Language:getAncestorChainOld() local chain = self._ancestorChain if chain == nil then chain = {} local step = self while true do local ancestors = step:getAncestors() step = #ancestors == 1 and ancestors[1] or nil if not step then break end insert(chain, step) end self._ancestorChain = chain end return chain end local function fetch_descendants(self, fmt) local descendants, family = {}, self:getFamily() -- Iterate over all three datasets. for _, data in ipairs{ require("Module:languages/code to canonical name"), require("Module:etymology languages/code to canonical name"), require("Module:families/code to canonical name"), } do for code in pairs(data) do local lang = get_by_code(code, nil, true, true) if not lang then error(("Internal error: code '%s' can't be fetched"):format(code)) end -- Test for a descendant. Earlier tests weed out most candidates, while the more intensive tests are only used sparingly. if ( code ~= self._code and -- Not self. lang:inFamily(family) and -- In the same family. ( family:getProtoLanguageCode() == self._code or -- Self is the protolanguage. self:hasDescendant(lang) or -- Full hasDescendant check. (lang:getFullCode() == self._code and not self:hasAncestor(lang)) -- Etymology-only child which isn't an ancestor. ) ) then if fmt == "object" then insert(descendants, lang) elseif fmt == "code" then insert(descendants, code) elseif fmt == "name" then insert(descendants, lang:getCanonicalName()) end end end end return descendants end function Language:getDescendants() local descendants = self._descendantObjects if descendants == nil then descendants = fetch_descendants(self, "object") self._descendantObjects = descendants end return descendants end function Language:getDescendantCodes() local descendants = self._descendantCodes if descendants == nil then descendants = fetch_descendants(self, "code") self._descendantCodes = descendants end return descendants end function Language:getDescendantNames() local descendants = self._descendantNames if descendants == nil then descendants = fetch_descendants(self, "name") self._descendantNames = descendants end return descendants end do local function check_lang(self, lang) if type(lang) == "string" then lang = get_by_code(lang, nil, true) end if lang:hasAncestor(self) then return true end end function Language:hasDescendant(...) return check_inputs(self, check_lang, false, ...) end end local function fetch_children(self, fmt) local m_etym_data = require(etymology_languages_data_module) local self_code, children = self._code, {} for code, lang in pairs(m_etym_data) do local _lang = lang repeat local parent = _lang.parent if parent == self_code then if fmt == "object" then insert(children, get_by_code(code, nil, true)) elseif fmt == "code" then insert(children, code) elseif fmt == "name" then insert(children, lang[1]) end break end _lang = m_etym_data[parent] until not _lang end return children end function Language:getChildren() local children = self._childObjects if children == nil then children = fetch_children(self, "object") self._childObjects = children end return children end function Language:getChildrenCodes() local children = self._childCodes if children == nil then children = fetch_children(self, "code") self._childCodes = children end return children end function Language:getChildrenNames() local children = self._childNames if children == nil then children = fetch_children(self, "name") self._childNames = children end return children end function Language:hasChild(...) local lang = ... if not lang then return false elseif type(lang) == "string" then lang = get_by_code(lang, nil, true) end if lang:hasParent(self) then return true end return self:hasChild(select(2, ...)) end --[==[ Return the name of the main category of that language. Example: {French language"} for French, whose category is at [[:Category:French language]]. Unless optional argument `nocap` is given, the language name at the beginning of the returned value will be capitalized. This capitalization is correct for category names, but not if the language name is lowercase and the returned value of this function is used in the middle of a sentence. ]==] function Language:getCategoryName(nocap) local name = self._categoryName if name == nil then name = self:getCanonicalName() -- If a substrate, omit any leading article. if self:getFamilyCode() == "qfa-sub" then name = name:gsub("^the ", ""):gsub("^a ", "") end -- Only add " language" if a full language. if self:hasType("full") then -- Unless the canonical name already ends with "language", "lect" or their derivatives, add " language". --if not (match(name, "[Ll]anguage$") or match(name, "[Ll]ect$")) then if not (match(name, "^ภาษา") or match(name, "^ภาษณ์")) then name = "ภาษา" .. name end end self._categoryName = name end if nocap then return name end return mw.getContentLanguage():ucfirst(name) end --[==[ Create a link to the category; the link text is the canonical name. ]==] function Language:makeCategoryLink() return make_link(self, ":Category:" .. self:getCategoryName(), self:getDisplayForm()) end function Language:getStandardCharacters(sc) local standard_chars = self._data.standard_chars if type(standard_chars) ~= "table" then return standard_chars elseif sc and type(sc) ~= "string" then check_object("script", nil, sc) sc = sc:getCode() end if (not sc) or sc == "None" then local scripts = {} for _, script in pairs(standard_chars) do insert(scripts, script) end return concat(scripts) end if standard_chars[sc] then return standard_chars[sc] .. (standard_chars[1] or "") end end --[==[ Strip diacritics from display text `text` (in a language-specific fashion), which is in the script `sc`. If `sc` is omitted or {nil}, the script is autodetected. This also strips certain punctuation characters from the end and (in the case of Spanish upside-down question mark and exclamation points) from the beginning; strips any whitespace at the end of the text or between the text and final stripped punctuation characters; and applies some language-specific Unicode normalizations to replace discouraged characters with their prescribed alternatives. Return the stripped text. ]==] function Language:stripDiacritics(text, sc) if (not text) or text == "" then return text end sc = checkScript(text, self, sc) text = normalize(text, sc) -- FIXME, rename makeEntryName to stripDiacritics and get rid of second and third return values -- everywhere local _ text, _, _ = iterateSectionSubstitutions(self, text, sc, nil, nil, self._data.strip_diacritics or self._data.entry_name, "strip_diacritics", "stripDiacritics") text = umatch(text, "^[¿¡]?(.-[^%s%p].-)%s*[؟?!;՛՜ ՞ ՟?!︖︕।॥။၊་།]?$") or text return text end --[==[ Convert a ''logical'' pagename (the pagename as it appears to the user, after diacritics and punctuation have been stripped) to a ''physical'' pagename (the pagename as it appears in the MediaWiki database). Reasons for a difference between the two are (a) unsupported titles such as `[ ]` (with square brackets in them), `#` (pound/hash sign) and `¯\_(ツ)_/¯` (with underscores), as well as overly long titles of various sorts; (b) "mammoth" pages that are split into parts (e.g. `a`, which is split into physical pagenames `a/languages A to L` and `a/languages M to Z`). For almost all purposes, you should work with logical and not physical pagenames. But there are certain use cases that require physical pagenames, such as checking the existence of a page or retrieving a page's contents. `pagename` is the logical pagename to be converted. `is_reconstructed_or_appendix` indicates whether the page is in the `Reconstruction` or `Appendix` namespaces. If it is omitted or has the value {nil}, the pagename is checked for an initial asterisk, and if found, the page is assumed to be a `Reconstruction` page. Setting a value of `false` or `true` to `is_reconstructed_or_appendix` disables this check and allows for mainspace pagenames that begin with an asterisk. ]==] function Language:logicalToPhysical(pagename, is_reconstructed_or_appendix) -- FIXME: This probably shouldn't happen but it happens when makeEntryName() receives nil. if pagename == nil then track("nil-passed-to-logicalToPhysical") return nil end local initial_asterisk if is_reconstructed_or_appendix == nil then local pagename_minus_initial_asterisk initial_asterisk, pagename_minus_initial_asterisk = pagename:match("^(%*)(.*)$") if pagename_minus_initial_asterisk then is_reconstructed_or_appendix = true pagename = pagename_minus_initial_asterisk elseif self:hasType("appendix-constructed") then is_reconstructed_or_appendix = true end end if not is_reconstructed_or_appendix then -- Check if the pagename is a listed unsupported title. local unsupportedTitles = load_data(links_data_module).unsupported_titles if unsupportedTitles[pagename] then return "Unsupported titles/" .. unsupportedTitles[pagename] end end -- Set `unsupported` as true if certain conditions are met. local unsupported -- Check if there's an unsupported character. \239\191\189 is the replacement character U+FFFD, which can't be typed -- directly here due to an abuse filter. Unix-style dot-slash notation is also unsupported, as it is used for -- relative paths in links, as are 3 or more consecutive tildes. Note: match is faster with magic -- characters/charsets; find is faster with plaintext. if ( match(pagename, "[#<>%[%]_{|}]") or find(pagename, "\239\191\189") or match(pagename, "%f[^%z/]%.%.?%f[%z/]") or find(pagename, "~~~") ) then unsupported = true -- If it looks like an interwiki link. elseif find(pagename, ":") then local prefix = gsub(pagename, "^:*(.-):.*", ulower) if ( load_data("Module:data/namespaces")[prefix] or load_data("Module:data/interwikis")[prefix] ) then unsupported = true end end -- Escape unsupported characters so they can be used in titles. ` is used as a delimiter for this, so a raw use of -- it in an unsupported title is also escaped here to prevent interference; this is only done with unsupported -- titles, though, so inclusion won't in itself mean a title is treated as unsupported (which is why it's excluded -- from the earlier test). if unsupported then -- FIXME: This conversion needs to be different for reconstructed pages with unsupported characters. There -- aren't any currently, but if there ever are, we need to fix this e.g. to put them in something like -- Reconstruction:Proto-Indo-European/Unsupported titles/`lowbar``num`. local unsupported_characters = load_data(links_data_module).unsupported_characters pagename = pagename:gsub("[#<>%[%]_`{|}\239]\191?\189?", unsupported_characters) :gsub("%f[^%z/]%.%.?%f[%z/]", function(m) return (gsub(m, "%.", "`period`")) end) :gsub("~~~+", function(m) return (gsub(m, "~", "`tilde`")) end) pagename = "ชื่อไม่รองรับ/" .. pagename elseif not is_reconstructed_or_appendix then -- Check if this is a mammoth page. If so, which subpage should we link to? local m_links_data = load_data(links_data_module) local mammoth_page_type = m_links_data.mammoth_pages[pagename] if mammoth_page_type then local canonical_name = self:getFullName() if canonical_name ~= "ร่วม" and canonical_name ~= "ไทย" then -- ร่วม>ไทย>อื่นๆ local this_subpage local L2_sort_key = get_L2_sort_key(canonical_name) for _, subpage_spec in ipairs(m_links_data.mammoth_page_subpage_types[mammoth_page_type]) do -- unpack() fails utterly on data loaded using mw.loadData() even if offsets are given local subpage, pattern = subpage_spec[1], subpage_spec[2] if pattern == true or L2_sort_key:match(pattern) then this_subpage = subpage break end end if not this_subpage then error(("Internal error: Bad data in mammoth_page_subpage_pages in [[Module:links/data]] for mammoth page %s, type %s; last entry didn't have 'true' in it"):format( pagename, mammoth_page_type)) end pagename = pagename .. "/" .. this_subpage end end end return (initial_asterisk or "") .. pagename end --[==[ Strip the diacritics from a display pagename and convert the resulting logical pagename into a physical pagename. This allows you, for example, to retrieve the contents of the page or check its existence. WARNING: This is deprecated and will be going away. It is a simple composition of `##Language:stripDiacritics` and `##Language:logicalToPhysical`; most callers only want the former, and if you need both, call them both yourself. `text` and `sc` are as in `##Language:stripDiacritics`, and `is_reconstructed_or_appendix` is as in `##Language:logicalToPhysical`. ]==] function Language:makeEntryName(text, sc, is_reconstructed_or_appendix) track("makeEntryName called") return self:logicalToPhysical(self:stripDiacritics(text, sc), is_reconstructed_or_appendix) end --[==[ Generate term alternants (e.g. simplified versions of traditional Chinese terms) using a language-specific method, and return them as a table. If the language provides no method for generating alternants, return a table containing only the input term. ]==] function Language:generateAlternants(text, sc) local generate_alternants = self._data.generate_alternants if generate_alternants == nil then return {text} end sc = checkScript(text, self, sc) return require("Module:" .. generate_alternants).generateAlternants(text, self, sc) end --[==[ Create a sort key for the given stripped text, following the rules appropriate for the language. This removes diacritical marks from the stripped text if they are not considered significant for sorting, and may perform some other changes. Any initial hyphen is also removed, and anything in parentheses is removed as well. The `sort_key` setting for each language in the data modules defines the replacements made by this function, or it gives the name of the module that takes the stripped text and returns a sortkey. ]==] function Language:makeSortKey(text, sc) if (not text) or text == "" then return text end if match(text, "<[^<>]+>") then track("track HTML tag") end -- Remove directionality/control characters, bold, italics, soft hyphens, strip markers and HTML tags. -- FIXME: Partly duplicated with remove_formatting() in [[Module:links]]. text = ugsub(text, "[\194\173\216\156\226\128\142\226\128\143\226\128\170-\226\128\174\226\129\166-\226\129\175]", "") text = text:gsub("('*)'''(.-'*)'''", "%1%2"):gsub("('*)''(.-'*)''", "%1%2") text = gsub(unstrip(text), "<[^<>]+>", "") text = decode_uri(text, "PATH") text = checkNoEntities(text) -- Remove initial hyphens and * unless the term only consists of spacing + punctuation characters. text = ugsub(text, "^([􀀀-􏿽]*)[-־ـ᠊*]+([􀀀-􏿽]*)(.*[^%s%p].*)", "%1%2%3") sc = checkScript(text, self, sc) text = normalize(text, sc) text = removeCarets(text, sc) -- For languages with dotted dotless i, ensure that "İ" is sorted as "i", and "I" is sorted as "ı". if self:hasDottedDotlessI() then text = gsub(text, "I\204\135", "i") -- decomposed "İ" :gsub("I", "ı") text = sc:toFixedNFD(text) end -- Convert to lowercase, make the sortkey, then convert to uppercase. Where the language has dotted dotless i, it is -- usually not necessary to convert "i" to "İ" and "ı" to "I" first, because "I" will always be interpreted as -- conventional "I" (not dotless "İ") by any sorting algorithms, which will have been taken into account by the -- sortkey substitutions themselves. However, if no sortkey substitutions have been specified, then conversion is -- necessary so as to prevent "i" and "ı" both being sorted as "I". -- -- An exception is made for scripts that (sometimes) sort by scraping page content, as that means they are sensitive -- to changes in capitalization (as it changes the target page). if not sc:sortByScraping() then text = ulower(text) end local actual_substitution_data, _ -- Don't trim whitespace here because it's significant at the beginning of a sort key or sort base. text, _, actual_substitution_data = iterateSectionSubstitutions(self, text, sc, nil, nil, self._data.sort_key, "sort_key", "makeSortKey", "notrim") if not sc:sortByScraping() then if self:hasDottedDotlessI() and not actual_substitution_data then text = text:gsub("ı", "I"):gsub("i", "İ") text = sc:toFixedNFC(text) end text = uupper(text) end -- Remove parentheses, as long as they are either preceded or followed by something. text = gsub(text, "(.)[()]+", "%1"):gsub("[()]+(.)", "%1") text = escape_risky_characters(text) return text end --[==[ Create the form used as as a basis for display text and transliteration. FIXME: Rename to correctInputText(). ]==] local function processDisplayText(text, self, sc, keepCarets, keepPrefixes) local subbedChars = {} text, subbedChars = doTempSubstitutions(text, subbedChars, keepCarets) text = decode_uri(text, "PATH") text = checkNoEntities(text) sc = checkScript(text, self, sc) text = normalize(text, sc) text, subbedChars = iterateSectionSubstitutions(self, text, sc, subbedChars, keepCarets, self._data.display_text, "display_text", "makeDisplayText") text = removeCarets(text, sc) -- Remove any interwiki link prefixes (unless they have been escaped or this has been disabled). if find(text, ":") and not keepPrefixes then local rep repeat text, rep = gsub(text, "\\\\(\\*:)", "\3%1") until rep == 0 text = gsub(text, "\\:", "\4") while true do local prefix = gsub(text, "^(.-):.+", function(m1) return (gsub(m1, "\244[\128-\191]*", "")) end) -- Check if the prefix is an interwiki, though ignore capitalised Wiktionary:, which is a namespace. if not prefix or prefix == text or prefix == "Wiktionary" or not (load_data("Module:data/interwikis")[ulower(prefix)] or prefix == "") then break end text = gsub(text, "^(.-):(.*)", function(m1, m2) local ret = {} for subbedChar in gmatch(m1, "\244[\128-\191]*") do insert(ret, subbedChar) end return concat(ret) .. m2 end) end text = gsub(text, "\3", "\\"):gsub("\4", ":") end return text, subbedChars end --[==[ Make the display text (i.e. what is displayed on the page). ]==] function Language:makeDisplayText(text, sc, keepPrefixes) if not text or text == "" then return text end local subbedChars text, subbedChars = processDisplayText(text, self, sc, nil, keepPrefixes) text = escape_risky_characters(text) return undoTempSubstitutions(text, subbedChars) end --[==[ Transliterate the text from the given script into Latin script (see [[Wiktionary:Transliteration and romanization]]). The language must have the `translit` property for this to work; if it is not present, {nil} is returned. The `sc` parameter is handled by the transliteration module, and how it is handled is specific to that module. Some transliteration modules may tolerate {nil} as the script, others require it to be one of the possible scripts that the module can transliterate, and will throw an error if it's not one of them. For this reason, the `sc` parameter should always be provided when writing non-language-specific code. The `module_override` parameter is used to override the default module that is used to provide the transliteration. This is useful in cases where you need to demonstrate a particular module in use, but there is no default module yet, or you want to demonstrate an alternative version of a transliteration module before making it official. It should not be used in real modules or templates, only for testing. All uses of this parameter are tracked by [[Wiktionary:Tracking/languages/module_override]]. '''Known bugs''': * This function assumes {tr(s1) .. tr(s2) == tr(s1 .. s2)}. When this assertion fails, wikitext markups like <nowiki>'''</nowiki> can cause wrong transliterations. * HTML entities like `&amp;apos;`, often used to escape wikitext markups, do not work. ]==] function Language:transliterate(text, sc, module_override) -- If there is no text, or the language doesn't have transliteration data and there's no override, return nil. if not text or text == "" or text == "-" then return text end -- If the script is not transliteratable (and no override is given), return nil. sc = checkScript(text, self, sc) if not (sc:isTransliterated() or module_override) then -- temporary tracking to see if/when this gets triggered track("non-transliterable") track("non-transliterable/" .. self._code) track("non-transliterable/" .. sc:getCode()) track("non-transliterable/" .. sc:getCode() .. "/" .. self._code) return nil end -- Remove any strip markers. text = unstrip(text) -- Do not process the formatting into PUA characters for certain languages. local processed = load_data(languages_data_module).substitution[self._code] ~= "none" -- Get the display text with the keepCarets flag set. local subbedChars if processed then text, subbedChars = processDisplayText(text, self, sc, true) end -- Transliterate (using the module override if applicable). text, subbedChars = iterateSectionSubstitutions(self, text, sc, subbedChars, true, module_override or self._data.translit, "translit", "tr") if not text then return nil end -- Incomplete transliterations return nil. local charset = sc.characters if charset and umatch(text, "[" .. charset .. "]") then -- Remove any characters in Latin, which includes Latin characters also included in other scripts (as these are -- false positives), as well as any PUA substitutions. Anything remaining should only be script code "None" -- (e.g. numerals). local check_text = ugsub(text, "[" .. get_script("Latn").characters .. "􀀀-􏿽]+", "") -- Set none_is_last_resort_only flag, so that any non-None chars will cause a script other than "None" to be -- returned. if find_best_script_without_lang(check_text, true):getCode() ~= "None" then return nil end end if processed then text = escape_risky_characters(text) text = undoTempSubstitutions(text, subbedChars) end -- If allowed for this script, capitalize transliteration letters preceded by ^ and remove the caret. if text and text:find("^", 1, true) then assertCaretCapitalizationAllowed(text, sc) if sc:hasCaretCapitalization() then text = processCarets(text, "%^([\128-\191\244]*%*?)([^\128-\191\244][\128-\191]*)", function(m1, m2) return m1 .. uupper(m2) end) end end -- Track module overrides. if module_override ~= nil then track("module_override") end return text end do local function handle_language_spec(self, spec, sc) local ret = self["_" .. spec] if ret == nil then ret = self._data[spec] if type(ret) == "string" then ret = list_to_set(split(ret, ",", true, true)) end self["_" .. spec] = ret end if type(ret) == "table" then ret = ret[sc:getCode()] end return not not ret end function Language:overrideManualTranslit(sc) return handle_language_spec(self, "override_translit", sc) end function Language:link_tr(sc) return handle_language_spec(self, "link_tr", sc) end end --[==[ Return {true} if the language has a transliteration module, or {false} if it doesn't. ]==] function Language:hasTranslit() return not not self._data.translit end --[==[ Return {true} if the language uses the letters I/ı and İ/i, or {false} if it doesn't.]==] function Language:hasDottedDotlessI() return not not self._data.dotted_dotless_i end function Language:toJSON(opts) local strip_diacritics, strip_diacritics_patterns, strip_diacritics_remove_diacritics = self._data.strip_diacritics if strip_diacritics then if strip_diacritics.from then strip_diacritics_patterns = {} for i, from in ipairs(strip_diacritics.from) do insert(strip_diacritics_patterns, {from = from, to = strip_diacritics.to[i] or ""}) end end strip_diacritics_remove_diacritics = strip_diacritics.remove_diacritics end -- mainCode should only end up non-nil if dontCanonicalizeAliases is passed to make_object(). -- props should either contain zero-argument functions to compute the value, or the value itself. local props = { ancestors = function() return self:getAncestorCodes() end, canonicalName = function() return self:getCanonicalName() end, categoryName = function() return self:getCategoryName("nocap") end, code = self._code, mainCode = self._mainCode, parent = function() return self:getParentCode() end, full = function() return self:getFullCode() end, stripDiacriticsPatterns = strip_diacritics_patterns, stripDiacriticsRemoveDiacritics = strip_diacritics_remove_diacritics, family = function() return self:getFamilyCode() end, aliases = function() return self:getAliases() end, varieties = function() return self:getVarieties() end, otherNames = function() return self:getOtherNames() end, pseudoFamilies = function() return self:getPseudoFamilyCodes() end, scripts = function() return self:getScriptCodes() end, type = function() return keys_to_list(self:getTypes()) end, wikimediaLanguages = function() return self:getWikimediaLanguageCodes() end, wikidataItem = function() return self:getWikidataItem() end, wikipediaArticle = function() return self:getWikipediaArticle(true) end, } local ret = {} for prop, val in pairs(props) do if not opts.skip_fields or not opts.skip_fields[prop] then if type(val) == "function" then ret[prop] = val() else ret[prop] = val end end end -- Use `deep_copy` when returning a table, so that there are no editing restrictions imposed by `mw.loadData`. return opts and opts.lua_table and deep_copy(ret) or to_json(ret, opts) end function export.getDataModuleName(code) local letter = match(code, "^(%l)%l%l?$") return "Module:" .. ( letter == nil and "languages/data/exceptional" or #code == 2 and "languages/data/2" or "languages/data/3/" .. letter ) end get_data_module_name = export.getDataModuleName function export.getExtraDataModuleName(code) return get_data_module_name(code) .. "/extra" end get_extra_data_module_name = export.getExtraDataModuleName do local function make_stack(data) local key_types = { [2] = "unique", aliases = "unique", other_names = "unique", type = "append", varieties = "unique", wikipedia_article = "unique", wikimedia_codes = "unique" } local function __index(self, k) local stack, key_type = getmetatable(self), key_types[k] -- Data that isn't inherited from the parent. if key_type == "unique" then local v = stack[stack[make_stack]][k] if v == nil then local layer = stack[0] if layer then -- Could be false if there's no extra data. v = layer[k] end end return v -- Data that is appended by each generation. elseif key_type == "append" then local parts, offset, n = {}, 0, stack[make_stack] for i = 1, n do local part = stack[i][k] if part == nil then offset = offset + 1 else parts[i - offset] = part end end return offset ~= n and concat(parts, ",") or nil end local n = stack[make_stack] while true do local layer = stack[n] if not layer then -- Could be false if there's no extra data. return nil end local v = layer[k] if v ~= nil then return v end n = n - 1 end end local function __newindex() error("table is read-only") end local function __pairs(self) -- Iterate down the stack, caching keys to avoid duplicate returns. local stack, seen = getmetatable(self), {} local n = stack[make_stack] local iter, state, k, v = pairs(stack[n]) return function() repeat repeat k = iter(state, k) if k == nil then n = n - 1 local layer = stack[n] if not layer then -- Could be false if there's no extra data. return nil end iter, state, k = pairs(layer) end until not (k == nil or seen[k]) -- Get the value via a lookup, as the one returned by the -- iterator will be the raw value from the current layer, -- which may not be the one __index will return for that -- key. Also memoize the key in `seen` (even if the lookup -- returns nil) so that it doesn't get looked up again. -- TODO: store values in `self`, avoiding the need to create -- the `seen` table. The iterator will need to iterate over -- `self` with `next` first to find these on future loops. v, seen[k] = self[k], true until v ~= nil return k, v end end local __ipairs = require(table_module).indexIpairs function make_stack(data) local stack = { data, [make_stack] = 1, -- stores the length and acts as a sentinel to confirm a given metatable is a stack. __index = __index, __newindex = __newindex, __pairs = __pairs, __ipairs = __ipairs, } stack.__metatable = stack return setmetatable({}, stack), stack end return make_stack(data) end local function get_stack(data) local stack = getmetatable(data) return stack and type(stack) == "table" and stack[make_stack] and stack or nil end --[==[ <span style="color: var(--wikt-palette-red,#BA0000)">This function is not for use in entries or other content pages.</span> Return a blob of data about the language. The format of this blob is undocumented, and perhaps unstable; it's intended for things like the module's own unit-tests, which are "close friends" with the module and will be kept up-to-date as the format changes. If `extra` is set, any extra data in the relevant `/extra` module will be included. (Note that it will be included anyway if it has already been loaded into the language object.) If `raw` is set, then the returned data will not contain any data inherited from parent objects. -- Do NOT use these methods! -- All uses should be pre-approved on the talk page! ]==] function Language:getData(extra, raw) if extra then self:loadInExtraData() end local data = self._data -- If raw is not set, just return the data. if not raw then return data end local stack = get_stack(data) -- If there isn't a stack or its length is 1, return the data. Extra data (if any) will be included, as it's -- stored at key 0 and doesn't affect the reported length. if stack == nil then return data end local n = stack[make_stack] if n == 1 then return data end extra = stack[0] -- If there isn't any extra data, return the top layer of the stack. if extra == nil then return stack[n] end -- If there is, return a new stack which has the top layer at key 1 and the extra data at key 0. data, stack = make_stack(stack[n]) stack[0] = extra return data end function Language:loadInExtraData() -- Only full languages have extra data. if not self:hasType("language", "full") then return end local data = self._data -- If there's no stack, create one. local stack = get_stack(self._data) if stack == nil then data, stack = make_stack(data) -- If already loaded, return. elseif stack[0] ~= nil then return end self._data = data -- Load extra data from the relevant module and add it to the stack at key 0, so that the __index and __pairs -- metamethods will pick it up, since they iterate down the stack until they run out of layers. local code = self._code local modulename = get_extra_data_module_name(code) -- No data cached as false. stack[0] = modulename and load_data(modulename)[code] or false end --[==[ Return the name of the module containing the language's data, e.g. [[Module:languages/data/3/k]] for three-letter codes beginning with `k`. ]==] function Language:getDataModuleName() local name = self._dataModuleName if name == nil then name = self:hasType("etymology-only") and etymology_languages_data_module or get_data_module_name(self._mainCode or self._code) self._dataModuleName = name end return name end --[==[ Return the name of the module containing the language's extra data, e.g. [[Module:languages/data/3/k/extra]] for three-letter codes beginning with `k`. ]==] function Language:getExtraDataModuleName() local name = self._extraDataModuleName if name == nil then name = not self:hasType("etymology-only") and get_extra_data_module_name(self._mainCode or self._code) or false self._extraDataModuleName = name end return name or nil end function export.makeObject(code, data, dontCanonicalizeAliases) local data_type = type(data) if data_type ~= "table" then error(("bad argument #2 to 'makeObject' (table expected, got %s)"):format(data_type)) end -- Convert any aliases. local input_code = code code = normalize_code(code) input_code = dontCanonicalizeAliases and input_code or code local parent if data.parent then parent = get_by_code(data.parent, nil, true, true) else parent = Language end parent.__index = parent local lang = {_code = input_code} -- This can only happen if dontCanonicalizeAliases is passed to make_object(). if code ~= input_code then lang._mainCode = code end local parent_data = parent._data if parent_data == nil then -- Full code is the same as the code. lang._fullCode = parent._code or code else -- Copy full code. lang._fullCode = parent._fullCode local stack = get_stack(parent_data) if stack == nil then parent_data, stack = make_stack(parent_data) end -- Insert the input data as the new top layer of the stack. local n = stack[make_stack] + 1 data, stack[n], stack[make_stack] = parent_data, data, n end lang._data = data return setmetatable(lang, parent) end make_object = export.makeObject end --[==[ Find the language whose code matches the one provided. If it exists, return a `Language` object representing the language. Otherwise, return {nil}, unless `paramForError` is given, in which case an error is generated. If `paramForError` is {true}, a generic error message mentioning the bad code is generated; otherwise `paramForError` should be a string or number specifying the parameter that the code came from, and this parameter will be mentioned in the error message along with the bad code. If `allowEtymLang` is specified, etymology-only language codes are allowed and looked up along with full language codes. If `allowFamily` is specified, language family codes are allowed and looked up along with normal language codes. ]==] function export.getByCode(code, paramForError, allowEtymLang, allowFamily) -- [[ Track uses of paramForError, ultimately so it can be removed, as error-handling should be done by -- [[Module:parameters]], not here. ]] -- I (Benwing) disagree; that is an idealistic goal unlikely to be achievable -- realistically. if paramForError ~= nil then track("paramForError") end if type(code) ~= "string" then local typ if not code then typ = "nil" elseif check_object("language", true, code) then typ = "a language object" elseif check_object("family", true, code) then typ = "a family object" else typ = "a " .. type(code) end error("The function getByCode expects a string as its first argument, but received " .. typ .. ".") end local m_data = load_data(languages_data_module) if m_data.aliases[code] or m_data.track[code] then track(code) end local norm_code = normalize_code(code) -- Get the data, checking for etymology-only languages if allowEtymLang is set. local data = load_data(get_data_module_name(norm_code))[norm_code] or allowEtymLang and load_data(etymology_languages_data_module)[norm_code] -- If no data was found and allowFamily is set, check the family data. If the main family data was found, make the -- object with [[Module:families]] instead, as family objects have different methods. However, if it's an -- etymology-only family, use make_object in this module (which handles object inheritance), and the -- family-specific methods will be inherited from the parent object. if data == nil and allowFamily then data = load_data("Module:families/data")[norm_code] if data ~= nil then if data.parent == nil then return make_family_object(norm_code, data) elseif not allowEtymLang then data = nil end end end local retval = code and data and make_object(code, data) if not retval and paramForError then require("Module:languages/errorGetBy").code(code, paramForError, allowEtymLang, allowFamily) end return retval end get_by_code = export.getByCode --[==[ Find the language whose canonical name (the name used to represent that language on Wiktionary) or other name matches the one provided. If it exists, return a `Language` object representing the language. Otherwise, return {nil}, unless `paramForError` is given, in which case an error is generated. If `allowEtymLang` is specified, etymology-only language codes are allowed and looked up along with full language codes. If `allowFamily` is specified, language family codes are allowed and looked up along with normal language codes. The canonical name of languages should always be unique (it is an error for two languages on Wiktionary to share the same canonical name), so this is guaranteed to give at most one result. This function is powered by [[Module:languages/canonical names]], which contains a pre-generated mapping of full-language canonical names to codes. It is generated by going through the [[:Category:Language data modules]] for full languages. When `allowEtymLang` is specified for the above function, [[Module:etymology languages/canonical names]] may also be used, and when `allowFamily` is specified for the above function, [[Module:families/canonical names]] may also be used. ]==] function export.getByCanonicalName(name, errorIfInvalid, allowEtymLang, allowFamily) local byName = load_data("Module:languages/canonical names") local code = byName and byName[name] if not code and allowEtymLang then byName = load_data("Module:etymology languages/canonical names") code = byName and byName[name] or byName[gsub(name, " [Ss]ubstrate$", "")] or byName[gsub(name, "^a ", "")] or byName[gsub(name, "^a ", ""):gsub(" [Ss]ubstrate$", "")] or -- For etymology families like "ira-pro". -- FIXME: This is not ideal, as it allows " languages" to be appended to any etymology-only language, too. byName[match(name, "^กลุ่มภาษา(.*)$")] end if not code and allowFamily then byName = load_data("Module:families/canonical names") code = byName[name] or byName[match(name, "^กลุ่มภาษา(.*)$")] end local retval = code and get_by_code(code, errorIfInvalid, allowEtymLang, allowFamily) if not retval and errorIfInvalid then require("Module:languages/errorGetBy").canonicalName(name, allowEtymLang, allowFamily) end return retval end --[==[ Used by [[Module:languages/data/2]] (et al.) and [[Module:etymology languages/data]], [[Module:families/data]], [[Module:scripts/data]] and [[Module:writing systems/data]] to finalize the data into the format that is actually returned. ]==] function export.finalizeData(data, main_type, variety) local fields = {"type"} if main_type == "language" then insert(fields, 4) -- script codes insert(fields, "ancestors") insert(fields, "link_tr") insert(fields, "override_translit") insert(fields, "wikimedia_codes") elseif main_type == "script" then insert(fields, 3) -- writing system codes end -- Families and writing systems have no extra fields to process. local fields_len = #fields for _, entity in next, data do if variety then -- Move parent from 3 to "parent" and family from "family" to 3. These are different for the sake of -- convenience, since very few varieties have the family specified, whereas all of them have a parent. entity.parent, entity[3] = entity[3], entity.family entity.family = nil -- Give the type "regular" iff not a variety and no other types are assigned. elseif not (entity.type or entity.parent) then entity.type = "regular" end for i = 1, fields_len do local key = fields[i] local field = entity[key] if field and type(field) == "string" then entity[key] = gsub(field, "%s*,%s*", ",") end end end return data end --[==[ For backwards compatibility only; modules should require the error themselves. ]==] function export.err(lang_code, param, code_desc, template_tag, not_real_lang) return require("Module:languages/error")(lang_code, param, code_desc, template_tag, not_real_lang) end return export a381dexrfxkn1jqr6kdkyd7q0qxj4z8 มอดูล:yesno 828 36740 5775225 5630141 2026-10-14T07:13:14Z Octahedron80 267 5775225 Scribunto text/plain -- Function allowing for consistent treatment of boolean-like wikitext input. -- It works similarly to the template {{yesno}}. local lower = string.lower local tonumber = tonumber local type = type local yesno return function (val, default) if val == nil then return nil elseif not yesno then yesno = { [true] = true, [false] = false, ["true"] = true, ["false"] = false, ["t"] = true, ["f"] = false, [1] = true, [0] = false, ["1"] = true, ["0"] = false, ["yes"] = true, ["no"] = false, ["y"] = true, ["n"] = false, ["on"] = true, ["off"] = false, ["ใช่"] = true, ["ไม่"] = false, ["ไม่ใช่"] = false, ["จริง"] = true, ["เท็จ"] = false, ["ถูก"] = true, ["ผิด"] = false, ["เปิด"] = true, ["ปิด"] = false, } end local ret = yesno[val] if ret ~= nil then return ret elseif type(val) ~= "string" then return default end -- Catch inputs like "00". ret = yesno[tonumber(val)] if ret ~= nil then return ret end -- Case-insensitive. ret = yesno[lower(val)] if ret ~= nil then return ret end return default end bavw814a97r32z2tmzaefmvwdwhlum7 มอดูล:links/templates 828 37349 5775186 5756375 2026-10-14T03:57:17Z Octahedron80 267 5775186 Scribunto text/plain -- Prevent substitution. if mw.isSubsting() then return require("Module:unsubst") end local export = {} local links_module = "Module:links" local process_params = require("Module:parameters").process local remove = table.remove local upper = require("Module:string utilities").upper --[=[ Modules used: [[Module:links]] [[Module:languages]] [[Module:scripts]] [[Module:parameters]] [[Module:debug]] ]=] do local function get_args(frame) -- `compat` is a compatibility mode for {{term}}. -- If given a nonempty value, the function uses lang= to specify the -- language, and all the positional parameters shift one number lower. local iargs = frame.args iargs.compat = iargs.compat and iargs.compat ~= "" iargs.langname = iargs.langname and iargs.langname ~= "" iargs.notself = iargs.notself and iargs.notself ~= "" local alias_of_4 = {alias_of = 4} local boolean = {type = "boolean"} local params = { [1] = {required = true, type = "language", default = "und"}, [2] = true, [3] = true, [4] = true, g = {list = true, type = "genders", flatten = true}, gloss = alias_of_4, id = true, lit = true, ng = true, pos = true, sc = {type = "script"}, t = alias_of_4, tr = true, ts = true, q = {type = "qualifier"}, qq = {type = "qualifier"}, l = {type = "labels"}, ll = {type = "labels"}, ref = {type = "references"}, ["accel-form"] = true, ["accel-translit"] = true, ["accel-lemma"] = true, ["accel-lemma-translit"] = true, ["accel-gender"] = true, ["accel-nostore"] = boolean, } if iargs.compat then params.lang = {type = "language", default = "und"} remove(params, 1) alias_of_4.alias_of = 3 end if iargs.langname then params.w = boolean end return process_params(frame:getParent().args, params), iargs end -- Used in [[Template:l]] and [[Template:m]]. function export.l_term_t(frame) local args, iargs = get_args(frame) local compat = iargs.compat local lang = args[compat and "lang" or 1] -- Tracking for und. if not compat and lang:getCode() == "und" then require("Module:debug").track("link/und") end local term = args[(compat and 1 or 2)] local alt = args[(compat and 2 or 3)] term = term ~= "" and term or nil if not term and not alt and iargs.demo then term = iargs.demo end local langname = iargs.langname and ( args.w and lang:makeWikipediaLink() or lang:getCanonicalName() ) or nil if langname and term == "-" then return langname end -- Forward the information to full_link return (langname and langname .. " " or "") .. require(links_module).full_link( { lang = lang, sc = args.sc, track_sc = true, term = term, alt = alt, gloss = args[4], id = args.id, tr = args.tr, ts = args.ts, genders = args.g, pos = args.pos, ng = args.ng, lit = args.lit, q = args.q, qq = args.qq, l = args.l, ll = args.ll, refs = args.ref, show_decorations = true, accel = args["accel-form"] and { form = args["accel-form"], translit = args["accel-translit"], lemma = args["accel-lemma"], lemma_translit = args["accel-lemma-translit"], gender = args["accel-gender"], nostore = args["accel-nostore"], } or nil }, iargs.face, not iargs.notself ) end -- Used in [[Template:link-annotations]]. function export.l_annotations_t(frame) local args, iargs = get_args(frame) -- Forward the information to format_link_annotations return require(links_module).format_link_annotations( { lang = args[1], tr = { args.tr }, ts = { args.ts }, genders = args.g, pos = args.pos, ng = args.ng, lit = args.lit }, iargs.face ) end end -- Used in [[Template:ll]]. do local function get_args(frame) return process_params(frame:getParent().args, { [1] = {required = true, type = "language", default = "und"}, [2] = {allow_empty = true}, [3] = true, id = true, sc = {type = "script"}, }) end function export.ll(frame) local args = get_args(frame) local lang = args[1] local sc = args.sc local term = args[2] term = term ~= "" and term or nil return require(links_module).language_link{ lang = lang, sc = sc, term = term, alt = args[3], id = args.id } or "<small>[Term?]</small>" .. require("Module:utilities").format_categories( {lang:getFullName() .. " term requests"}, lang, "-", nil, nil, sc ) end end function export.def_t(frame) local args = process_params(frame:getParent().args, { [1] = {required = true, default = ""}, }) local face = frame.args.face local ret = require("Module:script utilities").tag_definition(require(links_module).embedded_language_links{ term = args[1], lang = require("Module:languages").getByCode("th"), --th sc = require("Module:scripts").getByCode("Latn") }, face) if face == "non-gloss" then return ret end return '<span class="mention-gloss-paren">(</span>' .. ret .. '<span class="mention-gloss-paren">)</span>' end function export.linkify_t(frame) local args = process_params(frame:getParent().args, { [1] = {required = true, default = ""}, }) args[1] = mw.text.trim(args[1]) if args[1] == "" or args[1]:find("[[", nil, true) then return args[1] end return "[[" .. args[1] .. "]]" end function export.cap_t(frame) local args = process_params(frame:getParent().args, { [1] = {required = true}, [2] = true, lang = {type = "language", default = "und"}, --can't set either en or th }) local term = args[1] return require(links_module).full_link{ lang = args.lang, term = term, alt = term:gsub("^.[\128-\191]*", upper) .. (args[2] or "") } end function export.section_link_t(frame) local args = process_params(frame:getParent().args, { [1] = {}, }) return require(links_module).section_link(args[1]) end return export 3nu6idmbepgyyb2fwghysf40qqykvfg มอดูล:labels/data 828 37614 5775251 5759802 2026-10-14T08:58:31Z Octahedron80 267 ย้ายพ้นสมัยไปที่เลิกใช้ [นิติศาสตร์ ๑๑ มี.ค. ๒๕๔๕] 5775251 Scribunto text/plain local labels = {} -- Grammatical labels labels["คำย่อ"] = { aliases = {"abbreviation"}, glossary = true, pos_categories = "คำย่อ", } labels["อาการนาม"] = { aliases = {"abstract noun", "คำอาการนาม"}, --display = "อาการนาม", glossary = true, pos_categories = "คำอาการนาม", } labels["อักษรย่อรวมพยางค์"] = { aliases = {"acronym"}, glossary = true, pos_categories = "อักษรย่อรวมพยางค์", } labels["กรรตุวาจก"] = { aliases = {"active", "in the active", "active voice"}, glossary = true, } labels["สกรรมและอกรรม"] = { aliases = {"ambitransitive", "อกรรมและสกรรม"}, glossary = true, pos_categories = {"คำสกรรมกริยา", "คำอกรรมกริยา"}, } labels["angry register"] = { aliases = {"angry", "anger", "said in anger"}, glossary = true, pos_categories = "angry register terms", } labels["มีชีวิต"] = { aliases = {"animate"}, glossary = true, } labels["บอกเล่า"] = { aliases = {"indicative", "in the indicative", "indicative mood"}, glossary = "มาลาบอกเล่า", } labels["สมมุติ"] = { aliases = {"subjunctive", "in the subjunctive", "subjunctive mood"}, glossary = "มาลาสมมุติ", } labels["สั่ง"] = { aliases = {"imperative", "in the imperative", "imperative mood", "คำสั่ง"}, glossary = "มาลาสั่ง", } labels["ขอร้อง"] = { aliases = {"jussive", "in the jussive", "jussive mood", "ร้องขอ"}, glossary = "มาลาขอร้อง", } labels["atelic"] = { glossary = true, } labels["attenuative"] = { pos_categories = "attenuative verbs", } labels["attributive"] = { glossary = true, } labels["attributively"] = { glossary = "attributive", } labels["ช่วย"] = { aliases = {"auxiliary", "กริยาช่วย", "คำกริยาช่วย", "คำช่วยกริยา", "กริยานุเคราะห์", "คำกริยานุเคราะห์"}, glossary = true, pos_categories = "คำกริยาช่วย", } labels["จำนวนเชิงการนับ"] = { aliases = {"cardinal", "cardinal number", "เชิงการนับ"}, --display = "[[จำนวนเชิงการนับ]]", pos_categories = "จำนวนเชิงการนับ", } labels["catenative"] = { glossary = "catenative verb", } labels["causative"] = { glossary = true, } labels["causative verb"] = { display = "causative", glossary = true, pos_categories = "causative verbs", } labels["cognate object"] = { aliases = {"with cognate object"}, display = "with [[w:Cognate object|cognate object]]", pos_categories = "verbs used with cognate objects", } labels["สมุหนาม"] = { aliases = {"collective", "collectively", "คำสมุหนาม"}, glossary = true, --display = "สมุหนาม", pos_categories = "คำสมุหนาม", } --[[ รวมกับอันบน labels["collectively"] = { glossary = "collective", display = "สมุหนาม", pos_categories = "คำสมุหนาม", } --]] labels["collective number"] = { aliases = {"collective numeral"}, display = "[[collective number]]", pos_categories = "collective numbers", } labels["control verb"] = { aliases = {"control"}, Wikipedia = true, pos_categories = "control verbs", } labels["เพศรวม"] = { aliases = {"common", "รวม"}, glossary = true, } labels["เปรียบเทียบได้"] = { aliases = {"comparable"}, glossary = true, } labels["completive"] = { pos_categories = "completive verbs", } labels["เชื่อม"] = { aliases = {"copular"}, display = "[[คำกริยาเชื่อม|เชื่อม]]", pos_categories = "คำกริยาเชื่อม", } labels["นับได้"] = { aliases = {"countable"}, glossary = true, pos_categories = "คำนามนับได้", } labels["cumulative"] = { pos_categories = "cumulative verbs", } labels["delimitative"] = { pos_categories = "delimitative verbs", } labels["deponent"] = { glossary = true, pos_categories = "deponent verbs", } labels["distributive"] = { pos_categories = "distributive verbs", } labels["distributive number"] = { aliases = {"distributive numeral"}, display = "[[distributive number]]", pos_categories = "distributive numbers", } labels["ทวิกรรม"] = { aliases = {"ditransitive", "ทวิกรรมกริยา"}, glossary = true, pos_categories = "คำทวิกรรมกริยา", } labels["dysphemistic"] = { aliases = {"dysphemism"}, glossary = true, pos_categories = "dysphemisms", } labels["by ellipsis"] = { aliases = {"ellipsis"}, glossary = "ellipsis", pos_categories = "ellipses", } labels["emphatic"] = { glossary = true, } labels["ergative"] = { glossary = true, pos_categories = "ergative verbs", } labels["expressive"] = { glossary = true, pos_categories = "expressive terms", } labels["by extension"] = { aliases = {"hence"}, } labels["เพศหญิง"] = { aliases = {"feminine", "หญิง"}, glossary = true, } labels["focus"] = { glossary = true, pos_categories = "focus adverbs", } labels["fractional"] = { aliases = {"fractional number", "fractional numeral"}, display = "[[fractional number]]", pos_categories = "fractional numbers", } labels["frequentative"] = { glossary = true, pos_categories = "frequentative verbs", } labels["hedge"] = { aliases = {"hedges"}, glossary = true, pos_categories = "hedges", } labels["ideophonic"] = { aliases = {"ideophone"}, glossary = true, } labels["สำนวน"] = { aliases = {"idiomatic", "idiom", "idiomatically", "สำ"}, glossary = true, pos_categories = "สำนวน", } labels["imperfect"] = { glossary = true, } labels["ไม่สมบูรณ์"] = { aliases = {"imperfective"}, glossary = true, pos_categories = "คำกริยาไม่สมบูรณ์", } labels["อบุรุษ"] = { aliases = {"impersonal", "impersonal verbs", "อบุรุษกริยา", "คำอบุรุษกริยา"}, glossary = true, pos_categories = "คำอบุรุษกริยา", } labels["ในรูปเอกพจน์"] = { aliases = {"in singular", "in the singular"}, display = "ในรูป[[เอกพจน์]]", } labels["ในรูปทวิพจน์"] = { aliases = {"in dual", "in the dual"}, display = "ในรูป[[ทวิพจน์]]", } labels["ในรูปพหูพจน์"] = { aliases = {"in plural", "in the plural"}, display = "ในรูป[[ภาคผนวก:อภิธานศัพท์#พหูพจน์|พหูพจน์]]", } labels["ไม่มีชีวิต"] = { aliases = {"inanimate"}, glossary = true, } labels["inchoative"] = { pos_categories = "inchoative verbs", } labels["ไม่ชี้เฉพาะ"] = { aliases = {"indef", "indefinite"}, glossary = true, } labels["อักษรย่อ"] = { aliases = {"initialism"}, glossary = true, pos_categories = "อักษรย่อ", } labels["intensive verb"] = { display = "intensive", pos_categories = "intensive verbs", } labels["อกรรม"] = { aliases = {"intransitive", "อกรรมกริยา"}, glossary = true, pos_categories = "คำอกรรมกริยา", } labels["ไอพีเอ"] = { aliases = {"IPA", "International Phonetic Alphabet", "สัทอักษรสากล"}, Wikipedia = "สัทอักษรสากล", plain_categories = "สัญลักษณ์สำหรับสัทอักษรสากล", } labels["iterative"] = { glossary = true, pos_categories = "iterative verbs", } labels["litotes"] = { aliases = {"litote", "litotic", "litotical"}, glossary = true, pos_categories = true, } labels["เพศชาย"] = { aliases = {"masculine", "ชาย"}, glossary = true, } labels["mediopassive"] = { aliases = {"in the mediopassive", "in mediopassive"}, glossary = true, } labels["meiosis"] = { aliases = {"meioses", "meiotic"}, glossary = true, pos_categories = "meioses", } labels["middle voice"] = { aliases = {"middle", "in the middle", "in the middle voice"}, glossary = true, } labels["รูปสะกดผิด"] = { aliases = {"misspelling", "สะกดผิด"}, display = "[[รูปสะกดผิด]]", } labels["mnemonic"] = { display = "[[mnemonic]]", pos_categories = "mnemonics", } labels["แสดงมาลา"] = { aliases = {"modal", "แสดงทัศนะ"}, Wikipedia = "ทัศนภาวะ", } labels["NAPA"] = { aliases = {"Americanist_phonetic_notation"}, Wikipedia = "Americanist_phonetic_notation", plain_categories = "NAPA symbols", } labels["always in the negative"] = { glossary = "negative polarity item", pos_categories = "negative polarity items", } labels["chiefly in the negative"] = { aliases = {"chiefly used in the negative", "negative polarity", "negative polarity item", "usually in the negative", "usually used in the negative"}, glossary = "negative polarity item", pos_categories = "negative polarity items", } labels["chiefly in the negative plural"] = { aliases = {"chiefly used in the negative plural", "negative polarity plural", "negative polarity plural item", "usually in the negative plural", "usually used in the negative plural"}, display = "chiefly in the [[Appendix:Glossary#negative polarity item|negative]] [[Appendix:Glossary#plural|plural]]", pos_categories = "negative polarity items", } labels["chiefly in the positive"] = { aliases = {"chiefly used in the positive", "positive polarity", "positive polarity item", "usually in the positive", "usually used in the positive"}, glossary = "positive polarity item", -- pos_categories = {"positive polarity items"}, } labels["chiefly in the positive plural"] = { aliases = {"chiefly used in the positive plural", "positive polarity plural", "positive polarity plural item", "usually in the positive plural", "usually used in the positive plural"}, display = "chiefly in the [[Appendix:Glossary#positive polarity item|positive]] [[Appendix:Glossary#plural|plural]]", -- pos_categories = "positive polarity items", } labels["เพศกลาง"] = { aliases = {"neuter", "กลาง"}, glossary = true, } -- British English ("ise") labels["nominalised"] = { aliases = {"nominalisation", "substantivised", "substantivisation"}, glossary = "nominalization", pos_categories = "nominalized adjectives", } -- American English ("ize") labels["nominalized"] = { aliases = {"nominalization", "substantivized", "substantivization"}, glossary = "nominalization", pos_categories = "nominalized adjectives", } labels["เปรียบเทียบไม่ได้"] = { aliases = {"not comparable", "notcomp", "uncomparable"}, glossary = "เปรียบเทียบไม่ได้", } labels["เลียนเสียง"] = { aliases = {"onomatopoeia", "onomatopoeic", "เลียนเสียงธรรมชาติ", "คำเลียนเสียง"}, glossary = true, pos_categories = "ศัพท์ที่เลียนเสียงธรรมชาติ", } labels["เชิงอันดับที่"] = { aliases = {"ordinal", "ordinal numbers", "จำนวนเชิงอันดับที่"}, --display = "[[จำนวนเชิงอันดับที่]]", pos_categories = "จำนวนเชิงอันดับที่", } labels["partitive verb"] = { display = "[[ภาคผนวก:อภิธานศัพท์#สกรรม|สกรรม]], มักจะ[[ภาคผนวก:คำกริยามีจุดจบและไม่มีจุดจบภาษาฟินนิก|ไม่มีจุดจบ]]", pos_categories = "คำสกรรมกริยา", -- = "partitive verbs", } labels["perfect"] = { glossary = true, } labels["รูปกริยาขยาย"] = { aliases = {"participle"}, glossary = true, } labels["กรรมวาจก"] = { aliases = {"passive", "in the passive", "passive voice"}, glossary = true, } labels["perfect"] = { glossary = true, } labels["สมบูรณ์"] = { glossary = true, pos_categories = "คำกริยาสมบูรณ์", } labels["พหูพจน์เท่านั้น"] = { aliases = {"plural only", "pluralonly", "plurale tantum"}, pos_categories = "คำนามพหูพจน์เท่านั้น", } labels["possessional adjective"] = { aliases = {"possessional", "possessional adjectives"}, display = "possessional", glossary = true, pos_categories = "possessional adjectives", } labels["สรรพนามแสดงความเป็นเจ้าของ"] = { aliases = {"possessive pronoun", "possessive determiner"}, display = "แสดงความเป็นเจ้าของ", glossary = "possessive determiner", pos_categories = "คำสรรพนามแสดงความเป็นเจ้าของ", } labels["postpositive"] = { glossary = true, } labels["ภาคแสดง"] = { aliases = {"predicative"}, glossary = true, } labels["predicatively"] = { glossary = "ภาคแสดง", } labels["prescriptive"] = { aliases = {"normative", "prescribed"}, glossary = true, } labels["privative"] = { pos_categories = "privative verbs", } labels["procedure word"] = { display = "[[procedure word]]", } labels["productive"] = { glossary = true, } -- TODO: This label is probably inappropriate for many languages labels["pronominal"] = { glossary = "pronominal verb", } labels["pro-verb"] = { Wikipedia = true, } labels["กันและกัน"] = { aliases = {"reciprocal", "คำกริยากันและกัน", "กริยากันและกัน"}, glossary = true, pos_categories = "คำกริยากันและกัน", } labels["สะท้อน"] = { aliases = {"reflexive", "คำกริยาสะท้อน", "กริยาสะท้อน"}, glossary = true, pos_categories = "คำกริยาสะท้อน", } labels["สรรพนามสะท้อน"] = { aliases = {"reflexive pronoun", "คำสรรพนามสะท้อน"}, glossary = "สะท้อน", pos_categories = "คำสรรพนามสะท้อน", } labels["สัมพันธ์"] = { aliases = {"relational adjective", "relational", "คำคุณศัพท์สัมพันธ์", "คุณศัพท์สัมพันธ์"}, glossary = true, pos_categories = "คำคุณศัพท์สัมพันธ์", } labels["repetitive"] = { pos_categories = "repetitive verbs", } labels["respelling"] = { glossary = true, } labels["reversative"] = { pos_categories = "reversative verbs", } labels["rhetorical question"] = { glossary = true, pos_categories = "rhetorical questions", } labels["saturative"] = { aliases = {"sative"}, pos_categories = "saturative verbs", } labels["semelfactive"] = { glossary = true, pos_categories = "semelfactive verbs", } labels["sentence adverb"] = { glossary = true, pos_categories = "sentence adverbs", } labels["set phrase"] = { display = "[[set phrase]]", } labels["simile"] = { glossary = true, pos_categories = "similes", } labels["เอกพจน์เท่านั้น"] = { aliases = {"singular only", "singulare tantum", "no plural"}, display = "เอกพจน์เท่านั้น", pos_categories = "คำนามเอกพจน์เท่านั้น", } labels["snowclone"] = { glossary = true, pos_categories = "snowclones", } labels["สภาว"] = { aliases = {"stative", "stative verb", "สภาวกริยา"}, glossary = true, pos_categories = "คำสภาวกริยา", } labels["strictly"] = { aliases = {"strict", "narrowly", "narrow"}, glossary = true, } labels["substantive"] = { glossary = true, track = true, } labels["terminative"] = { pos_categories = "terminative verbs", } labels["สกรรม"] = { aliases = {"transitive", "สกรรมกริยา"}, glossary = true, pos_categories = "คำสกรรมกริยา", } labels["unaccusative"] = { Wikipedia = "Unaccusative verb", } labels["นับไม่ได้"] = { aliases = {"uncountable"}, glossary = true, pos_categories = "คำนามนับไม่ได้", } labels["unergative"] = { Wikipedia = "Unergative verb", } labels["UPA"] = { aliases = {"Uralic Phonetic Alphabet"}, Wikipedia = "Uralic Phonetic Alphabet", plain_categories = "UPA symbols", } labels["usually plural"] = { aliases = {"usually in the plural", "usually in plural"}, display = "usually in the [[Appendix:Glossary#plural|plural]]", deprecated = true, } -- Usage labels labels["โฟร์แชน"] = { aliases = {"4chan", "4chan slang"}, display = "สแลง[[w:โฟร์แชน|โฟร์แชน]]", pos_categories = "สแลงโฟร์แชน", } labels["4chan lgbt"] = { aliases = {"tttt"}, display = "[[w:4chan|4chan]] /lgbt/ slang", pos_categories = "4chan /lgbt/ slang", } labels["ACG"] = { display = "[[ACG]]", -- see also "fandom slang" pos_categories = "fandom slang", } labels["endearing"] = { aliases = {"affectionate"}, display = "[[endearing]]", -- should be "terms with X senses", leaving "X terms" to the term-context temp pos_categories = "endearing terms", } labels["pre-classical"] = { aliases = {"Pre-classical", "pre-Classical", "Pre-Classical", "Preclassical", "preclassical", "ante-classical", "Ante-classical", "ante-Classical", "Ante-Classical", "Anteclassical", "anteclassical"}, display = "pre-Classical", regional_categories = true, } labels["anti-LGBTQ slur"] = { -- don't add aliases "homophobia" or "transphobia" because these could be topical categories aliases = {"homophobic", "transphobic"}, display = "anti-[[LGBTQ]] [[slur]]", pos_categories = "anti-LGBTQ slurs", } labels["โบราณ"] = { aliases = {"archaic", "antiquated", "โบ"}, glossary = true, sense_categories = true, } labels["รูปโบราณ"] = { aliases = {"archaic form"}, glossary = "โบราณ", display = "โบราณ", pos_categories = "รูปโบราณ", } labels["Australian slang"] = { regional_categories = "Australian", plain_categories = true, } labels["avoidance"] = { glossary = true, } labels["back slang"] = { aliases = {"backslang", "back-slang"}, glossary = "backslang", pos_categories = true, } labels["Bargoens"] = { Wikipedia = true, plain_categories = true, } labels["Braille"] = { Wikipedia = true, } labels["British slang"] = { aliases = {"UK slang"}, plain_categories = true, } labels["Cambridge University slang"] = { aliases = {"University of Cambridge slang", "Cantab slang"}, display = "[[w:University of Cambridge|Cambridge University]] [[slang]]", topical_categories = "Universities", plain_categories = true, } labels["cant"] = { aliases = {"argot", "cryptolect"}, display = "[[cant]]", pos_categories = true, } labels["capitalized"] = { aliases = {"capitalised"}, display = "[[capitalisation|capitalized]]", } labels["Castilianism"] = { aliases = {"Hispanicism"}, display = "[[Castilianism]]", } labels["ภาษาเด็ก"] = { aliases = {"childish", "baby talk", "child language", "infantile", "เด็ก"}, display = "[[ภาษาเด็ก]]", -- should be "terms with X senses", leaving "X terms" to the term-context temp? pos_categories = "ศัพท์ที่เป็นภาษาเด็ก", } labels["chu Nom"] = { display = "[[Vietnamese]] [[chữ Nôm]]", plain_categories = "Vietnamese Han tu", } labels["Cockney rhyming slang"] = { display = "[[Cockney rhyming slang]]", plain_categories = true, } labels["ภาษาปาก"] = { aliases = {"colloquial", "colloquially", "ภาษาพูด", "ปาก", "พูด"}, glossary = true, pos_categories = "ศัพท์ที่เป็นภาษาปาก", } -- FIXME! The following two are apparently for Persian but probably don't belong in this file. labels["colloquial-um"] = { glossary = "ภาษาปาก", pos_categories = "colloquialisms containing sequence um", } labels["colloquial-un"] = { glossary = "ภาษาปาก", pos_categories = "colloquialisms containing sequence un", } labels["corporate jargon"] = { aliases = {"business jargon", "corporatese", "businessese", "corporate speak", "business speak"}, display = "[[corporate]] [[jargon]]", pos_categories = true, } labels["costermongers"] = { aliases = {"coster", "costers", "costermonger", "costermongers back slang", "costermongers' back slang"}, display = "[[Appendix:Costermongers' back slang|costermongers]]", plain_categories = "Costermongers' back slang", } labels["criminal slang"] = { aliases = {"thieves' cant", "Thieves' Cant", "thieves cant", "thieves'", "thieves", "thieves' cant"}, -- Thieves' Cant is English-only, so defined in the English submodule; if other languages try to use it, it's just criminal slang display = "[[criminal]] [[slang]]", topical_categories = "Crime", pos_categories = true, } labels["ล้าสมัย"] = { aliases = {"dated", "old-fashioned", "ล้า"}, glossary = true, -- should be "terms with X senses", leaving "X terms" to the term-context temp pos_categories = "ศัพท์ที่ล้าสมัย", } labels["dated form"] = { glossary = "dated", pos_categories = "dated forms", } -- combine with previous? labels["นัยล้าสมัย"] = { aliases = {"dated sense", "นัยล้า"}, glossary = "dated", sense_categories = "ล้าสมัย", } labels["ดูหมิ่น"] = { aliases = {"derogatory", "pejorative", "derogative", "disparaging", "หมิ่น", "ดูถูก", "หมิ่นประมาท", "เสื่อมเสีย"}, display = "[[ดูหมิ่น]]", -- should be "terms with X senses", leaving "X terms" to the term-context temp pos_categories = "ศัพท์ที่ดูหมิ่น", } --[=[ merged down labels["dialect"] = {-- separated from "dialectal" so e.g. "obsolete|outside|the|_|dialect|of..." displays right glossary = "ภาษาถิ่น", pos_categories = {"ศัพท์ที่เป็นภาษาถิ่น"}, } --]=] labels["ภาษาถิ่น"] = { aliases = {"dialect", "dialects", "dialectal", "ถิ่น", "เฉพาะถิ่น", "ภาษาเฉพาะถิ่น"}, glossary = true, -- should be "terms with X senses", leaving "X terms" to the term-context temp pos_categories = "ศัพท์ที่เป็นภาษาถิ่น", } --[=[ merged up labels["dialects"] = {-- separated from "dialectal" so e.g. "obsolete|outside|dialects" displays right glossary = "ภาษาถิ่น", pos_categories = {"ศัพท์ที่เป็นภาษาถิ่น"}, } --]=] labels["dis legomenon"] = { display = "[[dis legomenon]]", pos_categories = "dis legomena", } labels["dismissal"] = { display = "[[dismissal]]", pos_categories = "dismissals", } labels["drag slang"] = { aliases = {"Drag Race slang"}, display = "[[drag]] [[slang]]", pos_categories = "drag slang", } labels["solemn"] = { glossary = true, pos_categories = "solemn terms", } labels["ecclesiastical"] = { pos_categories = "ecclesiastical terms", } labels["ethnic slur"] = { aliases = {"racial slur"}, display = "[[ethnic]] [[slur]]", pos_categories = "ethnic slurs", } labels["เสาวพจน์"] = { aliases = {"เสาว", "รื่นหู", "คำรื่นหู", "นุ่มนวล", "ละมุนละม่อม", "คำละมุนละม่อม", "สละสลวย", "คำสละสลวย", "euphemistic", "euphemism"}, glossary = "เสาวพจน์", pos_categories = "เสาวพจน์", } labels["eye dialect"] = { display = "[[eye dialect]]", pos_categories = true, } labels["familiar"] = { glossary = true, -- should be "terms with X senses", leaving "X terms" to the term-context temp? pos_categories = "familiar terms", } labels["fandom slang"] = { aliases = {"fandom"}, display = "[[fandom]] [[slang]]", pos_categories = true, } --[=[ merged down labels["figurative"] = { aliases = {"metaphorical", "metaphoric", "metaphor"}, glossary = "ในเชิงเปรียบเทียบ", } --]=] labels["ในเชิงเปรียบเทียบ"] = { aliases = {"metaphorically", "metaphorical", "metaphoric", "metaphor", "figuratively", "figurative", "ภาพพจน์", "เปรียบเทียบ", "อุปมาอุปไมย", "อุปมา", "อุปลักษณ์", "โดยปริยาย"}, glossary = "ในเชิงเปรียบเทียบ", } labels["folk poetic"] = { -- should be "terms with X senses", leaving "X terms" to the term-context temp pos_categories = {"folk poetic terms", "poetic terms"}, } labels["ทางการ"] = { aliases = {"formal", "เป็นทางการ"}, glossary = true, -- should be "terms with X senses", leaving "X terms" to the term-context temp? pos_categories = "ศัพท์ที่เป็นทางการ", } labels["gay slang"] = { display = "[[gay]] [[slang]]", pos_categories = true, } labels["gender critical slang"] = { aliases = {"gender-critical slang", "GC slang", "TERF slang"}, display = "[[gender-critical]] [[slang]]", pos_categories = "gender-critical slang", } labels["ไม่ระบุเพศ"] = { aliases = {"gender-neutral"}, glossary = "ไม่ระบุเพศ", pos_categories = "ศัพท์ที่ไม่ระบุเพศ", } labels["genericized trademark"] = { aliases = {"generic trademark", "proprietary eponym"}, display = "[[genericized trademark]]", pos_categories = "genericized trademarks", } labels["ghost word"] = { aliases = {"ghost"}, display = "ghost word", glossary = true, pos_categories = "ghost words", } labels["hapax legomenon"] = { aliases = {"hapax"}, display = "hapax legomenon", glossary = true, pos_categories = "hapax legomena", } labels["higher register"] = { aliases = {"high register", "elevated register", "elevated"}, glossary = "higher register", pos_categories = "higher register terms", } labels["historical"] = { aliases = {"historic"}, glossary = true, sense_categories = true, } labels["non-native speakers"] = {-- language-agnostic version aliases = {"NNS"}, display = "[[non-native speaker]]s", -- so preceded by "used by", "error by children and", etc? or reword? regional_categories = {"Non-native speakers'"}, } labels["non-native speakers' English"] = { aliases = {"NNES", "NNSE"}, display = "[[non-native speaker]]s' English", regional_categories = "Non-native speakers'", } -- used exclusively by languages that use the "Jpan" script code labels["historical hiragana"] = { pos_categories = true, } -- used exclusively by languages that use the "Jpan" script code labels["historical katakana"] = { pos_categories = true, } -- applies to Japanese and Korean, etc., please do not confuse with "polite" labels["honorific"] = { Wikipedia = "Honorifics (linguistics)", -- should be "terms with X senses", leaving "X terms" to the term-context temp? pos_categories = "honorific terms", } -- for Ancient Greek labels["Homeric epithet"] = { display = "[[Homeric Greek|Homeric]] [[w:Epithets in Homer|epithet]]", omit_postComma = true, plain_categories = "Epic Greek", } -- applies to Japanese and Korean, etc. labels["humble"] = { -- should be "terms with X senses", leaving "X terms" to the term-context temp? display = "[[humble]]", pos_categories = "humble terms", } -- for Akkadian labels["in hendiadys"] = { aliases = {"hendiadys"}, pos_categories = "hendiadys", } labels["ขำขัน"] = { -- should be "terms with X senses", leaving "X terms" to the term-context temp; NB and cf a similar "jocular" label further up on this page aliases = {"humorous", "humorously", "jocular", "ตลก", "ขบขัน", "ตลกขบขัน"}, display = "[[ขำขัน]]", pos_categories = "ศัพท์ที่ขำขัน", } labels["อติพจน์"] = { aliases = {"hyperbolic", "hyperbole", "เกินจริง"}, glossary = true, pos_categories = "อติพจน์", } labels["แก้ไขเกินเหตุ"] = { aliases = {"hypercorrect", "hypercorrections", "การแก้ไขเกินเหตุ"}, glossary = true, pos_categories = "ศัพท์ที่แก้ไขเกินเหตุ", } labels["hyperforeign"] = { glossary = true, pos_categories = "hyperforeign terms", } labels["imperial"] = { aliases = {"emperor", "empress"}, pos_categories = "royal terms", } labels["incel slang"] = { display = "[[incel]] [[slang]]", pos_categories = true, } labels["ไม่ทางการ"] = { aliases = {"informal", "informally", "ไม่เป็นทางการ"}, glossary = true, -- should be "terms with X senses", leaving "X terms" to the term-context temp pos_categories = "ศัพท์ที่ไม่เป็นทางการ", } labels["สแลงอินเทอร์เน็ต"] = { aliases = {"Internet slang", "internet slang"}, display = "[[สแลง]][[อินเทอร์เน็ต]]", pos_categories = "สแลงอินเทอร์เน็ต", } labels["ไออาร์ซี"] = { aliases = {"IRC"}, display = "[[ไออาร์ซี]]", pos_categories = "สแลงอินเทอร์เน็ต", } labels["ironic"] = { display = "[[irony|ironic]]", } -- Not the same as "journalism", which maps to a topical category (e.g. [[:Category:en:Journalism]], instead of [[:Category:English journalistic terms]]). labels["ภาษาข่าว"] = { aliases = {"journalese", "journalistic"}, display = "[[ภาษาข่าว]]", pos_categories = "ศัพท์ที่เป็นภาษาข่าว", } labels["leet"] = { aliases = {"leetspeak"}, display = "[[leetspeak]]", pos_categories = true, } labels["LGBTQ slang"] = { aliases = {"LGBT slang"}, display = "[[LGBTQ]] [[slang]]", pos_categories = true, } labels["literal"] = { glossary = "literally", } labels["literally"] = { glossary = "literally", } labels["ภาษาหนังสือ"] = { -- should be "terms with X senses", leaving "X terms" to the term-context temp aliases = {"literary", "bookish", "ภาษาเขียน", "ภาษาแบบแผน", "หนังสือ", "เขียน", "แบบแผน", "แบบ", "วรรณกรรม"}, glossary = true, pos_categories = "ศัพท์ที่เป็นภาษาหนังสือ", } labels["loosely"] = { aliases = {"loose", "broadly", "broad"}, glossary = true, } labels["Lubunyaca"] = { display = "[[Lubunyaca]]", pos_categories = true, } labels["medical slang"] = { display = "[[medical]] [[slang]]", pos_categories = true, } -- for Awetí, Karajá, etc., where men and women use different words labels["men's speech"] = { aliases = {"male speech"}, glossary = "men's speech", pos_categories = "men's speech terms", } labels["metonymically"] = { aliases = {"metonymic", "metonymy", "metonym"}, glossary = true, pos_categories = "metonyms", } labels["military slang"] = { display = "[[military]] [[slang]]", pos_categories = true, } labels["คำสบถแปลง"] = { aliases = {"minced oath"}, display = "[[คำสบถแปลง]]", pos_categories = "คำสบถแปลง", } labels["multiplicative"] = { aliases = {"multiplicative number", "multiplicative numeral"}, display = "[[multiplicative number]]", pos_categories = "multiplicative numbers", } labels["multiplicity slang"] = { display = "{{l|en|multiplicity|id=multiple personalities}} [[slang]]", pos_categories = true, } labels["nativising coinage"] = { display = "[[w:Linguistic purism in Korean|nativising coinage]]", pos_categories = "nativising coinages", } labels["naval slang"] = { aliases = {"navy slang"}, display = "[[naval]] [[slang]]", pos_categories = true, } labels["นวศัพท์"] = { aliases = {"neologism", "neologistic", "ศัพท์ใหม่", "คำใหม่", "คำสร้างใหม่"}, glossary = true, pos_categories = "นวศัพท์", } labels["neopronoun"] = { display = "[[neopronoun]]", -- pos_categories = {"neopronouns"}, } labels["no longer productive"] = { display = "no longer [[Appendix:Glossary#productive|productive]]", } labels["nonce word"] = { -- should be "terms with X senses", leaving "X terms" to the term-context temp? aliases = {"nonce"}, glossary = true, pos_categories = "nonce terms", } labels["ภาษาไม่มาตรฐาน"] = { aliases = {"non-standard", "nonstandard", "substandard", "sub-standard", "ไม่มาตรฐาน"}, glossary = true, -- should be "terms with X senses", leaving "X terms" to the term-context temp pos_categories = "ศัพท์ที่เป็นภาษาไม่มาตรฐาน", } labels["nonstandard form"] = { aliases = {"non-standard form", "substandard form", "sub-standard form"}, glossary = "nonstandard", pos_categories = "nonstandard forms", } labels["เลิกใช้"] = { aliases = {"obsolete", "เลิก", "พ้นสมัย"}, glossary = true, sense_categories = true, } labels["obsolete term"] = { glossary = "obsolete", -- combine with previous two, q.v. pos_categories = "obsolete terms", } labels["ล่วงเกิน"] = { aliases = {"offensive", "ล่วงละเมิด", "โจมตี", "รุกราน", "ก้าวร้าว"}, glossary = true, -- should be "terms with X senses", leaving "X terms" to the term-context temp pos_categories = "ศัพท์ที่ล่วงเกิน", } labels["officialese"] = { aliases = {"bureaucratic"}, display = "[[officialese]]", pos_categories = "officialese terms", } labels["OpenStreetMap"] = { aliases = {"osm", "OSM"}, Wikipedia = true, pos_categories = "OpenStreetMap jargon", } labels["Oxbridge slang"] = { display = "[[w:Oxbridge|Oxbridge]] [[slang]]", topical_categories = "Universities", plain_categories = {"Cambridge University slang", "Oxford University slang"}, } labels["Oxford University slang"] = { aliases = {"University of Oxford slang", "Oxon slang"}, display = "[[w:University of Oxford|Oxford University]] [[slang]]", topical_categories = "Universities", plain_categories = true, } labels["ร้อยกรอง"] = { aliases = {"poi"}, -- Only used in Ancient Greek as a holdover from [[Module:grc:Dialects]]. -- should be "terms with X senses", leaving "X terms" to the term-context temp aliases = {"poetic", "กลอน", "กวี"}, glossary = true, pos_categories = "ศัพท์ที่ใช้ในบทร้อยกรอง", } labels["สุภาพ"] = { aliases = {"polite"}, pos_categories = "ศัพท์ที่สุภาพ", } labels["post-classical"] = { aliases = {"Post-classical", "post-Classical", "Post-Classical", "Postclassical", "postclassical"}, display = "post-Classical", regional_categories = true, } labels["prison slang"] = { display = "[[prison]] [[slang]]", pos_categories = true, } labels["ไม่ควรใช้"] = { aliases = {"proscribed", "ห้ามใช้", "ต้องห้าม", "ไม่เป็นที่ยอมรับ"}, glossary = true, pos_categories = "ศัพท์ที่ไม่ควรใช้", } labels["puristic"] = { aliases = {"purism"}, Wikipedia = "Linguistic purism", pos_categories = "puristic terms", } labels["radio slang"] = { display = "[[radio]] [[slang]]", pos_categories = true, } labels["Reddit slang"] = { display = "[[Reddit]] [[slang]]", pos_categories = true, } labels["พบได้ยาก"] = { aliases = {"rare", "rare sense", "พบยาก", "พบน้อย", "พบได้น้อย", "หายาก", "ใช้ไม่บ่อย"}, glossary = true, sense_categories = true, } labels["rare term"] = { display = "rare", -- see comments about "obsolete" pos_categories = "rare terms", } -- cf Cockney rhyming slang labels["rhyming slang"] = { display = "[[rhyming slang]]", pos_categories = true, } labels["religious slur"] = { aliases = {"sectarian slur"}, display = "[[religious]] [[slur]]", pos_categories = "religious slurs", } labels["นิวัตศัพท์"] = { aliases = {"retronym", "retro"}, glossary = true, pos_categories = "นิวัตศัพท์", } labels["reverential"] = { -- should be "terms with X senses", leaving "X terms" to the term-context temp? display = "[[reverential]]", pos_categories = "reverential terms", } labels["ราชาศัพท์"] = { aliases = {"regal", "royal"}, pos_categories = "ราชาศัพท์", } labels["rustic"] = { glossary = true, -- should be "terms with X senses", leaving "X terms" to the term-context temp? aliases = {"rural"}, pos_categories = "rustic terms", } labels["เสียดสี"] = { aliases = {"ประชด", "ประชดประชัน", "เหน็บแนม", "sarcastic", "sarcasm"}, display = "[[เสียดสี]]", pos_categories = "ศัพท์ที่เสียดสี", } labels["school slang"] = { aliases = {"public school slang"}, display = "[[school]] [[slang]]", pos_categories = true, } labels["self-deprecatory"] = { aliases = {"self-deprecating"}, display = "[[self-deprecatory]]", -- should be "terms with X senses", leaving "X terms" to the term-context temp? pos_categories = "self-deprecatory terms", } -- Swahili Sheng cant / argot -- should this be in a language-specific module? labels["Sheng"] = { Wikipedia = "Sheng slang", plain_categories = true, } labels["siglum"] = { aliases = {"sigla"}, glossary = true, pos_categories = "sigla", } labels["สแลง"] = { aliases = {"slang"}, glossary = true, pos_categories = true, } labels["university slang"] = { aliases = {"college slang", "student slang"}, display = "[[university]] [[slang]]", topical_categories = "Universities", pos_categories = "student slang", } labels["Stenoscript"] = { aliases = {"stenoscript"}, display = "[[Stenoscript]]", pos_categories = "Stenoscript abbreviations", } labels["superseded"] = { glossary = true } labels["swear word"] = { aliases = {"profanity", "expletive"}, pos_categories = "swear words", } labels["syncopated"] = { aliases = {"syncope", "syncopic", "syncopation"}, glossary = true, pos_categories = "syncopic forms", } labels["synecdochically"] = { aliases = {"synecdochic", "synecdochical", "synecdoche"}, glossary = true, pos_categories = "synecdoches", } labels["ศัพท์เฉพาะ"] = { aliases = {"technical"}, display = "[[ศัพท์เฉพาะ]]", pos_categories = "ศัพท์เฉพาะ", } labels["telic"] = { glossary = true, } labels["text messaging"] = { aliases = {"texting"}, display = "[[text messaging]]", pos_categories = "text messaging slang", } labels["tone indicator"] = { display = "[[tone indicator]]", pos_categories = "tone indicators", } labels["เครื่องหมายการค้า"] = { aliases = {"trademark", "ยี่ห้อ"}, display = "[[เครื่องหมายการค้า]]", pos_categories = true, } labels["transferred sense"] = { glossary = true, pos_categories = "terms with transferred senses", } labels["transferred senses"] = { display = "[[transferred sense#English|transferred senses]]", pos_categories = "terms with transferred senses", } labels["transgender slang"] = { aliases = {"trans slang"}, display = "[[transgender]] [[slang]]", pos_categories = true, } labels["Twitch-speak"] = { display = "[[Twitch-speak]]", pos_categories = true, } labels["uds."] = { display = "[[Appendix:Spanish pronouns#Ustedes and vosotros|used formally in Spain]]", } labels["uncommon"] = { glossary = true, sense_categories = true, } labels["used only in songs"] = { aliases = {"only used in songs", "songs"}, pos_categories = "terms used only in songs", display = "used only in [[song]]s", } labels["used only in tales"] = { aliases = {"only used in tales", "tales"}, pos_categories = "terms used only in tales", display = "used only in [[tale]]s", } labels["verlan"] = { glossary = true, plain_categories = true, } labels["very rare"] = { display = "very [[Appendix:Glossary#rare|rare]]", sense_categories = "rare", } labels["หยาบคาย"] = { aliases = {"vulgar", "coarse", "obscene", "profane", "หยาบ", "ไม่สุภาพ"}, glossary = true, pos_categories = "ศัพท์ที่หยาบคาย", } labels["vesre"] = { Wikipedia = true, plain_categories = true, } labels["youth slang"] = { display = "[[youth]] [[slang]]", pos_categories = "slang", } labels["2channel slang"] = { aliases = {"2channel", "2ch slang"}, display ="[[w:2channel|2channel]] [[slang]]", pos_categories = {"สแลงอินเทอร์เน็ต" , "2channel slang"}, } -- for Awetí, Karajá, etc., where men & women use different words labels["women's speech"] = { aliases = {"female speech"}, glossary = "women's speech", pos_categories = "women's speech terms", } -- terms applying to Old Norse skaldic poetry labels["kenning"] = { aliases = {"Kenning"}, Wikipedia = "Kenning", pos_categories = "kennings", } labels["heiti"] = { aliases = {"Heiti"}, Wikipedia = "Heiti", pos_categories = true, } return require("Module:labels").finalize_data(labels) r0gucyuum6ekhumghzdahh849edx7pb มอดูล:JSON 828 45169 5775193 1169257 2026-10-14T04:25:54Z Octahedron80 267 5775193 Scribunto text/plain local export = {} local m_math = require("Module:math") local m_str_utils = require("Module:string utilities") local m_table = require("Module:table") local codepoint = m_str_utils.codepoint local concat = table.concat local converter -- forward declaration local find = string.find local format = string.format local gsub = string.gsub local insert = table.insert local ipairs = ipairs local is_array = m_table.isArray local is_finite_real_number = m_math.is_finite_real_number local is_integer = m_math.is_integer local is_utf8 = mw.ustring.isutf8 local match = string.match local toNFC = mw.ustring.toNFC local tonumber = tonumber local pairs = pairs local sorted_pairs = m_table.sortedPairs local trycall = require("Module:fun/trycall") local type = type local function json_fromBoolean(b) return b and "true" or "false" end -- Given a finite real number x, returns a string containing its JSON -- representation, with enough precision that it should round-trip correctly -- (depending on the well-behavedness of the system on the other end). local function json_fromNumber(x, level) if not is_finite_real_number(x) then error(format("Cannot encode non-finite real number %g", x), level) end -- Give integers within the range RFC 7159 considers interoperable. if is_integer(x) and x < 0x1p53 and x > -0x1p53 then return format("%d", x) end -- Otherwise, give a (double) float with the %g specifier, which handles any -- leading/trailing 0s etc. Double floats have precision ranging from 15 to -- 17 digits, meaning rounding artefacts can appear when precision is set to -- 16 or 17 (e.g. 1.1 is converted to 1.1000000000000001). Avoid this by -- trying each in turn, returning the first one which converts back into the -- original number, which avoids implying that it has higher precision than -- it really does. for prec = 15, 17 do local poss = format(format("%%.%dg", prec), x) if prec == 17 or tonumber(poss) == x then x = poss break end end -- If there's an exponent, remove any + sign and leading 0s from it. if find(x, "e", nil, true) then return (gsub(x, "(e%-?)%+?0*", "%1")) end -- If it resembles an integer, convert it to scientific notation to avoid -- the other end interpreting it as one. local d, f = match(x, "^(%d)(%d-)0*$") return d and format("%s%s%se%d", d, f == "" and "" or ".", f, #x - 1) or x end local function escape_codepoint(cp) if cp < 0x10000 then return format("\\u%04X", cp) end cp = cp - 0x10000 return format("\\u%04X\\u%04X", 0xD800 + (cp / 1024), 0xDC00 + (cp % 1024)) end local escapes local function get_escapes() escapes, get_escapes = { [0x8] = [[\b]], [0x9] = [[\t]], [0xA] = [[\n]], [0xC] = [[\f]], [0xD] = [[\r]], [0x22] = [[\"]], [0x2F] = [[\/]], [0x5C] = [[\\]], }, nil local function _add(cp) if escapes[cp] == nil then escapes[cp] = escape_codepoint(cp) end end local function add(cp1, cp2) if cp2 == nil then return _add(cp1) end for cp = cp1, cp2 do _add(cp) end end add(0x0000, 0x001F) add(0x007F, 0x00A0) add(0x00AD) add(0x034F) add(0x0600, 0x0605) add(0x061C) add(0x06DD) add(0x070F) add(0x0890, 0x0891) add(0x08E2) add(0x115F, 0x1160) add(0x1680) add(0x17B4, 0x17B5) add(0x180B, 0x180F) add(0x2000, 0x200F) add(0x2028, 0x202F) add(0x205F, 0x206F) add(0x3000) add(0x3164) add(0xFDD0, 0xFDEF) add(0xFE00, 0xFE0F) add(0xFEFF) add(0xFFA0) add(0xFFF0, 0xFFFF) add(0x110BD) add(0x110CD) add(0x1107F) add(0x13430, 0x1343F) add(0x16FE4) add(0x1BC9D) add(0x1BCA0, 0x1BCA3) add(0x1D173, 0x1D17A) for i = 0x2, 0x11 do i = i * 0x10000 add(i - 2, i - 1) end return escapes end local function escape_char(ch) local cp = codepoint(ch) return (escapes or get_escapes())[cp] or escape_codepoint(cp) end local function maybe_escape_char(ch) local cp = codepoint(ch) if cp >= 0xE0000 and cp <= 0xE0FFF then return escape_char(ch) end return (escapes or get_escapes())[cp] or ch end -- Given a string, escapes any illegal characters and wraps it in double-quotes. -- Raises an error if the string is not valid UTF-8. local function json_fromString(s, ascii, level) if not is_utf8(s) then error(format("Cannot encode non-UTF-8 string '%s'", s), level) end local pattern = '[%c"/\\\128-\255][\128-\191]*' if not ascii then local escaped = gsub(s, pattern, maybe_escape_char) if escaped == toNFC(escaped) then return '"' .. escaped .. '"' end end return '"' .. gsub(s, pattern, escape_char) .. '"' end local function json_fromTable(t, opts, current, level) local ret, open, close = {} if is_array(t) then for key, value in ipairs(t) do ret[key] = converter(value, opts, current, level + 1) or "null" end open, close = "[", "]" else -- `seen_keys` memoizes keys already seen, to prevent collisions (e.g. 1 -- and "1"). local seen_keys, colon, ascii = {}, opts.compress and ":" or " : ", opts.ascii for key, value in (opts.sort_keys and sorted_pairs or pairs)(t) do local key_type = type(key) if key_type == "boolean" then key = json_fromBoolean(key) elseif key_type == "number" then key = json_fromNumber(key, level + 1) elseif key_type ~= "string" then error(format("Cannot use type '%s' as a table key", key_type), level) end key = json_fromString(key, ascii, level + 1) if seen_keys[key] then error(format("Collision for JSON key %s", key), level) end seen_keys[key] = true insert(ret, key .. colon .. (converter(value, opts, current, level + 1) or "null")) end open, close = "{", "}" end ret = open .. ( opts.compress and concat(ret, ",") .. close or " " .. concat(ret, ", ") .. ( #ret == 0 and "" or " " ) .. close ) current[t] = nil return ret end function converter(this, opts, current, level) -- local declared above local val_type = type(this) if val_type == "nil" then return "null" elseif val_type == "boolean" then return json_fromBoolean(this) elseif val_type == "number" then return json_fromNumber(this, level + 1) elseif val_type == "string" then return json_fromString(this, opts.ascii, level + 1) elseif val_type ~= "table" then error(format("Cannot encode type '%s'", val_type), level) elseif current[this] then error("Cannot use recursive tables", level) end -- Memoize the table to enable recursion checking. current[this] = true if opts.ignore_toJSON then return json_fromTable(this, opts, current, level + 1) end -- Check if a toJSON method can be used. Use the lua_table flag to get a Lua -- table, as any options need to be applied to the output. local to_json = this.toJSON if to_json == nil then return json_fromTable(this, opts, current, level + 1) end -- Try to call it. local success, new = trycall(to_json, this, {lua_table = true}) if success then -- If successful, use the returned value. local ret = converter(new, opts, current, level + 1) current[this] = nil return ret end -- Otherwise, treat as a conventional value. return json_fromTable(this, opts, current, level + 1) end -- This function makes an effort to convert an arbitrary Lua value to a string -- containing a JSON representation of it. function export.toJSON(this, opts) return converter(this, opts == nil and {} or opts, {}, 3) end return export 2ak4p5c82rula80h5a4l22jokudmyrq มอดูล:parameters 828 46816 5775230 5684267 2026-10-14T07:29:07Z Octahedron80 267 5775230 Scribunto text/plain --[==[TODO: * Change certain flag names, as some are misnomers: * Change `allow_holes` to `keep_holes`, because it's not the inverse of `disallow_holes`. * Change `allow_empty` to `keep_empty`, as it causes them to be kept as "" instead of deleted. * Sort out all the internal error calls. Manual error(format()) calls are used when certain parameters shouldn't be dumped, so find a way to avoid that. ]==] local export = {} local collation_module = "Module:collation" local families_module = "Module:families" local functions_module = "Module:fun" local gender_and_number_utilities_module = "Module:gender and number utilities" local labels_module = "Module:labels" local languages_module = "Module:languages" local math_module = "Module:math" local pages_module = "Module:pages" local parameters_finalize_set_module = "Module:parameters/finalizeSet" local parameters_track_module = "Module:parameters/track" local parse_utilities_module = "Module:parse utilities" local references_module = "Module:references" local scribunto_module = "Module:Scribunto" local scripts_module = "Module:scripts" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local wikimedia_languages_module = "Module:wikimedia languages" local yesno_module = "Module:yesno" local mw = mw local mw_title = mw.title local string = string local table = table local dump = mw.dumpObject local find = string.find local format = string.format local gsub = string.gsub local insert = table.insert local ipairs = ipairs local list_to_text = mw.text.listToText local make_title = mw_title.makeTitle local match = string.match local max = math.max local new_title = mw_title.new local next = next local pairs = pairs local pcall = pcall local require = require local sub = string.sub local tonumber = tonumber local type = type local unpack = unpack or table.unpack -- Lua 5.2 compatibility local current_title_text, current_namespace, sets -- Defined when needed. local namespaces = mw.site.namespaces --[==[ Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls. ]==] local function decode_entities(...) decode_entities = require(string_utilities_module).decode_entities return decode_entities(...) end local function extend(...) extend = require(table_module).extend return extend(...) end local function finalize_set(...) finalize_set = require(parameters_finalize_set_module) return finalize_set(...) end local function get_family_by_code(...) get_family_by_code = require(families_module).getByCode return get_family_by_code(...) end local function get_family_by_name(...) get_family_by_name = require(families_module).getByCanonicalName return get_family_by_name(...) end local function get_language_by_code(...) get_language_by_code = require(languages_module).getByCode return get_language_by_code(...) end local function get_language_by_name(...) get_language_by_name = require(languages_module).getByCanonicalName return get_language_by_name(...) end local function get_script_by_code(...) get_script_by_code = require(scripts_module).getByCode return get_script_by_code(...) end local function get_script_by_name(...) get_script_by_name = require(scripts_module).getByCanonicalName return get_script_by_name(...) end local function get_wm_lang_by_code(...) get_wm_lang_by_code = require(wikimedia_languages_module).getByCode return get_wm_lang_by_code(...) end local function get_wm_lang_by_code_with_fallback(...) get_wm_lang_by_code_with_fallback = require(wikimedia_languages_module).getByCodeWithFallback return get_wm_lang_by_code_with_fallback(...) end local function gsplit(...) gsplit = require(string_utilities_module).gsplit return gsplit(...) end local function is_callable(...) is_callable = require(functions_module).is_callable return is_callable(...) end local function is_integer(...) is_integer = require(math_module).is_integer return is_integer(...) end local function is_internal_title(...) is_internal_title = require(pages_module).is_internal_title return is_internal_title(...) end local function is_positive_integer(...) is_positive_integer = require(math_module).is_positive_integer return is_positive_integer(...) end local function iterate_list(...) iterate_list = require(table_module).iterateList return iterate_list(...) end local function num_keys(...) num_keys = require(table_module).numKeys return num_keys(...) end local function parse_gender_and_number_spec(...) parse_gender_and_number_spec = require(gender_and_number_utilities_module).parse_gender_and_number_spec return parse_gender_and_number_spec(...) end local function parse_references(...) parse_references = require(references_module).parse_references return parse_references(...) end local function pattern_escape(...) pattern_escape = require(string_utilities_module).pattern_escape return pattern_escape(...) end local function php_trim(...) php_trim = require(scribunto_module).php_trim return php_trim(...) end local function scribunto_parameter_key(...) scribunto_parameter_key = require(scribunto_module).scribunto_parameter_key return scribunto_parameter_key(...) end local function sort(...) sort = require(collation_module).sort return sort(...) end local function sorted_pairs(...) sorted_pairs = require(table_module).sortedPairs return sorted_pairs(...) end local function split(...) split = require(string_utilities_module).split return split(...) end local function split_labels_on_comma(...) split_labels_on_comma = require(labels_module).split_labels_on_comma return split_labels_on_comma(...) end local function split_on_comma(...) split_on_comma = require(parse_utilities_module).split_on_comma return split_on_comma(...) end local function tonumber_extended(...) tonumber_extended = require(math_module).tonumber_extended return tonumber_extended(...) end local function track(...) track = require(parameters_track_module) return track(...) end local function yesno(...) yesno = require(yesno_module) return yesno(...) end --[==[ intro: This module is used to standardize template argument processing and checking. A typical workflow is as follows (based on [[Module:translations]]): { ... local parent_args = frame:getParent().args local params = { [1] = {required = true, type = "language", default = "und"}, [2] = true, [3] = {list = true}, ["alt"] = true, ["id"] = true, ["sc"] = {type = "script"}, ["tr"] = true, ["ts"] = true, ["lit"] = true, } local args = require("Module:parameters").process(parent_args, params) -- Do further processing of the parsed arguments in `args`. ... } The `params` table should have the parameter names as the keys, and a (possibly empty) table of parameter tags as the value. An empty table as the value merely states that the parameter exists, but should not receive any special treatment; if desired, empty tables can be replaced with the value `true` as a perforamnce optimization. Possible parameter tags are listed below: ; {required = true} : The parameter is required; an error is shown if it is not present. The template's page itself is an exception; no error is shown there. ; {default =} : Specifies a default input value for the parameter, if it is absent or empty. This will be processed as though it were the input instead, so (for example) {default = "und"} with the type {"language"} will return a language object for [[:Category:Undetermined language|Undetermined language]] if no language code is provided. (As an exception, numbers and booleans can be specified either as-is or in string format.) When used on list parameters, this specifies a default value for the first item in the list only. Note that it is not possible to generate a default that depends on the value of other parameters. If used together with {required = true}, the default applies only to template pages (see the following entry), as a side effect of the fact that "required" parameters aren't actually required on template pages. This can be used to show an example of the template in action when the template page is visited; however, it is preferred to use `template_default` for this purpose, for clarity. ; {template_default =} : Specifies a default input value for absent or empty parameters only on the template demo invocation (the invocation of the template that is displayed when the template page that implements the template is viewed). Template pages are pages in template space that invoke (through {{tl|#invoke:}}) the module that implements the template and calls [[Module:parameters]]. For example, the page [[Template:en-noun]] implements the {{tl|en-noun}} template, which in turn invokes [[Module:en-headword]], and is a template page for [[Module:en-headword]]. When the template page [[Template:en-noun]] is visited, the {{tl|#invoke:}} of the template's module is expanded as if the template were called without arguments, and the output is inserted at that point into the processed page. This output serves as a sort of demo of the template's functionality. `template_default` can be used to supply default values for use only in this demo. Since the template page may also contain other invocations of the same template (e.g. on the template's documentation page, which is typically transcluded into the template page itself), `template_default` does not apply if there are any arguments passed to the template or if the template is invoked on any other page but its own template page (which is checked by comparing the name of the invoking template to the current pagename). Both `template_default` and `default` can be specified for the same parameter. If this is done, `template_default` applies for the argumentless template invocation on the template page, and `default` in all other circumstances As an example, {{tl|cs-IPA}} uses the equivalent of {[1] = {default = "+", template_default = "příklad"}} to supply a default of {"+"} for mainspace and documentation pages (which tells the module to use the value of the {{para|pagename}} parameter, falling back to the actual pagename), but {"příklad"} (which means "example"), on [[Template:cs-IPA]]. ; {alias_of =} : Treat the parameter as an alias of another. When arguments are specified for this parameter, they will automatically be renamed and stored under the alias name. This allows for parameters with multiple alternative names, while still treating them as if they had only one name. The conversion-related properties of an aliased parameter (e.g. `type`, `set`, `convert`, `sublist`) are taken from the aliasee, and the corrresponding properties set on the alias itself are ignored; but other properties on the alias are taken from the alias's spec and not from the aliasee's spec. This means, for example, that if you create an alias of a list parameter, the alias must also specify the `list` property or it is not a list. (In such a case, a value specified for the alias goes into the first item of the aliasee's list. You cannot make a list alias of a non-list parameter; this causes an error to be thrown.) Similarly, if you specify `separate_no_index` on an aliasee but not on the alias, uses of the unindexed aliasee parameter are stored into the `.default` key, but uses of the unindexed alias are stored into the first numbered key of the aliasee's list. Aliases cannot be required, as this prevents the other name or names of the parameter from being used. Parameters that are aliases and required at the same time cause an error to be thrown. ; {allow_empty = true} : If the argument is an empty string value, it is not converted to {nil}, but kept as-is. The use of `allow_empty` is disallowed if a type has been specified, and causes an error to be thrown. ; {no_trim = true} : Spacing characters such as spaces and newlines at the beginning and end of a positional parameter are not removed. (MediaWiki itself automatically trims spaces and newlines at the edge of named parameters.) The use of `no_trim` is disallowed if a type has been specified, and causes an error to be thrown. ; {type =} : Specifies what value type to convert the argument into. The default is to leave it as a text string. Alternatives are: :; {type = "boolean"} :: The value is treated as a boolean value, either true or false. No value, the empty string, and the strings {"0"}, {"no"}, {"n"}, {"false"}, {"f"} and {"off"} (case-insensitive) are treated as {false}, all other values are considered {true}. :; {type = "boolean or string"} :: The value is treated as either a boolean value (true or false) or some other string value. The strings {"0"}, {"no"}, {"n"}, {"false"}, {"f"} and {"off"} (case-insensitive) are treated as {false}; the string {"1"}, {"yes"}, {"y"}, {"true"}, {"t"} and {"on"} (case-insensitive) are treated as {true}; and all other values are left as-is. You can use `set` to restrict the possible strings that are allowed; the values specified using `set` should not include {true} or {false}. :; {type = "number"} :: The value is converted into a number, and throws an error if the value is not parsable as a number. Input values may be signed (`+` or `-`), and may contain decimal points and leading zeroes. If {allow_hex = true}, then hexadecimal values in the form {"0x100"} may optionally be used instead, which otherwise have the same syntax restrictions (including signs, decimal digits, and leading zeroes after {"0x"}). Hexadecimal inputs are not case-sensitive. Lua's special number values (`inf` and `nan`) are not possible inputs. :; {type = "range"} :: The value is interpreted as a hyphen-separated range of two numbers (e.g. {"2-4"} is interpreted as the range from {2} to {4}). A number input without a hyphen is interpreted as a range from that number to itself (e.g. the input {"1"} is interpreted as the range from {1} to {1}). Any optional flags which are available for numbers will also work for ranges. :; {type = "language"} :: The value is interpreted as a full or [[Wiktionary:Languages#Etymology-only languages|etymology-only language]] code language code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:languages]]). If the code or name is invalid, then an error is thrown. The additional setting {family = true} can be given to allow [[Wiktionary:Language families|language family codes]] to be considered valid and the corresponding object returned. Note that to distinguish an etymology-only language object from a full language object, use {object:hasType("language", "etymology-only")}. :; {type = "full language"} :: The value is interpreted as a full language code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:languages]]). If the code or name is invalid, then an error is thrown. Etymology-only languages are not allowed. The additional setting {family = true} can be given to allow [[Wiktionary:Language families|language family codes]] to be considered valid and the corresponding object returned. :; {type = "Wikimedia language"} :: The value is interpreted as a code and converted into a Wikimedia language object. If the code is invalid, then an error is thrown. If {fallback = true} is specified, conventional language codes which are different from their Wikimedia equivalent will also be accepted as a fallback. :; {type = "family"} :: The value is interpreted as a language family code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:families]]). If the code or name is invalid, then an error is thrown. :; {type = "script"} :: The value is interpreted as a script code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:scripts]]). If the code or name is invalid, then an error is thrown. :; {type = "title"} :: The value is interpreted as a page title and converted into the corresponding object (see the [[mw:Extension:Scribunto/Lua_reference_manual#Title_library|Title library]]). If the page title is invalid, then an error is thrown; by default, external titles (i.e. those on other wikis) are not treated as valid. Options are: ::; {namespace = n} ::: The default namespace, where {n} is a namespace number; this is treated as {0} (the mainspace) if not specified. ::; {allow_external = true} ::: External titles are treated as valid. ::; {prefix = "namespace override"} (default) ::: The default namespace prefix will be prefixed to the value is already prefixed by a namespace prefix. For instance, the input {"Foo"} with namespace {10} returns {"Template:Foo"}, {"Wiktionary:Foo"} returns {"Wiktionary:Foo"}, and {"Template:Foo"} returns {"Template:Foo"}. Interwiki prefixes cannot act as overrides, however: the input {"fr:Foo"} returns {"Template:fr:Foo"}. ::; {prefix = "force"} ::: The default namespace prefix will be prefixed unconditionally, even if the value already appears to be prefixed. This is the way that {{tl|#invoke:}} works when calling modules from the module namespace ({828}): the input {"Foo"} returns {"Module:Foo"}, {"Wiktionary:Foo"} returns {"Module:Wiktionary:Foo"}, and {"Module:Foo"} returns {"Module:Module:Foo"}. ::; {prefix = "full override"} ::: The same as {prefix = "namespace override"}, except that interwiki prefixes can also act as overrides. For instance, {"el:All topics"} with namespace {14} returns {"el:Category:All topics"}. Due to the limitations of MediaWiki, only the first prefix in the value may act as an override, so the namespace cannot be overridden if the first prefix is an interwiki prefix: e.g. {"el:Template:All topics"} with namespace {14} returns {"el:Category:Template:All topics"}. :; {type = "parameter"} :: The value is interpreted as the name of a parameter, and will be normalized using the method that Scribunto uses when constructing a {frame.args} table of arguments. This means that integers will be converted to numbers, but all other arguments will remain as strings (e.g. {"1"} will be normalized to {1}, but {"foo"} and {"1.5"} will remain unchanged). Note that Scribunto also trims parameter names, following the same trimming method that this module applies by default to all parameter types. :: This type is useful when one set of input arguments is used to construct a {params} table for use in a subsequent {export.process()} call with another set of input arguments; for instance, the set of valid parameters for a template might be defined as {{tl|#invoke:[some module]|args=}} in the template, where {args} is a sublist of valid parameters for the template. :; {type = "qualifier"} :: The value is interpreted as a qualifier and converted into the correct format for passing into `format_qualifiers()` in [[Module:qualifier]] (which currently just means converting it to a one-item list). :; {type = "labels"} :: The value is interpreted as a comma-separated list of labels and converted into the correct format for passing into `show_labels()` in [[Module:labels]] (which is currently a list of strings). Splitting is done on commas not followed by whitespace, except that commas inside of double angle brackets do not count even if not followed by whitespace. This type should be used by for normal labels (typically specified using {{para|l}} or {{para|ll}}) and accent qualifiers (typically specified using {{para|a}} and {{para|aa}}). :; {type = "references"} :: The value is interpreted as one or more references, in the format prescribed by `parse_references()` in [[Module:references]], and converted into a list of objects of the form accepted by `format_references()` in the same module. If a syntax error is found in the reference format, an error is thrown. :; {type = "genders"} :: The value is interpreted as one or more comma-separated gender/number specs, in the format prescribed by [[Module:gender and number]]. Inline modifiers (`<q:...>`, `<qq:...>`, `<l:...>`, `<ll:...>` or `<ref:...>`) may be attached to a gender/number spec. :; {type = "form of tags"} :: The value is interpreted as an ampersand-separated list of grammar tags and converted into the correct format for passing as `tags` into `tagged_inflections()` in [[Module:form of]] (which is currently a list of strings). Splitting is always done by ampersands. This type should be used by for inflection qualifiers that act as grammar tags (typically specified using {{para|infl}}). :; {type = function(val) ... end} :: `type` may be set to a function (or callable table), which must take the argument value as its sole argument, and must output one of the other recognized types. This is particularly useful for lists (see below), where certain values need to be interpreted differently to others. ; {list =} : Treat the parameter as a list of values, each having its own parameter name, rather than a single value. The parameters will have a number at the end, except optionally for the first (but see also {require_index = true}). For example, {list = true} on a parameter named "head" will include the parameters {{para|head}} (or {{para|head1}}), {{para|head2}}, {{para|head3}} and so on. If the parameter name is a number, another number doesn't get appended, but the counting simply continues, e.g. for parameter {3} the sequence is {{para|3}}, {{para|4}}, {{para|5}} etc. List parameters are returned as numbered lists, so for a template that is given the parameters `|head=a|head2=b|head3=c`, the processed value of the parameter {"head"} will be { { "a", "b", "c" }}}. : The value for {list =} can also be a string. This tells the module that parameters other than the first should have a different name, which is useful when the first parameter in a list is a number, but the remainder is named. An example would be for genders: {list = "g"} on a parameter named {1} would have parameters {{para|1}}, {{para|g2}}, {{para|g3}} etc. : If the number is not located at the end, it can be specified by putting {"\1"} at the number position. For example, parameters {{para|f1accel}}, {{para|f2accel}}, ... can be captured by using the parameter name {"f\1accel"}, as is done in [[Module:headword/templates]]. ; {set =} : Require that the value of the parameter be one of the specified values (or omitted, if {required = true} isn't given). Two formats are allowed; either a list of possible values can be supplied, or a table can be supplied where the keys are allowed values and the values are either `true` or a string naming a value found elsewhere in the table as a key. In the latter case, the key is an alias and the value is the canonical value, and if the user uses the alias, it will automatically be mapped to the canonical value. In such a case, the canonical value cannot itself be an alias. The use of `set` is disallowed if {type = "boolean"} and causes an error to be thrown. The values given should be strings (i.e. pre-conversion values) unless {type = "number"}, in which case they should be numbers. If {type = "boolean or string"}, the values specified using {set =} are those that ''cannot'' be interpreted as a boolean; boolean strings (e.g. {"1"}, {"0"}, {"true"}, {"false"}, etc.) are always allowed in addition. ; {sublist =} : The value of the parameter is a delimiter-separated list of individual raw values. The resulting field in `args` will be a Lua list (i.e. a table with numeric indices) of the converted values. If {sublist = true} is given, the values will be split on commas (possibly with whitespace on one or both sides of the comma, which is ignored). If {sublist = "comma without whitespace"} is given, the values will be split on commas which are not followed by whitespace, and which aren't preceded by an escaping backslash. Otherwise, the value of `sublist` should be either a Lua pattern specifying the delimiter(s) to split on or a function (or callable table) to do the splitting, which is passed two values (the value to split and a function to signal an error) and should return a list of the split values. ; {convert =} : If given, this specifies a function (or callable table) to convert the raw parameter value into the Lua object used during further processing. The function is passed two arguments, the raw parameter value itself and a function used to signal an error during parsing or conversion, and should return one value, the converted parameter. The error-signaling function contains the name and raw value of the parameter embedded into the message it generates, so these do not need to specified in the message passed into it. If `type` is specified in conjunction with `convert`, the processing by `type` happens first. If `sublist` is given in conjunction with `convert`, the raw parameter value will be split appropriately and `convert` called on each resulting item. ; {allow_hex = true} : When used in conjunction with {type = "number"}, allows hexadecimal numbers as inputs, in the format {"0x100"} (which is not case-sensitive). ; {family = true} : When used in conjunction with {type = "language"}, allows [[Wiktionary:Language families|language family codes]] to be returned. To check if a given object refers to a language family, use {object:hasType("family")}. ; {method = "name"} : When used in conjunction with {type = "language"}, {type = "family"} or {type = "script"}, checks for and parses a language, family or script name instead of a code. ; {allow_holes = true} : This is used in conjunction with list-type parameters. By default, the values are tightly packed in the resulting list. This means that if, for example, an entry specified `head=a|head3=c` but not {{para|head2}}, the returned list will be { {"a", "c"}}}, with the values stored at the indices {1} and {2}, not {1} and {3}. If it is desirable to keep the numbering intact, for example if the numbers of several list parameters correlate with each other (like those of {{tl|affix}}), then this tag should be specified. : If {allow_holes = true} is given, there may be {nil} values in between two real values, which makes many of Lua's table processing functions no longer work, like {#} or {ipairs()}. To remedy this, the resulting table will contain an additional named value, `maxindex`, which tells you the highest numeric index that is present in the table. In the example above, the resulting table will now be { { "a", nil, "c", maxindex = 3}}}. That way, you can iterate over the values from {1} to `maxindex`, while skipping {nil} values in between. ; {disallow_holes = true} : This is used in conjunction with list-type parameters. As mentioned above, normally if there is a hole in the source arguments, e.g. `head=a|head3=c` but not {{para|head2}}, it will be removed in the returned list. If {disallow_holes = true} is specified, however, an error is thrown in such a case. This should be used whenever there are multiple list-type parameters that need to line up (e.g. both {{para|head}} and {{para|tr}} are available and {{para|head3}} lines up with {{para|tr3}}), unless {allow_holes = true} is given and you are prepared to handle the holes in the returned lists. ; {disallow_missing = true} : This is similar to {disallow_holes = true}, but an error will not be thrown if an argument is blank, rather than completely missing. This may be used to tolerate intermediate blank numerical parameters, which sometimes occur in list templates. For instance, `head=a|head2=|head3=c` will not throw an error, but `head=a|head3=c` will. ; {require_index = true} : This is used in conjunction with list-type parameters. By default, the first parameter can have its index omitted. For example, a list parameter named `head` can have its first parameter specified as either {{para|head}} or {{para|head1}}. If {require_index = true} is specified, however, only {{para|head1}} is recognized, and {{para|head}} will be treated as an unknown parameter. {{tl|affixusex}} (and variants {{tl|suffixusex}}, {{tl|prefixusex}}) use this, for example, on all list parameters. ; {separate_no_index = true} : This is used to distinguish between {{para|head}} and {{para|head1}} as different parameters. For example, in {{tl|affixusex}}, to distinguish between {{para|sc}} (a script code for all elements in the usex's language) and {{para|sc1}} (the script code of the first element, used when the first element is prefixed with a language code to indicate that it is in a different language). When this is used, the resulting table will contain an additional named value, `default`, which contains the value for the indexless argument. ; {flatten = true} : This is used in conjunction with list-type parameters when `sublist` or a list-generating type such as {"labels"} or {"genders"} is also specified, and causes the resulting list to be flattened. Not currently compatible with {allow_holes = true}. ; {replaced_by =} : Specifies that the parameter is no longer valid, and has been replaced by some other mechanism. If the value of `replaced_by` is a string, it is the name of the new parameter to use instead. Use the `reason` tag to specify the reason why this change has been made, e.g. {reason = "for consistency with the corresponding parameter in other Romance-language headword templates"}. If the value of `replaced_by` is {false}, there is no replacement parameter. In this case, `instead` should be supplied with a description of what to do instead, e.g. {instead = "use an inline modifier on |2= such as <q:...>, <qq:...>, <l:...> or <ll:...>"}. You can also supply a justification in `reason` if you feel it is appropriate or necessary to do so. ; {reason =} : When used in conjunction with `replaced_by`, specifies the reason for the parameter replacement. ; {instead =} : When used in conjunction with {replaced_by = false}, specifies what to do instead of using the removed parameter. ; {demo = true} : This is used as a way to ensure that the parameter is only enabled on the template's own page (and its documentation page), and in the User: namespace; otherwise, it will be treated as an unknown parameter. This should only be used if special settings are required to showcase a template in its documentation (e.g. adjusting the pagename or disabling categorization). In most cases, it should be possible to do this without using demo parameters, but they may be required if a template/documentation page also contains real uses of the same template as well (e.g. {{tl|shortcut}}), as a way to distinguish them. ; {deprecated = true} : This is for tracking the use of deprecated parameters, including any aliases that are being brought out of use. See [[Wiktionary:Tracking]] for more information. ]==] -- Returns true if the current page is a template or module containing the current {{#invoke}}. -- If the include_documentation argument is given, also returns true if the current page is either page's documentation page. local own_page, own_page_or_documentation local function is_own_page(include_documentation) if own_page == nil then if current_namespace == nil then local current_title = mw_title.getCurrentTitle() current_title_text, current_namespace = current_title.prefixedText, current_title.namespace end local frame = current_namespace == 828 and mw.getCurrentFrame() or current_namespace == 10 and mw.getCurrentFrame():getParent() if frame then local frame_title_text = frame:getTitle() own_page = current_title_text == frame_title_text own_page_or_documentation = own_page or current_title_text == frame_title_text .. "/documentation" else own_page, own_page_or_documentation = false, false end end return include_documentation and own_page_or_documentation or own_page end -------------------------------------- Some helper functions ----------------------------- -- Convert a list in `list` to a string, separating the final element from the preceding one(s) by `conjunction`. If -- `dump_vals` is given, pass all values in `list` through mw.dumpObject() (WARNING: this destructively modifies -- `list`). This is similar to serialCommaJoin() in [[Module:table]] when used with the `dontTag = true` option, but -- internally uses mw.text.listToText(). local function concat_list(list, conjunction, dump_vals) if dump_vals then for k, v in pairs(list) do list[k] = dump(v) end end return list_to_text(list, nil, conjunction) end -- A helper function for use with generating error-signaling functions in the presence of raw value conversion. Format a -- message `msg`, including the processed value `processed` if it is different from the raw value `rawval`; otherwise, -- just return `msg`. local function msg_with_processed(msg, rawval, processed) if rawval == processed then return msg end local processed_type = type(processed) return format("%s (processed value %s)", msg, (processed_type == "string" or processed_type == "number") and processed or dump(processed) ) end -- Separate form of tags with ampersand (&). local function split_tags_on_ampersand(tags) return split(tags, "&") end -------------------------------------- Error handling ----------------------------- local function process_error(fmt, ...) local args = {...} for i, val in ipairs(args) do args[i] = dump(val) end if type(fmt) == "table" then -- hacky signal that we're called from internal_process_error(), and not to omit stack frames return error(format(fmt[1], unpack(args))) end return error(format(fmt, unpack(args)), 3) end local function internal_process_error(fmt, ...) process_error({"Internal error in `params` table: " .. fmt}, ...) end -- Check that a parameter or argument is in the form form Scribunto normalizes input argument keys into (e.g. 1 not "1", "foo" not " foo "). Otherwise, it won't be possible to normalize inputs in the expected way. Unless is_argument is set, also check that the name only contains one placeholder at most, and that strings don't resolve to numeric keys once the placeholder has been substituted. local function validate_name(name, desc, extra_name, is_argument) local normalized = scribunto_parameter_key(name) if name and name == normalized then if is_argument or type(name) ~= "string" then return end local placeholder = find(name, "\1", nil, true) if not placeholder then return elseif find(name, "\1", placeholder + 1, true) then error(format( "Internal error: expected %s to only contain one placeholder, but saw %s", extra_name and (desc .. dump(extra_name)) or desc, dump(name) )) end local first_name = gsub(name, "\1", "1") normalized = scribunto_parameter_key(first_name) if first_name == normalized then return end error(format( "Internal error: %s cannot resolve to numeric parameters once any placeholder has been substituted, but %s resolves to %s", extra_name and (desc .. dump(extra_name)) or desc, dump(name), dump(normalized) )) elseif normalized == nil then error(format( "Internal error: expected %s to be of type string or number, but saw %s", extra_name and (desc .. dump(extra_name)) or desc, type(name) )) end error(format( "Internal error: expected %s to be Scribunto-compatible: %s (a %s) should be %s (a %s)", extra_name and (desc .. dump(extra_name)) or desc, dump(name), type(name), dump(normalized), type(normalized) )) end local function validate_alias_options(...) local invalid = { required = true, default = true, template_default = true, allow_holes = true, disallow_holes = true, disallow_missing = true, } function validate_alias_options(param, name, main_param, alias_of) for k in pairs(param) do if invalid[k] then track("bad alias option") -- internal_process_error( -- "parameter %s cannot have the option %s, as it is an alias of parameter %s.", -- name, option, alias_of -- ) end end -- Soon, aliases will inherit options from the main parameter via __index. Track cases where this would happen. if main_param ~= true then for k in pairs(main_param) do if param[k] == nil and not invalid[k] then if k == "list" then -- these need to be changed to list = false to retain current behaviour track("mismatched list alias option") elseif not (k == "type" or k == "set" or k == "sublist") then -- rarely specified on aliases, as they're effectively inherited already track("mismatched alias option") end end end end end validate_alias_options(...) end -- TODO: give ranges instead of long lists, if possible. --[==[ func: export.params_list_error(params, msg) Given a key-value table of raw parameters `params`, display an error message about all the parameters seen in the table. The parameter names are displayed in sorted order. `msg` should be e.g. {"required"} or {"not used by this template"}. This is used internally to display error messages about required or invalid parameters, and can be used for the same purpose by code that processes its own parameters (e.g. if the `return_unknown` flag is specified to `process`). ]==] local function params_list_error(params, msg) local list, n = {}, 0 for name in sorted_pairs(params) do n = n + 1 list[n] = name end error(format( "Parameter%s %s.", format(n == 1 and " %s is" or "s %s are", concat_list(list, " and ", true)), msg ), 3) end export.params_list_error = params_list_error -- Helper function for use with convert_val_error(). Format a list of possible choices using `concat_list` and -- conjunction "or", displaying "either " before the choices if there's more than one. local function format_choice_list(valid) return (#valid > 1 and "either " or "") .. concat_list(valid, " or ") end -- Signal an error for a value `val` that is not of the right type `valid` (which is either a string specifying a type, or -- a list of possible values, in the case where `set` was used). `name` is the name of the parameter and can be a -- function to signal an error (which is assumed to automatically display the parameter's name and value). `seetext` is -- an optional additional explanatory link to display (e.g. [[WT:LOL]], the list of possible languages and codes). local function convert_val_error(val, name, valid, seetext) if is_callable(name) then if type(valid) == "table" then valid = "choice, must be " .. format_choice_list(valid) end name(format("Invalid %s; the value %s is not valid%s", valid, val, seetext and "; see " .. seetext or "")) else if type(valid) == "table" then valid = format_choice_list(valid) else valid = "a valid " .. valid end error(format("Parameter %s must be %s; the value %s is not valid.%s", dump(name), valid, dump(val), seetext and " See " .. seetext .. "." or "")) end end -- Generate the appropriate error-signaling function given parameter value `val` and name `name`. If `name` is already -- a function, it is just returned; otherwise a function is generated and returned that displays the passed-in messaeg -- along with the parameter's name and value. local function make_parse_err(val, name) if is_callable(name) then return name end return function(msg) error(format("%s: parameter %s=%s", msg, name, val)) end end -------------------------------------- Value conversion ----------------------------- -- For a list parameter `name` and corresponding value `list_name` of the `list` field (which should have the same value -- as `name` if `list = true` was given), generate a pattern to match parameters of the list and store the pattern as a -- key in `patterns`, with corresponding value set to `name`. For example, if `list_name` is "tr", the pattern will -- match "tr" as well as "tr1", "tr2", ..., "tr10", "tr11", etc. If the `list_name` contains a \1 in it, the numeric -- portion goes in place of the \1. For example, if `list_name` is "f\1accel", the pattern will match "faccel", -- "f1accel", "f2accel", etc. Any \1 in `name` is removed before storing into `patterns`. local function save_pattern(name, list_name, patterns) name = type(name) == "string" and gsub(name, "\1", "") or name if find(list_name, "\1", nil, true) then patterns["^" .. gsub(pattern_escape(list_name), "\1", "([1-9]%%d*)") .. "$"] = name else patterns["^" .. pattern_escape(list_name) .. "([1-9]%d*)$"] = name list_name = list_name .. "\1" end validate_name(list_name, "the list field of parameter ", name) return patterns end -- A helper function for use with `sublist`. It is an iterator function for use in a for-loop that returns split -- elements of `val` using `sublist` (a Lua split pattern; boolean `true` to split on commas optionally surrounded by -- whitespace; "comma without whitespace" to split only on commas not followed by whitespace which have not been escaped -- by a backslash; or a function to do the splitting, which is passed two values, the value to split and a function to -- signal an error, and should return a list of the split elements). `name` is the parameter name or error-signaling -- function passed into convert_val(). local function split_sublist(val, name, sublist) if sublist == true then return gsplit(val, "%s*,%s*") -- Split an argument on comma, but not comma followed by whitespace. elseif sublist == "comma without whitespace" then -- If difficult cases, use split_on_comma. if find(val, "\\", nil, true) or match(val, ",%s") then return iterate_list(split_on_comma(val)) end -- Otherwise, use gsplit. return gsplit(val, ",") elseif type(sublist) == "string" then return gsplit(val, sublist) elseif not is_callable(sublist) then error(format('Internal error: expected `sublist` to be of type "string" or "function" or boolean `true`, but saw %s', dump(sublist))) end return iterate_list(sublist(val, make_parse_err(val, name))) end --[=[ For parameter named `name` with value `val` and param spec `param`, if the `set` field is specified, verify that the value is one of the one specified in `set`, and throw an error otherwise. `name` is taken directly from the corresponding parameter passed into convert_val() and may be a function to signal an error. Optional `param_type` is a string specifying the conversion type of `val` and is used for special-casing: If `param_type` is "boolean", an internal error is thrown (since `set` cannot be used in conjunction with booleans); and if `param_type` is "number" or "boolean or string", and `called_from_converter` is not given, no checking happens because in these cases `set` needs to be called by the conversion function itself (respectively after converting `val` to a number or processing boolean strings). Return the canonical value of `val` (which may be different from `val` if an alias map is given). ]=] local function check_set(val, name, param, param_type, called_from_converter) -- Needs to be special cased because the check happens after conversion to numbers. if (param_type == "number" or param_type == "boolean or string") and not called_from_converter then return val end local set = param.set local map if sets == nil then map = finalize_set(set, name) sets = {[set] = map} else map = sets[set] if map == nil then map = finalize_set(set, name) sets[set] = map end end local newval = map[val] if newval == true then return val elseif newval ~= nil then return newval end local list = {} for k, v in sorted_pairs(map) do if v == true then insert(list, dump(k)) else insert(list, ("%s (alias of %s)"):format(dump(k), dump(v))) end end -- If the parameter type is "boolean or string", put "a boolean string value" because such values are allowed. if param_type == "boolean or string" then insert(list, "a boolean string value") end -- If the parameter is not required then put "or empty" at the end of the list, to avoid implying the parameter is -- actually required. if not param.required then insert(list, "empty") end convert_val_error(val, name, list) end local function convert_language(val, name, param, allow_etym) local method = param.method local func if method == nil or method == "code" then func, method = get_language_by_code, "code" elseif method == "name" then func, method = get_language_by_name, "name" else error(format('Internal error: expected `method` for type `language` to be "code", "name" or undefined, but saw %s', dump(method))) end local lang = func(val, nil, allow_etym, param.family) if lang then return lang end local list, links = {"language"}, {"[[WT:LOL]]"} if allow_etym then insert(list, "etymology language") insert(links, "[[WT:LOL/E]]") end if param.family then insert(list, "family") insert(links, "[[WT:LOF]]") end convert_val_error(val, name, concat_list(list, " or ") .. " " .. (method == "name" and "name" or "code"), concat_list(links, " and ")) end local function convert_number(val, allow_hex) -- Call tonumber_extended with the `real_finite` flag, which filters out ±infinity and NaN. -- By default, specify base 10, which prevents 0x hex inputs from being converted. -- If `allow_hex` is set, then don't give a base, which means 0x hex inputs will work. local num = tonumber_extended(val, not allow_hex and 10 or nil, "finite_real") if not num then return num end if match(val, "[eEpP.]") then -- float track("number not an integer") end if find(val, "+", nil, true) then track("number with +") end -- Track various unusual number inputs to determine if it should be restricted to positive integers by default (possibly including 0). if not is_positive_integer(num) then track("number not a positive integer") if num == 0 then track("number is 0") elseif not is_integer(num) then track("number not an integer") end end return num end -- TODO: validate parameter specs separately, as it's making the handler code really messy at the moment. local type_handlers = setmetatable({ ["boolean"] = function(val, name, param) local newval = yesno(val, val) if newval ~= true and newval ~= false then newval = true end return newval end, ["boolean or string"] = function(val, name, param) local newval = yesno(val, val) if newval ~= true and newval ~= false then if param.set then newval = check_set(newval, name, param, "boolean or string", "called_from_converter") else newval = true end end return newval end, ["family"] = function(val, name, param) local method = param.method local func if method == nil or method == "code" then func, method = get_family_by_code, "code" elseif method == "name" then func, method = get_family_by_name, "name" else error(format('Internal error: expected `method` for type `family` to be "code", "name" or undefined, but saw %s', dump(method))) end return func(val) or convert_val_error(val, name, "family " .. method, "[[WT:LOF]]") end, ["labels"] = function(val, name, param) -- FIXME: Should be able to pass in a parse_err function. return split_labels_on_comma(val) end, ["form of tags"] = function(val, name, param) return split_tags_on_ampersand(val) end, ["language"] = function(val, name, param) return convert_language(val, name, param, true) end, ["full language"] = convert_language, ["number"] = function(val, name, param) local allow_hex = param.allow_hex if allow_hex and allow_hex ~= true then error(format( 'Internal error: expected `allow_hex` for type `number` to be of type "boolean" or undefined, but saw %s', dump(allow_hex) )) end local num = convert_number(val, allow_hex) if param.set then num = check_set(num, name, param, "number", "called_from_converter") end if num then return num end convert_val_error(val, name, (allow_hex and "decimal or hexadecimal " or "") .. "number") end, ["range"] = function(val, name, param) local allow_hex = param.allow_hex if allow_hex and allow_hex ~= true then error(format( 'Internal error: expected `allow_hex` for type `range` to be of type "boolean" or undefined, but saw %s', dump(allow_hex) )) end -- Pattern ensures leading minus signs are accounted for. local m1, m2 = match(val, "^(%s*%S.-)%-(%s*%S.*)") if m1 then m1 = convert_number(m1, allow_hex) if m1 then m2 = convert_number(m2, allow_hex) if m2 then return {m1, m2} end end end -- Try `val` if it couldn't be split into a range, and return a range of `val` to `val` if possible. local num = convert_number(val, allow_hex) if num then return {num, num} end convert_val_error(val, name, (allow_hex and "decimal or hexadecimal " or "") .. "number or a hyphen-separated range of two numbers") end, ["parameter"] = function(val, name, param) -- Use the `no_trim` option, as any trimming will have already been done. return scribunto_parameter_key(val, true) end, ["qualifier"] = function(val, name, param) return {val} end, ["references"] = function(val, name, param) return parse_references(val, make_parse_err(val, name)) end, ["genders"] = function(val, name, param) if not val:find("[,<]") then return {{spec = val}} end -- NOTE: We don't pass in allow_space_around_comma. Consistent with other comma-separated types, there shouldn't -- be spaces around the comma. return parse_gender_and_number_spec { spec = val, parse_err = make_parse_err(val, name), allow_multiple = true, } end, ["script"] = function(val, name, param) local method, func = param.method if method == nil or method == "code" then func, method = get_script_by_code, "code" elseif method == "name" then func, method = get_script_by_name, "name" else error(format('Internal error: expected `method` for type `script` to be "code", "name" or undefined, but saw %s', dump(method))) end return func(val) or convert_val_error(val, name, "script " .. method, "[[WT:LOS]]") end, ["string"] = function(val, name, param) -- To be removed as unnecessary. track("string") return val end, -- TODO: add support for resolving to unsupported titles. -- TODO: split this into "page name" (i.e. internal) and "link target" (i.e. external as well), which is more intuitive. ["title"] = function(val, name, param) local namespace = param.namespace if namespace == nil then namespace = 0 else local valid_type = type(namespace) ~= "number" and 'of type "number" or undefined' or not namespaces[namespace] and "a valid namespace number" or nil if valid_type then error(format('Internal error: expected `namespace` for type `title` to be %s, but saw %s', valid_type, dump(namespace))) end end -- Decode entities. WARNING: mw.title.makeTitle must be called with `decoded` (as it doesn't decode) and mw.title.new must be called with `val` (as it does decode, so double-decoding needs to be avoided). local decoded, prefix, title = decode_entities(val), param.prefix -- If the input is a fragment, treat the title as the current title with the input fragment. if sub(decoded, 1, 1) == "#" then -- If prefix is "force", only get the current title if it's in the specified namespace. current_title includes the namespace prefix. if current_namespace == nil then local current_title = mw_title.getCurrentTitle() current_title_text, current_namespace = current_title.prefixedText, current_title.namespace end if not (prefix == "force" and namespace ~= current_namespace) then title = new_title(current_title_text .. val) end elseif prefix == "force" then -- Unconditionally add the namespace prefix (mw.title.makeTitle). title = make_title(namespace, decoded) elseif prefix == "full override" then -- The first input prefix will be used as an override (mw.title.new). This can be a namespace or interwiki prefix. title = new_title(val, namespace) elseif prefix == nil or prefix == "namespace override" then -- Only allow namespace prefixes to override. Interwiki prefixes therefore need to be treated as plaintext (e.g. "el:All topics" with namespace 14 returns "el:Category:All topics", but we want "Category:el:All topics" instead; if the former is really needed, then the input ":el:Category:All topics" will work, as the initial colon overrides the namespace). mw.title.new can take namespace names as well as numbers in the second argument, and will throw an error if the input isn't a valid namespace, so this can be used to determine if a prefix is for a namespace, since mw.title.new will return successfully only if there's either no prefix or the prefix is for a valid namespace (in which case we want the override). local success success, title = pcall(new_title, val, match(decoded, "^.-%f[:]") or namespace) -- Otherwise, get the title with mw.title.makeTitle, which unconditionally adds the namespace prefix, but behaves like mw.title.new if the namespace is 0. if not success then title = make_title(namespace, decoded) end else error(format('Internal error: expected `prefix` for type `title` to be "force", "full override", "namespace override" or undefined, but saw %s', dump(prefix))) end local allow_external = param.allow_external if allow_external == true then return title or convert_val_error(val, name, "Wiktionary or external page title") elseif not allow_external then return title and is_internal_title(title) and title or convert_val_error(val, name, "Wiktionary page title") end error(format('Internal error: expected `allow_external` for type `title` to be of type "boolean" or undefined, but saw %s', dump(allow_external))) end, ["Wikimedia language"] = function(val, name, param) local fallback = param.fallback if fallback == true then return get_wm_lang_by_code_with_fallback(val) or convert_val_error(val, name, "Wikimedia language or language code") elseif not fallback then return get_wm_lang_by_code(val) or convert_val_error(val, name, "Wikimedia language code") end error(format('Internal error: expected `fallback` for type `Wikimedia language` to be of type "boolean" or undefined, but saw %s', dump(fallback))) end, }, { -- TODO: decode HTML entities in all input values. Non-trivial to implement, because we need to avoid any downstream -- functions decoding the output from this module, which would be double-decoding. Note that "title" has this -- implemented already, and it needs to have both the raw input and the decoded input to avoid double-decoding by -- mw.title.new, so any implementation can't be as simple as decoding in __call then passing the result to the -- handler. __call = function(self, val, name, param, param_type, default) local val_type = type(val) -- TODO: check this for all possible parameter types. if val_type == param_type or val_type == "boolean" and param_type == "boolean or string" then return val elseif val_type ~= "string" then local expected = "string" if default then if param_type == "boolean or string" then expected = param_type elseif param_type == "boolean" or param_type == "number" then expected = param_type .. " or " .. expected end end error(format( "Internal error: %sargument %s has the type %s; expected a %s.", default and (default .. " for ") or "", name, dump(val_type), expected )) end local func = self[param_type] if func == nil then error(format("Internal error: %s is not a recognized parameter type.", dump(param_type))) end return func(val, name, param) end }) --[==[ func: export.convert_val(val, name, param) Convert a parameter value according to the associated specs listed in the `params` table passed to [[Module:parameters]]. `val` is the value to convert for a parameter whose name is `name` (used only in error messages). `param` is the spec (the value part of the `params` table for the parameter). In place of passing in the parameter name, `name` can be a function that throws an error, displaying the specified message along with the parameter name and value. This function processes all the conversion-related fields in `param`, including `type`, `set`, `sublist`, `convert`, etc. It returns the converted value. ]==] local function convert_val(val, name, param, default) local param_type = param.type or "string" -- If param.type is a function, resolve it to a recognized type. if is_callable(param_type) then param_type = param_type(val) end local convert, sublist = param.convert, param.sublist -- `val` might not be a string if it's the default value. if sublist and type(val) == "string" then local retlist, set = {}, param.set if convert then local thisindex = 0 local thisval, insval, parse_err if is_callable(name) then -- We assume the passed-in error function in `name` already shows the parameter name and raw value. function parse_err(msg) name(format("%s: item #%s=%s", msg_with_processed(msg, thisval, insval), thisindex, thisval) ) end else function parse_err(msg) error(format("%s: item #%s=%s of parameter %s=%s", msg_with_processed(msg, thisval, insval), thisindex, thisval, name, val) ) end end for v in split_sublist(val, name, sublist) do thisindex, thisval = thisindex + 1, v if set then v = check_set(v, name, param, param_type) end insval = v insert(retlist, convert(type_handlers(v, name, param, param_type, default), parse_err)) end else for v in split_sublist(val, name, sublist) do if set then v = check_set(v, name, param, param_type) end insert(retlist, type_handlers(v, name, param, param_type, default)) end end return retlist elseif param.set then val = check_set(val, name, param, param_type) end local retval = type_handlers(val, name, param, param_type, default) if convert then local parse_err if is_callable(name) then -- We assume the passed-in error function in `name` already shows the parameter name and raw value. if retval == val then -- This is an optimization to avoid creating a closure. The second arm works correctly even -- when retval == val. parse_err = name else function parse_err(msg) name(msg_with_processed(msg, val, retval)) end end else function parse_err(msg) error(format("%s: parameter %s=%s", msg_with_processed(msg, val, retval), name, val)) end end retval = convert(retval, parse_err) end -- If `sublist` is set but the input wasn't a string, return `retval` as a one-item list. if sublist then retval = {retval} end return retval end export.convert_val = convert_val -- used by [[Module:parameter utilities]] local function unknown_param(name, val, args_unknown) track("unknown parameters") args_unknown[name] = val return args_unknown end local function check_string_param_modifier(param_type, name, tag) if param_type and not (param_type == "string" or param_type == "parameter" or is_callable(param_type)) then internal_process_error( "%s cannot be set unless %s is set to %s (the default), %s or a function: parameter %s has the type %s.", tag, "type", "string", "parameter", name, param_type ) end end local function hole_error(params, name, listname, this, nxt, extra) -- `process_error` calls `dump` on values to be inserted into -- error messages, but with numeric lists this causes "numeric" -- to look like the name of the list rather than a description, -- as `dump` adds quote marks. Insert it early to avoid this, -- but add another %s specifier in all other cases, so that -- actual list names will be displayed properly. local offset, specifier, starting_from = 0, "%s", "" local msg = "Item %%d in the list of %s parameters must be given if item %%d is given, because %sthere shouldn't be any gaps due to missing%s parameters." local specs = {} if type(listname) == "string" then specs[2] = listname elseif type(name) == "number" then offset = name - 1 -- To get the original parameter. specifier = "numeric" -- If the list doesn't start at parameter 1, avoid implying -- there can't be any gaps in the numeric parameters if -- some parameter with a lower key is optional. for j = name - 1, 1, -1 do local _param = params[j] if not (_param and _param.required) then starting_from = format("(starting from parameter %d) ", dump(j + 1)) break end end else specs[2] = name end specs[1] = this + offset -- Absolute index for this item. insert(specs, nxt + offset) -- Absolute index for the next item. process_error(format(msg, specifier, starting_from, extra or ""), unpack(specs)) end local function check_disallow_holes(params, val, name, listname, extra) for i = 1, val.maxindex do if val[i] == nil then hole_error(params, name, listname, i, num_keys(val)[i], extra) end end end local function handle_holes(params, val, name) local param = params[name] local disallow_holes = param.disallow_holes -- Iterate up the list, and throw an error if a hole is found. if disallow_holes then check_disallow_holes(params, val, name, param.list, " or empty") end -- Iterate up the list, and throw an error if a hole is found due to a -- missing parameter, treating empty parameters as part of the list. This -- applies beyond maxindex if blank arguments are supplied beyond it, so -- isn't mutually exclusive with `disallow_holes`. local empty = val.empty if param.disallow_missing then if empty then -- Remove `empty` from `val`, so it doesn't get returned. val.empty = nil for i = 1, max(val.maxindex, empty.maxindex) do if val[i] == nil and not empty[i] then local keys = extend(num_keys(val), num_keys(empty)) sort(keys) hole_error(params, name, param.list, i, keys[i]) end end -- If there's no table of empty parameters, the check is identical to -- `disallow_holes`, except that the error message only refers to -- missing parameters, not missing or empty ones. If `disallow_holes` is -- also set, there's no point checking again. elseif not disallow_holes then check_disallow_holes(params, val, name, param.list) end end -- If `allow_holes` is set, there's nothing left to do. if param.allow_holes then -- do nothing -- Otherwise, remove any holes: `pairs` won't work, as it's unsorted, and -- iterating from 1 to `maxindex` times out with inputs like |100000000000=, -- so use num_keys to get a list of numerical keys sorted from lowest to -- highest, then iterate up the list, moving each value in `val` to the -- lowest unused positive integer key. This also avoids the need to create a -- new table. If `disallow_holes` is specified, then there can't be any -- holes in the list, so there's no reason to check again; this doesn't -- apply to `disallow_missing`, however. else if not disallow_holes then local keys, i = num_keys(val), 0 while true do i = i + 1 local key = keys[i] if key == nil then break elseif i ~= key then track("holes compressed") val[i], val[key] = val[key], nil end end end -- Some code depends on only numeric params being present when no holes are -- allowed (e.g. by checking for the presence of arguments using next()), so -- remove `maxindex`. val.maxindex = nil end end local function maybe_flatten(params, val, name) local param = params[name] if param.flatten then if param.allow_holes then process_error("For parameter %s, can't set both `allow_holes` and `flatten`", name) end if not param.sublist and param.type ~= "genders" and param.type ~= "labels" and param.type ~= "references" and param.type ~= "qualifier" and param.type ~= "form of tags" then process_error("For parameter %s, can only set `flatten` along with `sublist` or a list-generating type", name) end -- Do the flattening ourselves rather than calling flatten() in [[Module:table]], which will attempt to -- flatten non-list objects like title objects, and cause an error in the process. -- FIXME: We should do this in-place if possible. local newlist = {} for _, sublist in ipairs(val) do for _, item in ipairs(sublist) do insert(newlist, item) end end val = newlist end return val end -- If both `template_default` and `default` are given, `template_default` takes precedence, but only on the template or -- module page. This means a different default can be specified for the template or module page example. However, -- `template_default` doesn't apply if any args are set, which helps (somewhat) with examples on documentation pages -- transcluded into the template page. HACK: We still run into problems on documentation pages transcluded into the -- template page when pagename= is set. Check this on the assumption that pagename= is fairly standard. local function convert_default_val(name, param, pagename_set, any_args_set, add_empty_sublist) if not pagename_set then local val = param.template_default if val ~= nil and not any_args_set and is_own_page() then return convert_val(val, name, param, "template default") end end local val = param.default if val ~= nil then return convert_val(val, name, param, "default") -- Sublist parameters should return an empty table if not given, but only do -- this if the parameter isn't also a list (in which case it will already -- be an empty table). -- FIXME: do this once all modules that pass in a sublist parameter treat an empty sublist identically to a nil argument; some currently do things based on the fact an argument exists at all. -- elseif add_empty_sublist and param.sublist then --return {} end end --[==[ Process arguments with a given list of parameters. Return a table containing the processed arguments. The `args` parameter specifies the arguments to be processed; they are the arguments you might retrieve from {frame:getParent().args} (the template arguments) or in some cases {frame.args} (the invocation arguments). The `params` parameter specifies a list of valid parameters, and consists of a table. If an argument is encountered that is not in the parameter table, an error is thrown. The structure of the `params` table is as described above in the intro comment. '''WARNING:''' The `params` table is destructively modified to save memory. Nonetheless, different keys can share the same value objects in memory without causing problems. The `return_unknown` parameter, if set to {true}, prevents the function from triggering an error when it comes across an argument with a name that it doesn't recognise. Instead, the return value is a pair of values: the first is the processed arguments as usual, while the second contains all the unrecognised arguments that were left unprocessed. This allows you to do multi-stage processing, where the entire set of arguments that a template should accept is not known at once. For example, an inflection-table might do some generic processing on some arguments, but then defer processing of the remainder to the function that handles a specific inflectional type. ]==] function export.process(args, params, return_unknown) -- Process parameters for specific properties local args_new = {} local args_unknown, any_args_set, required, patterns, list_args, index_list, args_placeholders, placeholders_n -- TODO: memoize the processing of each unique `param` value, since it's common for the same value to be used for -- many parameter names. for name, param in pairs(params) do validate_name(name, "parameter names") if param ~= true then local spec_type = type(param) if type(param) ~= "table" then internal_process_error( "spec for parameter %s must be a table of specs or the value true, but found %s.", name, spec_type ~= "boolean" and spec_type or param ) end -- Populate required table, and make sure aliases aren't set to required. if param.required then if required == nil then required = {} end required[name] = true end local listname, alias_of = param.list, param.alias_of if alias_of then validate_name(alias_of, "the alias_of field of parameter ", name) if alias_of == name then internal_process_error( "parameter %s cannot be an alias of itself.", name ) end local main_param = params[alias_of] -- Check that the alias_of is set to a valid parameter. if not (main_param == true or type(main_param) == "table") then internal_process_error( "parameter %s is an alias of an invalid parameter.", name ) end validate_alias_options(param, name, main_param, alias_of) -- Aliases can't be lists unless the canonical parameter is also a list. if listname and (main_param == true or not main_param.list) then internal_process_error( "list parameter %s is set as an alias of %s, which is not a list parameter.", name, alias_of ) -- Can't be an alias of an alias. elseif main_param ~= true then local main_alias_of = main_param.alias_of if main_alias_of ~= nil then internal_process_error( "alias_of cannot be set to another alias: parameter %s is set as an alias of %s, which is in turn an alias of %s. Set alias_of for %s to %s.", name, alias_of, main_alias_of, name, main_alias_of ) end end end local replaced_by = param.replaced_by if replaced_by then -- replaced_by can be `false`, which is OK validate_name(replaced_by, "the replaced_by field of parameter ", name) if replaced_by == name then internal_process_error( "parameter %s cannot be replaced by itself.", name ) end local main_param = params[replaced_by] -- Check that the replaced_by is set to a valid parameter. if not (main_param == true or type(main_param) == "table") then internal_process_error( "parameter %s is set to be replaced by an invalid parameter.", name ) end -- Can't be a replaced-by of a replaced-by. if main_param ~= true then local main_replaced_by = main_param.replaced_by if main_replaced_by ~= nil then internal_process_error( "replaced_by cannot be set to another replaced-by parameter: parameter %s is set as replaced by %s, which is in turn replaced by %s. Set replaced_by for %s to %s.", name, replaced_by, main_replaced_by, name, main_replaced_by ) end end if param.instead ~= nil then internal_process_error("the `instead` tag can only be given when `replaced_by` is set to `false`.") end elseif replaced_by == false then if param.instead ~= nil and type(param.instead) ~= "string" then internal_process_error( "the `instead` tag must be a string, but saw %s.", param.instead ) end end if replaced_by ~= nil then if param.reason ~= nil and type(param.reason) ~= "string" then internal_process_error( "the `reason` tag must be a string, but saw %s.", param.reason ) end end if listname then if not alias_of then local key = name if type(name) == "string" then key = gsub(name, "\1", "") end local list_arg = {maxindex = 0} args_new[key] = list_arg if list_args == nil then list_args = {} end list_args[key] = list_arg end local list_type = type(listname) if list_type == "string" then -- If the list property is a string, then it represents the name -- to be used as the prefix for list items. This is for use with lists -- where the first item is a numbered parameter and the -- subsequent ones are named, such as 1, pl2, pl3. patterns = save_pattern(name, listname, patterns or {}) elseif listname ~= true then internal_process_error( "list field for parameter %s must be a boolean, string or undefined, but saw a %s.", name, list_type ) elseif type(name) == "number" then if index_list ~= nil then internal_process_error( "only one numeric parameter can be a list, unless the list property is a string." ) end -- If the name is a number, then all indexed parameters from -- this number onwards go in the list. index_list = name else patterns = save_pattern(name, name, patterns or {}) end if find(name, "\1", nil, true) then if args_placeholders then placeholders_n = placeholders_n + 1 args_placeholders[placeholders_n] = name else args_placeholders, placeholders_n = {name}, 1 end end end end end --Process required changes to `params`. if args_placeholders then for i = 1, placeholders_n do local name = args_placeholders[i] params[gsub(name, "\1", "")], params[name] = params[name], nil end end -- Process the arguments for name, val in pairs(args) do any_args_set = true validate_name(name, "argument names", nil, true) -- Guaranteeing that all values are strings avoids issues with type coercion being inconsistent between functions. local val_type = type(val) if val_type ~= "string" then internal_process_error( "argument %s has the type %s; all arguments must be strings.", name, val_type ) end local orig_name, raw_type = name, type(name) local index, canonical if raw_type == "number" then if index_list and name >= index_list then index = name - index_list + 1 name = index_list end elseif patterns then -- Does this argument name match a pattern? for pattern, pname in next, patterns do index = match(name, pattern) -- It matches, so store the parameter name and the -- numeric index extracted from the argument name. if index then index = tonumber(index) name = pname break end end end local param = params[name] -- If the argument is not in the list of parameters, store it in a separate list. if not param then args_unknown = unknown_param(name, val, args_unknown or {}) elseif param == true then canonical = orig_name val = php_trim(val) if val ~= "" then -- If the parameter is duplicated, throw an error. if args_new[name] ~= nil then process_error( "Parameter %s has been entered more than once. This is probably because a parameter alias has been used.", canonical ) end args_new[name] = val end else if param.replaced_by == false then process_error( ("Parameter %%s has been removed and is no longer valid%s.%s"):format( param.reason and ", " .. param.reason or "", param.instead and " Instead, " .. param.instead .. "." or ""), name ) elseif param.replaced_by then process_error( ("Parameter %%s has been replaced by %%s%s."):format( param.reason and ", " .. param.reason or ""), name, param.replaced_by ) end if param.deprecated then track("deprecated parameter", name) end if param.require_index then -- Disallow require_index for numeric parameter names, as this doesn't make sense. if raw_type == "number" then internal_process_error( "cannot set require_index for numeric parameter %s.", name ) -- If a parameter without the trailing index was found, and -- require_index is set on the param, treat it -- as if it isn't recognized. elseif not index then args_unknown = unknown_param(name, val, args_unknown or {}) end end -- Check that separate_no_index is not being used with a numeric parameter. if param.separate_no_index then if raw_type == "number" then internal_process_error( "cannot set separate_no_index for numeric parameter %s.", name ) elseif type(param.alias_of) == "number" then internal_process_error( "cannot set separate_no_index for parameter %s, as it is an alias of numeric parameter %s.", name, param.alias_of ) end end -- If no index was found, use 1 as the default index. -- This makes list parameters like g, g2, g3 put g at index 1. -- If `separate_no_index` is set, then use 0 as the default instead. if not index and param.list then index = param.separate_no_index and 0 or 1 end -- Normalize to the canonical parameter name. If it's a list, but the alias is not, then determine the index. local raw_name = param.alias_of if raw_name then raw_type = type(raw_name) if raw_type == "number" then name = raw_name local main_param = params[raw_name] if main_param ~= true and main_param.list then if not index then index = param.separate_no_index and 0 or 1 end canonical = raw_name + index - 1 else canonical = raw_name end else name = gsub(raw_name, "\1", "") local main_param = params[name] if not index and main_param ~= true and main_param.list then index = param.separate_no_index and 0 or 1 end if not index or index == 0 then canonical = name elseif name == raw_name then canonical = name .. index else canonical = gsub(raw_name, "\1", index) end end else canonical = orig_name end -- Only recognize demo parameters if this is the current template or module's -- page, or its documentation page. if param.demo and not is_own_page("include_documentation") then args_unknown = unknown_param(name, val, args_unknown or {}) end -- Remove leading and trailing whitespace unless no_trim is true. if param.no_trim then check_string_param_modifier(param.type, name, "no_trim") else val = php_trim(val) end -- Empty string is equivalent to nil unless allow_empty is true. if param.allow_empty then check_string_param_modifier(param.type, name, "allow_empty") elseif val == "" then -- If `disallow_missing` is set, keep track of empty parameters -- via the `empty` field in `arg`, which will be used by the -- `disallow_missing` check. This will be deleted before -- returning. if index and param.disallow_missing then local arg = args_new[name] local empty = arg.empty if empty == nil then empty = {maxindex = 0} arg.empty = empty end empty[index] = true if index > empty.maxindex then empty.maxindex = index end end val = nil end -- Allow boolean false. if val ~= nil then -- Convert to proper type if necessary. local main_param = params[raw_name] if main_param ~= true then val = convert_val(val, orig_name, main_param or param) end -- Mark it as no longer required, as it is present. if required then required[name] = nil end -- Store the argument value. if index then local arg = args_new[name] -- If the parameter is duplicated, throw an error. if arg[index] ~= nil then process_error( "Parameter %s has been entered more than once. This is probably because a list parameter has been entered without an index and with index 1 at the same time, or because a parameter alias has been used.", canonical ) end arg[index] = val -- Store the highest index we find. local maxindex = arg.maxindex if index > maxindex then maxindex = index end if arg[0] ~= nil then arg.default, arg[0] = arg[0], nil if maxindex < 1 then maxindex = 1 end end arg.maxindex = maxindex if not params[name].list then args_new[name] = val -- Don't store index 0, as it's a proxy for the default. elseif index > 0 then arg[index] = val end else -- If the parameter is duplicated, throw an error. if args_new[name] ~= nil then process_error( "Parameter %s has been entered more than once. This is probably because a parameter alias has been used.", canonical ) end if not raw_name then args_new[name] = val else local main_param = params[raw_name] if main_param ~= true and main_param.list then local main_arg = args_new[raw_name] main_arg[1] = val -- Store the highest index we find. if main_arg.maxindex < 1 then main_arg.maxindex = 1 end else args_new[raw_name] = val end end end end end end -- Remove holes in any list parameters if needed. This must be handled -- straight after the previous loop, as any instances of `empty` need to be -- converted to nil. if list_args then for name, val in next, list_args do handle_holes(params, val, name) end end -- If the current page is the template which invoked this Lua instance, then ignore the `require` flag, as it -- means we're viewing the template directly. Required parameters sometimes have a `template_default` key set, -- which gets used in such cases as a demo. -- Note: this won't work on other pages in the Template: namespace (including the /documentation subpage), -- or if the #invoke: is on a page in another namespace. local pagename_set = args_new.pagename -- Handle defaults. for name, param in pairs(params) do if param ~= true then local arg_new = args_new[name] if arg_new == nil then args_new[name] = convert_default_val(name, param, pagename_set, any_args_set, true) elseif param.list and arg_new[1] == nil then local default_val = convert_default_val(name, param, pagename_set, any_args_set) if default_val ~= nil then arg_new[1] = default_val if arg_new.maxindex == 0 then arg_new.maxindex = 1 end end end end end -- Flatten nested lists if called for. This must come after setting the default. if list_args then for name, val in next, list_args do args_new[name] = maybe_flatten(params, val, name) end end -- The required table should now be empty. -- If any parameters remain, throw an error, unless we're on the current template or module's page. if required and next(required) ~= nil and not is_own_page() then params_list_error(required, "required") -- Return the arguments table. -- If there are any unknown parameters, throw an error, unless return_unknown is set, in which case return args_unknown as a second return value. elseif return_unknown then return args_new, args_unknown or {} elseif args_unknown and next(args_unknown) ~= nil then params_list_error(args_unknown, "not used by this template") end return args_new end return export n22wbg166a0hooxofspixrredxb8pb5 სპილო 0 49595 5775276 1418780 2026-10-14T10:06:38Z Octahedron80 267 5775276 wikitext text/x-wiki == ภาษาจอร์เจีย == === รากศัพท์ === จาก{{der|ka|pal|𐭯𐭩𐭫}} === การออกเสียง === * {{ka-IPA}} === คำนาม === {{ka-noun}} # [[ช้าง]] [[หมวดหมู่:ภาษาจอร์เจีย:สัตว์เลี้ยงลูกด้วยน้ำนม]] lx7sszkmntkh14ipknfpllct6kmii5r איראן 0 51001 5775277 5714906 2026-10-14T10:09:20Z Octahedron80 267 5775277 wikitext text/x-wiki == ภาษาฮีบรู == {{wp|he:}} === รูปแบบอื่น === * {{alt|he|אִירָן}} === รากศัพท์ === จาก{{der|he|fa|ایران|tr=irân}}, จาก{{der|he|fa-cls|ایران|tr=ērān}}, จาก{{der|he|pal|𐭠𐭩𐭥𐭠𐭭||แห่งชาวอารยัน}} === การออกเสียง === * {{IPA|he|/iˈʁan/|a=IL}} * {{audio|he|LL-Q9288 (heb)-Buffer-איראן.wav}} === คำวิสามานยนาม === {{he-proper noun|wv=אִירָאן|g=f|tr=irán}} # {{tcl|he|อิหร่าน|id=Q794}} ==== ลูกคำ ==== * {{l|he|איראני}} ab7xugugm7cce7ehrffyw2idtseh8cv มอดูล:etymology/templates 828 135441 5775198 5713712 2026-10-14T04:44:57Z Octahedron80 267 5775198 Scribunto text/plain local export = {} local require_when_needed = require("Module:require when needed") local get_current_L2 = require_when_needed("Module:pages", "get_current_L2") local get_lang_by_name = require_when_needed("Module:languages", "getByCanonicalName") local is_content_page = require_when_needed("Module:pages", "is_content_page") local format_categories = require_when_needed("Module:utilities", "format_categories") local process_params = require_when_needed("Module:parameters", "process") local trim = mw.text.trim local lower = mw.ustring.lower local etymology_module = "Module:etymology" local headword_data_module = "Module:headword/data" local etymology_specialized_module = "Module:etymology/specialized" local parameter_utilities_module = "Module:parameter utilities" -- For testing local force_cat = false local allowed_conjs = {"และ", "หรือ", ",", "/", "~", ";"} -- Sinitic lects (Mandarin, Cantonese, Hokkien, etc.) are full languages, but Chinese entries sit -- under a single ==Chinese== L2, while romanization entries (pinyin, jyutping, pe̍h-ōe-jī) have lect -- L2s, and content is often shared between them. Contact languages (Chinese-based creoles and mixed -- languages) have their own L2s and are excluded. local function is_sinitic(lang) return lang:inFamily("zhx") and not lang:inFamily("qfa-cnt") end local content_page local function is_content_page_cached() if content_page == nil then content_page = is_content_page(mw.title.getCurrentTitle()) end return content_page end -- Throw an error if `lang` (the language of the entry) doesn't match -- the L2 header that the template is invoked under. local function check_lang_matches_L2(lang, nocat) if nocat or not lang or lang:getCode() == "und" or (lang.hasType and lang:hasType("family")) then return end local headword_data = mw.loadData(headword_data_module) if headword_data.large_pages[headword_data.pagename] then return end if not is_content_page_cached() then return end local current_L2 = get_current_L2() if mw.ustring.sub(current_L2, 1, 4) == "ภาษา" then --ตัดคำว่า "ภาษา" ออก current_L2 = mw.ustring.sub(current_L2, 5) end if not current_L2 then return end local full_name = lang:getFullName() if full_name == current_L2 then return end -- Accept any Sinitic language under any Sinitic L2. if is_sinitic(lang) then local L2_lang = get_lang_by_name(current_L2) if L2_lang and is_sinitic(L2_lang) then return end end local lang_desc = lang:getCode() .. " (" .. lang:getCanonicalName() .. ")" if lang:getFullCode() ~= lang:getCode() then lang_desc = lang_desc .. ", an etymology-only language whose full language is " .. lang:getFullCode() .. " (" .. full_name .. ")" end error("Language '" .. lang_desc .. "' does not match the L2 header (" .. current_L2 .. ").") end local function parse_etym_args(parent_args, base_params, has_dest_lang) local m_param_utils = require(parameter_utilities_module) local param_mods = m_param_utils.construct_param_mods { {group = {"link", "q", "l", "ref"}}, } local sourcearg, termarg if has_dest_lang then sourcearg, termarg = 2, 3 else sourcearg, termarg = 1, 2 end local terms, args = m_param_utils.parse_term_with_inline_modifiers_and_separate_params { params = base_params, param_mods = param_mods, raw_args = parent_args, termarg = termarg, track_module = "etymology", lang = function(args) return args[sourcearg][#args[sourcearg]] end, sc = "sc", -- Don't do this, doesn't seem to make sense. -- parse_lang_prefix = true, make_separate_g_into_list = true, splitchar = ",", subitem_param_handling = "last", } -- If term param 3= is empty, there will be no terms in terms.terms. To facilitate further code and for -- compatibility,, insert one. It will display as <small>[Term?]</small>. if not terms.terms[1] then terms.terms[1] = { lang = args[sourcearg][#args[sourcearg]], sc = args.sc, } end return terms.terms, args end function export.parse_2_lang_args(parent_args, has_text, no_family) local boolean = {type = "boolean"} local params = { [1] = { required = true, type = "language", default = "und" }, [2] = { required = true, sublist = true, type = "language", family = not no_family, default = "und" }, [3] = true, [4] = {alias_of = "alt"}, [5] = {alias_of = "t"}, ["senseid"] = true, ["nocat"] = boolean, ["sort"] = true, ["sourceconj"] = true, ["conj"] = {set = allowed_conjs, default = ","}, } if has_text then params["notext"] = boolean params["nocap"] = boolean end local terms, args = parse_etym_args(parent_args, params, "has dest lang") check_lang_matches_L2(args[1], args.nocat) return terms, args end -- Implementation of deprecated {{etyl}}. Provided to make histories more legible. function export.etyl(frame) local params = { [1] = {required = true, type = "language", default = "und"}, [2] = {type = "language", default = "th"}, --changed ["sort"] = {}, } -- Empty language means English, but "-" means no language. Yes, confusing... local args = frame:getParent().args if args[2] and trim(args[2]) == "-" then params[2] = nil args = process_params({ [1] = args[1], ["sort"] = args.sort }, params) else args = process_params(args, params) end check_lang_matches_L2(args[2]) return require(etymology_module).format_source { lang = args[2], source = args[1], sort_key = args.sort, force_cat = force_cat, } end -- Implementation of {{derived}}/{{der}}. function export.derived(frame) local parent_args = frame:getParent().args local terms, args = export.parse_2_lang_args(parent_args) return require(etymology_module).format_derived { lang = args[1], sources = args[2], terms = terms, sort_key = args.sort, nocat = args.nocat, sourceconj = args.sourceconj, conj = args.conj, template_name = "derived", force_cat = force_cat, } end -- Implementation of {{borrowed}}/{{bor}}. function export.borrowed(frame) local parent_args = frame:getParent().args local terms, args = export.parse_2_lang_args(parent_args) return require(etymology_module).format_borrowed { lang = args[1], sources = args[2], terms = terms, sort_key = args.sort, nocat = args.nocat, sourceconj = args.sourceconj, conj = args.conj, force_cat = force_cat, } end function export.inherited(frame) local parent_args = frame:getParent().args local terms, args = export.parse_2_lang_args(parent_args) local sources = args[2] if sources[2] then -- Because this doesn't really make sense. error("[[Template:inherited]] doesn't support multiple comma-separated sources") end return require(etymology_module).format_inherited { lang = args[1], terms = terms, sort_key = args.sort, nocat = args.nocat, conj = args.conj, force_cat = force_cat, } end function export.cognate(frame) local params = { [1] = { required = true, sublist = true, type = "language", family = true, default = "und" }, [2] = true, [3] = {alias_of = "alt"}, [4] = {alias_of = "t"}, sourceconj = true, ["conj"] = {set = allowed_conjs, default = ","}, sort = true, } local parent_args = frame:getParent().args local terms, args = parse_etym_args(parent_args, params, false) return require(etymology_module).format_cognate { sources = args[1], terms = terms, sort_key = args.sort, sourceconj = args.sourceconj, conj = args.conj, force_cat = force_cat, } end function export.noncognate(frame) return export.cognate(frame) end -- Supports various specialized types of borrowings, according to `frame.args.bortype`: -- "learned" = {{lbor}}/{{learned borrowing}} -- "semi-learned" = {{slbor}}/{{semi-learned borrowing}} -- "orthographic" = {{obor}}/{{orthographic borrowing}} -- "unadapted" = {{ubor}}/{{unadapted borrowing}} -- "calque" = {{cal}}/{{calque}} -- "partial-calque" = {{pcal}}/{{partial calque}} -- "semantic-loan" = {{sl}}/{{semantic loan}} -- "transliteration" = {{translit}}/{{transliteration}} -- "phono-semantic-matching" = {{psm}}/{{phono-semantic matching}} function export.specialized_borrowing(frame) local parent_args = frame:getParent().args local terms, args = export.parse_2_lang_args(parent_args, "has text") local m_etymology_specialized = require(etymology_specialized_module) local text, categories = m_etymology_specialized.specialized_borrowing { bortype = frame.args.bortype, lang = args[1], sources = args[2], terms = terms, sort_key = args.sort, nocap = args.nocap, notext = args.notext, nocat = args.nocat, sourceconj = args.sourceconj, conj = args.conj, senseid = args.senseid, } return text .. format_categories(categories, args[1], args.sort, nil, force_cat) end -- Implementation of miscellaneous templates such as {{abbrev}}, {{back-formation}}, {{clipping}}, {{ellipsis}}, -- {{rebracketing}} and {{reduplication}} that have a single associated term. function export.misc_variant(frame) local iparams = { ["ignore-params"] = true, text = {required = true}, oftext = true, cat = {list = true}, -- allow and compress holes conj = true, } local iargs = process_params(frame.args, iparams) local boolean = {type = "boolean"} local params = { [1] = {required = true, type = "language", default = "und"}, [2] = true, [3] = {alias_of = "alt"}, [4] = {alias_of = "t"}, nocap = boolean, -- should be processed in the template itself notext = boolean, nocat = boolean, conj = {set = allowed_conjs}, sort = true, } -- |ignore-params= parameter to module invocation specifies -- additional parameter names to allow in template invocation, separated by -- commas. They must consist of ASCII letters or numbers or hyphens. local ignore_params = iargs["ignore-params"] if ignore_params then ignore_params = trim(ignore_params) if not ignore_params:match("^[%w%-,]+$") then error("Invalid characters in |ignore-params=: " .. ignore_params:gsub("[%w%-,]+", "")) end for param in ignore_params:gmatch("[%w%-]+") do if params[param] then error("Duplicate param |" .. param .. " in |ignore-params=: already specified in params") end params[param] = true end end local m_param_utils = require(parameter_utilities_module) local param_mods = m_param_utils.construct_param_mods { {group = {"link", "q", "l", "ref"}}, } local parent_args = frame:getParent().args local terms, args = m_param_utils.parse_term_with_inline_modifiers_and_separate_params { params = params, param_mods = param_mods, raw_args = parent_args, termarg = 2, track_module = "etymology", -- Don't set lang here as we want to know whether there was a lang prefix or not. sc = "sc", parse_lang_prefix = true, allow_multiple_lang_prefixes = true, make_separate_g_into_list = true, splitchar = ",", subitem_param_handling = "last", } check_lang_matches_L2(args[1], args.nocat) return require(etymology_module).format_misc_variant { lang = args[1], notext = args.notext, text = iargs.text, oftext = iargs.oftext, terms = terms.terms, sort_key = args.sort, conj = args.conj or iargs.conj or "และ", nocat = args.nocat, cats = iargs.cat, force_cat = force_cat, } end -- Implementation of miscellaneous templates such as {{doublet}} that can take multiple terms. Doesn't handle {{blend}} -- or {{univerbation}}, which display + signs between elements and use compound_like in [[Module:affix/templates]]. function export.misc_variant_multiple_terms(frame) local iparams = { text = {required = true}, oftext = true, cat = {list = true}, -- allow and compress holes conj = true, } local iargs = process_params(frame.args, iparams) local boolean = {type = "boolean"} local params = { [1] = {required = true, type = "language", template_default = "und"}, [2] = {list = true, allow_holes = true}, nocap = boolean, -- should be processed in the template itself notext = boolean, nocat = boolean, conj = {set = allowed_conjs}, sort = true, } local m_param_utils = require(parameter_utilities_module) local param_mods = m_param_utils.construct_param_mods { -- We want to require an index for all params. {default = true, require_index = true}, {group = {"link", "q", "l", "ref"}}, } local parent_args = frame:getParent().args local terms, args = m_param_utils.parse_list_with_inline_modifiers_and_separate_params { params = params, param_mods = param_mods, raw_args = parent_args, termarg = 2, parse_lang_prefix = true, allow_multiple_lang_prefixes = true, track_module = "etymology-templates-doublet", disallow_custom_separators = true, -- For compatibility, we need to not skip completely unspecified items. It is common, for example, to do -- {{suffix|lang||foo}} to generate "+ -foo". dont_skip_items = true, -- Don't set lang here as we want to know whether there was a lang prefix or not. sc = "sc.default", } check_lang_matches_L2(args[1], args.nocat) return require(etymology_module).format_misc_variant { lang = args[1], notext = args.notext, text = iargs.text, oftext = iargs.oftext, terms = terms, sort_key = args.sort, conj = args.conj or iargs.conj or "และ", nocat = args.nocat, cats = iargs.cat, force_cat = force_cat, } end -- Implementation of miscellaneous templates such as {{unknown}} that have no associated terms. do local function get_args(frame) local boolean = {type = "boolean"} local params = { [1] = {required = true, type = "language", default = "und"}, ["title"] = true, ["nocap"] = boolean, -- should be processed in the template itself ["notext"] = boolean, ["nocat"] = boolean, ["sort"] = true, } if frame.args.title2_alias then params[2] = {alias_of = "title"} end local args = process_params(frame:getParent().args, params) check_lang_matches_L2(args[1], args.nocat) return args end function export.misc_variant_no_term(frame) local args = get_args(frame) return require(etymology_module).format_misc_variant_no_term { lang = args[1], notext = args.notext, title = args.title or frame.args.text, nocat = args.nocat, cat = frame.args.cat, sort_key = args.sort, force_cat = force_cat, } end -- This function works similarly to misc_variant_no_term(), but with some automatic linking to the glossary in -- `title`. function export.onomatopoeia(frame) local args = get_args(frame) local title = args.title if title and (lower(title) == "imitative" or lower(title) == "imitation") then title = "[[ภาคผนวก:อภิธานศัพท์#imitative|" .. title .. "]]" end return require(etymology_module).format_misc_variant_no_term { lang = args[1], notext = args.notext, title = title or frame.args.text, nocat = args.nocat, cat = frame.args.cat, sort_key = args.sort, force_cat = force_cat, } end end return export e8crjpdgc6na6pu6p6p2qtoonh0x33j 5775199 5775198 2026-10-14T04:48:35Z Octahedron80 267 5775199 Scribunto text/plain local export = {} local require_when_needed = require("Module:require when needed") local get_current_L2 = require_when_needed("Module:pages", "get_current_L2") local get_lang_by_name = require_when_needed("Module:languages", "getByCanonicalName") local is_content_page = require_when_needed("Module:pages", "is_content_page") local format_categories = require_when_needed("Module:utilities", "format_categories") local process_params = require_when_needed("Module:parameters", "process") local trim = mw.text.trim local lower = mw.ustring.lower local etymology_module = "Module:etymology" local headword_data_module = "Module:headword/data" local etymology_specialized_module = "Module:etymology/specialized" local parameter_utilities_module = "Module:parameter utilities" -- For testing local force_cat = false local allowed_conjs = {"และ", "หรือ", ",", "/", "~", ";"} -- Sinitic lects (Mandarin, Cantonese, Hokkien, etc.) are full languages, but Chinese entries sit -- under a single ==Chinese== L2, while romanization entries (pinyin, jyutping, pe̍h-ōe-jī) have lect -- L2s, and content is often shared between them. Contact languages (Chinese-based creoles and mixed -- languages) have their own L2s and are excluded. local function is_sinitic(lang) return lang:inFamily("zhx") and not lang:inFamily("qfa-cnt") end local content_page local function is_content_page_cached() if content_page == nil then content_page = is_content_page(mw.title.getCurrentTitle()) end return content_page end -- Throw an error if `lang` (the language of the entry) doesn't match -- the L2 header that the template is invoked under. local function check_lang_matches_L2(lang, nocat) if nocat or not lang or lang:getCode() == "und" or (lang.hasType and lang:hasType("family")) then return end local headword_data = mw.loadData(headword_data_module) if headword_data.large_pages[headword_data.pagename] then return end if not is_content_page_cached() then return end local current_L2 = get_current_L2() if not current_L2 then return end if mw.ustring.sub(current_L2, 1, 4) == "ภาษา" then --ตัดคำว่า "ภาษา" ออก current_L2 = mw.ustring.sub(current_L2, 5) end local full_name = lang:getFullName() if full_name == current_L2 then return end -- Accept any Sinitic language under any Sinitic L2. if is_sinitic(lang) then local L2_lang = get_lang_by_name(current_L2) if L2_lang and is_sinitic(L2_lang) then return end end local lang_desc = lang:getCode() .. " (" .. lang:getCanonicalName() .. ")" if lang:getFullCode() ~= lang:getCode() then lang_desc = lang_desc .. ", an etymology-only language whose full language is " .. lang:getFullCode() .. " (" .. full_name .. ")" end error("Language '" .. lang_desc .. "' does not match the L2 header (" .. current_L2 .. ").") end local function parse_etym_args(parent_args, base_params, has_dest_lang) local m_param_utils = require(parameter_utilities_module) local param_mods = m_param_utils.construct_param_mods { {group = {"link", "q", "l", "ref"}}, } local sourcearg, termarg if has_dest_lang then sourcearg, termarg = 2, 3 else sourcearg, termarg = 1, 2 end local terms, args = m_param_utils.parse_term_with_inline_modifiers_and_separate_params { params = base_params, param_mods = param_mods, raw_args = parent_args, termarg = termarg, track_module = "etymology", lang = function(args) return args[sourcearg][#args[sourcearg]] end, sc = "sc", -- Don't do this, doesn't seem to make sense. -- parse_lang_prefix = true, make_separate_g_into_list = true, splitchar = ",", subitem_param_handling = "last", } -- If term param 3= is empty, there will be no terms in terms.terms. To facilitate further code and for -- compatibility,, insert one. It will display as <small>[Term?]</small>. if not terms.terms[1] then terms.terms[1] = { lang = args[sourcearg][#args[sourcearg]], sc = args.sc, } end return terms.terms, args end function export.parse_2_lang_args(parent_args, has_text, no_family) local boolean = {type = "boolean"} local params = { [1] = { required = true, type = "language", default = "und" }, [2] = { required = true, sublist = true, type = "language", family = not no_family, default = "und" }, [3] = true, [4] = {alias_of = "alt"}, [5] = {alias_of = "t"}, ["senseid"] = true, ["nocat"] = boolean, ["sort"] = true, ["sourceconj"] = true, ["conj"] = {set = allowed_conjs, default = ","}, } if has_text then params["notext"] = boolean params["nocap"] = boolean end local terms, args = parse_etym_args(parent_args, params, "has dest lang") check_lang_matches_L2(args[1], args.nocat) return terms, args end -- Implementation of deprecated {{etyl}}. Provided to make histories more legible. function export.etyl(frame) local params = { [1] = {required = true, type = "language", default = "und"}, [2] = {type = "language", default = "th"}, --changed ["sort"] = {}, } -- Empty language means English, but "-" means no language. Yes, confusing... local args = frame:getParent().args if args[2] and trim(args[2]) == "-" then params[2] = nil args = process_params({ [1] = args[1], ["sort"] = args.sort }, params) else args = process_params(args, params) end check_lang_matches_L2(args[2]) return require(etymology_module).format_source { lang = args[2], source = args[1], sort_key = args.sort, force_cat = force_cat, } end -- Implementation of {{derived}}/{{der}}. function export.derived(frame) local parent_args = frame:getParent().args local terms, args = export.parse_2_lang_args(parent_args) return require(etymology_module).format_derived { lang = args[1], sources = args[2], terms = terms, sort_key = args.sort, nocat = args.nocat, sourceconj = args.sourceconj, conj = args.conj, template_name = "derived", force_cat = force_cat, } end -- Implementation of {{borrowed}}/{{bor}}. function export.borrowed(frame) local parent_args = frame:getParent().args local terms, args = export.parse_2_lang_args(parent_args) return require(etymology_module).format_borrowed { lang = args[1], sources = args[2], terms = terms, sort_key = args.sort, nocat = args.nocat, sourceconj = args.sourceconj, conj = args.conj, force_cat = force_cat, } end function export.inherited(frame) local parent_args = frame:getParent().args local terms, args = export.parse_2_lang_args(parent_args) local sources = args[2] if sources[2] then -- Because this doesn't really make sense. error("[[Template:inherited]] doesn't support multiple comma-separated sources") end return require(etymology_module).format_inherited { lang = args[1], terms = terms, sort_key = args.sort, nocat = args.nocat, conj = args.conj, force_cat = force_cat, } end function export.cognate(frame) local params = { [1] = { required = true, sublist = true, type = "language", family = true, default = "und" }, [2] = true, [3] = {alias_of = "alt"}, [4] = {alias_of = "t"}, sourceconj = true, ["conj"] = {set = allowed_conjs, default = ","}, sort = true, } local parent_args = frame:getParent().args local terms, args = parse_etym_args(parent_args, params, false) return require(etymology_module).format_cognate { sources = args[1], terms = terms, sort_key = args.sort, sourceconj = args.sourceconj, conj = args.conj, force_cat = force_cat, } end function export.noncognate(frame) return export.cognate(frame) end -- Supports various specialized types of borrowings, according to `frame.args.bortype`: -- "learned" = {{lbor}}/{{learned borrowing}} -- "semi-learned" = {{slbor}}/{{semi-learned borrowing}} -- "orthographic" = {{obor}}/{{orthographic borrowing}} -- "unadapted" = {{ubor}}/{{unadapted borrowing}} -- "calque" = {{cal}}/{{calque}} -- "partial-calque" = {{pcal}}/{{partial calque}} -- "semantic-loan" = {{sl}}/{{semantic loan}} -- "transliteration" = {{translit}}/{{transliteration}} -- "phono-semantic-matching" = {{psm}}/{{phono-semantic matching}} function export.specialized_borrowing(frame) local parent_args = frame:getParent().args local terms, args = export.parse_2_lang_args(parent_args, "has text") local m_etymology_specialized = require(etymology_specialized_module) local text, categories = m_etymology_specialized.specialized_borrowing { bortype = frame.args.bortype, lang = args[1], sources = args[2], terms = terms, sort_key = args.sort, nocap = args.nocap, notext = args.notext, nocat = args.nocat, sourceconj = args.sourceconj, conj = args.conj, senseid = args.senseid, } return text .. format_categories(categories, args[1], args.sort, nil, force_cat) end -- Implementation of miscellaneous templates such as {{abbrev}}, {{back-formation}}, {{clipping}}, {{ellipsis}}, -- {{rebracketing}} and {{reduplication}} that have a single associated term. function export.misc_variant(frame) local iparams = { ["ignore-params"] = true, text = {required = true}, oftext = true, cat = {list = true}, -- allow and compress holes conj = true, } local iargs = process_params(frame.args, iparams) local boolean = {type = "boolean"} local params = { [1] = {required = true, type = "language", default = "und"}, [2] = true, [3] = {alias_of = "alt"}, [4] = {alias_of = "t"}, nocap = boolean, -- should be processed in the template itself notext = boolean, nocat = boolean, conj = {set = allowed_conjs}, sort = true, } -- |ignore-params= parameter to module invocation specifies -- additional parameter names to allow in template invocation, separated by -- commas. They must consist of ASCII letters or numbers or hyphens. local ignore_params = iargs["ignore-params"] if ignore_params then ignore_params = trim(ignore_params) if not ignore_params:match("^[%w%-,]+$") then error("Invalid characters in |ignore-params=: " .. ignore_params:gsub("[%w%-,]+", "")) end for param in ignore_params:gmatch("[%w%-]+") do if params[param] then error("Duplicate param |" .. param .. " in |ignore-params=: already specified in params") end params[param] = true end end local m_param_utils = require(parameter_utilities_module) local param_mods = m_param_utils.construct_param_mods { {group = {"link", "q", "l", "ref"}}, } local parent_args = frame:getParent().args local terms, args = m_param_utils.parse_term_with_inline_modifiers_and_separate_params { params = params, param_mods = param_mods, raw_args = parent_args, termarg = 2, track_module = "etymology", -- Don't set lang here as we want to know whether there was a lang prefix or not. sc = "sc", parse_lang_prefix = true, allow_multiple_lang_prefixes = true, make_separate_g_into_list = true, splitchar = ",", subitem_param_handling = "last", } check_lang_matches_L2(args[1], args.nocat) return require(etymology_module).format_misc_variant { lang = args[1], notext = args.notext, text = iargs.text, oftext = iargs.oftext, terms = terms.terms, sort_key = args.sort, conj = args.conj or iargs.conj or "และ", nocat = args.nocat, cats = iargs.cat, force_cat = force_cat, } end -- Implementation of miscellaneous templates such as {{doublet}} that can take multiple terms. Doesn't handle {{blend}} -- or {{univerbation}}, which display + signs between elements and use compound_like in [[Module:affix/templates]]. function export.misc_variant_multiple_terms(frame) local iparams = { text = {required = true}, oftext = true, cat = {list = true}, -- allow and compress holes conj = true, } local iargs = process_params(frame.args, iparams) local boolean = {type = "boolean"} local params = { [1] = {required = true, type = "language", template_default = "und"}, [2] = {list = true, allow_holes = true}, nocap = boolean, -- should be processed in the template itself notext = boolean, nocat = boolean, conj = {set = allowed_conjs}, sort = true, } local m_param_utils = require(parameter_utilities_module) local param_mods = m_param_utils.construct_param_mods { -- We want to require an index for all params. {default = true, require_index = true}, {group = {"link", "q", "l", "ref"}}, } local parent_args = frame:getParent().args local terms, args = m_param_utils.parse_list_with_inline_modifiers_and_separate_params { params = params, param_mods = param_mods, raw_args = parent_args, termarg = 2, parse_lang_prefix = true, allow_multiple_lang_prefixes = true, track_module = "etymology-templates-doublet", disallow_custom_separators = true, -- For compatibility, we need to not skip completely unspecified items. It is common, for example, to do -- {{suffix|lang||foo}} to generate "+ -foo". dont_skip_items = true, -- Don't set lang here as we want to know whether there was a lang prefix or not. sc = "sc.default", } check_lang_matches_L2(args[1], args.nocat) return require(etymology_module).format_misc_variant { lang = args[1], notext = args.notext, text = iargs.text, oftext = iargs.oftext, terms = terms, sort_key = args.sort, conj = args.conj or iargs.conj or "และ", nocat = args.nocat, cats = iargs.cat, force_cat = force_cat, } end -- Implementation of miscellaneous templates such as {{unknown}} that have no associated terms. do local function get_args(frame) local boolean = {type = "boolean"} local params = { [1] = {required = true, type = "language", default = "und"}, ["title"] = true, ["nocap"] = boolean, -- should be processed in the template itself ["notext"] = boolean, ["nocat"] = boolean, ["sort"] = true, } if frame.args.title2_alias then params[2] = {alias_of = "title"} end local args = process_params(frame:getParent().args, params) check_lang_matches_L2(args[1], args.nocat) return args end function export.misc_variant_no_term(frame) local args = get_args(frame) return require(etymology_module).format_misc_variant_no_term { lang = args[1], notext = args.notext, title = args.title or frame.args.text, nocat = args.nocat, cat = frame.args.cat, sort_key = args.sort, force_cat = force_cat, } end -- This function works similarly to misc_variant_no_term(), but with some automatic linking to the glossary in -- `title`. function export.onomatopoeia(frame) local args = get_args(frame) local title = args.title if title and (lower(title) == "imitative" or lower(title) == "imitation") then title = "[[ภาคผนวก:อภิธานศัพท์#imitative|" .. title .. "]]" end return require(etymology_module).format_misc_variant_no_term { lang = args[1], notext = args.notext, title = title or frame.args.text, nocat = args.nocat, cat = frame.args.cat, sort_key = args.sort, force_cat = force_cat, } end end return export aibedqrkruikbba5rs2lzfo8roy2zmv มอดูล:etymology 828 135442 5775200 5759135 2026-10-14T04:50:32Z Octahedron80 267 5775200 Scribunto text/plain local export = {} -- For testing local force_cat = false local debug_track_module = "Module:debug/track" local languages_module = "Module:languages" local links_module = "Module:links" local table_module = "Module:table" local utilities_module = "Module:utilities" local concat = table.concat local insert = table.insert local new_title = mw.title.new local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) 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_language_data_module_name(...) get_language_data_module_name = require(languages_module).getDataModuleName return get_language_data_module_name(...) end local function get_link_page(...) get_link_page = require(links_module).get_link_page return get_link_page(...) end local function language_link(...) language_link = require(links_module).language_link return language_link(...) end local function serial_comma_join(...) serial_comma_join = require(table_module).serialCommaJoin return serial_comma_join(...) end local function shallow_copy(...) shallow_copy = require(table_module).shallowCopy return shallow_copy(...) end local function track(page, code) local tracking_page = "etymology/" .. page debug_track(tracking_page) if code then debug_track(tracking_page .. "/" .. code) end end local function join_segs(segs, conj) if not segs[2] then return segs[1] elseif conj == "และ" or conj == "หรือ" then return serial_comma_join(segs, {conj = conj}) end local sep if conj == "," or conj == ";" then sep = conj .. " " elseif conj == "/" then sep = "/" elseif conj == "~" then sep = " ~ " elseif conj then error(("Internal error: Unrecognized conjunction \"%s\""):format(conj)) else error(("Internal error: No value supplied for conjunction"):format(conj)) end return concat(segs, sep) end -- Returns true if `lang` is the same as `source`, or a variety of it. local function lang_is_source(lang, source) return lang:getCode() == source:getCode() or lang:hasParent(source) end --[==[ Format one or more links as specified in `termobjs`, a list of term objects of the format accepted by `full_link()` in [[Module:links]], including decorations (qualifiers, labels and references). `conj` is used to join multiple terms and must be specified if there is more than one term. `template_name` is the template name used in debug tracking and must be specified. Optional `sourcetext` is text to prepend to the concatenated terms, separated by a space if the concatenated terms are non-empty (which is always the case unless there is a single term with the value "-"). If `decorations_on_outside` is given, any decorations specified in the first term go on the outside of (i.e before) `sourcetext`; otherwise they will end up on the inside. ]==] function export.format_links(termobjs, conj, template_name, sourcetext, decorations_on_outside) if not template_name then error("Internal error: Must specify `template_name` to format_links()") end for i, termobj in ipairs(termobjs) do if termobj.lang:hasType("family") or termobj.lang:getFamilyCode() == "qfa-sub" then if termobj.term and termobj.term ~= "-" then debug_track(template_name .. "/family-with-term") end termobj.term = "-" end if termobj.term == "-" then --[=[ [[Special:WhatLinksHere/Wiktionary:Tracking/cognate/no-term]] [[Special:WhatLinksHere/Wiktionary:Tracking/derived/no-term]] [[Special:WhatLinksHere/Wiktionary:Tracking/borrowed/no-term]] [[Special:WhatLinksHere/Wiktionary:Tracking/calque/no-term]] ]=] debug_track(template_name .. "/no-term") termobjs[i] = i == 1 and sourcetext or "" else if i == 1 and decorations_on_outside and sourcetext then termobj.pretext = sourcetext .. " " sourcetext = nil end termobjs[i] = (i == 1 and sourcetext and sourcetext .. " " or "") .. full_link(termobj, "term") end end return join_segs(termobjs, conj) end function export.get_display_and_cat_name(source, raw) local display, cat_name if source:getCode() == "und" then display = "ยังไม่กำหนด" cat_name = "ภาษาอื่น" elseif source:getCode() == "mul" then display = raw and "ร่วม" or "[[w:ภาษาร่วม|ภาษาร่วม]]" cat_name = "ภาษาร่วม" elseif source:getCode() == "mul-tax" then display = raw and "ชื่ออนุกรมวิธาน" or "[[w:การตั้งชื่อทวินาม|ชื่ออนุกรมวิธาน]]" cat_name = "ชื่ออนุกรมวิธาน" else display = raw and source:getCanonicalName() or source:makeWikipediaLink() cat_name = "ภาษา" .. source:getDisplayForm() end return display, cat_name end function export.insert_source_cat_get_display(data) local categories, lang, source = data.categories, data.lang, data.source local display, cat_name = export.get_display_and_cat_name(source, data.raw) if lang and not data.nocat then -- Add the category, but only if there is a current language if not categories then categories = {} end local langname = lang:getFullName() -- If `lang` is an etym-only language, we need to check both it and its parent full language against `source`. -- Otherwise if e.g. `lang` is Medieval Latin and `source` is Latin, we'll end up wrongly constructing a -- category 'Latin terms derived from Latin'. --insert(categories, langname .. ( -- lang_is_source(lang, source) and " terms borrowed back into " .. cat_name or -- " " .. (data.borrowing_type or "terms derived") .. " from " .. cat_name --)) insert(categories, "ศัพท์ภาษา" .. langname .. ( lang_is_source(lang, source) and "ที่ยืมกลับไปยัง" .. cat_name or "ที่" .. (data.borrowing_type or "รับมา") .. "จาก" .. cat_name )) end return display, categories end function export.format_source(data) local lang, sort_key = data.lang, data.sort_key -- [[Special:WhatLinksHere/Wiktionary:Tracking/etymology/sortkey]] if sort_key then track("sortkey") end local display, categories = export.insert_source_cat_get_display(data) if lang and not data.nocat then -- Format categories, but only if there is a current language; {{cog}} currently gets no categories categories = format_categories(categories, lang, sort_key, nil, data.force_cat or force_cat) else categories = "" end return "<span class=\"etyl\">" .. display .. categories .. "</span>" end --[==[ Format sources for etymology templates such as {{tl|bor}}, {{tl|der}}, {{tl|inh}} and {{tl|cog}}. There may potentially be more than one source language (except currently {{tl|inh}}, which doesn't support it because it doesn't really make sense). In that case, all but the last source language is linked to the first term, but only if there is such a term and this linking makes sense, i.e. either (1) the term page exists after stripping diacritics according to the source language in question, or (2) the result of stripping diacritics according to the source language in question results in a different page from the same process applied with the last source language. For example, {{m|ru|соля́нка}} will link to [[солянка]] but {{m|en|соля́нка}} will link to [[соля́нка]] with an accent, and since they are different pages, the use of English as a non-final source with term 'соля́нка' will link to [[соля́нка]] even though it doesn't exist, on the assumption that it is merely a redlink that might exist. If none of the above criteria apply, a non-final source language will be linked to the Wikipedia entry for the language, just as final source languages always are. `data` contains the following fields: * `lang`: The destination language object into which the terms were borrowed, inherited or otherwise derived. Used for categorization and can be nil, as with {{tl|cog}}. * `sources`: List of source objects. Most commonly there is only one. If there are multiple, the non-final ones are handled specially; see above. * `terms`: List of term objects. Most commonly there is only one. If there are multiple source objects as well as multiple term objects, the non-final source objects link to the first term object. * `sort_key`: Sort key for categories. Usually nil. * `categories`: Categories to add to the page. Additional categories may be added to `categories` based on the source languages ('''in which case `categories` is destructively modified'''). If `lang` is nil, no categories will be added. * `nocat`: Don't add any categories to the page. * `sourceconj`: Conjunction used to separate multiple source languages. Defaults to {"and"}. Currently recognized values are `and`, `or`, `,`, `;`, `/` and `~`. * `borrowing_type`: Borrowing type used in categories, such as {"learned borrowings"}. Defaults to {"terms derived"}. * `force_cat`: Force category generation on non-mainspace pages. ]==] function export.format_sources(data) local lang, sources, terms, borrowing_type, sort_key, categories, nocat = data.lang, data.sources, data.terms, data.borrowing_type, data.sort_key, data.categories, data.nocat local term1, sources_n, source_segs = terms[1], #sources, {} local final_link_page local term1_term, term1_sc = term1.term, term1.sc if sources_n > 1 and term1_term and term1_term ~= "-" then final_link_page = get_link_page(term1_term, sources[sources_n], term1_sc) end for i, source in ipairs(sources) do local seg, display_term if i < sources_n and term1_term and term1_term ~= "-" then local link_page = get_link_page(term1_term, source, term1_sc) display_term = (link_page ~= final_link_page) or (link_page and not not new_title(link_page):getContent()) end -- TODO: if the display forms or transliterations are different, display the terms separately. if display_term then local display, this_cats = export.insert_source_cat_get_display{ lang = lang, source = source, borrowing_type = borrowing_type, raw = true, categories = categories, nocat = nocat, } seg = language_link { lang = source, term = term1_term, alt = display, tr = "-", } if lang and not nocat then -- Format categories, but only if there is a current language; {{cog}} currently gets no categories this_cats = format_categories(this_cats, lang, sort_key, nil, data.force_cat or force_cat) else this_cats = "" end seg = "<span class=\"etyl\">" .. seg .. this_cats .. "</span>" else seg = export.format_source{ lang = lang, source = source, borrowing_type = borrowing_type, sort_key = sort_key, categories = categories, nocat = nocat, } end insert(source_segs, seg) end return join_segs(source_segs, data.sourceconj or "และ") end -- Internal implementation of {{cognate}}/{{cog}} template. function export.format_cognate(data) return export.format_derived { sources = data.sources, terms = data.terms, sort_key = data.sort_key, sourceconj = data.sourceconj, conj = data.conj, template_name = "cognate", force_cat = data.force_cat, } end --[==[ Internal implementation of {{derived}}/{{der}} template. This is called externally from [[Module:affix]], [[Module:affixusex]] and [[Module:see]] and needs to support decorations (qualifiers, labels and references) on the outside of the sources for use by those modules. `data` contains the following fields: * `lang`: The destination language object into which the terms were derived. Used for categorization and can be nil, as with {{tl|cog}}; in this case, no categories are added. * `sources`: List of source objects. Most commonly there is only one. If there are multiple, the non-final ones are handled specially; see `format_sources()`. * `terms`: List of term objects. Most commonly there is only one. If there are multiple source objects as well as multiple term objects, the non-final source objects link to the first term object. * `conj`: Conjunction used to separate multiple terms. '''Required'''. Currently recognized values are `and`, `or`, `,`, `;`, `/` and `~`. * `sourceconj`: Conjunction used to separate multiple source languages. Defaults to {"and"}. Currently recognized values are as for `conj` above. * `decorations_on_outside`: If specified, any decorations (qualifiers, labels or references) in the first term in `terms` will be displayed on the outside of (before) the source language(s) in `sources`. Normally this should be specified if there is only one term possible in `terms`. * `template_name`: Name of the template invoking this function. Must be specified. Only used for tracking pages. * `sort_key`: Sort key for categories. Usually nil. * `categories`: Categories to add to the page. Additional categories may be added to `categories` based on the source languages ('''in which case `categories` is destructively modified'''). If `lang` is nil, no categories will be added. * `nocat`: Don't add any categories to the page. * `borrowing_type`: Borrowing type used in categories, such as {"learned borrowings"}. Defaults to {"terms derived"}. * `force_cat`: Force category generation on non-mainspace pages. ]==] function export.format_derived(data) local terms = data.terms local sourcetext = export.format_sources(data) return export.format_links(terms, data.conj, data.template_name, sourcetext, data.decorations_on_outside) end function export.insert_borrowed_cat(categories, lang, source) if lang_is_source(lang, source) then return end -- If both are the same, we want e.g. [[:Category:English terms borrowed back into English]] not -- [[:Category:English terms borrowed from English]]; the former is inserted automatically by format_source(). -- The second parameter here doesn't matter as it only affects `display`, which we don't use. --insert(categories, lang:getFullName() .. " terms borrowed from " .. select(2, export.get_display_and_cat_name(source, "raw"))) insert(categories, "ศัพท์ภาษา" .. lang:getFullName() .. "ที่ยืมมาจาก" .. select(2, export.get_display_and_cat_name(source, "raw"))) end -- Internal implementation of {{borrowed}}/{{bor}} template. function export.format_borrowed(data) local categories = {} if not data.nocat then local lang = data.lang for _, source in ipairs(data.sources) do export.insert_borrowed_cat(categories, lang, source) end end data = shallow_copy(data) data.categories = categories return export.format_links(data.terms, data.conj, "borrowed", export.format_sources(data)) end do -- Generate the non-ancestor error message. local function show_language(lang) local retval = ("%s (%s)"):format(lang:makeCategoryLink(), lang:getCode()) if lang:hasType("etymology-only") then retval = retval .. (" (an etymology-only language whose regular parent is %s)"):format( show_language(lang:getParent())) end return retval end -- Check that `lang` has `otherlang` (which may be an etymology-only language) as an ancestor. Throw an error if -- not. When `lang` is a family, verifies that `otherlang` is a language in that family. function export.check_ancestor(lang, otherlang) -- When `lang` is a family, verify `otherlang` is in that family or in its parent family. if lang.hasType and lang:hasType("family") then local family_code = lang:getCode() local function in_family_code(fcode, other) if not fcode or fcode == "" then return false end if other.inFamily and other:inFamily(fcode) then return true end if other.getFamilyCode and other:getFamilyCode() == fcode then return true end return false end local in_family = in_family_code(family_code, otherlang) if not in_family then local parent_code if lang.getParent then local parent_family = lang:getParent() if parent_family and parent_family.getCode then parent_code = parent_family:getCode() end end if not parent_code and family_code:find("-", 1, true) then parent_code = family_code:match("^(.+)-[^-]+$") end if parent_code then in_family = in_family_code(parent_code, otherlang) end end if not in_family then local other_display = (otherlang.getCanonicalName and otherlang:getCanonicalName()) or (otherlang.getCode and otherlang:getCode()) or tostring(otherlang) local fam_display = (lang.getCanonicalName and lang:getCanonicalName()) or family_code error(("%s is not in family %s; inherited ancestor under a family must be a language in that family or its parent family.") :format(other_display, fam_display)) end return end -- FIXME: I don't know if this function works correctly with etym-only languages in `lang`. I have fixed up -- the module link code appropriately (June 2024) but the remaining logic is untouched. if lang:hasAncestor(otherlang) then -- [[Special:WhatLinksHere/Wiktionary:Tracking/etymology/variety]] -- Track inheritance from varieties of Latin that shouldn't have any descendants (everything except Old Latin, Classical Latin and Vulgar Latin). if otherlang:getFullCode() == "la" then otherlang = otherlang:getCode() if not (otherlang == "itc-ola" or otherlang == "la-cla" or otherlang == "la-vul") then track("bad ancestor", otherlang) end end return end local ancestors = lang:getAncestors() local postscript local etym_module_link = lang:hasType("etymology-only") and "[[Module:etymology languages/data]] or " or "" local module_link = "[[" .. get_language_data_module_name(lang:getFullCode()) .. "]]" if not ancestors[1] then postscript = show_language(lang) .. " has no ancestors." else local ancestor_list = {} for _, ancestor in ipairs(ancestors) do insert(ancestor_list, show_language(ancestor)) end postscript = ("The ancestor%s of %s %s %s."):format( ancestors[2] and "s" or "", lang:getCanonicalName(), ancestors[2] and "are" or "is", concat(ancestor_list, " and ")) end error(("%s is not set as an ancestor of %s in %s%s. %s") :format(show_language(otherlang), show_language(lang), etym_module_link, module_link, postscript)) end end -- Internal implementation of {{inherited}}/{{inh}} template. function export.format_inherited(data) local lang, terms, nocat = data.lang, data.terms, data.nocat local source = terms[1].lang local categories = {} if not nocat then --table.insert(categories, lang:getFullName() .. " terms inherited from " .. source:getCanonicalName()) table.insert(categories, "ศัพท์ภาษา" .. lang:getFullName() .. "ที่สืบทอดจากภาษา" .. source:getCanonicalName()) end export.check_ancestor(lang, source) data = shallow_copy(data) data.categories = categories data.source = source return export.format_links(terms, data.conj, "inherited", export.format_source(data)) end -- Internal implementation of "misc variant" templates such as {{abbrev}}, {{clipping}}, {{reduplication}} and the like. function export.format_misc_variant(data) local lang, notext, terms, cats, parts = data.lang, data.notext, data.terms, data.cats, {} if not notext then insert(parts, data.text) end if terms[1] then if not notext then -- FIXME: If term is given as '-', we should consider displaying just "Clipping" not "Clipping of". insert(parts, data.oftext or "ของ") --th end local termparts = {} -- Make links out of all the parts. for _, termobj in ipairs(terms) do local result if termobj.lang then result = export.format_derived { lang = lang, terms = {termobj}, sources = termobj.termlangs or {termobj.lang}, template_name = "misc_variant", decorations_on_outside = true, force_cat = data.force_cat, } else termobj.lang = lang result = export.format_links({termobj}, nil, "misc_variant") end table.insert(termparts, result) end local linktext = join_segs(termparts, data.conj) if not notext and linktext ~= "" then insert(parts, " ") end insert(parts, linktext) end local categories = {} if not data.nocat and cats then for _, cat in ipairs(cats) do --insert(categories, lang:getFullName() .. " " .. cat) insert(categories, require("Module:th-utilities").th_categorize(cat, lang:getCategoryName())) --th end end if categories[1] then insert(parts, format_categories(categories, lang, data.sort_key, nil, data.force_cat or force_cat)) end return concat(parts) end -- Implementation of miscellaneous templates such as {{unknown}} and {{onomatopoeia}} that have no associated terms. function export.format_misc_variant_no_term(data) local parts = {} if not data.notext then insert(parts, data.title) end if not data.nocat and data.cat then local lang, categories = data.lang, {} --insert(categories, lang:getFullName() .. " " .. data.cat) insert(categories, require("Module:th-utilities").th_categorize(data.cat, lang:getCategoryName())) --th insert(parts, format_categories(categories, lang, data.sort_key, nil, data.force_cat or force_cat)) end return concat(parts) end return export a3txiy9r326xin5jyyaiuxe2w1he61x มอดูล:affix 828 135995 5775183 5757833 2026-10-14T03:39:43Z Octahedron80 267 5775183 Scribunto text/plain local export = {} local debug_force_cat = false -- if set to true, always display categories even on userspace pages local m_links = require("Module:links") local m_str_utils = require("Module:string utilities") local m_table = require("Module:table") local decorations_module = "Module:decorations" local en_utilities_module = "Module:en-utilities" local etymology_module = "Module:etymology" local scripts_module = "Module:scripts" local utilities_module = "Module:utilities" -- Export this so the category code in [[Module:category tree/etymology]] can access it. export.affix_lang_data_module_prefix = "Module:affix/lang-data/" local ulen = m_str_utils.len local ufind = m_str_utils.find local umatch = m_str_utils.match local pluralize = require(en_utilities_module).pluralize local singularize = require(en_utilities_module).singularize local u = m_str_utils.char local ucfirst = m_str_utils.ucfirst local concat = table.concat local insert = table.insert local unpack = unpack or table.unpack -- Lua 5.2 compatibility function export.affix_variants(canonical, variants) local mappings = {} for _, variant in ipairs(variants) do mappings[variant] = canonical end return mappings end function export.id_mapping(default, ids) local mapping = { default = default } if ids then for id, target in pairs(ids) do mapping[id] = target end end return mapping end function export.id_mapping_with_affix_variants(base, id_variants) local mappings = {} for id, variants in pairs(id_variants) do for _, variant in ipairs(variants) do mappings[variant] = export.id_mapping(base, {[id] = base}) end end return mappings end function export.merge_tables(...) local result = {} for i = 1, select('#', ...) do local t = select(i, ...) if t then for k, v in pairs(t) do result[k] = v end end end return result end -- Export this so the category code in [[Module:category tree/etymology]] can access it. export.langs_with_lang_specific_data = { ["az"] = true, ["fi"] = true, ["fr"] = true, ["izh"] = true, ["la"] = true, ["sah"] = true, ["tr"] = true, ["trk-pro"] = true, } local default_pos = "ศัพท์" local function pluralize_pos(pos) return --[[pluralize(singularize(pos))]] pos end --[==[ intro: ===About different types of hyphens ("template", "display" and "lookup"):=== * The "template hyphen" is the per-script hyphen character that is used in template calls to indicate that a term is an affix. This is always a single Unicode char, but there may be multiple possible hyphens for a given script. Normally this is just the regular hyphen character "-", but for some non-Latin-script languages (currently only right-to-left languages), it is different. * The "display hyphen" is the string (which might be an empty string) that is added onto a term as displayed and linked, to indicate that a term is an affix. Currently this is always either the same as the template hyphen or an empty string, but the code below is written generally enough to handle arbitrary display hyphens. Specifically: *# For East Asian languages, the display hyphen is always blank. *# For Arabic-script languages, either tatweel (ـ) or ZWNJ (zero-width non-joiner) are allowed as template hyphens, where ZWNJ is supported primarily for Farsi, because some suffixes have non-joining behavior. The display hyphen corresponding to tatweel is also tatweel, but the display hyphen corresponding to ZWNJ is blank (tatweel is also the default display hyphen, for calls to {{tl|prefix}}/{{tl|suffix}}/etc. that don't include an explicit hyphen). * The "lookup hyphen" is the hyphen that is used when looking up language-specific affix mappings. (These mappings are discussed in more detail below when discussing link affixes.) It depends only on the script of the affix in question. Most scripts (including East Asian scripts) use a regular hyphen "-" as the lookup hyphen, but Hebrew and Arabic have their own lookup hyphens (respectively maqqef and tatweel). Note that for Arabic in particular, there are three possible template hyphens that are recognized (tatweel, ZWNJ and regular hyphen), but mappings must use tatweel. ===About different types of affixes ("template", "display", "link", "lookup" and "category"):=== * A "template affix" is an affix in its source form as it appears in a template call. Generally, a template affix has an attached template hyphen (see above) to indicate that it is an affix and indicate what type of affix it is (prefix, suffix, interfix or circumfix), but some of the older-style templates such as {{tl|suffix}}, {{tl|prefix}}, {{tl|confix}}, etc. have "positional" affixes where the presence of the affix in a certain position (e.g. the second or third parameter) indicates that it is a certain type of affix, whether or not it has an attached template hyphen. * A "display affix" is the corresponding affix as it is actually displayed to the user. The display affix may differ from the template affix for various reasons: *# The display affix may be specified explicitly using the {{para|alt<var>N</var>}} parameter, the `<alt:...>` inline modifier or a piped link of the form e.g. `<nowiki>[[-kas|-käs]]</nowiki>` (here indicating that the affix should display as `-käs` but be linked as `-kas`). Here, the template affix is arguably the entire piped link, while the display affix is `-käs`. *# Even in the absence of {{para|alt<var>N</var>}} parameters, `<alt:...>` inline modifiers and piped links, certain languages have differences between the "template hyphen" specified in the template (which always needs to be specified somehow or other in templates like {{tl|affix}}, to indicate that the term is an affix and what type of affix it is) and the display hyphen (see above), with corresponding differences between template and display affixes. * A (regular) "link affix" is the affix that is linked to when the affix is shown to the user. The link affix is usually the same as the display affix, but will differ in one of three circumstances: *# The display and link affixes are explicitly made different using {{para|alt<var>N</var>}} parameters, `<alt:...>` inline modifiers or piped links, as described above under "display affix". *# For certain languages, certain affixes are mapped to canonical form using language-specific mappings. For example, in Finnish, the adjective-forming suffix {{m|fi|-kas}} appears as {{m|fi|-käs}} after front vowels, but logically both forms are the same suffix and should be linked and categorized the same. Similarly, in Latin, the negative and intensive prefixes spelled {{m|la|in-}} (etymologically two distinct prefixes) appear variously as {{m|la|il-}}, {{m|la|im-}} or {{m|la|ir-}} before certain consonants. Mappings are supplied in [[Module:affix/lang-data/LANGCODE]] to convert Finnish {{m|fi|-käs}} to {{m|fi|-kas}} for linking and categorization purposes. Note that the affixes in the mappings use "lookup hyphens" to indicate the different types of affixes, which is usually the same as the template hyphen but differs for Arabic scripts, because there are multiple possible template hyphens recognized but only one lookup hyphen (tatweel). The form of the affix as used to look up in the mapping tables is called the "lookup affix"; see below. * A "stripped link affix" is a link affix that has been passed through the language's `stripDiacritics()` function, which may strip certain diacritics: e.g. macrons in Latin and Old English (indicating length); acute and grave accents in Russian and various other Slavic languages (indicating stress); vowel diacritics in most Arabic-script languages; and also tatweel in some Arabic-script languages (currently, for example, Persian, Arabic and Urdu strip tatweel, but Ottoman Turkish does not). Stripped link affixes are currently what are used in category names. * A "lookup affix" is the form of the affix as it is looked up in the language-specific lookup mappings described above under link affixes. There are actually two lookup stages: *# First, the affix is looked up in a modified display form (specifically, the same as the display affix but using lookup hyphens). Note that this lookup does not occur if an explicit display form is given using {{para|alt<var>N</var>}} or an `<alt:...>` inline modifier, or if the template affix contains a piped or embedded link. *# If no entry is found, the affix is then looked up in a modified link form (specifically, the modified display form passed through the language's `stripDiacritics()` function, which strips out certain diacritics, but with the lookup hyphen re-added if it was stripped out, as in the case of tatweel in many Arabic-script languages). The reason for this double lookup procedure is to allow for mappings that are sensitive to the extra diacritics, but also allow for mappings that are not sensitive in this fashion (e.g. Russian {{m|ru|-ливый}} occurs both stressed and unstressed, but is the same prefix either way). * A "category affix" is the affix as it appears in categories such as [[:Category:Finnish terms suffixed with -kas| Category:Finnish terms suffixed with ''-kas'']]. The category affix is currently always the same as the stripped link affix. This means that for Arabic-script languages, it may or may not have a tatweel, even if the correponding display affix and regular link affix have a tatweel. As mentioned above, stripDiacritics() strips tatweel for Arabic, Persian and Urdu, but not for Ottoman Turkish. Hence affix categories for Arabic, Persian and Urdu will be missing the tatweel, but affix categories for Ottoman Turkish will have it. An additional complication is that if the template affix contains a ZWNJ, the display (and hence the link and category affixes) will have no hyphen attached in any case. ]==] ----------------------------------------------------------------------------------------- -- Template and display hyphens -- ----------------------------------------------------------------------------------------- --[=[ Per-script template hyphens. The template hyphen is what appears in the {{affix}}/{{prefix}}/{{suffix}}/etc. template (in the wikicode). See above. They key below is a script code, after removing a hyphen and anything preceding. Hence, script codes like 'mnc-Mong' and 'xwo-Mong' will match 'Mong'. The value below is a string consisting of one or more hyphen characters. If there is more than one character, the default hyphen must come last and a non-default function must be specified for the script in display_hyphens[] so the correct display hyphen will be specified when no template hyphen is given (in {{suffix}}/{{prefix}}/etc.). Script detection is normally done when linking, but we need to do it earlier. However, under most circumstances we don't need to do script detection. Specifically, we only need to do script detection for a given language if (a) the language has multiple scripts; and (b) at least one of those scripts is listed below or in display_hyphens. ]=] local ZWNJ = u(0x200C) -- zero-width non-joiner local template_hyphens = { -- This covers all Arabic scripts. See above. ["Arab"] = "ـ" .. ZWNJ .. "-", -- tatweel + zero-width non-joiner + regular hyphen ["Aran"] = "ـ" .. ZWNJ .. "-", -- tatweel + zero-width non-joiner + regular hyphen ["Hebr"] = "־", -- Hebrew-specific hyphen termed "maqqef" ["Mong"] = "᠊", -- FIXME! What about the following right-to-left scripts? -- Adlm (Adlam) -- Armi (Imperial Aramaic) -- Avst (Avestan) -- Cprt (Cypriot) -- Khar (Kharoshthi) -- Mand (Mandaic/Mandaean) -- Mani (Manichaean) -- Mend (Mende/Mende Kikakui) -- Narb (Old North Arabian) -- Nbat (Nabataean/Nabatean) -- Nkoo (N'Ko) -- Orkh (Orkhon runes) -- Phli (Inscriptional Pahlavi) -- Phlp (Psalter Pahlavi) -- Phlv (Book Pahlavi) -- Phnx (Phoenician) -- Prti (Inscriptional Parthian) -- Rohg (Hanifi Rohingya) -- Samr (Samaritan) -- Sarb (Old South Arabian) -- Sogd (Sogdian) -- Sogo (Old Sogdian) -- Syrc (Syriac) -- Thaa (Thaana) } -- Hyphens used when looking up an affix in a lang-specific affix mapping. Defaults to regular hyphen (-). The keys -- are script codes, after removing a hyphen and anything preceding. Hence, script codes like 'mnc-Mong' and 'xwo-Mong' -- will match 'Mong'. The value should be a single character. local lookup_hyphens = { ["Hebr"] = "־", -- This covers all Arabic scripts. See above. ["Arab"] = "ـ", ["Aran"] = "ـ", } -- Default display-hyphen function. local function default_display_hyphen(script, hyph) if not hyph then return template_hyphens[script] or "-" end return hyph end local function arab_get_display_hyphen(_script, hyph) if not hyph then return "ـ" -- tatweel elseif hyph == ZWNJ then return "" else return hyph end end local function no_display_hyphen(_script, _hyph) return "" end -- Per-script function to return the correct display hyphen given the script and template hyphen. The function should -- also handle the case where the passed-in template hyphen is nil, corresponding to the situation in -- {{prefix}}/{{suffix}}/etc. where no template hyphen is specified. The key is the script code after removing a hyphen -- and anything preceding, so 'mnc-Mong', 'xwo-Mong' etc. will match 'Mong'. local display_hyphens = { -- This covers all Arabic scripts. See above. ["Arab"] = arab_get_display_hyphen, ["Aran"] = arab_get_display_hyphen, ["Bopo"] = no_display_hyphen, ["Hani"] = no_display_hyphen, ["Hans"] = no_display_hyphen, ["Hant"] = no_display_hyphen, -- The following is a mixture of several scripts. Hopefully the specs here are correct! ["Jpan"] = no_display_hyphen, ["Jurc"] = no_display_hyphen, ["Kitl"] = no_display_hyphen, ["Kits"] = no_display_hyphen, ["Laoo"] = no_display_hyphen, ["Nshu"] = no_display_hyphen, ["Shui"] = no_display_hyphen, ["Tang"] = no_display_hyphen, ["Thaa"] = no_display_hyphen, ["Thai"] = no_display_hyphen, ["Tibt"] = no_display_hyphen, } ----------------------------------------------------------------------------------------- -- Basic Utility functions -- ----------------------------------------------------------------------------------------- local function glossary_link(entry, text) text = text or entry return "[[ภาคผนวก:อภิธานศัพท์#" .. entry .. "|" .. text .. "]]" end local function track(page) if type(page) == "table" then for i, pg in ipairs(page) do page[i] = "affix/" .. pg end else page = "affix/" .. page end require("Module:debug/track")(page) end local function ine(val) return val ~= "" and val or nil end ----------------------------------------------------------------------------------------- -- Compound types -- ----------------------------------------------------------------------------------------- local function make_compound_type(typ, alttext) return { text = "คำประสม" .. glossary_link(typ, alttext), cat = "คำประสม" .. typ, } end -- Make a compound type entry with a simple rather than glossary link. -- These should be replaced with a glossary link when the entry in the glossary -- is created. local function make_non_glossary_compound_type(typ, alttext) local link = alttext and "[[" .. typ .. "|" .. alttext .. "]]" or "[[" .. typ .. "]]" return { text = "คำประสม" .. link, cat = "คำประสม" .. typ, } end local function make_raw_compound_type(typ, alttext) return { text = glossary_link(typ, alttext), cat = pluralize(typ), } end local function make_borrowing_type(typ, alttext) return { text = glossary_link(typ, alttext), borrowing_type = pluralize(typ), } end export.etymology_types = { ["adapted borrowing"] = make_borrowing_type("adapted borrowing"), ["adap"] = "adapted borrowing", ["abor"] = "adapted borrowing", ["alliterative"] = make_non_glossary_compound_type("alliterative"), ["allit"] = "alliterative", ["ตรงข้าม"] = make_non_glossary_compound_type("ตรงข้าม"), ["antonymous"] = "ตรงข้าม", ["ant"] = "ตรงข้าม", ["พหุวรีหิ"] = make_compound_type("พหุวรีหิ"), ["bahuvrihi"] = "พหุวรีหิ", ["bahu"] = "พหุวรีหิ", ["bv"] = "พหุวรีหิ", ["coordinative"] = make_compound_type("coordinative"), ["coord"] = "coordinative", ["descriptive"] = make_compound_type("descriptive"), ["desc"] = "descriptive", ["determinative"] = make_compound_type("determinative"), ["det"] = "determinative", ["ทวันทวะ"] = make_compound_type("ทวันทวะ"), ["dvandva"] = "ทวันทวะ", ["dva"] = "ทวันทวะ", ["dvigu"] = make_compound_type("dvigu"), ["dvi"] = "dvigu", ["endocentric"] = make_compound_type("endocentric"), ["endo"] = "endocentric", ["exocentric"] = make_compound_type("exocentric"), ["exo"] = "exocentric", ["izafet I"] = make_compound_type("izafet I"), ["iz1"] = "izafet I", ["izafet II"] = make_compound_type("izafet II"), ["iz2"] = "izafet II", ["izafet III"] = make_compound_type("izafet III"), ["iz3"] = "izafet III", ["กรรมธารยะ"] = make_compound_type("กรรมธารยะ"), ["karmadharaya"] = "กรรมธารยะ", ["karma"] = "กรรมธารยะ", ["kd"] = "กรรมธารยะ", ["kenning"] = make_raw_compound_type("kenning"), ["ken"] = "kenning", ["สัมผัส"] = make_non_glossary_compound_type("สัมผัส"), ["rhyming"] = "สัมผัส", ["rhy"] = "สัมผัส", ["พ้องความ"] = make_non_glossary_compound_type("พ้องความ"), ["synonymous"] = "พ้องความ", ["syn"] = "พ้องความ", ["ตัตปุรุษะ"] = make_compound_type("ตัตปุรุษะ"), ["tatpurusa"] = "ตัตปุรุษะ", ["tat"] = "ตัตปุรุษะ", ["tp"] = "ตัตปุรุษะ", } local function process_etymology_type(typ, nocap, notext, has_parts) local text_sections = {} local categories = {} local borrowing_type if typ then local typdata = export.etymology_types[typ] if type(typdata) == "string" then typdata = export.etymology_types[typdata] end if not typdata then error("Internal error: Unrecognized type '" .. typ .. "'") end local text = typdata.text if not nocap then text = ucfirst(text) end local cat = typdata.cat borrowing_type = typdata.borrowing_type local oftext = typdata.oftext or " ของ" if not notext then insert(text_sections, text) if has_parts then insert(text_sections, oftext) insert(text_sections, " ") end end if cat then insert(categories, cat) end end return text_sections, categories, borrowing_type end ----------------------------------------------------------------------------------------- -- Utility functions -- ----------------------------------------------------------------------------------------- -- Iterate an array up to the greatest integer index found. local function ipairs_with_gaps(t) local indices = m_table.numKeys(t) local max_index = #indices > 0 and math.max(unpack(indices)) or 0 local i = 0 return function() if i < max_index then i = i + 1 return i, t[i] end end end export.ipairs_with_gaps = ipairs_with_gaps --[==[ Join formatted parts together with any overall {{para|lit}} spec, overall left and/or right qualifiers and labels, and categories. On input, `data` is a structure with the following fields: * `affixdata`: The affix data passed into an entry point such as `##show_affix()`; the code in this function examines the `lang`, `lit`, `force_cat`, `nocat`, `sort_key`, `q`, `qq`, `l` and `ll` fields of this structure, which represent overall values for these properties (as opposed to per-part values, which are found inside the list of per-part objects in `parts`, which are not examined); * `parts_formatted`: List of formatted parts, each a string; * `separator_already_added`: If true, any separators have already been included in `parts_formatted`, so the values should be concatenated with nothing in between; otherwise, the separator {" +&lrm; "} will be used, i.e. a `+` sign surrounded by spaces along with a Unicode left-to-right character to ensure that the parts appear in left-to-right order even if they contain right-to-left text; * `categories`: List of category strings or category objects; category strings are formatted using the `sort_key` field in the affix data structure in `affixdata`, while category objects, which are of the form `{ {cat=``category``, sort_key=``sort_key``, sort_base=``sort_base``}`, specify their own sort key and sort base; if `affixdata.nocat` is given, no categories are added; otherwise, `affixdata.force_cat` or global variable `debug_force_cat` causes categories to be added even on userspace pages. ]==] function export.join_formatted_parts(data) local cattext local affixdata = data.affixdata local lang = affixdata.lang local force_cat = affixdata.force_cat or debug_force_cat if affixdata.nocat then cattext = "" else for i, cat in ipairs(data.categories) do if type(cat) == "table" then --data.categories[i] = require(utilities_module).format_categories(lang:getFullName() .. " " .. cat.cat, -- lang, cat.sort_key, cat.sort_base, force_cat) data.categories[i] = require(utilities_module).format_categories( require("Module:th-utilities").th_categorize(cat.cat, lang:getCategoryName()), lang, cat.sort_key, cat.sort_base, force_cat) else --data.categories[i] = require(utilities_module).format_categories(lang:getFullName() .. " " .. cat, lang, -- affixdata.sort_key, nil, force_cat) data.categories[i] = require(utilities_module).format_categories( require("Module:th-utilities").th_categorize(cat, lang:getCategoryName()), lang, affixdata.sort_key, nil, force_cat) end end cattext = concat(data.categories) end local result = concat(data.parts_formatted, not data.separator_already_added and " +&lrm; " or nil) .. (affixdata.lit and ", แปลตามตัวอักษรว่า " .. m_links.mark(affixdata.lit, "gloss") or "") local q = affixdata.q local qq = affixdata.qq local l = affixdata.l local ll = affixdata.ll if q and q[1] or qq and qq[1] or l and l[1] or ll and ll[1] then result = require(decorations_module).format_decorations { lang = lang, text = result, q = q, qq = qq, l = l, ll = ll, } end return result .. cattext end -- Remove links and call lang:stripDiacritics(term). local function strip_diacritics_no_links(lang, term) return lang:stripDiacritics(m_links.remove_links(term)) end --[=[ Convert a raw part as passed into an entry point into a part ready for linking. `lang` and `sc` are the overall language and script objects. This uses the overall language and script objects as defaults for the part and parses off any fragment from the term. We need to do the latter so that fragments don't end up in categories and so that we correctly do affix mapping even in the presence of fragments. ]=] local function canonicalize_part(part, lang, sc) if not part then return end -- Save the original (user-specified, part-specific) value of `lang`. If such a value is specified, we don't insert -- a '*fixed with' category, and we format the part using format_derived() in [[Module:etymology]] rather than -- full_link() in [[Module:links]]. part.part_lang = part.lang part.lang = part.lang or lang part.sc = part.sc or sc local term = part.term if not term then return elseif not part.fragment then part.term, part.fragment = m_links.get_fragment(term) else part.term = m_links.get_fragment(term) end end --[==[ Construct a single linked part based on the information in `part`, for use by `show_affix()` and other entry points. This should be called after `canonicalize_part()` is called on the part. This is a thin wrapper around `full_link()` in [[Module:links]] unless `part.part_lang` is specified (indicating that a part-specific language was given), in which case `format_derived()` in [[Module:etymology]] is called to display a term in a language other than the language of the overall term (specified in `data.lang`). `data` contains the entire object passed into the entry point and is used to access information for constructing the categories added by `format_derived()`. ]==] function export.link_term(part, data, include_separator) local result if part.part_lang then result = require(etymology_module).format_derived { lang = data.lang, terms = {part}, sources = {part.lang}, sort_key = data.sort_key, nocat = data.nocat, template_name = "affix", decorations_on_outside = true, borrowing_type = data.borrowing_type, force_cat = data.force_cat or debug_force_cat, } else result = m_links.full_link(part, "term") end if include_separator and part.separator then return part.separator .. result else return result end end local function canonicalize_script_code(scode) -- Convert 'mnc-Mong', 'xwo-Mong' etc. to 'Mong'. return (scode:gsub("^.*%-", "")) end ----------------------------------------------------------------------------------------- -- Affix-handling functions -- ----------------------------------------------------------------------------------------- -- Figure out the appropriate script for the given affix and language (unless the script is explicitly passed in), and -- return the values of template_hyphens[], display_hyphens[] and lookup_hyphens[] for that script, substituting -- default values as appropriate. Four values are returned: -- DETECTED_SCRIPT, TEMPLATE_HYPHEN, DISPLAY_HYPHEN, LOOKUP_HYPHEN local function detect_script_and_hyphens(text, lang, sc) local scode -- 1. If the script is explicitly passed in, use it. if sc then scode = sc:getCode() else local possible_script_codes = lang:getScriptCodes() -- YUCK! `possible_script_codes` comes from loadData() so #possible_scripts doesn't work (always returns 0). local num_possible_script_codes = m_table.length(possible_script_codes) if num_possible_script_codes == 0 then -- This shouldn't happen; if the language has no script codes, -- the list {"None"} should be returned. error("Something is majorly wrong! Language " .. lang:getCanonicalName() .. " has no script codes.") end if num_possible_script_codes == 1 then -- 2. If the language has only one possible script, use it. scode = possible_script_codes[1] else -- 3. Check if any of the possible scripts for the language have non-default values for template_hyphens[] -- or display_hyphens[]. If so, we need to do script detection on the text. If not, just use "Latn", -- which may not be technically correct but produces the right results because Latn has all default -- values for template_hyphens[] and display_hyphens[]. local may_have_nondefault_hyphen = false for _, script_code in ipairs(possible_script_codes) do script_code = canonicalize_script_code(script_code) if template_hyphens[script_code] or display_hyphens[script_code] then may_have_nondefault_hyphen = true break end end if not may_have_nondefault_hyphen then scode = "Latn" else scode = lang:findBestScript(text):getCode() end end end scode = canonicalize_script_code(scode) local template_hyphen = template_hyphens[scode] or "-" local lookup_hyphen = lookup_hyphens[scode] or "-" local display_hyphen = display_hyphens[scode] or default_display_hyphen return scode, template_hyphen, display_hyphen, lookup_hyphen end --[=[ Given a template affix `term` and an affix type `affix_type`, change the relevant template hyphen(s) in the affix to the display or lookup hyphen specified in `new_hyphen`, or add them if they are missing. `new_hyphen` can be a string, specifying a fixed hyphen, or a function of two arguments (the script code `scode` and the discovered template hyphen, or nil of no relevant template hyphen is present). `thyph_re` is a Lua pattern (which must be enclosed in parens) that matches the possible template hyphens. Note that not all template hyphens present in the affix are changed, but only the "relevant" ones (e.g. for a prefix, a relevant template hyphen is one coming at the end of the affix). ]=] local function reconstruct_term_per_hyphens(term, affix_type, scode, thyph_re, new_hyphen) local function get_hyphen(hyph) if type(new_hyphen) == "string" then return new_hyphen end return new_hyphen(scode, hyph) end if affix_type == "ไม่ใช่หน่วยคำเติม" then return term elseif affix_type == "หน่วยคำเติมคร่อม" then local before, before_hyphen, after_hyphen, after = umatch(term, "^(.*)" .. thyph_re .. " " .. thyph_re .. "(.*)$") if not before or ulen(term) <= 3 then -- Unlike with other types of affixes, don't try to add hyphens in the middle of the term to convert it to -- a circumfix. Also, if the term is just hyphen + space + hyphen, return it. return term end return before .. get_hyphen(before_hyphen) .. " " .. get_hyphen(after_hyphen) .. after elseif affix_type == "อาคม" or affix_type == "หน่วยคำเติมเชื่อม" then local before_hyphen, middle, after_hyphen = umatch(term, "^" .. thyph_re .. "(.*)" .. thyph_re .. "$") if before_hyphen and ulen(term) <= 1 then -- If the term is just a hyphen, return it. return term end return get_hyphen(before_hyphen) .. (middle or term) .. get_hyphen(after_hyphen) elseif affix_type == "อุปสรรค" then local middle, after_hyphen = umatch(term, "^(.*)" .. thyph_re .. "$") if middle and ulen(term) <= 1 then -- If the term is just a hyphen, return it. return term end return (middle or term) .. get_hyphen(after_hyphen) elseif affix_type == "ปัจจัย" then local before_hyphen, middle = umatch(term, "^" .. thyph_re .. "(.*)$") if before_hyphen and ulen(term) <= 1 then -- If the term is just a hyphen, return it. return term end return get_hyphen(before_hyphen) .. (middle or term) else error(("Internal error: Unrecognized affix type '%s'"):format(affix_type)) end end --[=[ Look up a mapping from a given affix variant to the canonical form used in categories and links. The lookup tables are language-specific according to `lang`, and may be ID-specific according to `affix_id`. The affixes as they appear in the lookup tables (both the variant and the canonical form) are in "lookup affix" format (approximately speaking, they use a regular hyphen for most scripts, but a tatweel for Arabic-script entries and a maqqef for Hebrew-script entries), but the passed-in `affix` param is in "template affix" format (which differs from the lookup affix for Arabic-script entries, because more types of hyphens are allowed in template affixes; see the comments at the top of the file). The remaining parameters to this function are used to convert from template affixes to lookup affixes; see the reconstruct_term_per_hyphens() function above. If the affix contains brackets, no lookup is done. Otherwise, a two-stage process is used, first looking up the affix directly and then stripping diacritics and looking it up again. The reason for this is documented above in the comments at the top of the file (specifically, the comments describing lookup affixes). The value of a mapping can either be a string (do the mapping regardless of affix ID) or a table indexed by affix ID (where the special value `false` indicates no affix ID). The values of entries in this table can also be strings, or tables with keys `affix` and `id` (again, use `false` to indicate no ID). This allows an affix mapping to map from one ID to another (for example, this is used in English to map the [[an-]] prefix with no ID to the [[a-]] prefix with the ID 'not'). The Given a template affix `term` and an affix type `affix_type`, change the relevant template hyphen(s) in the affix to the display or lookup hyphen specified in `new_hyphen`, or add them if they are missing. `new_hyphen` can be a string, specifying a fixed hyphen, or a function of two arguments (the script code `scode` and the discovered template hyphen, or nil of no relevant template hyphen is present). `thyph_re` is a Lua pattern (which must be enclosed in parens) that matches the possible template hyphens. Note that not all template hyphens present in the affix are changed, but only the "relevant" ones (e.g. for a prefix, a relevant template hyphen is one coming at the end of the affix). ]=] local function lookup_affix_mapping(affix, affix_type, lang, scode, thyph_re, lookup_hyph, affix_id) local function do_lookup(afx) -- Ensure that the affix uses lookup hyphens regardless of whether it used a different type of hyphens before -- or no hyphens. local lookup_affix = reconstruct_term_per_hyphens(afx, affix_type, scode, thyph_re, lookup_hyph) local function do_lookup_for_langcode(langcode) if export.langs_with_lang_specific_data[langcode] then local langdata = mw.loadData(export.affix_lang_data_module_prefix .. langcode) if langdata.affix_mappings then local mapping = langdata.affix_mappings[lookup_affix] if mapping then if type(mapping) == "table" then mapping = mapping[affix_id] or mapping.default or mapping[affix_id or false] if mapping then return mapping end else return mapping end end end end end -- If `lang` is an etymology-only language, look for a mapping both for it and its full parent. local langcode = lang:getCode() local mapping = do_lookup_for_langcode(langcode) if mapping then return mapping end local full_langcode = lang:getFullCode() if full_langcode ~= langcode then mapping = do_lookup_for_langcode(full_langcode) if mapping then return mapping end end return nil end if affix:find("%[%[") then return nil end return do_lookup(affix) or do_lookup(lang:stripDiacritics(affix)) or nil end --[==[ For a given template term in a given language (see the definition of "template affix" near the top of the file), possibly in an explicitly specified script `sc` (but usually nil), return the term's affix type ({"prefix"}, {"interfix"}, {"suffix"}, {"circumfix"} or {"non-affix"}) along with the corresponding link and display affixes (see definitions near the top of the file); also the corresponding lookup affix (if `return_lookup_affix` is specified). The term passed in should already have any fragment (after the # sign) parsed off of it. Four values are returned: `affix_type`, `link_term`, `display_term` and `lookup_term`. The affix type can be passed in instead of autodetected; in this case, the template term need not have any attached hyphens, and the appropriate hyphens will be added in the appropriate places. If `do_affix_mapping` is specified, look up the affix in the lang-specific affix mappings, as described in the comment at the top of the file; otherwise, the link and display terms will always be the same. (They will be the same in any case if the template term has a bracketed link in it or is not an affix.) If `return_lookup_affix` is given, the fourth return value contains the term with appropriate lookup hyphens in the appropriate places; otherwise, it is the same as the display term. (This functionality is used in [[Module:category tree/affixes and compounds]] to convert link affixes into lookup affixes so that they can be looked up in the affix mapping tables.) Exported because used by [[Module:headword utilities]] to determine the affix type of a given pagename. ]==] function export.parse_term_for_affixes(term, lang, sc, affix_type, do_affix_mapping, return_lookup_affix, affix_id) if not term then return "ไม่ใช่หน่วยคำเติม", nil, nil, nil end if term == "^" then -- Indicates a null term to emulate the behavior of {{suffix|foo||bar}}. term = "" return "ไม่ใช่หน่วยคำเติม", term, term, term end if term:find("^%^") then -- HACK! ^ at the beginning of Korean languages has a special meaning, triggering capitalization of the -- transliteration. Don't interpret it as "force non-affix" for those languages. local langcode = lang:getCode() if langcode ~= "ko" and langcode ~= "okm" and langcode ~= "jje" then -- Formerly we allowed ^ to force non-affix type; this is now handled using an inline modifier -- <naf>, <root>, etc. Throw an error for the moment when the old way is encountered. error("Use of ^ to force non-affix status is no longer supported; use an inline modifier <naf> or <root> " .. "after the component") end end -- Remove an asterisk if the morpheme is reconstructed and add it back at the end. local reconstructed = "" if term:find("^%*") then reconstructed = "*" term = term:gsub("^%*", "") end local scode, thyph, dhyph, lhyph = detect_script_and_hyphens(term, lang, sc) thyph = "([" .. thyph .. "])" if not affix_type then if ufind(term, thyph .. " " .. thyph) then affix_type = "หน่วยคำเติมคร่อม" else local has_beginning_hyphen = ufind(term, "^" .. thyph) local has_ending_hyphen = ufind(term, thyph .. "$") if has_beginning_hyphen and has_ending_hyphen then affix_type = "หน่วยคำเติมเชื่อม" elseif has_ending_hyphen then affix_type = "อุปสรรค" elseif has_beginning_hyphen then affix_type = "ปัจจัย" else affix_type = "ไม่ใช่หน่วยคำเติม" end end end local link_term, display_term, lookup_term if affix_type == "ไม่ใช่หน่วยคำเติม" then link_term = term display_term = term lookup_term = term else display_term = reconstruct_term_per_hyphens(term, affix_type, scode, thyph, dhyph) if do_affix_mapping then link_term = lookup_affix_mapping(term, affix_type, lang, scode, thyph, lhyph, affix_id) -- The return value of lookup_affix_mapping() may be an affix mapping with lookup hyphens if a mapping -- was found, otherwise nil if a mapping was not found. We need to convert to display hyphens in -- either case, but in the latter case we can reuse the display term, which has already been converted. if link_term then link_term = reconstruct_term_per_hyphens(link_term, affix_type, scode, thyph, dhyph) else link_term = display_term end else link_term = display_term end if return_lookup_affix then lookup_term = reconstruct_term_per_hyphens(term, affix_type, scode, thyph, lhyph) else lookup_term = display_term end end link_term = reconstructed .. link_term display_term = reconstructed .. display_term lookup_term = reconstructed .. lookup_term return affix_type, link_term, display_term, lookup_term end --[==[ Add a hyphen to a term in the appropriate place, based on the specified affix type, stripping off any existing hyphens in that place. For example, if `affix_type` == {"prefix"}, we'll add a hyphen onto the end if it's not already there (or is of the wrong type). Three values are returned: the link term, display term and lookup term. This function is a thin wrapper around `parse_term_for_affixes`; see the comments above that function for more information. Note that this function is exposed externally because it is called by [[Module:category tree/affixes and compounds]]; see the comment in `parse_term_for_affixes` for more information. ]==] function export.make_affix(term, lang, sc, affix_type, do_affix_mapping, return_lookup_affix, affix_id) if not (affix_type == "อุปสรรค" or affix_type == "ปัจจัย" or affix_type == "หน่วยคำเติมคร่อม" or affix_type == "อาคม" or affix_type == "หน่วยคำเติมเชื่อม" or affix_type == "ไม่ใช่หน่วยคำเติม") then error("Internal error: Invalid affix type " .. (affix_type or "(nil)")) end local _, link_term, display_term, lookup_term = export.parse_term_for_affixes(term, lang, sc, affix_type, do_affix_mapping, return_lookup_affix, affix_id) return link_term, display_term, lookup_term end ----------------------------------------------------------------------------------------- -- Categorization logic -- ----------------------------------------------------------------------------------------- local function insert_category(categories, cat, sort_key, sort_base) if sort_key or sort_base then insert(categories, {cat = cat, sort_key = sort_key, sort_base = sort_base}) else insert(categories, cat) end end --[=[ Insert a `*fixed with` category such as [[:Category:Welsh terms prefixed with gwe-]] into `categories`. The arguments are: * `categories`: List of category strings or objects to insert into. * `pos`: Part of speech in the category (which is often "terms" but may be something more specific). * `affix_type`: Affix type in the category (e.g. "prefix"); automatically has "ed" appended. * `link_term`: The term that appears in the category, e.g. "gwe-". If this is empty or missing, no category is inserted. * `part`: The part structure being processed. The `lang`, `part_lang`, `nocat` and `id` fields are fetched. * `sort_key`: The sort key for the specific category. If this or `sort_base` are non-nil, a category object is created holding the specified sort key and/or base, and inserted; otherwise, a cateory string is inserted. * `sort_base`: The sort base for the specific category. See `sort_key` just above. ]=] local function insert_affix_category(categories, pos, affix_type, link_term, part, sort_key, sort_base) -- Don't add a category if the link term is empty or is in a different language, or if <nocat:1> was specified for -- the part. if ine(link_term) and not part.part_lang and not part.nocat then local cat = pos .. " " .. affix_type .. "ed with " .. strip_diacritics_no_links(part.lang, link_term) .. (part.id and " (" .. part.id .. ")" or "") insert_category(categories, cat, sort_key, sort_base) end end --[=[ Generate the categories for a single part and insert into `categories`. The arguments are: * `categories` is the list to insert the categories into. * `data` is the overall affix data structure. * `part` is the per-part data structure. * `affix_type` is the type of affix (e.g. "prefix"), as appears in the `*fixed by` category. * `link_term` is the term that (prefix, suffix, etc.) mentioned in the `*fixed by` category. * `sort_key` and `sort_base` are the per-part sort keys and bases for categorization, or nil to use the overall sort key. ]=] local function generate_one_part_categories(categories, data, part, affix_type, link_term, sort_key, sort_base) if data.nocat or part.nocat then return end if part.pos and part.pos:find("patronym") then insert_category(categories, "patronymics", sort_key, sort_base) end if data.pos ~= "terms" and part.pos and part.pos:find("diminutive") then insert_category(categories, "diminutive " .. data.pos, sort_key, sort_base) end insert_affix_category(categories, data.pos, affix_type, link_term, part, sort_key, sort_base) end --[=[ Core categorization logic for affixes. This is shared between show_affix(), show_compound_like() and get_affix_categories_only(). Returns the categories array and other metadata needed for formatting. ]=] local function generate_affix_categories(data) data.pos = data.pos or default_pos data.pos = pluralize_pos(data.pos) local text_sections, categories, borrowing_type = process_etymology_type(data.type, data.surface_analysis or data.nocap, data.notext, #data.parts > 0) data.borrowing_type = borrowing_type -- Process each part local whole_words = 0 local is_affix_or_compound = false -- Canonicalize and generate links for all the parts first; then do categorization in a separate step, because when -- processing the first part for categorization, we may access the second part and need it already canonicalized. for i, part in ipairs_with_gaps(data.parts) do part = part or {} data.parts[i] = part canonicalize_part(part, data.lang, data.sc) -- Determine affix type and get link and display terms (see text at top of file). Store them in the part -- (in fields that won't clash with fields used by full_link() in [[Module:links]] or link_term()), so they -- can be used in the loop below when categorizing. part.affix_type, part.affix_link_term, part.affix_display_term = export.parse_term_for_affixes(part.term, part.lang, part.sc, part.type, not part.alt, nil, part.id) -- If link_term is an empty string, either a bare ^ was specified or an empty term was used along with inline -- modifiers. The intention in either case is not to link the term. part.term = ine(part.affix_link_term) -- If part.alt would be the same as part.term, make it nil, so that it isn't erroneously tracked as being -- redundant alt text. part.alt = part.alt or (part.affix_display_term ~= part.affix_link_term and part.affix_display_term) or nil end if not data.noaffixcat then -- Now do categorization. for i, part in ipairs_with_gaps(data.parts) do local affix_type = part.affix_type if affix_type ~= "ไม่ใช่หน่วยคำเติม" then is_affix_or_compound = true -- Make a sort key. For the first part, use the second part as the sort key; the intention is that if the -- term has a prefix, sorting by the prefix won't be very useful so we sort by what follows, which is -- presumably the root. local part_sort_base = nil local part_sort = part.sort or data.sort_key if i == 1 and data.parts[2] and data.parts[2].term then local part2 = data.parts[2] -- If the second-part link term is empty, the user requested an unlinked term; avoid a wikitext error -- by using the alt value if available. part_sort_base = ine(part2.affix_link_term) or ine(part2.alt) if part_sort_base then part_sort_base = strip_diacritics_no_links(part2.lang, part_sort_base) end end generate_one_part_categories(categories, data, part, affix_type, part.affix_link_term, part_sort, part_sort_base) else whole_words = whole_words + 1 if whole_words == 2 then is_affix_or_compound = true --insert(categories, "compound " .. data.pos) if data.pos == "ศัพท์" then --th insert(categories, "คำประสม") else insert(categories, data.pos .. "ประสม") end end end end -- Make sure there was either an affix or a compound (two or more non-affix terms). if not is_affix_or_compound and not data.allow_no_affixes_or_compounds then error("The parameters did not include any affixes, and the term is not a compound. Please provide at least one affix.") end end return text_sections, categories, borrowing_type end ----------------------------------------------------------------------------------------- -- Main entry points -- ----------------------------------------------------------------------------------------- --[==[ Implementation of {{tl|affix}} and {{tl|surface analysis}}. `data` contains all the information describing the affixes to be displayed, and contains the following: * `.lang` ('''required'''): Overall language object. Different from term-specific language objects (see `.parts` below). * `.sc`: Overall script object (usually omitted). Different from term-specific script objects. * `.parts` ('''required'''): List of objects describing the affixes to show. The general format of each object is as would be passed to `full_link()`, except that the `.lang` field should be missing unless the term is of a language different from the overall `.lang` value (in such a case, the language name is shown along with the term and an additional "derived from" category is added). '''WARNING''': The data in `.parts` will be destructively modified. * `.pos`: Overall part of speech (used in categories, defaults to {"terms"}). Different from term-specific part of speech. * `.sort_key`: Overall sort key. Normally omitted except e.g. in Japanese. * `.type`: Type of compound, if the parts in `.parts` describe a compound. Strictly optional, and if supplied, the compound type is displayed before the parts (normally capitalized, unless `.nocap` is given). * `.nocap`: Don't capitalize the first letter of text displayed before the parts (relevant only if `.type` or `.surface_analysis` is given). * `.notext`: Don't display any text before the parts (relevant only if `.type` or `.surface_analysis` is given). * `.nocat`: Disable all categorization. * `.noaffixcat`: Disable affix (and compound) categorization. Relevant for e.g. blends, which may otherwise be incorrectly categorized as compound terms. * `.lit`: Overall literal definition. Different from term-specific literal definitions. * `.force_cat`: Always display categories, even on userspace pages. * `.surface_analysis`: Implement {{surface analysis}}; adds `By surface analysis, ` before the parts. '''WARNING''': This destructively modifies both `data` and the individual structures within `.parts`. ]==] function export.show_affix(data) local text_sections, categories, _ = generate_affix_categories(data) -- Process each part for display local parts_formatted = {} for i, part in ipairs_with_gaps(data.parts) do -- Make a link for the part insert(parts_formatted, export.link_term(part, data, "include_separator")) end if data.surface_analysis then --local text = "by " .. glossary_link("surface analysis") .. ", " local text = "พิจารณาคร่าว ๆ เทียบเท่า " if not data.nocap then text = ucfirst(text) end insert(text_sections, 1, text) end insert(text_sections, export.join_formatted_parts { affixdata = data, parts_formatted = parts_formatted, categories = categories, separator_already_added = true }) return concat(text_sections) end --[==[ Get only the categories that would be generated by show_affix(), without any text output or formatting. This is used by Module:etymon to get affix categorization. Returns an array of category objects, where each entry is either a string (simple category name) or a table with keys `cat`, `sort_key`, and `sort_base` for more complex categorization. `data` should have the same structure as passed to show_affix(): * `.lang` (required): Overall language object * `.parts` (required): Array of affix part objects with `.term`, `.lang`, `.id`, etc. * `.pos`: Part of speech (defaults to "terms") * `.sort_key`: Overall sort key for categories '''WARNING''': This destructively modifies both `data` and the individual structures within `.parts`. ]==] function export.get_affix_categories_only(data) local _, categories, _ = generate_affix_categories(data) return categories end function export.show_surface_analysis(data) data.surface_analysis = true data.allow_no_affixes_or_compounds = true return export.show_affix(data) end --[==[ Implementation of {{tl|compound}}. '''WARNING''': This destructively modifies both `data` and the individual structures within `.parts`. ]==] function export.show_compound(data) data.pos = data.pos or default_pos data.pos = pluralize_pos(data.pos) local text_sections, categories, borrowing_type = process_etymology_type(data.type, data.nocap, data.notext, #data.parts > 0) data.borrowing_type = borrowing_type local parts_formatted = {} --insert(categories, "compound " .. data.pos) if data.pos == "ศัพท์" then --th insert(categories, "คำประสม") else insert(categories, data.pos .. "ประสม") end -- Make links out of all the parts local whole_words = 0 for i, part in ipairs(data.parts) do canonicalize_part(part, data.lang, data.sc) -- Determine affix type and get link and display terms (see text at top of file). local affix_type, link_term, display_term = export.parse_term_for_affixes(part.term, part.lang, part.sc, part.type, not part.alt, nil, part.id) -- If the term is an interfix or the type was explicitly given, recognize it as such (which means e.g. that we -- will display the term without hyphens for East Asian languages). Otherwise, ignore the fact that it looks -- like an affix and display as specified in the template (but pay attention to the detected affix type for -- certain tracking purposes). if affix_type == "หน่วยคำเติมเชื่อม" or (part.type and part.type ~= "ไม่ใช่หน่วยคำเติม") then -- Add a `*fixed with` category. However, if link_term is an empty string, either a bare ^ was specified or -- an empty term was used along with inline modifiers. The intention in either case is not to link the term. -- Don't add a category in this case, or if the term is in a different language (or if overall nocat=1 was -- given, or part-specific <nocat:1>). if not data.nocat then -- FIXME, should we be specifying a sort base here? insert_affix_category(categories, data.pos, affix_type, link_term, part, part.sort or data.sort_key) end -- If part.alt would be the same as part.term, make it nil, so that it isn't erroneously tracked as being -- redundant alt text. part.term = link_term ~= "" and link_term or nil part.alt = part.alt or (display_term ~= link_term and display_term) or nil else if affix_type ~= "ไม่ใช่หน่วยคำเติม" then local langcode = data.lang:getCode() -- If `data.lang` is an etymology-only language, track both using its code and its full parent's code. track { affix_type, affix_type .. "/lang/" .. langcode } local full_langcode = data.lang:getFullCode() if langcode ~= full_langcode then track(affix_type .. "/lang/" .. full_langcode) end else whole_words = whole_words + 1 end end insert(parts_formatted, export.link_term(part, data, "include_separator")) end if whole_words == 1 then track("one whole word") elseif whole_words == 0 then track("looks like confix") end insert(text_sections, export.join_formatted_parts { affixdata = data, parts_formatted = parts_formatted, categories = categories, separator_already_added = true }) return concat(text_sections) end --[==[ Implementation of {{tl|blend}}, {{tl|univerbation}} and similar "compound-like" templates. '''WARNING''': This destructively modifies both `data` and the individual structures within `.parts`. ]==] function export.show_compound_like(data) data.allow_no_affixes_or_compounds = true local text_sections, categories, _ = generate_affix_categories(data) if data.cat then insert(categories, data.cat) end -- Process each part for display local parts_formatted = {} for i, part in ipairs_with_gaps(data.parts) do -- Make a link for the part insert(parts_formatted, export.link_term(part, data, "include_separator")) end if #data.parts > 0 and data.oftext then insert(text_sections, 1, " " .. data.oftext .. " ") end if data.text then insert(text_sections, 1, data.text) end insert(text_sections, export.join_formatted_parts { affixdata = data, parts_formatted = parts_formatted, categories = categories, separator_already_added = true }) return concat(text_sections) end --[==[ Make `part` (a structure holding information on an affix part) into an affix of type `affix_type`, and apply any relevant affix mappings. For example, if the desired affix type is "suffix", this will (in general) add a hyphen onto the beginning of the term, alt, tr and ts components of the part if not already present. The hyphen that's added is the "display hyphen" (see above) and may be script-specific. (In the case of East Asian scripts, the display hyphen is an empty string whereas the template hyphen is the regular hyphen, meaning that any regular hyphen at the beginning of the part will be effectively removed.) `lang` and `sc` hold overall language and script objects. Note that this also applies any language-specific affix mappings, so that e.g. if the language is Finnish and the user specified [[-käs]] in the affix and didn't specify an `.alt` value, `part.term` will contain [[-kas]] and `part.alt` will contain [[-käs]]. This function is used by the "legacy" templates ({{tl|prefix}}, {{tl|suffix}}, {{tl|confix}}, etc.) where the nature of the affix is specified by the template itself rather than auto-determined from the affix, as is the case with {{tl|affix}}. '''WARNING''': This destructively modifies `part`. ]==] local function make_part_into_affix(part, lang, sc, affix_type) canonicalize_part(part, lang, sc) local link_term, display_term = export.make_affix(part.term, part.lang, part.sc, affix_type, not part.alt, nil, part.id) part.term = link_term -- When we don't specify `do_affix_mapping` to make_affix(), link and display terms (first and second retvals of -- make_affix()) are the same. -- If part.alt would be the same as part.term, make it nil, so that it isn't erroneously tracked as being -- redundant alt text. part.alt = part.alt and export.make_affix(part.alt, part.lang, part.sc, affix_type) or (display_term ~= link_term and display_term) or nil local Latn = require(scripts_module).getByCode("Latn") part.tr = export.make_affix(part.tr, part.lang, Latn, affix_type) part.ts = export.make_affix(part.ts, part.lang, Latn, affix_type) end local function track_wrong_affix_type(template, part, expected_affix_type) if part and not part.type then local affix_type = export.parse_term_for_affixes(part.term, part.lang, part.sc) if affix_type ~= expected_affix_type then local part_name = expected_affix_type or "base" local langcode = part.lang:getCode() local full_langcode = part.lang:getFullCode() require("Module:debug/track") { template, template .. "/" .. part_name, template .. "/" .. part_name .. "/" .. (affix_type or "none"), template .. "/" .. part_name .. "/" .. (affix_type or "none") .. "/lang/" .. langcode } -- If `part.lang` is an etymology-only language, track both using its code and its full parent's code. if full_langcode ~= langcode then require("Module:debug/track")( template .. "/" .. part_name .. "/" .. (affix_type or "none") .. "/lang/" .. full_langcode ) end end end end --[==[ Implementation of {{tl|confix}}. '''WARNING''': This destructively modifies both `data` and `.prefix`, `.base` and `.suffix`. ]==] function export.show_confix(data) data.pos = data.pos or default_pos data.pos = pluralize_pos(data.pos) canonicalize_part(data.base, data.lang, data.sc) -- Hyphenate the affixes and apply any affix mappings. make_part_into_affix(data.prefix, data.lang, data.sc, "อุปสรรค") make_part_into_affix(data.suffix, data.lang, data.sc, "ปัจจัย") track_wrong_affix_type("confix", data.prefix, "อุปสรรค") track_wrong_affix_type("confix", data.base, nil) track_wrong_affix_type("confix", data.suffix, "ปัจจัย") -- Make links out of all the parts. local parts_formatted = {} local prefix_sort_base if data.base and data.base.term then prefix_sort_base = strip_diacritics_no_links(data.base.lang, data.base.term) elseif data.suffix.term then prefix_sort_base = strip_diacritics_no_links(data.suffix.lang, data.suffix.term) end -- Insert the categories and parts. local categories = {} insert(parts_formatted, export.link_term(data.prefix, data)) insert_affix_category(categories, data.pos, "อุปสรรค", data.prefix.term, data.prefix, data.sort_key, prefix_sort_base) if data.base then insert(parts_formatted, export.link_term(data.base, data)) end insert(parts_formatted, export.link_term(data.suffix, data)) -- FIXME, should we be specifying a sort base here? insert_affix_category(categories, data.pos, "ปัจจัย", data.suffix.term, data.suffix) return export.join_formatted_parts { affixdata = data, parts_formatted = parts_formatted, categories = categories } end --[==[ Implementation of {{tl|infix}}. '''WARNING''': This destructively modifies both `data` and `.base` and `.infix`. ]==] function export.show_infix(data) data.pos = data.pos or default_pos data.pos = pluralize_pos(data.pos) canonicalize_part(data.base, data.lang, data.sc) -- Hyphenate the affixes and apply any affix mappings. make_part_into_affix(data.infix, data.lang, data.sc, "อาคม") track_wrong_affix_type("อาคม", data.base, nil) track_wrong_affix_type("อาคม", data.infix, "อาคม") -- Make links out of all the parts. local parts_formatted = {} local categories = {} insert(parts_formatted, export.link_term(data.base, data)) insert(parts_formatted, export.link_term(data.infix, data)) -- Insert the categories. -- FIXME, should we be specifying a sort base here? insert_affix_category(categories, data.pos, "อาคม", data.infix.term, data.infix) return export.join_formatted_parts { affixdata = data, parts_formatted = parts_formatted, categories = categories } end --[==[ Implementation of {{tl|prefix}}. '''WARNING''': This destructively modifies both `data` and the structures within `.prefixes`, as well as `.base`. ]==] function export.show_prefix(data) data.pos = data.pos or default_pos data.pos = pluralize_pos(data.pos) canonicalize_part(data.base, data.lang, data.sc) -- Hyphenate the affixes and apply any affix mappings. for i, prefix in ipairs(data.prefixes) do make_part_into_affix(prefix, data.lang, data.sc, "อุปสรรค") end for i, prefix in ipairs(data.prefixes) do track_wrong_affix_type("อุปสรรค", prefix, "อุปสรรค") end track_wrong_affix_type("อุปสรรค", data.base, nil) -- Make links out of all the parts. local parts_formatted = {} local first_sort_base = nil local categories = {} if data.prefixes[2] then first_sort_base = ine(data.prefixes[2].term) or ine(data.prefixes[2].alt) if first_sort_base then first_sort_base = strip_diacritics_no_links(data.prefixes[2].lang, first_sort_base) end elseif data.base then first_sort_base = ine(data.base.term) or ine(data.base.alt) if first_sort_base then first_sort_base = strip_diacritics_no_links(data.base.lang, first_sort_base) end end for i, prefix in ipairs(data.prefixes) do insert(parts_formatted, export.link_term(prefix, data)) generate_one_part_categories(categories, data, prefix, "อุปสรรค", prefix.term, data.sort_key, i == 1 and first_sort_base or nil) end if data.base then insert(parts_formatted, export.link_term(data.base, data)) else insert(parts_formatted, "") end return export.join_formatted_parts { affixdata = data, parts_formatted = parts_formatted, categories = categories } end --[==[ Implementation of {{tl|suffix}}. '''WARNING''': This destructively modifies both `data` and the structures within `.suffixes`, as well as `.base`. ]==] function export.show_suffix(data) local categories = {} data.pos = data.pos or default_pos data.pos = pluralize_pos(data.pos) canonicalize_part(data.base, data.lang, data.sc) -- Hyphenate the affixes and apply any affix mappings. for i, suffix in ipairs(data.suffixes) do make_part_into_affix(suffix, data.lang, data.sc, "ปัจจัย") end track_wrong_affix_type("ปัจจัย", data.base, nil) for i, suffix in ipairs(data.suffixes) do track_wrong_affix_type("ปัจจัย", suffix, "ปัจจัย") end -- Make links out of all the parts. local parts_formatted = {} if data.base then insert(parts_formatted, export.link_term(data.base, data)) else insert(parts_formatted, "") end for i, suffix in ipairs(data.suffixes) do insert(parts_formatted, export.link_term(suffix, data)) end -- Insert the categories. for i, suffix in ipairs(data.suffixes) do -- FIXME, should we be specifying a sort base here? generate_one_part_categories(categories, data, suffix, "ปัจจัย", suffix.term) end return export.join_formatted_parts { affixdata = data, parts_formatted = parts_formatted, categories = categories } end return export qesykgqn5k1mevisqi027edhf7vpd0p 5775185 5775183 2026-10-14T03:56:11Z Octahedron80 267 5775185 Scribunto text/plain local export = {} local debug_force_cat = false -- if set to true, always display categories even on userspace pages local m_links = require("Module:links") local m_str_utils = require("Module:string utilities") local m_table = require("Module:table") local decorations_module = "Module:decorations" local en_utilities_module = "Module:en-utilities" local etymology_module = "Module:etymology" local scripts_module = "Module:scripts" local utilities_module = "Module:utilities" -- Export this so the category code in [[Module:category tree/etymology]] can access it. export.affix_lang_data_module_prefix = "Module:affix/lang-data/" local ulen = m_str_utils.len local ufind = m_str_utils.find local umatch = m_str_utils.match local pluralize = require(en_utilities_module).pluralize local singularize = require(en_utilities_module).singularize local u = m_str_utils.char local ucfirst = m_str_utils.ucfirst local concat = table.concat local insert = table.insert local unpack = unpack or table.unpack -- Lua 5.2 compatibility function export.affix_variants(canonical, variants) local mappings = {} for _, variant in ipairs(variants) do mappings[variant] = canonical end return mappings end function export.id_mapping(default, ids) local mapping = { default = default } if ids then for id, target in pairs(ids) do mapping[id] = target end end return mapping end function export.id_mapping_with_affix_variants(base, id_variants) local mappings = {} for id, variants in pairs(id_variants) do for _, variant in ipairs(variants) do mappings[variant] = export.id_mapping(base, {[id] = base}) end end return mappings end function export.merge_tables(...) local result = {} for i = 1, select('#', ...) do local t = select(i, ...) if t then for k, v in pairs(t) do result[k] = v end end end return result end -- Export this so the category code in [[Module:category tree/etymology]] can access it. export.langs_with_lang_specific_data = { ["az"] = true, ["fi"] = true, ["fr"] = true, ["izh"] = true, ["la"] = true, ["sah"] = true, ["tr"] = true, ["trk-pro"] = true, } local default_pos = "ศัพท์" local function pluralize_pos(pos) return --[[pluralize(singularize(pos))]] pos end --[==[ intro: ===About different types of hyphens ("template", "display" and "lookup"):=== * The "template hyphen" is the per-script hyphen character that is used in template calls to indicate that a term is an affix. This is always a single Unicode char, but there may be multiple possible hyphens for a given script. Normally this is just the regular hyphen character "-", but for some non-Latin-script languages (currently only right-to-left languages), it is different. * The "display hyphen" is the string (which might be an empty string) that is added onto a term as displayed and linked, to indicate that a term is an affix. Currently this is always either the same as the template hyphen or an empty string, but the code below is written generally enough to handle arbitrary display hyphens. Specifically: *# For East Asian languages, the display hyphen is always blank. *# For Arabic-script languages, either tatweel (ـ) or ZWNJ (zero-width non-joiner) are allowed as template hyphens, where ZWNJ is supported primarily for Farsi, because some suffixes have non-joining behavior. The display hyphen corresponding to tatweel is also tatweel, but the display hyphen corresponding to ZWNJ is blank (tatweel is also the default display hyphen, for calls to {{tl|prefix}}/{{tl|suffix}}/etc. that don't include an explicit hyphen). * The "lookup hyphen" is the hyphen that is used when looking up language-specific affix mappings. (These mappings are discussed in more detail below when discussing link affixes.) It depends only on the script of the affix in question. Most scripts (including East Asian scripts) use a regular hyphen "-" as the lookup hyphen, but Hebrew and Arabic have their own lookup hyphens (respectively maqqef and tatweel). Note that for Arabic in particular, there are three possible template hyphens that are recognized (tatweel, ZWNJ and regular hyphen), but mappings must use tatweel. ===About different types of affixes ("template", "display", "link", "lookup" and "category"):=== * A "template affix" is an affix in its source form as it appears in a template call. Generally, a template affix has an attached template hyphen (see above) to indicate that it is an affix and indicate what type of affix it is (prefix, suffix, interfix or circumfix), but some of the older-style templates such as {{tl|suffix}}, {{tl|prefix}}, {{tl|confix}}, etc. have "positional" affixes where the presence of the affix in a certain position (e.g. the second or third parameter) indicates that it is a certain type of affix, whether or not it has an attached template hyphen. * A "display affix" is the corresponding affix as it is actually displayed to the user. The display affix may differ from the template affix for various reasons: *# The display affix may be specified explicitly using the {{para|alt<var>N</var>}} parameter, the `<alt:...>` inline modifier or a piped link of the form e.g. `<nowiki>[[-kas|-käs]]</nowiki>` (here indicating that the affix should display as `-käs` but be linked as `-kas`). Here, the template affix is arguably the entire piped link, while the display affix is `-käs`. *# Even in the absence of {{para|alt<var>N</var>}} parameters, `<alt:...>` inline modifiers and piped links, certain languages have differences between the "template hyphen" specified in the template (which always needs to be specified somehow or other in templates like {{tl|affix}}, to indicate that the term is an affix and what type of affix it is) and the display hyphen (see above), with corresponding differences between template and display affixes. * A (regular) "link affix" is the affix that is linked to when the affix is shown to the user. The link affix is usually the same as the display affix, but will differ in one of three circumstances: *# The display and link affixes are explicitly made different using {{para|alt<var>N</var>}} parameters, `<alt:...>` inline modifiers or piped links, as described above under "display affix". *# For certain languages, certain affixes are mapped to canonical form using language-specific mappings. For example, in Finnish, the adjective-forming suffix {{m|fi|-kas}} appears as {{m|fi|-käs}} after front vowels, but logically both forms are the same suffix and should be linked and categorized the same. Similarly, in Latin, the negative and intensive prefixes spelled {{m|la|in-}} (etymologically two distinct prefixes) appear variously as {{m|la|il-}}, {{m|la|im-}} or {{m|la|ir-}} before certain consonants. Mappings are supplied in [[Module:affix/lang-data/LANGCODE]] to convert Finnish {{m|fi|-käs}} to {{m|fi|-kas}} for linking and categorization purposes. Note that the affixes in the mappings use "lookup hyphens" to indicate the different types of affixes, which is usually the same as the template hyphen but differs for Arabic scripts, because there are multiple possible template hyphens recognized but only one lookup hyphen (tatweel). The form of the affix as used to look up in the mapping tables is called the "lookup affix"; see below. * A "stripped link affix" is a link affix that has been passed through the language's `stripDiacritics()` function, which may strip certain diacritics: e.g. macrons in Latin and Old English (indicating length); acute and grave accents in Russian and various other Slavic languages (indicating stress); vowel diacritics in most Arabic-script languages; and also tatweel in some Arabic-script languages (currently, for example, Persian, Arabic and Urdu strip tatweel, but Ottoman Turkish does not). Stripped link affixes are currently what are used in category names. * A "lookup affix" is the form of the affix as it is looked up in the language-specific lookup mappings described above under link affixes. There are actually two lookup stages: *# First, the affix is looked up in a modified display form (specifically, the same as the display affix but using lookup hyphens). Note that this lookup does not occur if an explicit display form is given using {{para|alt<var>N</var>}} or an `<alt:...>` inline modifier, or if the template affix contains a piped or embedded link. *# If no entry is found, the affix is then looked up in a modified link form (specifically, the modified display form passed through the language's `stripDiacritics()` function, which strips out certain diacritics, but with the lookup hyphen re-added if it was stripped out, as in the case of tatweel in many Arabic-script languages). The reason for this double lookup procedure is to allow for mappings that are sensitive to the extra diacritics, but also allow for mappings that are not sensitive in this fashion (e.g. Russian {{m|ru|-ливый}} occurs both stressed and unstressed, but is the same prefix either way). * A "category affix" is the affix as it appears in categories such as [[:Category:Finnish terms suffixed with -kas| Category:Finnish terms suffixed with ''-kas'']]. The category affix is currently always the same as the stripped link affix. This means that for Arabic-script languages, it may or may not have a tatweel, even if the correponding display affix and regular link affix have a tatweel. As mentioned above, stripDiacritics() strips tatweel for Arabic, Persian and Urdu, but not for Ottoman Turkish. Hence affix categories for Arabic, Persian and Urdu will be missing the tatweel, but affix categories for Ottoman Turkish will have it. An additional complication is that if the template affix contains a ZWNJ, the display (and hence the link and category affixes) will have no hyphen attached in any case. ]==] ----------------------------------------------------------------------------------------- -- Template and display hyphens -- ----------------------------------------------------------------------------------------- --[=[ Per-script template hyphens. The template hyphen is what appears in the {{affix}}/{{prefix}}/{{suffix}}/etc. template (in the wikicode). See above. They key below is a script code, after removing a hyphen and anything preceding. Hence, script codes like 'mnc-Mong' and 'xwo-Mong' will match 'Mong'. The value below is a string consisting of one or more hyphen characters. If there is more than one character, the default hyphen must come last and a non-default function must be specified for the script in display_hyphens[] so the correct display hyphen will be specified when no template hyphen is given (in {{suffix}}/{{prefix}}/etc.). Script detection is normally done when linking, but we need to do it earlier. However, under most circumstances we don't need to do script detection. Specifically, we only need to do script detection for a given language if (a) the language has multiple scripts; and (b) at least one of those scripts is listed below or in display_hyphens. ]=] local ZWNJ = u(0x200C) -- zero-width non-joiner local template_hyphens = { -- This covers all Arabic scripts. See above. ["Arab"] = "ـ" .. ZWNJ .. "-", -- tatweel + zero-width non-joiner + regular hyphen ["Aran"] = "ـ" .. ZWNJ .. "-", -- tatweel + zero-width non-joiner + regular hyphen ["Hebr"] = "־", -- Hebrew-specific hyphen termed "maqqef" ["Mong"] = "᠊", -- FIXME! What about the following right-to-left scripts? -- Adlm (Adlam) -- Armi (Imperial Aramaic) -- Avst (Avestan) -- Cprt (Cypriot) -- Khar (Kharoshthi) -- Mand (Mandaic/Mandaean) -- Mani (Manichaean) -- Mend (Mende/Mende Kikakui) -- Narb (Old North Arabian) -- Nbat (Nabataean/Nabatean) -- Nkoo (N'Ko) -- Orkh (Orkhon runes) -- Phli (Inscriptional Pahlavi) -- Phlp (Psalter Pahlavi) -- Phlv (Book Pahlavi) -- Phnx (Phoenician) -- Prti (Inscriptional Parthian) -- Rohg (Hanifi Rohingya) -- Samr (Samaritan) -- Sarb (Old South Arabian) -- Sogd (Sogdian) -- Sogo (Old Sogdian) -- Syrc (Syriac) -- Thaa (Thaana) } -- Hyphens used when looking up an affix in a lang-specific affix mapping. Defaults to regular hyphen (-). The keys -- are script codes, after removing a hyphen and anything preceding. Hence, script codes like 'mnc-Mong' and 'xwo-Mong' -- will match 'Mong'. The value should be a single character. local lookup_hyphens = { ["Hebr"] = "־", -- This covers all Arabic scripts. See above. ["Arab"] = "ـ", ["Aran"] = "ـ", } -- Default display-hyphen function. local function default_display_hyphen(script, hyph) if not hyph then return template_hyphens[script] or "-" end return hyph end local function arab_get_display_hyphen(_script, hyph) if not hyph then return "ـ" -- tatweel elseif hyph == ZWNJ then return "" else return hyph end end local function no_display_hyphen(_script, _hyph) return "" end -- Per-script function to return the correct display hyphen given the script and template hyphen. The function should -- also handle the case where the passed-in template hyphen is nil, corresponding to the situation in -- {{prefix}}/{{suffix}}/etc. where no template hyphen is specified. The key is the script code after removing a hyphen -- and anything preceding, so 'mnc-Mong', 'xwo-Mong' etc. will match 'Mong'. local display_hyphens = { -- This covers all Arabic scripts. See above. ["Arab"] = arab_get_display_hyphen, ["Aran"] = arab_get_display_hyphen, ["Bopo"] = no_display_hyphen, ["Hani"] = no_display_hyphen, ["Hans"] = no_display_hyphen, ["Hant"] = no_display_hyphen, -- The following is a mixture of several scripts. Hopefully the specs here are correct! ["Jpan"] = no_display_hyphen, ["Jurc"] = no_display_hyphen, ["Kitl"] = no_display_hyphen, ["Kits"] = no_display_hyphen, ["Laoo"] = no_display_hyphen, ["Nshu"] = no_display_hyphen, ["Shui"] = no_display_hyphen, ["Tang"] = no_display_hyphen, ["Thaa"] = no_display_hyphen, ["Thai"] = no_display_hyphen, ["Tibt"] = no_display_hyphen, } ----------------------------------------------------------------------------------------- -- Basic Utility functions -- ----------------------------------------------------------------------------------------- local function glossary_link(entry, text) text = text or entry return "[[ภาคผนวก:อภิธานศัพท์#" .. entry .. "|" .. text .. "]]" end local function track(page) if type(page) == "table" then for i, pg in ipairs(page) do page[i] = "affix/" .. pg end else page = "affix/" .. page end require("Module:debug/track")(page) end local function ine(val) return val ~= "" and val or nil end ----------------------------------------------------------------------------------------- -- Compound types -- ----------------------------------------------------------------------------------------- local function make_compound_type(typ, alttext) return { text = "คำประสม" .. glossary_link(typ, alttext), cat = "คำประสม" .. typ, } end -- Make a compound type entry with a simple rather than glossary link. -- These should be replaced with a glossary link when the entry in the glossary -- is created. local function make_non_glossary_compound_type(typ, alttext) local link = alttext and "[[" .. typ .. "|" .. alttext .. "]]" or "[[" .. typ .. "]]" return { text = "คำประสม" .. link, cat = "คำประสม" .. typ, } end local function make_raw_compound_type(typ, alttext) return { text = glossary_link(typ, alttext), cat = pluralize(typ), } end local function make_borrowing_type(typ, alttext) return { text = glossary_link(typ, alttext), borrowing_type = pluralize(typ), } end export.etymology_types = { ["adapted borrowing"] = make_borrowing_type("adapted borrowing"), ["adap"] = "adapted borrowing", ["abor"] = "adapted borrowing", ["alliterative"] = make_non_glossary_compound_type("alliterative"), ["allit"] = "alliterative", ["ตรงข้าม"] = make_non_glossary_compound_type("ตรงข้าม"), ["antonymous"] = "ตรงข้าม", ["ant"] = "ตรงข้าม", ["พหุวรีหิ"] = make_compound_type("พหุวรีหิ"), ["bahuvrihi"] = "พหุวรีหิ", ["bahu"] = "พหุวรีหิ", ["bv"] = "พหุวรีหิ", ["coordinative"] = make_compound_type("coordinative"), ["coord"] = "coordinative", ["descriptive"] = make_compound_type("descriptive"), ["desc"] = "descriptive", ["determinative"] = make_compound_type("determinative"), ["det"] = "determinative", ["ทวันทวะ"] = make_compound_type("ทวันทวะ"), ["dvandva"] = "ทวันทวะ", ["dva"] = "ทวันทวะ", ["dvigu"] = make_compound_type("dvigu"), ["dvi"] = "dvigu", ["endocentric"] = make_compound_type("endocentric"), ["endo"] = "endocentric", ["exocentric"] = make_compound_type("exocentric"), ["exo"] = "exocentric", ["izafet I"] = make_compound_type("izafet I"), ["iz1"] = "izafet I", ["izafet II"] = make_compound_type("izafet II"), ["iz2"] = "izafet II", ["izafet III"] = make_compound_type("izafet III"), ["iz3"] = "izafet III", ["กรรมธารยะ"] = make_compound_type("กรรมธารยะ"), ["karmadharaya"] = "กรรมธารยะ", ["karma"] = "กรรมธารยะ", ["kd"] = "กรรมธารยะ", ["kenning"] = make_raw_compound_type("kenning"), ["ken"] = "kenning", ["สัมผัส"] = make_non_glossary_compound_type("สัมผัส"), ["rhyming"] = "สัมผัส", ["rhy"] = "สัมผัส", ["พ้องความ"] = make_non_glossary_compound_type("พ้องความ"), ["synonymous"] = "พ้องความ", ["syn"] = "พ้องความ", ["ตัตปุรุษะ"] = make_compound_type("ตัตปุรุษะ"), ["tatpurusa"] = "ตัตปุรุษะ", ["tat"] = "ตัตปุรุษะ", ["tp"] = "ตัตปุรุษะ", } local function process_etymology_type(typ, nocap, notext, has_parts) local text_sections = {} local categories = {} local borrowing_type if typ then local typdata = export.etymology_types[typ] if type(typdata) == "string" then typdata = export.etymology_types[typdata] end if not typdata then error("Internal error: Unrecognized type '" .. typ .. "'") end local text = typdata.text if not nocap then text = ucfirst(text) end local cat = typdata.cat borrowing_type = typdata.borrowing_type local oftext = typdata.oftext or " ของ" if not notext then insert(text_sections, text) if has_parts then insert(text_sections, oftext) insert(text_sections, " ") end end if cat then insert(categories, cat) end end return text_sections, categories, borrowing_type end ----------------------------------------------------------------------------------------- -- Utility functions -- ----------------------------------------------------------------------------------------- -- Iterate an array up to the greatest integer index found. local function ipairs_with_gaps(t) local indices = m_table.numKeys(t) local max_index = #indices > 0 and math.max(unpack(indices)) or 0 local i = 0 return function() if i < max_index then i = i + 1 return i, t[i] end end end export.ipairs_with_gaps = ipairs_with_gaps --[==[ Join formatted parts together with any overall {{para|lit}} spec, overall left and/or right qualifiers and labels, and categories. On input, `data` is a structure with the following fields: * `affixdata`: The affix data passed into an entry point such as `##show_affix()`; the code in this function examines the `lang`, `lit`, `force_cat`, `nocat`, `sort_key`, `q`, `qq`, `l` and `ll` fields of this structure, which represent overall values for these properties (as opposed to per-part values, which are found inside the list of per-part objects in `parts`, which are not examined); * `parts_formatted`: List of formatted parts, each a string; * `separator_already_added`: If true, any separators have already been included in `parts_formatted`, so the values should be concatenated with nothing in between; otherwise, the separator {" +&lrm; "} will be used, i.e. a `+` sign surrounded by spaces along with a Unicode left-to-right character to ensure that the parts appear in left-to-right order even if they contain right-to-left text; * `categories`: List of category strings or category objects; category strings are formatted using the `sort_key` field in the affix data structure in `affixdata`, while category objects, which are of the form `{ {cat=``category``, sort_key=``sort_key``, sort_base=``sort_base``}`, specify their own sort key and sort base; if `affixdata.nocat` is given, no categories are added; otherwise, `affixdata.force_cat` or global variable `debug_force_cat` causes categories to be added even on userspace pages. ]==] function export.join_formatted_parts(data) local cattext local affixdata = data.affixdata local lang = affixdata.lang local force_cat = affixdata.force_cat or debug_force_cat if affixdata.nocat then cattext = "" else for i, cat in ipairs(data.categories) do if type(cat) == "table" then --data.categories[i] = require(utilities_module).format_categories(lang:getFullName() .. " " .. cat.cat, -- lang, cat.sort_key, cat.sort_base, force_cat) data.categories[i] = require(utilities_module).format_categories( require("Module:th-utilities").th_categorize(cat.cat, lang:getCategoryName()), lang, cat.sort_key, cat.sort_base, force_cat) else --data.categories[i] = require(utilities_module).format_categories(lang:getFullName() .. " " .. cat, lang, -- affixdata.sort_key, nil, force_cat) data.categories[i] = require(utilities_module).format_categories( require("Module:th-utilities").th_categorize(cat, lang:getCategoryName()), lang, affixdata.sort_key, nil, force_cat) end end cattext = concat(data.categories) end local result = concat(data.parts_formatted, not data.separator_already_added and " +&lrm; " or nil) .. (affixdata.lit and ", แปลตามตัวอักษรว่า " .. m_links.mark(affixdata.lit, "gloss") or "") local q = affixdata.q local qq = affixdata.qq local l = affixdata.l local ll = affixdata.ll if q and q[1] or qq and qq[1] or l and l[1] or ll and ll[1] then result = require(decorations_module).format_decorations { lang = lang, text = result, q = q, qq = qq, l = l, ll = ll, } end return result .. cattext end -- Remove links and call lang:stripDiacritics(term). local function strip_diacritics_no_links(lang, term) return lang:stripDiacritics(m_links.remove_links(term)) end --[=[ Convert a raw part as passed into an entry point into a part ready for linking. `lang` and `sc` are the overall language and script objects. This uses the overall language and script objects as defaults for the part and parses off any fragment from the term. We need to do the latter so that fragments don't end up in categories and so that we correctly do affix mapping even in the presence of fragments. ]=] local function canonicalize_part(part, lang, sc) if not part then return end -- Save the original (user-specified, part-specific) value of `lang`. If such a value is specified, we don't insert -- a '*fixed with' category, and we format the part using format_derived() in [[Module:etymology]] rather than -- full_link() in [[Module:links]]. part.part_lang = part.lang part.lang = part.lang or lang part.sc = part.sc or sc local term = part.term if not term then return elseif not part.fragment then part.term, part.fragment = m_links.get_fragment(term) else part.term = m_links.get_fragment(term) end end --[==[ Construct a single linked part based on the information in `part`, for use by `show_affix()` and other entry points. This should be called after `canonicalize_part()` is called on the part. This is a thin wrapper around `full_link()` in [[Module:links]] unless `part.part_lang` is specified (indicating that a part-specific language was given), in which case `format_derived()` in [[Module:etymology]] is called to display a term in a language other than the language of the overall term (specified in `data.lang`). `data` contains the entire object passed into the entry point and is used to access information for constructing the categories added by `format_derived()`. ]==] function export.link_term(part, data, include_separator) local result if part.part_lang then result = require(etymology_module).format_derived { lang = data.lang, terms = {part}, sources = {part.lang}, sort_key = data.sort_key, nocat = data.nocat, template_name = "affix", decorations_on_outside = true, borrowing_type = data.borrowing_type, force_cat = data.force_cat or debug_force_cat, } else result = m_links.full_link(part, "term") end if include_separator and part.separator then return part.separator .. result else return result end end local function canonicalize_script_code(scode) -- Convert 'mnc-Mong', 'xwo-Mong' etc. to 'Mong'. return (scode:gsub("^.*%-", "")) end ----------------------------------------------------------------------------------------- -- Affix-handling functions -- ----------------------------------------------------------------------------------------- -- Figure out the appropriate script for the given affix and language (unless the script is explicitly passed in), and -- return the values of template_hyphens[], display_hyphens[] and lookup_hyphens[] for that script, substituting -- default values as appropriate. Four values are returned: -- DETECTED_SCRIPT, TEMPLATE_HYPHEN, DISPLAY_HYPHEN, LOOKUP_HYPHEN local function detect_script_and_hyphens(text, lang, sc) local scode -- 1. If the script is explicitly passed in, use it. if sc then scode = sc:getCode() else local possible_script_codes = lang:getScriptCodes() -- YUCK! `possible_script_codes` comes from loadData() so #possible_scripts doesn't work (always returns 0). local num_possible_script_codes = m_table.length(possible_script_codes) if num_possible_script_codes == 0 then -- This shouldn't happen; if the language has no script codes, -- the list {"None"} should be returned. error("Something is majorly wrong! Language " .. lang:getCanonicalName() .. " has no script codes.") end if num_possible_script_codes == 1 then -- 2. If the language has only one possible script, use it. scode = possible_script_codes[1] else -- 3. Check if any of the possible scripts for the language have non-default values for template_hyphens[] -- or display_hyphens[]. If so, we need to do script detection on the text. If not, just use "Latn", -- which may not be technically correct but produces the right results because Latn has all default -- values for template_hyphens[] and display_hyphens[]. local may_have_nondefault_hyphen = false for _, script_code in ipairs(possible_script_codes) do script_code = canonicalize_script_code(script_code) if template_hyphens[script_code] or display_hyphens[script_code] then may_have_nondefault_hyphen = true break end end if not may_have_nondefault_hyphen then scode = "Latn" else scode = lang:findBestScript(text):getCode() end end end scode = canonicalize_script_code(scode) local template_hyphen = template_hyphens[scode] or "-" local lookup_hyphen = lookup_hyphens[scode] or "-" local display_hyphen = display_hyphens[scode] or default_display_hyphen return scode, template_hyphen, display_hyphen, lookup_hyphen end --[=[ Given a template affix `term` and an affix type `affix_type`, change the relevant template hyphen(s) in the affix to the display or lookup hyphen specified in `new_hyphen`, or add them if they are missing. `new_hyphen` can be a string, specifying a fixed hyphen, or a function of two arguments (the script code `scode` and the discovered template hyphen, or nil of no relevant template hyphen is present). `thyph_re` is a Lua pattern (which must be enclosed in parens) that matches the possible template hyphens. Note that not all template hyphens present in the affix are changed, but only the "relevant" ones (e.g. for a prefix, a relevant template hyphen is one coming at the end of the affix). ]=] local function reconstruct_term_per_hyphens(term, affix_type, scode, thyph_re, new_hyphen) local function get_hyphen(hyph) if type(new_hyphen) == "string" then return new_hyphen end return new_hyphen(scode, hyph) end if affix_type == "ไม่ใช่หน่วยคำเติม" then return term elseif affix_type == "หน่วยคำเติมคร่อม" then local before, before_hyphen, after_hyphen, after = umatch(term, "^(.*)" .. thyph_re .. " " .. thyph_re .. "(.*)$") if not before or ulen(term) <= 3 then -- Unlike with other types of affixes, don't try to add hyphens in the middle of the term to convert it to -- a circumfix. Also, if the term is just hyphen + space + hyphen, return it. return term end return before .. get_hyphen(before_hyphen) .. " " .. get_hyphen(after_hyphen) .. after elseif affix_type == "อาคม" or affix_type == "หน่วยคำเติมเชื่อม" then local before_hyphen, middle, after_hyphen = umatch(term, "^" .. thyph_re .. "(.*)" .. thyph_re .. "$") if before_hyphen and ulen(term) <= 1 then -- If the term is just a hyphen, return it. return term end return get_hyphen(before_hyphen) .. (middle or term) .. get_hyphen(after_hyphen) elseif affix_type == "อุปสรรค" then local middle, after_hyphen = umatch(term, "^(.*)" .. thyph_re .. "$") if middle and ulen(term) <= 1 then -- If the term is just a hyphen, return it. return term end return (middle or term) .. get_hyphen(after_hyphen) elseif affix_type == "ปัจจัย" then local before_hyphen, middle = umatch(term, "^" .. thyph_re .. "(.*)$") if before_hyphen and ulen(term) <= 1 then -- If the term is just a hyphen, return it. return term end return get_hyphen(before_hyphen) .. (middle or term) else error(("Internal error: Unrecognized affix type '%s'"):format(affix_type)) end end --[=[ Look up a mapping from a given affix variant to the canonical form used in categories and links. The lookup tables are language-specific according to `lang`, and may be ID-specific according to `affix_id`. The affixes as they appear in the lookup tables (both the variant and the canonical form) are in "lookup affix" format (approximately speaking, they use a regular hyphen for most scripts, but a tatweel for Arabic-script entries and a maqqef for Hebrew-script entries), but the passed-in `affix` param is in "template affix" format (which differs from the lookup affix for Arabic-script entries, because more types of hyphens are allowed in template affixes; see the comments at the top of the file). The remaining parameters to this function are used to convert from template affixes to lookup affixes; see the reconstruct_term_per_hyphens() function above. If the affix contains brackets, no lookup is done. Otherwise, a two-stage process is used, first looking up the affix directly and then stripping diacritics and looking it up again. The reason for this is documented above in the comments at the top of the file (specifically, the comments describing lookup affixes). The value of a mapping can either be a string (do the mapping regardless of affix ID) or a table indexed by affix ID (where the special value `false` indicates no affix ID). The values of entries in this table can also be strings, or tables with keys `affix` and `id` (again, use `false` to indicate no ID). This allows an affix mapping to map from one ID to another (for example, this is used in English to map the [[an-]] prefix with no ID to the [[a-]] prefix with the ID 'not'). The Given a template affix `term` and an affix type `affix_type`, change the relevant template hyphen(s) in the affix to the display or lookup hyphen specified in `new_hyphen`, or add them if they are missing. `new_hyphen` can be a string, specifying a fixed hyphen, or a function of two arguments (the script code `scode` and the discovered template hyphen, or nil of no relevant template hyphen is present). `thyph_re` is a Lua pattern (which must be enclosed in parens) that matches the possible template hyphens. Note that not all template hyphens present in the affix are changed, but only the "relevant" ones (e.g. for a prefix, a relevant template hyphen is one coming at the end of the affix). ]=] local function lookup_affix_mapping(affix, affix_type, lang, scode, thyph_re, lookup_hyph, affix_id) local function do_lookup(afx) -- Ensure that the affix uses lookup hyphens regardless of whether it used a different type of hyphens before -- or no hyphens. local lookup_affix = reconstruct_term_per_hyphens(afx, affix_type, scode, thyph_re, lookup_hyph) local function do_lookup_for_langcode(langcode) if export.langs_with_lang_specific_data[langcode] then local langdata = mw.loadData(export.affix_lang_data_module_prefix .. langcode) if langdata.affix_mappings then local mapping = langdata.affix_mappings[lookup_affix] if mapping then if type(mapping) == "table" then mapping = mapping[affix_id] or mapping.default or mapping[affix_id or false] if mapping then return mapping end else return mapping end end end end end -- If `lang` is an etymology-only language, look for a mapping both for it and its full parent. local langcode = lang:getCode() local mapping = do_lookup_for_langcode(langcode) if mapping then return mapping end local full_langcode = lang:getFullCode() if full_langcode ~= langcode then mapping = do_lookup_for_langcode(full_langcode) if mapping then return mapping end end return nil end if affix:find("%[%[") then return nil end return do_lookup(affix) or do_lookup(lang:stripDiacritics(affix)) or nil end --[==[ For a given template term in a given language (see the definition of "template affix" near the top of the file), possibly in an explicitly specified script `sc` (but usually nil), return the term's affix type ({"prefix"}, {"interfix"}, {"suffix"}, {"circumfix"} or {"non-affix"}) along with the corresponding link and display affixes (see definitions near the top of the file); also the corresponding lookup affix (if `return_lookup_affix` is specified). The term passed in should already have any fragment (after the # sign) parsed off of it. Four values are returned: `affix_type`, `link_term`, `display_term` and `lookup_term`. The affix type can be passed in instead of autodetected; in this case, the template term need not have any attached hyphens, and the appropriate hyphens will be added in the appropriate places. If `do_affix_mapping` is specified, look up the affix in the lang-specific affix mappings, as described in the comment at the top of the file; otherwise, the link and display terms will always be the same. (They will be the same in any case if the template term has a bracketed link in it or is not an affix.) If `return_lookup_affix` is given, the fourth return value contains the term with appropriate lookup hyphens in the appropriate places; otherwise, it is the same as the display term. (This functionality is used in [[Module:category tree/affixes and compounds]] to convert link affixes into lookup affixes so that they can be looked up in the affix mapping tables.) Exported because used by [[Module:headword utilities]] to determine the affix type of a given pagename. ]==] function export.parse_term_for_affixes(term, lang, sc, affix_type, do_affix_mapping, return_lookup_affix, affix_id) if not term then return "ไม่ใช่หน่วยคำเติม", nil, nil, nil end if term == "^" then -- Indicates a null term to emulate the behavior of {{suffix|foo||bar}}. term = "" return "ไม่ใช่หน่วยคำเติม", term, term, term end if term:find("^%^") then -- HACK! ^ at the beginning of Korean languages has a special meaning, triggering capitalization of the -- transliteration. Don't interpret it as "force non-affix" for those languages. local langcode = lang:getCode() if langcode ~= "ko" and langcode ~= "okm" and langcode ~= "jje" then -- Formerly we allowed ^ to force non-affix type; this is now handled using an inline modifier -- <naf>, <root>, etc. Throw an error for the moment when the old way is encountered. error("Use of ^ to force non-affix status is no longer supported; use an inline modifier <naf> or <root> " .. "after the component") end end -- Remove an asterisk if the morpheme is reconstructed and add it back at the end. local reconstructed = "" if term:find("^%*") then reconstructed = "*" term = term:gsub("^%*", "") end local scode, thyph, dhyph, lhyph = detect_script_and_hyphens(term, lang, sc) thyph = "([" .. thyph .. "])" if not affix_type then if ufind(term, thyph .. " " .. thyph) then affix_type = "หน่วยคำเติมคร่อม" else local has_beginning_hyphen = ufind(term, "^" .. thyph) local has_ending_hyphen = ufind(term, thyph .. "$") if has_beginning_hyphen and has_ending_hyphen then affix_type = "หน่วยคำเติมเชื่อม" elseif has_ending_hyphen then affix_type = "อุปสรรค" elseif has_beginning_hyphen then affix_type = "ปัจจัย" else affix_type = "ไม่ใช่หน่วยคำเติม" end end end local link_term, display_term, lookup_term if affix_type == "ไม่ใช่หน่วยคำเติม" then link_term = term display_term = term lookup_term = term else display_term = reconstruct_term_per_hyphens(term, affix_type, scode, thyph, dhyph) if do_affix_mapping then link_term = lookup_affix_mapping(term, affix_type, lang, scode, thyph, lhyph, affix_id) -- The return value of lookup_affix_mapping() may be an affix mapping with lookup hyphens if a mapping -- was found, otherwise nil if a mapping was not found. We need to convert to display hyphens in -- either case, but in the latter case we can reuse the display term, which has already been converted. if link_term then link_term = reconstruct_term_per_hyphens(link_term, affix_type, scode, thyph, dhyph) else link_term = display_term end else link_term = display_term end if return_lookup_affix then lookup_term = reconstruct_term_per_hyphens(term, affix_type, scode, thyph, lhyph) else lookup_term = display_term end end link_term = reconstructed .. link_term display_term = reconstructed .. display_term lookup_term = reconstructed .. lookup_term return affix_type, link_term, display_term, lookup_term end --[==[ Add a hyphen to a term in the appropriate place, based on the specified affix type, stripping off any existing hyphens in that place. For example, if `affix_type` == {"prefix"}, we'll add a hyphen onto the end if it's not already there (or is of the wrong type). Three values are returned: the link term, display term and lookup term. This function is a thin wrapper around `parse_term_for_affixes`; see the comments above that function for more information. Note that this function is exposed externally because it is called by [[Module:category tree/affixes and compounds]]; see the comment in `parse_term_for_affixes` for more information. ]==] function export.make_affix(term, lang, sc, affix_type, do_affix_mapping, return_lookup_affix, affix_id) if not (affix_type == "อุปสรรค" or affix_type == "ปัจจัย" or affix_type == "หน่วยคำเติมคร่อม" or affix_type == "อาคม" or affix_type == "หน่วยคำเติมเชื่อม" or affix_type == "ไม่ใช่หน่วยคำเติม") then error("Internal error: Invalid affix type " .. (affix_type or "(nil)")) end local _, link_term, display_term, lookup_term = export.parse_term_for_affixes(term, lang, sc, affix_type, do_affix_mapping, return_lookup_affix, affix_id) return link_term, display_term, lookup_term end ----------------------------------------------------------------------------------------- -- Categorization logic -- ----------------------------------------------------------------------------------------- local function insert_category(categories, cat, sort_key, sort_base) if sort_key or sort_base then insert(categories, {cat = cat, sort_key = sort_key, sort_base = sort_base}) else insert(categories, cat) end end --[=[ Insert a `*fixed with` category such as [[:Category:Welsh terms prefixed with gwe-]] into `categories`. The arguments are: * `categories`: List of category strings or objects to insert into. * `pos`: Part of speech in the category (which is often "terms" but may be something more specific). * `affix_type`: Affix type in the category (e.g. "prefix"); automatically has "ed" appended. * `link_term`: The term that appears in the category, e.g. "gwe-". If this is empty or missing, no category is inserted. * `part`: The part structure being processed. The `lang`, `part_lang`, `nocat` and `id` fields are fetched. * `sort_key`: The sort key for the specific category. If this or `sort_base` are non-nil, a category object is created holding the specified sort key and/or base, and inserted; otherwise, a cateory string is inserted. * `sort_base`: The sort base for the specific category. See `sort_key` just above. ]=] local function insert_affix_category(categories, pos, affix_type, link_term, part, sort_key, sort_base) -- Don't add a category if the link term is empty or is in a different language, or if <nocat:1> was specified for -- the part. if ine(link_term) and not part.part_lang and not part.nocat then --local cat = pos .. " " .. affix_type .. "ed with " .. strip_diacritics_no_links(part.lang, link_term) .. -- (part.id and " (" .. part.id .. ")" or "") local cat = pos .. "ที่เติม" .. affix_type .. " " .. strip_diacritics_no_links(part.lang, link_term) .. (part.id and " (" .. part.id .. ")" or "") insert_category(categories, cat, sort_key, sort_base) end end --[=[ Generate the categories for a single part and insert into `categories`. The arguments are: * `categories` is the list to insert the categories into. * `data` is the overall affix data structure. * `part` is the per-part data structure. * `affix_type` is the type of affix (e.g. "prefix"), as appears in the `*fixed by` category. * `link_term` is the term that (prefix, suffix, etc.) mentioned in the `*fixed by` category. * `sort_key` and `sort_base` are the per-part sort keys and bases for categorization, or nil to use the overall sort key. ]=] local function generate_one_part_categories(categories, data, part, affix_type, link_term, sort_key, sort_base) if data.nocat or part.nocat then return end if part.pos and part.pos:find("patronym") then insert_category(categories, "patronymics", sort_key, sort_base) end if data.pos ~= "terms" and part.pos and part.pos:find("diminutive") then insert_category(categories, "diminutive " .. data.pos, sort_key, sort_base) end insert_affix_category(categories, data.pos, affix_type, link_term, part, sort_key, sort_base) end --[=[ Core categorization logic for affixes. This is shared between show_affix(), show_compound_like() and get_affix_categories_only(). Returns the categories array and other metadata needed for formatting. ]=] local function generate_affix_categories(data) data.pos = data.pos or default_pos data.pos = pluralize_pos(data.pos) local text_sections, categories, borrowing_type = process_etymology_type(data.type, data.surface_analysis or data.nocap, data.notext, #data.parts > 0) data.borrowing_type = borrowing_type -- Process each part local whole_words = 0 local is_affix_or_compound = false -- Canonicalize and generate links for all the parts first; then do categorization in a separate step, because when -- processing the first part for categorization, we may access the second part and need it already canonicalized. for i, part in ipairs_with_gaps(data.parts) do part = part or {} data.parts[i] = part canonicalize_part(part, data.lang, data.sc) -- Determine affix type and get link and display terms (see text at top of file). Store them in the part -- (in fields that won't clash with fields used by full_link() in [[Module:links]] or link_term()), so they -- can be used in the loop below when categorizing. part.affix_type, part.affix_link_term, part.affix_display_term = export.parse_term_for_affixes(part.term, part.lang, part.sc, part.type, not part.alt, nil, part.id) -- If link_term is an empty string, either a bare ^ was specified or an empty term was used along with inline -- modifiers. The intention in either case is not to link the term. part.term = ine(part.affix_link_term) -- If part.alt would be the same as part.term, make it nil, so that it isn't erroneously tracked as being -- redundant alt text. part.alt = part.alt or (part.affix_display_term ~= part.affix_link_term and part.affix_display_term) or nil end if not data.noaffixcat then -- Now do categorization. for i, part in ipairs_with_gaps(data.parts) do local affix_type = part.affix_type if affix_type ~= "ไม่ใช่หน่วยคำเติม" then is_affix_or_compound = true -- Make a sort key. For the first part, use the second part as the sort key; the intention is that if the -- term has a prefix, sorting by the prefix won't be very useful so we sort by what follows, which is -- presumably the root. local part_sort_base = nil local part_sort = part.sort or data.sort_key if i == 1 and data.parts[2] and data.parts[2].term then local part2 = data.parts[2] -- If the second-part link term is empty, the user requested an unlinked term; avoid a wikitext error -- by using the alt value if available. part_sort_base = ine(part2.affix_link_term) or ine(part2.alt) if part_sort_base then part_sort_base = strip_diacritics_no_links(part2.lang, part_sort_base) end end generate_one_part_categories(categories, data, part, affix_type, part.affix_link_term, part_sort, part_sort_base) else whole_words = whole_words + 1 if whole_words == 2 then is_affix_or_compound = true --insert(categories, "compound " .. data.pos) if data.pos == "ศัพท์" then --th insert(categories, "คำประสม") else insert(categories, data.pos .. "ประสม") end end end end -- Make sure there was either an affix or a compound (two or more non-affix terms). if not is_affix_or_compound and not data.allow_no_affixes_or_compounds then error("The parameters did not include any affixes, and the term is not a compound. Please provide at least one affix.") end end return text_sections, categories, borrowing_type end ----------------------------------------------------------------------------------------- -- Main entry points -- ----------------------------------------------------------------------------------------- --[==[ Implementation of {{tl|affix}} and {{tl|surface analysis}}. `data` contains all the information describing the affixes to be displayed, and contains the following: * `.lang` ('''required'''): Overall language object. Different from term-specific language objects (see `.parts` below). * `.sc`: Overall script object (usually omitted). Different from term-specific script objects. * `.parts` ('''required'''): List of objects describing the affixes to show. The general format of each object is as would be passed to `full_link()`, except that the `.lang` field should be missing unless the term is of a language different from the overall `.lang` value (in such a case, the language name is shown along with the term and an additional "derived from" category is added). '''WARNING''': The data in `.parts` will be destructively modified. * `.pos`: Overall part of speech (used in categories, defaults to {"terms"}). Different from term-specific part of speech. * `.sort_key`: Overall sort key. Normally omitted except e.g. in Japanese. * `.type`: Type of compound, if the parts in `.parts` describe a compound. Strictly optional, and if supplied, the compound type is displayed before the parts (normally capitalized, unless `.nocap` is given). * `.nocap`: Don't capitalize the first letter of text displayed before the parts (relevant only if `.type` or `.surface_analysis` is given). * `.notext`: Don't display any text before the parts (relevant only if `.type` or `.surface_analysis` is given). * `.nocat`: Disable all categorization. * `.noaffixcat`: Disable affix (and compound) categorization. Relevant for e.g. blends, which may otherwise be incorrectly categorized as compound terms. * `.lit`: Overall literal definition. Different from term-specific literal definitions. * `.force_cat`: Always display categories, even on userspace pages. * `.surface_analysis`: Implement {{surface analysis}}; adds `By surface analysis, ` before the parts. '''WARNING''': This destructively modifies both `data` and the individual structures within `.parts`. ]==] function export.show_affix(data) local text_sections, categories, _ = generate_affix_categories(data) -- Process each part for display local parts_formatted = {} for i, part in ipairs_with_gaps(data.parts) do -- Make a link for the part insert(parts_formatted, export.link_term(part, data, "include_separator")) end if data.surface_analysis then --local text = "by " .. glossary_link("surface analysis") .. ", " local text = "พิจารณาคร่าว ๆ เทียบเท่า " if not data.nocap then text = ucfirst(text) end insert(text_sections, 1, text) end insert(text_sections, export.join_formatted_parts { affixdata = data, parts_formatted = parts_formatted, categories = categories, separator_already_added = true }) return concat(text_sections) end --[==[ Get only the categories that would be generated by show_affix(), without any text output or formatting. This is used by Module:etymon to get affix categorization. Returns an array of category objects, where each entry is either a string (simple category name) or a table with keys `cat`, `sort_key`, and `sort_base` for more complex categorization. `data` should have the same structure as passed to show_affix(): * `.lang` (required): Overall language object * `.parts` (required): Array of affix part objects with `.term`, `.lang`, `.id`, etc. * `.pos`: Part of speech (defaults to "terms") * `.sort_key`: Overall sort key for categories '''WARNING''': This destructively modifies both `data` and the individual structures within `.parts`. ]==] function export.get_affix_categories_only(data) local _, categories, _ = generate_affix_categories(data) return categories end function export.show_surface_analysis(data) data.surface_analysis = true data.allow_no_affixes_or_compounds = true return export.show_affix(data) end --[==[ Implementation of {{tl|compound}}. '''WARNING''': This destructively modifies both `data` and the individual structures within `.parts`. ]==] function export.show_compound(data) data.pos = data.pos or default_pos data.pos = pluralize_pos(data.pos) local text_sections, categories, borrowing_type = process_etymology_type(data.type, data.nocap, data.notext, #data.parts > 0) data.borrowing_type = borrowing_type local parts_formatted = {} --insert(categories, "compound " .. data.pos) if data.pos == "ศัพท์" then --th insert(categories, "คำประสม") else insert(categories, data.pos .. "ประสม") end -- Make links out of all the parts local whole_words = 0 for i, part in ipairs(data.parts) do canonicalize_part(part, data.lang, data.sc) -- Determine affix type and get link and display terms (see text at top of file). local affix_type, link_term, display_term = export.parse_term_for_affixes(part.term, part.lang, part.sc, part.type, not part.alt, nil, part.id) -- If the term is an interfix or the type was explicitly given, recognize it as such (which means e.g. that we -- will display the term without hyphens for East Asian languages). Otherwise, ignore the fact that it looks -- like an affix and display as specified in the template (but pay attention to the detected affix type for -- certain tracking purposes). if affix_type == "หน่วยคำเติมเชื่อม" or (part.type and part.type ~= "ไม่ใช่หน่วยคำเติม") then -- Add a `*fixed with` category. However, if link_term is an empty string, either a bare ^ was specified or -- an empty term was used along with inline modifiers. The intention in either case is not to link the term. -- Don't add a category in this case, or if the term is in a different language (or if overall nocat=1 was -- given, or part-specific <nocat:1>). if not data.nocat then -- FIXME, should we be specifying a sort base here? insert_affix_category(categories, data.pos, affix_type, link_term, part, part.sort or data.sort_key) end -- If part.alt would be the same as part.term, make it nil, so that it isn't erroneously tracked as being -- redundant alt text. part.term = link_term ~= "" and link_term or nil part.alt = part.alt or (display_term ~= link_term and display_term) or nil else if affix_type ~= "ไม่ใช่หน่วยคำเติม" then local langcode = data.lang:getCode() -- If `data.lang` is an etymology-only language, track both using its code and its full parent's code. track { affix_type, affix_type .. "/lang/" .. langcode } local full_langcode = data.lang:getFullCode() if langcode ~= full_langcode then track(affix_type .. "/lang/" .. full_langcode) end else whole_words = whole_words + 1 end end insert(parts_formatted, export.link_term(part, data, "include_separator")) end if whole_words == 1 then track("one whole word") elseif whole_words == 0 then track("looks like confix") end insert(text_sections, export.join_formatted_parts { affixdata = data, parts_formatted = parts_formatted, categories = categories, separator_already_added = true }) return concat(text_sections) end --[==[ Implementation of {{tl|blend}}, {{tl|univerbation}} and similar "compound-like" templates. '''WARNING''': This destructively modifies both `data` and the individual structures within `.parts`. ]==] function export.show_compound_like(data) data.allow_no_affixes_or_compounds = true local text_sections, categories, _ = generate_affix_categories(data) if data.cat then insert(categories, data.cat) end -- Process each part for display local parts_formatted = {} for i, part in ipairs_with_gaps(data.parts) do -- Make a link for the part insert(parts_formatted, export.link_term(part, data, "include_separator")) end if #data.parts > 0 and data.oftext then insert(text_sections, 1, " " .. data.oftext .. " ") end if data.text then insert(text_sections, 1, data.text) end insert(text_sections, export.join_formatted_parts { affixdata = data, parts_formatted = parts_formatted, categories = categories, separator_already_added = true }) return concat(text_sections) end --[==[ Make `part` (a structure holding information on an affix part) into an affix of type `affix_type`, and apply any relevant affix mappings. For example, if the desired affix type is "suffix", this will (in general) add a hyphen onto the beginning of the term, alt, tr and ts components of the part if not already present. The hyphen that's added is the "display hyphen" (see above) and may be script-specific. (In the case of East Asian scripts, the display hyphen is an empty string whereas the template hyphen is the regular hyphen, meaning that any regular hyphen at the beginning of the part will be effectively removed.) `lang` and `sc` hold overall language and script objects. Note that this also applies any language-specific affix mappings, so that e.g. if the language is Finnish and the user specified [[-käs]] in the affix and didn't specify an `.alt` value, `part.term` will contain [[-kas]] and `part.alt` will contain [[-käs]]. This function is used by the "legacy" templates ({{tl|prefix}}, {{tl|suffix}}, {{tl|confix}}, etc.) where the nature of the affix is specified by the template itself rather than auto-determined from the affix, as is the case with {{tl|affix}}. '''WARNING''': This destructively modifies `part`. ]==] local function make_part_into_affix(part, lang, sc, affix_type) canonicalize_part(part, lang, sc) local link_term, display_term = export.make_affix(part.term, part.lang, part.sc, affix_type, not part.alt, nil, part.id) part.term = link_term -- When we don't specify `do_affix_mapping` to make_affix(), link and display terms (first and second retvals of -- make_affix()) are the same. -- If part.alt would be the same as part.term, make it nil, so that it isn't erroneously tracked as being -- redundant alt text. part.alt = part.alt and export.make_affix(part.alt, part.lang, part.sc, affix_type) or (display_term ~= link_term and display_term) or nil local Latn = require(scripts_module).getByCode("Latn") part.tr = export.make_affix(part.tr, part.lang, Latn, affix_type) part.ts = export.make_affix(part.ts, part.lang, Latn, affix_type) end local function track_wrong_affix_type(template, part, expected_affix_type) if part and not part.type then local affix_type = export.parse_term_for_affixes(part.term, part.lang, part.sc) if affix_type ~= expected_affix_type then local part_name = expected_affix_type or "base" local langcode = part.lang:getCode() local full_langcode = part.lang:getFullCode() require("Module:debug/track") { template, template .. "/" .. part_name, template .. "/" .. part_name .. "/" .. (affix_type or "none"), template .. "/" .. part_name .. "/" .. (affix_type or "none") .. "/lang/" .. langcode } -- If `part.lang` is an etymology-only language, track both using its code and its full parent's code. if full_langcode ~= langcode then require("Module:debug/track")( template .. "/" .. part_name .. "/" .. (affix_type or "none") .. "/lang/" .. full_langcode ) end end end end --[==[ Implementation of {{tl|confix}}. '''WARNING''': This destructively modifies both `data` and `.prefix`, `.base` and `.suffix`. ]==] function export.show_confix(data) data.pos = data.pos or default_pos data.pos = pluralize_pos(data.pos) canonicalize_part(data.base, data.lang, data.sc) -- Hyphenate the affixes and apply any affix mappings. make_part_into_affix(data.prefix, data.lang, data.sc, "อุปสรรค") make_part_into_affix(data.suffix, data.lang, data.sc, "ปัจจัย") track_wrong_affix_type("confix", data.prefix, "อุปสรรค") track_wrong_affix_type("confix", data.base, nil) track_wrong_affix_type("confix", data.suffix, "ปัจจัย") -- Make links out of all the parts. local parts_formatted = {} local prefix_sort_base if data.base and data.base.term then prefix_sort_base = strip_diacritics_no_links(data.base.lang, data.base.term) elseif data.suffix.term then prefix_sort_base = strip_diacritics_no_links(data.suffix.lang, data.suffix.term) end -- Insert the categories and parts. local categories = {} insert(parts_formatted, export.link_term(data.prefix, data)) insert_affix_category(categories, data.pos, "อุปสรรค", data.prefix.term, data.prefix, data.sort_key, prefix_sort_base) if data.base then insert(parts_formatted, export.link_term(data.base, data)) end insert(parts_formatted, export.link_term(data.suffix, data)) -- FIXME, should we be specifying a sort base here? insert_affix_category(categories, data.pos, "ปัจจัย", data.suffix.term, data.suffix) return export.join_formatted_parts { affixdata = data, parts_formatted = parts_formatted, categories = categories } end --[==[ Implementation of {{tl|infix}}. '''WARNING''': This destructively modifies both `data` and `.base` and `.infix`. ]==] function export.show_infix(data) data.pos = data.pos or default_pos data.pos = pluralize_pos(data.pos) canonicalize_part(data.base, data.lang, data.sc) -- Hyphenate the affixes and apply any affix mappings. make_part_into_affix(data.infix, data.lang, data.sc, "อาคม") track_wrong_affix_type("อาคม", data.base, nil) track_wrong_affix_type("อาคม", data.infix, "อาคม") -- Make links out of all the parts. local parts_formatted = {} local categories = {} insert(parts_formatted, export.link_term(data.base, data)) insert(parts_formatted, export.link_term(data.infix, data)) -- Insert the categories. -- FIXME, should we be specifying a sort base here? insert_affix_category(categories, data.pos, "อาคม", data.infix.term, data.infix) return export.join_formatted_parts { affixdata = data, parts_formatted = parts_formatted, categories = categories } end --[==[ Implementation of {{tl|prefix}}. '''WARNING''': This destructively modifies both `data` and the structures within `.prefixes`, as well as `.base`. ]==] function export.show_prefix(data) data.pos = data.pos or default_pos data.pos = pluralize_pos(data.pos) canonicalize_part(data.base, data.lang, data.sc) -- Hyphenate the affixes and apply any affix mappings. for i, prefix in ipairs(data.prefixes) do make_part_into_affix(prefix, data.lang, data.sc, "อุปสรรค") end for i, prefix in ipairs(data.prefixes) do track_wrong_affix_type("อุปสรรค", prefix, "อุปสรรค") end track_wrong_affix_type("อุปสรรค", data.base, nil) -- Make links out of all the parts. local parts_formatted = {} local first_sort_base = nil local categories = {} if data.prefixes[2] then first_sort_base = ine(data.prefixes[2].term) or ine(data.prefixes[2].alt) if first_sort_base then first_sort_base = strip_diacritics_no_links(data.prefixes[2].lang, first_sort_base) end elseif data.base then first_sort_base = ine(data.base.term) or ine(data.base.alt) if first_sort_base then first_sort_base = strip_diacritics_no_links(data.base.lang, first_sort_base) end end for i, prefix in ipairs(data.prefixes) do insert(parts_formatted, export.link_term(prefix, data)) generate_one_part_categories(categories, data, prefix, "อุปสรรค", prefix.term, data.sort_key, i == 1 and first_sort_base or nil) end if data.base then insert(parts_formatted, export.link_term(data.base, data)) else insert(parts_formatted, "") end return export.join_formatted_parts { affixdata = data, parts_formatted = parts_formatted, categories = categories } end --[==[ Implementation of {{tl|suffix}}. '''WARNING''': This destructively modifies both `data` and the structures within `.suffixes`, as well as `.base`. ]==] function export.show_suffix(data) local categories = {} data.pos = data.pos or default_pos data.pos = pluralize_pos(data.pos) canonicalize_part(data.base, data.lang, data.sc) -- Hyphenate the affixes and apply any affix mappings. for i, suffix in ipairs(data.suffixes) do make_part_into_affix(suffix, data.lang, data.sc, "ปัจจัย") end track_wrong_affix_type("ปัจจัย", data.base, nil) for i, suffix in ipairs(data.suffixes) do track_wrong_affix_type("ปัจจัย", suffix, "ปัจจัย") end -- Make links out of all the parts. local parts_formatted = {} if data.base then insert(parts_formatted, export.link_term(data.base, data)) else insert(parts_formatted, "") end for i, suffix in ipairs(data.suffixes) do insert(parts_formatted, export.link_term(suffix, data)) end -- Insert the categories. for i, suffix in ipairs(data.suffixes) do -- FIXME, should we be specifying a sort base here? generate_one_part_categories(categories, data, suffix, "ปัจจัย", suffix.term) end return export.join_formatted_parts { affixdata = data, parts_formatted = parts_formatted, categories = categories } end return export ozq9mni0o5jj7kuwxgghyqf2n4sksbb มอดูล:scripts/canonical names 828 137426 5775261 5719255 2026-10-14T09:53:50Z Octahedron80 267 5775261 Scribunto text/plain return { ["Ancient North Arabian"] = "Narb", ["Ancient South Arabian"] = "Sarb", ["Blissymbolic"] = "Blis", ["Canadian syllabic"] = "Cans", ["Caucasian Albanian"] = "Aghb", ["Chisoi"] = "Chis", ["Clear Script"] = "xwo-Mong", ["Cypro-Minoan"] = "Cpmn", ["Demotic"] = "Egyd", ["Deseret"] = "Dsrt", ["Dongba"] = "Nkdb", ["Duployan"] = "Dupl", ["Elymaic"] = "Elym", ["Garay"] = "Gara", ["Geba"] = "Nkgb", ["Hatran"] = "Hatr", ["Hieratic"] = "Egyh", ["Image-rendered"] = "Image", ["Inscriptional Parthian"] = "Prti", ["Khema"] = "Gukh", ["Khojki"] = "Khoj", ["Khutsuri"] = "Geok", ["Khwarezmian"] = "Chrs", ["Kirat Rai"] = "Krai", ["Kpelle"] = "Kpel", ["Kulitan"] = "Kulit", ["Leke"] = "Leke", ["Loma"] = "Loma", ["Manichaean"] = "Mani", ["Marchen"] = "Marc", ["Medefaidrin"] = "Medf", ["Mende"] = "Mend", ["Meroitic cursive"] = "Merc", ["Meroitic hieroglyphic"] = "Mero", ["Moon"] = "Moon", ["Multani"] = "Mult", ["Mundari Bani"] = "Nagm", ["N'Ko"] = "Nkoo", ["Nabataean"] = "Nbat", ["Newa"] = "Newa", ["Nyiakeng Puachue Hmong"] = "Hmnp", ["Nüshu"] = "Nshu", ["Ogham"] = "Ogam", ["Ol Onal"] = "Onao", ["Old Permic"] = "Perm", ["Old Turkic"] = "Orkh", ["Osage"] = "Osge", ["Pahawh Hmong"] = "Hmng", ["Palmyrene"] = "Palm", ["Pau Cin Hau"] = "Pauc", ["Pazend"] = "pal-Avst", ["Phags-pa"] = "Phag", ["Pollard"] = "Plrd", ["Proto-Cuneiform"] = "Pcun", ["Proto-Elamite"] = "Pelm", ["Proto-Sinaitic"] = "Psin", ["Ranjana"] = "Ranj", ["Rejang"] = "Rjng", ["Rongorongo"] = "Roro", ["Rumi numerals"] = "Rumin", ["Samaritan"] = "Samr", ["Saurashtra"] = "Saur", ["Shavian"] = "Shaw", ["Sidetic"] = "Sidt", ["SignWriting"] = "Sgnw", ["Sorang Sompeng"] = "Sora", ["Soyombo"] = "Soyo", ["Sui"] = "Shui", ["Sunuwar"] = "Sunu", ["Tangsa"] = "Tnsa", ["Tigalari"] = "Tutg", ["Todhri"] = "Todr", ["Tolong Siki"] = "Tols", ["Toto"] = "Toto", ["Visible Speech"] = "Visp", ["Vithkuqi"] = "Vith", ["Wancho"] = "Wcho", ["Woleai"] = "Wole", ["Yezidi"] = "Yezi", ["Zanabazar Square"] = "Zanb", ["Znamenny musical notation"] = "Zname", ["mathematical notation"] = "Zmth", ["กรีก"] = "Grek", ["กลาโกลิติก"] = "Glag", ["กวิ"] = "Kawi", ["กอท"] = "Goth", ["กะยา"] = "Kali", ["กันนฑะ"] = "Knda", ["ขอมไทย"] = "Khomt", ["ขุทาพาที"] = "Sind", ["ขโรษฐี"] = "Khar", ["ครันถะ"] = "Gran", ["คอปติก"] = "Copt", ["คาตากานะ"] = "Kana", ["คานะ"] = "Hrkt", ["คาเรีย"] = "Cari", ["คีตันเล็ก"] = "Kits", ["คีตันใหญ่"] = "Kitl", ["คุชราต"] = "Gujr", ["คุญชลาโคณฑี"] = "Gong", ["คุรมุขี"] = "Guru", ["คูนิฟอร์ม"] = "Xsux", ["จอร์เจีย"] = "Geor", ["จักมา"] = "Cakm", ["จาม"] = "Cham", ["จีน"] = "Hani", ["จีนตัวย่อ"] = "Hans", ["จีนตัวเต็ม"] = "Hant", ["จูร์เชน"] = "Jurc", ["จู้อิน"] = "Bopo", ["ชวา"] = "Java", ["ซอกเดีย"] = "Sogd", ["ซอกเดียเก่า"] = "Sogo", ["ซิเบ"] = "sjo-Mong", ["ซีริลลิก"] = "Cyrl", ["ซีริลลิกเก่า"] = "Cyrs", ["ซีรีแอก"] = "Syrc", ["ซุนดา"] = "Sund", ["ญี่ปุ่น"] = "Jpan", ["ฏากรี"] = "Takr", ["ดิเวส อกุรุ"] = "Diak", ["ตักบันวา"] = "Tagb", ["ตังกุต"] = "Tang", ["ตัมยิก"] = "sit-tam-Tibt", ["ติรหุตา"] = "Tirh", ["ทมิฬ"] = "Taml", ["ทานะ"] = "Thaa", ["ทิฟินาค"] = "Tfng", ["ทิเบต"] = "Tibt", ["นันทินาครี"] = "Nand", ["บัสซา"] = "Bass", ["บาตัก"] = "Batk", ["บามุม"] = "Bamu", ["บาหลี"] = "Bali", ["บูฮิด"] = "Buhd", ["ปาห์ลาวีคัมภีร์"] = "Phlv", ["ปาห์ลาวีจารึก"] = "Phli", ["ปาห์ลาวีเพลงสดุดี"] = "Phlp", ["พม่า"] = "Mymr", ["พราหมี"] = "Brah", ["ฟรักทูร์"] = "Latf", ["ฟินิเชีย"] = "Phnx", ["มณีปุระ"] = "Mtei", ["มรู"] = "Mroo", ["มลยาฬัม"] = "Mlym", ["มสรามโคณฑี"] = "Gonm", ["มหาชนี"] = "Mahj", ["มองโกเลีย"] = "Mong", ["มันดาอิก"] = "Mand", ["มากัซซาร์"] = "Maka", ["มายา"] = "Maya", ["ยังไม่กำหนด"] = "Zyyy", ["ยังไม่มีรหัส"] = "Zzzz", ["ยูการิต"] = "Ugar", ["รหัสมอร์ส"] = "Morse", ["รูน"] = "Runr", ["ลนตารา"] = "Bugi", ["ละติน"] = "Latn", ["ลายตัย"] = "Tayo", ["ลาว"] = "Laoo", ["ลิมบู"] = "Limb", ["ลิเชีย"] = "Lyci", ["ลิเดีย"] = "Lydi", ["ลิเนียร์บี"] = "Linb", ["ลิเนียร์เอ"] = "Lina", ["ลีสู่"] = "Lisu", ["วรังจิติ"] = "Wara", ["ศารทา"] = "Shrd", ["ศาหมุขี"] = "pa-Arab", ["สัญกรณ์ดนตรี"] = "Music", ["สัญญาณธง"] = "Semap", ["สัญลักษณ์"] = "Zsym", ["สัทอักษรสากล"] = "Ipach", ["สิงหล"] = "Sinh", ["สิทธัม"] = "Sidd", ["สินธุ"] = "Inds", ["สิเลฏินาครี"] = "Sylo", ["อัดลัม"] = "Adlm", ["อัสสัม"] = "as-Beng", ["อาฟากา"] = "Afak", ["อาร์มีเนีย"] = "Armn", ["อาหม"] = "Ahom", ["อาหรับ"] = "Arab", ["อิตาลิกเก่า"] = "Ital", ["อี๋"] = "Yiii", ["อุยกูร์เก่า"] = "Ougr", ["อุสมาน"] = "Osma", ["อเวสตะ"] = "Avst", ["อ็อลจิกิ"] = "Olck", ["ฮังการีเก่า"] = "Hung", ["ฮันกึล"] = "Hang", ["ฮานิฟ"] = "Rohg", ["ฮานูโนโอ"] = "Hano", ["ฮิรางานะ"] = "Hira", ["ฮีบรู"] = "Hebr", ["เกาหลี"] = "Kore", ["เขมร"] = "Khmr", ["เชโรกี"] = "Cher", ["เตลูกู"] = "Telu", ["เทงกวาร์"] = "Teng", ["เทวนาครี"] = "Deva", ["เบงกอล"] = "Beng", ["เบรลล์"] = "Brai", ["เปอร์เซียเก่า"] = "Xpeo", ["เลปชา"] = "Lepc", ["เอธิโอเปีย"] = "Ethi", ["เอลบาซาน"] = "Elba", ["แกลิก"] = "Latg", ["แมนจู"] = "mnc-Mong", ["แอราเมอิกจักรวรรดิ"] = "Armi", ["โฑครี"] = "Dogr", ["โมฑี"] = "Modi", ["โอริยา"] = "Orya", ["ไกถี"] = "Kthi", ["ไซปรัส"] = "Cprt", ["ไทธรรม"] = "Lana", ["ไทย"] = "Thai", ["ไทลื้อใหม่"] = "Talu", ["ไทเวียด"] = "Tavt", ["ไทใต้คง"] = "Tale", ["ไบบายิน"] = "Tglg", ["ไภกษุกี"] = "Bhks", ["ไม่ระบุ"] = "None", ["ไว"] = "Vaii", ["ไอบีเรียตะวันออกเฉียงเหนือ"] = "Ibrnn", ["ไอบีเรียตะวันออกเฉียงใต้"] = "Ibrns", ["ไฮเออโรกลีฟอานาโตเลีย"] = "Hluw", ["ไฮเออโรกลีฟอียิปต์"] = "Egyp", } 5igq6skzboo2cwmzbmkgtrcfjqzkpog 5775291 5775261 2026-10-14T10:20:37Z Octahedron80 267 5775291 Scribunto text/plain return { ["Ancient North Arabian"] = "Narb", ["Ancient South Arabian"] = "Sarb", ["Blissymbolic"] = "Blis", ["Canadian syllabic"] = "Cans", ["Caucasian Albanian"] = "Aghb", ["Chisoi"] = "Chis", ["Clear Script"] = "xwo-Mong", ["Cypro-Minoan"] = "Cpmn", ["Demotic"] = "Egyd", ["Deseret"] = "Dsrt", ["Dongba"] = "Nkdb", ["Duployan"] = "Dupl", ["Elymaic"] = "Elym", ["Garay"] = "Gara", ["Geba"] = "Nkgb", ["Hatran"] = "Hatr", ["Hieratic"] = "Egyh", ["Image-rendered"] = "Image", ["พาร์เทียจารึก"] = "Prti", ["Khema"] = "Gukh", ["Khojki"] = "Khoj", ["Khutsuri"] = "Geok", ["Khwarezmian"] = "Chrs", ["Kirat Rai"] = "Krai", ["Kpelle"] = "Kpel", ["Kulitan"] = "Kulit", ["Leke"] = "Leke", ["Loma"] = "Loma", ["Manichaean"] = "Mani", ["Marchen"] = "Marc", ["Medefaidrin"] = "Medf", ["Mende"] = "Mend", ["Meroitic cursive"] = "Merc", ["Meroitic hieroglyphic"] = "Mero", ["Moon"] = "Moon", ["Multani"] = "Mult", ["Mundari Bani"] = "Nagm", ["N'Ko"] = "Nkoo", ["Nabataean"] = "Nbat", ["Newa"] = "Newa", ["Nyiakeng Puachue Hmong"] = "Hmnp", ["Nüshu"] = "Nshu", ["Ogham"] = "Ogam", ["Ol Onal"] = "Onao", ["Old Permic"] = "Perm", ["Old Turkic"] = "Orkh", ["Osage"] = "Osge", ["Pahawh Hmong"] = "Hmng", ["Palmyrene"] = "Palm", ["Pau Cin Hau"] = "Pauc", ["Pazend"] = "pal-Avst", ["Phags-pa"] = "Phag", ["Pollard"] = "Plrd", ["Proto-Cuneiform"] = "Pcun", ["Proto-Elamite"] = "Pelm", ["Proto-Sinaitic"] = "Psin", ["Ranjana"] = "Ranj", ["Rejang"] = "Rjng", ["Rongorongo"] = "Roro", ["Rumi numerals"] = "Rumin", ["Samaritan"] = "Samr", ["Saurashtra"] = "Saur", ["Shavian"] = "Shaw", ["Sidetic"] = "Sidt", ["SignWriting"] = "Sgnw", ["Sorang Sompeng"] = "Sora", ["Soyombo"] = "Soyo", ["Sui"] = "Shui", ["Sunuwar"] = "Sunu", ["Tangsa"] = "Tnsa", ["Tigalari"] = "Tutg", ["Todhri"] = "Todr", ["Tolong Siki"] = "Tols", ["Toto"] = "Toto", ["Visible Speech"] = "Visp", ["Vithkuqi"] = "Vith", ["Wancho"] = "Wcho", ["Woleai"] = "Wole", ["Yezidi"] = "Yezi", ["Zanabazar Square"] = "Zanb", ["Znamenny musical notation"] = "Zname", ["mathematical notation"] = "Zmth", ["กรีก"] = "Grek", ["กลาโกลิติก"] = "Glag", ["กวิ"] = "Kawi", ["กอท"] = "Goth", ["กะยา"] = "Kali", ["กันนฑะ"] = "Knda", ["ขอมไทย"] = "Khomt", ["ขุทาพาที"] = "Sind", ["ขโรษฐี"] = "Khar", ["ครันถะ"] = "Gran", ["คอปติก"] = "Copt", ["คาตากานะ"] = "Kana", ["คานะ"] = "Hrkt", ["คาเรีย"] = "Cari", ["คีตันเล็ก"] = "Kits", ["คีตันใหญ่"] = "Kitl", ["คุชราต"] = "Gujr", ["คุญชลาโคณฑี"] = "Gong", ["คุรมุขี"] = "Guru", ["คูนิฟอร์ม"] = "Xsux", ["จอร์เจีย"] = "Geor", ["จักมา"] = "Cakm", ["จาม"] = "Cham", ["จีน"] = "Hani", ["จีนตัวย่อ"] = "Hans", ["จีนตัวเต็ม"] = "Hant", ["จูร์เชน"] = "Jurc", ["จู้อิน"] = "Bopo", ["ชวา"] = "Java", ["ซอกเดีย"] = "Sogd", ["ซอกเดียเก่า"] = "Sogo", ["ซิเบ"] = "sjo-Mong", ["ซีริลลิก"] = "Cyrl", ["ซีริลลิกเก่า"] = "Cyrs", ["ซีรีแอก"] = "Syrc", ["ซุนดา"] = "Sund", ["ญี่ปุ่น"] = "Jpan", ["ฏากรี"] = "Takr", ["ดิเวส อกุรุ"] = "Diak", ["ตักบันวา"] = "Tagb", ["ตังกุต"] = "Tang", ["ตัมยิก"] = "sit-tam-Tibt", ["ติรหุตา"] = "Tirh", ["ทมิฬ"] = "Taml", ["ทานะ"] = "Thaa", ["ทิฟินาค"] = "Tfng", ["ทิเบต"] = "Tibt", ["นันทินาครี"] = "Nand", ["บัสซา"] = "Bass", ["บาตัก"] = "Batk", ["บามุม"] = "Bamu", ["บาหลี"] = "Bali", ["บูฮิด"] = "Buhd", ["ปาห์ลาวีคัมภีร์"] = "Phlv", ["ปาห์ลาวีจารึก"] = "Phli", ["ปาห์ลาวีเพลงสดุดี"] = "Phlp", ["พม่า"] = "Mymr", ["พราหมี"] = "Brah", ["ฟรักทูร์"] = "Latf", ["ฟินิเชีย"] = "Phnx", ["มณีปุระ"] = "Mtei", ["มรู"] = "Mroo", ["มลยาฬัม"] = "Mlym", ["มสรามโคณฑี"] = "Gonm", ["มหาชนี"] = "Mahj", ["มองโกเลีย"] = "Mong", ["มันดาอิก"] = "Mand", ["มากัซซาร์"] = "Maka", ["มายา"] = "Maya", ["ยังไม่กำหนด"] = "Zyyy", ["ยังไม่มีรหัส"] = "Zzzz", ["ยูการิต"] = "Ugar", ["รหัสมอร์ส"] = "Morse", ["รูน"] = "Runr", ["ลนตารา"] = "Bugi", ["ละติน"] = "Latn", ["ลายตัย"] = "Tayo", ["ลาว"] = "Laoo", ["ลิมบู"] = "Limb", ["ลิเชีย"] = "Lyci", ["ลิเดีย"] = "Lydi", ["ลิเนียร์บี"] = "Linb", ["ลิเนียร์เอ"] = "Lina", ["ลีสู่"] = "Lisu", ["วรังจิติ"] = "Wara", ["ศารทา"] = "Shrd", ["ศาหมุขี"] = "pa-Arab", ["สัญกรณ์ดนตรี"] = "Music", ["สัญญาณธง"] = "Semap", ["สัญลักษณ์"] = "Zsym", ["สัทอักษรสากล"] = "Ipach", ["สิงหล"] = "Sinh", ["สิทธัม"] = "Sidd", ["สินธุ"] = "Inds", ["สิเลฏินาครี"] = "Sylo", ["อัดลัม"] = "Adlm", ["อัสสัม"] = "as-Beng", ["อาฟากา"] = "Afak", ["อาร์มีเนีย"] = "Armn", ["อาหม"] = "Ahom", ["อาหรับ"] = "Arab", ["อิตาลิกเก่า"] = "Ital", ["อี๋"] = "Yiii", ["อุยกูร์เก่า"] = "Ougr", ["อุสมาน"] = "Osma", ["อเวสตะ"] = "Avst", ["อ็อลจิกิ"] = "Olck", ["ฮังการีเก่า"] = "Hung", ["ฮันกึล"] = "Hang", ["ฮานิฟ"] = "Rohg", ["ฮานูโนโอ"] = "Hano", ["ฮิรางานะ"] = "Hira", ["ฮีบรู"] = "Hebr", ["เกาหลี"] = "Kore", ["เขมร"] = "Khmr", ["เชโรกี"] = "Cher", ["เตลูกู"] = "Telu", ["เทงกวาร์"] = "Teng", ["เทวนาครี"] = "Deva", ["เบงกอล"] = "Beng", ["เบรลล์"] = "Brai", ["เปอร์เซียเก่า"] = "Xpeo", ["เลปชา"] = "Lepc", ["เอธิโอเปีย"] = "Ethi", ["เอลบาซาน"] = "Elba", ["แกลิก"] = "Latg", ["แมนจู"] = "mnc-Mong", ["แอราเมอิกจักรวรรดิ"] = "Armi", ["โฑครี"] = "Dogr", ["โมฑี"] = "Modi", ["โอริยา"] = "Orya", ["ไกถี"] = "Kthi", ["ไซปรัส"] = "Cprt", ["ไทธรรม"] = "Lana", ["ไทย"] = "Thai", ["ไทลื้อใหม่"] = "Talu", ["ไทเวียด"] = "Tavt", ["ไทใต้คง"] = "Tale", ["ไบบายิน"] = "Tglg", ["ไภกษุกี"] = "Bhks", ["ไม่ระบุ"] = "None", ["ไว"] = "Vaii", ["ไอบีเรียตะวันออกเฉียงเหนือ"] = "Ibrnn", ["ไอบีเรียตะวันออกเฉียงใต้"] = "Ibrns", ["ไฮเออโรกลีฟอานาโตเลีย"] = "Hluw", ["ไฮเออโรกลีฟอียิปต์"] = "Egyp", } jl9qzf9b8rpylvfpx0senvksmzk5huz 5775296 5775291 2026-10-14T10:31:54Z Octahedron80 267 5775296 Scribunto text/plain return { ["Ancient North Arabian"] = "Narb", ["Ancient South Arabian"] = "Sarb", ["Blissymbolic"] = "Blis", ["Canadian syllabic"] = "Cans", ["Caucasian Albanian"] = "Aghb", ["Chisoi"] = "Chis", ["Clear Script"] = "xwo-Mong", ["Cypro-Minoan"] = "Cpmn", ["Demotic"] = "Egyd", ["Deseret"] = "Dsrt", ["Dongba"] = "Nkdb", ["Duployan"] = "Dupl", ["Elymaic"] = "Elym", ["Garay"] = "Gara", ["Geba"] = "Nkgb", ["Hatran"] = "Hatr", ["Hieratic"] = "Egyh", ["Image-rendered"] = "Image", ["พาร์เทียจารึก"] = "Prti", ["Khema"] = "Gukh", ["Khojki"] = "Khoj", ["Khutsuri"] = "Geok", ["ฆวารัซม์"] = "Chrs", ["Kirat Rai"] = "Krai", ["Kpelle"] = "Kpel", ["Kulitan"] = "Kulit", ["Leke"] = "Leke", ["Loma"] = "Loma", ["Manichaean"] = "Mani", ["Marchen"] = "Marc", ["Medefaidrin"] = "Medf", ["Mende"] = "Mend", ["Meroitic cursive"] = "Merc", ["Meroitic hieroglyphic"] = "Mero", ["Moon"] = "Moon", ["Multani"] = "Mult", ["Mundari Bani"] = "Nagm", ["N'Ko"] = "Nkoo", ["Nabataean"] = "Nbat", ["Newa"] = "Newa", ["Nyiakeng Puachue Hmong"] = "Hmnp", ["Nüshu"] = "Nshu", ["Ogham"] = "Ogam", ["Ol Onal"] = "Onao", ["Old Permic"] = "Perm", ["Old Turkic"] = "Orkh", ["Osage"] = "Osge", ["Pahawh Hmong"] = "Hmng", ["Palmyrene"] = "Palm", ["Pau Cin Hau"] = "Pauc", ["Pazend"] = "pal-Avst", ["Phags-pa"] = "Phag", ["Pollard"] = "Plrd", ["Proto-Cuneiform"] = "Pcun", ["Proto-Elamite"] = "Pelm", ["Proto-Sinaitic"] = "Psin", ["Ranjana"] = "Ranj", ["Rejang"] = "Rjng", ["Rongorongo"] = "Roro", ["Rumi numerals"] = "Rumin", ["Samaritan"] = "Samr", ["Saurashtra"] = "Saur", ["Shavian"] = "Shaw", ["Sidetic"] = "Sidt", ["SignWriting"] = "Sgnw", ["Sorang Sompeng"] = "Sora", ["Soyombo"] = "Soyo", ["Sui"] = "Shui", ["Sunuwar"] = "Sunu", ["Tangsa"] = "Tnsa", ["Tigalari"] = "Tutg", ["Todhri"] = "Todr", ["Tolong Siki"] = "Tols", ["Toto"] = "Toto", ["Visible Speech"] = "Visp", ["Vithkuqi"] = "Vith", ["Wancho"] = "Wcho", ["Woleai"] = "Wole", ["Yezidi"] = "Yezi", ["Zanabazar Square"] = "Zanb", ["Znamenny musical notation"] = "Zname", ["mathematical notation"] = "Zmth", ["กรีก"] = "Grek", ["กลาโกลิติก"] = "Glag", ["กวิ"] = "Kawi", ["กอท"] = "Goth", ["กะยา"] = "Kali", ["กันนฑะ"] = "Knda", ["ขอมไทย"] = "Khomt", ["ขุทาพาที"] = "Sind", ["ขโรษฐี"] = "Khar", ["ครันถะ"] = "Gran", ["คอปติก"] = "Copt", ["คาตากานะ"] = "Kana", ["คานะ"] = "Hrkt", ["คาเรีย"] = "Cari", ["คีตันเล็ก"] = "Kits", ["คีตันใหญ่"] = "Kitl", ["คุชราต"] = "Gujr", ["คุญชลาโคณฑี"] = "Gong", ["คุรมุขี"] = "Guru", ["คูนิฟอร์ม"] = "Xsux", ["จอร์เจีย"] = "Geor", ["จักมา"] = "Cakm", ["จาม"] = "Cham", ["จีน"] = "Hani", ["จีนตัวย่อ"] = "Hans", ["จีนตัวเต็ม"] = "Hant", ["จูร์เชน"] = "Jurc", ["จู้อิน"] = "Bopo", ["ชวา"] = "Java", ["ซอกเดีย"] = "Sogd", ["ซอกเดียเก่า"] = "Sogo", ["ซิเบ"] = "sjo-Mong", ["ซีริลลิก"] = "Cyrl", ["ซีริลลิกเก่า"] = "Cyrs", ["ซีรีแอก"] = "Syrc", ["ซุนดา"] = "Sund", ["ญี่ปุ่น"] = "Jpan", ["ฏากรี"] = "Takr", ["ดิเวส อกุรุ"] = "Diak", ["ตักบันวา"] = "Tagb", ["ตังกุต"] = "Tang", ["ตัมยิก"] = "sit-tam-Tibt", ["ติรหุตา"] = "Tirh", ["ทมิฬ"] = "Taml", ["ทานะ"] = "Thaa", ["ทิฟินาค"] = "Tfng", ["ทิเบต"] = "Tibt", ["นันทินาครี"] = "Nand", ["บัสซา"] = "Bass", ["บาตัก"] = "Batk", ["บามุม"] = "Bamu", ["บาหลี"] = "Bali", ["บูฮิด"] = "Buhd", ["ปาห์ลาวีคัมภีร์"] = "Phlv", ["ปาห์ลาวีจารึก"] = "Phli", ["ปาห์ลาวีเพลงสดุดี"] = "Phlp", ["พม่า"] = "Mymr", ["พราหมี"] = "Brah", ["ฟรักทูร์"] = "Latf", ["ฟินิเชีย"] = "Phnx", ["มณีปุระ"] = "Mtei", ["มรู"] = "Mroo", ["มลยาฬัม"] = "Mlym", ["มสรามโคณฑี"] = "Gonm", ["มหาชนี"] = "Mahj", ["มองโกเลีย"] = "Mong", ["มันดาอิก"] = "Mand", ["มากัซซาร์"] = "Maka", ["มายา"] = "Maya", ["ยังไม่กำหนด"] = "Zyyy", ["ยังไม่มีรหัส"] = "Zzzz", ["ยูการิต"] = "Ugar", ["รหัสมอร์ส"] = "Morse", ["รูน"] = "Runr", ["ลนตารา"] = "Bugi", ["ละติน"] = "Latn", ["ลายตัย"] = "Tayo", ["ลาว"] = "Laoo", ["ลิมบู"] = "Limb", ["ลิเชีย"] = "Lyci", ["ลิเดีย"] = "Lydi", ["ลิเนียร์บี"] = "Linb", ["ลิเนียร์เอ"] = "Lina", ["ลีสู่"] = "Lisu", ["วรังจิติ"] = "Wara", ["ศารทา"] = "Shrd", ["ศาหมุขี"] = "pa-Arab", ["สัญกรณ์ดนตรี"] = "Music", ["สัญญาณธง"] = "Semap", ["สัญลักษณ์"] = "Zsym", ["สัทอักษรสากล"] = "Ipach", ["สิงหล"] = "Sinh", ["สิทธัม"] = "Sidd", ["สินธุ"] = "Inds", ["สิเลฏินาครี"] = "Sylo", ["อัดลัม"] = "Adlm", ["อัสสัม"] = "as-Beng", ["อาฟากา"] = "Afak", ["อาร์มีเนีย"] = "Armn", ["อาหม"] = "Ahom", ["อาหรับ"] = "Arab", ["อิตาลิกเก่า"] = "Ital", ["อี๋"] = "Yiii", ["อุยกูร์เก่า"] = "Ougr", ["อุสมาน"] = "Osma", ["อเวสตะ"] = "Avst", ["อ็อลจิกิ"] = "Olck", ["ฮังการีเก่า"] = "Hung", ["ฮันกึล"] = "Hang", ["ฮานิฟ"] = "Rohg", ["ฮานูโนโอ"] = "Hano", ["ฮิรางานะ"] = "Hira", ["ฮีบรู"] = "Hebr", ["เกาหลี"] = "Kore", ["เขมร"] = "Khmr", ["เชโรกี"] = "Cher", ["เตลูกู"] = "Telu", ["เทงกวาร์"] = "Teng", ["เทวนาครี"] = "Deva", ["เบงกอล"] = "Beng", ["เบรลล์"] = "Brai", ["เปอร์เซียเก่า"] = "Xpeo", ["เลปชา"] = "Lepc", ["เอธิโอเปีย"] = "Ethi", ["เอลบาซาน"] = "Elba", ["แกลิก"] = "Latg", ["แมนจู"] = "mnc-Mong", ["แอราเมอิกจักรวรรดิ"] = "Armi", ["โฑครี"] = "Dogr", ["โมฑี"] = "Modi", ["โอริยา"] = "Orya", ["ไกถี"] = "Kthi", ["ไซปรัส"] = "Cprt", ["ไทธรรม"] = "Lana", ["ไทย"] = "Thai", ["ไทลื้อใหม่"] = "Talu", ["ไทเวียด"] = "Tavt", ["ไทใต้คง"] = "Tale", ["ไบบายิน"] = "Tglg", ["ไภกษุกี"] = "Bhks", ["ไม่ระบุ"] = "None", ["ไว"] = "Vaii", ["ไอบีเรียตะวันออกเฉียงเหนือ"] = "Ibrnn", ["ไอบีเรียตะวันออกเฉียงใต้"] = "Ibrns", ["ไฮเออโรกลีฟอานาโตเลีย"] = "Hluw", ["ไฮเออโรกลีฟอียิปต์"] = "Egyp", } px8cbg5icl8say4jvg3f005ztk13nsw มอดูล:IPA 828 137524 5775273 5759944 2026-10-14T10:03:56Z Octahedron80 267 5775273 Scribunto text/plain local export = {} local force_cat = false -- for testing local decorations_module = "Module:decorations" local pages_module = "Module:pages" local qualifier_module = "Module:qualifier" local string_utilities_module = "Module:string utilities" local syllables_module = "Module:syllables" local utilities_module = "Module:utilities" local m_data = mw.loadData("Module:IPA/data") local m_str_utils = require(string_utilities_module) local m_syllables -- [[Module:syllables]]; loaded below if needed local m_symbols = mw.loadData("Module:IPA/data/symbols") local concat = table.concat local decode_entities = m_str_utils.decode_entities local find = string.find local gcodepoint = m_str_utils.gcodepoint local gmatch = m_str_utils.gmatch local gsub = string.gsub local insert = table.insert local is_preview = require(pages_module).is_preview local len = m_str_utils.len local listToText = mw.text.listToText local match = string.match local pattern_escape = m_str_utils.pattern_escape local sub = string.sub local u = m_str_utils.char local ugsub = m_str_utils.gsub local umatch = m_str_utils.match local usub = m_str_utils.sub local function with_codepoints(s) if find(s, "%S%s") then local parts = {} for ch in gmatch(s, "%S") do parts[#parts + 1] = with_codepoints(ch) end return concat(parts, ", ") end local cps = {} for cp in gcodepoint(s) do cps[#cps + 1] = ("U+%04X"):format(cp) end return s .. " [" .. concat(cps, " ") .. "]" end local namespace = mw.title.getCurrentTitle().nsText local function is_content_page(lang, namespace) return namespace == "" or namespace == "การสืบสร้าง" or lang and lang:hasType("appendix-constructed") and namespace == "ภาคผนวก" end -- Etymology-only languages are not L2 entry languages; IPA should use the parent full language. local function assert_not_etymology_only_lang(lang) if lang and lang.hasType and lang:hasType("language", "etymology-only") then local parent_code = lang.getParentCode and lang:getParentCode() or nil error(("Cannot use IPA with the etymology-only language %q; use the parent full language %q instead."):format(lang:getCode(), parent_code)) end end local function track(page) require("Module:debug/track")("IPA/" .. page) return true end local function process_maybe_split_categories(split_output, categories, prontext, lang, errtext) if split_output ~= "raw" then if categories[1] then categories = require(utilities_module).format_categories(categories, lang, nil, nil, force_cat) else categories = "" end end if split_output then -- for use of IPA in links, etc. if errtext then return prontext, categories, errtext else return prontext, categories end else return prontext .. (errtext or "") .. categories end end --[==[ Format a line of one or more IPA pronunciations as {{tl|IPA}} would do it, i.e. with a preceding {"IPA:"} followed by the word {"key"} linking to an Appendix page describing the language's phonology, and with an added category ` ``lang`` terms with IPA pronunciation`. Other than the extra preceding text and category, this is identical to {format_IPA_multiple()}, and the considerations described there in the documentation apply here as well. There is a single parameter `data`, an object with the following fields: * `lang`: Object representing the language of the pronunciations, which is used when adding cleanup categories for pronunciations with invalid phonemes; for determining how many syllables the pronunciations have in them, in order to add a category such as [[:Category:Italian 2-syllable words]] (for certain languages only); for adding a category ` ``lang`` terms with IPA pronunciation`; and for determining the proper sort keys for categories. Unlike for {format_IPA_multiple()}, `lang` may not be {nil}. * `items`: List of pronunciations, in exactly the same format as for {format_IPA_multiple()}. * `err`: If not {nil}, a string containing an error message to use in place of the link to the language's phonology. * `separator`: The default separator to use when separating formatted items. Defaults to {", "}. Does not apply to the first item, where the default separator is always the empty string. Overridden by the per-item `separator` field in `items`. * `sort_key`: Explicit sort key used for categories. * `no_count`: Suppress adding a {#-syllable words} category such as [[:Category:Italian 2-syllable words]]. Note that only certain languages add such categories to begin with, because it depends on knowing how to count syllables in a given language, which depends on the phonology of the language. Also, this does not suppress the addition of cleanup or other categories. If you need them suppressed, use `split_output` to return the categories separately and ignore them. * `split_output`: If not given, the return value is a concatenation of the formatted pronunciation and formatted categories. Otherwise, two values are returned: the formatted pronunciation and the categories. If `split_output` is the value {"raw"}, the categories are returned in list form, where the list elements are a combination of category strings and category objects of the form suitable for passing to {format_categories()} in [[Module:utilities]]. If `split_output` is any other value besides {nil}, the categories are returned as a pre-formatted concatenated string. * `include_langname`: If specified, prefix the result with the language name, followed by a colon. * `q`: {nil} or a list of left qualifiers (as in {{tl|q}}) to display at the beginning, before the formatted pronunciations and preceding {"IPA:"}. * `qq`: {nil} or a list of right qualifiers to display after all formatted pronunciations. * `a`: {nil} or a list of left accent qualifiers (as in {{tl|a}}) to display at the beginning, before the formatted pronunciations and preceding {"IPA:"}. * `aa`: {nil} or a list of right accent qualifiers to display after all formatted pronunciations. ]==] function export.format_IPA_full(data) if type(data) ~= "table" or data.getCode then error("Must now supply a table of arguments to format_IPA_full(); first argument should be that table, not a language object") end local lang = data.lang local items = data.items local err = data.err local separator = data.separator local sort_key = data.sort_key local no_count = data.no_count local split_output = data.split_output local q = data.q local qq = data.qq local a = data.a local aa = data.aa local include_langname = data.include_langname if data.qualifiers then -- FIXME: added 2026-09-18; consider removing eventually. error("overall `.qualifiers` is no longer supported; change the code to use `.q` or `.qq`") end local hasKey = m_data.langs_with_infopages if not lang or not lang.getCode then error("Must specify language to format_IPA_full()") end assert_not_etymology_only_lang(lang) local langname = lang:getCanonicalName() local langcat = lang:getCategoryName() --th local prefix_text if err then prefix_text = '<span class="error">' .. err .. '</span>' else if hasKey[lang:getCode()] then prefix_text = "ภาคผนวก:การออกเสียง" .. langcat else prefix_text = "วิกิพีเดีย:ระบบเสียง" .. langcat end prefix_text = "[[" .. prefix_text .. "|อธิบาย]]" end local prefix = "[[วิกิพจนานุกรม:สัทอักษรสากล|ไอพีเอ]]<sup>(" .. prefix_text .. ")</sup>:&#32;" local IPAs, categories = export.format_IPA_multiple(lang, items, separator, no_count, "raw") if is_content_page(lang, namespace) then insert(categories, { cat = "ศัพท์" .. langcat .. "ที่มีการออกเสียงไอพีเอ", sort_key = sort_key }) end local prontext = prefix .. IPAs if q and q[1] or qq and qq[1] or a and a[1] or aa and aa[1] then prontext = require(decorations_module).format_decorations { lang = lang, text = prontext, q = q, qq = qq, a = a, aa = aa, } end if include_langname then prontext = langname .. ": " .. prontext end return process_maybe_split_categories(split_output, categories, prontext, lang) end local function split_phonemic_phonetic(pron) local reconstructed, phonemic, phonetic = match(pron, "^(%*?)(/.-/)%s+(%[.-%])$") if reconstructed then return reconstructed .. phonemic, reconstructed .. phonetic else return pron, nil end end local function determine_repr(pron) local reconstructed -- Temporarily remove any initial asterisk before representation marks, -- which avoids having to account for it in the data, but set the -- `reconstructed` flag. if sub(pron, 1, 1) == "*" then reconstructed = true pron = sub(pron, 2) end -- Some representation types have aliases for convenience (e.g. "// //" is -- an alias for "⫽ ⫽"). and these need to be substituted in before checking -- for other data. local opening, n = match(pron, "^.[\128-\191]*") local subs_data = m_data.representation_subs[opening] if subs_data then pron, n = ugsub(pron, subs_data[1], subs_data[2]) -- If the substitution was made, `opening` needs to be changed to the -- new opening character. if n ~= 0 then opening = subs_data[3] end end -- Get the type data based on the opening character (if any), and set the -- representation type if the closing character matches. local type_data, repr, closing = m_data.representation_types[opening] if type_data then closing = type_data[2] if type_data and match(pron, pattern_escape(closing) .. "$", #opening + 1) then repr = type_data[1] end end -- Default to the empty string. if not repr then opening, closing = "", "" end -- Reattach the asterisk if reconstructed. if reconstructed then pron = "*" .. pron end return pron, repr, opening, closing, reconstructed end local function hasInvalidSeparators(transcription) -- Escape certain characters as well as pauses, which have the format "(...)" (with any number of dots), to avoid false-positives. transcription = transcription:gsub(".[\128-\191]*", m_symbols.separator_escapes) :gsub("%(%.+%)", "\3") :gsub("[()]+", "") return ( transcription:find("..", nil, true) or transcription:match("%.%f[%z \1\2\3,:;]") or transcription:match("\1%f[%z \2\3,:;]") or transcription:match("\2%f[%z \1\3,:;]") or transcription:match("\3[:;]") or transcription:match("%f[^%z \1\2\3,]%.") ) and true or false end --[==[ Format a line of one or more bare IPA pronunciations (i.e. without any preceding {"IPA:"} and without adding to a category ` ``lang`` terms with IPA pronunciation`). Individual pronunciations are formatted using {format_IPA()} and are combined with separators, decorations, pre-text, post-text, etc. to form a line of pronunciations. Parameters accepted are: * `lang` is an object representing the language of the pronunciations, which is used when adding cleanup categories for pronunciations with invalid phonemes; for determining how many syllables the pronunciations have in them, in order to add a category such as [[:Category:Italian 2-syllable words]] (for certain languages only); and for computing the proper sort keys for categories. `lang` may be {nil}. * `items` is a list of pronunciations, each of which is an object with the following properties: ** `pron`: the pronunciation, in the same format as is accepted by {format_IPA()}, i.e. it should be either phonemic (surrounded by {/.../}), phonetic (surrounded by {[...]}), orthographic (surrounded by {⟨...⟩}) or a rhyme (beginning with a hyphen); ** `pretext`: text to display directly before the formatted pronunciation, inside of any qualifiers or accent qualifiers; ** `posttext`: text to display directly after the formatted pronunciation, inside of any qualifiers or accent qualifiers; ** `q`: {nil} or a list of left qualifiers (as in {{tl|q}}) to display before the formatted pronunciation; ** `qq`: {nil} or a list of right qualifiers to display after the formatted pronunciation; ** `a`: {nil} or a list of left accent qualifiers (as in {{tl|a}}) to display before the formatted pronunciation; ** `aa`: {nil} or a list of right accent qualifiers to after before the formatted pronunciation; ** `refs`: {nil} or a list of references or reference specs to add after the pronunciation and any posttext and qualifiers; the value of a list item is either a string containing the reference text (typically a call to a citation template such as {{tl|cite-book}}, or a template wrapping such a call), or an object with fields `text` (the reference text), `name` (the name of the reference, as in {{cd|<nowiki><ref name="foo">...</ref></nowiki>}} or {{cd|<nowiki><ref name="foo" /></nowiki>}}) and/or `group` (the group of the reference, as in {{cd|<nowiki><ref name="foo" group="bar">...</ref></nowiki>}} or {{cd|<nowiki><ref name="foo" group="bar"/></nowiki>}}); this uses a parser function to format the reference appropriately and insert a footnote number that hyperlinks to the actual reference, located in the {{cd|<nowiki><references /></nowiki>}} section; ** `gloss`: {nil} or a gloss (definition) for this item, if different definitions have different pronunciations; ** `pos`: {nil} or a part of speech for this item, if different parts of speech have different pronunciations; ** `separator`: the separator text to insert directly before the formatted pronunciation and all decorations and pre-text; defaults to the outer `separator` parameter. * `separator`: The default separator to use when separating formatted items. Defaults to {", "}. Does not apply to the first item, where the default separator is always the empty string. Overridden by the per-item `separator` field in `items`. * `no_count`: Suppress adding a {#-syllable words} category such as [[:Category:Italian 2-syllable words]]. Note that only certain languages add such categories to begin with, because it depends on knowing how to count syllables in a given language, which depends on the phonology of the language. Also, this does not suppress the addition of cleanup categories. If you need them suppressed, use `split_output` to return the categories separately and ignore them. * `split_output`: If not given, the return value is a concatenation of the formatted pronunciation and formatted categories. Otherwise, two values are returned: the formatted pronunciation and the categories. If `split_output` is the value {"raw"}, the categories are returned in list form, where the list elements are a combination of category strings and category objects of the form suitable for passing to {format_categories()} in [[Module:utilities]]. If `split_output` is any other value besides {nil}, the categories are returned as a pre-formatted concatenated string. ]==] function export.format_IPA_multiple(lang, items, separator, no_count, split_output) local categories = {} separator = separator or ", " if not lang then track("format-multiple-nolang") else assert_not_etymology_only_lang(lang) end -- Format if not items[1] then if namespace == "แม่แบบ" then insert(items, {pron = "/aɪ piː ˈeɪ/"}) else insert(categories, "Pronunciation templates without a pronunciation") end end local bits = {} for i, item in ipairs(items) do local bit -- If the pronunciation is entirely empty, allow this and don't do anything, so that e.g. the pretext and/or -- posttext can be specified to force something like ''unknown'' to appear in place of the pronunciation -- (as happens e.g. when ? is used as a respelling in [[Module:ca-IPA]]; see [[guèiser]] for an example). if item.pron == "" then bit = "" else local item_categories, errtext bit, item_categories, errtext = export.format_IPA(lang, item.pron, "raw") bit = bit .. errtext for _, cat in ipairs(item_categories) do insert(categories, cat) end end if item.pretext then bit = item.pretext .. bit end if item.posttext then bit = bit .. item.posttext end if item.qualifiers then -- FIXME: added 2026-09-18; consider removing eventually. error("`.qualifiers` is no longer supported; change the code to use `.q` or `.qq`") end local has_decorations = item.q and item.q[1] or item.qq and item.qq[1] or item.a and item.a[1] or item.aa and item.aa[1] or item.refs and item.refs[1] local has_gloss_or_pos = item.gloss or item.pos if has_decorations or has_gloss_or_pos then -- FIXME: Currently we tack the gloss and POS (in that order) onto the end of the regular left qualifiers. -- Should we do something different? local q = item.q if has_gloss_or_pos then q = mw.clone(item.q) or {} if item.gloss then local m_qualifier = require(qualifier_module) insert(q, m_qualifier.wrap_qualifier_css("“", "quote") .. item.gloss .. m_qualifier.wrap_qualifier_css("”", "quote")) end if item.pos then -- FIXME: Consider expanding aliases as found in [[Module:headword/data]] or similar. insert(q, item.pos) end end bit = require(decorations_module).format_decorations { lang = lang, text = bit, q = q, qq = item.qq, a = item.a, aa = item.aa, refs = item.refs, } end bit = (item.separator or (i == 1 and "" or separator)) .. bit insert(bits, bit) --[=[ [[Special:WhatLinksHere/Wiktionary:Tracking/IPA/syntax-error]] The length or gemination symbol should not appear after a syllable break or stress symbol. ]=] -- The nature of the following pattern match is such that we don't have to split a combined '/.../ [...]' spec -- into its parts in order to process. if match(item.pron, "[.\203][\136\140]?\203[\144\145]") then -- [.ˈˌ][ːˑ] track("syntax-error") end if lang then -- Add syllable count if the language's diphthongs are listed in [[Module:syllables]]. -- Don't do this if the term has spaces, a liaison mark (‿) or isn't in mainspace. if not no_count and namespace == "" then m_syllables = m_syllables or require(syllables_module) local langcode = lang:getCode() if m_data.langs_to_generate_syllable_count_categories[langcode] then local raw_phonemic, phonetic, use_it = split_phonemic_phonetic(item.pron) local phonemic, repr = determine_repr(raw_phonemic) if not phonetic then -- not a '/.../ [...]' combined pronunciation if m_data.langs_to_use_phonetic_or_phonemic_notation[langcode] then use_it = phonemic elseif m_data.langs_to_use_phonetic_notation[langcode] then use_it = repr == "phonetic" and phonemic or nil else use_it = repr == "phonemic" and phonemic or nil end elseif repr == "phonetic" then use_it = phonetic elseif repr == "phonemic" then use_it = phonemic end -- Note: two uses of find with plain patterns is much faster than umatch with [ ‿]. if use_it and not (find(use_it, " ") or find(use_it, "‿")) then local syllable_count = m_syllables.getVowels(use_it, lang) if syllable_count then insert(categories, "ศัพท์" .. lang:getCategoryName() .. "ที่มี " .. syllable_count .. " พยางค์") end end end end end end return process_maybe_split_categories(split_output, categories, concat(bits), lang) end --[=[ Format a single IPA pronunciation, which cannot be a combined spec (such as {/.../ [...]}). This has been extracted from {format_IPA()} to allow the latter to handle such combined specs. This works like {format_IPA()} but requires that pre-created {err} (for error messages) and {categories} lists be passed in, and adds any generated error messages and categories to those lists. A single value is returned, the pronunciation, which is usually the same as passed in, but may have HTML added surrounding invalid characters so they appear in red. ]=] local function format_one_IPA(lang, raw_pron, err, categories) -- Disallow wikilinks. if match(raw_pron, "%[%[.-%]%]") then error("IPA input must not contain wikilinks.") end raw_pron = decode_entities(raw_pron) -- Detect the type of transcription. local pron, repr, opening, closing, reconstructed = determine_repr(raw_pron) -- Strip any reconstruction asterisk and representation marks. pron = sub(pron, #opening + 1 + (reconstructed and 1 or 0), -#closing - 1) if not repr then insert(categories, "IPA pronunciations with invalid representation marks") -- insert(err, "invalid representation marks") -- Removed because it's annoying when previewing pronunciation pages. end if repr ~= "orthographic" and lang and lang:getCode() == "en" and hasInvalidSeparators(pron) then insert(categories, "English IPA pronunciations with invalid separators") end if pron == "" then insert(categories, "IPA pronunciations with no pronunciation present") end -- Check for obsolete and nonstandard symbols for _, symbol in ipairs(m_data.nonstandard) do local result for nonstandard in gmatch(pron, symbol) do if not result then result = {} end insert(result, nonstandard) insert(categories, {cat = "IPA pronunciations with obsolete or nonstandard characters", sort_key = nonstandard} ) end if result then insert(err, "obsolete or nonstandard characters (" .. concat(result) .. ")") break end end --[[ Check for invalid symbols after removing the following: 1. wikilinks (handled above) 2. paired HTML tags 3. bolding 4. italics 5. asterisk at beginning of transcription 6. comma followed by spacing characters 7. superscripts enclosed in superscript parentheses ]] local found_HTML local result = gsub(pron, "<(%a+)[^>]*>([^<]+)</%1>", function(tagName, content) found_HTML = true return content end) result = gsub(result, "'''([^']*)'''", "%1") result = gsub(result, "''([^']*)''", "%1") result = gsub(result, "^%*", "") result = ugsub(result, ",%s+", "") -- VS15 local vs15_class = "[" .. m_symbols.add_vs15 .. "]" if umatch(pron, vs15_class) then local vs15 = u(0xFE0E) if find(result, vs15) then result = gsub(result, vs15, "") pron = gsub(pron, vs15, "") end pron = ugsub(pron, vs15_class, "%0" .. vs15) end if result ~= "" then local content_page = is_content_page(lang, namespace) if lang then -- Get the per_lang_valid data, and convert any per-language valid sequences to spaces. local per_lang_valid = m_symbols.per_lang_valid[lang:getCode()] if per_lang_valid then if type(per_lang_valid) == "table" then for _, pattern in pairs(per_lang_valid) do result = ugsub(result, pattern, " ") end else -- Should be a string. result = ugsub(result, per_lang_valid, " ") end end end local suggestions = {} -- Check for any invalid sequences, excluding anything in the per-language lookup table. for k, v in pairs(m_symbols.invalid) do if find(result, k, nil, true) then insert(suggestions, with_codepoints(k) .. " with " .. with_codepoints(v)) end end if suggestions[1] then local replacements = "replace " .. listToText(suggestions) if content_page then error("Invalid IPA: " .. replacements) end insert(err, replacements) end -- Convert any valid character sequences to spaces for _, pattern in pairs(m_symbols.valid) do result = ugsub(result, pattern, " ") end if not match(result, "^ *$") then local category = "IPA pronunciations with invalid IPA characters" if not content_page then category = category .. "/non_mainspace" end insert(categories, category) insert(err, "invalid IPA characters: " .. with_codepoints(result)) end end if found_HTML then insert(categories, "IPA pronunciations with paired HTML tags") end if (repr == "phonemic" or repr == "rhyme") and lang and m_data.phonemes[lang:getCode()] then local valid_phonemes = m_data.phonemes[lang:getCode()] local rest = pron local phonemes = {} while #rest > 0 do local longestmatch, longestmatch_len = "", 0 local rest_init = sub(rest, 1, 1) if rest_init == "(" or rest_init == ")" then longestmatch = rest_init longestmatch_len = 1 else for _, phoneme in ipairs(valid_phonemes) do local phoneme_len = len(phoneme) if phoneme_len > longestmatch_len and usub(rest, 1, phoneme_len) == phoneme then longestmatch = phoneme longestmatch_len = len(longestmatch) end end end if longestmatch_len > 0 then insert(phonemes, longestmatch) rest = usub(rest, longestmatch_len + 1) else local phoneme = usub(rest, 1, 1) insert(phonemes, "<span style=\"color: var(--wikt-palette-red,red)\">" .. phoneme .. "</span>") rest = usub(rest, 2) insert(categories, "IPA pronunciations with invalid phonemes/" .. lang:getCode()) track("invalid phonemes/" .. phoneme) end end pron = concat(phonemes) end return (reconstructed and "*" or "") .. opening .. pron .. closing end --[==[ Format an IPA pronunciation. This wraps the pronunciation in appropriate CSS classes and adds cleanup categories and error messages as needed. The pronunciation `pron` should be either phonemic (surrounded by {/.../}), phonetic (surrounded by {[...]}), orthographic (surrounded by {⟨...⟩}), a rhyme (beginning with a hyphen) or a combined phonemic/phonetic spec (of the form {/.../ [...]}). `lang` indicates the language of the pronunciation and can be {nil}. If not {nil}, and the specified language has data in [[Module:IPA/data]] indicating the allowed phonemes, then the page will be added to a cleanup category and an error message displayed next to the outputted pronunciation. Note that {lang} also determines sort key processing in the added cleanup categories. If `split_output` is not given, the return value is a concatenation of the formatted pronunciation, error messages and formatted cleanup categories. Otherwise, three values are returned: the formatted pronunciation, the cleanup categories and the concatenated error messages. If `split_output` is the value {"raw"}, the cleanup categories are returned in list form, where the list elements are a combination of category strings and category objects of the form suitable for passing to {format_categories()} in [[Module:utilities]]. If `split_output` is any other value besides {nil}, the cleanup categories are returned as a pre-formatted concatenated string. ]==] function export.format_IPA(lang, pron, split_output) local err = {} local categories = {} -- `pron` shouldn't contain ref tags. if match(pron, "\127'\"`UNIQ%-%-ref%-[%dA-F]+%-QINU`\"'\127") then error("<ref> tags found inside pronunciation parameter.") end if not lang then track("format-nolang") else assert_not_etymology_only_lang(lang) end local phonemic, phonetic = split_phonemic_phonetic(pron) pron = format_one_IPA(lang, phonemic, err, categories) if phonetic then track("phonemic-phonetic") -- There's no benefit to supporting the "/.../ [...]" format within one parameter. phonetic = format_one_IPA(lang, phonetic, err, categories) pron = pron .. " " .. phonetic end if err[1] and is_preview() then err = '<span class="error" style="font-size: small;>&#32;' .. concat(err, ", ") .. "</span>" else err = "" end return process_maybe_split_categories(split_output, categories, '<span class="IPA nowrap">' .. pron .. "</span>", lang, err) end --[==[ Format a line of one or more enPR pronunciations as {{tl|enPR}} would do it, i.e. with a preceding {"enPR:"} (linked to [[Appendix:English pronunciation]]) followed by one or more formatted, comma-separated enPR pronunciations. The pronunciations are formatted by wrapping them in the `AHD` and `enPR` CSS classes and adding any decorations (qualifiers, accent qualifiers and references). In addition, the overall result is wrapped in any overall decorations. There is a single parameter `data`, an object with the following fields: * `items` is a list of enPR pronunciations, each of which is an object with the following properties: ** `pron`: the enPR pronunciation; ** `q`: {nil} or a list of left qualifiers (as in {{tl|q}}) to display before the formatted pronunciation; ** `qq`: {nil} or a list of right qualifiers to display after the formatted pronunciation; ** `a`: {nil} or a list of left accent qualifiers (as in {{tl|a}}) to display before the formatted pronunciation; ** `aa`: {nil} or a list of right accent qualifiers to after before the formatted pronunciation. * `q`: {nil} or a list of left qualifiers (as in {{tl|q}}) to display at the beginning, before the formatted pronunciations and preceding {"enPR:"}. * `qq`: {nil} or a list of right qualifiers to display after all formatted pronunciations. * `a`: {nil} or a list of left accent qualifiers (as in {{tl|a}}) to display at the beginning, before the formatted pronunciations and preceding {"enPR:"}. * `aa`: {nil} or a list of right accent qualifiers to display after all formatted pronunciations. ]==] function export.format_enPR_full(data) local prefix = "[[ภาคผนวก:การออกเสียงภาษาอังกฤษ|enPR]]: " local lang = require("Module:languages").getByCode("en") local parts = {} for _, item in ipairs(data.items) do local part = '<span class="AHD enPR">' .. item.pron .. "</span>" if item.qualifiers then -- FIXME: added 2026-09-18; consider removing eventually. error("`.qualifiers` is no longer supported; change the code to use `.q` or `.qq`") end if item.q and item.q[1] or item.qq and item.qq[1] or item.a and item.a[1] or item.aa and item.aa[1] then part = require(decorations_module).format_decorations { lang = lang, text = part, q = item.q, qq = item.qq, a = item.a, aa = item.aa, } end insert(parts, part) end local prontext = prefix .. concat(parts, ", ") if data.qualifiers then -- FIXME: added 2026-09-18; consider removing eventually. error("overall `.qualifiers` is no longer supported; change the code to use `.q` or `.qq`") end if data.q and data.q[1] or data.qq and data.qq[1] or data.a and data.a[1] or data.aa and data.aa[1] then prontext = require(decorations_module).format_decorations { lang = lang, text = prontext, q = data.q, qq = data.qq, a = data.a, aa = data.aa, } end return prontext end return export cwy0th1e2wgudqxjetuhepyl6b11fuk มอดูล:IPA/data 828 137525 5775272 5759943 2026-10-14T10:02:07Z Octahedron80 267 5775272 Scribunto text/plain local list_to_set = require("Module:table").listToSet local data = {} --[=[ A list of representation types (e.g. /foo/ for phonemic and [bar] for phonetic), given as a table. The key is the opening character, the first value the representation type, and the second value the closing symbol.]=] data.representation_types = { ["/"] = {"phonemic", "/"}, ["["] = {"phonetic", "]"}, ["⫽"] = {"morphophonemic", "⫽"}, ["⟨"] = {"orthographic", "⟩"}, ["-"] = {"rhyme", ""}, } --[=[ A list of convenience inputs for certain representation types. The key is the opening character, and the table is a three-item array consisting of (1) an mw.ustring.gsub pattern which is anchored to the start and end of the string, with a single capture group that excludes the characters to be substituted, (2) a corresponding replacement pattern to be used with the pattern, and (3) the replacement opening character.]=] data.representation_subs = { ["<"] = {"^<(.*)>$", "⟨%1⟩", "⟨"}, ["/"] = {"^//(.*)//$", "⫽%1⫽", "⫽"}, } --[=[ This should list the language codes of all languages that have a pronunciation page in the appendix of the form ''Appendix:LANG pronunciation'', e.g. [[Appendix:Russian pronunciation]]. For these languages, the text "key" next to the generated pronunciation links to such pages; for other languages, it links to the "LANG phonology" page in Wikipedia (which may or may not exist). [[Module:IPA]] is responsible for this linking; see format_IPA_full().]=] data.langs_with_infopages = list_to_set{ "acw", "ady", "ang", "arc", "ba", "bg", "bo", "ca", "ceb", "cho", "cmn", "cs", "cv", "cy", "da", "de", "dsb", "dz", "egl", "egy", "el", "en", "enm", "eo", "es", "fa", "fi", "fo", "fr", "fy", "ga", "gd", "ghc", "gmh", "gmw-msc", "got", "he", "hi", "hrx", "hu", "hy", "id", "ii", "is", "it", "iu", "ja", "jbo", "ka", "kls", "ko", "kw", "la", "lb", "liv", "lt", "lv", "mdf", "mfe", "mic", "mk", "mns-nor", "ms", "mt", "mul", "my", "nan", "nci", "nl", "nn", "no", "nov", "nv", "pjt", "pl", "ps", "pt", "ro", "ru", "scn", "sco", "sga", "sh", "sl", "sq", "sv", "sw", "syc", "szl", "tg", "th", "tl", "tpw", "tr", "tyv", "ug", "uk", "vi", "vo", "wlm", "yi", "yrl", "yue", "zlw-mas" } --[=[ This should list the diphthongs of a language (in the form of Lua patterns), provided they do *NOT* contain semivowel symbols such as /j w ɰ ɥ/ or vowels with nonsyllabic diacritics such as /i̯ u̯/. For example, list /au/ or /aʊ/, but do not list /aw/ or /au̯/. The data in this table is used to count the number of syllables in a word. [[Module:syllables]] automatically knows how to correctly handle semivowel symbols and nonsyllabic diacritics. Any language listed here will automatically have categories of the form "LANG #-syllable words" generated. In addition, any language listed below under `langs_to_generate_syllable_count_categories` will also have such categories generated. NOTE: There are some additional languages that have these categories. For example: * Thai words have these categories added by [[Module:th-pron]].]=] data.diphthongs = { ["cs"] = { -- [[w:Czech phonology#Diphthongs]] "[aeo]u", }, ["de"] = { "a[ɪʊ]", "ɔ[ʏɪ]", }, ["en"] = { -- from [[Appendix:English pronunciation]] mostly, but /ʌɪ/ is from the OED "[aɑæeɛoɔʌ][ɪi]", "[ɑɒæo]e", "[əɐ]ʉ", "[aɒəoɔæ]ʊ", "æo", "[ɛeɪiɔʊʉ]ə", -- /iə/ is a diphthong in NZE, but a disyllabic sequence in GA. -- /ɪə/ is both a disyllabic sequence and a diphthong in old-fashioned RP. "[aʌ][ʊɪ]ə", -- May be a disyllabic sequence in some or all dialects? }, ["grc"] = { "[aeyo]i", "[ae]u", "[ɛɔa]ː[iu]", }, ["hrx"] = { "aɪ̯", "aʊ̯", "oɪ̯", "eʊ̯", }, ["is"] = { -- [[w:Icelandic phonology#Vowels]] "[aeɔœʏ]i", -- diphthongs as the module generates them "[ao]u", -- diphthongs as the module generates them "ø[iɪy]", -- additional forms that may occur; Wikipedia is oddly specific about the second element: ei and ai, but øɪ. }, ["it"] = { "[aeɛoɔu]i", "[aeɛioɔ]u", }, ["lb"] = { "[iu]ə", "[ɜoæɑ]ɪ", "[əæɑ]ʊ", }, ["lt"] = { "ɐɪ", "ɒʊ", "ɛɪ", "ɛʊ", "ʊɪ", "ɔɪ", "ɔʊ", -- Simple diphthongs (unstressed forms) "iɛ", "uɔ", -- Complex diphthongs "ɑˑɪ", "ɑˑʊ", "æˑɪ", "æˑʊ", "oˑɪ", -- Falling tone (acute) "ɐɪˑ", "ɒʊˑ", "ɛɪˑ", "ɛʊˑ", "ʊɪˑ", -- Rising tone (tilde) - lengthened second element -- Note: Mixed diphthongs (e.g., ɐlˑ, æˑn, ʊl, etc.) are omitted since they are inherently monosyllabic }, } --[=[ This should list any languages for which categories of the form "LANG #-syllable words", e.g. [[:Category:Russian 3-syllable words]], should be generated. Do not list languages here if they have an entry above under `data.diphthongs`; such languages are automatically added to this list.]=] local langs_to_generate_syllable_count_categories = list_to_set{ "ar", -- Arabic has diphthongs, but they are transcribed -- with semivowel symbols. "ary", -- Moroccan Arabic has diphthongs, but they are transcribed -- with semivowel symbols. "bg", -- Bulgarian has diphthongs with /j/ and marginally with /w/, -- but these are semivowels. "ca", -- Catalan has diphthongs, but they are generally transcribed using -- /w/ and /j/, so do not need to be listed (see [[w:Catalan language#Diphthongs and triphthongs]]. "eo", "es", -- Spanish has diphthongs, but they are transcribed with i̯ etc. "eu", -- Basque has dipthongs, but they are transcribed with i̯ and u̯. "fi", -- Finnish has diphthongs, but they are now automatically transcribed with -- the nonsyllabic diacritic "fr", -- French has diphthongs, but they are transcribed -- with semivowel symbols: [[w:French phonology#Glides and diphthongs]]. "hnn", "id", -- Indonesian has diphthongs, but they are transcribed with i̯ or /j/ etc. "ka", "kne", "kmr", "ku", "la", -- All diphthongs transcribed with e̯ or /j/ etc. "mk", "ms", -- Malay has diphthongs, but they are transcribed with i̯ or /j/ etc. "mt", -- Maltese has diphthongs, but they are transcribed -- with semivowel symbols. "pl", -- No diphthongs, properly speaking; sequences of a vowel and /w/ or /j/ though. "pt", -- Portuguese has diphthongs, but they are transcribed with i̯ or /j/ etc. "rsk", -- No diphthongs but there are sequences of vowel and /j/ or /w/. "ru", -- No diphthongs, properly speaking; sequences of a vowel and /j/ though. "sk", -- Slovak has rising diphthongs, /i̯e, i̯a, i̯u, u̯o/, which are probably always spelled with the nonsyllabic diacritic, so do not need to be listed. "sl", -- No diphthongs, properly speaking; sequences of a vowel, /j/ and /w/ though "sq", -- [[w:Albanian language#Vowels]] doesn't mention anything about diphthongs. "szy", -- All diphthongs are transcribed with /j/ or /w/ "tl", -- Tagalog has diphthongs, but they are transcribed with i̯ or /j/ etc "tsg", "ug", -- No diphthongs. } -- Also add languages listed under `data.diphthongs`. for langcode, _ in pairs(data.diphthongs) do langs_to_generate_syllable_count_categories[langcode] = true end data.langs_to_generate_syllable_count_categories = langs_to_generate_syllable_count_categories -- Languages to use the phonetic not phonemic notation to compute syllable counts. data.langs_to_use_phonetic_notation = list_to_set{ "bg", "es", "id", "la", "lt", "mk", "ms", "rsk", "ru", } -- Languages to use the phonetic or phonemic notation to compute syllable counts, whichever is available. data.langs_to_use_phonetic_or_phonemic_notation = list_to_set{ -- [[Module:is-IPA]] generates [...] but many manual pronuns use /.../. "is", } -- Non-standard or obsolete IPA symbols. data.nonstandard = { --[[ The following symbols consist of more than one character, so we can't put them in the line below. ]] "ɑ̢", "ɔ̗", "ɔ̖", "[?ƍσƺƪƞƛłščžǰǧǯẋⱻʚω∅ØȣᴀᴇⱻQKPT]" } -- See valid IPA characters at [[Module:IPA/data/symbols]]. data.phonemes = {} data.phonemes["dz"] = { "m", "n", "ŋ", "p", "t", "ʈ", "k", "pʰ", "tʰ", "ʈʰ", "kʰ", "t͡s", "t͡ɕ", "t͡sʰ", "t͡ɕʰ", "w", "s", "z", "ɬ", "l", "r", "ɕ", "ʑ", "j", "h", "ɑ", "e", "i", "o", "u", "ɑː", "eː", "ɛː", "iː", "oː", "øː", "uː", "yː", "ɑ˥", "e˥", "i˥", "o˥", "u˥", "ɑː˥", "eː˥", "ɛː˥", "iː˥", "oː˥", "øː˥", "uː˥", "yː˥", "m˥", "n˥", "ŋ˥", "p˥", "k˥", "k̚˥", "w˥", "l˥", "r˥", "ɕ˥", "j˥", ")˥", "ɑ˩", "e˩", "i˩", "o˩", "u˩", "ɑː˩", "eː˩", "ɛː˩", "iː˩", "oː˩", "øː˩", "uː˩", "yː˩", "m˩", "n˩", "ŋ˩", "p˩", "k˩", "k̚˩", "w˩", "l˩", "r˩", "ɕ˩", "j˩", ")˩", ".", ",", "-", } data.phonemes["eo"] = { "a", "b", "d", "d͡ʒ", "d͡z", "e", "f", "h", "i", "j", "k", "l", "m", "n", "o", "p", "r", "s", "t", "t͡s", "t͡ʃ", "u", "u̯", "v", "w", "x", "z", "ɡ", "ʃ", "ʒ", "ˈ", ".", " ", "-", "u̯", "i̯" } data.phonemes["hy"] = { "ɑ", "b", "ɡ", "d", "e", "z", "ə", "tʰ", "ʒ", "i", "l", "χ", "t͡s", "k", "h", "d͡z", "ʁ", "t͡ʃ", "m", "j", "n", "ʃ", "ɔ", "t͡ʃʰ", "p", "d͡ʒ", "r", "s", "v", "t", "ɾ", "t͡sʰ", "v", "pʰ", "kʰ", "o", "f", "ŋɡ", "ŋk", "ŋχ", "u", "œ", "ʏ", "ˈ", "ˌ", ".", " ", "ː", } data.phonemes["nl"] = { "m", "n", "ŋ", "p", "b", "t", "d", "k", "ɡ", "f", "v", "s", "z", "ʃ", "ʒ", "x", "ɣ", "ɦ", "ʋ", "l", "j", "r", "ɪ", "ʏ", "ɛ", "ə", "ɔ", "ɑ", "i", "iː", "y", "yː", "u", "uː", "eː", "øː", "oː", "ɛː", "œː", "ɔː", "aː", "ɛi̯", "œy̯", "ɔi̯", "ɑu̯", "ɑi̯", "iu̯", "yu̯", "ui̯", "eːu̯", "oːi̯", "aːi̯", "ˈ", "ˌ", ".", " ", "-", } data.phonemes["mt"] = { "m", "n", "p", "t", "k", "ʔ", "b", "d", "ɡ", "t͡s", "t͡ʃ", "d͡z", "d͡ʒ", "f", "s", "ʃ", "ħ", "v", "z", "ʒ", "ɣ", "l", "j", "w", "r", "ɪ", "ɛ", "ɔ", "a", "u", "ɛˤ", "ɔˤ", "aˤ", "əˤ", "ɛˤː", "ɔˤː", "aˤː", "əˤː", "ɪˤː", "iː", "ɪː", "ɛː", "ɔː", "aː", "uː", "ˈ", "ˌ", ".", " ", "‿", "-" } return data 1l5gqhh6kjp07cgmkg2dfudtmmreld7 มอดูล:de-headword 828 141377 5775205 5730635 2026-10-14T05:05:09Z Octahedron80 267 5775205 Scribunto text/plain local export = {} local pos_functions = {} local headword_utilities_module = "Module:headword utilities" local inflection_utilities_module = "Module:inflection utilities" local lang = require("Module:languages").getByCode("de") local legal_gender = { ["m"] = true, ["f"] = true, ["n"] = true, ["p"] = true, } local gender_names = { ["m"] = "เพศชาย", ["f"] = "เพศหญิง", ["n"] = "เพศกลาง", } local legal_verb_classes = { ["1"] = true, ["2"] = true, ["3"] = true, ["4"] = true, ["5"] = true, ["6"] = true, ["7"] = true, } local function ine(val) if val == "" then return nil else return val end end local function glossary_link(entry, text) text = text or entry return "[[ภาคผนวก:อภิธานศัพท์#" .. entry .. "|" .. text .. "]]" end local function track(page) require("Module:debug").track("de-headword/" .. page) return true end -- The main entry point. -- This is the only function that can be invoked from a template. function export.show(frame) local args = frame:getParent().args PAGENAME = mw.loadData("Module:headword/data").pagename local poscat = frame.args[1] or error("Part of speech has not been specified. Please pass parameter 1 to the module invocation.") -- หมวดหมู่เป็นภาษาไทย local poscat_th = require("Module:th-utilities").th_pos(poscat) local class = frame.args[2]; if class == "" then class = nil end local data = {lang = lang, pos_category = poscat_th, categories = {}, heads = {}, genders = {}, inflections = {}} if pos_functions[poscat_th] then pos_functions[poscat_th](class, args, data) end return require("Module:headword").full_headword(data) end local function old_adjectives(class, args, data) track("de-adj-old") local params = { [1] = {list = "comp"}, [2] = {list = "sup"}, ["head"] = {list = true}, } local args = require("Module:parameters").process(args, params) data.heads = args["head"] if args[1][1] == "-" then table.insert(data.inflections, {label = "เปรียบเทียบไม่ได้"}) table.insert(data.categories, "คำคุณศัพท์เปรียบเทียบไม่ได้ภาษาเยอรมัน") return end if #args[1] > 0 then for i, form in ipairs(args[1]) do args[1][i] = {term = (form == "er" and PAGENAME .. "er" or form), accel = {form = "ขั้นกว่า"}} end else args[1] = {request = true} track("de-adj lacking comparative") end args[1].label = glossary_link("ขั้นกว่า") table.insert(data.inflections, args[1]) if #args[2] > 0 then for i, form in ipairs(args[2]) do args[2][i] = { term = "am [[" .. ((form == "st" or form == "sten") and PAGENAME .. "sten" or (form == "est" or form == "esten") and PAGENAME .. "esten" or form) .. "]]", accel = {form = "ขั้นสุด"}} end else args[2] = {request = true} track("de-adj lacking superlative") end args[2].label = glossary_link("ขั้นสุด") table.insert(data.inflections, args[2]) end pos_functions["คำคุณศัพท์"] = function(class, args, data, proper) -- Compatibility with old calling convention, either if old= is given or any arg no longer supported is given. if ine(args.old) or ine(args[2]) or ine(args.comp1) or ine(args.comp2) or ine(args.comp3) or ine(args.sup1) or ine(args.sup2) or ine(args.sup3) or args[1] == "-" then return old_adjectives(class, args, data) else -- Remove this code once we convert to the new format. track("de-adj-need-old") end local alternant_multiword_spec = require("Module:de-adjective").do_generate_forms(args, nil, "from headword") data.heads = alternant_multiword_spec.args.head data.id = alternant_multiword_spec.args.id data.sort = alternant_multiword_spec.args.sort local function do_adj_form(slot, label, should_be_present, accel_form) local forms = alternant_multiword_spec.forms[slot] local retval if not forms then if not should_be_present then return end retval = {label = "ไม่มี" .. label} else retval = {label = label, accel = accel_form and {form = accel_form} or nil} local prev_footnotes for _, form in ipairs(forms) do local footnotes = form.footnotes if footnotes and prev_footnotes and require("Module:table").deepEquals(footnotes, prev_footnotes) then footnotes = nil end prev_footnotes = form.footnotes local quals, refs = require(inflection_utilities_module). convert_footnotes_to_qualifiers_and_references(footnotes) local term = form.form table.insert(retval, {term = term, q = quals, refs = refs, genders = genders}) end end table.insert(data.inflections, retval) end if alternant_multiword_spec.props.indecl then table.insert(data.inflections, {label = glossary_link("ผันรูปไม่ได้")}) if alternant_multiword_spec.props.predonly then table.insert(data.inflections, {label = "predicative only"}) elseif alternant_multiword_spec.props.nopred then table.insert(data.inflections, {label = "no predicative form"}) end else if alternant_multiword_spec.props.nopred then table.insert(data.inflections, {label = "no predicative form"}) end do_adj_form("str_nom_m", "strong nominative masculine singular", true) local should_comp_sup = alternant_multiword_spec.forms.comp_pred or alternant_multiword_spec.forms.sup_pred or alternant_multiword_spec.forms.comp_str_nom_m or alternant_multiword_spec.forms.sup_str_nom_m if not should_comp_sup then table.insert(data.inflections, {label = "เปรียบเทียบไม่ได้"}) else -- If there is no predicative comparative, but there is an attributive comparative, include it; -- otherwise, include the predicative comparative (if any). If there is no comparative but a superlative, -- this will display "no comparative". if not alternant_multiword_spec.forms.comp_pred and alternant_multiword_spec.forms.comp_str_nom_m then do_adj_form("comp_str_nom_m", "strong comparative nominative masculine singular", true) else do_adj_form("comp_pred", glossary_link("ขั้นกว่า"), true, "comparative") end -- Same as above, for the superlative. if not alternant_multiword_spec.forms.sup_pred and alternant_multiword_spec.forms.sup_str_nom_m then do_adj_form("sup_str_nom_m", "strong superlative nominative masculine singular", true) else do_adj_form("sup_pred", glossary_link("ขั้นสุด"), true, "superlative") end end end -- Add categories. for _, cat in ipairs(alternant_multiword_spec.categories) do table.insert(data.categories, cat) end -- Use the "linked" form of the lemma as the head if no head= explicitly given. data.no_redundant_head_cat = true -- always set since we have a different lemma linking algorithm from [[Module:headword]]. if #data.heads == 0 then data.heads = {} local lemmas = alternant_multiword_spec.forms.the_lemma or {} for _, lemma_obj in ipairs(lemmas) do local head = alternant_multiword_spec.args.nolinkhead and lemma_obj.form or require(headword_utilities_module).add_links_to_multiword_term(lemma_obj.form, {split_hyphen_when_space = alternant_multiword_spec.args.splithyph}) local quals, refs = require(inflection_utilities_module). convert_footnotes_to_qualifiers_and_references(lemma_obj.footnotes) table.insert(data.heads, {term = head, q = quals, refs = refs}) end end end local function old_nouns(class, args, data) track("de-noun-old") local params = { [1] = {list = "g", default = "?"}, [2] = {list = "gen"}, [3] = {list = "pl"}, [4] = {list = "dim"}, ["head"] = {list = true}, ["m"] = {list = true}, ["f"] = {list = true}, ["old"] = {type = "boolean"}, } local args = require("Module:parameters").process(args, params) data.heads = args["head"] -- Gender for _, g in ipairs(args[1]) do if legal_gender[g] then table.insert(data.genders, g) if g == "p" then table.insert(data.categories, "คำนามพหูพจน์เท่านั้นภาษาเยอรมัน") else table.insert(data.categories, "คำนาม" .. gender_names[g] .. "ภาษาเยอรมัน") end else if g == "m-s" or g == "f-s" or g == "n-s" or g == "m-p" or g == "f-p" or g == "n-p" then require("Module:debug").track("de-headword/genders") end table.insert(data.genders, "?") end end if args[1][1] ~= "p" then -- Genitive if not args[2][1] then if args[1][1] == "m" or args[1][1] == "n" then table.insert(args[2], PAGENAME .. "s") else table.insert(args[2], PAGENAME) end end for i, form in ipairs(args[2]) do args[2][i] = {term = form} end args[2].accel = {form = "gen|s"} args[2].label = "สัมพันธการก" table.insert(data.inflections, args[2]) -- Plural if not args[3][1] and data.pos_category == "คำนาม" then table.insert(args[3], PAGENAME .. "en") end if args[3][1] == "-" then table.insert(data.inflections, {label = "ไม่มีพหูพจน์"}) table.insert(data.categories, "คำนามนับไม่ได้ภาษาเยอรมัน") elseif #args[3] > 0 then for i, form in ipairs(args[3]) do args[3][i] = {term = form} end args[3].accel = {form = "p"} args[3].label = "พหูพจน์" table.insert(data.inflections, args[3]) end end -- Diminutive if #args[4] > 0 then for i, form in ipairs(args[4]) do args[4][i] = {term = form, genders = {"n"}} end args[4].accel = {form = "ตัวบอกความเล็ก", gender = "n"} args[4].label = "ตัวบอกความเล็ก" table.insert(data.inflections, args[4]) end -- Other gender if #args.f > 0 then args.f.label = "เพศหญิง" if args.f[1] == "in" then args.f[1] = PAGENAME .. "in" end if args.f[1] == PAGENAME .. "in" then args.f.accel = {form = "เพศหญิง", gender = "f"} args.f.label = "เพศหญิง" end table.insert(data.inflections, args.f) end if #args.m > 0 then args.m.label = "เพศชาย" table.insert(data.inflections, args.m) end end pos_functions["คำนาม"] = function(class, args, data, proper) -- Compatibility with old calling convention, either if old= is given or any arg no longer supported is given. if ine(args.old) or ine(args[2]) or ine(args[3]) or ine(args[4]) or ine(args.g1) or ine(args.g2) or ine(args.g3) or ine(args.gen1) or ine(args.gen2) or ine(args.gen3) or ine(args.pl1) or ine(args.pl2) or ine(args.pl3) then return old_nouns(class, args, data) end local m_de_noun = require("Module:de-noun") local alternant_multiword_spec = m_de_noun.do_generate_forms(args, nil, "from headword", proper) data.heads = alternant_multiword_spec.args.head data.genders = alternant_multiword_spec.genders data.id = alternant_multiword_spec.args.id data.sort = alternant_multiword_spec.args.sort if not proper then data.pos_category = alternant_multiword_spec.pos if alternant_multiword_spec.pos == "ปัจจัย" then table.insert(data.categories, "ปัจจัยสร้างคำนามภาษาเยอรมัน") end end local function get_nom_articles(alternant_multiword_spec) local articles = {} local m_table = require("Module:table") if alternant_multiword_spec.number == "pl" then m_table.insertIfNot(articles, "[[die]]") else for _, gender in ipairs(alternant_multiword_spec.genders) do if gender.spec == "m" then m_table.insertIfNot(articles, "[[der]]") elseif gender.spec == "f" then m_table.insertIfNot(articles, "[[die]]") elseif gender.spec == "n" then m_table.insertIfNot(articles, "[[das]]") else error("Internal error: Unrecognized gender '" .. gender.spec .. "'") end end end return table.concat(articles, "/") end local function get_gen_s_articles(alternant_multiword_spec) local articles = {} local m_table = require("Module:table") if alternant_multiword_spec.number == "pl" then error("Internal error: Should not be called on plural-only nouns") else for _, gender in ipairs(alternant_multiword_spec.genders) do if gender.spec == "m" or gender.spec == "n" then m_table.insertIfNot(articles, "[[des]]") elseif gender.spec == "f" then m_table.insertIfNot(articles, "[[der]]") else error("Internal error: Unrecognized gender '" .. gender.spec .. "'") end end end return table.concat(articles, "/") end local function do_noun_form(slot, label, should_be_present, accel_form, genders, prefix) local forms = alternant_multiword_spec.forms[slot] local retval if not forms then if not should_be_present then return end retval = {label = "ไม่มี" .. label} else retval = {label = label, accel = accel_form and {form = accel_form} or nil} local prev_footnotes for _, form in ipairs(forms) do local footnotes = form.footnotes if footnotes and prev_footnotes and require("Module:table").deepEquals(footnotes, prev_footnotes) then footnotes = nil end prev_footnotes = form.footnotes local quals, refs = require(inflection_utilities_module).convert_footnotes_to_qualifiers_and_references(footnotes) local term = form.form if prefix then if not term:find("[%[%]]") then term = "[[" .. term .. "]]" end term = prefix .. " " .. term end table.insert(retval, {term = term, q = quals, refs = refs, genders = genders}) end end table.insert(data.inflections, retval) end if proper then table.insert(data.inflections, {label = glossary_link("คำวิสามานยนาม")}) end local decl_types = alternant_multiword_spec.decl_type if decl_types and #decl_types > 0 then local decl_descs = {} for _, decl_type in ipairs(decl_types) do table.insert(decl_descs, glossary_link("การผันรูป" .. decl_type, decl_type)) end table.insert(data.inflections, {label = table.concat(decl_descs, " หรือ ")}) end local overall_adj = alternant_multiword_spec.props.overall_adj local article = alternant_multiword_spec.props.article local surname = alternant_multiword_spec.props.surname local langname = alternant_multiword_spec.props.langname local saw_f = false local saw_mn = false for _, gender in ipairs(alternant_multiword_spec.genders) do if gender.spec == "f" then saw_f = true elseif gender.spec == "m" or gender.spec == "n" then saw_mn = true end end local fem_sg_only = not saw_mn and alternant_multiword_spec.number ~= "pl" if not alternant_multiword_spec.first_noun and alternant_multiword_spec.first_adj then table.insert(data.inflections, {label = "เชิงคุณศัพท์"}) end if surname then table.insert(data.inflections, {label = "นามสกุล"}) end if langname then table.insert(data.inflections, {label = "ชื่อภาษา"}) end if alternant_multiword_spec.number == "pl" then table.insert(data.inflections, {label = glossary_link("พหูพจน์เท่านั้น")}) end if article then table.insert(data.inflections, {label = "โดยปกติชี้เฉพาะ"}) end if alternant_multiword_spec.number == "pl" then if overall_adj then do_noun_form("wk_nom_p", "พหูพจน์ชี้เฉพาะ", true, nil, nil, "[[die]]") end elseif surname then do_noun_form("gen_m_s", "สัมพันธการกเพศชาย", true) do_noun_form("gen_f_s", "สัมพันธการกเพศหญิง", true) do_noun_form("nom_p", "พหูพจน์", true) elseif langname then do_noun_form("gen_s", "สัมพันธการก", true) do_noun_form("nom_s_alt", "กรรตุการกแบบอื่น (ใช้กับคำกำกับนามชี้เฉพาะ)", true) do_noun_form("gen_s_alt", "สัมพันธการกแบบอื่น", true) -- There should be no plural; this will trigger the display of 'no plural'. do_noun_form("nom_p", "พหูพจน์", true) elseif overall_adj then if article then if not fem_sg_only then do_noun_form("wk_nom_s", "กรรตุการกชี้เฉพาะ", true, nil, nil, get_nom_articles(alternant_multiword_spec)) end do_noun_form("wk_gen_s", "สัมพันธการกชี้เฉพาะ", true, nil, nil, get_gen_s_articles(alternant_multiword_spec)) do_noun_form("wk_nom_p", "พหูพจน์ชี้เฉพาะ", not proper, nil, nil, "[[die]]") else do_noun_form("wk_nom_s", "กรรตุการกชี้เฉพาะ", true, nil, nil, get_nom_articles(alternant_multiword_spec)) do_noun_form("str_gen_s", "สัมพันธการก", true, nil, nil, not saw_f and "([[des]])" or nil) if saw_f then do_noun_form("wk_gen_s", "สัมพันธการกชี้เฉพาะ", true, nil, nil, get_gen_s_articles(alternant_multiword_spec)) end do_noun_form("str_nom_p", "พหูพจน์", not proper) do_noun_form("wk_nom_p", "พหูพจน์ชี้เฉพาะ", nil, nil, nil, "[[die]]") end else if article then do_noun_form("gen_s", "สัมพันธการกชี้เฉพาะ", true, nil, nil, get_gen_s_articles(alternant_multiword_spec)) do_noun_form("nom_p", "พหูพจน์ชี้เฉพาะ", not proper, nil, nil, "[[die]]") else do_noun_form("gen_s", "สัมพันธการก", true) do_noun_form("nom_p", "พหูพจน์", not proper) end end -- FIXME: Should we include the article in the singular equivalent if .article is given? I have no examples to go by. do_noun_form("sg", "เอกพจน์") do_noun_form("dim", "ตัวบอกความเล็ก", nil, "ตัวบอกความเล็ก", {"n"}, article and "[[das]]" or nil) do_noun_form("m", "เพศชาย", nil, nil, nil, article and "[[der]]" or nil) do_noun_form("f", "เพศหญิง", nil, "เพศหญิง", nil, article and "[[die]]" or nil) do_noun_form("n", "เพศกลาง", nil, "เพศกลาง", nil, article and "[[das]]" or nil) -- Add categories. for _, cat in ipairs(alternant_multiword_spec.categories) do table.insert(data.categories, cat) end -- Use the "linked" form of the lemma as the head if no head= explicitly given. data.no_redundant_head_cat = true -- always set since we have a different lemma linking algorithm from [[Module:headword]]. if #data.heads == 0 then data.heads = {} local lemmas = m_de_noun.get_lemmas(alternant_multiword_spec, "linked variant") for _, lemma_obj in ipairs(lemmas) do local head = alternant_multiword_spec.args.nolinkhead and lemma_obj.form or require(headword_utilities_module).add_links_to_multiword_term(lemma_obj.form, {split_hyphen_when_space = alternant_multiword_spec.args.splithyph}) if article and (not overall_adj or fem_sg_only) then head = get_nom_articles(alternant_multiword_spec) .. " " .. head end local quals, refs = require(inflection_utilities_module). convert_footnotes_to_qualifiers_and_references(lemma_obj.footnotes) table.insert(data.heads, {term = head, q = quals, refs = refs}) end end end pos_functions["คำวิสามานยนาม"] = function(class, args, data) return pos_functions["คำนาม"](class, args, data, "คำวิสามานยนาม") end pos_functions["คำกริยา"] = function(class, args, data) if args[2] then -- old-style local params = { [1] = {list = "pres", required = true}, ["pres_qual"] = {list = "pres\1_qual", allow_holes = true}, [2] = {list = "past", required = true}, ["past_qual"] = {list = "past\1_qual", allow_holes = true}, [3] = {list = "pp", required = true}, ["pp_qual"] = {list = "pp\1_qual", allow_holes = true}, [4] = {list = "pastsubj"}, ["pastsubj_qual"] = {list = "pastsubj\1_qual", allow_holes = true}, ["aux"] = {list = true}, ["aux_qual"] = {list = "aux\1_qual", allow_holes = true}, ["head"] = {list = true}, ["class"] = {list = true}, } local args = require("Module:parameters").process(args, params) data.heads = args["head"] local function collect_forms(label, accel_form, forms, qualifiers) if forms[1] == "-" then return {label = "ไม่มี" .. label} else -- Disable this for now as deduplication between headword and conjugation table doesn't work correctly. -- local into_table = accel_form and {label = label, accel = {form = accel_form}} or {label = label} local into_table = {label = label} for i, form in ipairs(forms) do table.insert(into_table, {term = form, q = qualifiers[i] and {qualifiers[i]} or nil}) end return into_table end end if #args.class > 0 then local class_descs, cats = require("Module:de-verb").process_verb_classes(args.class) for _, cats in ipairs(cats) do table.insert(data.categories, cats) end table.insert(data.inflections, {label = require("Module:table").serialCommaJoin(class_descs, {conj = "หรือ"})}) end table.insert(data.inflections, collect_forms("บุรุษที่สามเอกพจน์ปัจจุบันกาล", "3|s|pres", args[1], args.pres_qual)) table.insert(data.inflections, collect_forms("อดีตกาลสิ้นสุด", "1//3|s|pret", args[2], args.past_qual)) table.insert(data.inflections, collect_forms("รูปกริยาขยายอดีตกาล", "perf|part", args[3], args.pp_qual)) if #args[4] > 0 then table.insert(data.inflections, collect_forms("อดีตกาลมาลาสมมุติ", "1//3|s|sub|II", args[4], args.pastsubj_qual)) end if #args.aux > 0 then table.insert(data.inflections, collect_forms("คำกริยาช่วย", nil, args.aux, args.aux_qual)) end return end local function get_headword_inflection(forms, label, accel_form) if forms then -- Disable this for now as deduplication between heardword and conjugation table doesn't work correctly. -- local inflection = accel_form and {label = label, accel = {form = accel_form}} or {label = label} local inflection = {label = label} for _, form in ipairs(forms) do local qualifiers if form.footnotes then qualifiers = {} for _, footnote in ipairs(form.footnotes) do footnote = footnote:gsub("^%[(.*)%]$", "%1") table.insert(qualifiers, footnote) end end table.insert(inflection, {term = form.form, q = qualifiers}) end return inflection elseif label then return {label = "ไม่มี" .. label} else return {} end end local alternant_multiword_spec = require("Module:de-verb").do_generate_forms(args, "from headword") for _, cat in ipairs(alternant_multiword_spec.categories) do table.insert(data.categories, cat) end table.insert(data.inflections, {label = table.concat(alternant_multiword_spec.verb_types, " หรือ ")}) if #data.heads == 0 then data.no_redundant_head_cat = true for _, head in ipairs(alternant_multiword_spec.forms.infinitive_linked) do table.insert(data.heads, head.form) end end table.insert(data.inflections, get_headword_inflection(alternant_multiword_spec.forms.pres_3s, "บุรุษที่สามเอกพจน์ปัจจุบันกาล", "3|s|pres")) local pret_3s = alternant_multiword_spec.forms.pret_3s table.insert(data.inflections, get_headword_inflection(pret_3s, "อดีตกาลสิ้นสุด", "1//3|s|pret")) table.insert(data.inflections, get_headword_inflection(alternant_multiword_spec.forms.perf_part, "รูปกริยาขยายอดีตกาล", "perf|part")) -- See if we need the past subjunctive, i.e. there exist past subjunctive forms whose stem is not the -- same as some past tense form. To facilitate comparison, we truncate final -e in both preterite 3s -- and past subjunctive 3s, to handle cases like subjunctive 'ginge aus' vs. preterite 'ging aus'. -- We need to compare 3s forms (and not e.g. 3p forms, where the issue with truncating -e doesn't -- occur) so we work correctly with impersonal verbs. local need_past_subj local truncated_pret_3s_forms = {} if pret_3s then for _, form in ipairs(pret_3s) do local truncated_form = form.form:gsub("e$", ""):gsub("e ", " ") -- discard 2nd retval table.insert(truncated_pret_3s_forms, truncated_form) end end local subii_3s = alternant_multiword_spec.forms.subii_3s local truncated_subii_3s_forms = {} if subii_3s then for _, form in ipairs(subii_3s) do local truncated_form = form.form:gsub("e$", ""):gsub("e ", " ") -- discard 2nd retval table.insert(truncated_subii_3s_forms, truncated_form) end end for _, past_subj_form in ipairs(truncated_subii_3s_forms) do local saw_same = false for _, pret_3s_form in ipairs(truncated_pret_3s_forms) do if past_subj_form == pret_3s_form then saw_same = true break end end if not saw_same then need_past_subj = true break end end if need_past_subj then table.insert(data.inflections, get_headword_inflection(subii_3s, "อดีตกาลมาลาสมมุติ", "1//3|s|sub|II")) end local auxes = alternant_multiword_spec.forms.aux table.insert(data.inflections, get_headword_inflection(auxes, "คำกริยาช่วย")) end return export fu3gnfolv9gs9cnfv0ysdk5hhg9n4jw มอดูล:IPA/templates 828 142314 5775269 3139662 2026-10-14T10:00:15Z Octahedron80 267 5775269 Scribunto text/plain local export = {} local m_IPA = require("Module:IPA") local parameter_utilities_module = "Module:parameter utilities" local function track(template, page) require("Module:debug/track")(template .. "/" .. page) return true end -- Used for [[Template:IPA]]. function export.IPA(frame) local parent_args = frame:getParent().args -- Track uses of n so they can be converted to ref. -- Track uses of qual so they can be converted to q. for k, v in pairs(parent_args) do if type(k) == "string" and k:find("^qual%d*$") then track("IPA", "q") end end local include_langname = frame.args.include_langname local compat = parent_args.lang local offset = compat and 0 or 1 local lang_arg = compat and "lang" or 1 local params = { [lang_arg] = {required = true, type = "language", default = "en"}, [1 + offset] = {list = true, disallow_holes = true}, -- Deprecated; don't use in new code. ["qual"] = {list = true, separate_no_index = true, alias_of = "q"}, ["nocount"] = {type = "boolean"}, ["nocat"] = {type = "boolean"}, ["sort"] = {}, } local m_param_utils = require(parameter_utilities_module) local param_mods = m_param_utils.construct_param_mods { {group = {"ref", "a", "q"}}, {group = "link", include = {"t", "gloss", "pos"}}, } local items, args = m_param_utils.parse_list_with_inline_modifiers_and_separate_params { params = params, param_mods = param_mods, raw_args = parent_args, termarg = 1 + offset, term_dest = "pron", track_module = "IPA", } local lang = args[lang_arg] for _, item in ipairs(items) do require("Module:IPA/tracking").run_tracking(item.pron, lang) end -- Tracking for direct (manual) uses of {{IPA}} instead of lang-specific automatic templates such as {{is-IPA}}. -- [[Category:LANG terms with IPA pronunciation]] tracks terms with IPA pronunciation added in any manner, -- but doesn't distinguish manual {{IPA}} calls from calls to the automatic template. track("IPA", "manual") track("IPA", "manual/" .. lang:getCode()) local data = { lang = lang, items = items, no_count = args.nocount, nocat = args.nocat, sort_key = args.sort, include_langname = include_langname, q = args.q.default, qq = args.qq.default, a = args.a.default, aa = args.aa.default, } return m_IPA.format_IPA_full(data) end -- Used for [[Template:IPAchar]]. function export.IPAchar(frame) local parent_args = frame.getParent and frame:getParent().args or frame -- Track uses of n so they can be converted to ref. -- Track uses of qual so they can be converted to q. for k, v in pairs(parent_args) do if type(k) == "string" and k:find("^n%d*$") then track("IPAchar", "n") end if type(k) == "string" and k:find("^qual%d*$") then track("IPAchar", "q") end end local params = { [1] = {list = true, disallow_holes = true}, -- FIXME, remove this. ["lang"] = {}, -- This parameter is not used and does nothing, but is allowed for futureproofing. } local m_param_utils = require(parameter_utilities_module) local param_mods = m_param_utils.construct_param_mods { -- It doesn't really make sense to have separate overall a=/aa=/q=/qq= for {{IPAchar}}, which doesn't format a -- whole line but just individual pronunciations. Instead they are associated with the first item. {group = {"ref", "a", "q"}, separate_no_index = false}, -- Deprecated; don't use in new code. {param = "qual", alias_of = "q"}, } local items, args = m_param_utils.parse_list_with_inline_modifiers_and_separate_params { params = params, param_mods = param_mods, raw_args = parent_args, termarg = 1, term_dest = "pron", track_module = "IPAchar", } -- [[Special:WhatLinksHere/Wiktionary:Tracking/IPAchar/lang]] if args.lang then track("IPAchar", "lang") end -- Format return m_IPA.format_IPA_multiple(nil, items) end function export.XSAMPA(frame) local params = { [1] = { required = true }, } local args = require("Module:parameters").process(frame:getParent().args, params) return m_IPA.XSAMPA_to_IPA(args[1] or "[Eg'zA:mp5=]") end -- Used by [[Template:X2IPA]] function export.X2IPAtemplate(frame) local parent_args = frame.getParent and frame:getParent().args or frame local compat = parent_args["lang"] local offset = compat and 0 or 1 local params = { [compat and "lang" or 1] = {required = true, default = "und"}, [1 + offset] = {list = true, allow_holes = true}, ["ref"] = {list = true, allow_holes = true}, ["a"] = {list = true, allow_holes = true, separate_no_index = true}, ["aa"] = {list = true, allow_holes = true, separate_no_index = true}, ["q"] = {list = true, allow_holes = true, separate_no_index = true}, ["qq"] = {list = true, allow_holes = true, separate_no_index = true}, ["qual"] = {list = true, allow_holes = true}, ["nocount"] = {type = "boolean"}, ["sort"] = {}, } local args = require("Module:parameters").process(parent_args, params) local m_XSAMPA = require("Module:IPA/X-SAMPA") local pronunciations, refs, a, aa, q, qq, qual, lang = args[1 + offset], args.ref, args.a, args.aa, args.q, args.qq, args.qual, args[compat and "lang" or 1] local output = {} table.insert(output, "{{IPA") table.insert(output, "|" .. lang) if a.default then table.insert(output, "|a=" .. a.default) end if q.default then table.insert(output, "|q=" .. q.default) end for i = 1, math.max(pronunciations.maxindex, refs.maxindex, a.maxindex, aa.maxindex, q.maxindex, qq.maxindex, qual.maxindex) do if pronunciations[i] then table.insert(output, "|" .. m_XSAMPA.XSAMPA_to_IPA(pronunciations[i])) end if a[i] then table.insert(output, "|a" .. i .. "=" .. a[i]) end if aa[i] then table.insert(output, "|aa" .. i .. "=" .. aa[i]) end if q[i] then table.insert(output, "|q" .. i .. "=" .. q[i]) end if qq[i] then table.insert(output, "|qq" .. i .. "=" .. qq[i]) end if refs[i] then table.insert(output, "|ref" .. i .. "=" .. refs[i]) end if qual[i] then table.insert(output, "|qual" .. i .. "=" .. qual[i]) end end if aa.default then table.insert(output, "|aa=" .. aa.default) end if qq.default then table.insert(output, "|qq=" .. qq.default) end if args.nocount then table.insert(output, "|nocount=1") end if args.sort then table.insert(output, "|sort=" .. args.sort) end table.insert(output, "}}") return table.concat(output) end -- Used by [[Template:X2IPAchar]] function export.X2IPAchar(frame) local params = { [1] = { list = true, allow_holes = true }, ["ref"] = {list = true, allow_holes = true}, ["q"] = {list = true, allow_holes = true, require_index = true}, ["qq"] = {list = true, allow_holes = true, require_index = true}, ["qual"] = { list = true, allow_holes = true }, -- FIXME, remove this. ["lang"] = {}, } local args = require("Module:parameters").process(frame:getParent().args, params) -- [[Special:WhatLinksHere/Wiktionary:Tracking/X2IPAchar/lang]] if args.lang then track("X2IPAchar", "lang") end local m_XSAMPA = require("Module:IPA/X-SAMPA") local pronunciations, refs, q, qq, qual, lang = args[1], args.ref, args.q, args.qq, args.qual, args.lang local output = {} table.insert(output, "{{IPAchar") for i = 1, math.max(pronunciations.maxindex, refs.maxindex, q.maxindex, qq.maxindex, qual.maxindex) do if pronunciations[i] then table.insert(output, "|" .. m_XSAMPA.XSAMPA_to_IPA(pronunciations[i])) end if q[i] then table.insert(output, "|q" .. i .. "=" .. q[i]) end if qq[i] then table.insert(output, "|qq" .. i .. "=" .. qq[i]) end if qual[i] then table.insert(output, "|qual" .. i .. "=" .. qual[i]) end if refs[i] then table.insert(output, "|ref" .. i .. "=" .. refs[i]) end end if lang then table.insert(output, "|lang=" .. lang) end table.insert(output, "}}") return table.concat(output) end -- Used by [[Template:x2rhymes]] function export.X2rhymes(frame) local parent_args = frame.getParent and frame:getParent().args or frame local compat = parent_args["lang"] local offset = compat and 0 or 1 local params = { [compat and "lang" or 1] = {required = true, default = "und"}, [1 + offset] = {required = true, list = true, allow_holes = true}, } local args = require("Module:parameters").process(parent_args, params) local m_XSAMPA = require("Module:IPA/X-SAMPA") local pronunciations, lang = args[1 + offset], args[compat and "lang" or 1] local output = {} table.insert(output, "{{rhymes") table.insert(output, "|" .. lang) for i = 1, pronunciations.maxindex do if pronunciations[i] then table.insert(output, "|" .. m_XSAMPA.XSAMPA_to_IPA(pronunciations[i])) end end table.insert(output, "}}") return table.concat(output) end -- Used for [[Template:enPR]]. function export.enPR(frame) local parent_args = frame:getParent().args local params = { [1] = {list = true, disallow_holes = true}, } local m_param_utils = require(parameter_utilities_module) local param_mods = m_param_utils.construct_param_mods { {group = {"q", "a", "ref"}}, } local items, args = m_param_utils.parse_list_with_inline_modifiers_and_separate_params { params = params, param_mods = param_mods, raw_args = parent_args, termarg = 1, term_dest = "pron", track_module = "enPR", } local data = { items = items, q = args.q.default, qq = args.qq.default, a = args.a.default, aa = args.aa.default, } return m_IPA.format_enPR_full(data) end return export 4d032bpwth3egruuulwhrdv60ldftme มอดูล:IPA/data/symbols 828 142319 5775271 5690262 2026-10-14T10:01:22Z Octahedron80 267 5775271 Scribunto text/plain local data = {} --[=[ Valid IPA symbols. Currently almost all values of "title" and "link" keys are just the comments that were used in [[Module:IPA]]. The "link" fields should be checked (those that start with an uppercase letter are checked). ]=] --[=[ local phones = {} -- Vowels. phones["i"] = { close = true, front = true, unrounded = true, vowel = true, } phones["e"] = { ["close-mid"] = true, front = true, unrounded = true, vowel = true, } phones["ɛ"] = { ["open-mid"] = true, front = true, unrounded = true, vowel = true, } phones["æ"] = { ["near-open"] = true, front = true, unrounded = true, vowel = true, } phones["a"] = { open = true, front = true, unrounded = true, vowel = true, } phones["y"] = { close = true, front = true, rounded = true, vowel = true, } phones["ø"] = { ["close-mid"] = true, front = true, rounded = true, vowel = true, } phones["œ"] = { ["open-mid"] = true, front = true, rounded = true, vowel = true, } phones["ɶ"] = { open = true, front = true, rounded = true, vowel = true, } phones["ɪ"] = { ["near-close"] = true, ["near-front"] = true, unrounded = true, vowel = true, } phones["ʏ"] = { ["near-close"] = true, ["near-front"] = true, rounded = true, vowel = true, } phones["ɨ"] = { close = true, central = true, unrounded = true, vowel = true, } phones["ᵻ"] = { ["near-close"] = true, central = true, unrounded = true, vowel = true, } phones["ɘ"] = { ["close-mid"] = true, central = true, unrounded = true, vowel = true, } phones["ɜ"] = { ["open-mid"] = true, central = true, unrounded = true, vowel = true, } phones["ɝ"] = { rhotic = true, ["open-mid"] = true, central = true, unrounded = true, vowel = true, } phones["ə"] = { mid = true, central = true, vowel = true, } phones["ɚ"] = { rhotic = true, mid = true, central = true, vowel = true, } phones["ɐ"] = { ["near-open"] = true, central = true, vowel = true, } phones["ʉ"] = { close = true, central = true, rounded = true, vowel = true, } phones["ᵿ"] = { ["near-close"] = true, central = true, rounded = true, vowel = true, } phones["ɵ"] = { ["close-mid"] = true, central = true, rounded = true, vowel = true, } phones["ɞ"] = { ["open-mid"] = true, central = true, rounded = true, vowel = true, } phones["ʊ"] = { ["near-close"] = true, ["near-back"] = true, rounded = true, vowel = true, } phones["ɯ"] = { close = true, back = true, unrounded = true, vowel = true, } phones["ɤ"] = { ["close-mid"] = true, back = true, unrounded = true, vowel = true, } phones["ʌ"] = { ["open-mid"] = true, back = true, unrounded = true, vowel = true, } phones["ɑ"] = { open = true, back = true, unrounded = true, vowel = true, } phones["u"] = { close = true, back = true, rounded = true, vowel = true, } phones["o"] = { ["close-mid"] = true, back = true, rounded = true, vowel = true, } phones["ɔ"] = { ["open-mid"] = true, back = true, rounded = true, vowel = true, } phones["ɒ"] = { open = true, back = true, rounded = true, vowel = true, } -- Nasals. phones["m"] = { voiced = true, bilabial = true, nasal = true, } phones["ɱ"] = { voiced = true, labiodental = true, nasal = true, } phones["n"] = { voiced = true, alveolar = true, nasal = true, } phones["ɳ"] = { voiced = true, retroflex = true, nasal = true, } phones["ɲ"] = { voiced = true, palatal = true, nasal = true, } phones["ŋ"] = { voiced = true, velar = true, nasal = true, } phones["𝼇"] = { voiced = true, velodorsal = true, nasal = true, } phones["ɴ"] = { voiced = true, uvular = true, nasal = true, } -- Plosives. phones["p"] = { voiceless = true, bilabial = true, plosive = true, } phones["b"] = { voiced = true, bilabial = true, plosive = true, } phones["t"] = { voiceless = true, alveolar = true, plosive = true, } phones["d"] = { voiced = true, alveolar = true, plosive = true, } phones["ʈ"] = { voiceless = true, retroflex = true, plosive = true, } phones["ɖ"] = { voiced = true, retroflex = true, plosive = true, } phones["c"] = { voiceless = true, palatal = true, plosive = true, } phones["ɟ"] = { voiced = true, palatal = true, plosive = true, } phones["k"] = { voiceless = true, velar = true, plosive = true, } phones["ɡ"] = { voiced = true, velar = true, plosive = true, } phones["𝼃"] = { voiceless = true, velodorsal = true, plosive = true, } phones["𝼁"] = { voiced = true, velodorsal = true, plosive = true, } phones["q"] = { voiceless = true, uvular = true, plosive = true, } phones["ɢ"] = { voiced = true, uvular = true, plosive = true, } phones["ꞯ"] = { voiceless = true, ["upper-pharyngeal"] = true, plosive = true, } phones["𝼂"] = { voiced = true, ["upper-pharyngeal"] = true, plosive = true, } phones["ʡ"] = { epiglottal = true, plosive = true, } phones["ʔ"] = { glottal = true, plosive = true, } -- Fricatives. phones["ɸ"] = { voiceless = true, bilabial = true, fricative = true, } phones["β"] = { voiced = true, bilabial = true, fricative = true, } phones["ʍ"] = { voiceless = true, ["labial-velar"] = true, fricative = true, } phones["f"] = { voiceless = true, labiodental = true, fricative = true, } phones["v"] = { voiced = true, labiodental = true, fricative = true, } phones["θ"] = { voiceless = true, dental = true, ["non-sibilant"] = true, fricative = true, } phones["ð"] = { voiced = true, dental = true, ["non-sibilant"] = true, fricative = true, } phones["s"] = { voiceless = true, alveolar = true, sibilant = true, fricative = true, } phones["z"] = { voiced = true, alveolar = true, sibilant = true, fricative = true, } phones["ɬ"] = { voiceless = true, alveolar = true, lateral = true, fricative = true, } phones["ɮ"] = { voiced = true, alveolar = true, lateral = true, fricative = true, } phones["ʃ"] = { voiceless = true, postalveolar = true, sibilant = true, fricative = true, } phones["ʒ"] = { voiced = true, postalveolar = true, sibilant = true, fricative = true, } phones["ʂ"] = { voiceless = true, retroflex = true, sibilant = true, fricative = true, } phones["ʐ"] = { voiced = true, retroflex = true, sibilant = true, fricative = true, } phones["ꞎ"] = { voiceless = true, retroflex = true, lateral = true, fricative = true, } phones["𝼅"] = { voiced = true, retroflex = true, lateral = true, fricative = true, } phones["ɕ"] = { voiceless = true, ["alveolo-palatal"] = true, sibilant = true, fricative = true, } phones["ʑ"] = { voiced = true, ["alveolo-palatal"] = true, sibilant = true, fricative = true, } phones["ç"] = { voiceless = true, palatal = true, fricative = true, } phones["ʝ"] = { voiced = true, palatal = true, fricative = true, } phones["𝼆"] = { voiceless = true, palatal = true, lateral = true, fricative = true, } phones["ɧ"] = { voiceless = true, ["palatal-velar"] = true, fricative = true, } phones["x"] = { voiceless = true, velar = true, fricative = true, } phones["ɣ"] = { voiced = true, velar = true, fricative = true, } phones["𝼄"] = { voiceless = true, velar = true, lateral = true, fricative = true, } phones["ʩ"] = { voiceless = true, velopharyngeal = true, fricative = true, } phones["χ"] = { voiceless = true, uvular = true, fricative = true, } phones["ʁ"] = { voiced = true, uvular = true, fricative = true, } phones["ħ"] = { voiceless = true, pharyngeal = true, fricative = true, } phones["ʕ"] = { voiced = true, pharyngeal = true, fricative = true, } phones["ʜ"] = { voiceless = true, epiglottal = true, fricative = true, } phones["ʢ"] = { voiced = true, epiglottal = true, fricative = true, } phones["h"] = { voiceless = true, glottal = true, fricative = true, } phones["ɦ"] = { voiced = true, glottal = true, fricative = true, } -- Approximants. phones["ʋ"] = { voiced = true, labiodental = true, approximant = true, } phones["ɥ"] = { voiced = true, ["labial–palatal"] = true, approximant = true, } phones["w"] = { voiced = true, ["labial–velar"] = true, approximant = true, } phones["ɹ"] = { voiced = true, alveolar = true, approximant = true, } phones["ꭨ"] = { ["velarized or pharyngealized"] = true, voiced = true, alveolar = true, approximant = true, } phones["l"] = { voiced = true, alveolar = true, lateral = true, approximant = true, } phones["ɫ"] = { ["velarized or pharyngealized"] = true, voiced = true, alveolar = true, lateral = true, approximant = true, } phones["ɻ"] = { voiced = true, retroflex = true, approximant = true, } phones["ɭ"] = { voiced = true, retroflex = true, lateral = true, approximant = true, } phones["j"] = { voiced = true, palatal = true, approximant = true, } phones["ʎ"] = { voiced = true, palatal = true, lateral = true, approximant = true, } phones["ɰ"] = { voiced = true, velar = true, approximant = true, } phones["ʟ"] = { voiced = true, velar = true, lateral = true, approximant = true, } -- Flaps. phones["ⱱ"] = { voiced = true, labiodental = true, flap = true, } phones["ɾ"] = { voiced = true, alveolar = true, flap = true, } phones["ɺ"] = { voiced = true, alveolar = true, lateral = true, flap = true, } phones["ɽ"] = { voiced = true, retroflex = true, flap = true, } phones["𝼈"] = { voiced = true, retroflex = true, lateral = true, flap = true, } -- Trills. phones["ʙ"] = { voiced = true, bilabial = true, trill = true, } phones["r"] = { voiced = true, alveolar = true, trill = true, } phones["𝼀"] = { voiceless = true, velopharyngeal = true, trill = true, } phones["ʀ"] = { voiced = true, uvular = true, trill = true, } phones["ᴙ"] = { voiced = true, pharyngeal = true, trill = true, } -- Clicks. phones["ʘ"] = { bilabial = true, click = true, } phones["ǀ"] = { dental = true, click = true, } phones["ǃ"] = { alveolar = true, click = true, } phones["𝼊"] = { retroflex = true, click = true, } phones["ǂ"] = { palatal = true, click = true, } phones["ʞ"] = { velar = true, click = true, } phones["ǁ"] = { lateral = true, click = true, } -- Implosives. phones["ɓ"] = { voiced = true, bilabial = true, implosive = true, } phones["ɗ"] = { voiced = true, alveolar = true, implosive = true, } phones["ᶑ"] = { voiced = true, retroflex = true, implosive = true, } phones["ʄ"] = { voiced = true, palatal = true, implosive = true, } phones["ɠ"] = { voiced = true, velar = true, implosive = true, } phones["ʛ"] = { voiced = true, uvular = true, implosive = true, } -- Percussives. phones["ʬ"] = { bilabial = true, percussive = true, } phones["ʭ"] = { bidental = true, percussive = true, } phones["¡"] = { sublaminal = true, ["lower-alveolar"] = true, percussive = true, } ]=] local u = require("Module:string/char") data[1] = { -- PULMONIC CONSONANTS -- nasal ["m"] = { title = "bilabial nasal", link = "w:Bilabial nasal", }, ["ɱ"] = { title = "labiodental nasal", link = "w:Labiodental nasal", }, ["n"] = { title = "alveolar nasal", link = "w:Alveolar nasal", }, ["ɳ"] = { title = "retroflex nasal", link = "w:Retroflex nasal", }, ["ɲ"] = { title = "palatal nasal", link = "w:Palatal nasal", }, ["ŋ"] = { title = "velar nasal", link = "w:Velar nasal", }, ["ɴ"] = { title = "uvular nasal", link = "w:Uvular nasal", }, -- plosive ["p"] = { title = "voiceless bilabial plosive", link = "w:Voiceless bilabial stop", }, ["b"] = { title = "voiced bilabial plosive", link = "w:Voiced bilabial stop", }, ["t"] = { title = "voiceless alveolar plosive", link = "w:Voiceless alveolar stop", }, ["d"] = { title = "voiced alveolar plosive", link = "w:Voiced alveolar stop", }, ["ʈ"] = { title = "voiceless retroflex plosive", link = "w:Voiceless retroflex stop", }, ["ɖ"] = { title = "voiced retroflex plosive", link = "w:Voiced retroflex stop", }, ["c"] = { title = "voiceless palatal plosive", link = "w:Voiceless palatal stop", }, ["ɟ"] = { title = "voiced palatal plosive", link = "w:Voiced palatal stop", }, ["k"] = { title = "voiceless velar plosive", link = "w:Voiceless velar stop", }, ["ɡ"] = { title = "voiced velar plosive", link = "w:Voiced velar stop", }, ["q"] = { title = "voiceless uvular plosive", link = "w:Voiceless uvular stop", }, ["ɢ"] = { title = "voiced uvular plosive", link = "w:Voiced uvular stop", }, ["ʡ"] = { title = "epiglottal plosive", link = "w:Epiglottal stop", }, ["ʔ"] = { title = "glottal stop", link = "w:Glottal stop", }, -- fricative ["ɸ"] = { title = "voiceless bilabial fricative", link = "w:Voiceless bilabial fricative", }, ["β"] = { title = "voiced bilabial fricative", link = "w:Voiced bilabial fricative", }, ["f"] = { title = "voiceless labiodental fricative", link = "w:Voiceless labiodental fricative", }, ["v"] = { title = "voiced labiodental fricative", link = "w:Voiced labiodental fricative", }, ["θ"] = { title = "voiceless dental fricative", link = "w:Voiceless dental fricative", }, ["ð"] = { title = "voiced dental fricative", link = "w:Voiced dental fricative", }, ["s"] = { title = "voiceless alveolar fricative", link = "w:Voiceless alveolar fricative", }, ["z"] = { title = "voiced alveolar fricative", link = "w:Voiced alveolar fricative", }, ["ʃ"] = { title = "voiceless postalveolar fricative", link = "w:Voiceless palato-alveolar sibilant", }, ["ʒ"] = { title = "voiced postalveolar fricative", link = "w:Voiced palato-alveolar sibilant", }, ["ʂ"] = { title = "voiceless retroflex fricative", link = "w:Voiceless retroflex sibilant", }, ["ʐ"] = { title = "voiced retroflex fricative", link = "w:Voiced retroflex sibilant", }, ["ɕ"] = { title = "voiceless alveolo-palatal fricative", link = "w:Voiceless alveolo-palatal sibilant", }, ["ʑ"] = { title = "voiced alveolo-palatal fricative", link = "w:Voiced alveolo-palatal sibilant", }, ["ç"] = { title = "voiceless palatal fricative", link = "w:Voiceless palatal fricative", }, ["ʝ"] = { title = "voiced palatal fricative", link = "w:Voiced palatal fricative", }, ["x"] = { title = "voiceless velar fricative", link = "w:Voiceless velar fricative", }, ["ɣ"] = { title = "voiced velar fricative", link = "w:Voiced velar fricative", }, ["χ"] = { title = "voiceless uvular fricative", link = "w:Voiceless uvular fricative", }, ["ʁ"] = { title = "voiced uvular fricative", link = "w:Voiced uvular fricative", }, ["ħ"] = { title = "voiceless pharyngeal fricative", link = "w:Voiceless pharyngeal fricative", }, ["ʕ"] = { title = "voiced pharyngeal fricative", link = "w:Voiced pharyngeal fricative", }, ["ʜ"] = { title = "voiceless epiglottal fricative", link = "w:Voiceless epiglottal fricative", }, ["ʢ"] = { title = "voiced epiglottal fricative", link = "w:Voiced epiglottal fricative", }, ["h"] = { title = "voiceless glottal fricative", link = "w:Voiceless glottal fricative", }, ["ɦ"] = { title = "voiced glottal fricative", link = "w:Voiced glottal fricative", }, -- approximant ["ʋ"] = { title = "labiodental approximant", link = "w:Labiodental approximant", }, ["ɹ"] = { title = "alveolar approximant", link = "w:Alveolar approximant", }, ["ɻ"] = { title = "retroflex approximant", link = "w:Retroflex approximant", }, ["j"] = { title = "palatal approximant", link = "w:Palatal approximant", }, ["ɰ"] = { title = "velar approximant", link = "w:Velar approximant", }, -- tap, flap ["ⱱ"] = { title = "labiodental tap", link = "w:Labiodental flap", }, ["ɾ"] = { title = "alveolar flap", link = "w:Alveolar flap", }, ["ɽ"] = { title = "retroflex flap", link = "w:Retroflex flap", }, -- trill ["ʙ"] = { title = "bilabial trill", link = "w:Bilabial trill", }, ["r"] = { title = "alveolar trill", link = "w:Alveolar trill", }, ["ʀ"] = { title = "uvular trill", link = "w:Uvular trill", }, ["ᴙ"] = { title = "epiglottal trill", link = "w:Epiglottal trill", }, -- lateral fricative ["ɬ"] = { title = "voiceless alveolar lateral fricative", link = "w:Voiceless alveolar lateral fricative", }, ["ɮ"] = { title = "voiced alveolar lateral fricative", link = "w:Voiced alveolar lateral fricative", }, -- no precomposed Unicode character --TOMOVE --["ɬ̢"] = {title = "voiceless retroflex lateral fricative", link = "w:voiceless retroflex lateral fricative"}, -- no precomposed Unicode character --TOMOVE:3 --["ʎ̝̊"] = {title = "voiceless palatal lateral fricative", link = "w:voiceless palatal lateral fricative"}, -- no precomposed Unicode character --TOMOVE:3 --["ʟ̝̊"] = {title = "voiceless velar lateral fricative", link = "w:voiceless velar lateral fricative"}, -- no precomposed Unicode character --TOMOVE --["ʟ̝"] = {title = "voiced velar lateral fricative", link = "w:voiced velar lateral fricative"}, -- lateral approximant ["l"] = { title = "alveolar lateral approximant", link = "w:Alveolar lateral approximant", }, ["ɭ"] = { title = "retroflex lateral approximant", link = "w:Retroflex lateral approximant", }, ["ʎ"] = { title = "palatal lateral approximant", link = "w:Palatal lateral approximant", }, ["ʟ"] = { title = "velar lateral approximant", link = "w:Velar lateral approximant", }, -- lateral flap ["ɺ"] = { title = "alveolar lateral flap", link = "w:Alveolar lateral flap", }, --["ɭ̆"] = {title = "retroflex lateral flap", link = "w:retroflex lateral flap"}, -- no precomposed Unicode character --TOMOVE --["ɺ˞"] = {title = "retroflex lateral flap", link = "w:retroflex lateral flap"}, -- no precomposed Unicode character --TOMOVE -- NON-PULMONIC CONSONANTS -- clicks ["ʘ"] = { title = "bilabial click", link = "w:Bilabial clicks", }, ["ǀ"] = { title = "dental click", link = "w:Dental clicks", }, ["ǃ"] = { title = "postalveolar click", link = "w:Alveolar clicks", }, ["𝼊"] = { title = "subapical retroflex", link = "w:Retroflex clicks", }, -- NOT IN X-SAMPA ["ǂ"] = { title = "palatal click", link = "w:Palatal clicks", }, ["ǁ"] = { title = "alveolar lateral click", link = "w:Lateral clicks", }, -- implosives ["ɓ"] = { title = "voiced bilabial implosive", link = "w:Voiced bilabial implosive", }, ["ɗ"] = { title = "voiced alveolar implosive", link = "w:Voiced alveolar implosive", }, -- NOT IN X-SAMPA ["ᶑ"] = { title = "retroflex implosive", link = "w:Voiced retroflex implosive", }, ["ʄ"] = { title = "voiced palatal implosive", link = "w:Voiced palatal implosive", }, ["ɠ"] = { title = "voiced velar implosive", link = "w:Voiced velar implosive", }, ["ʛ"] = { title = "voiced uvular implosive", link = "w:Voiced uvular implosive", }, -- ejectives ["ʼ"] = { title = "ejective", link = "w:Ejective consonant", }, -- CO-ARTICULATED CONSONANTS ["ʍ"] = { title = "voiceless labial-velar fricative", link = "w:Voiceless labio-velar approximant", }, ["w"] = { title = "labial-velar approximant", link = "w:Labio-velar approximant", }, ["ɥ"] = { title = "labial-palatal approximant", link = "w:Labialized palatal approximant", }, ["ɧ"] = { title = "voiceless palatal-velar fricative", link = "w:Sj-sound", }, -- should be handled in [[Module:IPA]] and not through this table -- BRACKETS --[[ -- ["//"] = { title = "morphophonemic", link = "w:morphophonemic", }, ["/"] = { title = "phonemic", link = "w:phonemic", }, ["["] = { title = "phonetic", link = "w:phonetic", }, ["["] = { title = "phonetic", link = "w:phonetic", }, ["〈"] = { title = "orthographic", link = "w:orthographic", }, ["〉"] = { title = "orthographic", link = "w:orthographic", }, ["⟨"] = { title = "orthographic", link = "w:orthographic", }, ["⟩"] = { title = "orthographic", link = "w:orthographic", }, ]] -- VOWELS -- close ["i"] = { title = "close front unrounded vowel", link = "w:Close front unrounded vowel", }, ["y"] = { title = "close front rounded vowel", link = "w:Close front rounded vowel", }, ["ɨ"] = { title = "close central unrounded vowel", link = "w:Close central unrounded vowel", }, ["ʉ"] = { title = "close central rounded vowel", link = "w:Close central rounded vowel", }, ["ɯ"] = { title = "close back unrounded vowel", link = "w:Close back unrounded vowel", }, ["u"] = { title = "close back rounded vowel", link = "w:Close back rounded vowel", }, -- near close ["ɪ"] = { title = "near-close near-front unrounded vowel", link = "w:Near-close near-front unrounded vowel", }, ["ʏ"] = { title = "near-close near-front rounded vowel", link = "w:Near-close near-front rounded vowel", }, ["ᵻ"] = { title = "near-close central unrounded vowel", link = "w:Near-close central unrounded vowel", }, -- (alternative) --TOMOVE --[[ ["ɪ̈"] = { title = "near-close central unrounded vowel", link = "w:near-close central unrounded vowel", }, ]] ["ᵿ"] = { title = "near-close central rounded vowel", link = "w:Near-close central rounded vowel", }, --[[ (alternative) TOMOVE ["ʊ̈"] = { title = "near-close central rounded vowel", link = "w:near-close central rounded vowel", }, ]] ["ʊ"] = { title = "near-close near-back rounded vowel", link = "w:Near-close near-back rounded vowel", }, --close mid ["e"] = { title = "close-mid front unrounded vowel", link = "w:Close-mid front unrounded vowel", }, ["ø"] = { title = "close-mid front rounded vowel", link = "w:Close-mid front rounded vowel", }, ["ɘ"] = { title = "close-mid central unrounded vowel", link = "w:Close-mid central unrounded vowel", }, ["ɵ"] = { title = "close-mid central rounded vowel", link = "w:Close-mid central rounded vowel", }, ["ɤ"] = { title = "close-mid back unrounded vowel", link = "w:Close-mid back unrounded vowel", }, ["o"] = { title = "close-mid back rounded vowel", link = "w:Close-mid back rounded vowel", }, -- mid ["ə"] = { title = "schwa", link = "w:Schwa", }, ["ɚ"] = { title = "schwa+r", link = "w:R-colored vowel", }, -- open mid ["ɛ"] = { title = "open-mid front unrounded vowel", link = "w:Open-mid front unrounded vowel", }, ["œ"] = { title = "open-mid front rounded vowel", link = "w:Open-mid front rounded vowel", }, ["ɜ"] = { title = "open-mid central unrounded vowel", link = "w:Open-mid central unrounded vowel", }, ["ɝ"] = { title = "open-mid central unrounded vowel+r", link = "w:R-colored vowel", }, ["ɞ"] = { title = "open-mid central rounded vowel", link = "w:Open-mid central rounded vowel", }, ["ʌ"] = { title = "open-mid back unrounded vowel", link = "w:Open-mid back unrounded vowel", }, ["ɔ"] = { title = "open-mid back rounded vowel", link = "w:Open-mid back rounded vowel", }, -- near open ["æ"] = { title = "near-open front unrounded vowel", link = "w:Near-open front unrounded vowel", }, ["ɐ"] = { title = "near-open central vowel", link = "w:Near-open central vowel", }, -- open ["a"] = { title = "open front unrounded vowel", link = "w:Open front unrounded vowel", }, ["ɶ"] = { title = "open front rounded vowel", link = "w:Open front rounded vowel", }, ["ɑ"] = { title = "open back unrounded vowel", link = "w:Open back unrounded vowel", }, ["ɒ"] = { title = "open back rounded vowel", link = "w:Open back rounded vowel", }, -- SUPRASEGMENTALS ["ˈ"] = {title = "primary stress", link = "w:Stress (linguistics)", XSAMPA = "\""}, --[[ ["???"] = { title = "extra stress: no Unicode char; double primary stress instead", link = "w:extra stress: no Unicode char; double primary stress instead", XSAMPA = "" }, --TOMOVE:3 ]] ["ˌ"] = { title = "secondary stress", link = "w:Secondary stress", }, ["ː"] = { title = "long", link = "w:Length (phonetics)", }, ["ˑ"] = { title = "half long", link = "w:Length (phonetics)", }, ["̆"] = { title = "extra-short", link = "w:Length (phonetics)", }, --[[ ["%."] = { title = "syllable break", link = "w:syllable break", }, ]] --TOMOVE ["‿"] = { title = "linking mark (absence of a break)", link = "w:Tie (typography)#International_Phonetic_Alphabet", }, [" "] = { title = "separator", link = "w:separator", }, -- TONE -- level tones ["˥"] = { title = "top", link = "w:Tone letter", }, ["˦"] = { title = "high", link = "w:Tone letter", }, ["˧"] = { title = "mid", link = "w:Tone letter", }, ["˨"] = { title = "low", link = "w:Tone letter", }, ["˩"] = { title = "bottom", link = "w:Tone letter", }, ["̋"] = { title = "extra high tone", link = "w:Tone letter", }, ["́"] = { title = "high tone", link = "w:Tone letter", }, ["̄"] = { title = "mid tone", link = "w:Tone letter", }, ["̀"] = { title = "low tone", link = "w:Tone letter", }, ["̏"] = { title = "extra low tone", link = "w:Tone letter", }, -- tone terracing ["ꜛ"] = { title = "upstep", link = "w:Upstep", }, ["ꜜ"] = { title = "downstep", link = "w:Downstep", }, -- contour tones ["̌"] = { title = "rising tone", link = "w:Tone (linguistics)", }, ["̂"] = { title = "falling tone", link = "w:Tone (linguistics)", }, ["᷄"] = { title = "high rising tone", link = "w:Tone (linguistics)", }, ["᷅"] = { title = "low rising tone", link = "w:Tone (linguistics)", }, ["᷇"] = { title = "high falling tone", link = "w:Tone (linguistics)", }, ["᷆"] = { title = "low falling tone", link = "w:Tone (linguistics)", }, ["᷈"] = { title = "rising falling tone (peaking)", link = "w:Tone (linguistics)", }, ["᷉"] = { title = "dipping", link = "w:Tone (linguistics)", }, -- [extrapolated from the chart -- please confirm] -- intonation ["|"] = { title = "minor (foot) group", link = "w:Prosodic unit", }, ["‖"] = { title = "major (intonation) group", link = "w:Prosodic unit", }, ["↗"] = { title = "global rise", link = "w:Intonation (linguistics)", }, ["↘"] = { title = "global fall", link = "w:Intonation (linguistics)", }, -- DIACRITICS -- syllabicity & releases ["̩"] = { title = "syllabi ", link = "w:Syllabic consonant", withdescender = "̍" }, -- (or "_=" ["̯"] = { title = "non-syllabic", link = "w:Semivowel", withdescender = "̑" }, ["ʰ"] = { title = "aspirated", link = "w:Aspirated consonant", }, ["ⁿ"] = { title = "nasal release", link = "w:Nasal release", }, ["ˡ"] = { title = "lateral release", link = "w:Lateral release (phonetics)", }, ["̚"] = { title = "no audible release", link = "w:No audible release", }, -- phonation ["̥"] = { title = "voiceless", link = "w:Voicelessness", withdescender = "̊" }, ["̬"] = { title = "voiced", link = "w:Voice (phonetics)", }, ["̤"] = { title = "breathy voice", link = "w:Breathy voice", }, ["̰"] = { title = "creaky voice", link = "w:Creaky voice", }, ["᷽"] = { title = "strident", link = "w:Strident vowel", }, -- primary articulation ["̪"] = { title = "dental", link = "w:Dental consonant", }, ["̺"] = { title = "apical", link = "w:Apical consonant", }, ["̻"] = { title = "laminal", link = "w:Laminal consonant", }, ["̟"] = { title = "advanced", link = "w:Relative articulation#Advanced_and_retracted", withdescender = "˖" }, ["̠"] = { title = "retracted", link = "w:Relative articulation#Retracted", withdescender = "˗" }, ["̼"] = { title = "linguolabial", link = "w:Linguolabial consonant", }, ["̈"] = { title = "centralized", link = "w:Relative articulation#Centralized_vowels", XSAMPA = "_\"" }, ["̽"] = { title = "mid-centralized", link = "Relative articulation#Mid-centralized_vowel", }, ["̞"] = { title = "lowered", link = "w:Relative articulation#Raised_and_lowered", withdescender = "˕" }, ["̝"] = { title = "raised", link = "w:Relative articulation#Raised_and_lowered", withdescender = "˔" }, ["͡"] = { title = "coarticulated", link = "w:Co-articulated consonant", }, ["͈"] = { title = "strong articulation", link = "w:Fortis and lenis", }, -- secondary articulation ["ʷ"] = { title = "labialized", link = "w:Labialization", }, ["ʲ"] = { title = "palatalized", link = "w:Palatalization (phonetics)", }, ["ˠ"] = { title = "velarized", link = "w:Velarization", }, ["ˤ"] = { title = "pharyngealized", link = "w:Pharyngealization", }, -- also see _e ["ɫ"] = { title = "velarized alveolar lateral approximant", link = "w:Alveolar lateral approximant", }, ["̴"] = { title = "velarized or pharyngealized; also see 5", link = "w:Velarization", }, ["̹"] = { title = "more rounded", link = "w:Roundedness", }, ["̜"] = { title = "less rounded", link = "w:Roundedness", }, ["̃"] = { title = "nasalization", link = "w:Nasalization", }, ["˞"] = { title = "rhotacization in vowels, retroflexion in consonants", link = "w:R-colored vowel", }, ["̘"] = { title = "advanced tongue root", link = "w:Advanced and retracted tongue root", }, ["̙"] = { title = "retracted tongue root", link = "w:Advanced and retracted tongue root", }, } data[2] = { -- TODO --["%("] = {}, --["%)"] = {}, ["ːː"] = { title = "extra long", link = "w:Length (phonetics)", }, ["r̥"] = {title = "voiceless alveolar trill", link = "w:Voiceless alveolar trill"}, ["ɬ’"] = {title = "alveolar lateral ejective fricative", link = "w:Alveolar lateral ejective fricative"}, } data[3] = { ["t͡s"] = {title = "voiceless alveolar sibilant affricate", link = "w:Voiceless alveolar affricate"}, ["d͡z"] = {title = "voiced alveolar sibilant affricate", link = "w:Voiced alveolar affricate"}, ["t͡ʃ"] = {title = "voiceless palato-alveolar affricate", link = "w:Voiceless palato-alveolar affricate", descender = true}, ["d͡ʒ"] = {title = "voiced palato-alveolar affricate", link = "w:Voiced palato-alveolar affricate"}, ["ʈ͡ʂ"] = {title = "voiceless retroflex affricate", link = "w:Voiceless retroflex affricate", descender = true}, ["ɖ͡ʐ"] = {title = "voiced retroflex affricate", link = "w:Voiced retroflex affricate, descender = true"}, ["t͡ɕ"] = {title = "voiceless alveolo-palatal affricate", link = "w:Voiceless alveolo-palatal affricate"}, ["d͡ʑ"] = {title = "voiced alveolo-palatal affricate", link = "w:Voiced alveolo-palatal affricate"}, ["c͡ç"] = {title = "voiceless palatal affricate", link = "w:Voiceless palatal affricate, descender = true"}, ["ɟ͡ʝ"] = {title = "voiced palatal affricate", link = "w:Voiced palatal affricate, descender = true"}, ["k͡x"] = {title = "voiceless velar affricate", link = "w:Voiceless velar affricate"}, ["ɡ͡ɣ"] = {title = "voiced velar affricate", link = "w:Voiced velar affricate, descender = true"}, } data[4] = { ["ǃ͡qʼ"] = {title = "alveolar linguo-glottalic stop", link = "w:Ejective-contour clicks, descender = true"}, ["ǁ͡χʼ"] = {title = "lateral linguo-glottalic affricate (homorganic)", link = "w:Ejective-contour clicks", descender = true}, } data[5] = { ["k͡ʟ̝̊"] = {title = "voiceless velar lateral affricate", link = "w:Voiceless velar lateral affricate"}, ["ᶢǀ͡qʼ"] = {title = "voiced dental linguo-glottalic stop", link = "w:Ejective-contour clicks"}, ["ǂ͡kxʼ"] = {title = "palatal linguo-glottalic affricate (heterorganic)", link = "w:Ejective-contour clicks"}, } data[6] = { ["k͡ʟ̝̊ʼ"] = {title = "velar lateral ejective affricate", link = "w:Velar lateral ejective affricate"}, ["ᶢʘ͡kxʼ"] = {title = "voiced labial linguo-glottalic affricate", link = "w:Ejective-contour clicks"}, } data.separator_escapes = { ["⁽"] = "(", ["⁾"] = ")", ["₍"] = "(", ["₎"] = ")", ["ˈ"] = "\1", ["ˌ"] = "\2", ["ː"] = ":", ["ˑ"] = ";", } -- acute and grave tone marks local diacritics = u( -- grave, acute, circumflex, tilde, macron, breve 0x300, 0x301, 0x302, 0x303, 0x304, 0x306, -- diaeresis, ring above, double acute, caron, vertical line above, double grave, left tack 0x308, 0x30A, 0x30B, 0x30C, 0x30D, 0x30F, 0x318, -- right tack, left angle, left half ring below, up tack below, down tack below, plus sign below 0x319, 0x31A, 0x31C, 0x31D, 0x31E, 0x31F, -- minus sign below, rhotic hook below, dot below, diaeresis below, ring below, vertical line below, bridge below 0x320, 0x322, 0x323, 0x324, 0x325, 0x329, 0x32A, -- caron below, inverted breve below 0x32C, 0x32F, -- tilde below, combining tilde overlay, right half ring below, inverted bridge below, square below, seagull below, x above 0x330, 0x334, 0x339, 0x33A, 0x33B, 0x33C, 0x33D, -- grave tone mark, acute tone mark, bridge above, equals sign below, double vertical line below 0x340, 0x341, 0x346, 0x347, 0x348, -- left angle below, not tilde above, homothetic above, almost equal above, left right arrow below 0x349, 0x34A, 0x34B, 0x34C, 0x34D, -- upwards arrow below, left arrowhead below, right arrowhead below 0x34E, 0x354, 0x355, -- double rightwards arrow below, combining Latin small letter a 0x362, 0x361, -- macron–acute, grave–macron, macron–grave, acute–macron, grave–acute–grave, acute–grave–acute 0x1DC4, 0x1DC5, 0x1DC6, 0x1DC7, 0x1DC8, 0x1DC9) data.diacritics = diacritics data.vowels = "iyɨʉɯuɪᵻʏʊᵿeøɘɵɤoəɚɛœɜɝɞʌɔæɐaɶɑɒäëïöüÿ" local tones = "˥˦˧˨˩꜒꜓꜔꜕꜖꜈꜉꜊꜋꜌꜍꜎꜏꜐꜑¹²³⁴⁵⁶⁷⁸⁹⁰" data.tones = tones local superscripts = u(0xA0) .. " ⁰¹²³⁴⁵⁶⁷⁸⁹ᵃ𐞃ᵄᵅᶛᵇ𐞄𐞅ᶜᶝᵈᶞ𐞋𐞌𐞍ᵉᵊᵋ𐞎ᶟᵌ𐞏𐞑ᶠᶢ𐞒𐞓𐞔ˠʰ𐞕𐞖ʱ𐞗ⁱᶦᶤʲᶨᶡ𐞘ᵏˡᶫꭞ𐞛ᶩ𐞞𐞠𐞡ᵐᶬⁿᶰᶮᶯᵑᵒ𐞢ꟹ𐞣ᵓᶱᵖᶲ𐞥ʳ𐞪ʴ𐞦𐞧ʵ𐞨𐞩ʶˢᶳᶴᵗ𐞯ᵘᶶᶣᵚᶭᶷᵛᶹ𐞰ᶺʷꭩˣʸ𐞲ᶻᶼᶽᶾˀˤ𐞳𐞴𐞶𐞷𐞸𐞹𐞵ᵝᶿᵡ˞⁻𐞁𐞂" data.superscripts = superscripts -- An array of patterns of valid character sequences. data.valid = { "⁽[" .. superscripts .. "]+⁾", "[ %(%)%%<>{|}%-→~⁓%.◌abcdefhijklmnopqrstuvwxyz¡àáâãāăēäæçèéêëĕěħìíîïĩīĭĺḿǹńňðòóôõöōŏőœøŕùúûüũūŭűýÿŷŋ" .. "ǀǁǂǃǎǐǒǔřǖǘǚǜǟǣǽǿȁȅȉȍȕȫȭȳɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɪᵻɫɬɭɮɯɰɱɲɳɴɵɶɸɹɺ𝼈ɻɽɾʀʁʂʃʄʈʉʊᵿʋṽʌʍʎ𝼆ʏʐʑʒʔʕʘʞʙʛʜʝʟʡʢ𝼊ʬʭ" .. "ʼˈˌːˑˣ˔˕ˬ͗˭ˇ˖β͜θχᴙᶑ᷽ḁḛḭḯṍṏṳṵṹṻạẹẽịọụỳỵỹ‖․‥…‿↑↓↗↘ⱱꜛꜜꟸ𝆏𝆑˗ˋˊ–⸨⸩⁽⁾" .. diacritics .. tones .. superscripts .. "]+" } -- Character sequences which are valid only in a particular language. -- These can be either a single pattern (as a string), or an array of patterns (as a table). data.per_lang_valid = { ["egy"] = "V+", -- V for uncertain vowel ["okm"] = "[LHR!WT]+", -- irregular verb morphophonemes } -- Characters to add VARIATION SELECTOR-15 (U+FE0E) after. -- These are characters with emoji variants that are used by default by some clients. -- Adding VS15 after them instructs them to draw the characters as text instead. data.add_vs15 = "↗↘" data.invalid = { ["!"] = "ǃ", ["ꜝ"] = "ꜜ", ["ꜞ"] = "ꜛ", ["ꜟ"] = "ꜛ", ["'"] = "ˈ", ["’"] = "ʼ", [":"] = "ː", -- Confusable Latin letters ["B"] = "ʙ", ["g"] = "ɡ", ["G"] = "ɢ", ["Ɠ"] = "ʛ", ["H"] = "ʜ", ["ı"] = "ɪ", ["I"] = "ɪ", ["L"] = "ʟ", ["N"] = "ɴ", ["Œ"] = "ɶ", ["Q"] = "ꞯ", ["R"] = "ʀ", ["∫"] = "ʃ", ["⨎"] = "ǂ", -- due to confusion with obsolete 𝼋 below ["ß"] = "β", ["ẞ"] = "β", ["Y"] = "ʏ", ["Ə"] = "ə", ["ǝ"] = "ə", ["Ɂ"] = "ʔ", ["ɂ"] = "ʔ", ["ˁ"] = "ˤ", -- Confusable Greek letters ["α"] = "ɑ", ["γ"] = "ɣ", ["δ"] = "ð", ["ε"] = "ɛ", ["Η"] = "ʜ", ["η"] = "ŋ", ["ι"] = "ɪ", ["λ"] = "ʎ", ["υ"] = "ʋ", ["Ψ"] = "𝼊", ["ψ"] = "𝼊", ["Φ"] = "ɸ", ["ϕ"] = "ɸ", ["ꭓ"] = "χ", -- Actually Latin, since IPA uses the Greek letter(!) -- Confusable Cyrillic letters ["ӕ"] = "æ", ["Ә"] = "ə", ["ә"] = "ə", ["В"] = "ʙ", ["в"] = "ʙ", ["е"] = "e", ["З"] = "ɜ", ["з"] = "ɜ", ["Ѕ"] = "s", ["ѕ"] = "s", ["і"] = "i", ["ј"] = "j", ["Н"] = "ʜ", ["н"] = "ʜ", ["О"] = "o", ["о"] = "o", ["р"] = "p", ["с"] = "c", ["у"] = "y", ["Ү"] = "ʏ", ["ү"] = "ʏ", ["Ф"] = "ɸ", ["ф"] = "ɸ", ["х"] = "x", ["Һ"] = "h", ["һ"] = "h", ["Я"] = "ᴙ", ["я"] = "ᴙ", ["Ѱ"] = "𝼊", ["ѱ"] = "𝼊", ["Ѵ"] = "ⱱ", ["ѵ"] = "ⱱ", ["Ҁ"] = "ʕ", ["ҁ"] = "ʕ", -- Palatalization ["ᶀ"] = "bʲ", ["ꞔ"] = "cʲ", ["ᶁ"] = "dʲ", ["ȡ"] = "d̠ʲ", ["d̂"] = "d̠ʲ", ["ᶂ"] = "fʲ", ["ᶃ"] = "ɡʲ", ["ꞕ"] = "hʲ", ["ᶄ"] = "kʲ", ["ᶅ"] = "lʲ", ["ȴ"] = "l̠ʲ", ["l̂"] = "l̠ʲ", ["𝼓"] = "ɬʲ", ["ᶆ"] = "mʲ", ["ᶇ"] = "nʲ", ["ȵ"] = "n̠ʲ", ["n̂"] = "n̠ʲ", ["𝼔"] = "ŋʲ", ["ᶈ"] = "pʲ", ["ᶉ"] = "rʲ", ["𝼕"] = "ɹʲ", ["𝼖"] = "ɾʲ", ["ᶊ"] = "sʲ", ["𝼞"] = "ɕ", ["𐞺"] = "ᶝ", ["ᶋ"] = "ʃʲ", ["ʆ"] = "ʃʲ", ["ƫ"] = "tʲ", ["ȶ"] = "t̠ʲ", ["t̂"] = "t̠ʲ", ["ᶌ"] = "vʲ", ["ᶍ"] = "xʲ", ["ᶎ"] = "zʲ", ["𝼘"] = "ʒʲ", ["ʓ"] = "ʒʲ", -- Retroflex ["𝼝"] = "ʈ͡ʂ", ["𝼥"] = "ɖ", ["𝼦"] = "ɭ", ["𝼧"] = "ɳ", ["𝼨"] = "ɽ", ["𝼩"] = "ʂ", ["𝼪"] = "ʈ", -- Rhotic vowels ["ᶏ"] = "a˞", ["ᶐ"] = "ɑ˞", ["ᶒ"] = "e˞", ["ə˞"] = "ɚ", ["ᶕ"] = "ɚ", ["ᶓ"] = "ɛ˞", ["ɜ˞"] = "ɝ", ["ᶔ"] = "ɝ", ["ᶖ"] = "i˞", ["𝼚"] = "ɨ˞", ["𝼛"] = "o˞", ["ᶗ"] = "ɔ˞", ["ᶙ"] = "u˞", -- Syllabic approximants ["ɿ"] = "ɹ̩", ["ʅ"] = "ɻ̩", ["ʮ"] = "ɹ̩ʷ", ["ʯ"] = "ɻ̩ʷ", -- Clicks ["ʗ"] = "ǃ", ["𝼋"] = "ǂ", ["ʇ"] = "ǀ", ["ʖ"] = "ǁ", ["‼"] = "𝼊", -- Voiceless implosives ["ƈ"] = "ʄ̊", ["ƙ"] = "ɠ̊", ["ƥ"] = "ɓ̥", ["ʠ"] = "ʛ̥", ["ƭ"] = "ɗ̥", ["𝼉"] = "ᶑ̥", -- Monographs ["ꜰ"] = "ɸ", ["ɩ"] = "ɪ", ["ɼ"] = "r̝", ["ᴜ"] = "ʊ", ["ɷ"] = "ʊ", ["𐞤"] = "ᶷ", ["ƛ"] = "t͡ɬ", ["ƻ"] = "d͡z", ["ƾ"] = "t͡s", -- Digraphs ["ȸ"] = "b̪", ["ʣ"] = "d͡z", ["ʥ"] = "d͡ʑ", ["ꭦ"] = "ɖ͡ʐ", ["ʤ"] = "d͡ʒ", ["𝼒"] = "d͡ʒʲ", ["𝼙"] = "d͡ᶚ", ["ʪ"] = "ɬ͡s", ["ʫ"] = "ɮ͡z", ["ȹ"] = "p̪", ["ʦ"] = "t͡s", ["ʨ"] = "t͡ɕ", ["ꭧ"] = "ʈ͡ʂ", ["ʧ"] = "t͡ʃ", ["𝼗"] = "t͡ʃʲ", ["𝼜"] = "t͡ᶘ", -- Deprecated or confusable diacritics ["̫"] = "ʷ", ["͂"] = "̃", ["᫇"] = "ʷ", ["⸋"] = "̚", ["̱"] = "̠", -- COMBINING MACRON BELOW (U+0331) -> COMBINING MINUS SIGN BELOW (U+0320) -- Precomposed characters with deprecated or confusable diacritics; the left is a precomposed -- version of a lowercase letter with COMBINING MACRON BELOW and the right is the equivalent -- using COMBINING MINUS SIGN BELOW ["ḇ"] = "b̠", ["ḏ"] = "d̠", ["ẖ"] = "h̠", ["ḵ"] = "k̠", ["ḻ"] = "l̠", ["ṉ"] = "n̠", ["ṟ"] = "r̠", ["ṯ"] = "t̠", ["ẕ"] = "z̠", } return data mjopki8sklky9m3kv63kzgka1usuiki Schach 0 150683 5775279 1874222 2026-10-14T10:09:53Z Octahedron80 267 5775279 wikitext text/x-wiki ==ภาษาเยอรมัน== {{wikipedia|lang=de}} ===รากศัพท์=== จาก{{der|de|fa|شاه|tr=šāh}}, จาก{{der|de|pal|𐭬𐭫𐭪𐭠}}, จาก{{der|de|peo|𐏋|tr=xšāyaθiya||king|sc=Xpeo}} ===การออกเสียง=== * {{IPA|de|/ʃax/}} * {{audio|de|De-Schach.ogg}} * {{audio|de|De-at-Schach.ogg|เสียง (ออสเตรีย)}} ===คำนาม=== {{de-noun|n||-}} # [[หมากรุกสากล]] kjm13oya73i5cmncwxnjwk7zs0yaine ye2 0 150803 5775170 1444464 2026-10-13T18:18:38Z ~2026-55063-29 19696 Delete user wikipedia name Octahedron80 5775170 wikitext text/x-wiki == •√²(πDelete user Octahedron80 == 6hzc58xh8du64ucnpu944y35ky6fj54 5775171 5775170 2026-10-13T18:19:05Z NDG 17825 ย้อนการแก้ไขโดย [[Special:Contributions/~2026-55063-29|~2026-55063-29]] ([[User talk:~2026-55063-29|คุย]]) ไปยังรุ่นแก้ไขล่าสุดโดย [[User:OctraBot|OctraBot]] 1444464 wikitext text/x-wiki == ภาษาจีนกลาง == === การถอดเป็นอักษรโรมัน === {{cmn-pinyin}} # {{alternative spelling of|cmn|yé}} 985o8zzo1j1iplluxmwd0j26vhye900 ਦੁਨੀਆ 0 152675 5775215 1276495 2026-10-14T06:06:06Z Octahedron80 267 /* ภาษาปัญจาบ */ เก็บกวาด 5775215 wikitext text/x-wiki == ภาษาปัญจาบ == === รูปแบบอื่น === * {{alter|pa|ਦੁਨੀ||กลอน}} === รากศัพท์ === {{etymon|pa|:inh|inc-opa:ਦੁਨੀਆ<ety:bor<fa-cls:دنیا<tr:dunyā>>><ref:{{R:inc-opa:Glossary|ਦੁਨੀਆ|156}}!!!{{R:inc-opa:SGGS}}>|tree=++|text=++}} === การออกเสียง === * {{pa-IPA}} === คำนาม === {{pa-noun|f|tr=dunīā|sha=دُنیا}}<ref>{{R:pa:Singh}}</ref><ref>{{R:pa:Patiala}}</ref> # [[โลก]] #* ''{{w|Guru Granth Sahib}}'', ''{{w|Kabir}}, translation by Dr. Sant Singh Khalsa, ang 717'': #*: {{quote|pa|ਇਹ ਜੁ '''ਦੁਨੀਆ''' ਸਿਹਰੁ ਮੇਲਾ ਦਸਤਗੀਰੀ ਨਾਹਿ|tr=eh jo '''dunīā''' sihar melā dastgīrī nāhe|This '''world''' is just a magic-show; no one will be holding your hand. }} ==== การผันรูป ==== {{rfinfl|pa}} ==== อ้างอิง ==== {{box-top}} {{reflist}} {{box-bottom}} oy5tj9172fn73cj1xq2zzv3d7r2p496 5775216 5775215 2026-10-14T06:06:31Z Octahedron80 267 /* คำนาม */ 5775216 wikitext text/x-wiki == ภาษาปัญจาบ == === รูปแบบอื่น === * {{alter|pa|ਦੁਨੀ||กลอน}} === รากศัพท์ === {{etymon|pa|:inh|inc-opa:ਦੁਨੀਆ<ety:bor<fa-cls:دنیا<tr:dunyā>>><ref:{{R:inc-opa:Glossary|ਦੁਨੀਆ|156}}!!!{{R:inc-opa:SGGS}}>|tree=++|text=++}} === การออกเสียง === * {{pa-IPA}} === คำนาม === {{pa-noun|f|tr=dunīā|sha=دُنیا}}<ref>{{R:pa:Singh}}</ref><ref>{{R:pa:Patiala}}</ref> # [[โลก]] #* ''{{w|Guru Granth Sahib}}'', ''{{w|Kabir}}, translation by Dr. Sant Singh Khalsa, ang 717'': #*: {{quote|pa|ਇਹ ਜੁ '''ਦੁਨੀਆ''' ਸਿਹਰੁ ਮੇਲਾ ਦਸਤਗੀਰੀ ਨਾਹਿ|tr=eh jo '''dunīā''' sihar melā dastgīrī nāhe|t='''โลก'''นี้เป็นเพียงงานแสดงมายากล ไม่มีใครจะคอยกุมมือเจ้าไว้หรอก}} ==== การผันรูป ==== {{rfinfl|pa}} ==== อ้างอิง ==== {{box-top}} {{reflist}} {{box-bottom}} 9ufh5gwqt0flngiup5w5to410yg6ac6 5775217 5775216 2026-10-14T06:06:56Z Octahedron80 267 /* ภาษาปัญจาบ */ 5775217 wikitext text/x-wiki == ภาษาปัญจาบ == === รูปแบบอื่น === * {{alter|pa|ਦੁਨੀ||กลอน}} === รากศัพท์ === {{etymon|pa|:inh|inc-opa:ਦੁਨੀਆ<ety:bor<fa-cls:دنیا<tr:dunyā>>><ref:{{R:inc-opa:Glossary|ਦੁਨੀਆ|156}}!!!{{R:inc-opa:SGGS}}>|tree=++|text=++}} === การออกเสียง === * {{pa-IPA}} === คำนาม === {{pa-noun|f|sha=دُنیا}}<ref>{{R:pa:Singh}}</ref><ref>{{R:pa:Patiala}}</ref> # [[โลก]] #* ''{{w|Guru Granth Sahib}}'', ''{{w|Kabir}}, translation by Dr. Sant Singh Khalsa, ang 717'': #*: {{quote|pa|ਇਹ ਜੁ '''ਦੁਨੀਆ''' ਸਿਹਰੁ ਮੇਲਾ ਦਸਤਗੀਰੀ ਨਾਹਿ|t='''โลก'''นี้เป็นเพียงงานแสดงมายากล ไม่มีใครจะคอยกุมมือเจ้าไว้หรอก}} ==== การผันรูป ==== {{rfinfl|pa}} ==== อ้างอิง ==== {{box-top}} {{reflist}} {{box-bottom}} kq44tpzojmc0an604nlvg88o59ehxfi ਦੁਬਿੰਦੀ 0 152677 5775214 1276497 2026-10-14T06:05:03Z Octahedron80 267 /* ภาษาปัญจาบ */ เก็บกวาด 5775214 wikitext text/x-wiki == ภาษาปัญจาบ == === รากศัพท์ === {{affix|pa|ਦੁ-|ਬਿੰਦੀ}} === การออกเสียง === * {{IPA|pa|/d̪ʊbɪnd̪iː/}} === คำนาม === {{pa-noun|f|pl=ਦੁਬਿੰਦੀਆਂ|sha=دُبِندی}} # [[ทวิภาค]] {{q|เครื่องหมายวรรคตอน}} #: {{syn|pa|ਕੋਲਨ}} ==== การผันรูป ==== {{pa-noun-f-ī|ਦੁਬਿੰਦ}} ==== คำเกี่ยวข้อง ==== * {{l|pa|ਬਿੰਦੀ ਕਾਮਾ|g=m|t=อัฒภาค}} === อ่านเพิ่ม === * {{R:pa:Punjabi University}} {{topics|pa|เครื่องหมายวรรคตอน (หัวข้อ)}} 1hxd9kmf1iq9qhcuahnbr2vhz7homba ਦੋਸਤੀ 0 152681 5775218 1642511 2026-10-14T06:08:13Z Octahedron80 267 /* ภาษาปัญจาบ */ เก็บกวาด 5775218 wikitext text/x-wiki == ภาษาปัญจาบ == === รากศัพท์ === {{bor+|pa|fa-cls|دوستی|tr=dōstī}}; เทียบเท่า {{affix|pa|ਦੋਸਤ|-ਈ}}; เทียบ{{cog|hi|दोस्ती}} === คำนาม === {{pa-noun|f|sha=دوستی}} # [[friendship]], [[companionship]] ==== การผันรูป ==== {{pa-ndecl|t=f-ī*}} ==== คำพ้องความ ==== * {{l|pa|ਦੋਸਤਾਨਾ|g=m}}, {{l|pa|ਮਿੱਤਰਤਾ|g=f}} === อ่านเพิ่ม === * {{R:pa:Punjabi University}} * {{R:pa:Singh}} 07zml76r3cf03he5j1ud7zij4gjkzir 5775219 5775218 2026-10-14T06:08:43Z Octahedron80 267 /* ภาษาปัญจาบ */ 5775219 wikitext text/x-wiki == ภาษาปัญจาบ == === รากศัพท์ === {{bor+|pa|fa-cls|دوستی|tr=dōstī}}; เทียบเท่า {{affix|pa|ਦੋਸਤ|-ਈ}}; เทียบ{{cog|hi|दोस्ती}} === คำนาม === {{pa-noun|f|sha=دوستی}} # [[มิตรภาพ]], [[ความเป็นเพื่อน]] ==== การผันรูป ==== {{pa-ndecl|t=f-ī*}} ==== คำพ้องความ ==== * {{l|pa|ਦੋਸਤਾਨਾ|g=m}}, {{l|pa|ਮਿੱਤਰਤਾ|g=f}} === อ่านเพิ่ม === * {{R:pa:Punjabi University}} * {{R:pa:Singh}} fscj06lnrr45g4wjs9egqomciwgi8hu มอดูล:headword/data 828 162970 5775189 5759302 2026-10-14T04:15:03Z Octahedron80 267 5775189 Scribunto text/plain local headword_page_module = "Module:headword/page" local list_to_set = require("Module:table").listToSet local data = {} ------ 1. Lists which are converted into sets. ------ --[==[ var: Large pages where we disable label tracking, red link checking and similar. ]==] data.large_pages = list_to_set { -- pages that consistently hit timeouts "a", -- pages that sometimes hit timeouts "A", "baba", "de", "e", "i", "lima", "o", "u", "и", "山", "子", "月", "一", "人", } --[==[ var: Map from singular to plural, and from plural to itself, for recognized parts of speech with irregular plurals. Most of these are invariable plurals, e.g. `kanji` is its own plural; but we also have `mora` plural `morae`. ]==] data.irregular_plurals = list_to_set({ "ชมาโว", --cmavo "ชเมเน", --cmene "ฟูฮิฝลา", --fu'ivla "กิสมู", --gismu "ฮั้นถื่อ", --Han tu "ฮันจา", --hanja "ฮั่นจื้อ", --hanzi "ยฺหวืดเพ็ง", --jyutping "คานะ", --kana "คันจิ", --kanji "ลุฌโว", --lujvo "หนังสือวลี", --phrasebook "พินอิน", --pinyin "รัฟซี", --rafsi }, function(_, item) return item end) local irregular_plurals = data.irregular_plurals -- Irregular non-zero plurals AND any regular plurals where the singular ends in "s", -- because the module assumes that inputs ending in "s" are plurals. The singular and -- plural both need to be added, as the module will generate a default plural if -- the input doesn't match a key in this table. for sg, pl in next, { mora = "morae" } do irregular_plurals[sg], irregular_plurals[pl] = pl, pl end --[==[ var: Recognized lemmas. If the part of speech in {{tl|head}} is set to one of these or its singular equivalent, the category 'LANG lemmas' will automatically be added. If the part of speech is not a singular or plural lemma or non-lemma form and is not an abbreviation that expands to a recognized lemma or non-lemma form, the page will be added to various tracking categories: * [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos]] * [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos/LANG]] * [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos/pos/POS]] * [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos/pos/POS/LANG]] ]==] data.lemmas = list_to_set{ "คำย่อ", --abbreviations "อักษรย่อรวมพยางค์", --acronyms "คำคุณศัพท์", --adjectives "adnominals", "adpositions", "คำกริยาวิเศษณ์", --adverbs "หน่วยคำเติม", --affixes "ambipositions", "คำกำกับนาม", --articles "หน่วยคำเติมคร่อม", --circumfixes "circumpositions", "คำลักษณนาม", --classifiers "ชมาโว", --cmavo "cmavo clusters", "ชเมเน", --cmene "combining forms", "คำสันธาน", --conjunctions "คำลักษณนาม", --counters = classifiers "ตัวกำหนด", --determiners "diacritical marks", "ทวิอักษร", --digraphs "equative adjectives", "ฟูฮิฝลา", --fu'ivla "กิสมู", --gismu "อักษรจีน", --Han characters "ฮั้นถื่อ", --Han tu "ฮันจา", --hanja "ฮั่นจื้อ", --hanzi "ideophones", "สำนวน", --idioms "อาคม", --infixes "อักษรย่อ", --initialisms "เครื่องหมายซ้ำ", --iteration marks "หน่วยคำเติมเชื่อม", --interfixes "คำอุทาน", --interjections "คานะ", --kana "คันจิ", --kanji "ตัวอักษร", --letters "ตัวอักษรควบ", --ligatures "logograms", "ลุฌโว", --lujvo "morae", "หน่วยคำ", --morphemes "non-constituents", "คำนาม", --nouns "จำนวน", --numbers "ตัวเลข", --numeral symbols "เลข", --numerals "คำอนุภาค", --particles "วลี", --phrases "คำปัจฉบท", --postpositions "วลีปัจฉบท", --postpositional phrases "predicatives", "อุปสรรค", --prefixes "วลีบุพบท", --prepositional phrases "คำบุพบท", --prepositions "คำกริยาเติมหน้า", --preverbs "pronominal adverbs", "คำสรรพนาม", --pronouns "คำวิสามานยนาม", --proper nouns "สุภาษิต", --proverbs "เครื่องหมายวรรคตอน", --punctuation marks "relatives", "ราก", --roots "ต้นเค้าศัพท์", --stems "ปัจจัย", --suffixes "พยางค์", --syllables "สัญลักษณ์", --symbols "คำกริยา", --verbs } --[==[ var: Recognized non-lemma forms. If the part of speech in {{tl|head}} is set to one of these or its singular equivalent, the category 'LANG non-lemma forms' will automatically be added. If the part of speech is not a singular or plural lemma or non-lemma form and is not an abbreviation that expands to a recognized lemma or non-lemma form, the page will be added to various tracking categories; see the documentation of `data.lemmas`. ]==] data.nonlemmas = list_to_set{ "รูปผันรูปกริยาขยายกรรตุวาจก", --active participle forms "รูปกริยาขยายกรรตุวาจก", --active participles "รูปกริยาขยายเชิงคุณศัพท์", --adjectival participles "รูปคุณศัพท์", --adjective forms "adjective feminine forms", "รูปกริยาวิเศษณ์", --adverb forms "รูปกริยาขยายเชิงกริยาวิเศษณ์", --adverbial participles "agent participles", "รูปกำกับนาม", --article forms "รูปหน่วยคำเติมคร่อม", --circumfix forms "combined forms", "รูปคุณศัพท์ขั้นกว่า", --comparative adjective forms "คำคุณศัพท์ขั้นกว่า", --comparative adjectives "รูปกริยาวิเศษณ์ขั้นกว่า", --comparative adverb forms "คำกริยาวิเศษณ์ขั้นกว่า", --comparative adverbs "รูปสันธาน", --conjunction forms "รูปหดย่อ", --contractions "converbs", "รูปตัวกำหนด", --determiner forms "คำนามบอกความเล็ก", --diminutive nouns "elative adjectives", "รูปคุณศัพท์ขั้นเท่า", --equative adjective forms "คำคุณศัพท์ขั้นเท่า", --equative adjectives "รูปกริยาขยายอนาคตกาล", --future participles "รูปกริยาเป็นนาม", --gerunds "infinitive forms", "infinitives", "รูปอุทาน", --interjection forms "ยฺหวืดเพ็ง", --jyutping "รูปสะกดผิด", --misspellings "คำอนุภาคปฏิเสธ", --negative participles "nominal participles", "noun construct forms", "รูปนาม", --noun forms "รูปผันเลข", --numeral forms "รูปกริยาขยาย", --participles "รูปผันรูปกริยาขยาย", --participle forms "รูปอนุภาค", --particle forms "รูปกริยาขยายกรรมวาจก", --passive participles "รูปกริยาขยายกรรตุวาจกอดีตกาล", --past active participles "past adverbial participles", "รูปกริยาขยายอดีตกาล", --past participles "รูปผันรูปกริยาขยายอดีตกาล", --past participle forms "รูปกริยาขยายกรรมวาจกอดีตกาล", --past passive participles "perfect active participles", "perfect participles", "perfect passive participles", "พินอิน", --pinyin "รูปปัจฉบท", --postposition forms "รูปอุปสรรค", --prefix forms "preposition contractions", "รูปบุพบท", --preposition forms "prepositional pronouns", "รูปกริยาขยายกรรตุวาจกปัจจุบันกาล", --present active participles "present adverbial participles", "รูปกริยาขยายปัจจุบันกาล", --present participles "รูปกริยาขยายกรรมวาจกปัจจุบันกาล", --present passive participles "preverb forms", "รูปสรรพนาม", --pronoun forms "รูปวิสามานยนาม", --proper noun forms "รัฟซี", --rafsi "การถอดเป็นอักษรโรมัน", --romanizations "root forms", "singulatives", "รูปปัจจัย", --suffix forms "รูปคุณศัพท์ขั้นสุด", --superlative adjective forms "คำคุณศัพท์ขั้นสุด", --superlative adjectives "รูปกริยาวิเศษณ์ขั้นสุด", --superlative adverb forms "คำกริยาวิเศษณ์ขั้นสุด", --superlative adverbs "รูปกริยา", --verb forms "คำกริยานาม", --verbal nouns } --[==[ var: List of languages that will not have links to separate parts of the headword. ]==] data.no_multiword_links = list_to_set{ "zh", } --[==[ var: List of languages that will not have `LANG multiword terms` categories added. There are various reasons why languages are in this list: (a) words are written without spaces between them; (b) syllables are written with spaces between them; (c) variant reconstructions are notated with a tilde surrounded by spaces; (d) the language is a sign language, where pagenames are multiword descriptions of the gesture(s) required to make an individual sign; (e) some other weirdnesses. ]==] data.no_multiword_cat = list_to_set{ -------- Languages without spaces between words (sometimes spaces between phrases) -------- "blt", -- Tai Dam "ja", -- Japanese "khb", -- Lü "km", -- Khmer "lo", -- Lao "mnw", -- Mon "my", -- Burmese "nan", -- Min Nan (some words in Latin script; hyphens between syllables) "nan-hbl", -- Hokkien (some words in Latin script; hyphens between syllables) "nod", -- Northern Thai "ojp", -- Old Japanese "shn", -- Shan "sou", -- Southern Thai "tdd", -- Tai Nüa "th", -- Thai "tts", -- Isan "twh", -- Tai Dón "txg", -- Tangut "zh", -- Chinese (all varieties with Chinese characters) "zkt", -- Khitan -------- Languages with spaces between syllables -------- "ahk", -- Akha "aou", -- A'ou "atb", -- Zaiwa "byk", -- Biao "cdy", -- Chadong --"duu", -- Drung; not sure --"hmx-pro", -- Proto-Hmong-Mien --"hnj", -- Green Hmong; not sure "huq", -- Tsat "ium", -- Iu Mien --"lis", -- Lisu; not sure "mtq", -- Muong --"mww", -- White Hmong; not sure "onb", -- Lingao --"sit-gkh", -- Gokhy; not sure --"swi", -- Sui; not sure "tbq-lol-pro", -- Proto-Loloish "tdh", -- Thulung "ukk", -- Muak Sa-aak "vi", -- Vietnamese "yig", -- Wusa Nasu "zng", -- Mang -------- Languages with ~ with surrounding spaces used to separate variants -------- "mkh-ban-pro", -- Proto-Bahnaric "sit-pro", -- Proto-Sino-Tibetan; listed above -------- Other weirdnesses -------- "mul", -- Translingual; gestures, Morse code, etc. "aot", -- Atong (India); bullet is a letter -------- All sign languages -------- "ads", "aed", "aen", "afg", "ase", "asf", "asp", "asq", "asw", "bfi", "bfk", "bog", "bqn", "bqy", "bvl", "bzs", "cds", "csc", "csd", "cse", "csf", "csg", "csl", "csn", "csq", "csr", "doq", "dse", "dsl", "ecs", "esl", "esn", "eso", "eth", "fcs", "fse", "fsl", "fss", "gds", "gse", "gsg", "gsm", "gss", "gus", "hab", "haf", "hds", "hks", "hos", "hps", "hsh", "hsl", "icl", "iks", "ils", "inl", "ins", "ise", "isg", "isr", "jcs", "jhs", "jls", "jos", "jsl", "jus", "kgi", "kvk", "lbs", "lls", "lsl", "lso", "lsp", "lst", "lsy", "lws", "mdl", "mfs", "mre", "msd", "msr", "mzc", "mzg", "mzy", "nbs", "ncs", "nsi", "nsl", "nsp", "nsr", "nzs", "okl", "pgz", "pks", "prl", "prz", "psc", "psd", "psg", "psl", "pso", "psp", "psr", "pys", "rms", "rsl", "rsm", "sdl", "sfb", "sfs", "sgg", "sgx", "slf", "sls", "sqk", "sqs", "ssp", "ssr", "svk", "swl", "syy", "tse", "tsm", "tsq", "tss", "tsy", "tza", "ugn", "ugy", "ukl", "uks", "vgt", "vsi", "vsl", "vsv", "xki", "xml", "xms", "ygs", "ysl", "zib", "zsl", } --[==[ var: List of languages where a hyphen is not considered a word separator for the `LANG multiword terms` category. There are numerous reasons why languages are in this list; by each language should be listed the reason for inclusion. ]==] data.hyphen_not_multiword_sep = list_to_set{ "akk", -- Akkadian; hyphens between syllables "akl", -- Aklanon; hyphens for mid-word glottal stops "ber-pro", -- Proto-Berber; morphemes separated by hyphens "ceb", -- Cebuano; hyphens for mid-word glottal stops "cnk", -- Khumi Chin; hyphens used in single words "cpi", -- Chinese Pidgin English; Chinese-derived words with hyphens between syllables "de", -- German; too many false positives "esx-esk-pro", -- hyphen used to separate morphemes "fi", -- Finnish; hyphen used to separate components in compound words if the final and initial vowels match, respectively "gd", -- Scottish Gaelic; too many false positives like [[a-chianaibh]], [[a-nìos]], [[an-dè]] and other adverbs in a- and an- "hil", -- Hiligaynon; hyphens for mid-word glottal stops "hnn", -- Hanunoo; too many false positives "ilo", -- Ilocano; hyphens for mid-word glottal stops "kne", -- Kankanaey; hyphens for mid-word glottal stops "lcp", -- Western Lawa; dash as syllable joiner "lwl", -- Eastern Lawa; dash as syllable joiner "mfa", -- Pattani Malay in Thai script; dash as syllable joiner "mkh-vie-pro", -- Proto-Vietic; morphemes separated by hyphens "msb", -- Masbatenyo; too many false positives "tl", -- Tagalog; too many false positives "war", -- Waray-Waray; too many false positives "yo", -- Yoruba; hyphens used to show lengthened nasal vowels } --[==[ var: List of languages that will not have `LANG masculine nouns` and similar categories added. Generally, these languages are lacking gender but use the gender field for other purposes. (This is a massive hack and should be changed.) ]==] data.no_gender_cat = list_to_set{ -- Languages without gender but which use the gender field for other purposes "ja", "th", } --[==[ var: List of languages where [[Module:headword]] should not attempt to generate a transliteration even if the term is written in a non-Latin script. FIXME: Notate reasons why each language is in this list. ]==] data.notranslit = list_to_set{ "ams", "az", "bbc", "bug", "cdo", "cia", "cjm", "cjy", "cmn", "cnp", "cpi", "cpx", "csp", "czh", "czo", "gan", "hak", "hnm", "hsn", --"ja", "kzg", "lad", "ltc", "luh", "lzh", "mnp", "ms", "mul", "mvi", "nan", "nan-dat", "nan-hbl", "nan-hlh", "nan-lnx", "nan-tws", "nan-zhe", "nan-zsh", "och", "oj", "okn", "ryn", "rys", "ryu", "sh", "sjc", "tgt", --"th", "tkn", "tly", "txg", "und", --"vi", "wuu", "xug", "yoi", "yox", "yue", "za", "zh", "zhx-sic", "zhx-tai", -- ภาษาที่ใช้ Thai อย่างเดียว หรือ Thai/Latin นอกนั้นไปกำหนดใน languages "th", --ไทย "bzi", --บีซู "cbn", --ญัฮกุร "lcp", --เลอเวือะตะวันตก "lwl", --เลอเวือะตะวันออก "nyw", --ญ้อ "skb", --แสก "sou", --ปักษ์ใต้ "thm", --ทะวืง "tts", --อีสาน "ugo", --กฺ๋อง "urk", --อูรักลาโวยจ } --[==[ var: Languages where we track all direct uses of {{tl|head}} in place of a language-specific template, such as {{tl|en-head}}, {{tl|hi-head}}, {{tl|sa-head}}, {{tl|tl-head}}. ]==] data.track_head_template = list_to_set{ -- "en", -- when the time comes? "hi", "sa", "tl", } --[==[ var: List of script codes for which a script-tagged display title will be added. ]==] data.toBeTagged = list_to_set{ "Ahom", "Arab", "fa-Arab", "Aran", "Armi", "Armn", "Avst", "Bali", "Bamu", "Batk", "Beng", "as-Beng", "Bopo", "Brah", "Brai", "Bugi", "Buhd", "Cakm", "Cans", "Cari", "Cham", "Cher", "Copt", "Cprt", "Cyrl", "Cyrs", "Deva", "Dsrt", "Egyd", "Egyp", "Ethi", "Geok", "Geor", "Glag", "Goth", "Grek", "Polyt", "Gujr", "Guru", "Hang", "Hani", "Hano", "Hebr", "Hira", "Hluw", "Ital", "Java", "Kali", "Kana", "Khar", "Khmr", "Knda", "Kthi", "Lana", "Laoo", "Latn", "Latf", "Latg", "pjt-Latn", "Lepc", "Limb", "Linb", "Lisu", "Lyci", "Lydi", "Mand", "Mani", "Marc", "Merc", "Mero", "Mlym", "Mong", "mnc-Mong", "sjo-Mong", "xwo-Mong", "Mtei", "Mymr", "Narb", "Nkoo", "Nshu", "Ogam", "Olck", "Orkh", "Orya", "Osma", "Ougr", "Palm", "Phag", "Phli", "Phlv", "Phnx", "Plrd", "Prti", "Rjng", "Runr", "Samr", "Sarb", "Saur", "Sgnw", "Shaw", "Shrd", "Sinh", "Sora", "Sund", "Sylo", "Syrc", "Tagb", "Tale", "Talu", "Taml", "Tang", "Tavt", "Telu", "Tfng", "Tglg", "Thaa", "Thai", "Tibt", "Ugar", "Vaii", "Xpeo", "Xsux", "Yiii", "Zmth", "Zsym", "Ipach", "Music", "Rumin", } --[==[ var: Parts of speech which will not be categorised in categories like `English terms spelled with É` if the term is the character in question (e.g. the letter entry for English [[é]]). This contrasts with entries like the French adjective [[m̂]], which is a one-letter word spelled with the letter. ]==] data.pos_not_spelled_with_self = list_to_set{ "diacritical marks", "อักษรจีน", --Han characters "ฮั้นถื่อ", --Han tu "ฮันจา", --hanja "ฮั่นจื้อ", --hanzi "iteration marks", "คานะ", --kana "คันจิ", --kanji "ตัวอักษร", --letters "ตัวอักษรควบ", --ligatures "logograms", "morae", "ตัวเลข", --numeral symbols "เลข", --numerals "เครื่องหมายวรรคตอน", --punctuation marks "พยางค์", --syllables "สัญลักษณ์", --symbols } ------ 2. Lists not converted into sets. ------ --[==[ var: List of languages that will default to `sccat` being true, i.e. categories like `LANG POS in SCRIPT script` will automatically be generated. This can be overridden using {{para|sccat|0}} in {{tl|head}} or setting `sccat` to {false} in Lua. If the value on the right-hand side is {true}, such categories will be generated for all scripts. If {false}, no categories will be generated (this is useful for etym languages that override the spec of their parent). If a list of scripts, such categories will be generated only for the specified scripts. If a list of scripts where the first element is {"not"}, such categories will be generated for all scripts ''except'' the specified scripts. ]==] data.default_sccat = { ["af"] = {"not", "Latn"}, ["az"] = {"not", "Latn"}, ["inc-apa"] = true, ["inc-ash"] = true, ["kfr"] = true, ["ks"] = true, ["mr"] = true, ["mwr"] = true, ["inc-oaw"] = true, ["inc-ohi"] = true, ["omr"] = true, ["inc-opa"] = true, ["phr"] = true, ["pi"] = true, ["pra"] = true, ["sa"] = true, ["skr"] = true, ["sd"] = true, ["yi"] = {"not", "Hebr"}, } --[==[ var: Recognized aliases for parts of speech (param 2=). Key is the short form and value is the canonical singular (not pluralized) form. It is singular so the same table can be used in [[Module:form of]] for the {{para|p}}/{{para|POS}} param and [[Module:links]] for the pos= param. Note that any part of speech, abbreviated or not, can be suffixed with `f` to generate the corresponding non-lemma form part of speech, such as `adjf`, `af` or `adjectivef` for `adjective form`, and `nounf` or `nf` for `noun form`. This expansion happens even when it does not make sense for the given part of speech (e.g. `pclf` expands to `particle form` and `symf` expands to `symbol form`), and currently also, at least in [[Module:headword]] (but not [[Module:links]]), even if the part before the `f` is not a recognized part of speech or abbreviation (hence `nerf` expands to `ner form`). ]==] data.pos_aliases = { a = "คำคุณศัพท์", adj = "คำคุณศัพท์", adv = "คำกริยาวิเศษณ์", art = "คำกำกับนาม", aug = "ตัวบอกความใหญ่", cls = "คำลักษณนาม", compadj = "คำคุณศัพท์ขั้นกว่า", compadv = "คำกริยาวิเศษณ์ขั้นกว่า", compdet = "comparative determiner", comppron = "comparative pronoun", conj = "คำสันธาน", contr = "รูปหดย่อ", conv = "converb", det = "ตัวกำหนด", dim = "ตัวบอกความเล็ก", infin = "infinitive", -- not inf, which is ambiguous due to infixes int = "คำอุทาน", interj = "คำอุทาน", intj = "คำอุทาน", n = "คำนาม", -- the next two support Algonquian languages; see also vii/vai/vti/vta below na = "คำนามมีชีวิต", ni = "คำนามไม่มีชีวิต", num = "เลข", part = "รูปกริยาขยาย", pastpart = "รูปกริยาขยายอดีตกาล", pastptcp = "รูปกริยาขยายอดีตกาล", pcl = "คำอนุภาค", phr = "วลี", pn = "คำวิสามานยนาม", postp = "คำปัจฉบท", pref = "อุปสรรค", prep = "คำบุพบท", prepphr = "prepositional phrase", prespart = "รูปกริยาขยายปัจจุบันกาล", presptcp = "รูปกริยาขยายปัจจุบันกาล", pron = "คำสรรพนาม", prop = "คำวิสามานยนาม", proper = "คำวิสามานยนาม", propn = "คำวิสามานยนาม", ptcp = "รูปกริยาขยาย", rom = "การถอดเป็นอักษรโรมัน", roman = "การถอดเป็นอักษรโรมัน", romanisation = "การถอดเป็นอักษรโรมัน", romanisations = "การถอดเป็นอักษรโรมัน", suf = "ปัจจัย", supadj = "คำคุณศัพท์ขั้นสุด", supadv = "คำกริยาวิเศษณ์ขั้นสุด", supdet = "superlative determiner", suppron = "superlative pronoun", sym = "สัญลักษณ์", v = "คำกริยา", vb = "คำกริยา", vi = "คำอกรรมกริยา", vm = "modal verb", vt = "คำสกรรมกริยา", -- the next four support Algonquian languages vii = "คำอกรรมกริยาไม่มีชีวิต", vai = "คำอกรรมกริยามีชีวิต", vti = "คำสกรรมกริยาไม่มีชีวิต", vta = "คำสกรรมกริยามีชีวิต", } --[==[ var: Map of parts of speech for which categories like `German masculine nouns` or `Russian imperfective verbs` will be generated if the headword is of the appropriate gender/number. The map is used to canonicalize parts of speech for categorization purposes; specifically, proper nouns categorizes like nouns. ]==] data.pos_for_gender_number_cat = { ["nouns"] = "nouns", ["proper nouns"] = "nouns", ["suffixes"] = "suffixes", -- We include verbs because impf and pf are valid "genders". ["verbs"] = "verbs", } --[==[ var: Lower limit for a "long" word in a particular language. Used to categorize terms into e.g. [[:Category:Long English words]] automatically. Languages with no mapping here do not get categorized. ]==] data.long_word_thresholds = { ["af"] = 20, ["bg"] = 20, ["cy"] = 25, ["de"] = 20, ["en"] = 25, ["es"] = 20, ["fr"] = 20, ["ka"] = 20, ["sv"] = 20, ["tl"] = 25, } ------ 3. Page-wide processing (so that it only needs to be done once per page). ------ data.page = require(headword_page_module).process_page() -- Set some page properties directly on `data` for ease of use. data.pagename = data.page.pagename data.encoded_pagename = data.page.encoded_pagename return data rwisrku91hksp3um43395j85413ki76 มอดูล:Phnx-translit 828 169252 5775254 905222 2026-10-14T09:18:14Z Octahedron80 267 5775254 Scribunto text/plain local export = {} -- Beware! Phnx is rtl local tt = { ['𐤀'] = 'ʾ', ['𐤁'] = 'b', ['𐤂'] = 'g', ['𐤃'] = 'd', ['𐤄'] = 'h', ['𐤅'] = 'w', ['𐤆'] = 'z', ['𐤇'] = 'ḥ', ['𐤈'] = 'ṭ', ['𐤉'] = 'y', ['𐤊'] = 'k', ['𐤋'] = 'l', ['𐤌'] = 'm', ['𐤍'] = 'n', ['𐤎'] = 's', ['𐤏'] = 'ʿ', ['𐤐'] = 'p', ['𐤑'] = 'ṣ', ['𐤒'] = 'q', ['𐤓'] = 'r', ['𐤔'] = 'š', ['𐤕'] = 't', ['𐤖'] = '[1]', ['𐤗'] = '[10]', ['𐤘'] = '[20]', ['𐤙'] = '[100]', ['𐤚'] = '[2]', ['𐤛'] = '[3]', ['𐤟'] = ' ', } function export.tr(text, lang, sc) if type(text) == 'table' then -- called directly from a template text = text.args[1] end return (text:gsub('[%z\1-\127\194-\244][\128-\191]*', tt)) -- UTF-8 character pattern end return export mdrxj5su4a3or438g3t8bqjq13ap6te 5775256 5775254 2026-10-14T09:19:59Z Octahedron80 267 5775256 Scribunto text/plain local export = {} -- Beware! Phnx is rtl local tt = { ['𐤀'] = 'อ', ['𐤁'] = 'บ', ['𐤂'] = 'ค', ['𐤃'] = 'ด', ['𐤄'] = 'ฮ', ['𐤅'] = 'ว', ['𐤆'] = 'ซ', ['𐤇'] = 'ฮฺ', ['𐤈'] = 'ฏ', ['𐤉'] = 'ย', ['𐤊'] = 'ก', ['𐤋'] = 'ล', ['𐤌'] = 'ม', ['𐤍'] = 'น', ['𐤎'] = 'ส', ['𐤏'] = 'อฺ', ['𐤐'] = 'พ', ['𐤑'] = 'ศ', ['𐤒'] = 'กฺ', ['𐤓'] = 'ร', ['𐤔'] = 'ช', ['𐤕'] = 'ต', ['𐤖'] = '[1]', ['𐤗'] = '[10]', ['𐤘'] = '[20]', ['𐤙'] = '[100]', ['𐤚'] = '[2]', ['𐤛'] = '[3]', ['𐤟'] = ' ', } function export.tr(text, lang, sc) if type(text) == 'table' then -- called directly from a template text = text.args[1] end return (text:gsub('[%z\1-\127\194-\244][\128-\191]*', tt)) -- UTF-8 character pattern end return export 294dn2epn9up1qcwuoftblgwndzv1oz มอดูล:languages/code to canonical name 828 174988 5775301 5771175 2026-10-14T10:35:23Z Octahedron80 267 5775301 Scribunto text/plain return { ["aa"] = "อาฟาร์", ["aaa"] = "Ghotuo", ["aab"] = "Alumu-Tesu", ["aac"] = "Ari", ["aad"] = "Amal", ["aaf"] = "Aranadan", ["aag"] = "Ambrak", ["aah"] = "Abu'", ["aai"] = "Arifama-Miniafia", ["aak"] = "Ankave", ["aal"] = "Afade", ["aan"] = "Anambé", ["aap"] = "Arára (Pará)", ["aaq"] = "Penobscot", ["aas"] = "Aasax", ["aau"] = "Abau", ["aav-khs-pro"] = "คาเซียนดั้งเดิม", ["aav-nic-pro"] = "นิโคบารีสดั้งเดิม", ["aav-pkl-pro"] = "ปนัร-คาซี-ลึงงัมดั้งเดิม", ["aav-pro"] = "ออสโตรเอเชียติกดั้งเดิม", ["aaw"] = "Solong", ["aax"] = "Mandobo Atas", ["aaz"] = "Amarasi", ["ab"] = "อับคาเซีย", ["aba"] = "อาเบ", ["abb"] = "Bankon", ["abc"] = "Ambala Ayta", ["abd"] = "Camarines Norte Agta", ["abe"] = "Abenaki", ["abf"] = "Abai Sungai", ["abg"] = "Abaga", ["abh"] = "อาหรับแบบทาจิกิสถาน", ["abi"] = "Abidji", ["abj"] = "Aka-Bea", ["abl"] = "Abung", ["abm"] = "Abanyom", ["abn"] = "Abua", ["abo"] = "Abon", ["abp"] = "Abenlen Ayta", ["abq"] = "อาบาซา", ["abs"] = "Ambonese Malay", ["abt"] = "Ambulas", ["abu"] = "Abure", ["abv"] = "อาหรับแบบบาห์เรน", ["abw"] = "Pal", ["abx"] = "Inabaknon", ["aby"] = "Aneme Wake", ["abz"] = "Abui", ["aca"] = "Achagua", ["acb"] = "Áncá", ["acd"] = "Gikyode", ["ace"] = "อาเจะฮ์", ["ach"] = "Acholi", ["aci"] = "Aka-Cari", ["ack"] = "Aka-Kora", ["acl"] = "Akar-Bale", ["acm"] = "อาหรับแบบอิรัก", ["acn"] = "Achang", ["acp"] = "Eastern Acipa", ["acr"] = "Achi", ["acs"] = "Acroá", ["acu"] = "Achuar", ["acv"] = "Achumawi", ["acw"] = "อาหรับแบบฮิญาซ", ["acx"] = "อาหรับแบบโอมาน", ["acy"] = "อาหรับแบบไซปรัส", ["acz"] = "Acheron", ["ada"] = "Adangme", ["adb"] = "Atauran", ["add"] = "Dzodinka", ["ade"] = "Adele", ["adf"] = "Dhofari Arabic", ["adg"] = "Andegerebinha", ["adh"] = "Adhola", ["adi"] = "Adi", ["adj"] = "Adioukrou", ["adl"] = "Galo", ["adn"] = "Adang", ["ado"] = "Abu", ["adp"] = "Adap", ["adq"] = "Adangbe", ["adr"] = "Adonara", ["ads"] = "Adamorobe Sign Language", ["adt"] = "Adnyamathanha", ["adu"] = "Aduge", ["adw"] = "Amondawa", ["ady"] = "อะดีเกยา", ["adz"] = "Adzera", ["ae"] = "อเวสตะ", ["aea"] = "Areba", ["aeb"] = "อาหรับแบบตูนิเซีย", ["aed"] = "Argentine Sign Language", ["aee"] = "Northeast Pashayi", ["aek"] = "Haeke", ["ael"] = "Ambele", ["aem"] = "Arem", ["aen"] = "Armenian Sign Language", ["aeq"] = "Aer", ["aer"] = "Eastern Arrernte", ["aes"] = "Alsea", ["aeu"] = "Akeu", ["aew"] = "Ambakich", ["aey"] = "Amele", ["aez"] = "Aeka", ["af"] = "อาฟรีกานส์", ["afa-pro"] = "แอฟโฟรเอเชียติกดั้งเดิม", ["afb"] = "Gulf Arabic", ["afd"] = "Andai", ["afe"] = "Putukwam", ["afg"] = "Afghan Sign Language", ["afh"] = "Afrihili", ["afi"] = "Akrukay", ["afk"] = "Nanubae", ["afn"] = "Defaka", ["afo"] = "Eloyi", ["afp"] = "Tapei", ["afs"] = "Afro-Seminole Creole", ["aft"] = "Afitti", ["afu"] = "Awutu", ["afz"] = "Obokuitai", ["aga"] = "Aguano", ["agb"] = "Legbo", ["agc"] = "Agatu", ["agd"] = "Agarabi", ["age"] = "Angal", ["agf"] = "Arguni", ["agg"] = "Angor", ["agh"] = "Ngelima", ["agi"] = "Agariya", ["agj"] = "Argobba", ["agk"] = "Isarog Agta", ["agl"] = "Fembe", ["agm"] = "Angaataha", ["agn"] = "อากูตายา", ["ago"] = "Tainae", ["agq"] = "Aghem", ["agr"] = "Aguaruna", ["ags"] = "Esimbi", ["agt"] = "Central Cagayan Agta", ["agu"] = "Aguacateca", ["agv"] = "Remontado Agta", ["agw"] = "Kahua", ["agx"] = "Aghul", ["agy"] = "Southern Alta", ["agz"] = "Mount Iriga Agta", ["aha"] = "Ahanta", ["ahb"] = "Axamb", ["ahg"] = "Qimant", ["ahh"] = "Aghu", ["ahi"] = "Tiagba", ["ahk"] = "อาข่า", ["ahl"] = "Igo", ["ahm"] = "Mobu", ["ahn"] = "Àhàn", ["aho"] = "อาหม", ["ahp"] = "Apro", ["ahr"] = "Ahirani", ["ahs"] = "Ashe", ["aht"] = "Ahtna", ["aia"] = "Arosi", ["aib"] = "Äynu", ["aic"] = "Ainbai", ["aid"] = "Alngith", ["aie"] = "Amara", ["aif"] = "Agi", ["aig"] = "Antigua and Barbuda Creole English", ["aih"] = "Ai-Cham", ["aii"] = "อารามายาใหม่แบบอัสซีเรีย", ["aij"] = "Lishanid Noshan", ["aik"] = "Ake", ["ail"] = "Aimele", ["aim"] = "Aimol", ["ain"] = "ไอนุ", ["aio"] = "อ่ายตน", ["aip"] = "Burumakok", ["air"] = "Airoran", ["ait"] = "Arikem", ["aiw"] = "Aari", ["aix"] = "Aighon", ["aiy"] = "Ali", ["aja"] = "Aja", ["ajg"] = "Adja", ["aji"] = "Ajië", ["ajn"] = "Andajin", ["ajp"] = "อาหรับแบบลิแวนต์ใต้", ["ajw"] = "Ajawa", ["ajz"] = "Amri Karbi", ["ak"] = "อาคัน", ["akb"] = "Angkola Batak", ["akc"] = "Mpur", ["akd"] = "Ukpet-Ehom", ["ake"] = "Akawaio", ["akf"] = "Akpa", ["akg"] = "Anakalangu", ["akh"] = "Angal Heneng", ["aki"] = "Aiome", ["akj"] = "Jeru", ["akk"] = "แอกแคด", ["akl"] = "อักลัน", ["akm"] = "Aka-Bo", ["ako"] = "Akurio", ["akp"] = "Siwu", ["akq"] = "Ak", ["akr"] = "Araki", ["aks"] = "Akaselem", ["akt"] = "Akolet", ["aku"] = "Akum", ["akv"] = "Akhvakh", ["akw"] = "Akwa", ["akx"] = "Aka-Kede", ["aky"] = "Aka-Kol", ["akz"] = "แอละแบมา", ["ala"] = "Alago", ["alc"] = "Kawésqar", ["ald"] = "Alladian", ["ale"] = "Aleut", ["alf"] = "Alege", ["alg-aga"] = "Agawam", ["alg-pro"] = "แอลกองเคียนดั้งเดิม", ["alh"] = "Alawa", ["ali"] = "Amaimon", ["alj"] = "Alangan", ["alk"] = "Alak", ["all"] = "Allar", ["alm"] = "Amblong", ["alo"] = "Larike-Wakasihu", ["alp"] = "Alune", ["alq"] = "Algonquin", ["alr"] = "Alutor", ["alt"] = "อัลไตใต้", ["alu"] = "'Are'are", ["alv-ama"] = "Amasi", ["alv-bgu"] = "Baïnounk Gubëeher", ["alv-bua-pro"] = "Proto-Bua", ["alv-cng-pro"] = "Proto-Cangin", ["alv-edk-pro"] = "Proto-Edekiri", ["alv-edo-pro"] = "Proto-Edoid", ["alv-fli-pro"] = "Proto-Fali", ["alv-gbe-pro"] = "กเบดั้งเดิม", ["alv-gng-pro"] = "Proto-Guang", ["alv-gtm-pro"] = "Proto-Central Togo", ["alv-gwa"] = "Gwara", ["alv-hei-pro"] = "Proto-Heiban", ["alv-ido-pro"] = "Proto-Idomoid", ["alv-igb-pro"] = "Proto-Igboid", ["alv-kwa-pro"] = "Proto-Kwa", ["alv-mum-pro"] = "Proto-Mumuye", ["alv-nup-pro"] = "Proto-Nupoid", ["alv-pro"] = "แอตแลนติก-คองโกดั้งเดิม", ["alv-von-pro"] = "วอลตา-ไนเจอร์ดั้งเดิม", ["alv-yor-pro"] = "โยรูบาดั้งเดิม", ["alv-yrd-pro"] = "โยรูบอยด์ดั้งเดิม", ["alw"] = "Alaba", ["alx"] = "Amol", ["aly"] = "Alyawarr", ["alz"] = "Alur", ["am"] = "อัมฮารา", ["ama"] = "Amanayé", ["amb"] = "Ambo", ["amc"] = "Amahuaca", ["ame"] = "Yanesha'", ["amf"] = "Hamer-Banna", ["amg"] = "Amurdag", ["ami"] = "Amis", ["amj"] = "Amdang", ["amk"] = "Ambai", ["aml"] = "War-Jaintia", ["amm"] = "Ama", ["amn"] = "Amanab", ["amo"] = "Amo", ["amp"] = "Alamblak", ["amq"] = "Amahai", ["amr"] = "Amarakaeri", ["ams"] = "อามามิโอชิมะใต้", ["amt"] = "Amto", ["amu"] = "Guerrero Amuzgo", ["amv"] = "Ambelau", ["amw"] = "Western Neo-Aramaic", ["amx"] = "Anmatyerre", ["amy"] = "Ami", ["amz"] = "Atampaya", ["an"] = "อารากอน", ["ana"] = "Andaqui", ["anb"] = "Andoa", ["anc"] = "Ngas", ["and"] = "Ansus", ["ane"] = "คังรังชือ", ["anf"] = "Animere", ["ang"] = "อังกฤษเก่า", ["anh"] = "Nend", ["ani"] = "Andi", ["anj"] = "Anor", ["ank"] = "Goemai", ["anl"] = "Anu", ["anm"] = "Anāl", ["ann"] = "Obolo", ["ano"] = "Andoque", ["anp"] = "Angika", ["anq"] = "Jarawa", ["anr"] = "Andh", ["ans"] = "Anserma", ["ant"] = "Antakarinya", ["anu"] = "Anuak", ["anv"] = "Denya", ["anw"] = "Anaang", ["anx"] = "Andra-Hus", ["any"] = "Anyi", ["anz"] = "Anem", ["aoa"] = "Angolar", ["aob"] = "Abom", ["aoc"] = "Pemon", ["aod"] = "Andarum", ["aoe"] = "Angal Enen", ["aof"] = "Bragat", ["aog"] = "Angoram", ["aoi"] = "Anindilyakwa", ["aoj"] = "Mufian", ["aok"] = "Arhö", ["aol"] = "Alorese", ["aom"] = "Ömie", ["aon"] = "Bumbita Arapesh", ["aor"] = "Aore", ["aos"] = "Taikat", ["aot"] = "อะตง", ["aou"] = "A'ou", ["aox"] = "Atorada", ["aoz"] = "Uab Meto", ["apa-pro"] = "Proto-Apachean", ["apb"] = "Sa'a", ["apc"] = "North Levantine Arabic", ["apd"] = "อาหรับแบบซูดาน", ["ape"] = "Bukiyip", ["apf"] = "Pahanan Agta", ["apg"] = "Ampanang", ["aph"] = "Athpare", ["api"] = "Apiaká", ["apj"] = "Jicarilla", ["apk"] = "Plains Apache", ["apl"] = "Lipan", ["apm"] = "Chiricahua", ["apn"] = "Apinayé", ["apo"] = "Ambul", ["app"] = "Apma", ["apq"] = "A-Pucikwar", ["apr"] = "Arop-Lokep", ["aps"] = "Arop-Sissano", ["apt"] = "Apatani", ["apu"] = "Apurinã", ["apv"] = "Alapmunte", ["apw"] = "อะแพชีตะวันตก", ["apx"] = "Aputai", ["apy"] = "Apalaí", ["apz"] = "Safeyoka", ["aqc"] = "Archi", ["aqd"] = "Ampari Dogon", ["aqg"] = "Arigidi", ["aql-pro"] = "แอลจิกดั้งเดิม", ["aqm"] = "Atohwaim", ["aqn"] = "Northern Alta", ["aqp"] = "Atakapa", ["aqr"] = "Arhâ", ["aqt"] = "Angaité", ["aqz"] = "Akuntsu", ["ar"] = "อาหรับ", ["arc"] = "อารามายา", ["ard"] = "Arabana", ["are"] = "Western Arrernte", ["arh"] = "Arhuaco", ["ari"] = "Arikara", ["arj"] = "Arapaso", ["ark"] = "Arikapú", ["arl"] = "Arabela", ["arn"] = "Mapudungun", ["aro"] = "Araona", ["arp"] = "Arapaho", ["arq"] = "อาหรับแบบแอลจีเรีย", ["arr"] = "Arara-Karo", ["ars"] = "Najdi Arabic", ["art-adu"] = "Adûni", ["art-bel"] = "Belter Creole", ["art-blk"] = "Bolak", ["art-bsp"] = "แบล็กสปีช", ["art-com"] = "Communicationssprache", ["art-dtk"] = "Dothraki", ["art-elo"] = "Eloi", ["art-gld"] = "Goa'uld", ["art-lap"] = "Lapine", ["art-man"] = "Mandalorian", ["art-mun"] = "Mundolinco", ["art-nav"] = "Naʼvi", ["art-vlh"] = "High Valyrian", ["aru"] = "Arua", ["arv"] = "Arbore", ["arw"] = "โลโกโน", ["arx"] = "Aruá", ["ary"] = "อาหรับแบบโมร็อกโก", ["arz"] = "อาหรับแบบอียิปต์", ["as"] = "อัสสัม", ["asa"] = "Pare", ["asb"] = "Assiniboine", ["asc"] = "Casuarina Coast Asmat", ["ase"] = "มืออเมริกัน", ["asf"] = "Auslan", ["asg"] = "Cishingini", ["ash"] = "Abishira", ["asi"] = "Buruwai", ["asj"] = "Nsari", ["ask"] = "Ashkun", ["asl"] = "Asilulu", ["asn"] = "Xingú Asuriní", ["aso"] = "Dano", ["asp"] = "Algerian Sign Language", ["asq"] = "Austrian Sign Language", ["asr"] = "Asuri", ["ass"] = "Ipulo", ["ast"] = "อัสตูเรียส", ["asu"] = "Tocantins Asurini", ["asv"] = "Asoa", ["asw"] = "Australian Aboriginal Sign Language", ["asx"] = "Muratayak", ["asy"] = "Yaosakor Asmat", ["asz"] = "As", ["ata"] = "Pele-Ata", ["atb"] = "Zaiwa", ["atc"] = "Atsahuaca", ["atd"] = "Ata Manobo", ["ate"] = "Atemble", ["atg"] = "Okpela", ["ath-nic"] = "Nicola", ["ath-pro"] = "Proto-Athabaskan", ["ati"] = "Attié", ["atj"] = "Atikamekw", ["atk"] = "Ati", ["atl"] = "Mount Iraya Agta", ["atm"] = "Ata", ["ato"] = "Atong (Cameroon)", ["atp"] = "Pudtol Atta", ["atq"] = "Aralle-Tabulahan", ["atr"] = "Waimiri-Atroari", ["ats"] = "Gros Ventre", ["att"] = "Pamplona Atta", ["atu"] = "Reel", ["atv"] = "อัลไตเหนือ", ["atw"] = "Atsugewi", ["atx"] = "Arutani", ["aty"] = "อาเนตยูม", ["atz"] = "Arta", ["aua"] = "Asumboa", ["aub"] = "Alugu", ["auc"] = "Huaorani", ["aud"] = "Anuta", ["auf-pro"] = "Proto-Arawa", ["aug"] = "Aguna", ["auh"] = "Aushi", ["aui"] = "Anuki", ["auj"] = "Awjila", ["auk"] = "Heyo", ["aul"] = "Aulua", ["aum"] = "Asu", ["aun"] = "Molmo One", ["auo"] = "Auyokawa", ["aup"] = "Makayam", ["auq"] = "Anus", ["aur"] = "Aruek", ["aus-alu"] = "Alungul", ["aus-and"] = "Andjingith", ["aus-ang"] = "Angkula", ["aus-arn-pro"] = "Proto-Arnhem", ["aus-bra"] = "Barranbinya", ["aus-brm"] = "Barunggam", ["aus-cww-pro"] = "Proto-Central New South Wales", ["aus-dal-pro"] = "Proto-Daly", ["aus-guw"] = "Guwar", ["aus-lsw"] = "Little Swanport", ["aus-mbi"] = "Mbiywom", ["aus-ngk"] = "Ngkoth", ["aus-nyu-pro"] = "Proto-Nyulnyulan", ["aus-pam-pro"] = "Proto-Pama-Nyungan", ["aus-tul"] = "Tulua", ["aus-uwi"] = "Uwinymil", ["aus-wdj-pro"] = "Proto-Iwaidjan", ["aus-won"] = "Wong-gie", ["aus-wul"] = "Wulguru", ["aus-ynk"] = "Yangkaal", ["aut"] = "Austral", ["auu"] = "Auye", ["auw"] = "Awyi", ["aux"] = "Aurá", ["auy"] = "Auyana", ["auz"] = "อาหรับแบบอุซเบกิสถาน", ["av"] = "อะวาร์", ["avb"] = "Avau", ["avd"] = "Alviri-Vidari", ["avi"] = "Avikam", ["avk"] = "โคทาวา", ["avm"] = "Angkamuthi", ["avn"] = "Avatime", ["avo"] = "Agavotaguerra", ["avs"] = "Aushiri", ["avt"] = "Au", ["avu"] = "Avokaya", ["avv"] = "Avá-Canoeiro", ["awa"] = "อวัธ", ["awb"] = "Awa (New Guinea)", ["awc"] = "Cicipu", ["awd-ama"] = "Amarizana", ["awd-amc-pro"] = "Proto-Amuesha-Chamicuro", ["awd-ana"] = "Anauyá", ["awd-apo"] = "Apolista", ["awd-cab"] = "Cabre", ["awd-gnu"] = "Guinau", ["awd-kar"] = "Cariay", ["awd-kaw"] = "Kawishana", ["awd-kmp-pro"] = "Proto-Kampa", ["awd-kus"] = "Kustenau", ["awd-man"] = "Manao", ["awd-mar"] = "Marawan", ["awd-mpr"] = "Maipure", ["awd-mrt"] = "Mariaté", ["awd-nwk-pro"] = "Proto-Nawiki", ["awd-pai"] = "Paikoneka", ["awd-pas"] = "Pasé", ["awd-pro"] = "Proto-Arawak", ["awd-prw-pro"] = "Proto-Paresi-Waura", ["awd-she"] = "Shebayo", ["awd-taa-pro"] = "Proto-Ta-Arawak", ["awd-wai"] = "Wainumá", ["awd-yum"] = "Yumana", ["awe"] = "Awetí", ["awg"] = "Anguthimri", ["awh"] = "Awbono", ["awi"] = "Aekyom", ["awk"] = "Awabakal", ["awm"] = "Arawum", ["awn"] = "Awngi", ["awo"] = "Awak", ["awr"] = "Awera", ["aws"] = "South Awyu", ["awt"] = "Araweté", ["awu"] = "Central Awyu", ["awv"] = "Jair Awyu", ["aww"] = "Awun", ["awx"] = "Awara", ["awy"] = "Edera Awyu", ["axb"] = "Abipón", ["axe"] = "Ayerrerenge", ["axg"] = "Arára (Mato Grosso)", ["axk"] = "Aka (Central Africa)", ["axl"] = "Lower Southern Aranda", ["axm"] = "อาร์มีเนียกลาง", ["axx"] = "Xârâgurè", ["ay"] = "ไอย์มารา", ["aya"] = "Awar", ["ayb"] = "Ayizo", ["ayd"] = "Ayabadhu", ["aye"] = "Ayere", ["ayg"] = "Nyanga (Togo)", ["ayi"] = "Leyigha", ["ayk"] = "Akuku", ["ayl"] = "อาหรับแบบลิเบีย", ["ayn"] = "อาหรับแบบเยเมน", ["ayo"] = "Ayoreo", ["ayp"] = "อาหรับแบบเมโสโปเตเมียเหนือ", ["ayq"] = "Ayi", ["ays"] = "Sorsogon Ayta", ["ayt"] = "Bataan Ayta", ["ayu"] = "Ayu", ["ayy"] = "Tayabas Ayta", ["ayz"] = "Maybrat", ["az"] = "อาเซอร์ไบจาน", ["aza"] = "Azha", ["azc-caz"] = "Cazcan", ["azc-cup-pro"] = "Proto-Cupan", ["azc-ktn"] = "Kitanemuk", ["azc-nah-pro"] = "นาวันดั้งเดิม", ["azc-num-pro"] = "Proto-Numic", ["azc-pro"] = "ยูโต-แอซเทกันดั้งเดิม", ["azc-tak-pro"] = "Proto-Takic", ["azc-tat"] = "Tataviam", ["azd"] = "Eastern Durango Nahuatl", ["azg"] = "San Pedro Amuzgos Amuzgo", ["azm"] = "Ipalapa Amuzgo", ["azn"] = "Western Durango Nahuatl", ["azo"] = "Awing", ["azt"] = "Faire Atta", ["azz"] = "Highland Puebla Nahuatl", ["ba"] = "แบชเคียร์", ["baa"] = "Babatana", ["bab"] = "Bainouk-Gunyuño", ["bac"] = "Baduy", ["bae"] = "Baré", ["baf"] = "Nubaca", ["bag"] = "Tuki", ["bah"] = "Bahamian Creole", ["baj"] = "Barakai", ["bal"] = "บาโลจ", ["ban"] = "บาหลี", ["bao"] = "Waimaha", ["bap"] = "Bantawa", ["bar"] = "บาวาเรีย", ["bas"] = "Basaa", ["bau"] = "Badanchi", ["bav"] = "Babungo", ["baw"] = "Bambili-Bambui", ["bax"] = "Bamum", ["bay"] = "Batuley", ["bba"] = "Baatonum", ["bbb"] = "Barai", ["bbc"] = "Toba Batak", ["bbd"] = "Bau", ["bbe"] = "Bangba", ["bbf"] = "Baibai", ["bbg"] = "Barama", ["bbh"] = "Bugan", ["bbi"] = "Barombi", ["bbj"] = "Ghomala'", ["bbk"] = "Babanki", ["bbl"] = "บัตส์", ["bbm"] = "Babango", ["bbn"] = "Uneapa", ["bbo"] = "Konabéré", ["bbp"] = "West Central Banda", ["bbq"] = "Bamali", ["bbr"] = "Girawa", ["bbs"] = "Bakpinka", ["bbt"] = "Mburku", ["bbu"] = "Bakulung", ["bbv"] = "Karnai", ["bbw"] = "Baba", ["bbx"] = "Bubia", ["bby"] = "Befang", ["bca"] = "Central Bai", ["bcb"] = "Bainouk-Samik", ["bcd"] = "North Babar", ["bce"] = "Bamenyam", ["bcf"] = "Bamu", ["bcg"] = "Baga Pokur", ["bch"] = "Bariai", ["bci"] = "Baoule", ["bcj"] = "Bardi", ["bck"] = "Bunaba", ["bcl"] = "บีโคลตอนกลาง", ["bcm"] = "Banoni", ["bcn"] = "Bibaali", ["bco"] = "Kaluli", ["bcp"] = "Bali", ["bcq"] = "Bench", ["bcr"] = "Babine-Witsuwit'en", ["bcs"] = "Kohumono", ["bct"] = "Bendi", ["bcu"] = "Biliau", ["bcv"] = "Shoo-Minda-Nye", ["bcw"] = "Bana", ["bcy"] = "Bacama", ["bcz"] = "Bainouk-Gunyaamolo", ["bda"] = "Bayot", ["bdb"] = "Basap", ["bdc"] = "Emberá-Baudó", ["bdd"] = "Bunama", ["bde"] = "Bade", ["bdf"] = "Biage", ["bdg"] = "Bonggi", ["bdh"] = "Tara Baka", ["bdi"] = "Burun", ["bdj"] = "Bai (South Sudan)", ["bdk"] = "Budukh", ["bdl"] = "บาเจาแบบอินโดนีเซีย", ["bdm"] = "Buduma", ["bdn"] = "Baldemu", ["bdo"] = "Morom", ["bdp"] = "Bende", ["bdq"] = "บะห์นัร", ["bdr"] = "บาเจาแบบเวสต์โคสต์", ["bds"] = "Burunge", ["bdt"] = "Bokoto", ["bdu"] = "Oroko", ["bdv"] = "Bodo Parja", ["bdw"] = "Baham", ["bdx"] = "Budong-Budong", ["bdy"] = "Bandjalang", ["bdz"] = "Badeshi", ["be"] = "เบลารุส", ["bea"] = "Beaver", ["beb"] = "Bebele", ["bec"] = "Iceve-Maci", ["bed"] = "Bedoanas", ["bee"] = "Byangsi", ["bef"] = "Benabena", ["beg"] = "Belait", ["beh"] = "Biali", ["bei"] = "Bekati'", ["bej"] = "Beja", ["bek"] = "Bebeli", ["bem"] = "Bemba", ["beo"] = "Beami", ["bep"] = "Besoa", ["beq"] = "Beembe", ["ber-fog"] = "Fogaha", ["ber-pro"] = "เบอร์เบอร์ดั้งเดิม", ["ber-zuw"] = "Zuwara", ["bes"] = "Besme", ["bet"] = "Guiberoua Bété", ["beu"] = "บลาการ์", ["bev"] = "Daloa Bété", ["bew"] = "เบอตาวี", ["bex"] = "Jur Modo", ["bey"] = "Akuwagel", ["bez"] = "Kibena", ["bfa"] = "Bari", ["bfb"] = "Pauri Bareli", ["bfc"] = "Panyi Bai", ["bfd"] = "Bafut", ["bfe"] = "Betaf", ["bff"] = "Bofi", ["bfg"] = "Busang Kayan", ["bfh"] = "Blafe", ["bfi"] = "British Sign Language", ["bfj"] = "Bafanji", ["bfk"] = "Ban Khor Sign Language", ["bfl"] = "Banda-Ndélé", ["bfm"] = "Mmen", ["bfn"] = "Bunak", ["bfo"] = "Malba Birifor", ["bfp"] = "Beba", ["bfq"] = "พทคะ", ["bfr"] = "Bazigar", ["bfs"] = "Southern Bai", ["bft"] = "บัลติ", ["bfu"] = "Gahri", ["bfw"] = "Bondo", ["bfx"] = "Bantayanon", ["bfy"] = "Bagheli", ["bfz"] = "Mahasu Pahari", ["bg"] = "บัลแกเรีย", ["bga"] = "Gwamhi-Wuri", ["bgb"] = "Bobongko", ["bgc"] = "หริยาณวี", ["bgd"] = "Rathwi Bareli", ["bge"] = "Bauria", ["bgf"] = "Bangandu", ["bgg"] = "Bugun", ["bgi"] = "Giangan", ["bgj"] = "Bangolan", ["bgk"] = "Bit", ["bgl"] = "Bo", ["bgo"] = "Baga Koga", ["bgq"] = "Bagri", ["bgr"] = "Bawm Chin", ["bgs"] = "Tagabawa", ["bgt"] = "Bughotu", ["bgu"] = "Mbongno", ["bgv"] = "Warkay-Bipim", ["bgw"] = "Bhatri", ["bgx"] = "Balkan Gagauz Turkish", ["bgy"] = "Benggoi", ["bgz"] = "Banggai", ["bh"] = "พิหาร", ["bha"] = "Bharia", ["bhb"] = "Bhili", ["bhc"] = "Biga", ["bhd"] = "Bhadrawahi", ["bhe"] = "Bhaya", ["bhf"] = "Odiai", ["bhg"] = "Binandere", ["bhh"] = "Bukhari", ["bhi"] = "Bhilali", ["bhj"] = "Bahing", ["bhl"] = "Bimin", ["bhm"] = "Bathari", ["bhn"] = "Bohtan Neo-Aramaic", ["bho"] = "โภชปุระ", ["bhp"] = "Bima", ["bhq"] = "Tukang Besi South", ["bhs"] = "Buwal", ["bht"] = "Bhattiyali", ["bhu"] = "Bhunjia", ["bhv"] = "Bahau", ["bhw"] = "Biak", ["bhx"] = "Bhalay", ["bhy"] = "Bhele", ["bhz"] = "Bada", ["bi"] = "บิสลามา", ["bia"] = "Badimaya", ["bib"] = "Bissa", ["bic"] = "Bikaru", ["bid"] = "Bidiyo", ["bie"] = "Bepour", ["bif"] = "Biafada", ["big"] = "Biangai", ["bij"] = "Kwanka", ["bil"] = "Bile", ["bim"] = "Bimoba", ["bin"] = "Edo", ["bio"] = "Nai", ["bip"] = "Bila", ["biq"] = "Bipi", ["bir"] = "Bisorio", ["bit"] = "Berinomo", ["biu"] = "Biete", ["biv"] = "Southern Birifor", ["biw"] = "Kol (Cameroon)", ["bix"] = "Bijori", ["biy"] = "Birhor", ["biz"] = "Baloi", ["bja"] = "Budza", ["bjb"] = "Barngarla", ["bjc"] = "Bariji", ["bje"] = "Biao-Jiao Mien", ["bjf"] = "Barzani Jewish Neo-Aramaic", ["bjg"] = "Bidyogo", ["bjh"] = "Bahinemo", ["bji"] = "Burji", ["bjj"] = "Kannauji", ["bjk"] = "Barok", ["bjl"] = "Bulu (New Guinea)", ["bjm"] = "Bajelani", ["bjn"] = "บันจาร์", ["bjo"] = "Mid-Southern Banda", ["bjp"] = "Fanamaket", ["bjr"] = "Binumarien", ["bjs"] = "Bajan", ["bjt"] = "Balanta-Ganja", ["bju"] = "Busuu", ["bjv"] = "Bedjond", ["bjw"] = "Bakwé", ["bjx"] = "Banao Itneg", ["bjy"] = "Bayali", ["bjz"] = "Baruga", ["bka"] = "Kyak", ["bkc"] = "Baka", ["bkd"] = "บีนูกิด", ["bkf"] = "Beeke", ["bkg"] = "Buraka", ["bkh"] = "Bakoko", ["bki"] = "Baki", ["bkj"] = "Pande", ["bkk"] = "Brokskat", ["bkl"] = "Berik", ["bkm"] = "Kom (Cameroon)", ["bkn"] = "Bukitan", ["bko"] = "Kwa'", ["bkp"] = "Iboko", ["bkq"] = "Bakairí", ["bkr"] = "Bakumpai", ["bks"] = "Masbate Sorsogon", ["bkt"] = "Boloki", ["bku"] = "Buhid", ["bkv"] = "Bekwarra", ["bkw"] = "Bekwel", ["bkx"] = "Baikeno", ["bky"] = "Bokyi", ["bkz"] = "Bungku", ["bla"] = "Blackfoot", ["blb"] = "Bilua", ["blc"] = "Bella Coola", ["bld"] = "Bolango", ["ble"] = "Balanta-Kentohe", ["blf"] = "Buol", ["blg"] = "Balau", ["blh"] = "Kuwaa", ["bli"] = "Bolia", ["blj"] = "Bulungan", ["blk"] = "กะเหรี่ยงปะโอ", ["bll"] = "Biloxi", ["blm"] = "Beli", ["bln"] = "Southern Catanduanes Bicolano", ["blo"] = "Anii", ["blp"] = "Blablanga", ["blq"] = "Baluan-Pam", ["blr"] = "Blang", ["bls"] = "Balaesang", ["blt"] = "ไทดำ", ["blv"] = "Kibala", ["blw"] = "Balangao", ["blx"] = "Mag-Indi Ayta", ["bly"] = "Notre", ["blz"] = "Balantak", ["bm"] = "บัมบารา", ["bma"] = "Lame", ["bmb"] = "Bembe", ["bmc"] = "Biem", ["bmd"] = "Baga Manduri", ["bme"] = "Limassa", ["bmf"] = "Bom", ["bmg"] = "Bamwe", ["bmh"] = "Kein", ["bmi"] = "Bagirmi", ["bmj"] = "Bote-Majhi", ["bmk"] = "Ghayavi", ["bml"] = "Bomboli", ["bmn"] = "Bina", ["bmo"] = "Bambalang", ["bmp"] = "Bulgebi", ["bmq"] = "Bomu", ["bmr"] = "Muinane", ["bmt"] = "Biao Mon", ["bmu"] = "Somba-Siawari", ["bmv"] = "Bum", ["bmw"] = "Bomwali", ["bmx"] = "Baimak", ["bmz"] = "Baramu", ["bn"] = "เบงกอล", ["bna"] = "Bonerate", ["bnb"] = "Bookan", ["bnd"] = "Banda", ["bne"] = "Bintauna", ["bnf"] = "Masiwang", ["bng"] = "Benga", ["bni"] = "Bangi", ["bnj"] = "Eastern Tawbuid", ["bnk"] = "Bierebo", ["bnl"] = "Boon", ["bnm"] = "Batanga", ["bnn"] = "Bunun", ["bno"] = "อาซี", ["bnp"] = "Bola", ["bnq"] = "Bantik", ["bnr"] = "Butmas-Tur", ["bns"] = "Bundeli", ["bnt-bal"] = "Balong", ["bnt-bon"] = "Boma Nkuu", ["bnt-boy"] = "Boma Yumu", ["bnt-bwa"] = "Bwala", ["bnt-cmw"] = "Chimwiini", ["bnt-ind"] = "Indanga", ["bnt-lal"] = "Lala (South Africa)", ["bnt-mpi"] = "Mpiin", ["bnt-mpu"] = "Mpuono", ["bnt-ngu-pro"] = "งูนีดั้งเดิม", ["bnt-phu"] = "Phuthi", ["bnt-pro"] = "แบนทูดั้งเดิม", ["bnt-sab-pro"] = "Proto-Sabaki", ["bnt-sbo"] = "South Boma", ["bnt-sts-pro"] = "Proto-Sotho-Tswana", ["bnu"] = "Bentong", ["bnv"] = "Beneraf", ["bnw"] = "Bisis", ["bnx"] = "Bangubangu", ["bny"] = "Bintulu", ["bnz"] = "Beezen", ["bo"] = "ทิเบต", ["boa"] = "Bora", ["bob"] = "Aweer", ["boe"] = "Mundabli", ["bof"] = "Bolon", ["bog"] = "Bamako Sign Language", ["boh"] = "North Boma", ["boi"] = "Barbareño", ["boj"] = "Anjam", ["bok"] = "Bonjo", ["bol"] = "โบล", ["bom"] = "Berom", ["bon"] = "Bine", ["boo"] = "Tiemacèwè Bozo", ["bop"] = "Bonkiman", ["boq"] = "Bogaya", ["bor"] = "Borôro", ["bot"] = "Bongo", ["bou"] = "Bondei", ["bov"] = "Tuwuli", ["bow"] = "Rema", ["box"] = "Buamu", ["boy"] = "Bodo (Central Africa)", ["boz"] = "Tiéyaxo Bozo", ["bpa"] = "Daakaka", ["bpd"] = "Banda-Banda", ["bpg"] = "Bonggo", ["bph"] = "Botlikh", ["bpi"] = "Bagupi", ["bpj"] = "Binji", ["bpk"] = "Orowe", ["bpl"] = "Broome Pearling Lugger Pidgin", ["bpm"] = "Biyom", ["bpn"] = "Dzao Min", ["bpo"] = "Anasi", ["bpp"] = "Kaure", ["bpq"] = "Banda Malay", ["bpr"] = "Koronadal Blaan", ["bps"] = "Sarangani Blaan", ["bpt"] = "Barrow Point", ["bpu"] = "Bongu", ["bpv"] = "Bian Marind", ["bpx"] = "Palya Bareli", ["bpy"] = "Bishnupriya Manipuri", ["bpz"] = "Bilba", ["bqa"] = "Tchumbuli", ["bqb"] = "Bagusa", ["bqc"] = "Boko", ["bqd"] = "Bung", ["bqf"] = "Baga Kaloum", ["bqg"] = "Bago-Kusuntu", ["bqh"] = "Baima", ["bqi"] = "Bakhtiari", ["bqj"] = "Bandial", ["bqk"] = "Banda-Mbrès", ["bql"] = "Bilakura", ["bqm"] = "Wumboko", ["bqn"] = "Bulgarian Sign Language", ["bqo"] = "Balo", ["bqp"] = "Busa", ["bqq"] = "Biritai", ["bqr"] = "Burusu", ["bqs"] = "Bosngun", ["bqt"] = "Bamukumbit", ["bqu"] = "Boguru", ["bqv"] = "Begbere-Ejar", ["bqw"] = "Buru (Nigeria)", ["bqx"] = "Baangi", ["bqy"] = "Bengkala Sign Language", ["bqz"] = "Bakaka", ["br"] = "เบรอตง", ["bra"] = "พรัช", ["brb"] = "Lave", ["brc"] = "Berbice Creole Dutch", ["brd"] = "Baraamu", ["brf"] = "Bera", ["brg"] = "Baure", ["brh"] = "บราฮุอี", ["bri"] = "Mokpwe", ["brj"] = "Bieria", ["brk"] = "Birgid", ["brl"] = "Birwa", ["brm"] = "Barambu", ["brn"] = "Boruca", ["bro"] = "Brokkat", ["brp"] = "Barapasi", ["brq"] = "Breri", ["brr"] = "Birao", ["brs"] = "Baras", ["brt"] = "Bitare", ["bru"] = "บรูตะวันออก", ["brv"] = "บรูตะวันตก", ["brw"] = "Bellari", ["brx"] = "โบโด", ["bry"] = "Burui", ["brz"] = "Bilbil", ["bsa"] = "Abinomn", ["bsb"] = "Brunei Bisaya", ["bsc"] = "Bassari", ["bse"] = "Wushi", ["bsf"] = "Bauchi", ["bsg"] = "Bashkardi", ["bsh"] = "Kamkata-viri", ["bsi"] = "Bassossi", ["bsj"] = "Bangwinji", ["bsk"] = "Burushaski", ["bsl"] = "Basa-Gumna", ["bsm"] = "Busami", ["bsn"] = "Barasana", ["bso"] = "Buso", ["bsp"] = "Baga Sitemu", ["bsq"] = "Bassa", ["bsr"] = "Bassa-Kontagora", ["bss"] = "Akoose", ["bst"] = "Basketo", ["bsu"] = "Bahonsuai", ["bsv"] = "Baga Sobané", ["bsw"] = "Baiso", ["bsx"] = "Yangkam", ["bsy"] = "Sabah Bisaya", ["bta"] = "Bata", ["btc"] = "Bati (Cameroon)", ["btd"] = "Dairi Batak", ["bte"] = "Gamo-Ningi", ["btf"] = "Birgit", ["btg"] = "Gagnoa Bété", ["bth"] = "Biatah Bidayuh", ["bti"] = "Burate", ["btj"] = "Bacanese Malay", ["btk-pro"] = "Proto-Batak", ["btm"] = "Mandailing Batak", ["btn"] = "Ratagnon", ["bto"] = "Iriga Bicolano", ["btp"] = "Budibud", ["btq"] = "Batek", ["btr"] = "Baetora", ["bts"] = "Simalungun Batak", ["btt"] = "Bete-Bendi", ["btu"] = "Batu", ["btv"] = "Bateri", ["btw"] = "Butuanon", ["btx"] = "Karo Batak", ["bty"] = "Bobot", ["btz"] = "Alas-Kluet Batak", ["bua"] = "บูร์ยัต", ["bub"] = "Bua", ["bud"] = "Ntcham", ["bue"] = "Beothuk", ["buf"] = "Bushoong", ["bug"] = "บูกิส", ["buh"] = "Younuo Bunu", ["bui"] = "Bongili", ["buj"] = "Basa-Gurmana", ["buk"] = "Bukawa", ["bum"] = "Bulu (Cameroon)", ["bun"] = "Sherbro", ["buo"] = "Terei", ["bup"] = "Busoa", ["buq"] = "Brem", ["bus"] = "Bokobaru", ["but"] = "Bungain", ["buu"] = "Budu", ["buv"] = "Bun", ["buw"] = "Bubi", ["bux"] = "Boghom", ["buy"] = "Mmani", ["bva"] = "Barein", ["bvb"] = "Bube", ["bvc"] = "Baelelea", ["bvd"] = "Baeggu", ["bve"] = "Berau Malay", ["bvf"] = "Boor", ["bvg"] = "Bonkeng", ["bvh"] = "Bure", ["bvi"] = "Belanda Viri", ["bvj"] = "Baan", ["bvk"] = "Bukat", ["bvl"] = "Bolivian Sign Language", ["bvm"] = "Bamunka", ["bvn"] = "Buna", ["bvo"] = "Bolgo", ["bvp"] = "Bumang", ["bvq"] = "Birri", ["bvr"] = "Burarra", ["bvt"] = "Bati (Indonesia)", ["bvu"] = "Bukit Malay", ["bvv"] = "Baniva", ["bvw"] = "Boga", ["bvx"] = "Babole", ["bvy"] = "Baybayanon", ["bvz"] = "Bauzi", ["bwa"] = "Bwatoo", ["bwb"] = "Namosi-Naitasiri-Serua", ["bwc"] = "Bwile", ["bwd"] = "Bwaidoka", ["bwe"] = "Bwe Karen", ["bwf"] = "Boselewa", ["bwg"] = "Barwe", ["bwh"] = "Bishuo", ["bwi"] = "Baniwa", ["bwj"] = "Láá Láá Bwamu", ["bwk"] = "Bauwaki", ["bwl"] = "Bwela", ["bwm"] = "Biwat", ["bwn"] = "Wunai Bunu", ["bwo"] = "Shinasha", ["bwp"] = "Mandobo Bawah", ["bwq"] = "Southern Bobo", ["bwr"] = "Bura", ["bws"] = "Bomboma", ["bwt"] = "Bafaw", ["bwu"] = "Buli (Ghana)", ["bww"] = "Bwa", ["bwx"] = "Bu-Nao Bunu", ["bwy"] = "Cwi Bwamu", ["bwz"] = "Bwisi", ["bxa"] = "Bauro", ["bxb"] = "Belanda Bor", ["bxc"] = "Molengue", ["bxd"] = "Pela", ["bxe"] = "Ongota", ["bxf"] = "Bilur", ["bxg"] = "Bangala", ["bxh"] = "Buhutu", ["bxi"] = "Pirlatapa", ["bxj"] = "Bayungu", ["bxk"] = "Bukusu", ["bxl"] = "Jalkunan", ["bxn"] = "Burduna", ["bxo"] = "Barikanchi", ["bxp"] = "Bebil", ["bxq"] = "Beele", ["bxs"] = "Busam", ["bxv"] = "Berakou", ["bxw"] = "Banka", ["bxz"] = "Binahari", ["bya"] = "Palawan Batak", ["byb"] = "Bikya", ["byc"] = "Ubaghara", ["byd"] = "Benyadu'", ["bye"] = "Pouye", ["byf"] = "Bete", ["byg"] = "Baygo", ["byh"] = "Bujhyal", ["byi"] = "Buyu", ["byj"] = "Binawa", ["byk"] = "Biao", ["byl"] = "Bayono", ["bym"] = "Bidyara", ["byn"] = "Blin", ["byo"] = "Biyo", ["byp"] = "Bumaji", ["byq"] = "Basay", ["byr"] = "Baruya", ["bys"] = "Burak", ["byt"] = "Berti", ["byv"] = "Medumba", ["byw"] = "Belhariya", ["byx"] = "Qaqet", ["byz"] = "Banaro", ["bza"] = "Bandi", ["bzb"] = "Andio", ["bzd"] = "Bribri", ["bze"] = "Jenaama Bozo", ["bzf"] = "Boikin", ["bzg"] = "Babuza", ["bzh"] = "Mapos Buang", ["bzi"] = "บีซู", ["bzj"] = "Belizean Creole", ["bzk"] = "Nicaraguan Creole", ["bzl"] = "Boano (Sulawesi)", ["bzm"] = "Bolondo", ["bzn"] = "Boano (Maluku)", ["bzo"] = "Bozaba", ["bzp"] = "Kemberano", ["bzq"] = "Buli (Indonesia)", ["bzr"] = "Biri", ["bzs"] = "Brazilian Sign Language", ["bzu"] = "Burmeso", ["bzv"] = "Bebe", ["bzw"] = "Basa", ["bzx"] = "Hainyaxo Bozo", ["bzy"] = "Obanliku", ["bzz"] = "Evant", ["ca"] = "กาตาลา", ["caa"] = "Ch'orti'", ["cab"] = "Garifuna", ["cac"] = "Chuj", ["cad"] = "Caddo", ["cae"] = "Laalaa", ["caf"] = "Southern Carrier", ["cag"] = "Nivaclé", ["cah"] = "Cahuarano", ["caj"] = "Chané", ["cak"] = "Kaqchikel", ["cal"] = "Carolinian", ["cam"] = "Cèmuhî", ["can"] = "Chambri", ["cao"] = "Chácobo", ["cap"] = "Chipaya", ["caq"] = "คาร์นิโคบาร์", ["car"] = "Kari'na", ["cas"] = "Tsimané", ["cau-abz-pro"] = "Proto-Abkhaz-Abaza", ["cau-and-pro"] = "Proto-Andian", ["cau-ava-pro"] = "Proto-Avaro-Andian", ["cau-cir-pro"] = "เซอร์แคสเซียนดั้งเดิม", ["cau-drg-pro"] = "Proto-Dargwa", ["cau-lzg-pro"] = "Proto-Lezghian", ["cau-nec-pro"] = "คอเคเซียนตะวันออกเฉียงเหนือดั้งเดิม", ["cau-nkh-pro"] = "นัคดั้งเดิม", ["cau-nwc-pro"] = "คอเคเซียนตะวันตกเฉียงเหนือดั้งเดิม", ["cau-tsz-pro"] = "Proto-Tsezian", ["cav"] = "Cavineña", ["caw"] = "Kallawaya", ["cax"] = "Chiquitano", ["cay"] = "Cayuga", ["caz"] = "Canichana", ["cba-ata"] = "Atanques", ["cba-cat"] = "Catío Chibcha", ["cba-dor"] = "Dorasque", ["cba-dui"] = "Duit", ["cba-hue"] = "Huetar", ["cba-nut"] = "Nutabe", ["cba-pro"] = "Proto-Chibchan", ["cbb"] = "Cabiyarí", ["cbc"] = "Carapana", ["cbd"] = "Carijona", ["cbg"] = "Chimila", ["cbi"] = "Chachi", ["cbj"] = "Ede Cabe", ["cbk"] = "ชาบากาโน", ["cbl"] = "Bualkhaw Chin", ["cbn"] = "ญัฮกุร", ["cbo"] = "Izora", ["cbq"] = "Tsucuba", ["cbr"] = "Cashibo-Cacataibo", ["cbs"] = "Cashinahua", ["cbt"] = "Chayahuita", ["cbu"] = "Candoshi-Shapra", ["cbv"] = "Cacua", ["cbw"] = "Kinabalian", ["cby"] = "Carabayo", ["cca"] = "Cauca", ["ccc"] = "Chamicuro", ["ccd"] = "Cafundó", ["cce"] = "Chopi", ["ccg"] = "Chamba Daka", ["cch"] = "Atsam", ["ccj"] = "Kasanga", ["ccl"] = "Cutchi-Swahili", ["ccm"] = "Malaccan Creole Malay", ["cco"] = "Comaltepec Chinantec", ["ccp"] = "จักมา", ["ccr"] = "Cacaopera", ["ccs-gzn-pro"] = "จอร์เจียน-แซนดั้งเดิม", ["ccs-pro"] = "คาร์ทเวเลียนดั้งเดิม", ["cda"] = "Choni", ["cdc-cbm-pro"] = "ชาดิกตอนกลางดั้งเดิม", ["cdc-mas-pro"] = "Proto-Masa", ["cdc-pro"] = "ชาดิกดั้งเดิม", ["cdd-pro"] = "Proto-Caddoan", ["cde"] = "Chenchu", ["cdf"] = "Chiru", ["cdh"] = "Chambeali", ["cdi"] = "Chodri", ["cdj"] = "Churahi", ["cdm"] = "Chepang", ["cdn"] = "Chaudangsi", ["cdo"] = "หมิ่นตะวันออก", ["cdr"] = "Cinda-Regi-Tiyal", ["cds"] = "Chadian Sign Language", ["cdy"] = "Chadong", ["cdz"] = "Koda", ["ce"] = "เชเชน", ["cea"] = "Lower Chehalis", ["ceb"] = "เซบัวโน", ["ceg"] = "Chamacoco", ["cel-bry-pro"] = "บริทอนิกดั้งเดิม", ["cel-gal"] = "Gallaecian", ["cel-gau"] = "กอล", ["cel-pro"] = "เคลติกดั้งเดิม", ["cen"] = "Cen", ["cet"] = "Centúúm", ["cfa"] = "Dijim-Bwilim", ["cfd"] = "Cara", ["cfg"] = "Como Karim", ["cfm"] = "Falam Chin", ["cga"] = "Changriwa", ["cgc"] = "Kagayanen", ["cgg"] = "Rukiga", ["cgk"] = "Chocangaca", ["ch"] = "ชามอร์โร", ["chb"] = "Chibcha", ["chc"] = "Catawba", ["chd"] = "Highland Oaxaca Chontal", ["chf"] = "Chontal Maya", ["chg"] = "ชากาทาย", ["chh"] = "Chinook", ["chi-pro"] = "Proto-Chimakuan", ["chj"] = "Ojitlán Chinantec", ["chk"] = "Chuukese", ["chl"] = "Cahuilla", ["chm-pro"] = "Proto-Mari", ["chn"] = "Chinook Jargon", ["cho"] = "Choctaw", ["chp"] = "Chipewyan", ["chq"] = "Quiotepec Chinantec", ["chr"] = "เชโรกี", ["cht"] = "Cholón", ["chw"] = "Chuabo", ["chx"] = "Chantyal", ["chy"] = "เชเยนน์", ["chz"] = "Ozumacín Chinantec", ["cia"] = "Cia-Cia", ["cib"] = "Ci Gbe", ["cic"] = "Chickasaw", ["cid"] = "Chimariko", ["cie"] = "Cineni", ["cih"] = "Chinali", ["cik"] = "Chitkuli Kinnauri", ["cim"] = "คิมบรี", ["cin"] = "Cinta Larga", ["cip"] = "Chiapanec", ["cir"] = "Tinrin", ["ciy"] = "Chaima", ["cja"] = "จามตะวันตก", ["cje"] = "Chru", ["cjh"] = "Upper Chehalis", ["cji"] = "Chamalal", ["cjk"] = "Chokwe", ["cjm"] = "จามตะวันออก", ["cjn"] = "Chenapian", ["cjo"] = "Ashéninka Pajonal", ["cjp"] = "Cabécar", ["cjs"] = "โชร์", ["cjv"] = "Chuave", ["cjy"] = "จิ้น", ["ckb"] = "เคิร์ดตอนกลาง", ["ckh"] = "Chak", ["ckl"] = "Cibak", ["ckn"] = "Kaang Chin", ["cko"] = "Anufo", ["ckq"] = "Kajakse", ["ckr"] = "Kairak", ["cks"] = "Tayo", ["ckt"] = "ชุกชี", ["cku"] = "Koasati", ["ckv"] = "กบาลัน", ["ckx"] = "Caka", ["cky"] = "Cakfem-Mushere", ["ckz"] = "Kaqchikel-K'iche' Mixed Language", ["cla"] = "Ron", ["clc"] = "Chilcotin", ["cld"] = "Chaldean Neo-Aramaic", ["cle"] = "Lealao Chinantec", ["clh"] = "Chilisso", ["cli"] = "Chakali", ["clj"] = "Laitu Chin", ["clk"] = "Idu", ["cll"] = "Chala", ["clm"] = "Klallam", ["clo"] = "Lowland Oaxaca Chontal", ["clt"] = "Lutuv", ["clu"] = "Caluyanun", ["clw"] = "Chulym", ["cly"] = "Eastern Highland Chatino", ["cma"] = "หมะ", ["cmc-pro"] = "จามิกดั้งเดิม", ["cme"] = "Cerma", ["cmg"] = "มองโกเลียคลาสสิก", ["cmi"] = "Emberá-Chamí", ["cml"] = "Campalagian", ["cmm"] = "Michigamea", ["cmn"] = "จีนกลาง", ["cmo"] = "มนองตอนกลาง", ["cmr"] = "Mro Chin", ["cms"] = "Messapic", ["cmt"] = "Camtho", ["cna"] = "Changthang", ["cnb"] = "Chinbon Chin", ["cnc"] = "Cốông", ["cng"] = "Northern Qiang", ["cnh"] = "ฮาคา", ["cni"] = "Asháninka", ["cnk"] = "Khumi Chin", ["cnl"] = "Lalana Chinantec", ["cno"] = "Con", ["cnp"] = "ผิงเหนือ", ["cns"] = "Central Asmat", ["cnt"] = "Tepetotutla Chinantec", ["cnu"] = "Chenoua", ["cnw"] = "Ngawn Chin", ["cnx"] = "Middle Cornish", ["co"] = "คอร์ซิกา", ["coa"] = "Cocos Islands Malay", ["cob"] = "Chicomuceltec", ["coc"] = "Cocopa", ["cod"] = "Cocama", ["coe"] = "Koreguaje", ["cof"] = "Tsafiki", ["cog"] = "ชอง", ["coh"] = "Chichonyi-Chidzihana-Chikauma", ["coj"] = "Cochimi", ["cok"] = "Santa Teresa Cora", ["col"] = "Columbia-Wenatchi", ["com"] = "Comanche", ["con"] = "Cofán", ["coo"] = "Comox", ["cop"] = "คอปติก", ["coq"] = "Coquille", ["cot"] = "Caquinte", ["cou"] = "Wamey", ["cov"] = "เฉ่าเหมียว", ["cow"] = "Cowlitz", ["cox"] = "Nanti", ["coy"] = "Coyaima", ["coz"] = "Chochotec", ["cpa"] = "Palantla Chinantec", ["cpb"] = "Ucayali-Yurúa Ashéninka", ["cpc"] = "Ajyíninka Apurucayali", ["cpg"] = "Cappadocian Greek", ["cpi"] = "Chinese Pidgin English", ["cpn"] = "Cherepon", ["cpo"] = "Kpee", ["cps"] = "Capiznon", ["cpu"] = "Pichis Ashéninka", ["cpx"] = "ผูเซียน", ["cpy"] = "South Ucayali Ashéninka", ["cqd"] = "Chuanqiandian Cluster Miao", ["cr"] = "ครี", ["cra"] = "Chara", ["crb"] = "Kalinago", ["crc"] = "Lonwolwol", ["crd"] = "Coeur d'Alene", ["crf"] = "Caramanta", ["crg"] = "Michif", ["crh"] = "ตาตาร์แบบไครเมีย", ["cri"] = "Sãotomense", ["crj"] = "Southern East Cree", ["crk"] = "Plains Cree", ["crl"] = "Northern East Cree", ["crm"] = "Moose Cree", ["crn"] = "Cora", ["cro"] = "Crow", ["crp-bip"] = "Basque-Icelandic Pidgin", ["crp-gep"] = "West Greenlandic Pidgin", ["crp-kia"] = "Kiautschou German Pidgin", ["crp-mar"] = "Maroon Spirit Language", ["crp-mpp"] = "Macau Pidgin Portuguese", ["crp-rsn"] = "Russenorsk", ["crp-slb"] = "Solombala English", ["crp-spp"] = "Samoan Plantation Pidgin", ["crp-tpr"] = "Taimyr Pidgin Russian", ["crq"] = "Iyo'wujwa Chorote", ["crr"] = "Carolina Algonquian", ["crs"] = "Seychellois Creole", ["crt"] = "Iyojwa'ja Chorote", ["crv"] = "Chaura", ["crw"] = "Chrau", ["crx"] = "Carrier", ["cry"] = "Cori", ["crz"] = "Cruzeño", ["cs"] = "เช็ก", ["csa"] = "Chiltepec Chinantec", ["csb"] = "คาชุบ", ["csc"] = "Catalan Sign Language", ["csd"] = "Chiangmai Sign Language", ["cse"] = "Czech Sign Language", ["csf"] = "Cuban Sign Language", ["csg"] = "Chilean Sign Language", ["csh"] = "Asho Chin", ["csi"] = "Coast Miwok", ["csj"] = "Songlai Chin", ["csk"] = "Jola-Kasa", ["csl"] = "Chinese Sign Language", ["csm"] = "Central Sierra Miwok", ["csn"] = "Colombian Sign Language", ["cso"] = "Sochiapam Chinantec", ["csp"] = "ผิงใต้", ["csq"] = "Croatian Sign Language", ["csr"] = "Costa Rican Sign Language", ["css"] = "Southern Ohlone", ["cst"] = "Northern Ohlone", ["csu-bba-pro"] = "Proto-Bongo-Bagirmi", ["csu-maa-pro"] = "Proto-Mangbetu", ["csu-pro"] = "Proto-Central Sudanic", ["csu-sar-pro"] = "Proto-Sara", ["csv"] = "Sumtu Chin", ["csw"] = "Swampy Cree", ["csx"] = "Cambodian Sign Language", ["csy"] = "Siyin Chin", ["csz"] = "Coos", ["cta"] = "Tataltepec Chatino", ["ctc"] = "Chetco-Tolowa", ["ctd"] = "Tedim Chin", ["cte"] = "Tepinapa Chinantec", ["ctg"] = "Chittagonian", ["cth"] = "Thaiphum Chin", ["ctl"] = "Tlacoatzintepec Chinantec", ["ctm"] = "Chitimacha", ["ctn"] = "Chhintange", ["cto"] = "Emberá-Catío", ["ctp"] = "Western Highland Chatino", ["cts"] = "Northern Catanduanes Bicolano", ["ctt"] = "Wayanad Chetti", ["ctu"] = "Chol", ["ctz"] = "Zacatepec Chatino", ["cu"] = "สลาวอนิกคริสตจักรเก่า", ["cua"] = "Cua", ["cub"] = "Cubeo", ["cuc"] = "Usila Chinantec", ["cug"] = "Cung", ["cuh"] = "Chuka", ["cui"] = "Cuiba", ["cuj"] = "Mashco Piro", ["cuk"] = "Kuna", ["cul"] = "Culina", ["cuo"] = "Cumanagoto", ["cup"] = "Cupeño", ["cuq"] = "จุน", ["cur"] = "Chhulung", ["cus-ash"] = "Ashraaf", ["cus-hec-pro"] = "Proto-Highland East Cushitic", ["cus-pro"] = "Proto-Cushitic", ["cus-som-pro"] = "โซมาลอยด์ดั้งเดิม", ["cus-sou-pro"] = "Proto-South Cushitic", ["cut"] = "Teutila Cuicatec", ["cuu"] = "Tai Ya", ["cuv"] = "Cuvok", ["cuw"] = "Chukwa", ["cux"] = "Tepeuxila Cuicatec", ["cuy"] = "Cuitlatec", ["cv"] = "ชูวัช", ["cvg"] = "Chug", ["cvn"] = "Valle Nacional Chinantec", ["cwa"] = "Kabwa", ["cwb"] = "Maindo", ["cwd"] = "Woods Cree", ["cwe"] = "Kwere", ["cwg"] = "Chewong", ["cwt"] = "Kuwaataay", ["cy"] = "เวลส์", ["cya"] = "Nopala Chatino", ["cyb"] = "Cayubaba", ["cyo"] = "Cuyunon", ["czh"] = "Huizhou", ["czk"] = "Knaanic", ["czn"] = "Zenzontepec Chatino", ["czo"] = "หมิ่นตอนกลาง", ["czt"] = "Zotung Chin", ["da"] = "เดนมาร์ก", ["daa"] = "Dangaléat", ["dac"] = "Dambi", ["dad"] = "Marik", ["dae"] = "Duupa", ["dag"] = "Dagbani", ["dah"] = "Gwahatike", ["dai"] = "Day", ["daj"] = "Dar Fur Daju", ["dak"] = "ดาโคตา", ["dal"] = "Dahalo", ["dam"] = "Damakawa", ["dao"] = "Daai Chin", ["daq"] = "Dandami Maria", ["dar"] = "Dargwa", ["das"] = "Daho-Doo", ["dau"] = "Dar Sila Daju", ["dav"] = "Taita", ["daw"] = "Davawenyo", ["dax"] = "Dayi", ["daz"] = "Dao", ["dba"] = "Bangime", ["dbb"] = "Deno", ["dbd"] = "Dadiya", ["dbe"] = "Dabe", ["dbf"] = "Edopi", ["dbg"] = "Dogul Dom", ["dbi"] = "Doka", ["dbj"] = "อีดาอัน", ["dbl"] = "Dyirbal", ["dbm"] = "Duguri", ["dbn"] = "Duriankere", ["dbo"] = "Dulbu", ["dbp"] = "Duwai", ["dbq"] = "Daba", ["dbr"] = "Dabarre", ["dbt"] = "Ben Tey", ["dbu"] = "Bondum Dom Dogon", ["dbv"] = "Dungu", ["dbw"] = "Bankan Tey Dogon", ["dby"] = "Dibiyaso", ["dcc"] = "Deccani", ["dcr"] = "เนเกอร์ฮอลันดส์", ["dda"] = "Dadi Dadi", ["ddd"] = "Dongotono", ["dde"] = "Doondo", ["ddg"] = "Fataluku", ["ddi"] = "Diodio", ["ddj"] = "Jaru", ["ddn"] = "Dendi", ["ddo"] = "Tsez", ["ddr"] = "Dhudhuroa", ["dds"] = "Donno So Dogon", ["ddw"] = "Dawera-Daweloor", ["de"] = "เยอรมัน", ["dec"] = "Dagik", ["ded"] = "Dedua", ["dee"] = "Dewoin", ["def"] = "Dezfuli", ["deg"] = "Degema", ["deh"] = "Dehwari", ["dei"] = "Demisa", ["dem"] = "Dem", ["dep"] = "Pidgin Delaware", ["der"] = "Deori", ["des"] = "Desano", ["dev"] = "Domung", ["dez"] = "Dengese", ["dga"] = "Southern Dagaare", ["dgb"] = "Bunoge", ["dgc"] = "Casiguran Dumagat Agta", ["dgd"] = "Dagaari Dioula", ["dge"] = "Degenan", ["dgg"] = "Doga", ["dgh"] = "Dghwede", ["dgi"] = "Northern Dagara", ["dgk"] = "Dagba", ["dgn"] = "Dagoman", ["dgo"] = "Hindi Dogri", ["dgr"] = "Dogrib", ["dgs"] = "Dogoso", ["dgt"] = "Ntra'ngith", ["dgw"] = "Daungwurrung", ["dgx"] = "Doghoro", ["dgz"] = "Daga", ["dhd"] = "Dhundhari", ["dhg"] = "Dhangu", ["dhi"] = "Dhimal", ["dhl"] = "Dhalandji", ["dhm"] = "Zemba", ["dhn"] = "Dhanki", ["dho"] = "Dhodia", ["dhr"] = "Tharrgari", ["dhs"] = "Dhaiso", ["dhu"] = "Dhurga", ["dhv"] = "Drehu", ["dhw"] = "Danuwar", ["dhx"] = "Dhungaloo", ["dia"] = "Dia", ["dib"] = "South Central Dinka", ["dic"] = "Lakota Dida", ["did"] = "Didinga", ["dif"] = "Dieri", ["dig"] = "Digo", ["dii"] = "Dimbong", ["dij"] = "Dai", ["dik"] = "Southwestern Dinka", ["dil"] = "Dilling", ["dim"] = "Dime", ["din"] = "Dinka", ["dio"] = "Dibo", ["dip"] = "Northeastern Dinka", ["dir"] = "Dirim", ["dis"] = "Dimasa", ["diu"] = "Gciriku", ["diw"] = "Northwestern Dinka", ["dix"] = "Dixon Reef", ["diy"] = "Diuwe", ["diz"] = "Ding", ["dja"] = "Djadjawurrung", ["djb"] = "Djinba", ["djc"] = "Dar Daju Daju", ["djd"] = "Jaminjung", ["dje"] = "Zarma", ["djf"] = "Djangun", ["dji"] = "Djinang", ["djj"] = "Ndjébbana", ["djk"] = "Aukan", ["djl"] = "Djiwarli", ["djm"] = "Jamsay", ["djn"] = "Djauan", ["djo"] = "Jangkang", ["djr"] = "Djambarrpuyngu", ["dju"] = "Kapriman", ["djw"] = "Djawi", ["dka"] = "Dakpa", ["dkk"] = "Dakka", ["dkr"] = "Kuijau", ["dks"] = "Southeastern Dinka", ["dkx"] = "Mazagway", ["dlg"] = "Dolgan", ["dlk"] = "Dahalik", ["dlm"] = "แดลเมเชีย", ["dln"] = "Darlong", ["dma"] = "Duma", ["dmb"] = "Mombo Dogon", ["dmc"] = "Gavak", ["dmd"] = "Madhi Madhi", ["dme"] = "Dugwor", ["dmf"] = "Medefaidrin", ["dmg"] = "กีนาบาตางันตอนบน", ["dmk"] = "Domaaki", ["dml"] = "Dameli", ["dmm"] = "Dama (Nigeria)", ["dmn-dam"] = "Dama (Sierra Leone)", ["dmn-mdw-pro"] = "Proto-Western Mande", ["dmn-pro"] = "Proto-Mande", ["dmo"] = "Kemezung", ["dmr"] = "East Damar", ["dms"] = "Dampelas", ["dmu"] = "Dubu", ["dmv"] = "Dumpas", ["dmw"] = "Mudburra", ["dmx"] = "Dema", ["dmy"] = "Demta", ["dna"] = "Upper Grand Valley Dani", ["dnd"] = "Daonda", ["dne"] = "Ndendeule", ["dng"] = "ดุงกาน", ["dni"] = "Lower Grand Valley Dani", ["dnj"] = "Dan", ["dnk"] = "Dengka", ["dnn"] = "Dzuun", ["dno"] = "Ndrulo", ["dnr"] = "Danaru", ["dnt"] = "Mid Grand Valley Dani", ["dnu"] = "Danau", ["dnv"] = "Danu", ["dnw"] = "Western Dani", ["dny"] = "Dení", ["doa"] = "Dom", ["dob"] = "Dobu", ["doc"] = "ต้งเหนือ", ["doe"] = "Doe", ["dof"] = "Domu", ["doh"] = "Dong", ["doi"] = "Dogri", ["dok"] = "Dondo", ["dol"] = "Doso", ["don"] = "Doura", ["doo"] = "Dongo", ["dop"] = "Lukpa", ["doq"] = "Dominican Sign Language", ["dor"] = "Dori'o", ["dos"] = "Dogosé", ["dot"] = "Dass", ["dov"] = "Toka-Leya", ["dow"] = "Doyayo", ["dox"] = "Bussa", ["doy"] = "Dompo", ["doz"] = "Dorze", ["dpp"] = "Papar", ["dra-bry"] = "Beary", ["dra-cen-pro"] = "ดราวิเดียนตอนกลางดั้งเดิม", ["dra-mkn"] = "Middle Kannada", ["dra-nor-pro"] = "ดราวิเดียนเหนือดั้งเดิม", ["dra-okn"] = "Old Kannada", ["dra-ote"] = "Old Telugu", ["dra-pro"] = "ดราวิเดียนดั้งเดิม", ["dra-sdo-pro"] = "ดราวิเดียนใต้ที่หนึ่งดั้งเดิม", ["dra-sdt-pro"] = "ดราวิเดียนใต้ที่สองดั้งเดิม", ["dra-sou-pro"] = "ดราวิเดียนใต้ดั้งเดิม", ["drb"] = "Dair", ["drc"] = "Minderico", ["drd"] = "Darmiya", ["drg"] = "Rungus", ["dri"] = "Lela", ["drl"] = "Baagandji", ["drn"] = "West Damar", ["dro"] = "Daro-Matu Melanau", ["drq"] = "Dura", ["drs"] = "Gedeo", ["dru"] = "Rukai", ["dru-pro"] = "Proto-Rukai", ["dry"] = "Darai", ["dsb"] = "ซอร์เบียตอนล่าง", ["dse"] = "Dutch Sign Language", ["dsh"] = "Daasanach", ["dsi"] = "Disa", ["dsl"] = "Danish Sign Language", ["dsn"] = "Dusner", ["dso"] = "Desiya", ["dsq"] = "Tadaksahak", ["dta"] = "Daur", ["dtb"] = "Labuk-Kinabatangan Kadazan", ["dtd"] = "Ditidaht", ["dth"] = "Adithinngithigh", ["dti"] = "Ana Tinga Dogon", ["dtk"] = "Tene Kan Dogon", ["dtm"] = "Tomo Kan Dogon", ["dto"] = "Tommo So", ["dtp"] = "ดูซุนตอนกลาง", ["dtr"] = "Lotud", ["dts"] = "Toro So Dogon", ["dtt"] = "Toro Tegu Dogon", ["dtu"] = "Tebul Ure Dogon", ["dty"] = "Doteli", ["dua"] = "Duala", ["dub"] = "Dubli", ["duc"] = "Duna", ["due"] = "Umiray Dumaget Agta", ["duf"] = "Dumbea", ["dug"] = "Chiduruma", ["duh"] = "Dungra Bhil", ["dui"] = "Dumun", ["duk"] = "Uyajitaya", ["dul"] = "Alabat Island Agta", ["dum"] = "ดัตช์กลาง", ["dun"] = "Dusun Deyah", ["duo"] = "Dupaningan Agta", ["dup"] = "Duano", ["duq"] = "Dusun Malang", ["dur"] = "Dii", ["dus"] = "Dumi", ["duu"] = "Drung", ["duv"] = "Duvle", ["duw"] = "Dusun Witu", ["dux"] = "Duun", ["duy"] = "Dicamay Agta", ["duz"] = "Duli", ["dv"] = "มัลดีฟส์", ["dva"] = "Duau", ["dwa"] = "Diri", ["dwr"] = "เดาโร", ["dwu"] = "Dhuwal", ["dww"] = "Dawawa", ["dwy"] = "Dhuwaya", ["dwz"] = "Dewas Rai", ["dya"] = "Dyan", ["dyb"] = "Dyaberdyaber", ["dyd"] = "Dyugun", ["dyg"] = "Villa Viciosa Agta", ["dyi"] = "Djimini", ["dym"] = "Yanda Dogon", ["dyn"] = "Dyangadi", ["dyo"] = "Jola-Fonyi", ["dyu"] = "Dyula", ["dyy"] = "Dyaabugay", ["dz"] = "ซองคา", ["dza"] = "Tunzu", ["dzg"] = "Dazaga", ["dzl"] = "Dzala", ["dzn"] = "Dzando", ["ebg"] = "Ebughu", ["ebk"] = "Eastern Bontoc", ["ebr"] = "Ebrié", ["ebu"] = "Embu", ["ecr"] = "Eteocretan", ["ecs"] = "Ecuadorian Sign Language", ["ecy"] = "Eteocypriot", ["ee"] = "เอเว", ["eee"] = "E", ["efa"] = "Efai", ["efe"] = "Efe", ["efi"] = "Efik", ["ega"] = "Ega", ["egl"] = "เอมีเลีย", ["ego"] = "Eggon", ["egx-dem"] = "Demotic Egyptian", ["egy"] = "อียิปต์", ["ehu"] = "Ehueun", ["eip"] = "Eipomek", ["eit"] = "Eitiep", ["eiv"] = "Askopan", ["eja"] = "Ejamat", ["eka"] = "Ekajuk", ["eke"] = "Ekit", ["ekg"] = "Ekari", ["eki"] = "Eki", ["ekl"] = "Kolhe", ["ekm"] = "Elip", ["eko"] = "Koti", ["ekp"] = "Ekpeye", ["ekr"] = "Yace", ["eky"] = "กะยาตะวันออก", ["el"] = "กรีก", ["ele"] = "Elepi", ["elh"] = "El Hugeirat", ["eli"] = "Nding", ["elk"] = "Elkei", ["elm"] = "Eleme", ["elo"] = "El Molo", ["elu"] = "Elu", ["elx"] = "Elamite", ["ema"] = "Emai", ["emb"] = "Embaloh", ["eme"] = "Emerillon", ["emg"] = "Eastern Meohang", ["emi"] = "Mussau-Emira", ["emk"] = "Eastern Maninkakan", ["emm"] = "Mamulique", ["emn"] = "Eman", ["emp"] = "Northern Emberá", ["ems"] = "อาลูตีก", ["emu"] = "Eastern Muria", ["emw"] = "Emplawas", ["emx"] = "Erromintxela", ["emy"] = "Epigraphic Mayan", ["en"] = "อังกฤษ", ["ena"] = "Apali", ["enb"] = "Markweeta", ["enc"] = "En", ["end"] = "Ende", ["enf"] = "Forest Enets", ["enh"] = "Tundra Enets", ["enl"] = "Enlhet", ["enm"] = "อังกฤษกลาง", ["enn"] = "Engenni", ["eno"] = "Enggano", ["enq"] = "Enga", ["enr"] = "Emem", ["enu"] = "Enu", ["env"] = "Enwan", ["enw"] = "Enwang", ["enx"] = "Enxet", ["eo"] = "เอสเปรันโต", ["eot"] = "Eotile", ["epi"] = "Epie", ["era"] = "Eravallan", ["erg"] = "Sie", ["erh"] = "Eruwa", ["eri"] = "Ogea", ["erk"] = "South Efate", ["ero-gsz"] = "Geshiza", ["ero-nya"] = "Nyagrong Minyag", ["ero-tau"] = "Stau", ["err"] = "Erre", ["ers"] = "Ersu", ["ert"] = "Eritai", ["erw"] = "Erokwanas", ["es"] = "สเปน", ["ese"] = "Ese Ejja", ["esh"] = "เอชเตฮาร์ด", ["esl"] = "Egyptian Sign Language", ["esm"] = "Esuma", ["esn"] = "Salvadoran Sign Language", ["eso"] = "Estonian Sign Language", ["esq"] = "Esselen", ["ess"] = "Central Siberian Yupik", ["esu"] = "ยุปปิก", ["esx-esk-pro"] = "เอสกิโมดั้งเดิม", ["esx-ink"] = "Inuktun", ["esx-inq"] = "Inuinnaqtun", ["esx-inu-pro"] = "อินุอิตดั้งเดิม", ["esx-pro"] = "Proto-Eskimo-Aleut", ["esx-tut"] = "Tunumiisut", ["esy"] = "Eskayan", ["et"] = "เอสโตเนีย", ["etb"] = "Etebi", ["etc"] = "Etchemin", ["eth"] = "Ethiopian Sign Language", ["etn"] = "Eton (Vanuatu)", ["eto"] = "Eton (Cameroon)", ["etr"] = "Edolo", ["ets"] = "Yekhee", ["ett"] = "อีทรัสคัน", ["etu"] = "Ejagham", ["etx"] = "Eten", ["etz"] = "Semimi", ["eu"] = "บาสก์", ["euq-pro"] = "บาสก์ดั้งเดิม", ["eve"] = "เอเว็น", ["evh"] = "Uvbie", ["evn"] = "เอเวนค์", ["ewo"] = "Ewondo", ["ext"] = "เอซเตรมาดูรา", ["eya"] = "Eyak", ["eyo"] = "Keiyo", ["eza"] = "Ezaa", ["eze"] = "Uzekwe", ["fa"] = "เปอร์เซีย", ["faa"] = "Fasu", ["fab"] = "Annobonese", ["fad"] = "Wagi", ["faf"] = "Fagani", ["fag"] = "Finongan", ["fah"] = "Baissa Fali", ["fai"] = "Faiwol", ["faj"] = "Faita", ["fak"] = "Fang (Beboid)", ["fal"] = "South Fali", ["fam"] = "Fam", ["fan"] = "Fang (Bantu)", ["fap"] = "Palor", ["far"] = "Fataleka", ["fau"] = "Fayu", ["fax"] = "ฟาลา", ["fay"] = "Southwestern Fars", ["faz"] = "Northwestern Fars", ["fbl"] = "West Albay Bikol", ["fcs"] = "Quebec Sign Language", ["fer"] = "Feroge", ["ff"] = "ฟูลา", ["ffi"] = "Foia Foia", ["fgr"] = "Fongoro", ["fi"] = "ฟินแลนด์", ["fia"] = "Nobiin", ["fie"] = "Fyer", ["fip"] = "Fipa", ["fir"] = "Firan", ["fit"] = "Meänkieli", ["fiw"] = "Fiwaga", ["fj"] = "ฟีจี", ["fkk"] = "Kirya-Konzel", ["fkv"] = "Kven", ["fla"] = "Montana Salish", ["flh"] = "Foau", ["fli"] = "Fali", ["fll"] = "North Fali", ["fln"] = "Flinders Island", ["flr"] = "Fuliiru", ["fly"] = "Tsotsitaal", ["fmp"] = "Fe'fe'", ["fmu"] = "Far Western Muria", ["fng"] = "Fanagalo", ["fni"] = "Fania", ["fo"] = "แฟโร", ["fod"] = "Foodo", ["foi"] = "Foi", ["fom"] = "Foma", ["fon"] = "Fon", ["for"] = "Fore", ["fos"] = "Siraya", ["fpe"] = "Pichinglis", ["fqs"] = "Fas", ["fr"] = "ฝรั่งเศส", ["frd"] = "Fordata", ["frm"] = "ฝรั่งเศสกลาง", ["fro"] = "ฝรั่งเศสเก่า", ["frp"] = "ฟร็องโก-พรอว็องส์", ["frq"] = "Forak", ["frr"] = "ฟรีเชียเหนือ", ["frt"] = "Fortsenal", ["fse"] = "Finnish Sign Language", ["fsl"] = "French Sign Language", ["fss"] = "Finnish-Swedish Sign Language", ["fud"] = "East Futuna", ["fuj"] = "Ko", ["fum"] = "Fum", ["fun"] = "Fulniô", ["fur"] = "ฟรียูลี", ["fut"] = "Futuna-Aniwa", ["fuu"] = "Furu", ["fuy"] = "Fuyug", ["fvr"] = "Fur", ["fwa"] = "Fwâi", ["fwe"] = "Fwe", ["fy"] = "ฟรีเชียตะวันตก", ["ga"] = "ไอริช", ["gaa"] = "Ga", ["gab"] = "Gabri", ["gac"] = "Mixed Great Andamanese", ["gad"] = "Gaddang", ["gae"] = "Warekena", ["gaf"] = "Gende", ["gag"] = "กากาอุซ", ["gah"] = "Alekano", ["gai"] = "Borei", ["gaj"] = "Gadsup", ["gak"] = "Gamkonora", ["gal"] = "Galoli", ["gam"] = "Kandawo", ["gan"] = "กั้น", ["gao"] = "Gants", ["gap"] = "Gal", ["gaq"] = "Gata'", ["gar"] = "Galeya", ["gas"] = "Adiwasi Garasia", ["gat"] = "Kenati", ["gau"] = "Kondekor", ["gaw"] = "Nobonob", ["gay"] = "Gayo", ["gba-pro"] = "Proto-Gbaya", ["gbb"] = "Kaytetye", ["gbd"] = "Karadjeri", ["gbe"] = "Niksek", ["gbf"] = "Gaikundi", ["gbg"] = "Gbanziri", ["gbh"] = "Defi Gbe", ["gbi"] = "Galela", ["gbj"] = "Bodo Gadaba", ["gbk"] = "Gaddi", ["gbl"] = "Gamit", ["gbm"] = "Garhwali", ["gbn"] = "Mo'da", ["gbo"] = "Northern Grebo", ["gbp"] = "Gbaya-Bossangoa", ["gbq"] = "Gbaya-Bozoum", ["gbr"] = "Gbagyi", ["gbs"] = "Gbesi Gbe", ["gbu"] = "Gagadu", ["gbv"] = "Gbanu", ["gbw"] = "Gabi", ["gbx"] = "Eastern Xwla Gbe", ["gby"] = "Gbari", ["gcc"] = "Mali", ["gcd"] = "Ganggalida", ["gce"] = "Galice", ["gcf"] = "Antillean Creole", ["gcl"] = "Grenadian Creole English", ["gcn"] = "Gaina", ["gcr"] = "Guianese Creole", ["gct"] = "Colonia Tovar German", ["gd"] = "แกลิกแบบสกอตแลนด์", ["gdb"] = "Ollari", ["gdc"] = "Gugu Badhun", ["gdd"] = "Gedaged", ["gde"] = "Gude", ["gdf"] = "Guduf-Gava", ["gdg"] = "Ga'dang", ["gdh"] = "Gadjerawang", ["gdi"] = "Gundi", ["gdj"] = "Kurtjar", ["gdk"] = "Gadang", ["gdl"] = "Dirasha", ["gdm"] = "Laal", ["gdn"] = "Umanakaina", ["gdo"] = "Godoberi", ["gdq"] = "Mehri", ["gdr"] = "Wipi", ["gds"] = "Ghandruk Sign Language", ["gdt"] = "Kungardutyi", ["gdu"] = "Gudu", ["gdx"] = "Godwari", ["gea"] = "Geruma", ["geb"] = "Kire", ["gec"] = "Gboloo Grebo", ["ged"] = "Gade", ["geg"] = "Gengle", ["geh"] = "Hutterisch", ["gei"] = "Gebe", ["gej"] = "Gen", ["gek"] = "Gerka", ["gel"] = "Fakkanci", ["gem-pro"] = "เจอร์แมนิกดั้งเดิม", ["geq"] = "Geme", ["ges"] = "Geser-Gorom", ["gev"] = "Viya", ["gew"] = "Gera", ["gex"] = "Garre", ["gey"] = "Enya", ["gez"] = "กืออึซ", ["gfk"] = "Patpatar", ["gft"] = "Gafat", ["gga"] = "Gao", ["ggb"] = "Gbii", ["ggd"] = "Gugadj", ["gge"] = "Guragone", ["ggg"] = "Gurgula", ["ggk"] = "Kungarakany", ["ggl"] = "Ganglau", ["ggn"] = "Eastern Gurung", ["ggt"] = "Gitua", ["ggu"] = "Gban", ["ggw"] = "Gogodala", ["gha"] = "Ghadames", ["ghc"] = "แกลิกคลาสสิก", ["ghe"] = "Southern Ghale", ["ghh"] = "Northern Ghale", ["ghk"] = "Geko Karen", ["ghl"] = "Ghulfan", ["ghn"] = "Ghanongga", ["gho"] = "Ghomara", ["ghr"] = "Ghera", ["ghs"] = "Guhu-Samane", ["ght"] = "Kutang Ghale", ["gia"] = "Kitja", ["gib"] = "Gibanawa", ["gid"] = "Gidar", ["gie"] = "Guébie", ["gig"] = "Goaria", ["gih"] = "Githabul", ["gii"] = "Girirra", ["gil"] = "กิลเบิร์ต", ["gim"] = "Gimi (Papuan)", ["gin"] = "Hinukh", ["gip"] = "Gimi (Austronesian)", ["giq"] = "Green Gelao", ["gir"] = "Red Gelao", ["gis"] = "North Giziga", ["git"] = "Gitxsan", ["giu"] = "Mulao", ["giw"] = "White Gelao", ["gix"] = "Gilima", ["giy"] = "Giyug", ["giz"] = "South Giziga", ["gji"] = "Geji", ["gjk"] = "Kachi Koli", ["gjm"] = "Gunditjmara", ["gjn"] = "Gonja", ["gjr"] = "Gurindji Kriol", ["gju"] = "Gojri", ["gka"] = "Guya", ["gkd"] = "Magɨ", ["gke"] = "Ndai", ["gkn"] = "Gokana", ["gko"] = "Kok-Nar", ["gkp"] = "Guinea Kpelle", ["gl"] = "กาลิเซีย", ["glc"] = "Bon Gula", ["gld"] = "Nanai", ["glh"] = "Northwest Pashayi", ["glj"] = "Kulaal", ["glk"] = "Gilaki", ["glo"] = "Galambu", ["glr"] = "Glaro-Twabo", ["glu"] = "Gula", ["glw"] = "Glavda", ["gly"] = "Gule", ["gma"] = "Gambera", ["gmb"] = "Gula'alaa", ["gmd"] = "Mághdì", ["gme-bur"] = "Burgundian", ["gme-cgo"] = "กอทแบบไครเมีย", ["gmg"] = "Magiyi", ["gmh"] = "เยอรมันสูงกลาง", ["gml"] = "เยอรมันต่ำกลาง", ["gmm"] = "Gbaya-Mbodomo", ["gmn"] = "Gimnime", ["gmq-gut"] = "Gutnish", ["gmq-jmk"] = "Jamtish", ["gmq-mno"] = "นอร์เวย์กลาง", ["gmq-oda"] = "เดนมาร์กเก่า", ["gmq-ogt"] = "Old Gutnish", ["gmq-osw"] = "สวีเดนเก่า", ["gmq-pro"] = "นอร์สดั้งเดิม", ["gmq-scy"] = "Scanian", ["gmr"] = "Mirning", ["gmu"] = "Gumalu", ["gmv"] = "กาโม", ["gmw-bgh"] = "Bergish", ["gmw-cfr"] = "ฟรังโกเนียตอนกลาง", ["gmw-ecg"] = "เยอรมันตอนกลางตะวันออก", ["gmw-fin"] = "Fingallian", ["gmw-gts"] = "Gottscheerish", ["gmw-jdt"] = "Jersey Dutch", ["gmw-msc"] = "Middle Scots", ["gmw-pro"] = "เจอร์แมนิกตะวันตกดั้งเดิม", ["gmw-rfr"] = "ฟรังโกเนียแบบไรน์", ["gmw-stm"] = "Sathmar Swabian", ["gmw-tsx"] = "Transylvanian Saxon", ["gmw-vog"] = "เยอรมันแบบว็อลกา", ["gmw-zps"] = "เยอรมันแบบซิพเซอร์", ["gmx"] = "Magoma", ["gmy"] = "กรีกแบบไมซีนี", ["gmz"] = "Mgbo", ["gn-cls"] = "กัวรานีคลาสสิก", ["gna"] = "Kaansa", ["gnb"] = "Gangte", ["gnc"] = "Guanche", ["gnd"] = "Zulgo-Gemzek", ["gne"] = "Ganang", ["gng"] = "Ngangam", ["gnh"] = "Lere", ["gni"] = "กูนียันดี", ["gnj"] = "Ngen of Djonkro", ["gnk"] = "ǁGana", ["gnl"] = "Gangulu", ["gnm"] = "Ginuman", ["gnn"] = "Gumatj", ["gnq"] = "Gana", ["gnr"] = "Gureng Gureng", ["gnt"] = "Guntai", ["gnu"] = "Gnau", ["gnw"] = "Western Bolivian Guarani", ["gnz"] = "Ganzi", ["goa"] = "Guro", ["gob"] = "Playero", ["goc"] = "Gorakor", ["god"] = "Godié", ["goe"] = "Gongduk", ["gof"] = "โกฟา", ["gog"] = "Gogo", ["goh"] = "เยอรมันสูงเก่า", ["goi"] = "Gobasi", ["goj"] = "Gowlan", ["gol"] = "Gola", ["gon"] = "โคณฑี", ["goo"] = "Gone Dau", ["gop"] = "Yeretuar", ["goq"] = "Gorap", ["gor"] = "Gorontalo", ["got"] = "กอท", ["gou"] = "Gavar", ["gov"] = "Goo", ["gow"] = "Gorwaa", ["gox"] = "Gobu", ["goy"] = "Goundo", ["goz"] = "Gozarkhani", ["gpa"] = "Gupa-Abawa", ["gpn"] = "Taiap", ["gqa"] = "Ga'anda", ["gqi"] = "Guiqiong", ["gqn"] = "Kinikinao", ["gqr"] = "Gor", ["gqu"] = "Qau", ["gra"] = "Rajput Garasia", ["grc"] = "กรีกโบราณ", ["grd"] = "Guruntum", ["grg"] = "Madi", ["grh"] = "Gbiri-Niragu", ["gri"] = "Ghari", ["grj"] = "Southern Grebo", ["grk-cal"] = "Calabrian Greek", ["grk-ita"] = "Italiot Greek", ["grk-mar"] = "Mariupol Greek", ["grk-pro"] = "เฮลเลนิกดั้งเดิม", ["grm"] = "Kota Marudu Talantang", ["gro"] = "Groma", ["grq"] = "Gorovu", ["grs"] = "Gresi", ["grt"] = "กาโร", ["gru"] = "Kistane", ["grv"] = "Central Grebo", ["grw"] = "Gweda", ["grx"] = "Guriaso", ["gry"] = "Barclayville Grebo", ["grz"] = "Guramalum", ["gse"] = "Ghanaian Sign Language", ["gsg"] = "German Sign Language", ["gsl"] = "Gusilay", ["gsm"] = "Guatemalan Sign Language", ["gsn"] = "Gusan", ["gso"] = "Southwest Gbaya", ["gsp"] = "Wasembo", ["gss"] = "Greek Sign Language", ["gsw"] = "เยอรมันแบบอลามันเนีย", ["gta"] = "Guató", ["gtu"] = "Aghu Tharrnggala", ["gu"] = "คุชราต", ["gua"] = "Shiki", ["gub"] = "Guajajára", ["guc"] = "Wayuu", ["gud"] = "Yocoboué Dida", ["gue"] = "Gurindji", ["guf"] = "Gupapuyngu", ["gug"] = "กัวรานีแบบปารากวัย", ["guh"] = "Guahibo", ["gui"] = "Eastern Bolivian Guarani", ["guk"] = "Gumuz", ["gul"] = "Gullah", ["gum"] = "Guambiano", ["gun"] = "Mbya Guarani", ["guo"] = "Guayabero", ["gup"] = "Gunwinggu", ["guq"] = "Aché", ["gur"] = "ฟราฟรา", ["gus"] = "Guinean Sign Language", ["gut"] = "Maléku Jaíka", ["guu"] = "Yanomamö", ["guv"] = "Gey", ["guw"] = "Gun", ["gux"] = "Gourmanchéma", ["guz"] = "Gusii", ["gv"] = "แมงซ์", ["gva"] = "Kaskihá", ["gvc"] = "Guanano", ["gve"] = "Duwet", ["gvf"] = "Golin", ["gvj"] = "Guajá", ["gvl"] = "Gulay", ["gvm"] = "Gurmana", ["gvn"] = "Kuku-Yalanji", ["gvo"] = "Gavião do Jiparaná", ["gvp"] = "Pará Gavião", ["gvr"] = "Western Gurung", ["gvs"] = "Gumawana", ["gvy"] = "Guyani", ["gwa"] = "Mbato", ["gwb"] = "Gwa", ["gwc"] = "Kalami", ["gwd"] = "Gawwada", ["gwe"] = "Gweno", ["gwf"] = "Gowro", ["gwg"] = "Moo", ["gwi"] = "Gwich'in", ["gwj"] = "Gcwi", ["gwm"] = "Awngthim", ["gwn"] = "Gwandara", ["gwr"] = "Gwere", ["gwt"] = "Gawar-Bati", ["gwu"] = "Guwamu", ["gww"] = "Kwini", ["gwx"] = "Gua", ["gxx"] = "Wè Southern", ["gya"] = "Northwest Gbaya", ["gyb"] = "Garus", ["gyd"] = "Kayardild", ["gye"] = "Gyem", ["gyf"] = "Gungabula", ["gyg"] = "Gbayi", ["gyi"] = "Gyele", ["gyl"] = "Gayil", ["gym"] = "Ngäbere", ["gyn"] = "Guyanese Creole English", ["gyo"] = "Gyalsumdo", ["gyr"] = "Guarayu", ["gyy"] = "Gunya", ["gza"] = "Ganza", ["gzn"] = "Gane", ["ha"] = "เฮาซา", ["haa"] = "Hän", ["hab"] = "Hanoi Sign Language", ["hac"] = "Gurani", ["had"] = "Hatam", ["haf"] = "Haiphong Sign Language", ["hag"] = "Hanga", ["hah"] = "Hahon", ["hai"] = "Haida", ["haj"] = "ฮาชอง", ["hak"] = "แคะ", ["hal"] = "Halang", ["ham"] = "Hewa", ["hao"] = "Hakö", ["hap"] = "Hupla", ["har"] = "Harari", ["has"] = "Haisla", ["hav"] = "Havu", ["haw"] = "ฮาวาย", ["hax"] = "Southern Haida", ["hay"] = "Haya", ["hba"] = "Hamba", ["hbb"] = "Huba", ["hbn"] = "Heiban", ["hbu"] = "Habu", ["hca"] = "Andaman Creole Hindi", ["hch"] = "Huichol", ["hdn"] = "Northern Haida", ["hds"] = "Honduras Sign Language", ["hdy"] = "Hadiyya", ["he"] = "ฮีบรู", ["hea"] = "Northern Qiandong Miao", ["hed"] = "Herdé", ["heg"] = "Helong", ["heh"] = "Hehe", ["hei"] = "Heiltsuk", ["hem"] = "Hemba", ["hgm"] = "Haiǁom", ["hgw"] = "Haigwai", ["hhi"] = "Hoia Hoia", ["hhr"] = "Kerak", ["hhy"] = "Hoyahoya", ["hi"] = "ฮินดี", ["hia"] = "Lamang", ["hib"] = "Hibito", ["hid"] = "Hidatsa", ["hif"] = "ฮินดีแบบฟีจี", ["hig"] = "Kamwe", ["hih"] = "Pamosu", ["hii"] = "Hinduri", ["hij"] = "Hijuk", ["hik"] = "Seit-Kaitetu", ["hil"] = "ฮีลีไกโนน", ["hio"] = "Tshwa", ["hir"] = "Himarimã", ["hit"] = "Hittite", ["hiw"] = "Hiw", ["hix"] = "Hixkaryana", ["hji"] = "Haji", ["hka"] = "Kahe", ["hke"] = "Hunde", ["hkh"] = "Pogali", ["hkk"] = "Hunjara-Kaina Ke", ["hkn"] = "Mel-Khaonh", ["hks"] = "Hong Kong Sign Language", ["hla"] = "Halia", ["hlb"] = "Halbi", ["hld"] = "Halang Doan", ["hle"] = "Hlersu", ["hlt"] = "Nga La", ["hma"] = "Southern Mashan Hmong", ["hmb"] = "Humburi Senni", ["hmc"] = "Central Huishui Hmong", ["hmd"] = "A-Hmao", ["hme"] = "Eastern Huishui Hmong", ["hmf"] = "Hmong Don", ["hmg"] = "Southwestern Guiyang Hmong", ["hmh"] = "Southwestern Huishui Hmong", ["hmi"] = "Northern Huishui Hmong", ["hmj"] = "Ge", ["hmk"] = "Yemaek", ["hml"] = "Luopohe Hmong", ["hmm"] = "Central Mashan Hmong", ["hmn-pro"] = "ม้งดั้งเดิม", ["hmp"] = "Northern Mashan Hmong", ["hmq"] = "Eastern Qiandong Miao", ["hmr"] = "Hmar", ["hms"] = "Southern Qiandong Miao", ["hmt"] = "Hamtai", ["hmu"] = "Hamap", ["hmv"] = "Hmong Dô", ["hmw"] = "Western Mashan Hmong", ["hmx-mie-pro"] = "เมี่ยนดั้งเดิม", ["hmx-pro"] = "ม้ง-เมี่ยนดั้งเดิม", ["hmy"] = "Southern Guiyang Hmong", ["hmz"] = "Hmong Shua", ["hna"] = "Mina", ["hnd"] = "Southern Hindko", ["hne"] = "Chhattisgarhi", ["hnh"] = "ǁAni", ["hni"] = "Hani", ["hnj"] = "ม้งเขียว", ["hnm"] = "ไหหลำ", ["hnn"] = "ฮานูโนโอ", ["hno"] = "Northern Hindko", ["hns"] = "ฮินดูสตานีแบบแคริบเบียน", ["hnu"] = "Hung", ["ho"] = "ฮีรีโมตู", ["hoa"] = "Hoava", ["hob"] = "Austronesian Mari", ["hoc"] = "Ho", ["hod"] = "Holma", ["hoe"] = "Horom", ["hoh"] = "Hobyót", ["hoi"] = "Holikachuk", ["hoj"] = "Hadoti", ["hol"] = "Holu", ["hom"] = "Homa", ["hoo"] = "Holoholo", ["hop"] = "Hopi", ["hor"] = "Horo", ["hos"] = "Ho Chi Minh City Sign Language", ["hot"] = "Hote", ["hov"] = "Hovongan", ["how"] = "Honi", ["hoy"] = "Holiya", ["hoz"] = "Hozo", ["hpo"] = "Hpon", ["hps"] = "Hawai'i Pidgin Sign Language", ["hra"] = "Hrangkhol", ["hrc"] = "Niwer Mil", ["hre"] = "Hrê", ["hrk"] = "Haruku", ["hrm"] = "Horned Miao", ["hro"] = "Haroi", ["hrp"] = "Nhirrpi", ["hrt"] = "Hértevin", ["hru"] = "Hruso", ["hrw"] = "Warwar Feni", ["hrx"] = "ฮุนสริก", ["hrz"] = "Harzani", ["hsb"] = "ซอร์เบียตอนบน", ["hsh"] = "Hungarian Sign Language", ["hsl"] = "Hausa Sign Language", ["hsn"] = "เซียง", ["hss"] = "Harsusi", ["ht"] = "ครีโอลเฮติ", ["hti"] = "Hoti", ["hto"] = "Minica Huitoto", ["hts"] = "Hadza", ["htu"] = "Hitu", ["hu"] = "ฮังการี", ["hub"] = "Huambisa", ["huc"] = "ǂHoan", ["hud"] = "Huaulu", ["huf"] = "Humene", ["hug"] = "Huachipaeri", ["huh"] = "Huilliche", ["hui"] = "Huli", ["huj"] = "Northern Guiyang Hmong", ["huk"] = "Hulung", ["hul"] = "Hula", ["hum"] = "Hungana", ["huo"] = "Hu", ["hup"] = "Hupa", ["huq"] = "Tsat", ["hur"] = "Halkomelem", ["hus"] = "Wastek", ["huu"] = "Murui Huitoto", ["huv"] = "Huave", ["huw"] = "Hukumina", ["hux"] = "Nüpode Huitoto", ["huy"] = "Hulaulá", ["huz"] = "Hunzib", ["hvc"] = "Haitian Vodoun Culture Language", ["hvk"] = "Haveke", ["hvn"] = "Sabu", ["hwa"] = "Wané", ["hwc"] = "Hawaiian Creole", ["hwo"] = "Hwana", ["hy"] = "อาร์มีเนีย", ["hya"] = "Hya", ["hyx-pro"] = "อาร์มีเนียนดั้งเดิม", ["hz"] = "เฮเรโร", ["ia"] = "อินเทอร์ลิงกวา", ["iai"] = "Iaai", ["ian"] = "Iatmul", ["iar"] = "Purari", ["iba"] = "อีบัน", ["ibb"] = "Ibibio", ["ibd"] = "Iwaidja", ["ibe"] = "Akpes", ["ibg"] = "Ibanag", ["ibh"] = "Bih", ["ibl"] = "Ibaloi", ["ibm"] = "Agoi", ["ibn"] = "Ibino", ["ibr"] = "Ibuoro", ["ibu"] = "Ibu", ["iby"] = "Ibani", ["ica"] = "Ede Ica", ["ich"] = "Etkywan", ["icl"] = "Icelandic Sign Language", ["icr"] = "Islander Creole English", ["id"] = "อินโดนีเซีย", ["ida"] = "Idakho-Isukha-Tiriki", ["idb"] = "Indo-Portuguese", ["idc"] = "Idon", ["idd"] = "Ede Idaca", ["ide"] = "Idere", ["idi"] = "Idi", ["idr"] = "Indri", ["ids"] = "Idesa", ["idt"] = "Idaté", ["idu"] = "Idoma", ["ie"] = "อินเทร์ลิงเกว", ["ifa"] = "Amganad Ifugao", ["ifb"] = "Batad Ifugao", ["ife"] = "Ifè", ["iff"] = "Ifo", ["ifk"] = "Tuwali Ifugao", ["ifm"] = "Teke-Fuumu", ["ifu"] = "Mayoyao Ifugao", ["ify"] = "Keley-I Kallahan", ["ig"] = "อิกโบ", ["igb"] = "Ebira", ["ige"] = "Igede", ["igg"] = "Igana", ["igl"] = "Igala", ["igm"] = "Kanggape", ["ign"] = "Ignaciano", ["igo"] = "Isebe", ["igs"] = "Glosa", ["igw"] = "Igwe", ["ihb"] = "Pidgin Iha", ["ihi"] = "Ihievbe", ["ihp"] = "Iha", ["ii"] = "นอซู", ["iir-nur-pro"] = "Proto-Nuristani", ["iir-pro"] = "อินโด-อิเรเนียนดั้งเดิม", ["ijc"] = "Izon", ["ije"] = "Biseni", ["ijj"] = "Ede Ije", ["ijn"] = "Kalabari", ["ijo-pro"] = "Proto-Ijoid", ["ijs"] = "Southeast Ijo", ["ik"] = "Inupiaq", ["ike"] = "Eastern Canadian Inuktitut", ["iki"] = "Iko", ["ikk"] = "Ika", ["ikl"] = "Ikulu", ["iko"] = "Olulumo-Ikom", ["ikp"] = "Ikpeshi", ["ikr"] = "Ikaranggal", ["iks"] = "Inuit Sign Language", ["ikt"] = "Inuvialuktun", ["ikv"] = "Iku-Gora-Ankwa", ["ikw"] = "Ikwere", ["ikx"] = "Ik", ["ikz"] = "Ikizu", ["ila"] = "Ile Ape", ["ilb"] = "Ila", ["ilg"] = "Ilgar", ["ili"] = "Ili Turki", ["ilk"] = "Ilongot", ["ill"] = "อีรานุน", ["ilo"] = "อีโลกาโน", ["ils"] = "International Sign", ["ilu"] = "Ili'uun", ["ilv"] = "Ilue", ["ima"] = "Mala Malasar", ["imi"] = "Anamgura", ["iml"] = "Miluk", ["imn"] = "Imonda", ["imo"] = "Imbongu", ["imr"] = "Imroing", ["ims"] = "Marsian", ["imy"] = "Milyan", ["inb"] = "Inga", ["inc-apa"] = "Apabhramsa", ["inc-ash"] = "ปรากฤตแบบอโศก", ["inc-dng-pro"] = "Proto-Dangari", ["inc-kam"] = "กามรูป", ["inc-kho"] = "Kholosi", ["inc-krd-pro"] = "Proto-Kamta", ["inc-mas"] = "อัสสัมกลาง", ["inc-mbn"] = "เบงกอลกลาง", ["inc-mgu"] = "คุชราตกลาง", ["inc-mor"] = "โอริยากลาง", ["inc-oas"] = "อัสสัมช่วงต้น", ["inc-oaw"] = "Old Awadhi", ["inc-obn"] = "เบงกอลเก่า", ["inc-ogu"] = "คุชราตเก่า", ["inc-ohi"] = "ฮินดีเก่า", ["inc-oor"] = "โอริยาเก่า", ["inc-opa"] = "ปัญจาบเก่า", ["inc-pro"] = "อินโด-อารยันดั้งเดิม", ["ine-ana-pro"] = "อานาโตเลียนดั้งเดิม", ["ine-bsl-pro"] = "บอลโต-สลาวิกดั้งเดิม", ["ine-kal"] = "Kalašma", ["ine-pae"] = "Paeonian", ["ine-pro"] = "อินโด-ยูโรเปียนดั้งเดิม", ["ine-toc-pro"] = "โทแคเรียนดั้งเดิม", ["ing"] = "Deg Xinag", ["inh"] = "อิงกุช", ["inj"] = "Jungle Inga", ["inl"] = "มืออินโดนีเซีย", ["inm"] = "Minaean", ["inn"] = "Isinai", ["ino"] = "Inoke-Yate", ["inp"] = "Iñapari", ["ins"] = "มืออินเดีย", ["int"] = "Intha", ["inz"] = "Ineseño", ["io"] = "อีโด", ["ior"] = "Inor", ["iou"] = "Tuma-Irumu", ["iow"] = "Chiwere", ["ipi"] = "Ipili", ["ipo"] = "Ipiko", ["iqu"] = "Iquito", ["iqw"] = "Ikwo", ["ira-kms-pro"] = "Proto-Komisenian", ["ira-mny-pro"] = "Proto-Munji-Yidgha", ["ira-mpr-pro"] = "Proto-Medo-Parthian", ["ira-pat-pro"] = "ปาทานดั้งเดิม", ["ira-pro"] = "อิเรเนียนดั้งเดิม", ["ira-sgc-pro"] = "ซอกดิกดั้งเดิม", ["ira-sgi-pro"] = "Proto-Sanglechi-Ishkashimi", ["ira-shr-pro"] = "Proto-Shughni-Roshani", ["ira-shy-pro"] = "Proto-Shughni-Yazghulami", ["ira-sym-pro"] = "Proto-Shughni-Yazghulami-Munji", ["ira-wnj"] = "Vanji", ["ira-zgr-pro"] = "Proto-Zaza-Gorani", ["ire"] = "Iresim", ["irh"] = "Irarutu", ["iri"] = "Rigwe", ["irk"] = "Iraqw", ["irn"] = "Irantxe", ["iro-ere"] = "Erie", ["iro-min"] = "Mingo", ["iro-nor-pro"] = "Proto-North Iroquoian", ["iro-pro"] = "Proto-Iroquoian", ["irr"] = "Ir", ["iru"] = "Irula", ["irx"] = "Kamberau", ["iry"] = "Iraya", ["is"] = "ไอซ์แลนด์", ["isa"] = "Isabi", ["isc"] = "Isconahua", ["isd"] = "Isnag", ["ise"] = "Italian Sign Language", ["isg"] = "Irish Sign Language", ["ish"] = "Esan", ["isi"] = "Nkem-Nkum", ["isk"] = "Ishkashimi", ["ism"] = "Masimasi", ["isn"] = "Isanzu", ["iso"] = "Isoko", ["isr"] = "Israeli Sign Language", ["ist"] = "อิสเตรีย", ["isu"] = "Isu", ["isv"] = "Interslavic", ["it"] = "อิตาลี", ["itb"] = "Binongan Itneg", ["itc-pro"] = "อิตาลิกดั้งเดิม", ["itc-psa"] = "Pre-Samnite", ["itd"] = "Southern Tidung", ["ite"] = "Itene", ["iti"] = "Inlaod Itneg", ["itk"] = "Judeo-Italian", ["itl"] = "Itelmen", ["itm"] = "Itu Mbon Uzo", ["ito"] = "Itonama", ["itr"] = "Iteri", ["its"] = "Itsekiri", ["itt"] = "Maeng Itneg", ["itv"] = "Itawit", ["itw"] = "Ito", ["itx"] = "Itik", ["ity"] = "Moyadan Itneg", ["itz"] = "Itza'", ["iu"] = "อินุกติตุต", ["ium"] = "อิ้วเมี่ยน", ["ivb"] = "Ibatan", ["ivv"] = "Ivatan", ["iwk"] = "I-Wak", ["iwm"] = "Iwam", ["iwo"] = "Iwur", ["iws"] = "Sepik Iwam", ["ixc"] = "Ixcatec", ["ixl"] = "Ixil", ["iya"] = "Iyayu", ["iyo"] = "Mesaka", ["iyx"] = "Yaa", ["izh"] = "อิงเกรีย", ["izi"] = "Izi-Ezaa-Ikwo-Mgbo", ["izr"] = "Izere", ["izz"] = "Izi", ["ja"] = "ญี่ปุ่น", ["jaa"] = "Jamamadí", ["jab"] = "Hyam", ["jac"] = "Jakaltek", ["jad"] = "Jahanka", ["jae"] = "Jabem", ["jaf"] = "Jara", ["jah"] = "Jah Hut", ["jaj"] = "Zazao", ["jal"] = "Yalahatan", ["jam"] = "ครีโอลจาเมกา", ["jan"] = "Janday", ["jao"] = "Yanyuwa", ["jaq"] = "Yaqay", ["jas"] = "ชวาแบบนิวแคลิโดเนีย", ["jat"] = "Jakati", ["jau"] = "Yaur", ["jax"] = "Jambi Malay", ["jay"] = "Yan-nhangu", ["jaz"] = "Jawe", ["jbj"] = "Arandai", ["jbk"] = "Barikewa", ["jbn"] = "Nefusa", ["jbo"] = "โลจบาน", ["jbr"] = "Jofotek-Bromnya", ["jbt"] = "Jabutí", ["jbu"] = "Jukun Takum", ["jbw"] = "Yawijibaya", ["jcs"] = "Jamaican Country Sign Language", ["jct"] = "Krymchak", ["jda"] = "Jad", ["jdg"] = "Jadgali", ["jdt"] = "Judeo-Tat", ["jeb"] = "Jebero", ["jee"] = "Jerung", ["jeg"] = "Jeng", ["jeh"] = "Jeh", ["jei"] = "Yei", ["jek"] = "Jeri Kuo", ["jel"] = "Yelmek", ["jen"] = "Dza", ["jer"] = "Jere", ["jet"] = "Manem", ["jeu"] = "Jonkor Bourmataguil", ["jgb"] = "Ngbee", ["jgk"] = "Gwak", ["jgo"] = "Ngomba", ["jhi"] = "Jehai", ["jhs"] = "Jhankot Sign Language", ["jia"] = "Jina", ["jib"] = "Jibu", ["jic"] = "Tol", ["jid"] = "Bu", ["jie"] = "Jilbe", ["jig"] = "Jingulu", ["jih"] = "Shangzhai", ["jii"] = "Jiiddu", ["jil"] = "Jilim", ["jim"] = "Jimjimen", ["jio"] = "เจียเม่า", ["jiq"] = "Khroskyabs", ["jit"] = "Jita", ["jiu"] = "Youle Jino", ["jiv"] = "Shuar", ["jiy"] = "Buyuan Jino", ["jje"] = "เชจู", ["jjr"] = "Zhár", ["jka"] = "Kaera", ["jko"] = "Kubo", ["jkp"] = "Paku Karen", ["jkr"] = "Koro (India)", ["jku"] = "Labir", ["jle"] = "Ngile", ["jls"] = "Jamaican Sign Language", ["jma"] = "Dima", ["jmb"] = "Zumbun", ["jmc"] = "Machame", ["jmd"] = "Yamdena", ["jmi"] = "Jimi", ["jml"] = "Jumli", ["jmn"] = "Makuri Naga", ["jmr"] = "Kamara", ["jmw"] = "Mouwase", ["jmx"] = "Western Juxtlahuaca Mixtec", ["jna"] = "Jangshung", ["jnd"] = "Jandavra", ["jng"] = "Yangman", ["jni"] = "Janji", ["jnj"] = "Yemsa", ["jnl"] = "Rawat", ["jns"] = "Jaunsari", ["job"] = "Joba", ["jod"] = "Wojenaka", ["jor"] = "Jorá", ["jos"] = "Jordanian Sign Language", ["jow"] = "Jowulu", ["jpr"] = "Judeo-Persian", ["jpx-hcj"] = "Hachijō", ["jpx-pro"] = "แจพอนิกดั้งเดิม", ["jpx-ryu-pro"] = "รีวกีวอันดั้งเดิม", ["jqr"] = "Jaqaru", ["jra"] = "Jarai", ["jrr"] = "Jiru", ["jru"] = "Japrería", ["jsl"] = "Japanese Sign Language", ["jua"] = "Júma", ["jub"] = "Wannu", ["juc"] = "จูร์เชน", ["jud"] = "Worodougou", ["juh"] = "Hone", ["jui"] = "Ngadjuri", ["juk"] = "Wapan", ["jul"] = "Jirel", ["jum"] = "Jumjum", ["jun"] = "Juang", ["juo"] = "Jiba", ["jup"] = "Hupdë", ["jur"] = "Jurúna", ["jus"] = "Jumla Sign Language", ["jut"] = "Jutish", ["juu"] = "Ju", ["juw"] = "Wãpha", ["juy"] = "Juray", ["jv"] = "ชวา", ["jvd"] = "Javindo", ["jvn"] = "ชวาแบบแคริบเบียน", ["jwi"] = "Jwira-Pepesa", ["jyy"] = "Jaya", ["ka"] = "จอร์เจีย", ["kaa"] = "การากัลปัก", ["kab"] = "กะไบล์", ["kac"] = "จิ่งเผาะ", ["kad"] = "Kadara", ["kae"] = "Ketangalan", ["kaf"] = "Katso", ["kag"] = "Kajaman", ["kah"] = "Fer", ["kai"] = "Karekare", ["kaj"] = "Jju", ["kak"] = "Kayapa Kallahan", ["kam"] = "Kamba", ["kao"] = "Kassonke", ["kap"] = "Bezhta", ["kaq"] = "Capanahua", ["kar-pro"] = "กะเหรี่ยงดั้งเดิม", ["kaw"] = "ชวาเก่า", ["kax"] = "Kao", ["kay"] = "Kamayurá", ["kba"] = "Kalarko", ["kbb"] = "Kaxuyana", ["kbc"] = "Kadiwéu", ["kbd"] = "คาบาร์เดีย", ["kbe"] = "Kanju", ["kbh"] = "Camsá", ["kbi"] = "Kaptiau", ["kbj"] = "Kari", ["kbk"] = "Grass Koiari", ["kbm"] = "Iwal", ["kbn"] = "Kare (Central Africa)", ["kbo"] = "Keliko", ["kbp"] = "Kabiyé", ["kbq"] = "Kamano", ["kbr"] = "Kafa", ["kbs"] = "Kande", ["kbt"] = "Gabadi", ["kbu"] = "Kabutra", ["kbv"] = "Kamberataro", ["kbw"] = "Kaiep", ["kbx"] = "Ap Ma", ["kbz"] = "Duhwa", ["kca-eas"] = "Eastern Khanty", ["kca-nor"] = "Northern Khanty", ["kca-pro"] = "Proto-Khanty", ["kca-sou"] = "Southern Khanty", ["kcb"] = "Kawacha", ["kcc"] = "Lubila", ["kcd"] = "Ngkâlmpw Kanum", ["kce"] = "Kaivi", ["kcf"] = "Ukaan", ["kcg"] = "Tyap", ["kch"] = "Vono", ["kci"] = "Kamantan", ["kcj"] = "Kobiana", ["kck"] = "Kalanga", ["kcl"] = "Kala", ["kcm"] = "Tar Gula", ["kcn"] = "Nubi", ["kco"] = "Kinalakna", ["kcp"] = "Kanga", ["kcq"] = "Kamo", ["kcr"] = "Katla", ["kcs"] = "Koenoem", ["kct"] = "Kaian", ["kcu"] = "Kikami", ["kcv"] = "Kete", ["kcw"] = "Kabwari", ["kcx"] = "Kachama-Ganjule", ["kcy"] = "Korandje", ["kcz"] = "Konongo", ["kda"] = "Worimi", ["kdc"] = "Kutu", ["kdd"] = "Yankunytjatjara", ["kde"] = "Makonde", ["kdf"] = "Mamusi", ["kdg"] = "Seba", ["kdh"] = "Tem", ["kdi"] = "Kumam", ["kdj"] = "Karamojong", ["kdk"] = "Numèè", ["kdl"] = "Tsikimba", ["kdm"] = "Kagoma", ["kdn"] = "Kunda", ["kdp"] = "Kaningdon-Nindem", ["kdq"] = "Koch", ["kdr"] = "Karaim", ["kdt"] = "กูย", ["kdu"] = "Kadaru", ["kdv"] = "Kado", ["kdw"] = "Koneraw", ["kdx"] = "Kam", ["kdy"] = "Keder", ["kdz"] = "Kwaja", ["kea"] = "ครีโอลกาบูเวร์ดี", ["keb"] = "Kélé", ["kec"] = "Keiga", ["ked"] = "Kerewe", ["kee"] = "Eastern Keres", ["kef"] = "Kpessi", ["keg"] = "Tese", ["keh"] = "Keak", ["kei"] = "Kei", ["kej"] = "Kadar", ["kek"] = "Q'eqchi", ["kel"] = "Kela-Yela", ["kem"] = "Kemak", ["ken"] = "Kenyang", ["keo"] = "Kakwa", ["kep"] = "Kaikadi", ["keq"] = "Kamar", ["ker"] = "Kera", ["kes"] = "Kugbo", ["ket"] = "Ket", ["keu"] = "Akebu", ["kev"] = "Kanikkaran", ["kew"] = "Kewa", ["kex"] = "Kukna", ["key"] = "Kupia", ["kez"] = "Kukele", ["kfa"] = "Kodava", ["kfb"] = "Kolami", ["kfc"] = "Konda-Dora", ["kfd"] = "Korra Koraga", ["kfe"] = "Kota (India)", ["kff"] = "Koya", ["kfg"] = "Kudiya", ["kfh"] = "Kurichiya", ["kfi"] = "Kannada Kurumba", ["kfj"] = "Kemiehua", ["kfk"] = "Kinnauri", ["kfl"] = "Kung", ["kfn"] = "Kuk", ["kfo"] = "Koro (West Africa)", ["kfp"] = "Korwa", ["kfq"] = "Korku", ["kfr"] = "กัจฉ์", ["kfs"] = "Bilaspuri", ["kft"] = "Kanjari", ["kfu"] = "Katkari", ["kfv"] = "Kurmukar", ["kfw"] = "Kharam Naga", ["kfx"] = "Kullu Pahari", ["kfy"] = "Kumaoni", ["kfz"] = "Koromfé", ["kg"] = "คองโก", ["kga"] = "Koyaga", ["kgb"] = "Kawe", ["kgd"] = "Kataang", ["kge"] = "Komering", ["kgf"] = "Kube", ["kgg"] = "Kusunda", ["kgi"] = "Selangor Sign Language", ["kgj"] = "Gamale Kham", ["kgk"] = "Kaiwá", ["kgl"] = "Kunggari", ["kgn"] = "Karingani", ["kgo"] = "Krongo", ["kgp"] = "Kaingang", ["kgq"] = "Kamoro", ["kgr"] = "Abun", ["kgs"] = "Kumbainggar", ["kgt"] = "Somyev", ["kgu"] = "Kobol", ["kgv"] = "Karas", ["kgw"] = "Karon Dori", ["kgx"] = "Kamaru", ["kgy"] = "Kyerung", ["kha"] = "คาซี", ["khb"] = "ไทลื้อ", ["khc"] = "Tukang Besi North", ["khd"] = "Bädi Kanum", ["khe"] = "Korowai", ["khf"] = "Khuen", ["khh"] = "Kehu", ["khi-kho-pro"] = "Proto-Khoe", ["khi-kun"] = "ǃKung", ["khj"] = "Kuturmi", ["khl"] = "Lusi", ["khn"] = "Khandeshi", ["kho"] = "โคตาน", ["khp"] = "Kapauri", ["khq"] = "Koyra Chiini", ["khr"] = "Kharia", ["khs"] = "Kasua", ["kht"] = "คำตี้", ["khu"] = "Nkhumbi", ["khv"] = "Khvarshi", ["khw"] = "Khowar", ["khx"] = "Kanu", ["khy"] = "Ekele", ["khz"] = "Keapara", ["ki"] = "เกโกโย", ["kia"] = "Kim", ["kib"] = "Koalib", ["kic"] = "Kickapoo", ["kid"] = "Koshin", ["kie"] = "Kibet", ["kif"] = "Eastern Parbate Kham", ["kig"] = "Kimaama", ["kih"] = "Kilmeri", ["kii"] = "Kitsai", ["kij"] = "Kilivila", ["kil"] = "Kariya", ["kim"] = "โตฟา", ["kio"] = "Kiowa", ["kip"] = "Sheshi Kham", ["kiq"] = "Kosadle", ["kis"] = "Kis", ["kit"] = "Agob", ["kiv"] = "Kimbu", ["kiw"] = "Northeast Kiwai", ["kix"] = "Khiamniungan Naga", ["kiy"] = "Kirikiri", ["kiz"] = "Kisi", ["kj"] = "Kwanyama", ["kja"] = "Mlap", ["kjb"] = "Q'anjob'al", ["kjc"] = "Coastal Konjo", ["kjd"] = "Southern Kiwai", ["kje"] = "Kisar", ["kjg"] = "ขมุ", ["kjh"] = "คาคัส", ["kji"] = "Zabana", ["kjj"] = "Khinalug", ["kjk"] = "Highland Konjo", ["kjl"] = "Western Parbate Kham", ["kjm"] = "Kháng", ["kjn"] = "Kunjen", ["kjo"] = "Harijan Kinnauri", ["kjp"] = "กะเหรี่ยงโปตะวันออก", ["kjq"] = "Western Keres", ["kjr"] = "Kurudu", ["kjs"] = "East Kewa", ["kjt"] = "กะเหรี่ยงโปแพร่", ["kju"] = "Kashaya", ["kjx"] = "Ramopa", ["kjy"] = "Erave", ["kjz"] = "Bumthangkha", ["kk"] = "คาซัค", ["kka"] = "Kakanda", ["kkb"] = "Kwerisa", ["kkc"] = "Odoodee", ["kkd"] = "Kinuku", ["kke"] = "Kakabe", ["kkf"] = "Kalaktang Monpa", ["kkg"] = "Mabaka Valley Kalinga", ["kkh"] = "เขิน", ["kki"] = "Kagulu", ["kkj"] = "Kako", ["kkk"] = "Kokota", ["kkl"] = "Kosarek Yale", ["kkm"] = "Kiong", ["kkn"] = "Kon Keu", ["kko"] = "Karko", ["kkp"] = "Koko-Bera", ["kkq"] = "Kaiku", ["kkr"] = "Kir-Balar", ["kks"] = "Kirfi", ["kkt"] = "Koi", ["kku"] = "Tumi", ["kkv"] = "Kangean", ["kkw"] = "Teke-Kukuya", ["kkx"] = "Kohin", ["kky"] = "Guugu Yimidhirr", ["kkz"] = "Kaska", ["kl"] = "กรีนแลนด์", ["kla"] = "Klamath-Modoc", ["klb"] = "Kiliwa", ["klc"] = "Kolbila", ["kld"] = "Gamilaraay", ["kle"] = "Kulung", ["klf"] = "Kendeje", ["klg"] = "กาลากันแบบตากาเกาลู", ["klh"] = "Weliki", ["kli"] = "Kalumpang", ["klj"] = "แฆแลจ", ["klk"] = "Kono (Nigeria)", ["kll"] = "กาลากันแบบกากัน", ["klm"] = "Kolom", ["kln"] = "Kalenjin", ["klo"] = "Kapya", ["klp"] = "Kamasa", ["klq"] = "Rumu", ["klr"] = "Khaling", ["kls"] = "Kalasha", ["klt"] = "Nukna", ["klu"] = "Klao", ["klv"] = "Maskelynes", ["klw"] = "ลินดู", ["klx"] = "Koluwawa", ["kly"] = "Kalao", ["klz"] = "Kabola", ["km"] = "เขมร", ["kma"] = "Konni", ["kmb"] = "Kimbundu", ["kmc"] = "ต้งใต้", ["kmd"] = "Madukayang Kalinga", ["kme"] = "Bakole", ["kmf"] = "Kare (New Guinea)", ["kmg"] = "Kâte", ["kmh"] = "Kalam", ["kmi"] = "Kami", ["kmj"] = "Kumarbhag Paharia", ["kmk"] = "Limos Kalinga", ["kml"] = "Tanudan Kalinga", ["kmm"] = "Kom (India)", ["kmn"] = "Awtuw", ["kmo"] = "Kwoma", ["kmp"] = "Gimme", ["kmq"] = "Kwama", ["kmr"] = "เคิร์ดเหนือ", ["kms"] = "Kamasau", ["kmt"] = "Kemtuik", ["kmu"] = "Kanite", ["kmv"] = "Karipúna Creole French", ["kmw"] = "Kumu", ["kmx"] = "Waboda", ["kmy"] = "Koma", ["kmz"] = "Khorasani Turkish", ["kn"] = "กันนฑะ", ["kna"] = "Kanakuru", ["knb"] = "Lubuagan Kalinga", ["knd"] = "Konda", ["kne"] = "กันกานาอือ", ["knf"] = "Mankanya", ["kni"] = "Kanufi", ["knj"] = "Akatek", ["knk"] = "Kuranko", ["knl"] = "Keninjal", ["knm"] = "Kanamari", ["kno"] = "Kono (Sierra Leone)", ["knp"] = "Kwanja", ["knq"] = "Kintaq", ["knr"] = "Kaningra", ["kns"] = "Kensiu", ["knt"] = "Katukina", ["knu"] = "Kono (Guinea)", ["knv"] = "Tabo", ["knx"] = "Kendayan", ["kny"] = "Kanyok", ["knz"] = "Kalamsé", ["ko"] = "เกาหลี", ["ko-ear"] = "เกาหลีใหม่ช่วงต้น", ["koa"] = "Konomala", ["koc"] = "Kpati", ["kod"] = "Kodi", ["koe"] = "Kacipo-Balesi", ["kof"] = "Kubi", ["kog"] = "Cogui", ["koh"] = "Koyo", ["koi"] = "Komi-Permyak", ["kok"] = "กงกัณ", ["kol"] = "Kol (New Guinea)", ["koo"] = "Konzo", ["kop"] = "Waube", ["koq"] = "Kota (Gabon)", ["kos"] = "Kosraean", ["kot"] = "Lagwan", ["kou"] = "Koke", ["kov"] = "Kudu-Camo", ["kow"] = "Kugama", ["koy"] = "Koyukon", ["koz"] = "Korak", ["kpa"] = "Kutto", ["kpb"] = "Mullu Kurumba", ["kpc"] = "Curripaco", ["kpd"] = "Koba", ["kpe"] = "Kpelle", ["kpf"] = "Komba", ["kpg"] = "Kapingamarangi", ["kph"] = "Kplang", ["kpi"] = "Kofei", ["kpj"] = "Karajá", ["kpk"] = "Kpan", ["kpl"] = "Kpala", ["kpm"] = "เกอฮอ", ["kpn"] = "Kepkiriwát", ["kpo"] = "Ikposo", ["kpq"] = "Korupun-Sela", ["kpr"] = "Korafe-Yegha", ["kps"] = "Tehit", ["kpt"] = "Karata", ["kpu"] = "Kafoa", ["kpv"] = "Komi-Zyrian", ["kpw"] = "Kobon", ["kpx"] = "Mountain Koiari", ["kpy"] = "Koryak", ["kpz"] = "Kupsabiny", ["kqa"] = "Mum", ["kqb"] = "Kovai", ["kqc"] = "Doromu-Koki", ["kqd"] = "Koy Sanjaq Surat", ["kqe"] = "กาลากัน", ["kqf"] = "Kakabai", ["kqg"] = "Khe", ["kqh"] = "Kisankasa", ["kqi"] = "Koitabu", ["kqj"] = "Koromira", ["kqk"] = "Kotafon Gbe", ["kql"] = "Kyenele", ["kqm"] = "Khisa", ["kqn"] = "Kaonde", ["kqo"] = "Eastern Krahn", ["kqp"] = "Kimré", ["kqq"] = "Krenak", ["kqr"] = "Kimaragang", ["kqs"] = "Northern Kissi", ["kqt"] = "Klias River Kadazan", ["kqu"] = "Seroa", ["kqv"] = "Okolod", ["kqw"] = "Kandas", ["kqx"] = "Mser", ["kqy"] = "Koorete", ["kqz"] = "Korana", ["kr"] = "กานูรี", ["kra"] = "Kumhali", ["krb"] = "Karkin", ["krc"] = "Karachay-Balkar", ["krd"] = "Kairui-Midiki", ["kre"] = "Panará", ["krf"] = "Koro (Vanuatu)", ["krh"] = "Kurama", ["kri"] = "Krio", ["krj"] = "Kinaray-a", ["krk"] = "Kerek", ["krl"] = "คาเรเลีย", ["krm"] = "Krim", ["krn"] = "Sapo", ["kro-pro"] = "Proto-Kru", ["krp"] = "Korop", ["krr"] = "Kru'ng", ["krs"] = "Kresh", ["kru"] = "กุรุข", ["krv"] = "Kavet", ["krw"] = "Western Krahn", ["krx"] = "Karon", ["kry"] = "Kryts", ["krz"] = "Sota Kanum", ["ks"] = "แคชเมียร์", ["ksa"] = "Shuwa-Zamani", ["ksb"] = "Shambala", ["ksc"] = "Southern Kalinga", ["ksd"] = "Tolai", ["kse"] = "Kuni", ["ksf"] = "Bafia", ["ksg"] = "Kusaghe", ["ksi"] = "Krisa", ["ksj"] = "Uare", ["ksk"] = "Kansa", ["ksl"] = "Kumalu", ["ksm"] = "Kumba", ["ksn"] = "Kasiguranin", ["kso"] = "Kofa", ["ksp"] = "Kaba", ["ksq"] = "Kwaami", ["ksr"] = "Borong", ["kss"] = "Southern Kissi", ["kst"] = "Winyé", ["ksu"] = "Khamyang", ["ksv"] = "Kusu", ["ksw"] = "กะเหรี่ยงสะกอ", ["ksx"] = "Kedang", ["ksy"] = "Kharia Thar", ["ksz"] = "Kodaku", ["kta"] = "Katua", ["ktb"] = "Kambaata", ["ktc"] = "Kholok", ["ktd"] = "Kokata", ["ktf"] = "Kwami", ["ktg"] = "Kalkatungu", ["kth"] = "Karanga", ["kti"] = "North Muyu", ["ktj"] = "Plapo Krumen", ["ktk"] = "Kaniet", ["ktl"] = "Koroshi", ["ktm"] = "Kurti", ["ktn"] = "Karitiâna", ["kto"] = "Kuot", ["ktp"] = "Kaduo", ["ktq"] = "Katabaga", ["ktr"] = "Kota Marudu Tinagas", ["kts"] = "South Muyu", ["ktt"] = "Ketum", ["ktu"] = "Kituba", ["ktv"] = "กะตูตะวันออก", ["ktw"] = "Kato", ["ktx"] = "Kaxararí", ["kty"] = "Kango", ["ktz"] = "Juǀ'hoan", ["ku-pro"] = "เคอร์ดิชดั้งเดิม", ["kub"] = "Kutep", ["kuc"] = "Kwinsu", ["kud"] = "Auhelawa", ["kue"] = "Kuman", ["kuf"] = "กะตูตะวันตก", ["kug"] = "Kupa", ["kuh"] = "Kushi", ["kui"] = "Kuikúro", ["kuj"] = "Kuria", ["kuk"] = "Kepo'", ["kul"] = "Kulere", ["kum"] = "คูมุก", ["kun"] = "Kunama", ["kuo"] = "Kumukio", ["kup"] = "Kunimaipa", ["kuq"] = "Karipuna", ["kus"] = "Kusaal", ["kut"] = "Ktunaxa", ["kuu"] = "Upper Kuskokwim", ["kuv"] = "Kur", ["kuw"] = "Kpagua", ["kux"] = "Kukatja", ["kuy"] = "Kuuku-Ya'u", ["kuz"] = "Kunza", ["kva"] = "Bagvalal", ["kvb"] = "Kubu", ["kvc"] = "Kove", ["kvd"] = "Kui (Indonesia)", ["kve"] = "Kalabakan", ["kvf"] = "Kabalai", ["kvg"] = "Kuni-Boazi", ["kvh"] = "Komodo", ["kvi"] = "Kwang", ["kvj"] = "Psikye", ["kvk"] = "Korean Sign Language", ["kvl"] = "Brek Karen", ["kvm"] = "Kendem", ["kvn"] = "Border Kuna", ["kvo"] = "Dobel", ["kvp"] = "Kompane", ["kvq"] = "Geba Karen", ["kvr"] = "Kerinci", ["kvt"] = "Lahta Karen", ["kvu"] = "Yinbaw Karen", ["kvv"] = "Kola", ["kvw"] = "Wersing", ["kvx"] = "Parkari Koli", ["kvy"] = "Yintale Karen", ["kvz"] = "Tsakwambo", ["kw"] = "คอร์นวอลล์", ["kwa"] = "Dâw", ["kwb"] = "Baa", ["kwc"] = "Likwala", ["kwd"] = "Kwaio", ["kwe"] = "Kwerba", ["kwf"] = "Kwara'ae", ["kwg"] = "Sara Kaba Deme", ["kwh"] = "Kowiai", ["kwi"] = "Awa-Cuaiquer", ["kwj"] = "Kwanga", ["kwk"] = "Kwak'wala", ["kwl"] = "Kofyar", ["kwm"] = "Kwambi", ["kwn"] = "Kwangali", ["kwo"] = "Kwomtari", ["kwp"] = "Kodia", ["kwq"] = "Kwak", ["kwr"] = "Kwer", ["kws"] = "Kwese", ["kwt"] = "Kwesten", ["kwu"] = "Kwakum", ["kwv"] = "Sara Kaba Náà", ["kww"] = "Kwinti", ["kwx"] = "Khirwar", ["kwz"] = "Kwadi", ["kxa"] = "Kairiru", ["kxb"] = "Krobu", ["kxc"] = "Konso", ["kxd"] = "มลายูแบบบรูไน", ["kxe"] = "Kakihum", ["kxf"] = "Manumanaw Karen", ["kxh"] = "Karo", ["kxi"] = "Keningau Murut", ["kxj"] = "Kulfa", ["kxk"] = "Zayein Karen", ["kxl"] = "Nepali Kurux", ["kxm"] = "เขมรเหนือ", ["kxn"] = "Kanowit", ["kxo"] = "Kanoé", ["kxp"] = "Wadiyara Koli", ["kxq"] = "Smärky Kanum", ["kxr"] = "Manus Koro", ["kxs"] = "Kangjia", ["kxt"] = "Koiwat", ["kxu"] = "Kui (India)", ["kxv"] = "Kuvi", ["kxw"] = "Konai", ["kxx"] = "Likuba", ["kxy"] = "Kayong", ["kxz"] = "Kerewo", ["ky"] = "คีร์กีซ", ["kya"] = "Kwaya", ["kyb"] = "Butbut Kalinga", ["kyc"] = "Kyaka", ["kyd"] = "Karey", ["kye"] = "Krache", ["kyf"] = "Kouya", ["kyg"] = "Keyagana", ["kyh"] = "Karok", ["kyi"] = "Kiput", ["kyj"] = "กาเรา", ["kyk"] = "Kamayo", ["kyl"] = "Kalapuya", ["kym"] = "Kpatili", ["kyn"] = "Karolanos", ["kyo"] = "Kelon", ["kyp"] = "Kang", ["kyq"] = "Kenga", ["kyr"] = "Kuruáya", ["kys"] = "Baram Kayan", ["kyt"] = "Kayagar", ["kyu"] = "กะยาตะวันตก", ["kyv"] = "Kayort", ["kyw"] = "Kudmali", ["kyx"] = "Rapoisi", ["kyy"] = "Kambaira", ["kyz"] = "Kayabí", ["kza"] = "Western Karaboro", ["kzb"] = "Kaibobo", ["kzc"] = "Bondoukou Kulango", ["kzd"] = "Kadai", ["kzf"] = "Da'a Kaili", ["kzg"] = "คิไก", ["kzh"] = "Dongolawi", ["kzi"] = "Kelabit", ["kzj"] = "กาดาซันชายฝั่ง", ["kzk"] = "Kazukuru", ["kzl"] = "Kayeli", ["kzm"] = "Kais", ["kzn"] = "Kokola", ["kzo"] = "Kaningi", ["kzp"] = "Kaidipang", ["kzq"] = "Kaike", ["kzr"] = "Karang", ["kzs"] = "Sugut Dusun", ["kzt"] = "Tambunan Dusun", ["kzu"] = "Kayupulau", ["kzv"] = "Komyandaret", ["kzw"] = "Kariri", ["kzx"] = "Kamarian", ["kzy"] = "Kango-Sua", ["kzz"] = "Kalabra", ["la"] = "ละติน", ["laa"] = "Lapuyan Subanun", ["lab"] = "Linear A", ["lac"] = "Lacandon", ["lad"] = "ลาดิโน", ["lae"] = "Pattani", ["laf"] = "Lafofa", ["lag"] = "Langi", ["lah"] = "Lahnda", ["lai"] = "Lambya", ["laj"] = "Lango (Uganda)", ["lak"] = "Laka", ["lam"] = "Lamba", ["lan"] = "Laru", ["lap"] = "Kabba-Laka", ["laq"] = "Qabiao", ["lar"] = "Larteh", ["las"] = "Gur Lama", ["lau"] = "Laba", ["law"] = "Lauje", ["lax"] = "Tiwa", ["lay"] = "Lama Bai", ["laz"] = "Aribwatsa", ["lb"] = "ลักเซมเบิร์ก", ["lbb"] = "Label", ["lbc"] = "ลักเกีย", ["lbe"] = "ลัก", ["lbf"] = "Tinani", ["lbg"] = "Laopang", ["lbi"] = "La'bi", ["lbj"] = "ลาดัก", ["lbk"] = "Central Bontoc", ["lbl"] = "Libon Bikol", ["lbm"] = "Lodhi", ["lbn"] = "Lamet", ["lbo"] = "Laven", ["lbq"] = "Wampar", ["lbr"] = "Northern Lorung", ["lbs"] = "Libyan Sign Language", ["lbt"] = "Lachi", ["lbu"] = "Labu", ["lbv"] = "Lavatbura-Lamusong", ["lbw"] = "Tolaki", ["lbx"] = "Lawangan", ["lby"] = "Lamu-Lamu", ["lbz"] = "Lardil", ["lcc"] = "Legenyem", ["lcd"] = "Lola", ["lce"] = "Loncong", ["lcf"] = "Lubu", ["lch"] = "Luchazi", ["lcl"] = "Lisela", ["lcm"] = "Tungag", ["lcp"] = "เลอเวือะตะวันตก", ["lcq"] = "Luhu", ["lcs"] = "Lisabata-Nuniali", ["lda"] = "Kla", ["ldb"] = "Idun", ["ldd"] = "Luri (Nigeria)", ["ldg"] = "Lenyima", ["ldh"] = "Lamja-Dengsa-Tola", ["ldj"] = "Lemoro", ["ldk"] = "Leelau", ["ldl"] = "Kaan", ["ldm"] = "Landoma", ["ldn"] = "Láadan", ["ldo"] = "Loo", ["ldp"] = "Tso", ["ldq"] = "Lufu", ["lea"] = "Lega-Shabunda", ["leb"] = "Lala-Bisa", ["lec"] = "Leco", ["led"] = "Lendu", ["lee"] = "Lyélé", ["lef"] = "Lelemi", ["leh"] = "Lenje", ["lei"] = "Lemio", ["lej"] = "Lengola", ["lek"] = "Leipon", ["lel"] = "Lele (Congo)", ["lem"] = "Nomaande", ["len"] = "Honduran Lenca", ["leo"] = "Mengisa", ["lep"] = "Lepcha", ["leq"] = "Lembena", ["ler"] = "Lenkau", ["les"] = "Lese", ["let"] = "Lesing-Gelimi", ["leu"] = "Kara (New Guinea)", ["lev"] = "Lamma", ["lew"] = "Ledo Kaili", ["lex"] = "Luang", ["ley"] = "Lemolang", ["lez"] = "เลซกี", ["lfa"] = "Lefa", ["lfn"] = "Lingua Franca Nova", ["lg"] = "ลูกันดา", ["lga"] = "Lungga", ["lgb"] = "Laghu", ["lgg"] = "Lugbara", ["lgh"] = "Laghuu", ["lgi"] = "Lengilu", ["lgk"] = "Neverver", ["lgl"] = "Wala", ["lgm"] = "Lega-Mwenga", ["lgn"] = "Opuuo", ["lgq"] = "Logba", ["lgr"] = "Lengo", ["lgs"] = "Guinea-Bissau Sign Language", ["lgt"] = "Pahi", ["lgu"] = "Longgu", ["lgz"] = "Ligenza", ["lha"] = "Laha (Vietnam)", ["lhh"] = "Laha (Indonesia)", ["lhi"] = "Lahu Shi", ["lhl"] = "Lahul Lohar", ["lhn"] = "Lahanan", ["lhp"] = "Lhokpu", ["lhs"] = "Mlahsö", ["lht"] = "Lo-Toga", ["lhu"] = "Lahu", ["li"] = "ลิมเบิร์ก", ["lia"] = "West-Central Limba", ["lib"] = "Likum", ["lic"] = "ไหล", ["lid"] = "Nyindrou", ["lie"] = "Likila", ["lif"] = "ลิมบู", ["lig"] = "Ligbi", ["lih"] = "Lihir", ["lii"] = "Lingkhim", ["lij"] = "ลีกูเรีย", ["lik"] = "Lika", ["lil"] = "Lillooet", ["lio"] = "Liki", ["lip"] = "Sekpele", ["liq"] = "Libido", ["lir"] = "Liberian Kreyol", ["lis"] = "ลีสู่", ["liu"] = "Logorik", ["liv"] = "ลีโวเนีย", ["liw"] = "Col", ["lix"] = "Liabuku", ["liy"] = "Banda-Bambari", ["liz"] = "Libinza", ["lja"] = "Golpa", ["lje"] = "Rampi", ["lji"] = "Laiyolo", ["ljl"] = "Li'o", ["ljp"] = "Lampung Api", ["ljw"] = "Yirandali", ["ljx"] = "Yuru", ["lka"] = "Lakalei", ["lkb"] = "Kabras", ["lkc"] = "Kucong", ["lkd"] = "Lakondê", ["lke"] = "Kenyi", ["lkh"] = "Lakha", ["lki"] = "Laki", ["lkj"] = "Remun", ["lkl"] = "Laeko-Libuat", ["lkm"] = "Kalaamaya", ["lkn"] = "Lakon", ["lko"] = "Khayo", ["lkr"] = "Päri", ["lks"] = "Kisa", ["lkt"] = "ลาโคตา", ["lku"] = "Kungkari", ["lky"] = "Lokoya", ["lla"] = "Lala-Roba", ["llb"] = "Lolo", ["llc"] = "Lele (Guinea)", ["lld"] = "ลาดิน", ["lle"] = "Lele (New Guinea)", ["llf"] = "Hermit", ["llg"] = "Lole", ["llh"] = "Lamu", ["lli"] = "Teke-Laali", ["llj"] = "Ladji-Ladji", ["llk"] = "Lelak", ["lll"] = "Lilau", ["llm"] = "Lasalimu", ["lln"] = "Lele (Chad)", ["llo"] = "Khlor", ["llp"] = "North Efate", ["llq"] = "Lolak", ["lls"] = "Lithuanian Sign Language", ["llu"] = "Lau", ["llx"] = "Lauan", ["lma"] = "East Limba", ["lmb"] = "Merei", ["lmc"] = "Limilngan", ["lmd"] = "Lumun", ["lme"] = "Pévé", ["lmf"] = "South Lembata", ["lmg"] = "Lamogai", ["lmh"] = "Lambichhong", ["lmi"] = "Lombi", ["lmj"] = "West Lembata", ["lmk"] = "Lamkang", ["lml"] = "Raga", ["lmn"] = "Lambadi", ["lmo"] = "ลอมบาร์ด", ["lmp"] = "Limbum", ["lmq"] = "Lamatuka", ["lmr"] = "Lamalera", ["lmu"] = "Lamenu", ["lmv"] = "Lomaiviti", ["lmw"] = "Lake Miwok", ["lmx"] = "Laimbue", ["lmy"] = "ลัมโบยา", ["lmz"] = "Lumbee", ["ln"] = "ลิงกาลา", ["lna"] = "Langbashe", ["lnb"] = "Mbalanhu", ["lnd"] = "Lun Bawang", ["lnh"] = "Lanoh", ["lni"] = "Daantanai'", ["lnj"] = "Linngithigh", ["lnl"] = "South Central Banda", ["lnm"] = "Langam", ["lnn"] = "Lorediakarkar", ["lno"] = "Lango (Sudan)", ["lns"] = "Lamnso'", ["lnu"] = "Longuda", ["lnw"] = "Lanima", ["lo"] = "ลาว", ["loa"] = "Loloda", ["lob"] = "Lobi", ["loc"] = "Inonhan", ["lod"] = "เบอราวัน", ["loe"] = "Saluan", ["lof"] = "Logol", ["log"] = "Logo", ["loh"] = "Narim", ["loi"] = "Lomakka", ["loj"] = "Lou", ["lok"] = "Loko", ["lol"] = "Mongo", ["lom"] = "Loma", ["lon"] = "Malawi Lomwe", ["loo"] = "Lombo", ["lop"] = "Lopa", ["loq"] = "Lobala", ["lor"] = "Téén", ["los"] = "Loniu", ["lot"] = "Lotuko", ["lou"] = "ครีโอลลุยเซียนา", ["lov"] = "Lopi", ["low"] = "Tampias Lobu", ["lox"] = "Loun", ["loz"] = "Lozi", ["lpa"] = "Lelepa", ["lpe"] = "Lepki", ["lpn"] = "Long Phuri Naga", ["lpo"] = "Lipo", ["lpx"] = "Lopit", ["lra"] = "Rara Bakati'", ["lrc"] = "Northern Luri", ["lre"] = "Laurentian", ["lrg"] = "Laragia", ["lri"] = "Marachi", ["lrk"] = "Loarki", ["lrl"] = "Larestani", ["lrm"] = "Marama", ["lrn"] = "Lorang", ["lro"] = "Laro", ["lrr"] = "Southern Lorung", ["lrt"] = "Larantuka Malay", ["lrv"] = "Larëvat", ["lrz"] = "Lemerig", ["lsa"] = "Lasgerdi", ["lsd"] = "Lishana Deni", ["lse"] = "Lusengo", ["lsh"] = "Lish", ["lsi"] = "Lashi", ["lsl"] = "Latvian Sign Language", ["lsm"] = "Saamia", ["lsn"] = "Tibetan Sign Language", ["lso"] = "Laos Sign Language", ["lsp"] = "Panamanian Sign Language", ["lsr"] = "Aruop", ["lss"] = "Lasi", ["lst"] = "Trinidad and Tobago Sign Language", ["lsv"] = "Sivia Sign Language", ["lsy"] = "Mauritian Sign Language", ["lt"] = "ลิทัวเนีย", ["ltc"] = "จีนยุคกลาง", ["ltg"] = "ลัตเกล", ["lti"] = "Leti", ["ltn"] = "Latundê", ["lto"] = "Olutsotso", ["lts"] = "Lutachoni", ["ltu"] = "Latu", ["lu"] = "Luba-Katanga", ["lua"] = "Luba-Kasai", ["luc"] = "Aringa", ["lud"] = "Ludian", ["lue"] = "Luvale", ["luf"] = "Laua", ["luh"] = "หล่อยแอว๋", ["lui"] = "Luiseño", ["luj"] = "Luna", ["luk"] = "Lunanakha", ["lul"] = "Olu'bo", ["lum"] = "Luimbi", ["lun"] = "Lunda", ["luo"] = "Luo", ["lup"] = "Lumbu", ["luq"] = "Lucumí", ["lur"] = "Laura", ["lus"] = "มีโซ", ["lut"] = "Lushootseed", ["luu"] = "Lumba-Yakkha", ["luv"] = "Luwati", ["luy"] = "Luhya", ["luz"] = "Southern Luri", ["lv"] = "ลัตเวีย", ["lva"] = "Maku'a", ["lvi"] = "Lawi", ["lvk"] = "Lavukaleve", ["lvl"] = "Lwel", ["lvu"] = "Levuka", ["lwa"] = "Lwalu", ["lwe"] = "Lewo Eleng", ["lwg"] = "Wanga", ["lwh"] = "White Lachi", ["lwl"] = "เลอเวือะตะวันออก", ["lwm"] = "Laomian", ["lwo"] = "Luwo", ["lws"] = "Malawian Sign Language", ["lwt"] = "Lewotobi", ["lwu"] = "Lawu", ["lww"] = "Lewo", ["lya"] = "Layakha", ["lyg"] = "ลึงงัม", ["lyn"] = "Luyana", ["lzh"] = "จีนวรรณกรรม", ["lzl"] = "Litzlitz", ["lzn"] = "Leinong Naga", ["lzz"] = "ลัซ", ["maa"] = "San Jerónimo Tecóatl Mazatec", ["mab"] = "Yutanduchi Mixtec", ["mad"] = "Madurese", ["mae"] = "Bo-Rukul", ["maf"] = "Mafa", ["mag"] = "มคหะ", ["mai"] = "ไมถิลี", ["maj"] = "Jalapa de Díaz Mazatec", ["mak"] = "มากัซซาร์", ["mam"] = "Mam", ["man"] = "Mandingo", ["map-ata-pro"] = "Proto-Atayalic", ["map-bms"] = "Banyumasan", ["map-pro"] = "ออสโตรนีเซียนดั้งเดิม", ["maq"] = "Chiquihuitlán Mazatec", ["mas"] = "มาไซ", ["mat"] = "Matlatzinca", ["mau"] = "Huautla Mazatec", ["mav"] = "Sateré-Mawé", ["maw"] = "Mampruli", ["max"] = "มลายูแบบโมลุกกะเหนือ", ["maz"] = "Central Mazahua", ["mba"] = "Higaonon", ["mbb"] = "Western Bukidnon Manobo", ["mbc"] = "Macushi", ["mbd"] = "Dibabawon Manobo", ["mbe"] = "Molale", ["mbf"] = "Baba Malay", ["mbh"] = "Mangseng", ["mbi"] = "Ilianen Manobo", ["mbj"] = "Nadëb", ["mbk"] = "Malol", ["mbl"] = "Maxakalí", ["mbm"] = "Ombamba", ["mbn"] = "Macaguán", ["mbo"] = "Mbo (Cameroon)", ["mbp"] = "Wiwa", ["mbq"] = "Maisin", ["mbr"] = "Nukak Makú", ["mbs"] = "Sarangani Manobo", ["mbt"] = "Matigsalug Manobo", ["mbu"] = "Mbula-Bwazza", ["mbv"] = "Mbulungish", ["mbw"] = "Maring", ["mbx"] = "Sepik Mari", ["mby"] = "Memoni", ["mbz"] = "Amoltepec Mixtec", ["mca"] = "Maca", ["mcb"] = "Machiguenga", ["mcc"] = "Bitur", ["mcd"] = "Sharanahua", ["mce"] = "Itundujia Mixtec", ["mcf"] = "Matsés", ["mcg"] = "Mapoyo", ["mch"] = "Ye'kwana", ["mci"] = "Mese", ["mcj"] = "Mvanip", ["mck"] = "Mbunda", ["mcl"] = "Macaguaje", ["mcm"] = "Kristang", ["mcn"] = "Masana", ["mco"] = "Coatlán Mixe", ["mcp"] = "Makaa", ["mcq"] = "Ese", ["mcr"] = "Menya", ["mcs"] = "Mambai", ["mcu"] = "Cameroon Mambila", ["mcw"] = "Mawa", ["mcx"] = "Mpiemo", ["mcy"] = "South Watut", ["mcz"] = "Mawan", ["mda"] = "Mada (Nigeria)", ["mdb"] = "Morigi", ["mdc"] = "Male", ["mdd"] = "Mbum", ["mde"] = "Bura Mabang", ["mdf"] = "มอกชา", ["mdg"] = "Massalat", ["mdh"] = "มากินดาเนา", ["mdi"] = "Mamvu", ["mdj"] = "Mangbetu", ["mdk"] = "Mangbutu", ["mdl"] = "Maltese Sign Language", ["mdm"] = "Mayogo", ["mdn"] = "Mbati", ["mdp"] = "Mbala", ["mdq"] = "Mbole", ["mdr"] = "Mandar", ["mds"] = "Maria", ["mdt"] = "Mbere", ["mdu"] = "Mboko", ["mdv"] = "Santa Lucía Monteverde Mixtec", ["mdw"] = "Mbosi", ["mdx"] = "Dizin", ["mdy"] = "Maale", ["mdz"] = "Suruí Do Pará", ["mea"] = "Menka", ["meb"] = "Ikobi-Mena", ["mec"] = "Mara", ["med"] = "Melpa", ["mee"] = "Mengen", ["mef"] = "Megam", ["meh"] = "Southwestern Tlaxiaco Mixtec", ["mei"] = "Midob", ["mej"] = "Meyah", ["mek"] = "Mekeo", ["mel"] = "Central Melanau", ["mem"] = "Mangala", ["men"] = "Mende", ["meo"] = "มลายูแบบเกอดะฮ์", ["mep"] = "Miriwung", ["meq"] = "Merey", ["mer"] = "Meru", ["mes"] = "Masmaje", ["met"] = "Mato", ["meu"] = "Motu", ["mev"] = "Mano", ["mew"] = "Maaka", ["mey"] = "Hassaniya Arabic", ["mez"] = "Menominee", ["mfa"] = "มลายูแบบปัตตานี", ["mfb"] = "Bangka", ["mfc"] = "Mba", ["mfd"] = "Mendankwe-Nkwen", ["mfe"] = "ครีโอลมอริเชียส", ["mff"] = "Naki", ["mfg"] = "Mixifore", ["mfh"] = "Matal", ["mfi"] = "Wandala", ["mfj"] = "Mefele", ["mfk"] = "North Mofu", ["mfl"] = "Putai", ["mfm"] = "Marghi South", ["mfn"] = "Cross River Mbembe", ["mfo"] = "Mbe", ["mfp"] = "Makassar Malay", ["mfq"] = "Moba", ["mfr"] = "Marrithiyel", ["mfs"] = "Mexican Sign Language", ["mft"] = "Mokerang", ["mfu"] = "Mbwela", ["mfv"] = "Mandjak", ["mfw"] = "Mulaha", ["mfx"] = "Melo", ["mfy"] = "Mayo", ["mfz"] = "Mabaan", ["mg"] = "มาลากาซี", ["mga"] = "ไอริชกลาง", ["mgb"] = "Mararit", ["mgc"] = "Morokodo", ["mgd"] = "Moru", ["mge"] = "Mango", ["mgf"] = "Maklew", ["mgg"] = "Mpongmpong", ["mgh"] = "Makhuwa-Meetto", ["mgi"] = "Jili", ["mgj"] = "Abureni", ["mgk"] = "Mawes", ["mgl"] = "Maleu-Kilenge", ["mgm"] = "Mambae", ["mgn"] = "Mbangi", ["mgo"] = "Meta'", ["mgp"] = "Eastern Magar", ["mgq"] = "Malila", ["mgr"] = "Mambwe-Lungu", ["mgs"] = "Manda (Tanzania)", ["mgt"] = "Mongol", ["mgu"] = "Mailu", ["mgv"] = "Matengo", ["mgw"] = "Matumbi", ["mgy"] = "Mbunga", ["mgz"] = "Mbugwe", ["mh"] = "มาร์แชลล์", ["mha"] = "Manda (India)", ["mhb"] = "Mahongwe", ["mhc"] = "Mocho", ["mhd"] = "Mbugu", ["mhe"] = "Besisi", ["mhf"] = "Mamaa", ["mhg"] = "Marrgu", ["mhi"] = "Ma'di", ["mhj"] = "Mogholi", ["mhk"] = "Mungaka", ["mhl"] = "Mauwake", ["mhm"] = "Makhuwa-Moniga", ["mhn"] = "โมเชโน", ["mho"] = "Mashi", ["mhp"] = "Balinese Malay", ["mhq"] = "Mandan", ["mhr"] = "Eastern Mari", ["mhs"] = "Buru (Indonesia)", ["mht"] = "Mandahuaca", ["mhu"] = "Taraon", ["mhw"] = "Mbukushu", ["mhx"] = "Lhao Vo", ["mhy"] = "Ma'anyan", ["mhz"] = "Mor (Austronesian)", ["mi"] = "มาวรี", ["mia"] = "Miami", ["mib"] = "Atatláhuca Mixtec", ["mic"] = "Mi'kmaq", ["mid"] = "Mandaic", ["mie"] = "Ocotepec Mixtec", ["mif"] = "Mofu-Gudur", ["mig"] = "San Miguel el Grande Mixtec", ["mih"] = "Chayuco Mixtec", ["mii"] = "Chigmecatitlán Mixtec", ["mij"] = "Mungbam", ["mik"] = "Mikasuki", ["mil"] = "Peñoles Mixtec", ["mim"] = "Alacatlatzala Mixtec", ["min"] = "มีนังกาเบา", ["mio"] = "Pinotepa Nacional Mixtec", ["mip"] = "Apasco-Apoala Mixtec", ["miq"] = "Miskito", ["mir"] = "Isthmus Mixe", ["mis-hkl"] = "Kelantan Peranakan Hokkien", ["mis-idn"] = "Idiom Neutral", ["mis-isa"] = "Isaurian", ["mis-jie"] = "Jie", ["mis-jzh"] = "Jizhao", ["mis-kas"] = "Kassite", ["mis-mmd"] = "Mimi of Decorse", ["mis-mmn"] = "Mimi of Nachtigal", ["mis-phi"] = "Philistine", ["mis-rou"] = "Rouran", ["mis-tdl"] = "Turdulian", ["mis-tdt"] = "Turdetanian", ["mis-tnw"] = "Tangwang", ["mis-tuh"] = "Tuyuhun", ["mis-tuo"] = "Tuoba", ["mis-wuh"] = "Wuhuan", ["mis-xbi"] = "Xianbei", ["mis-xnu"] = "Xiongnu", ["mit"] = "Southern Puebla Mixtec", ["miu"] = "Cacaloxtepec Mixtec", ["miw"] = "Akoye", ["mix"] = "Mixtepec Mixtec", ["miy"] = "Ayutla Mixtec", ["miz"] = "Coatzospan Mixtec", ["mjb"] = "Makalero", ["mjc"] = "San Juan Colorado Mixtec", ["mjd"] = "Northwest Maidu", ["mje"] = "Muskum", ["mjg-mgl"] = "Mongghul", ["mjg-mgr"] = "Mangghuer", ["mji"] = "Kim Mun", ["mjj"] = "Mawak", ["mjk"] = "Matukar", ["mjl"] = "Mandeali", ["mjm"] = "Medebur", ["mjn"] = "Mebu", ["mjo"] = "Malankuravan", ["mjp"] = "Malapandaram", ["mjq"] = "Malaryan", ["mjr"] = "Malavedan", ["mjs"] = "Miship", ["mjt"] = "Sawriya Paharia", ["mju"] = "Manna-Dora", ["mjv"] = "Mannan", ["mjw"] = "Karbi", ["mjx"] = "Mahali", ["mjy"] = "Mahican", ["mjz"] = "Majhi", ["mk"] = "มาซิโดเนีย", ["mka"] = "Mbre", ["mkb"] = "Mal Paharia", ["mkc"] = "Siliput", ["mke"] = "Mawchi", ["mkf"] = "Miya", ["mkg"] = "Mak (China)", ["mkh-asl-pro"] = "Proto-Aslian", ["mkh-ban-pro"] = "Proto-Bahnaric", ["mkh-kat-pro"] = "Proto-Katuic", ["mkh-khm-pro"] = "ขมุอิกดั้งเดิม", ["mkh-kmr-pro"] = "เขมรดั้งเดิม", ["mkh-mmn"] = "มอญกลาง", ["mkh-mnc-pro"] = "มอญดั้งเดิม", ["mkh-mvi"] = "เวียดนามกลาง", ["mkh-pal-pro"] = "ปะหล่องดั้งเดิม", ["mkh-pea-pro"] = "แพริกดั้งเดิม", ["mkh-pkn-pro"] = "Proto-Pakanic", ["mkh-pro"] = "มอญ-เขมรดั้งเดิม", ["mkh-vie-pro"] = "เวียตติกดั้งเดิม", ["mki"] = "Dhatki", ["mkj"] = "โมกิล", ["mkk"] = "Byep", ["mkl"] = "Mokole", ["mkm"] = "Moklen", ["mkn"] = "Kupang Malay", ["mko"] = "Mingang Doso", ["mkp"] = "Moikodi", ["mkq"] = "Bay Miwok", ["mkr"] = "Malas", ["mks"] = "Silacayoapan Mixtec", ["mkt"] = "Vamale", ["mku"] = "Konyanka Maninka", ["mkv"] = "Mav̋ea", ["mkx"] = "Cinamiguin Manobo", ["mky"] = "Taba", ["mkz"] = "Makasae", ["ml"] = "มลยาฬัม", ["mla"] = "Tamambo", ["mlb"] = "Mbule", ["mlc"] = "Caolan", ["mle"] = "Manambu", ["mlf"] = "มัล", ["mlh"] = "Mape", ["mli"] = "Malimpung", ["mlj"] = "Miltu", ["mlk"] = "Ilwana", ["mll"] = "Malua Bay", ["mlm"] = "Mulam", ["mln"] = "Malango", ["mlo"] = "Mlomp", ["mlp"] = "Bargam", ["mlq"] = "Western Maninkakan", ["mlr"] = "Vame", ["mls"] = "Masalit", ["mlu"] = "To'abaita", ["mlv"] = "Mwotlap", ["mlw"] = "Moloko", ["mlx"] = "Malfaxal", ["mlz"] = "Malaynon", ["mma"] = "Mama", ["mmb"] = "Momina", ["mmc"] = "Michoacán Mazahua", ["mmd"] = "Maonan", ["mme"] = "Tirax", ["mmf"] = "Mundat", ["mmg"] = "North Ambrym", ["mmh"] = "Mehináku", ["mmi"] = "Musar", ["mmj"] = "Majhwar", ["mmk"] = "Mukha-Dora", ["mml"] = "Man Met", ["mmm"] = "Maii", ["mmn"] = "Mamanwa", ["mmo"] = "Mangga Buang", ["mmp"] = "Musan", ["mmq"] = "Aisi", ["mmr"] = "Western Xiangxi Miao", ["mmt"] = "Malalamai", ["mmu"] = "Mmaala", ["mmv"] = "Miriti", ["mmw"] = "Emae", ["mmx"] = "Madak", ["mmy"] = "Migaama", ["mmz"] = "Mabaale", ["mn"] = "มองโกเลีย", ["mna"] = "Mbula", ["mnb"] = "Muna", ["mnc"] = "แมนจู", ["mnd"] = "Mondé", ["mne"] = "Naba", ["mnf"] = "Mundani", ["mng"] = "Eastern Mnong", ["mnh"] = "Mono (Congo)", ["mni"] = "มณีปุระ", ["mnj"] = "Munji", ["mnk"] = "Mandinka", ["mnl"] = "Tiale", ["mnm"] = "Mapena", ["mnn"] = "มนองใต้", ["mnp"] = "หมิ่นเหนือ", ["mnq"] = "Minriq", ["mnr"] = "Mono (California)", ["mns-cen"] = "Central Mansi", ["mns-nor"] = "Northern Mansi", ["mns-pro"] = "Proto-Mansi", ["mns-sou"] = "Southern Mansi", ["mnt"] = "Maykulan", ["mnu"] = "Mer", ["mnv"] = "Rennellese", ["mnw"] = "มอญ", ["mnw-tha"] = "มอญแบบไทย", ["mnx"] = "Manikion", ["mny"] = "Manyawa", ["mnz"] = "Moni", ["moa"] = "Mwan", ["moc"] = "Mocoví", ["mod"] = "Mobilian", ["moe"] = "มงตาแญ", ["mog"] = "Mongondow", ["moh"] = "Mohawk", ["moi"] = "Mboi", ["moj"] = "Monzombo", ["mok"] = "Morori", ["mom"] = "Monimbo", ["moo"] = "Monom", ["mop"] = "Mopan Maya", ["moq"] = "Mor (Papuan)", ["mor"] = "Moro", ["mos"] = "Moore", ["mot"] = "Barí", ["mou"] = "Mogum", ["mov"] = "Mojave", ["mow"] = "Moi (Congo)", ["mox"] = "Molima", ["moy"] = "Shekkacho", ["moz"] = "Mukulu", ["mpa"] = "Mpoto", ["mpb"] = "Mullukmulluk", ["mpc"] = "Mangarayi", ["mpd"] = "Machinere", ["mpe"] = "Majang", ["mpg"] = "Marba", ["mph"] = "Maung", ["mpi"] = "Mpade", ["mpj"] = "Martu Wangka", ["mpk"] = "Mbara (Chad)", ["mpl"] = "Middle Watut", ["mpm"] = "Yosondúa Mixtec", ["mpn"] = "Mindiri", ["mpo"] = "Miu", ["mpp"] = "Migabac", ["mpq"] = "Matís", ["mpr"] = "Vangunu", ["mps"] = "Dadibi", ["mpt"] = "Mian", ["mpu"] = "Makuráp", ["mpv"] = "Mungkip", ["mpw"] = "Mapidian", ["mpx"] = "Misima-Paneati", ["mpy"] = "Mapia", ["mpz"] = "Mpi", ["mqa"] = "Maba", ["mqb"] = "Mbuko", ["mqc"] = "Mangole", ["mqe"] = "Matepi", ["mqf"] = "Momuna", ["mqg"] = "Kota Bangun Kutai Malay", ["mqh"] = "Tlazoyaltepec Mixtec", ["mqi"] = "Mariri", ["mqj"] = "Mamasa", ["mqk"] = "Rajah Kabunsuwan Manobo", ["mql"] = "Mbelime", ["mqm"] = "South Marquesan", ["mqn"] = "Moronene", ["mqo"] = "Modole", ["mqp"] = "Manipa", ["mqq"] = "Minokok", ["mqr"] = "Mander", ["mqs"] = "West Makian", ["mqt"] = "Mok", ["mqu"] = "Mandari", ["mqv"] = "Mosimo", ["mqw"] = "Murupi", ["mqx"] = "Mamuju", ["mqy"] = "Manggarai", ["mqz"] = "Malasanga", ["mr"] = "มราฐี", ["mra"] = "Mlabri", ["mrb"] = "Sungwadia", ["mrc"] = "Maricopa", ["mrd"] = "Western Magar", ["mre"] = "Martha's Vineyard Sign Language", ["mrf"] = "Elseng", ["mrg"] = "Mising", ["mrh"] = "Mara Chin", ["mrj"] = "Western Mari", ["mrk"] = "Hmwaveke", ["mrl"] = "Mortlockese", ["mrm"] = "Mwerlap", ["mrn"] = "Cheke Holo", ["mro"] = "Mru", ["mrp"] = "Morouas", ["mrq"] = "North Marquesan", ["mrr"] = "Hill Maria", ["mrs"] = "Maragus", ["mrt"] = "Margi", ["mru"] = "Mono (Cameroon)", ["mrv"] = "Mangarevan", ["mrw"] = "มาราเนา", ["mrx"] = "Dineor", ["mry"] = "Karaga Mandaya", ["mrz"] = "Marind", ["ms"] = "มาเลเซีย", ["msb"] = "มัสบาเต", ["msc"] = "Sankaran Maninka", ["msd"] = "Yucatec Maya Sign Language", ["mse"] = "Musey", ["msf"] = "Mekwei", ["msg"] = "Moraid", ["msi"] = "Sabah Malay", ["msj"] = "Ma", ["msk"] = "Mansaka", ["msl"] = "Molof", ["msm"] = "Agusan Manobo", ["msn"] = "Vurës", ["mso"] = "Mombum", ["msp"] = "Maritsauá", ["msq"] = "Caac", ["msr"] = "Mongolian Sign Language", ["mss"] = "West Masela", ["msu"] = "Musom", ["msv"] = "Maslam", ["msw"] = "Mansoanka", ["msx"] = "Moresada", ["msy"] = "Aruamu", ["msz"] = "Momare", ["mt"] = "มอลตา", ["mta"] = "Cotabato Manobo", ["mtb"] = "Anyin Morofo", ["mtc"] = "Munit", ["mtd"] = "Mualang", ["mte"] = "Alu", ["mtf"] = "Murik (New Guinea)", ["mtg"] = "Una", ["mth"] = "Munggui", ["mti"] = "Maiwa (New Guinea)", ["mtj"] = "Moskona", ["mtk"] = "Mbe'", ["mtl"] = "Montol", ["mtm"] = "Mator", ["mtn"] = "Matagalpa", ["mto"] = "Totontepec Mixe", ["mtp"] = "Wichí Lhamtés Nocten", ["mtq"] = "เหมื่อง", ["mtr"] = "เมวาร์", ["mts"] = "Yora", ["mtt"] = "Mota", ["mtu"] = "Tututepec Mixtec", ["mtv"] = "Asaro'o", ["mtw"] = "Magahat", ["mtx"] = "Tidaá Mixtec", ["mty"] = "Nabi", ["mua"] = "Mundang", ["mub"] = "Mubi", ["muc"] = "Mbu'", ["mud"] = "Mednyj Aleut", ["mue"] = "Media Lengua", ["mug"] = "Musgu", ["muh"] = "Mündü", ["mui"] = "มูซี", ["muj"] = "Mabire", ["mul"] = "ร่วม", ["mum"] = "Maiwala", ["mun-pro"] = "มุนดาดั้งเดิม", ["muo"] = "Nyong", ["mup"] = "Malvi", ["muq"] = "Eastern Xiangxi Miao", ["mur"] = "Murle", ["mus"] = "Creek", ["mut"] = "Western Muria", ["muu"] = "Yaaku", ["muv"] = "Muthuvan", ["mux"] = "Bo-Ung", ["muy"] = "Muyang", ["muz"] = "Mursi", ["mva"] = "Manam", ["mvb"] = "Mattole", ["mvd"] = "Mamboru", ["mvg"] = "Yucuañe Mixtec", ["mvh"] = "Mire", ["mvi"] = "มิยาโกะ", ["mvk"] = "Mekmek", ["mvl"] = "Mbara (Australia)", ["mvm"] = "Muya", ["mvn"] = "Minaveha", ["mvo"] = "Marovo", ["mvp"] = "Duri", ["mvq"] = "Moere", ["mvr"] = "Marau", ["mvs"] = "Massep", ["mvt"] = "Mpotovoro", ["mvu"] = "Marfa", ["mvv"] = "Tagal Murut", ["mvw"] = "Machinga", ["mvx"] = "Meoswar", ["mvy"] = "Indus Kohistani", ["mvz"] = "Mesqan", ["mwa"] = "Mwatebu", ["mwb"] = "Juwal", ["mwc"] = "Are", ["mwe"] = "Mwera", ["mwf"] = "Murrinh-Patha", ["mwg"] = "Aiklep", ["mwh"] = "Mouk-Aria", ["mwi"] = "Labo", ["mwk"] = "Kita Maninkakan", ["mwl"] = "มีรังดา", ["mwm"] = "Sar", ["mwn"] = "Nyamwanga", ["mwo"] = "Sungwadaga", ["mwp"] = "Kala Lagaw Ya", ["mwq"] = "Mün Chin", ["mwr"] = "มาร์วาร์", ["mws"] = "Mwimbi-Muthambi", ["mwt"] = "Moken", ["mwu"] = "Mittu", ["mwv"] = "Mentawai", ["mww"] = "ม้งขาว", ["mwz"] = "Moingi", ["mxa"] = "Northwest Oaxaca Mixtec", ["mxb"] = "Tezoatlán Mixtec", ["mxd"] = "Modang", ["mxe"] = "Mele-Fila", ["mxf"] = "Malgbe", ["mxg"] = "Mbangala", ["mxh"] = "Mvuba", ["mxi"] = "Mozarabic", ["mxj"] = "Miju", ["mxk"] = "Monumbo", ["mxl"] = "Maxi Gbe", ["mxm"] = "Meramera", ["mxn"] = "Moi (Indonesia)", ["mxo"] = "Mbowe", ["mxp"] = "Tlahuitoltepec Mixe", ["mxq"] = "Juquila Mixe", ["mxr"] = "Murik (Malaysia)", ["mxs"] = "Huitepec Mixtec", ["mxt"] = "Jamiltepec Mixtec", ["mxu"] = "Mada (Cameroon)", ["mxv"] = "Metlatónoc Mixtec", ["mxw"] = "Namo", ["mxx"] = "Mahou", ["mxy"] = "Southeastern Nochixtlán Mixtec", ["mxz"] = "Central Masela", ["my"] = "พม่า", ["myb"] = "Mbay", ["myc"] = "Mayeka", ["mye"] = "Myene", ["myf"] = "Bambassi", ["myg"] = "Manta", ["myh"] = "Makah", ["myj"] = "Mangayat", ["myk"] = "Mamara Senoufo", ["myl"] = "Moma", ["mym"] = "Me'en", ["myn-chl"] = "Ch'olti'", ["myn-pro"] = "มายันดั้งเดิม", ["myo"] = "Anfillo", ["myp"] = "Pirahã", ["myr"] = "Muniche", ["mys"] = "Mesmes", ["myu"] = "Mundurukú", ["myv"] = "เอร์เซีย", ["myw"] = "Muyuw", ["myx"] = "Masaba", ["myy"] = "Macuna", ["myz"] = "Classical Mandaic", ["mza"] = "Santa María Zacatepec Mixtec", ["mzb"] = "Northern Saharan Berber", ["mzc"] = "Madagascar Sign Language", ["mzd"] = "Malimba", ["mze"] = "Morawa", ["mzg"] = "Monastic Sign Language", ["mzh"] = "Wichí Lhamtés Güisnay", ["mzi"] = "Ixcatlán Mazatec", ["mzj"] = "Manya", ["mzk"] = "Nigeria Mambila", ["mzl"] = "Mazatlán Mixe", ["mzm"] = "Mumuye", ["mzn"] = "มอแซนแดรอน", ["mzo"] = "Matipuhy", ["mzp"] = "Movima", ["mzq"] = "Mori Atas", ["mzr"] = "Marúbo", ["mzs"] = "ครีโอลมาเก๊า", ["mzt"] = "Mintil", ["mzu"] = "Inapang", ["mzv"] = "Manza", ["mzw"] = "Deg", ["mzx"] = "Mawayana", ["mzy"] = "Mozambican Sign Language", ["mzz"] = "Maiadomu", ["na"] = "นาอูรู", ["naa"] = "Namla", ["nab"] = "Nambikwara", ["nac"] = "Narak", ["nae"] = "Naka'ela", ["naf"] = "Nabak", ["nag"] = "Naga Pidgin", ["nah"] = "นาวัตล์", ["nai-ala"] = "Alazapa", ["nai-bay"] = "Bayogoula", ["nai-cal"] = "Calusa", ["nai-chi"] = "Chiquimulilla", ["nai-chu-pro"] = "Proto-Chumash", ["nai-cig"] = "Ciguayo", ["nai-ckn-pro"] = "Proto-Chinookan", ["nai-guz"] = "Guazacapán", ["nai-hit"] = "Hitchiti", ["nai-ipa"] = "Ipai", ["nai-jtp"] = "Jutiapa", ["nai-jum"] = "Jumaytepeque", ["nai-kat"] = "Kathlamet", ["nai-klp-pro"] = "Proto-Kalapuyan", ["nai-knm"] = "Konomihu", ["nai-kum"] = "Kumeyaay", ["nai-mac"] = "Macoris", ["nai-mdu-pro"] = "Proto-Maidun", ["nai-miz-pro"] = "Proto-Mixe-Zoque", ["nai-mus-pro"] = "Proto-Muskogean", ["nai-nao"] = "Naolan", ["nai-nrs"] = "New River Shasta", ["nai-okw"] = "Okwanuchu", ["nai-per"] = "Pericú", ["nai-pic"] = "Picuris", ["nai-plp-pro"] = "Proto-Plateau Penutian", ["nai-pom-pro"] = "Proto-Pomo", ["nai-qng"] = "Quinigua", ["nai-sca-pro"] = "Proto-Siouan-Catawban", ["nai-sin"] = "Sinacantán", ["nai-sln"] = "Salvadoran Lenca", ["nai-spt"] = "Sahaptin", ["nai-tap"] = "Tapachultec", ["nai-taw"] = "Tawasa", ["nai-teq"] = "Tequistlatec", ["nai-tip"] = "Tipai", ["nai-tot-pro"] = "Proto-Totozoquean", ["nai-tsi-pro"] = "Proto-Tsimshianic", ["nai-utn-pro"] = "Proto-Utian", ["nai-wai"] = "Waikuri", ["nai-wji"] = "Western Jicaque", ["nai-yup"] = "Yupiltepeque", ["naj"] = "Nalu", ["nak"] = "Nakanai", ["nal"] = "Nalik", ["nam"] = "Ngan'gityemerri", ["nan"] = "หมิ่นใต้", ["nan-dat"] = "Datian Min", ["nan-hbl"] = "ฮกเกี้ยน", ["nan-hlh"] = "Hailufeng Min", ["nan-lnx"] = "Longyan Min", ["nan-tws"] = "แต้จิ๋ว", ["nan-zhe"] = "Zhenan Min", ["nan-zsh"] = "Sanxiang Min", ["nao"] = "Naaba", ["nap"] = "นาโปลี", ["naq"] = "Khoekhoe", ["nar"] = "Iguta", ["nas"] = "Nasioi", ["nat"] = "Hungworo", ["naw"] = "Nawuri", ["nax"] = "Nakwi", ["nay"] = "Ngarrindjeri", ["naz"] = "Coatepec Nahuatl", ["nb"] = "นอร์เวย์แบบบุ๊กมอล", ["nba"] = "Nyemba", ["nbb"] = "Ndoe", ["nbc"] = "Chang", ["nbd"] = "Ngbinda", ["nbe"] = "Konyak Naga", ["nbg"] = "Nagarchal", ["nbh"] = "Ngamo", ["nbi"] = "Mao Naga", ["nbj"] = "Ngarinman", ["nbk"] = "Nake", ["nbm"] = "Ngbaka Ma'bo", ["nbn"] = "Kuri", ["nbo"] = "Nkukoli", ["nbp"] = "Nnam", ["nbq"] = "Nggem", ["nbr"] = "Numana", ["nbs"] = "Namibian Sign Language", ["nbt"] = "Na", ["nbu"] = "Rongmei Naga", ["nbv"] = "Ngamambo", ["nbw"] = "Southern Ngbandi", ["nby"] = "Ningera", ["nca"] = "Iyo", ["ncb"] = "นิโคบาร์กลาง", ["ncc"] = "Ponam", ["ncd"] = "Nachering", ["nce"] = "Yale", ["ncf"] = "Notsi", ["ncg"] = "Nisga'a", ["nch"] = "Central Huasteca Nahuatl", ["nci"] = "นาวัตล์คลาสสิก", ["ncj"] = "Northern Puebla Nahuatl", ["nck"] = "Nakara", ["ncl"] = "Michoacán Nahuatl", ["ncm"] = "Nambo", ["ncn"] = "Nauna", ["nco"] = "Sibe", ["ncr"] = "Ncane", ["ncs"] = "Nicaraguan Sign Language", ["nct"] = "Chothe Naga", ["ncu"] = "Chumburung", ["ncx"] = "Central Puebla Nahuatl", ["ncz"] = "Natchez", ["nd"] = "Northern Ndebele", ["nda"] = "Ndasa", ["ndb"] = "Kenswei Nsei", ["ndc"] = "Ndau", ["ndd"] = "Nde-Nsele-Nta", ["ndf"] = "Nadruvian", ["ndg"] = "Ndengereko", ["ndh"] = "Ndali", ["ndi"] = "Chamba Leko", ["ndj"] = "Ndamba", ["ndk"] = "Ndaka", ["ndl"] = "Ndolo", ["ndm"] = "Ndam", ["ndn"] = "Ngundi", ["ndp"] = "Ndo", ["ndq"] = "Ndombe", ["ndr"] = "Ndoola", ["nds"] = "เยอรมันต่ำ", ["nds-de"] = "เยอรมันต่ำแบบเยอรมนี", ["nds-nl"] = "Dutch Low Saxon", ["ndt"] = "Ndunga", ["ndu"] = "Dugun", ["ndv"] = "Ndut", ["ndw"] = "Ndobo", ["ndx"] = "Nduga", ["ndy"] = "Lutos", ["ndz"] = "Ndogo", ["ne"] = "เนปาล", ["nea"] = "Eastern Ngad'a", ["neb"] = "Toura", ["nec"] = "Nedebang", ["ned"] = "Nde-Gbite", ["nee"] = "Kumak", ["nef"] = "Nefamese", ["neg"] = "Negidal", ["neh"] = "Nyenkha", ["nej"] = "Neko", ["nek"] = "Neku", ["nem"] = "Nemi", ["nen"] = "Nengone", ["neo"] = "Ná-Meo", ["neq"] = "North Central Mixe", ["ner"] = "Yahadian", ["nes"] = "Bhoti Kinnauri", ["net"] = "Nete", ["neu"] = "Neo", ["nev"] = "Nyaheun", ["new"] = "เนวาร์", ["nex"] = "Neme", ["ney"] = "Neyo", ["nez"] = "Nez Perce", ["nfa"] = "Dhao", ["nfd"] = "Ahwai", ["nfl"] = "Äiwoo", ["nfr"] = "Nafaanra", ["nfu"] = "Mfumte", ["ng"] = "Ndonga", ["nga"] = "Ngbaka", ["ngb"] = "Northern Ngbandi", ["ngc"] = "Ngombe (Congo)", ["ngd"] = "Ngando (Central African Republic)", ["nge"] = "Ngemba", ["ngf-pro"] = "Proto-Trans-New Guinea", ["ngg"] = "Ngbaka Manza", ["ngh"] = "Nǀuu", ["ngi"] = "Ngizim", ["ngj"] = "Ngie", ["ngk"] = "Ngalkbun", ["ngl"] = "Lomwe", ["ngm"] = "Ngatik Men's Creole", ["ngn"] = "Ngwo", ["ngo"] = "Ngoni", ["ngp"] = "Ngulu", ["ngq"] = "Ngoreme", ["ngr"] = "Nagu", ["ngs"] = "Gvoko", ["ngt"] = "Ngeq", ["ngu"] = "Guerrero Nahuatl", ["ngv"] = "Nagumi", ["ngw"] = "Ngwaba", ["ngx"] = "Nggwahyi", ["ngy"] = "Tibea", ["ngz"] = "Ngungwel", ["nha"] = "Nhanda", ["nhb"] = "Beng", ["nhc"] = "Tabasco Nahuatl", ["nhd"] = "Chiripá", ["nhe"] = "Eastern Huasteca Nahuatl", ["nhf"] = "Nhuwala", ["nhg"] = "Tetelcingo Nahuatl", ["nhh"] = "Nahari", ["nhi"] = "Zacatlán-Ahuacatlán-Tepetzintla Nahuatl", ["nhk"] = "Cosoleacaque Nahuatl", ["nhm"] = "Morelos Nahuatl", ["nhn"] = "นาวัตล์ตอนกลาง", ["nho"] = "Takuu", ["nhp"] = "Pajapan Nahuatl", ["nhq"] = "Huaxcaleca Nahuatl", ["nhr"] = "Naro", ["nht"] = "Ometepec Nahuatl", ["nhu"] = "Noone", ["nhv"] = "Temascaltepec Nahuatl", ["nhw"] = "Western Huasteca Nahuatl", ["nhx"] = "Mecayapan Nahuatl", ["nhy"] = "Northern Oaxaca Nahuatl", ["nhz"] = "Santa María La Alta Nahuatl", ["nia"] = "Nias", ["nib"] = "Nakame", ["nic-bco-pro"] = "เบนูเอ-คองโกดั้งเดิม", ["nic-bod-pro"] = "แบนทอยด์ดั้งเดิม", ["nic-eov-pro"] = "Proto-Eastern Oti-Volta", ["nic-gns-pro"] = "Proto-Gurunsi", ["nic-grf-pro"] = "Proto-Grassfields", ["nic-gur-pro"] = "กูร์ดั้งเดิม", ["nic-jkn-pro"] = "Proto-Jukunoid", ["nic-lcr-pro"] = "Proto-Lower Cross River", ["nic-ogo-pro"] = "Proto-Ogoni", ["nic-ovo-pro"] = "Proto-Oti-Volta", ["nic-plt-pro"] = "Proto-Plateau", ["nic-pro"] = "ไนเจอร์-คองโกดั้งเดิม", ["nic-ubg-pro"] = "Proto-Ubangian", ["nic-ucr-pro"] = "Proto-Upper Cross River", ["nic-vco-pro"] = "วอลตา-คองโกดั้งเดิม", ["nid"] = "Ngandi", ["nie"] = "Niellim", ["nif"] = "Nek", ["nig"] = "Ngalakan", ["nih"] = "Nyiha", ["nii"] = "Nii", ["nij"] = "งาจู", ["nik"] = "Southern Nicobarese", ["nil"] = "Nila", ["nim"] = "Nilamba", ["nin"] = "Ninzo", ["nio"] = "Nganasan", ["niq"] = "Nandi", ["nir"] = "Nimboran", ["nis"] = "Nimi", ["nit"] = "Southeastern Kolami", ["niu"] = "นีวเว", ["niv"] = "Nivkh", ["niw"] = "Nimo", ["nix"] = "Hema", ["niy"] = "Ngiti", ["niz"] = "Ningil", ["nja"] = "Nzanyi", ["njb"] = "Nocte", ["njh"] = "Lotha Naga", ["nji"] = "Gudanji", ["njj"] = "Njen", ["njl"] = "Njalgulgule", ["njm"] = "Angami", ["njn"] = "Liangmai Naga", ["njo"] = "Ao", ["njr"] = "Njerep", ["njs"] = "Nisa", ["njt"] = "Ndyuka-Trio Pidgin", ["nju"] = "Ngadjunmaya", ["njx"] = "Kunyi", ["njy"] = "Njyem", ["njz"] = "Nyishi", ["nka"] = "Nkoya", ["nkb"] = "Khoibu Naga", ["nkc"] = "Nkongho", ["nkd"] = "Koireng", ["nke"] = "Duke", ["nkf"] = "Inpui Naga", ["nkg"] = "Nekgini", ["nkh"] = "Khezha Naga", ["nki"] = "Thangal Naga", ["nkj"] = "Nakai", ["nkk"] = "Nokuku", ["nkm"] = "Namat", ["nkn"] = "Nkangala", ["nko"] = "Nkonya", ["nkp"] = "Niuatoputapu", ["nkq"] = "Nkami", ["nkr"] = "Nukuoro", ["nks"] = "North Asmat", ["nkt"] = "Nyika", ["nku"] = "Bouna Kulango", ["nkw"] = "Nkutu", ["nkx"] = "Nkoroo", ["nkz"] = "Nkari", ["nl"] = "ดัตช์", ["nla"] = "Ngombale", ["nlc"] = "Nalca", ["nle"] = "East Nyala", ["nlg"] = "Gela", ["nli"] = "Grangali", ["nlj"] = "Nyali", ["nlk"] = "Ninia Yali", ["nll"] = "Nihali", ["nlm"] = "Mankiyali", ["nlo"] = "Ngul", ["nlq"] = "Lao Naga", ["nlu"] = "Nchumbulu", ["nlv"] = "Orizaba Nahuatl", ["nlw"] = "Walangama", ["nlx"] = "Nahali", ["nly"] = "Nyamal", ["nlz"] = "Nalögo", ["nma"] = "Maram Naga", ["nmb"] = "Big Nambas", ["nmc"] = "Ngam", ["nmd"] = "Ndumu", ["nme"] = "Mzieme Naga", ["nmf"] = "Tangkhul Naga", ["nmg"] = "Kwasio", ["nmh"] = "Monsang Naga", ["nmi"] = "Nyam", ["nmj"] = "Ngombe (Central African Republic)", ["nmk"] = "Namakura", ["nml"] = "Ndemli", ["nmm"] = "Manangba", ["nmn"] = "ǃXóõ", ["nmo"] = "Moyon Naga", ["nmp"] = "Nimanbur", ["nmq"] = "Nambya", ["nmr"] = "Nimbari", ["nms"] = "Letemboi", ["nmt"] = "Namonuito", ["nmu"] = "Northeast Maidu", ["nmv"] = "Ngamini", ["nmw"] = "Nimoa", ["nmy"] = "Namuyi", ["nmz"] = "Nawdm", ["nn"] = "นอร์เวย์แบบนือนอสก์", ["nna"] = "Nyangumarta", ["nnb"] = "Nande", ["nnc"] = "Nancere", ["nnd"] = "West Ambae", ["nne"] = "Ngandyera", ["nnf"] = "Ngaing", ["nng"] = "Maring Naga", ["nnh"] = "Ngiemboon", ["nni"] = "North Nuaulu", ["nnj"] = "Nyangatom", ["nnk"] = "Nankina", ["nnl"] = "Northern Rengma Naga", ["nnm"] = "Namia", ["nnn"] = "Ngete", ["nnp"] = "Wancho", ["nnq"] = "Ngindo", ["nnr"] = "Narungga", ["nnt"] = "Nanticoke", ["nnu"] = "Dwang", ["nnv"] = "Nukunu", ["nnw"] = "Southern Nuni", ["nnx"] = "Ngong", ["nny"] = "Nyangga", ["nnz"] = "Nda'nda'", ["no"] = "นอร์เวย์", ["noa"] = "Woun Meu", ["noc"] = "Nuk", ["nod"] = "คำเมือง", ["noe"] = "Nimadi", ["nof"] = "Nomane", ["nog"] = "Nogai", ["noh"] = "Nomu", ["noi"] = "Noiri", ["noj"] = "Nonuya", ["nok"] = "Nooksack", ["nol"] = "Nomlaki", ["nom"] = "Nocamán", ["non"] = "นอร์สเก่า", ["nop"] = "Numanggang", ["noq"] = "Ngongo", ["nos"] = "Eastern Nisu", ["not"] = "Nomatsiguenga", ["nou"] = "Ewage-Notu", ["nov"] = "โนเวียล", ["now"] = "Nyambo", ["noy"] = "Noy", ["noz"] = "Nayi", ["npa"] = "Nar Phu", ["npb"] = "Nupbikha", ["npg"] = "Ponyo", ["nph"] = "Phom", ["npl"] = "Southeastern Puebla Nahuatl", ["npn"] = "Mondropolon", ["npo"] = "Pochuri Naga", ["nps"] = "Nipsan", ["npu"] = "Puimei Naga", ["npy"] = "Napu", ["nqg"] = "Ede Nago", ["nqk"] = "Kura Ede Nago", ["nql"] = "Ngendelengo", ["nqm"] = "Ndom", ["nqn"] = "Nen", ["nqo"] = "N'Ko", ["nqq"] = "Kyan-Karyaw Naga", ["nqy"] = "Akyaung Ari", ["nr"] = "Southern Ndebele", ["nra"] = "Ngom", ["nrb"] = "Nara", ["nrc"] = "Noric", ["nre"] = "Southern Rengma Naga", ["nrf"] = "นอร์มัน", ["nrg"] = "Narango", ["nri"] = "Chokri Naga", ["nrk"] = "Ngarla", ["nrl"] = "Ngarluma", ["nrm"] = "Narom", ["nrn"] = "Norn", ["nrp"] = "North Picene", ["nrr"] = "Norra", ["nrt"] = "Northern Kalapuya", ["nru"] = "Narua", ["nrx"] = "Ngurmbur", ["nrz"] = "Lala (New Guinea)", ["nsa"] = "Sangtam Naga", ["nsb"] = "Lower Nossob", ["nsc"] = "Nshi", ["nsd"] = "Southern Nisu", ["nse"] = "Nsenga", ["nsg"] = "Ngasa", ["nsh"] = "Ngoshie", ["nsi"] = "Nigerian Sign Language", ["nsk"] = "Naskapi", ["nsl"] = "Norwegian Sign Language", ["nsm"] = "Sema", ["nsn"] = "Nehan", ["nso"] = "ซูทูเหนือ", ["nsp"] = "มือเนปาล", ["nsq"] = "Northern Sierra Miwok", ["nsr"] = "Maritime Sign Language", ["nss"] = "Nali", ["nst"] = "Tangsa", ["nsu"] = "Sierra Negra Nahuatl", ["nsv"] = "Southwestern Nisu", ["nsw"] = "Navut", ["nsx"] = "Nsongo", ["nsy"] = "Nasal", ["nsz"] = "Nisenan", ["ntd"] = "Northern Tidung", ["ntg"] = "Ngantangarra", ["nti"] = "Natioro", ["ntj"] = "Ngaanyatjarra", ["ntk"] = "Ikoma", ["ntm"] = "Nateni", ["nto"] = "Ntomba", ["ntp"] = "Northern Tepehuan", ["ntr"] = "Delo", ["nts"] = "Natagaimas", ["ntu"] = "Natügu", ["ntw"] = "Nottoway", ["ntx"] = "Somra", ["nty"] = "Mantsi", ["nua"] = "Yuanga", ["nub-har"] = "Haraza", ["nub-pro"] = "นูเบียนดั้งเดิม", ["nuc"] = "Nukuini", ["nud"] = "Ngala", ["nue"] = "Ngundu", ["nuf"] = "Nusu", ["nug"] = "Nungali", ["nuh"] = "Ndunda", ["nui"] = "Ngumbi", ["nuj"] = "Nyole (Uganda)", ["nuk"] = "Nootka", ["nul"] = "Nusa Laut", ["num"] = "Niuafo'ou", ["nun"] = "Anong", ["nuo"] = "หง่วน", ["nup"] = "Nupe", ["nuq"] = "Nukumanu", ["nur"] = "Nuguria", ["nus"] = "Nuer", ["nut"] = "นุง", ["nuu"] = "Ngbundu", ["nuv"] = "Northern Nuni", ["nuw"] = "Nguluwan", ["nux"] = "Mehek", ["nuy"] = "Nunggubuyu", ["nuz"] = "Tlamacazapa Nahuatl", ["nv"] = "นาวาโฮ", ["nvh"] = "Nasarian", ["nvm"] = "Namiae", ["nvo"] = "Nyokon", ["nwa"] = "Nawathinehena", ["nwb"] = "Nyabwa", ["nwc"] = "เนวาร์คลาสสิก", ["nwe"] = "Ngwe", ["nwi"] = "Southwest Tanna", ["nwm"] = "Nyamusa-Molo", ["nwo"] = "Nauo", ["nwr"] = "Nawaru", ["nwx"] = "เนวาร์กลาง", ["nwy"] = "Nottoway-Meherrin", ["nxa"] = "Nauete", ["nxd"] = "Ngando (Congo)", ["nxe"] = "Nage", ["nxg"] = "Ngadha", ["nxi"] = "Nindi", ["nxl"] = "South Nuaulu", ["nxm"] = "Numidian", ["nxn"] = "Ngawun", ["nxo"] = "Ndambomo", ["nxq"] = "Naxi", ["nxr"] = "Ninggerum", ["nxx"] = "Nafri", ["ny"] = "เจวา", ["nyb"] = "Nyangbo", ["nyc"] = "Nyanga-li", ["nyd"] = "Nyole (Kenya)", ["nye"] = "Nyengo", ["nyf"] = "Giryama", ["nyg"] = "Nyindu", ["nyh"] = "Nyigina", ["nyi"] = "Nyimang", ["nyj"] = "Nyanga (Congo)", ["nyk"] = "Nyaneka", ["nyl"] = "Nyeu", ["nym"] = "Nyamwezi", ["nyn"] = "Nyankole", ["nyo"] = "Nyoro", ["nyp"] = "Nyang'i", ["nys"] = "Nyunga", ["nyt"] = "Nyawaygi", ["nyu"] = "Nyungwe", ["nyv"] = "Nyulnyul", ["nyw"] = "ญ้อ", ["nyx"] = "Nganyaywana", ["nyy"] = "Nyakyusa", ["nza"] = "Tigon Mbembe", ["nzb"] = "Njebi", ["nzd"] = "Nzadi", ["nzi"] = "Nzima", ["nzk"] = "Nzakara", ["nzm"] = "Zeme Naga", ["nzs"] = "New Zealand Sign Language", ["nzu"] = "Central Teke", ["nzy"] = "Nzakambay", ["nzz"] = "Nanga Dama Dogon", ["oaa"] = "Orok", ["oac"] = "Oroch", ["oak"] = "Noakhali", ["oav"] = "อะวาร์เก่า", ["obi"] = "Obispeño", ["obk"] = "Southern Bontoc", ["obl"] = "Oblo", ["obm"] = "โมอับ", ["obo"] = "Obo Manobo", ["obr"] = "พม่าเก่า", ["obt"] = "เบรอตงเก่า", ["obu"] = "Obulom", ["oc"] = "อุตซิตา", ["oca"] = "Ocaina", ["och"] = "จีนเก่า", ["oco"] = "คอร์นวอลล์เก่า", ["ocu"] = "Tlahuica", ["oda"] = "Odut", ["odk"] = "Od", ["odt"] = "ดัตช์เก่า", ["odu"] = "Odual", ["ofo"] = "Ofo", ["ofs"] = "ฟรีเชียเก่า", ["ofu"] = "Efutop", ["ogb"] = "Ogbia", ["ogc"] = "Ogbah", ["oge"] = "จอร์เจียเก่า", ["ogg"] = "Ogbogolo", ["ogo"] = "Khana", ["ogu"] = "Ogbronuagum", ["ohu"] = "ฮังการีเก่า", ["oia"] = "Oirata", ["oin"] = "Inebu One", ["oj"] = "โอจิบเว", ["ojb"] = "Northwestern Ojibwa", ["ojc"] = "Central Ojibwa", ["ojg"] = "Eastern Ojibwa", ["ojp"] = "ญี่ปุ่นเก่า", ["ojs"] = "Severn Ojibwa", ["ojv"] = "Ontong Java", ["ojw"] = "Western Ojibwa", ["oka"] = "Okanagan", ["okb"] = "Okobo", ["okd"] = "Okodia", ["oke"] = "Okpe (Southwestern Edo)", ["okg"] = "Kok-Paponk", ["okh"] = "Koresh-e Rostam", ["oki"] = "Okiek", ["okj"] = "Oko-Juwoi", ["okk"] = "Kwamtim One", ["okl"] = "Old Kentish Sign Language", ["okm"] = "เกาหลีกลาง", ["okn"] = "โอกิโนเอราบุ", ["oko"] = "เกาหลีเก่า", ["okr"] = "Kirike", ["oks"] = "Oko-Eni-Osayen", ["oku"] = "Oku", ["okv"] = "Orokaiva", ["okx"] = "Okpe (Northwestern Edo)", ["okz"] = "เขมรเก่า", ["old"] = "Mochi", ["ole"] = "Olekha", ["olm"] = "Oloma", ["olo"] = "ลิววี", ["olr"] = "Olrat", ["olt"] = "ลิทัวเนียเก่า", ["olu"] = "Kuvale", ["om"] = "ออโรโม", ["oma"] = "Omaha-Ponca", ["omb"] = "Omba", ["omc"] = "Mochica", ["omg"] = "Omagua", ["omi"] = "Omi", ["omk"] = "Omok", ["oml"] = "Ombo", ["omn"] = "ไมนอส", ["omo"] = "Utarmbung", ["omp"] = "มณีปุระเก่า", ["omq-cha-pro"] = "Proto-Chatino", ["omq-maz-pro"] = "Proto-Mazatec", ["omq-mix-pro"] = "Proto-Mixtecan", ["omq-mxt-pro"] = "Proto-Mixtec", ["omq-otp-pro"] = "Proto-Oto-Pamean", ["omq-pro"] = "Proto-Oto-Manguean", ["omq-sjq"] = "San Juan Quiahije Chatino", ["omq-tel"] = "Teposcolula Mixtec", ["omq-teo"] = "Teojomulco Chatino", ["omq-tri-pro"] = "Proto-Triqui", ["omq-zap-pro"] = "Proto-Zapotecan", ["omq-zpc-pro"] = "Proto-Zapotec", ["omr"] = "มราฐีเก่า", ["omt"] = "Omotik", ["omu"] = "Omurano", ["omv-aro-pro"] = "Proto-Aroid", ["omv-diz-pro"] = "Proto-Dizoid", ["omv-pro"] = "Proto-Omotic", ["omw"] = "South Tairora", ["omx"] = "มอญเก่า", ["ona"] = "Selk'nam", ["onb"] = "เบ", ["one"] = "Oneida", ["ong"] = "Olo", ["oni"] = "Onin", ["onj"] = "Onjob", ["onk"] = "Kabore One", ["onn"] = "Onobasulu", ["ono"] = "Onondaga", ["onp"] = "Sartang", ["onr"] = "Northern One", ["ons"] = "Ono", ["ont"] = "Ontenu", ["onu"] = "Unua", ["onw"] = "นิวเบียเก่า", ["onx"] = "Pidgin Onin", ["ood"] = "O'odham", ["oog"] = "Ong", ["oon"] = "Önge", ["oor"] = "Oorlams", ["opa"] = "Okpamheri", ["opk"] = "Kopkaka", ["opm"] = "Oksapmin", ["opo"] = "Opao", ["opt"] = "Opata", ["opy"] = "Ofayé", ["or"] = "โอริยา", ["ora"] = "Oroha", ["ore"] = "Orejón", ["org"] = "Oring", ["orh"] = "Oroqen", ["oro"] = "Orokolo", ["orr"] = "Oruma", ["ort"] = "Adivasi Odia", ["oru"] = "Ormuri", ["orv"] = "สลาวิกตะวันออกเก่า", ["orw"] = "Oro Win", ["orx"] = "Oro", ["orz"] = "Ormu", ["os"] = "ออสซีเซีย", ["osa"] = "Osage", ["osc"] = "Oscan", ["osi"] = "โอซิง", ["osn"] = "Old Sundanese", ["oso"] = "Ososo", ["osp"] = "สเปนเก่า", ["ost"] = "Osatu", ["osu"] = "Southern One", ["osx"] = "แซกซันเก่า", ["ota"] = "ตุรกีแบบออตโตมัน", ["otb"] = "ทิเบตเก่า", ["otd"] = "Ot Danum", ["ote"] = "Mezquital Otomi", ["oti"] = "Oti", ["otk"] = "เตอร์กิกเก่า", ["otl"] = "Tilapa Otomi", ["otm"] = "Eastern Highland Otomi", ["otn"] = "Tenango Otomi", ["oto-otm-pro"] = "Proto-Otomi", ["oto-pro"] = "Proto-Otomian", ["otq"] = "Querétaro Otomi", ["otr"] = "Otoro", ["ots"] = "Estado de México Otomi", ["ott"] = "Temoaya Otomi", ["otu"] = "Otuke", ["otw"] = "Ottawa", ["otx"] = "Texcatepec Otomi", ["oty"] = "ทมิฬเก่า", ["otz"] = "Ixtenco Otomi", ["oub"] = "Glio-Oubi", ["oue"] = "Oune", ["oui"] = "อุยกูร์เก่า", ["oum"] = "Ouma", ["ovd"] = "แอลฟ์ดาเลิน", ["owi"] = "Owiniga", ["owl"] = "เวลส์เก่า", ["oyb"] = "Oy", ["oyd"] = "Oyda", ["oym"] = "Wayampi", ["oyy"] = "Oya'oya", ["ozm"] = "Koonzime", ["pa"] = "ปัญจาบ", ["paa-bin-pro"] = "Proto-Binanderean", ["paa-kom"] = "Kómnzo", ["paa-kwn"] = "Kuwani", ["paa-nha-pro"] = "Proto-North Halmahera", ["paa-nun"] = "Nungon", ["pab"] = "Pareci", ["pac"] = "ปาโกะห์", ["pad"] = "Paumarí", ["pae"] = "Pagibete", ["paf"] = "Paranawát", ["pag"] = "Pangasinan", ["pah"] = "Tenharim", ["pai"] = "Pe", ["pak"] = "Parakanã", ["pal"] = "เปอร์เซียกลาง", ["pam"] = "กาปัมปางัน", ["pao"] = "Northern Paiute", ["pap"] = "ปาเปียเมนตู", ["paq"] = "Parya", ["par"] = "Panamint", ["pas"] = "Papasena", ["pau"] = "ปาเลา", ["pav"] = "Wari'", ["paw"] = "Pawnee", ["pax"] = "Pankararé", ["pay"] = "Pech", ["paz"] = "Pankararú", ["pbb"] = "Páez", ["pbc"] = "Patamona", ["pbe"] = "Mezontla Popoloca", ["pbf"] = "Coyotepec Popoloca", ["pbg"] = "Paraujano", ["pbh"] = "Panare", ["pbi"] = "Podoko", ["pbl"] = "Mak (Nigeria)", ["pbm"] = "Puebla Mazatec", ["pbn"] = "Kpasam", ["pbo"] = "Papel", ["pbp"] = "Badyara", ["pbr"] = "Pangwa", ["pbs"] = "Central Pame", ["pbv"] = "ปนัร", ["pby"] = "Pyu (New Guinea)", ["pca"] = "Santa Inés Ahuatempan Popoloca", ["pcb"] = "ปอร์", ["pcc"] = "ปู้อี", ["pcd"] = "ปีการ์", ["pce"] = "Ruching Palaung", ["pcf"] = "Paliyan", ["pcg"] = "Paniya", ["pch"] = "Pardhan", ["pci"] = "Duruwa", ["pcj"] = "Parenga", ["pck"] = "Paite", ["pcl"] = "Pardhi", ["pcm"] = "Nigerian Pidgin", ["pcn"] = "Piti", ["pcp"] = "Pacahuara", ["pcw"] = "Pyapun", ["pda"] = "Anam", ["pdc"] = "เยอรมันแบบเพนซิลเวเนีย", ["pdi"] = "Pa Di", ["pdn"] = "Fedan", ["pdo"] = "Padoe", ["pdt"] = "เพลาท์ดิทช์", ["pdu"] = "กะยัน", ["pea"] = "Peranakan Indonesian", ["peb"] = "Eastern Pomo", ["ped"] = "Mala (New Guinea)", ["pee"] = "Taje", ["pef"] = "Northeastern Pomo", ["peg"] = "Pengo", ["peh"] = "Bonan", ["pei"] = "Chichimeca-Jonaz", ["pej"] = "Northern Pomo", ["pek"] = "Penchal", ["pel"] = "Pekal", ["pem"] = "Phende", ["peo"] = "เปอร์เซียเก่า", ["pep"] = "Kunja", ["peq"] = "Southern Pomo", ["pev"] = "Pémono", ["pex"] = "Petats", ["pey"] = "Petjo", ["pez"] = "Eastern Penan", ["pfa"] = "Pááfang", ["pfe"] = "Peere", ["pga"] = "Juba Arabic", ["pgd"] = "คานธาระ", ["pgg"] = "ปังควาฬฺ", ["pgi"] = "Pagi", ["pgk"] = "Rerep", ["pgl"] = "Primitive Irish", ["pgn"] = "Paelignian", ["pgs"] = "Pangseng", ["pgu"] = "Pagu", ["pgz"] = "Papua New Guinean Sign Language", ["pha"] = "Pa-Hng", ["phd"] = "Phudagi", ["phg"] = "Phuong", ["phh"] = "Phukha", ["phi-din"] = "Dinapigue Agta", ["phi-kal-pro"] = "คาลาเมียนดั้งเดิม", ["phi-nag"] = "Nagtipunan Agta", ["phi-pro"] = "ฟิลิปปินส์ดั้งเดิม", ["phk"] = "พ่าเก", ["phl"] = "Palula", ["phm"] = "Phimbi", ["phn"] = "ฟินิเชีย", ["pho"] = "ผู้น้อย", ["phq"] = "Phana'", ["phr"] = "Pahari-Potwari", ["pht"] = "ผู้ไท", ["phu"] = "พวน", ["phv"] = "Pahlavani", ["phw"] = "Phangduwali", ["pi"] = "บาลี", ["pia"] = "Pima Bajo", ["pib"] = "Yine", ["pic"] = "Pinji", ["pid"] = "Piaroa", ["pie"] = "Piro", ["pif"] = "Pingelapese", ["pig"] = "Pisabo", ["pih"] = "Pitcairn-Norfolk", ["pii"] = "Pini", ["pij"] = "Pijao", ["pil"] = "Yom", ["pim"] = "Powhatan", ["pin"] = "Piame", ["pio"] = "Piapoco", ["pip"] = "Pero", ["pir"] = "Piratapuyo", ["pis"] = "Pijin", ["pit"] = "Pitta-Pitta", ["piu"] = "Pintupi-Luritja", ["piv"] = "Pileni", ["piw"] = "Pimbwe", ["pix"] = "Piu", ["piy"] = "Piya-Kwonci", ["piz"] = "Pije", ["pjt"] = "Pitjantjatjara", ["pkb"] = "Kipfokomo", ["pkc"] = "แพ็กเจ", ["pkg"] = "Pak-Tong", ["pkh"] = "Pankhu", ["pkn"] = "Pakanha", ["pko"] = "Pökoot", ["pkp"] = "ปูกาปูกา", ["pkr"] = "Attapady Kurumba", ["pks"] = "Pakistan Sign Language", ["pkt"] = "Maleng", ["pku"] = "Paku", ["pl"] = "โปแลนด์", ["pla"] = "Miani", ["plb"] = "Polonombauk", ["plc"] = "ปาลาวาโนตอนกลาง", ["ple"] = "Palu'e", ["plg"] = "Pilagá", ["plh"] = "Paulohi", ["plj"] = "Polci", ["plk"] = "Kohistani Shina", ["pll"] = "Shwe Palaung", ["pln"] = "Palenquero", ["plo"] = "Oluta Popoluca", ["plq"] = "Palaic", ["plr"] = "Palaka Senoufo", ["pls"] = "San Marcos Tlalcoyalco Popoloca", ["plu"] = "Palikur", ["plv"] = "ปาลาวาโนตะวันตกเฉียงใต้", ["plw"] = "ปาลาวาโนแบบบรูกส์พอยต์", ["ply"] = "Bolyu", ["plz"] = "Paluan", ["pma"] = "Paamese", ["pmb"] = "Pambia", ["pmd"] = "Pallanganmiddang", ["pme"] = "Pwaamèi", ["pmf"] = "Pamona", ["pmi"] = "Northern Pumi", ["pmj"] = "Southern Pumi", ["pmk"] = "Pamlico", ["pml"] = "Sabir", ["pmm"] = "Pol", ["pmn"] = "Pam", ["pmo"] = "Pom", ["pmq"] = "Northern Pame", ["pmr"] = "Paynamar", ["pms"] = "ปีเยมอนเต", ["pmt"] = "Tuamotuan", ["pmu"] = "Mirpur Panjabi", ["pmw"] = "Plains Miwok", ["pmx"] = "Poumei Naga", ["pmy"] = "Papuan Malay", ["pmz"] = "Southern Pame", ["pna"] = "Punan Bah-Biau", ["pnc"] = "Pannei", ["pnd"] = "Mpinda", ["pne"] = "Western Penan", ["png"] = "Pongu", ["pnh"] = "Penrhyn", ["pni"] = "Aoheng", ["pnj"] = "Pinjarup", ["pnk"] = "Paunaka", ["pnl"] = "Paleni", ["pnm"] = "Punan Batu", ["pnn"] = "Pinai-Hagahai", ["pno"] = "Panobo", ["pnp"] = "Pancana", ["pnq"] = "Pana (West Africa)", ["pnr"] = "Panim", ["pns"] = "Ponosakan", ["pnt"] = "Pontic Greek", ["pnu"] = "Jiongnai Bunu", ["pnv"] = "Pinigura", ["pnw"] = "Panyjima", ["pnx"] = "Phong-Kniang", ["pny"] = "Pinyin", ["pnz"] = "Pana (Central Africa)", ["poc"] = "Poqomam", ["poe"] = "San Juan Atzingo Popoloca", ["pof"] = "Poke", ["pog"] = "Potiguára", ["poh"] = "Poqomchi'", ["poi"] = "Highland Popoluca", ["pok"] = "Pokangá", ["pom"] = "Southeastern Pomo", ["pon"] = "Pohnpeian", ["poo"] = "Central Pomo", ["pop"] = "Pwapwâ", ["poq"] = "Texistepec Popoluca", ["pos"] = "Sayula Popoluca", ["pot"] = "Potawatomi", ["pov"] = "ครีโอลกินี-บิสเซา", ["pow"] = "San Felipe Otlaltepec Popoloca", ["pox"] = "Polabian", ["poy"] = "Pogolo", ["poz-abi"] = "Abai", ["poz-bal"] = "Baliledo", ["poz-btk-pro"] = "Proto-Bungku-Tolaki", ["poz-cet-pro"] = "มาลาโย-พอลินีเชียนตอนกลาง-ตะวันออกดั้งเดิม", ["poz-hce-pro"] = "Proto-Halmahera-Cenderawasih", ["poz-lgx-pro"] = "ลัมปุงกิกดั้งเดิม", ["poz-mcm-pro"] = "มาลาโย-จามิกดั้งเดิม", ["poz-mic-pro"] = "ไมโครนีเซียนดั้งเดิม", ["poz-mly-pro"] = "มาเลย์อิกดั้งเดิม", ["poz-msa-pro"] = "มาลาโย-ซุมบาวันดั้งเดิม", ["poz-oce-pro"] = "โอเชียนิกดั้งเดิม", ["poz-pep-pro"] = "พอลินีเชียนตะวันออกดั้งเดิม", ["poz-pnp-pro"] = "นิวเคลียร์พอลินีเชียนดั้งเดิม", ["poz-pol-pro"] = "พอลินีเชียนดั้งเดิม", ["poz-pro"] = "มาลาโย-พอลินีเชียนดั้งเดิม", ["poz-sml"] = "Sarawak Malay", ["poz-ssw-pro"] = "ซูลาเวซีใต้ดั้งเดิม", ["poz-swa-pro"] = "ซาราวักเหนือดั้งเดิม", ["poz-ter"] = "มลายูแบบตรังกานู", ["ppa"] = "Pao", ["ppe"] = "Papi", ["ppi"] = "Paipai", ["ppk"] = "Uma", ["ppl"] = "ปีปิล", ["ppm"] = "Papuma", ["ppn"] = "Papapana", ["ppo"] = "Folopa", ["ppq"] = "Pei", ["pps"] = "San Luís Temalacayuca Popoloca", ["ppt"] = "Pa", ["ppu"] = "Papora", ["pqa"] = "Pa'a", ["pqe-pro"] = "มาลาโย-พอลินีเชียนตะวันออกดั้งเดิม", ["pqm"] = "Malecite-Passamaquoddy", ["pra"] = "ปรากฤต", ["pra-niy"] = "Niya Prakrit", ["prc"] = "Parachi", ["pre"] = "Principense", ["prf"] = "Paranan", ["prg"] = "Old Prussian", ["prh"] = "Porohanon", ["pri"] = "Paicî", ["prk"] = "Parauk", ["prl"] = "Peruvian Sign Language", ["prm"] = "Kibiri", ["prn"] = "Prasuni", ["pro"] = "อุตซิตาเก่า", ["prq"] = "Ashéninka Perené", ["prr"] = "Puri", ["prt"] = "Phai", ["pru"] = "Puragi", ["prw"] = "Parawen", ["prx"] = "Purik", ["prz"] = "Providencia Sign Language", ["ps"] = "ปาทาน", ["psa"] = "Asue Awyu", ["psc"] = "Persian Sign Language", ["psd"] = "Plains Indian Sign Language", ["pse"] = "มลายูตอนกลาง", ["psg"] = "Penang Sign Language", ["psh"] = "Southwest Pashayi", ["psi"] = "Southeast Pashayi", ["psl"] = "Puerto Rican Sign Language", ["psm"] = "Pauserna", ["psn"] = "Panasuan", ["pso"] = "Polish Sign Language", ["psp"] = "Philippine Sign Language", ["psq"] = "Pasi", ["psr"] = "Portuguese Sign Language", ["pss"] = "Kaulong", ["psw"] = "Port Sandwich", ["psy"] = "Piscataway", ["pt"] = "โปรตุเกส", ["pta"] = "Pai Tavytera", ["pth"] = "Pataxó Hã-Ha-Hãe", ["pti"] = "Pintiini", ["ptn"] = "Patani", ["pto"] = "Zo'é", ["ptp"] = "Patep", ["ptq"] = "Pattapu", ["ptr"] = "Piamatsina", ["ptt"] = "Enrekang", ["ptu"] = "Bambam", ["ptv"] = "Port Vato", ["ptw"] = "Pentlatch", ["pty"] = "Pathiya", ["pua"] = "Purepecha", ["pub"] = "Purum", ["puc"] = "Punan Merap", ["pud"] = "Punan Aput", ["pue"] = "Puelche", ["puf"] = "Punan Merah", ["pug"] = "Phuie", ["pui"] = "Puinave", ["puj"] = "Punan Tubu", ["pum"] = "Puma", ["puo"] = "Puoc", ["pup"] = "Pulabu", ["puq"] = "Puquina", ["pur"] = "Puruborá", ["put"] = "Putoh", ["puu"] = "Punu", ["puw"] = "Puluwat", ["pux"] = "Puare", ["puy"] = "Purisimeño", ["pwa"] = "Pawaia", ["pwb"] = "Panawa", ["pwg"] = "Gapapaiwa", ["pwi"] = "Patwin", ["pwm"] = "Molbog", ["pwn"] = "ไปวัน", ["pwo"] = "กะเหรี่ยงโปตะวันตก", ["pwr"] = "Powari", ["pww"] = "กะเหรี่ยงโปเหนือ", ["pxm"] = "Quetzaltepec Mixe", ["pye"] = "Pye Krumen", ["pym"] = "Fyam", ["pyn"] = "Poyanáwa", ["pys"] = "Paraguayan Sign Language", ["pyu"] = "Puyuma", ["pyx"] = "ปยู", ["pyy"] = "Pyen", ["pzh"] = "Pazeh", ["pzn"] = "Para Naga", ["qfa-adm-pro"] = "เกรตอันดามันนีสดั้งเดิม", ["qfa-bet-pro"] = "เบ-ไทดั้งเดิม", ["qfa-cka-pro"] = "Proto-Chukotko-Kamchatkan", ["qfa-hur-pro"] = "Proto-Hurro-Urartian", ["qfa-kad-pro"] = "Proto-Kadu", ["qfa-kms-pro"] = "Proto-Kam-Sui", ["qfa-kor-pro"] = "เกาหลีดั้งเดิม", ["qfa-kra-pro"] = "ขร้าดั้งเดิม", ["qfa-lic-pro"] = "ไหลดั้งเดิม", ["qfa-onb-pro"] = "เบดั้งเดิม", ["qfa-ong-pro"] = "Proto-Ongan", ["qfa-tak-pro"] = "ขร้า-ไทดั้งเดิม", ["qfa-yen-pro"] = "Proto-Yeniseian", ["qfa-yuk-pro"] = "Proto-Yukaghir", ["qu"] = "เกชัว", ["qua"] = "Quapaw", ["quc"] = "K'iche'", ["qui"] = "Quileute", ["qum"] = "Sipakapense", ["qun"] = "Quinault", ["quq"] = "Quinqui", ["quv"] = "Sacapulteco", ["qvy"] = "Queyu", ["qwc"] = "เกชัวคลาสสิก", ["qwe-kch"] = "Kichwa", ["qwe-pro"] = "เกชวนดั้งเดิม", ["qwm"] = "คูมาน", ["qwt"] = "Kwalhioqua-Tlatskanai", ["qxs"] = "Southern Qiang", ["qya"] = "เควนยา", ["qyp"] = "Quiripi", ["raa"] = "Dungmali", ["rab"] = "Chamling", ["rac"] = "Rasawa", ["rad"] = "ระแด", ["raf"] = "Western Meohang", ["rag"] = "Logooli", ["rah"] = "Rabha", ["rai"] = "Ramoaaina", ["rak"] = "Tulu-Bohuai", ["ral"] = "Ralte", ["ram"] = "Canela", ["ran"] = "Riantana", ["rao"] = "Rao", ["rap"] = "ราปานูอี", ["raq"] = "Saam", ["rar"] = "ราโรโตงา", ["ras"] = "Tegali", ["rat"] = "Razajerdi", ["rau"] = "Raute", ["rav"] = "Sampang", ["raw"] = "เรอหวั่ง", ["rax"] = "Rang", ["ray"] = "Rapa", ["raz"] = "Rahambuu", ["rbb"] = "Rumai Palaung", ["rbk"] = "Northern Bontoc", ["rbl"] = "Miraya Bikol", ["rcf"] = "Réunion Creole French", ["rdb"] = "Rudbari", ["rea"] = "Rerau", ["reb"] = "Rembong", ["ree"] = "Rejang Kayan", ["reg"] = "Kara (Tanzania)", ["rei"] = "Reli", ["rej"] = "Rejang", ["rel"] = "Rendille", ["rem"] = "Remo", ["ren"] = "Rengao", ["rer"] = "Rer Bare", ["res"] = "Reshe", ["ret"] = "Retta", ["rey"] = "Reyesano", ["rga"] = "Roria", ["rge"] = "Romano-Greek", ["rgk"] = "Rangkas", ["rgn"] = "โรมัญญา", ["rgr"] = "Resígaro", ["rgs"] = "Southern Roglai", ["rgu"] = "Ringgou", ["rhg"] = "โรฮีนจา", ["rhp"] = "Yahang", ["ria"] = "Reang", ["rif"] = "ริฟ", ["ril"] = "Riang", ["rim"] = "Nyaturu", ["rin"] = "Nungu", ["rir"] = "Ribun", ["rit"] = "Ritarungo", ["riu"] = "Riung", ["rjg"] = "Rajong", ["rji"] = "Raji", ["rjs"] = "ราชพังสี", ["rka"] = "Kraol", ["rkb"] = "Rikbaktsa", ["rkh"] = "Rakahanga-Manihiki", ["rki"] = "ยะไข่", ["rkm"] = "Marka", ["rkt"] = "Kamta", ["rkw"] = "Arakwal", ["rm"] = "รูมันช์", ["rma"] = "Rama", ["rmb"] = "Rembarunga", ["rmc"] = "Carpathian Romani", ["rmd"] = "Traveller Danish", ["rme"] = "Angloromani", ["rmf"] = "Kalo Finnish Romani", ["rmg"] = "Traveller Norwegian", ["rmh"] = "Murkim", ["rmi"] = "Lomavren", ["rmk"] = "Romkun", ["rml"] = "Baltic Romani", ["rmm"] = "Roma", ["rmn"] = "Balkan Romani", ["rmo"] = "Sinte Romani", ["rmp"] = "Rempi", ["rmq"] = "Caló", ["rms"] = "Romanian Sign Language", ["rmt"] = "Domari", ["rmu"] = "Tavringer Romani", ["rmv"] = "Romanova", ["rmw"] = "Welsh Romani", ["rmx"] = "Romam", ["rmy"] = "Vlax Romani", ["rmz"] = "Marma", ["rnd"] = "Ruwund", ["rng"] = "Ronga", ["rnl"] = "Ranglong", ["rnn"] = "Roon", ["rnp"] = "Rongpo", ["rnw"] = "Rungwa", ["ro"] = "โรมาเนีย", ["roa-ang"] = "Angevin", ["roa-bbn"] = "บูร์บอแน-แบรีชง", ["roa-brg"] = "บูร์กีญง", ["roa-can"] = "Cantabrian", ["roa-cha"] = "ช็องปาญ", ["roa-fcm"] = "ฟร็องช์-กงเต", ["roa-gal"] = "Gallo", ["roa-gib"] = "Gallo-Italic of Basilicata", ["roa-gis"] = "Gallo-Italic of Sicily", ["roa-leo"] = "เลออน", ["roa-lor"] = "Lorrain", ["roa-oca"] = "กาตาลาเก่า", ["roa-ole"] = "เลออนเก่า", ["roa-ona"] = "นาวาร์โร-อารากอนเก่า", ["roa-opt"] = "กาลิเซีย-โปรตุเกสเก่า", ["roa-orl"] = "Orléanais", ["roa-poi"] = "ปัวเตอแว็ง-แซ็งต็องแฌ", ["roa-tar"] = "Tarantino", ["rob"] = "Tae'", ["roc"] = "Cacgia Roglai", ["rod"] = "Rogo", ["roe"] = "Ronji", ["rof"] = "Rombo", ["rog"] = "Northern Roglai", ["rol"] = "Romblomanon", ["rom"] = "โรมานี", ["roo"] = "Rotokas", ["rop"] = "Australian Kriol", ["ror"] = "Rongga", ["rou"] = "Runga", ["row"] = "Dela-Oenale", ["rpn"] = "Repanbitip", ["rpt"] = "Rapting", ["rri"] = "Ririo", ["rro"] = "Roro", ["rrt"] = "Arritinngithigh", ["rsb"] = "Romano-Serbian", ["rsk"] = "รูซินแบบพันโนเนีย", ["rsl"] = "มือรัสเซีย", ["rsm"] = "Miriwoong Sign Language", ["rsn"] = "Rwandan Sign Language", ["rtc"] = "Rungtu", ["rth"] = "Ratahan", ["rtm"] = "Rotuman", ["rtw"] = "Rathawi", ["ru"] = "รัสเซีย", ["rub"] = "Gungu", ["ruc"] = "Ruuli", ["rue"] = "รูซินแบบคาร์พาเทีย", ["ruf"] = "Luguru", ["rug"] = "Roviana", ["ruh"] = "Ruga", ["rui"] = "Rufiji", ["ruk"] = "Che", ["ruo"] = "อิสโตร-โรมาเนีย", ["rup"] = "Aromanian", ["ruq"] = "Megleno-Romanian", ["rut"] = "Rutul", ["ruu"] = "Lanas Lobu", ["ruy"] = "Mala (Nigeria)", ["ruz"] = "Ruma", ["rw"] = "รวันดา-รุนดี", ["rwa"] = "Rawo", ["rwk"] = "Rwa", ["rwm"] = "Amba", ["rwo"] = "Rawa", ["rxd"] = "Ngardi", ["rxw"] = "Karuwali", ["ryn"] = "อามามิโอชิมะเหนือ", ["rys"] = "ยาเอยามะ", ["ryu"] = "โอกินาวะ", ["rzh"] = "Razihi", ["sa"] = "สันสกฤต", ["saa"] = "Saba", ["sab"] = "Buglere", ["sac"] = "Fox", ["sad"] = "Sandawe", ["sae"] = "Sabanê", ["saf"] = "Safaliba", ["sah"] = "ซาคา", ["sai-all"] = "Allentiac", ["sai-and"] = "Andoquero", ["sai-ayo"] = "Ayomán", ["sai-bae"] = "Baenan", ["sai-bag"] = "Bagua", ["sai-bet"] = "Betoi", ["sai-bor-pro"] = "Proto-Boran", ["sai-cac"] = "Cacán", ["sai-caq"] = "Caranqui", ["sai-car-pro"] = "Proto-Cariban", ["sai-cat"] = "Catacao", ["sai-cer-pro"] = "Proto-Cerrado", ["sai-chi"] = "Chirino", ["sai-chn"] = "Chaná", ["sai-chp"] = "Chapacura", ["sai-chr"] = "Charrua", ["sai-chu"] = "Churuya", ["sai-cje-pro"] = "Proto-Central Jê", ["sai-cmg"] = "Comechingon", ["sai-cno"] = "Chono", ["sai-cnr"] = "Cañari", ["sai-coe"] = "Coeruna", ["sai-col"] = "Colán", ["sai-cop"] = "Copallén", ["sai-crd"] = "Coroado Puri", ["sai-ctq"] = "Catuquinaru", ["sai-cul"] = "Culli", ["sai-cva"] = "Cueva", ["sai-esm"] = "Esmeralda", ["sai-ewa"] = "Ewarhuyana", ["sai-gam"] = "Gamela", ["sai-gay"] = "Gayón", ["sai-gmo"] = "Guamo", ["sai-gua"] = "Guachí", ["sai-gue"] = "Güenoa", ["sai-hau"] = "Haush", ["sai-jee-pro"] = "Proto-Jê", ["sai-jko"] = "Jeikó", ["sai-jrj"] = "Jirajara", ["sai-kat"] = "Katembri", ["sai-mal"] = "Malalí", ["sai-mar"] = "Maratino", ["sai-mat"] = "Matanawi", ["sai-mcn"] = "Mocana", ["sai-men"] = "Menien", ["sai-mil"] = "Millcayac", ["sai-mlb"] = "Malibu", ["sai-msk"] = "Masakará", ["sai-muc"] = "Mucuchí", ["sai-mue"] = "Muellama", ["sai-muz"] = "Muzo", ["sai-mys"] = "Maynas", ["sai-nat"] = "Natú", ["sai-nje-pro"] = "Proto-Northern Jê", ["sai-opo"] = "Opón", ["sai-oto"] = "Otomaco", ["sai-pal"] = "Palta", ["sai-pam"] = "Pamigua", ["sai-par"] = "Paratió", ["sai-peb"] = "Peba", ["sai-pnz"] = "Panzaleo", ["sai-prh"] = "Puruhá", ["sai-ptg"] = "Patagón", ["sai-pur"] = "Purukotó", ["sai-pyg"] = "Payaguá", ["sai-pyk"] = "Pykobjê", ["sai-qmb"] = "Quimbaya", ["sai-qtm"] = "Quitemo", ["sai-rab"] = "Rabona", ["sai-ram"] = "Ramanos", ["sai-sac"] = "Sácata", ["sai-san"] = "Sanaviron", ["sai-sap"] = "Sapará", ["sai-sec"] = "Sechura", ["sai-sin"] = "Sinúfana", ["sai-sje-pro"] = "Proto-Southern Jê", ["sai-tab"] = "Tabancale", ["sai-tal"] = "Tallán", ["sai-tap"] = "Tapayuna", ["sai-tar-pro"] = "Proto-Taranoan", ["sai-teu"] = "Teushen", ["sai-tim"] = "Timote", ["sai-tpr"] = "Taparita", ["sai-trr"] = "Tarairiú", ["sai-wai"] = "Waitaká", ["sai-way"] = "Wayumara", ["sai-wit-pro"] = "Proto-Witotoan", ["sai-wnm"] = "Wanham", ["sai-xoc"] = "Xocó", ["sai-yao"] = "Yao (South America)", ["sai-yar"] = "Yarumá", ["sai-yri"] = "Yuri", ["sai-yup"] = "Yupua", ["sai-yur"] = "Yurumanguí", ["saj"] = "Sahu", ["sak"] = "Sake", ["sal-pro"] = "Proto-Salish", ["sam"] = "Samaritan Aramaic", ["sao"] = "Sause", ["saq"] = "Samburu", ["sar"] = "Saraveca", ["sas"] = "Sasak", ["sat"] = "สันถาลี", ["sau"] = "Saleman", ["sav"] = "Saafi-Saafi", ["saw"] = "Sawi", ["sax"] = "Sa", ["say"] = "Saya", ["saz"] = "Saurashtra", ["sba"] = "Ngambay", ["sbb"] = "Simbo", ["sbc"] = "Gele'", ["sbd"] = "Southern Samo", ["sbe"] = "Saliba (New Guinea)", ["sbf"] = "Shabo", ["sbg"] = "Seget", ["sbh"] = "Sori-Harengan", ["sbi"] = "Seti", ["sbj"] = "Surbakhal", ["sbk"] = "Safwa", ["sbl"] = "Botolan Sambal", ["sbm"] = "Sagala", ["sbn"] = "Sindhi Bhil", ["sbo"] = "Sabüm", ["sbp"] = "Sangu (Tanzania)", ["sbq"] = "Sileibi", ["sbr"] = "Sembakung Murut", ["sbs"] = "Subiya", ["sbt"] = "Kimki", ["sbu"] = "Stod Bhoti", ["sbv"] = "Sabine", ["sbw"] = "Simba", ["sbx"] = "Seberuang", ["sby"] = "Soli", ["sbz"] = "Sara Kaba", ["sc"] = "ซาร์ดิเนีย", ["scb"] = "Chut", ["sce"] = "ตงเซียง", ["scf"] = "San Miguel Creole French", ["scg"] = "Sanggau", ["sch"] = "Sakachep", ["sci"] = "Sri Lankan Creole Malay", ["sck"] = "Sadri", ["scl"] = "Shina", ["scn"] = "ซิซิลี", ["sco"] = "สกอต", ["scp"] = "Yolmo", ["scq"] = "สะโอจ", ["scs"] = "North Slavey", ["scu"] = "Shumcho", ["scv"] = "Sheni", ["scw"] = "Sha", ["scx"] = "Sicel", ["scz"] = "Shetland", ["sd"] = "สินธ์", ["sda"] = "Toraja-Sa'dan", ["sdb"] = "Shabak", ["sdc"] = "ซัสซารี", ["sde"] = "Surubu", ["sdf"] = "Sarli", ["sdg"] = "Savi", ["sdh"] = "Southern Kurdish", ["sdj"] = "Suundi", ["sdk"] = "Sos Kundi", ["sdl"] = "Saudi Arabian Sign Language", ["sdm"] = "Semandang", ["sdn"] = "Gallurese", ["sdo"] = "Bukar-Sadung Bidayuh", ["sdp"] = "Sherdukpen", ["sdr"] = "Oraon Sadri", ["sds"] = "เบอร์เบอร์แบบตูนีเซีย", ["sdu"] = "Sarudu", ["sdv-daj-pro"] = "Proto-Daju", ["sdv-eje-pro"] = "Proto-Eastern Jebel", ["sdv-nil-pro"] = "Proto-Nilotic", ["sdv-nyi-pro"] = "Proto-Nyima", ["sdv-tmn-pro"] = "Proto-Taman", ["sdx"] = "Sibu Melanau", ["se"] = "ซามีเหนือ", ["sea"] = "เซอไม", ["sec"] = "Sechelt", ["sed"] = "Sedang", ["see"] = "Seneca", ["sef"] = "Cebaara Senoufo", ["seg"] = "Segeju", ["seh"] = "Sena", ["sei"] = "Seri", ["sej"] = "Sene", ["sek"] = "Sekani", ["sel-nor"] = "Northern Selkup", ["sel-pro"] = "Proto-Selkup", ["sel-sou"] = "Southern Selkup", ["sem-amm"] = "อัมโมน", ["sem-amo"] = "Amorite", ["sem-cha"] = "Chaha", ["sem-dad"] = "Dadanitic", ["sem-dum"] = "Dumaitic", ["sem-has"] = "Hasaitic", ["sem-his"] = "Hismaic", ["sem-mhr"] = "Muher", ["sem-pro"] = "เซมิติกดั้งเดิม", ["sem-saf"] = "Safaitic", ["sem-sam"] = "Samalian", ["sem-srb"] = "อาหรับใต้เก่า", ["sem-tay"] = "Taymanitic", ["sem-tha"] = "Thamudic", ["sem-wes-pro"] = "เซมิติกตะวันตกดั้งเดิม", ["sen"] = "Nanerigé Sénoufo", ["seo"] = "Suarmin", ["sep"] = "Sìcìté Sénoufo", ["seq"] = "Senara Sénoufo", ["ser"] = "Serrano", ["ses"] = "Koyraboro Senni", ["set"] = "Sentani", ["seu"] = "Serui-Laut", ["sev"] = "Nyarafolo Senoufo", ["sew"] = "Sewa Bay", ["sey"] = "Secoya", ["sez"] = "Senthang Chin", ["sfb"] = "French Belgian Sign Language", ["sfe"] = "Eastern Subanun", ["sfm"] = "Small Flowery Miao", ["sfs"] = "South African Sign Language", ["sfw"] = "Sehwi", ["sg"] = "ซังโก", ["sga"] = "ไอริชเก่า", ["sgb"] = "Mag-Anchi Ayta", ["sgc"] = "Kipsigis", ["sgd"] = "ซูรีเกาโนน", ["sge"] = "Segai", ["sgg"] = "Swiss-German Sign Language", ["sgh"] = "Shughni", ["sgi"] = "Suga", ["sgk"] = "Sangkong", ["sgm"] = "Singa", ["sgp"] = "Singpho", ["sgr"] = "Sangisari", ["sgs"] = "Samogitian", ["sgt"] = "Brokpake", ["sgu"] = "Salas", ["sgw"] = "Sebat Bet Gurage", ["sgx"] = "Sierra Leone Sign Language", ["sgy"] = "Sanglechi", ["sgz"] = "Sursurunga", ["sh"] = "เซอร์โบ-โครเอเชีย", ["sha"] = "Shall-Zwall", ["shb"] = "Ninam", ["shc"] = "Sonde", ["shd"] = "Kundal Shahi", ["she"] = "Sheko", ["shg"] = "Shua", ["shh"] = "Shoshone", ["shi"] = "Tashelhit", ["shj"] = "Shatt", ["shk"] = "Shilluk", ["shl"] = "Shendu", ["shm"] = "Shahrudi", ["shn"] = "ไทใหญ่", ["sho"] = "Shanga", ["shp"] = "Shipibo-Conibo", ["shq"] = "Sala", ["shr"] = "Shi", ["shs"] = "Shuswap", ["sht"] = "Shasta", ["shu"] = "Chadian Arabic", ["shv"] = "Shehri", ["shw"] = "Shwai", ["shx"] = "She", ["shy"] = "Tachawit", ["shz"] = "Syenara Senoufo", ["si"] = "สิงหล", ["sia"] = "Akkala Sami", ["sib"] = "Sebop", ["sid"] = "ซีดามา", ["sie"] = "Simaa", ["sif"] = "Siamou", ["sig"] = "Paasaal", ["sih"] = "Sîshëë", ["sii"] = "Shom Peng", ["sij"] = "Numbami", ["sik"] = "Sikiana", ["sil"] = "Tumulung Sisaala", ["sim"] = "Seim", ["sio-pro"] = "Proto-Siouan", ["sip"] = "สิกขิม", ["siq"] = "Sonia", ["sir"] = "Siri", ["sis"] = "Siuslaw", ["sit-aao-pro"] = "Proto-Central Naga", ["sit-bai-pro"] = "Proto-Bai", ["sit-ban"] = "Bangru", ["sit-bdi-pro"] = "Proto-Bodish", ["sit-bok"] = "Bokar", ["sit-cai"] = "Caijia", ["sit-cha"] = "Chairel", ["sit-ers-pro"] = "Proto-Ersuic", ["sit-hrs-pro"] = "Proto-Hrusish", ["sit-jap"] = "Japhug", ["sit-kha-pro"] = "Proto-Kham", ["sit-khb-pro"] = "Proto-Kho-Bwa", ["sit-khp-pro"] = "Proto-Puroik", ["sit-khw-pro"] = "Proto-Western Kho-Bwa", ["sit-kon-pro"] = "Proto-Northern Naga", ["sit-liz"] = "Lizu", ["sit-lnj"] = "Longjia", ["sit-lrn"] = "Luren", ["sit-luu-pro"] = "ลูอิชดั้งเดิม", ["sit-nas-pro"] = "Proto-Naish", ["sit-prn"] = "Puiron", ["sit-pro"] = "ซิโน-ทิเบตันดั้งเดิม", ["sit-sit"] = "Situ", ["sit-tam-pro"] = "Proto-Tamangic", ["sit-tan-pro"] = "Proto-Tani", ["sit-tgm"] = "ตางัม", ["sit-tng-pro"] = "Proto-Tangkhulic", ["sit-tos"] = "Tosu", ["sit-tsh"] = "Tshobdun", ["sit-zbu"] = "Zbu", ["siu"] = "Sinagen", ["siv"] = "Sumariup", ["siw"] = "Siwai", ["six"] = "Sumau", ["siy"] = "Sivandi", ["siz"] = "Siwi", ["sja"] = "Epena", ["sjb"] = "Sajau Basap", ["sjc"] = "Shaojiang Min", ["sjd"] = "ซามีแบบกิลดิน", ["sje"] = "ซามีแบบปีเต", ["sjg"] = "Assangori", ["sjk"] = "Kemi Sami", ["sjl"] = "Miji", ["sjm"] = "Mapun", ["sjn"] = "ซินดาริน", ["sjo"] = "Xibe", ["sjp"] = "Surjapuri", ["sjr"] = "Siar-Lak", ["sjs"] = "Senhaja de Srair", ["sjt"] = "Ter Sami", ["sju"] = "Ume Sami", ["sjw"] = "Shawnee", ["sk"] = "สโลวัก", ["ska"] = "Skagit", ["skb"] = "แสก", ["skc"] = "Ma Manda", ["skd"] = "Southern Sierra Miwok", ["ske"] = "Ske", ["skf"] = "Mekéns", ["skh"] = "Sikule", ["ski"] = "Sika", ["skj"] = "Seke", ["skk"] = "Sok", ["skm"] = "Sakam", ["skn"] = "Kolibugan Subanon", ["sko"] = "Seko Tengah", ["skp"] = "Sekapan", ["skq"] = "Sininkere", ["skr"] = "Saraiki", ["sks"] = "ไมยา", ["skt"] = "Sakata", ["sku"] = "Sakao", ["skv"] = "Skou", ["skw"] = "Skepi Creole Dutch", ["skx"] = "Seko Padang", ["sky"] = "ซีกายานา", ["skz"] = "Sekar", ["sl"] = "สโลวีเนีย", ["sla-pro"] = "สลาวิกดั้งเดิม", ["slc"] = "Saliba (Colombia)", ["sld"] = "Sisaala", ["sle"] = "Sholaga", ["slf"] = "Swiss-Italian Sign Language", ["slg"] = "Selungai Murut", ["slh"] = "Southern Puget Sound Salish", ["slj"] = "Salumá", ["sll"] = "Salt-Yui", ["slm"] = "ซามาแบบปางูตารัน", ["sln"] = "Salinan", ["slp"] = "Lamaholot", ["slr"] = "ซาลาร์", ["sls"] = "Singapore Sign Language", ["slt"] = "Sila", ["slu"] = "Selaru", ["slw"] = "Sialum", ["slx"] = "Salampasu", ["sly"] = "Selayar", ["slz"] = "Ma'ya", ["sm"] = "ซามัว", ["sma"] = "ซามีใต้", ["smb"] = "Simbari", ["smc"] = "Som", ["smd"] = "Sama", ["smf"] = "Auwe", ["smg"] = "Simbali", ["smh"] = "Samei", ["smi-pro"] = "ซามิกดั้งเดิม", ["smj"] = "ซามีแบบลูเล", ["smk"] = "โบลีเนา", ["sml"] = "ซามาตอนกลาง", ["smm"] = "Musasa", ["smn"] = "ซามีแบบอีนารี", ["smp"] = "Samaritan Hebrew", ["smq"] = "Samo", ["smr"] = "Simeulue", ["sms"] = "Skolt Sami", ["smt"] = "Simte", ["smu"] = "สมราย", ["smv"] = "Samvedi", ["smw"] = "Sumbawa", ["smx"] = "Samba", ["smy"] = "Semnani", ["smz"] = "Simeku", ["sn"] = "โชนา", ["snb"] = "Sebuyau", ["snc"] = "Sinaugoro", ["sne"] = "Bau Bidayuh", ["snf"] = "Noon", ["sng"] = "Sanga (Congo)", ["sni"] = "Sensi", ["snj"] = "Riverain Sango", ["snk"] = "Soninke", ["snl"] = "Sangil", ["snm"] = "Southern Ma'di", ["snn"] = "Siona", ["sno"] = "Snohomish", ["snp"] = "Siane", ["snq"] = "Sangu (Gabon)", ["snr"] = "Sihan", ["sns"] = "Nahavaq", ["snu"] = "Senggi", ["snv"] = "Sa'ban", ["snw"] = "Selee", ["snx"] = "Sam", ["sny"] = "Saniyo-Hiyewe", ["snz"] = "Kou", ["so"] = "โซมาลี", ["soa"] = "โซ่ง", ["sob"] = "Sobei", ["soc"] = "Soko", ["sod"] = "Songoora", ["soe"] = "Songomeno", ["sog"] = "Sogdian", ["soh"] = "Aka (Sudan)", ["soi"] = "Sonha", ["sok"] = "Sokoro", ["sol"] = "Solos", ["son-pro"] = "Proto-Songhay", ["soo"] = "Nsong", ["sop"] = "Songe", ["soq"] = "Kanasi", ["sor"] = "Somrai", ["sos"] = "Seenku", ["sou"] = "ปักษ์ใต้", ["sov"] = "Sonsorolese", ["sow"] = "Sowanda", ["sox"] = "Swo", ["soy"] = "Miyobe", ["soz"] = "Temi", ["spb"] = "Sepa (Indonesia)", ["spc"] = "Sapé", ["spd"] = "Saep", ["spe"] = "Sepa (New Guinea)", ["spg"] = "Sian", ["spi"] = "Saponi", ["spk"] = "Sengo", ["spl"] = "Selepet", ["spm"] = "Sepen", ["spn"] = "Sanapaná", ["spo"] = "Spokane", ["spp"] = "Supyire", ["spr"] = "Saparua", ["sps"] = "Saposa", ["spt"] = "Spiti Bhoti", ["spu"] = "Sapuan", ["spv"] = "Sambalpuri", ["spx"] = "South Picene", ["spy"] = "Sabaot", ["sq"] = "แอลเบเนีย", ["sqa"] = "Shama-Sambuga", ["sqh"] = "Shau", ["sqj-pro"] = "แอลเบเนียนดั้งเดิม", ["sqk"] = "Albanian Sign Language", ["sqm"] = "Suma", ["sqn"] = "Susquehannock", ["sqo"] = "Sorkhei", ["sqq"] = "Sou", ["sqr"] = "Siculo-Arabic", ["sqs"] = "Sri Lankan Sign Language", ["sqt"] = "Soqotri", ["squ"] = "Squamish", ["sra"] = "Saruga", ["srb"] = "Sora", ["sre"] = "Sara", ["srf"] = "Nafi", ["srg"] = "Sulod", ["srh"] = "Sarikoli", ["sri"] = "Siriano", ["srk"] = "Serudung Murut", ["srl"] = "Isirawa", ["srm"] = "ซารามักกา", ["srn"] = "ซรานันโตโง", ["srq"] = "Sirionó", ["srr"] = "Serer", ["srs"] = "Tsuut'ina", ["srt"] = "Sauri", ["sru"] = "Suruí", ["srv"] = "Waray Sorsogon", ["srw"] = "Serua", ["srx"] = "Sirmauri", ["sry"] = "Sera", ["srz"] = "Shahmirzadi", ["ss"] = "สวาซี", ["ssa-klk-pro"] = "Proto-Kuliak", ["ssa-kom-pro"] = "Proto-Koman", ["ssa-pro"] = "Proto-Nilo-Saharan", ["ssb"] = "Southern Sama", ["ssc"] = "Suba-Simbiti", ["ssd"] = "Siroi", ["sse"] = "Balangingi", ["ssf"] = "Thao", ["ssg"] = "Seimat", ["ssh"] = "Shihhi Arabic", ["ssi"] = "Sansi", ["ssj"] = "Sausi", ["ssk"] = "Sunam", ["ssl"] = "Western Sisaala", ["ssm"] = "Semnam", ["sso"] = "Sissano", ["ssp"] = "Spanish Sign Language", ["ssq"] = "So'a", ["ssr"] = "Swiss-French Sign Language", ["sss"] = "Sô", ["sst"] = "Sinasina", ["ssu"] = "Susuami", ["ssv"] = "Shark Bay", ["ssx"] = "Samberigi", ["ssy"] = "ซาโฮ", ["ssz"] = "Sengseng", ["st"] = "ซูทู", ["stb"] = "Northern Subanen", ["std"] = "Sentinelese", ["ste"] = "Liana-Seti", ["stf"] = "Seta", ["stg"] = "Trieng", ["sth"] = "Shelta", ["sti"] = "Bulo Stieng", ["stj"] = "Matya Samo", ["stk"] = "Arammba", ["stm"] = "Setaman", ["stn"] = "Owa", ["sto"] = "Stoney", ["stp"] = "Southeastern Tepehuan", ["stq"] = "ฟรีเชียแบบซาเทอร์ลันท์", ["str"] = "Saanich", ["sts"] = "Shumashti", ["stt"] = "Budeh Stieng", ["stu"] = "Samtao", ["stv"] = "Silt'e", ["stw"] = "Satawalese", ["sty"] = "Siberian Tatar", ["su"] = "ซุนดา", ["sua"] = "Sulka", ["sub"] = "Suku", ["suc"] = "Western Subanon", ["sue"] = "Suena", ["sug"] = "Suganga", ["sui"] = "Suki", ["suk"] = "Sukuma", ["suq"] = "Suri", ["sur"] = "Mwaghavul", ["sus"] = "Susu", ["sut"] = "Subtiaba", ["suv"] = "Puroik", ["suw"] = "Sumbwa", ["sux"] = "ซูเมอร์", ["suy"] = "Suyá", ["suz"] = "Sunwar", ["sv"] = "สวีเดน", ["sva"] = "Svan", ["svb"] = "Ulau-Suain", ["svc"] = "Vincentian Creole English", ["sve"] = "Serili", ["svk"] = "Slovakian Sign Language", ["svm"] = "Slavomolisano", ["svs"] = "Savosavo", ["svx"] = "Skalvian", ["sw"] = "สวาฮีลี", ["swb"] = "Maore Comorian", ["swf"] = "Sere", ["swg"] = "Swabian", ["swi"] = "สุ่ย", ["swj"] = "Sira", ["swl"] = "Swedish Sign Language", ["swm"] = "Samosa", ["swn"] = "Sokna", ["swo"] = "Shanenawa", ["swp"] = "Suau", ["swq"] = "Sharwa", ["swr"] = "Saweru", ["sws"] = "Seluwasan", ["swt"] = "Sawila", ["swu"] = "Suwawa", ["sww"] = "Sowa", ["swx"] = "Suruahá", ["swy"] = "Sarua", ["sxb"] = "Suba", ["sxc"] = "Sicanian", ["sxe"] = "Sighu", ["sxg"] = "Shixing", ["sxk"] = "Southern Kalapuya", ["sxl"] = "Selonian", ["sxm"] = "ซำเร", ["sxn"] = "Sangir", ["sxo"] = "Sorothaptic", ["sxr"] = "Saaroa", ["sxs"] = "Sasaru", ["sxw"] = "Saxwe Gbe", ["sya"] = "Siang", ["syb"] = "Central Subanen", ["syc"] = "ซีรีแอกคลาสสิก", ["syd-pro"] = "Proto-Samoyedic", ["syi"] = "Seki", ["syk"] = "Sukur", ["syl"] = "สิเลฏ", ["sym"] = "Maya Samo", ["syn"] = "Senaya", ["syo"] = "Suoy", ["sys"] = "Sinyar", ["syw"] = "Kagate", ["syx"] = "Osamayi", ["syy"] = "Al-Sayyid Bedouin Sign Language", ["sza"] = "Semelai", ["szb"] = "Ngalum", ["szc"] = "Semaq Beri", ["szd"] = "Seru", ["sze"] = "Seze", ["szg"] = "Sengele", ["szl"] = "ไซลีเซีย", ["szn"] = "Sula", ["szp"] = "Suabo", ["szv"] = "Isubu", ["szw"] = "Sawai", ["szy"] = "ซากีซายา", ["ta"] = "ทมิฬ", ["taa"] = "Lower Tanana", ["tab"] = "ทาบาซารัน", ["tac"] = "Lowland Tarahumara", ["tad"] = "Tause", ["tae"] = "Tariana", ["taf"] = "Tapirapé", ["tag"] = "Tagoi", ["tai-pro"] = "ไทดั้งเดิม", ["tai-swe-pro"] = "ไทตะวันตกเฉียงใต้ดั้งเดิม", ["taj"] = "Eastern Tamang", ["tak"] = "Tala", ["tal"] = "Tal", ["tan"] = "Tangale", ["tao"] = "ยามี", ["tap"] = "Taabwa", ["tar"] = "Central Tarahumara", ["tas"] = "Tây Bồi", ["tau"] = "Upper Tanana", ["tav"] = "Tatuyo", ["taw"] = "Tai", ["tax"] = "Tamki", ["tay"] = "Atayal", ["taz"] = "Tocho", ["tba"] = "Aikanã", ["tbb"] = "Tapeba", ["tbc"] = "Takia", ["tbd"] = "Kaki Ae", ["tbe"] = "Tanimbili", ["tbf"] = "Mandara", ["tbg"] = "North Tairora", ["tbh"] = "Thurawal", ["tbi"] = "Gaam", ["tbj"] = "Tiang", ["tbk"] = "Calamian Tagbanwa", ["tbl"] = "ตโบลี", ["tbm"] = "Tagbu", ["tbn"] = "Barro Negro Tunebo", ["tbo"] = "Tawala", ["tbp"] = "Taworta", ["tbq-bdg-pro"] = "โบโด-กาโรดั้งเดิม", ["tbq-blg"] = "Bailang", ["tbq-brm-pro"] = "Proto-Burmish", ["tbq-gkh"] = "Gokhy", ["tbq-kuk-pro"] = "Proto-Kuki-Chin", ["tbq-lal-pro"] = "Proto-Lalo", ["tbq-laz"] = "Laze", ["tbq-lob-pro"] = "โลโล-เบอร์มีซดั้งเดิม", ["tbq-lol-pro"] = "โลโลอิชดั้งเดิม", ["tbq-mil"] = "Milang", ["tbq-mor"] = "Moran", ["tbq-ngo"] = "Ngochang", ["tbr"] = "Tumtum", ["tbs"] = "Tanguat", ["tbt"] = "Kitembo", ["tbu"] = "Tubar", ["tbv"] = "Tobo", ["tbw"] = "Aborlan Tagbanwa", ["tbx"] = "Kapin", ["tby"] = "Tabaru", ["tbz"] = "Ditammari", ["tca"] = "Ticuna", ["tcb"] = "Tanacross", ["tcc"] = "Datooga", ["tcd"] = "Tafi", ["tce"] = "Southern Tutchone", ["tcf"] = "Malinaltepec Tlapanec", ["tcg"] = "Tamagario", ["tch"] = "Turks and Caicos Creole English", ["tci"] = "Wára", ["tck"] = "Tchitchege", ["tcl"] = "Taman (Myanmar)", ["tcm"] = "Tanahmerah", ["tco"] = "Taungyo", ["tcp"] = "Tawr Chin", ["tcq"] = "Kaiy", ["tcs"] = "ครีโอลช่องแคบทอร์เรส", ["tct"] = "T'en", ["tcu"] = "Southeastern Tarahumara", ["tcw"] = "Tecpatlán Totonac", ["tcx"] = "โตทา", ["tcy"] = "ตูลู", ["tcz"] = "Thado Chin", ["tda"] = "Tagdal", ["tdb"] = "Panchpargania", ["tdc"] = "Emberá-Tadó", ["tdd"] = "ไทใต้คง", ["tde"] = "Tiranige Diga Dogon", ["tdf"] = "Talieng", ["tdg"] = "Western Tamang", ["tdh"] = "Thulung", ["tdi"] = "Tomadino", ["tdj"] = "Tajio", ["tdk"] = "Tambas", ["tdl"] = "Sur", ["tdm"] = "Taruma", ["tdn"] = "Tondano", ["tdo"] = "Teme", ["tdq"] = "Tita", ["tdr"] = "Todrah", ["tds"] = "Doutai", ["tdt"] = "Tetun Dili", ["tdu"] = "Tempasuk Dusun", ["tdv"] = "Toro", ["tdy"] = "Tadyawan", ["te"] = "เตลูกู", ["tea"] = "เตอเมียร์", ["teb"] = "Tetete", ["tec"] = "Terik", ["ted"] = "Tepo Krumen", ["tee"] = "Huehuetla Tepehua", ["tef"] = "Teressa", ["teg"] = "Teke-Tege", ["teh"] = "Tehuelche", ["tei"] = "Torricelli", ["tek"] = "Ibali Teke", ["tem"] = "Temne", ["ten"] = "Tama (Colombia)", ["teo"] = "Ateso", ["tep"] = "Tepecano", ["teq"] = "Temein", ["ter"] = "Tereno", ["tes"] = "Tengger", ["tet"] = "เตตุน", ["teu"] = "Soo", ["tev"] = "Teor", ["tew"] = "Tewa", ["tex"] = "Tennet", ["tey"] = "Tulishi", ["tez"] = "Tetserret", ["tfi"] = "Tofin Gbe", ["tfn"] = "Dena'ina", ["tfo"] = "Tefaro", ["tfr"] = "Teribe", ["tft"] = "เตอร์นาเต", ["tg"] = "ทาจิก", ["tga"] = "Sagalla", ["tgb"] = "Tobilung", ["tgc"] = "Tigak", ["tgd"] = "Ciwogai", ["tge"] = "Eastern Gorkha Tamang", ["tgf"] = "Chali", ["tgh"] = "Tobagonian Creole English", ["tgi"] = "Lawunuia", ["tgn"] = "ตันดากาโนน", ["tgo"] = "Sudest", ["tgp"] = "Tangoa", ["tgq"] = "Tring", ["tgr"] = "Tareng", ["tgs"] = "Nume", ["tgt"] = "Central Tagbanwa", ["tgu"] = "Tanggu", ["tgv"] = "Tingui-Boto", ["tgw"] = "Tagwana Senoufo", ["tgx"] = "Tagish", ["tgy"] = "Togoyo", ["th"] = "ไทย", ["thc"] = "Tai Hang Tong", ["thd"] = "Kuuk Thaayorre", ["the"] = "Chitwania Tharu", ["thf"] = "Thangmi", ["thh"] = "Northern Tarahumara", ["thi"] = "Tai Long", ["thk"] = "Tharaka", ["thl"] = "Dangaura Tharu", ["thm"] = "ทะวืง", ["thn"] = "Thachanadan", ["thp"] = "Thompson", ["thq"] = "Kochila Tharu", ["thr"] = "Rana Tharu", ["ths"] = "Thakali", ["tht"] = "Tahltan", ["thu"] = "Thuri", ["thy"] = "Tha", ["ti"] = "ทือกรึญญา", ["tic"] = "Tira", ["tif"] = "Tifal", ["tig"] = "ทือเกร", ["tih"] = "Timugon Murut", ["tii"] = "Tiene", ["tij"] = "Tilung", ["tik"] = "Tikar", ["til"] = "Tillamook", ["tim"] = "Timbe", ["tin"] = "Tindi", ["tio"] = "Teop", ["tip"] = "Trimuris", ["tiq"] = "Tiéfo", ["tis"] = "Masadiit Itneg", ["tit"] = "Tinigua", ["tiu"] = "Adasen", ["tiv"] = "Tiv", ["tiw"] = "Tiwi", ["tix"] = "Southern Tiwa", ["tiy"] = "ตีรูไร", ["tiz"] = "Tai Hongjin", ["tja"] = "Tajuasohn", ["tjg"] = "Tunjung", ["tji"] = "Northern Tujia", ["tjl"] = "ไทแหล่ง", ["tjm"] = "Timucua", ["tjn"] = "Tonjon", ["tjs"] = "Southern Tujia", ["tju"] = "Tjurruru", ["tjw"] = "Chaap Wuurong", ["tk"] = "เติร์กเมน", ["tka"] = "Truká", ["tkb"] = "Buksa", ["tkd"] = "Tukudede", ["tke"] = "Takwane", ["tkf"] = "Tukumanféd", ["tkl"] = "Tokelauan", ["tkm"] = "Takelma", ["tkn"] = "โทกูโนชิมะ", ["tkp"] = "Tikopia", ["tkq"] = "Tee", ["tkr"] = "Tsakhur", ["tks"] = "Ramandi", ["tkt"] = "Kathoriya Tharu", ["tku"] = "Upper Necaxa Totonac", ["tkv"] = "Mur Pano", ["tkw"] = "Teanu", ["tkx"] = "Tangko", ["tkz"] = "Takua", ["tl"] = "ตากาล็อก", ["tla"] = "Southwestern Tepehuan", ["tlb"] = "Tobelo", ["tlc"] = "Misantla Totonac", ["tld"] = "Talaud", ["tlf"] = "Telefol", ["tlg"] = "Tofanma", ["tlh"] = "Klingon", ["tli"] = "Tlingit", ["tlj"] = "Talinga-Bwisi", ["tlk"] = "Taloki", ["tll"] = "Tetela", ["tlm"] = "Tolomako", ["tln"] = "Talondo'", ["tlo"] = "Talodi", ["tlp"] = "Filomena Mata-Coahuitlán Totonac", ["tlq"] = "Tai Loi", ["tlr"] = "Talise", ["tls"] = "Tambotalo", ["tlt"] = "Teluti", ["tlu"] = "Tulehu", ["tlv"] = "Taliabu", ["tlx"] = "Khehek", ["tly"] = "Talysh", ["tma"] = "Tama (Chad)", ["tmb"] = "Avava", ["tmc"] = "Tumak", ["tmd"] = "Haruai", ["tme"] = "Tremembé", ["tmf"] = "Toba-Maskoy", ["tmg"] = "Ternateño", ["tmh"] = "Tuareg", ["tmi"] = "Tutuba", ["tmj"] = "Samarokena", ["tmk"] = "Northwestern Tamang", ["tml"] = "Tamnim Citak", ["tmm"] = "Tai Thanh", ["tmn"] = "Taman (Indonesia)", ["tmo"] = "Temoq", ["tmq"] = "Tumleo", ["tms"] = "Tima", ["tmt"] = "Tasmate", ["tmu"] = "Iau", ["tmv"] = "Motembo", ["tmy"] = "Tami", ["tmz"] = "Tamanaku", ["tn"] = "สวานา", ["tna"] = "Tacana", ["tnb"] = "Western Tunebo", ["tnc"] = "Tanimuca-Retuarã", ["tnd"] = "Angosturas Tunebo", ["tne"] = "Tinoc Kallahan", ["tng"] = "Tobanga", ["tnh"] = "Maiani", ["tni"] = "Tandia", ["tnk"] = "Kwamera", ["tnl"] = "Lenakel", ["tnm"] = "Tabla", ["tnn"] = "North Tanna", ["tno"] = "Toromono", ["tnp"] = "Whitesands", ["tnq"] = "ตาอีโน", ["tnr"] = "Bedik", ["tns"] = "Tenis", ["tnt"] = "Tontemboan", ["tnu"] = "Tay Khang", ["tnv"] = "Tanchangya", ["tnw"] = "Tonsawang", ["tnx"] = "Tanema", ["tny"] = "Tongwe", ["tnz"] = "Ten'edn", ["to"] = "ตองงา", ["tob"] = "Toba", ["toc"] = "Coyutla Totonac", ["tod"] = "Toma", ["tof"] = "Gizrra", ["tog"] = "Tonga (Malawi)", ["toh"] = "Tonga (Mozambique)", ["toi"] = "Tonga (Zambia)", ["toj"] = "Tojolabal", ["tok"] = "Toki Pona", ["tol"] = "Tolowa", ["tom"] = "Tombulu", ["too"] = "Xicotepec de Juárez Totonac", ["top"] = "Papantla Totonac", ["toq"] = "Toposa", ["tor"] = "Togbo-Vara Banda", ["tos"] = "Highland Totonac", ["tou"] = "Tho", ["tov"] = "Upper Taromi", ["tow"] = "Jemez", ["tox"] = "Tobian", ["toy"] = "Topoiyo", ["toz"] = "To", ["tpa"] = "Taupota", ["tpc"] = "Azoyú Me'phaa", ["tpe"] = "Tippera", ["tpf"] = "Tarpia", ["tpg"] = "Kula", ["tpi"] = "ตอกปีซิน", ["tpj"] = "Tapieté", ["tpk"] = "Tupinikin", ["tpl"] = "Tlacoapa Me'phaa", ["tpm"] = "Tampulma", ["tpn"] = "Tupinambá", ["tpo"] = "Tai Pao", ["tpp"] = "Pisaflores Tepehua", ["tpq"] = "Tukpa", ["tpr"] = "Tuparí", ["tpt"] = "Tlachichilco Tepehua", ["tpu"] = "Tampuan", ["tpv"] = "Tanapag", ["tpw"] = "ตูปีเก่า", ["tpx"] = "Acatepec Me'phaa", ["tpy"] = "Trumai", ["tpz"] = "Tinputz", ["tqb"] = "Tembé", ["tql"] = "Lehali", ["tqm"] = "Turumsa", ["tqn"] = "Tenino", ["tqo"] = "Toaripi", ["tqp"] = "Tomoip", ["tqq"] = "Tunni", ["tqr"] = "Torona", ["tqt"] = "Western Totonac", ["tqu"] = "Touo", ["tqw"] = "Tonkawa", ["tr"] = "ตุรกี", ["tra"] = "Tirahi", ["trb"] = "Terebu", ["trc"] = "Copala Triqui", ["trd"] = "Turi", ["tre"] = "East Tarangan", ["trf"] = "Trinidadian Creole English", ["trg"] = "Lishán Didán", ["trh"] = "Turaka", ["tri"] = "Trió", ["trj"] = "Toram", ["trk-dkh"] = "Dukhan", ["trk-eog"] = "Early Old Oghuz", ["trk-oat"] = "ตุรกีแบบอานาโตเลียเก่า", ["trk-pro"] = "เตอร์กิกดั้งเดิม", ["trl"] = "Traveller Scottish", ["trm"] = "Tregami", ["trn"] = "Trinitario", ["tro"] = "Tarao", ["trp"] = "กอกบอรอก", ["trq"] = "San Martín Itunyoso Triqui", ["trr"] = "Taushiro", ["trs"] = "Chicahuaxtla Triqui", ["trt"] = "Tunggare", ["tru"] = "Turoyo", ["trv"] = "Taroko", ["trw"] = "Torwali", ["trx"] = "Tringgus", ["try"] = "ตุรุง", ["trz"] = "Torá", ["ts"] = "Tsonga", ["tsa"] = "Tsaangi", ["tsb"] = "Tsamai", ["tsc"] = "Tswa", ["tsd"] = "Tsakonian", ["tse"] = "Tunisian Sign Language", ["tsf"] = "Southwestern Tamang", ["tsg"] = "ซูก", ["tsh"] = "Tsuvan", ["tsi"] = "Tsimshian", ["tsj"] = "Tshangla", ["tsl"] = "Ts'ün-Lao", ["tsm"] = "Turkish Sign Language", ["tsp"] = "Northern Toussian", ["tsq"] = "มือไทย", ["tsr"] = "Akei", ["tss"] = "Taiwan Sign Language", ["tsu"] = "โจว", ["tsv"] = "Tsogo", ["tsw"] = "Tsishingini", ["tsx"] = "Mubami", ["tsy"] = "Tebul Sign Language", ["tt"] = "ตาตาร์", ["tta"] = "Tutelo", ["ttb"] = "Gaa", ["ttc"] = "Tektiteko", ["ttd"] = "Tauade", ["tte"] = "Bwanabwana", ["ttf"] = "Tuotomb", ["ttg"] = "Tutong", ["tth"] = "Upper Ta'oih", ["tti"] = "Tobati", ["ttj"] = "Tooro", ["ttk"] = "Totoro", ["ttl"] = "Totela", ["ttm"] = "Northern Tutchone", ["ttn"] = "Towei", ["tto"] = "Lower Ta'oih", ["ttp"] = "Tombelala", ["ttr"] = "Tera", ["tts"] = "อีสาน", ["ttt"] = "Tat", ["ttu"] = "Torau", ["ttv"] = "Titan", ["ttw"] = "Long Wat", ["tty"] = "Sikaritai", ["ttz"] = "Tsum", ["tua"] = "Wiarumus", ["tub"] = "Tübatulabal", ["tuc"] = "Mutu", ["tud"] = "Tuxá", ["tue"] = "Tuyuca", ["tuf"] = "Central Tunebo", ["tug"] = "Tunia", ["tuh"] = "Taulil", ["tui"] = "Tupuri", ["tuj"] = "Tugutil", ["tul"] = "Tula", ["tum"] = "Tumbuka", ["tun"] = "Tunica", ["tuo"] = "Tucano", ["tup-gua-pro"] = "ตูปี-กัวรานีดั้งเดิม", ["tup-kab"] = "Kabishiana", ["tup-pro"] = "ตูเปียนดั้งเดิม", ["tuq"] = "Tedaga", ["tus"] = "Tuscarora", ["tuu"] = "Tututni", ["tuv"] = "Turkana", ["tuw-alk"] = "Alchuka", ["tuw-bal"] = "Bala", ["tuw-kkl"] = "Kyakala", ["tuw-kli"] = "Kili", ["tuw-pro"] = "Proto-Tungusic", ["tuw-sol"] = "Solon", ["tux"] = "Tuxináwa", ["tuy"] = "Tugen", ["tuz"] = "Turka", ["tva"] = "Vaghua", ["tvd"] = "Tsuvadi", ["tve"] = "Te'un", ["tvk"] = "Southeast Ambrym", ["tvl"] = "ตูวาลู", ["tvm"] = "Tela-Masbuar", ["tvn"] = "Tavoyan", ["tvo"] = "ตีโดเร", ["tvs"] = "Taveta", ["tvt"] = "Tutsa Naga", ["tvu"] = "Tunen", ["tvw"] = "Sedoa", ["tvx"] = "Taivoan", ["tvy"] = "Timor Pidgin", ["twa"] = "Twana", ["twb"] = "Western Tawbuid", ["twc"] = "Teshenawa", ["twe"] = "Teiwa", ["twf"] = "เทาส์", ["twg"] = "Tereweng", ["twh"] = "ไทขาว", ["twm"] = "Tawang Monpa", ["twn"] = "Twendi", ["two"] = "Tswapong", ["twp"] = "Ere", ["twq"] = "Tasawaq", ["twr"] = "Southwestern Tarahumara", ["twt"] = "Turiwára", ["twu"] = "Termanu", ["tww"] = "Tuwari", ["twy"] = "Tawoyan", ["txa"] = "Tombonuo", ["txb"] = "โทแคเรียนบี", ["txc"] = "Tsetsaut", ["txe"] = "Totoli", ["txg"] = "ตังกุต", ["txh"] = "Thracian", ["txi"] = "Ikpeng", ["txj"] = "Tarjumo", ["txm"] = "Tomini", ["txn"] = "West Tarangan", ["txo"] = "Toto", ["txq"] = "Tii", ["txr"] = "Tartessian", ["txs"] = "Tonsea", ["txt"] = "Citak", ["txu"] = "Kayapó", ["txx"] = "Tatana", ["ty"] = "ตาฮีตี", ["tya"] = "Tauya", ["tye"] = "Kyenga", ["tyh"] = "O'du", ["tyi"] = "Teke-Tsaayi", ["tyj"] = "ไทแมน", ["tyl"] = "Thu Lao", ["tyn"] = "Kombai", ["typ"] = "Kuku-Thaypan", ["tyr"] = "ไทแดง", ["tys"] = "ซาปา", ["tyt"] = "Tày Tac", ["tyu"] = "Kua", ["tyv"] = "ตูวา", ["tyx"] = "Teke-Tyee", ["tyz"] = "ตั่ย", ["tza"] = "Tanzanian Sign Language", ["tzh"] = "Tzeltal", ["tzj"] = "Tz'utujil", ["tzl"] = "Talossan", ["tzm"] = "Central Atlas Tamazight", ["tzn"] = "Tugun", ["tzo"] = "โซตซิล", ["tzx"] = "Tabriak", ["uam"] = "Uamué", ["uan"] = "Kuan", ["uar"] = "Tairuma", ["uba"] = "Ubang", ["ubi"] = "Ubi", ["ubl"] = "Buhi'non Bikol", ["ubr"] = "Ubir", ["ubu"] = "Umbu-Ungu", ["uby"] = "Ubykh", ["uda"] = "Uda", ["ude"] = "Udihe", ["udg"] = "Muduga", ["udi"] = "อูดี", ["udj"] = "Ujir", ["udl"] = "Uldeme", ["udm"] = "อุดมูร์ต", ["udu"] = "Uduk", ["ues"] = "Kioko", ["ufi"] = "Ufim", ["ug"] = "อุยกูร์", ["uga"] = "ยูการิต", ["ugb"] = "Kuku-Ugbanh", ["uge"] = "Ughele", ["ugn"] = "Ugandan Sign Language", ["ugo"] = "กฺ๋อง", ["ugy"] = "Uruguayan Sign Language", ["uha"] = "Uhami", ["uhn"] = "Damal", ["uis"] = "Uisai", ["uiv"] = "Iyive", ["uji"] = "Tanjijili", ["uk"] = "ยูเครน", ["uka"] = "Kaburi", ["ukg"] = "Ukuriguma", ["ukh"] = "Ukhwejo", ["ukk"] = "Muak Sa-aak", ["ukl"] = "Ukrainian Sign Language", ["ukp"] = "Ukpe-Bayobiri", ["ukq"] = "Ukwa", ["uks"] = "Kaapor Sign Language", ["uku"] = "Ukue", ["ukw"] = "Ukwuani-Aboh-Ndoni", ["uky"] = "Kuuk Yak", ["ula"] = "Fungwa", ["ulb"] = "Olukumi", ["ulc"] = "Ulch", ["ule"] = "Lule", ["ulf"] = "Afra", ["uli"] = "Ulithian", ["ulk"] = "Meriam", ["ull"] = "Ullatan", ["ulm"] = "Ulumanda'", ["uln"] = "อุนเซอร์ด็อยทช์", ["ulu"] = "Uma' Lung", ["ulw"] = "Ulwa", ["uma"] = "Umatilla", ["umb"] = "Umbundu", ["umc"] = "Marrucinian", ["umd"] = "Umbindhamu", ["umg"] = "Umbuygamu", ["umi"] = "Ukit", ["umm"] = "Umon", ["umn"] = "Makyan Naga", ["umo"] = "Umotína", ["ump"] = "Umpila", ["umr"] = "Umbugarla", ["ums"] = "Pendau", ["umu"] = "Munsee", ["una"] = "North Watut", ["und"] = "Undetermined", ["une"] = "Uneme", ["ung"] = "Ngarinyin", ["unk"] = "Enawené-Nawé", ["unm"] = "Unami", ["unn"] = "Kurnai", ["unr"] = "Mundari", ["unu"] = "Unubahe", ["unx"] = "Munda", ["unz"] = "Unde Kaili", ["uok"] = "Uokha", ["uon"] = "Kulon", ["upi"] = "Umeda", ["upv"] = "Northeast Malakula", ["ur"] = "อูรดู", ["ura"] = "Urarina", ["urb"] = "Urubú-Kaapor", ["urc"] = "Urningangg", ["ure"] = "Uru", ["urf"] = "Uradhi", ["urg"] = "Urigina", ["urh"] = "Urhobo", ["uri"] = "Urim", ["urj-fin-pro"] = "ฟินนิกดั้งเดิม", ["urj-koo"] = "Old Komi", ["urj-kuk"] = "Kukkuzi", ["urj-kya"] = "Komi-Yazva", ["urj-mdv-pro"] = "Proto-Mordvinic", ["urj-prm-pro"] = "เปอร์มิกดั้งเดิม", ["urj-pro"] = "ยูราลิกดั้งเดิม", ["urj-ugr-pro"] = "ยูกริกดั้งเดิม", ["urk"] = "อูรักลาโวยจ", ["url"] = "Urali", ["urm"] = "Urapmin", ["urn"] = "Uruangnirin", ["uro"] = "Ura (New Guinea)", ["urp"] = "Uru-Pa-In", ["urr"] = "Löyöp", ["urt"] = "Urat", ["uru"] = "Urumi", ["urv"] = "Uruava", ["urw"] = "Sop", ["urx"] = "Urimo", ["ury"] = "Orya", ["urz"] = "Uru-Eu-Wau-Wau", ["usa"] = "Usarufa", ["ush"] = "Ushojo", ["usi"] = "Usui", ["usk"] = "Usaghade", ["usp"] = "Uspanteco", ["uss"] = "Saare", ["usu"] = "Uya", ["uta"] = "Otank", ["ute"] = "Ute", ["uth"] = "Hun", ["utp"] = "Aba", ["utr"] = "Etulo", ["utu"] = "Utu", ["uum"] = "Urum", ["uun"] = "Kulon-Pazeh", ["uur"] = "Ura (Vanuatu)", ["uuu"] = "U", ["uve"] = "West Uvean", ["uvh"] = "Uri", ["uvl"] = "Lote", ["uwa"] = "Kuku-Uwanh", ["uya"] = "Doko-Uyanga", ["uz"] = "อุซเบก", ["vaa"] = "Vaagri Booli", ["vae"] = "Vale", ["vag"] = "Vagla", ["vah"] = "Varhadi", ["vai"] = "ไว", ["vaj"] = "Sekele", ["val"] = "Vehes", ["vam"] = "Vanimo", ["van"] = "Valman", ["vao"] = "Vao", ["vap"] = "Vaiphei", ["var"] = "Huarijio", ["vas"] = "Vasavi", ["vau"] = "Vanuma", ["vav"] = "Varli", ["vay"] = "Vayu", ["vbb"] = "Southeast Babar", ["vbk"] = "Southwestern Bontoc", ["ve"] = "เวนดา", ["vec"] = "เวเนโต", ["ved"] = "Veddah", ["vem"] = "Vemgo-Mabas", ["veo"] = "Ventureño", ["vep"] = "เวปส์", ["ver"] = "Mom Jango", ["vgr"] = "Vaghri", ["vgt"] = "Flemish Sign Language", ["vi"] = "เวียดนาม", ["vic"] = "Virgin Islands Creole", ["vid"] = "Vidunda", ["vif"] = "Vili", ["vig"] = "Viemo", ["vil"] = "Vilela", ["vis"] = "Vishavan", ["vit"] = "Viti", ["viv"] = "Iduna", ["vjk"] = "Bajjika", ["vka"] = "Kariyarra", ["vki"] = "Ija-Zuba", ["vkj"] = "Kujarge", ["vkk"] = "Kaur", ["vkl"] = "Kulisusu", ["vkm"] = "Kamakan", ["vko"] = "Kodeoha", ["vkp"] = "Korlai Creole Portuguese", ["vkt"] = "Tenggarong Kutai Malay", ["vku"] = "Kurrama", ["vlp"] = "Valpei", ["vls"] = "West Flemish", ["vma"] = "Martuthunira", ["vmb"] = "Mbabaram", ["vmc"] = "Juxtlahuaca Mixtec", ["vmd"] = "Mudu Koraga", ["vme"] = "East Masela", ["vmf"] = "ฟรังโกเนียตะวันออก", ["vmg"] = "Minigir", ["vmh"] = "Maraghei", ["vmi"] = "Miwa", ["vmj"] = "Ixtayutla Mixtec", ["vmk"] = "Makhuwa-Shirima", ["vml"] = "Malgana", ["vmm"] = "Mitlatongo Mixtec", ["vmp"] = "Soyaltepec Mazatec", ["vmq"] = "Soyaltepec Mixtec", ["vmr"] = "Marenje", ["vmu"] = "Muluridyi", ["vmv"] = "Valley Maidu", ["vmw"] = "Makhuwa", ["vmx"] = "Tamazola Mixtec", ["vmy"] = "Ayautla Mazatec", ["vmz"] = "Mazatlán Mazatec", ["vnk"] = "Lovono", ["vnm"] = "Neve'ei", ["vnp"] = "Vunapu", ["vo"] = "โวลาปุก", ["vor"] = "Voro", ["vot"] = "โวต", ["vra"] = "Vera'a", ["vro"] = "เวอโร", ["vrs"] = "Varisi", ["vrt"] = "Burmbar", ["vsi"] = "Moldova Sign Language", ["vsl"] = "Venezuelan Sign Language", ["vsv"] = "Valencian Sign Language", ["vto"] = "Vitou", ["vum"] = "Vumbu", ["vun"] = "Vunjo", ["vut"] = "Vute", ["vwa"] = "Awa (China)", ["wa"] = "วัลลูน", ["waa"] = "Walla Walla", ["wab"] = "Wab", ["wac"] = "Wasco-Wishram", ["wad"] = "Wandamen", ["waf"] = "Wakoná", ["wag"] = "Wa'ema", ["wah"] = "Watubela", ["waj"] = "Waffa", ["wal"] = "Wolaytta", ["wam"] = "Massachusett", ["wan"] = "Wan", ["wao"] = "Wappo", ["wap"] = "Wapishana", ["waq"] = "Wageman", ["war"] = "วาไร", ["was"] = "Washo", ["wat"] = "Kaninuwa", ["wau"] = "Wauja", ["wav"] = "Waka", ["waw"] = "Waiwai", ["wax"] = "Watam", ["way"] = "Wayana", ["waz"] = "Wampur", ["wba"] = "วาราโอ", ["wbb"] = "Wabo", ["wbe"] = "Waritai", ["wbf"] = "Wara", ["wbh"] = "Wanda", ["wbi"] = "Wanji", ["wbj"] = "Alagwa", ["wbk"] = "Waigali", ["wbl"] = "Wakhi", ["wbm"] = "Wa", ["wbp"] = "Warlpiri", ["wbq"] = "Waddar", ["wbr"] = "Wagdi", ["wbt"] = "Wanman", ["wbv"] = "Wajarri", ["wbw"] = "Woi", ["wca"] = "Yanomam", ["wci"] = "Waci Gbe", ["wdd"] = "Wandji", ["wdg"] = "Wadaginam", ["wdj"] = "Wadjiginy", ["wdt"] = "Wendat", ["wdu"] = "Wadjigu", ["wdy"] = "Wadjabangayi", ["wea"] = "Wewaw", ["wec"] = "Wè Western", ["wed"] = "Wedau", ["weh"] = "Weh", ["wei"] = "Kiunum", ["wem"] = "Weme Gbe", ["weo"] = "Wemale", ["wer"] = "Weri", ["wes"] = "Cameroon Pidgin", ["wet"] = "Perai", ["weu"] = "Welaung", ["wew"] = "Weyewa", ["wfg"] = "Yafi", ["wga"] = "Wagaya", ["wgb"] = "Wagawaga", ["wgg"] = "Wangganguru", ["wgi"] = "Wahgi", ["wgo"] = "Waigeo", ["wgu"] = "Wirangu", ["wgy"] = "Warrgamay", ["wha"] = "Manusela", ["whg"] = "North Wahgi", ["whk"] = "Wahau Kenyah", ["whu"] = "Wahau Kayan", ["wib"] = "Southern Toussian", ["wic"] = "Wichita", ["wie"] = "Wik-Epa", ["wif"] = "Wik-Keyangan", ["wig"] = "Wik-Ngathana", ["wih"] = "Wik-Me'anha", ["wii"] = "Minidien", ["wij"] = "Wik-Iiyanh", ["wik"] = "Wikalkan", ["wil"] = "Wilawila", ["wim"] = "Wik-Mungkan", ["win"] = "วินเนอเบโก", ["wir"] = "Wiraféd", ["wiu"] = "Wiru", ["wiv"] = "Muduapa", ["wiy"] = "Wiyot", ["wja"] = "Waja", ["wji"] = "Warji", ["wka"] = "Kw'adza", ["wkb"] = "Kumbaran", ["wkd"] = "Mo", ["wkl"] = "Kalanadi", ["wku"] = "Kunduvadi", ["wkw"] = "Wakawaka", ["wky"] = "Wangkayutyuru", ["wla"] = "Walio", ["wlc"] = "Mwali Comorian", ["wle"] = "Wolane", ["wlg"] = "Kunbarlang", ["wli"] = "Waioli", ["wlk"] = "Wailaki", ["wll"] = "Wali (Sudan)", ["wlm"] = "เวลส์กลาง", ["wlo"] = "Wolio", ["wlr"] = "Wailapa", ["wls"] = "Wallisian", ["wlu"] = "Wuliwuli", ["wlv"] = "Wichí Lhamtés Vejoz", ["wlw"] = "Walak", ["wlx"] = "Wali (Ghana)", ["wly"] = "Waling", ["wmb"] = "Wambaya", ["wmc"] = "Wamas", ["wmd"] = "Mamaindé", ["wme"] = "Wambule", ["wmh"] = "Waima'a", ["wmi"] = "Wamin", ["wmm"] = "Maiwa (Indonesia)", ["wmn"] = "Waamwang", ["wmo"] = "Wam", ["wms"] = "Wambon", ["wmt"] = "Walmajarri", ["wmw"] = "มวานี", ["wmx"] = "Womo", ["wnb"] = "Wanambre", ["wnc"] = "Wantoat", ["wnd"] = "Wandarang", ["wne"] = "Waneci", ["wng"] = "Wanggom", ["wni"] = "Ndzwani Comorian", ["wnk"] = "Wanukaka", ["wnm"] = "Wanggamala", ["wno"] = "Wano", ["wnp"] = "Wanap", ["wnu"] = "Usan", ["wnw"] = "Wintu", ["wny"] = "Wanyi", ["wo"] = "โวลอฟ", ["woa"] = "Tyaraity", ["wob"] = "Wobé", ["woc"] = "Wogeo", ["wod"] = "Wolani", ["woe"] = "Woleaian", ["wog"] = "Wogamusin", ["woi"] = "Kamang", ["wok"] = "Longto", ["wom"] = "Perema", ["won"] = "Wongo", ["woo"] = "Manombai", ["wor"] = "Woria", ["wos"] = "Hanga Hundi", ["wow"] = "Wawonii", ["woy"] = "Weyto", ["wpc"] = "Wirö", ["wra"] = "Warapu", ["wrb"] = "Warluwara", ["wrg"] = "Warungu", ["wrh"] = "Wiradjuri", ["wri"] = "Wariyangga", ["wrk"] = "Garawa", ["wrl"] = "Warlmanpa", ["wrm"] = "Warumungu", ["wrn"] = "Warnang", ["wro"] = "Worora", ["wrp"] = "Waropen", ["wrr"] = "Wardaman", ["wrs"] = "Waris", ["wru"] = "Waru", ["wrv"] = "Waruna", ["wrw"] = "Gugu Warra", ["wrx"] = "Wae Rana", ["wrz"] = "Warray", ["wsa"] = "Warembori", ["wsi"] = "Wusi", ["wsk"] = "Waskia", ["wsr"] = "Owenia", ["wsu"] = "Wasu", ["wsv"] = "Wotapuri-Katarqalai", ["wtf"] = "วาตีวา", ["wth"] = "Wathaurong", ["wti"] = "Berta", ["wtk"] = "Watakataui", ["wtm"] = "Mewati", ["wtw"] = "Wotu", ["wua"] = "Wikngenchera", ["wub"] = "Wunambal", ["wud"] = "Wudu", ["wuh"] = "Wutunhua", ["wul"] = "Silimo", ["wum"] = "Wumbvu", ["wun"] = "Bungu", ["wur"] = "Wurrugu", ["wut"] = "Wutung", ["wuu"] = "อู๋", ["wuv"] = "Wuvulu-Aua", ["wux"] = "Wulna", ["wuy"] = "Wauyai", ["wwa"] = "Waama", ["wwo"] = "Dorig", ["wwr"] = "Warrwa", ["www"] = "Wawa", ["wxa"] = "Waxiang", ["wxw"] = "Wardandi", ["wya"] = "Wyandot", ["wyb"] = "Ngiyambaa", ["wyi"] = "Woiwurrung", ["wym"] = "วีลามอวิตแซ", ["wyr"] = "Wayoró", ["wyy"] = "Western Fijian", ["xaa"] = "อาหรับแบบอันดะลุส", ["xab"] = "Sambe", ["xac"] = "Kachari", ["xad"] = "Adai", ["xae"] = "Aequian", ["xag"] = "Aghwan", ["xai"] = "Kaimbé", ["xaj"] = "Ararandewára", ["xak"] = "Maku", ["xal"] = "คัลมึค", ["xam"] = "ǀXam", ["xan"] = "Xamtanga", ["xao"] = "Khao", ["xap"] = "Apalachee", ["xaq"] = "Aquitanian", ["xar"] = "Karami", ["xas"] = "Kamassian", ["xat"] = "Katawixi", ["xau"] = "Kauwera", ["xav"] = "Xavante", ["xaw"] = "Kawaiisu", ["xay"] = "Kayan Mahakam", ["xbb"] = "Lower Burdekin", ["xbc"] = "แบกเตรีย", ["xbd"] = "Bindal", ["xbe"] = "Bigambal", ["xbg"] = "Bunganditj", ["xbi"] = "Kombio", ["xbj"] = "Birrpayi", ["xbm"] = "เบรอตงกลาง", ["xbn"] = "Kenaboi", ["xbo"] = "Bulgar", ["xbp"] = "Bibbulman", ["xbr"] = "กัมเบอรา", ["xbw"] = "Kambiwá", ["xby"] = "Butchulla", ["xcb"] = "Cumbric", ["xcc"] = "Camunic", ["xce"] = "Celtiberian", ["xch"] = "Chemakum", ["xcl"] = "อาร์มีเนียเก่า", ["xcm"] = "Comecrudo", ["xcn"] = "Cotoname", ["xco"] = "ฆวารัซม์", ["xcr"] = "Carian", ["xct"] = "ทิเบตคลาสสิก", ["xcu"] = "Curonian", ["xcv"] = "Chuvan", ["xcw"] = "Coahuilteco", ["xcy"] = "Cayuse", ["xda"] = "Darkinjung", ["xdc"] = "Dacian", ["xdk"] = "Dharug", ["xdm"] = "เอโดม", ["xdq"] = "Kaitag", ["xdy"] = "Malayic Dayak", ["xeb"] = "Eblaite", ["xed"] = "Hdi", ["xeg"] = "ǁXegwi", ["xel"] = "Kelo", ["xem"] = "Kembayan", ["xep"] = "Epi-Olmec", ["xer"] = "Xerénte", ["xes"] = "Kesawai", ["xet"] = "Xetá", ["xeu"] = "Keoru-Ahia", ["xfa"] = "ฟาลิสกุส", ["xga"] = "Galatian", ["xgb"] = "Gbin", ["xgd"] = "Gudang", ["xgf"] = "Gabrielino-Fernandeño", ["xgg"] = "Goreng", ["xgi"] = "Garingbal", ["xgl"] = "Galindian", ["xgm"] = "Darumbal", ["xgn-pro"] = "มองโกลิกดั้งเดิม", ["xgr"] = "Garza", ["xgu"] = "Unggumi", ["xgw"] = "Guwa", ["xh"] = "โคซา", ["xha"] = "Harami", ["xhc"] = "Hunnic", ["xhd"] = "Hadrami", ["xhe"] = "Khetrani", ["xhm"] = "เขมรกลาง", ["xhr"] = "Hernican", ["xht"] = "Hattic", ["xhu"] = "ฮูร์เรีย", ["xhv"] = "Khua", ["xib"] = "Iberian", ["xii"] = "Xiri", ["xil"] = "Illyrian", ["xin"] = "Xinca", ["xir"] = "Xiriâna", ["xis"] = "Kisan", ["xiv"] = "Harappan", ["xiy"] = "Xipaya", ["xjb"] = "Minjungbal", ["xka"] = "Kalkoti", ["xkb"] = "Manigri-Kambolé Ede Nago", ["xkc"] = "Khoini", ["xkd"] = "Mendalam Kayan", ["xke"] = "Kereho", ["xkf"] = "Khengkha", ["xkg"] = "Kagoro", ["xki"] = "Kenyan Sign Language", ["xkj"] = "Kajali", ["xkk"] = "Kaco'", ["xkl"] = "Bakung", ["xkn"] = "Kayan River Kayan", ["xko"] = "Kiorr", ["xkp"] = "Kabatei", ["xkq"] = "Koroni", ["xkr"] = "Xakriabá", ["xks"] = "Kumbewaha", ["xkt"] = "Kantosi", ["xku"] = "Kaamba", ["xkv"] = "Kgalagadi", ["xkw"] = "Kembra", ["xkx"] = "Karore", ["xky"] = "Uma' Lasan", ["xkz"] = "Kurtöp", ["xla"] = "Kamula", ["xlb"] = "Loup B", ["xlc"] = "Lycian", ["xld"] = "Lydian", ["xle"] = "Lemnian", ["xlg"] = "Ancient Ligurian", ["xli"] = "Liburnian", ["xln"] = "Alanic", ["xlo"] = "Loup A", ["xlp"] = "Lepontic", ["xls"] = "Lusitanian", ["xlu"] = "Luwian", ["xly"] = "Elymian", ["xmb"] = "Mbonga", ["xmc"] = "Makhuwa-Marrevone", ["xmd"] = "Mbudum", ["xme-ker"] = "Kermanic", ["xme-kls"] = "Kalasuri", ["xme-klt"] = "Kilit", ["xme-mid"] = "Middle Median", ["xme-old"] = "Old Median", ["xme-ott"] = "Old Tati", ["xme-taf"] = "Tafreshi", ["xme-ttc-pro"] = "Proto-Tatic", ["xmf"] = "มิงเกรเลีย", ["xmg"] = "Mengaka", ["xmh"] = "Kugu-Muminh", ["xmj"] = "Majera", ["xmk"] = "Ancient Macedonian", ["xml"] = "Malaysian Sign Language", ["xmm"] = "มลายูแบบมานาโด", ["xmo"] = "Morerebi", ["xmp"] = "Kuku-Mu'inh", ["xmq"] = "Kuku-Mangk", ["xmr"] = "Meroitic", ["xms"] = "Moroccan Sign Language", ["xmt"] = "Matbat", ["xmu"] = "Kamu", ["xmx"] = "Maden", ["xmy"] = "Mayaguduna", ["xmz"] = "Mori Bawah", ["xna"] = "Ancient North Arabian", ["xnb"] = "Kanakanabu", ["xnd-pro"] = "Proto-Na-Dene", ["xng"] = "มองโกเลียกลาง", ["xnh"] = "Kuanhua", ["xni"] = "Ngarigu", ["xnk"] = "Nganakarti", ["xnn"] = "กันกานาอือเหนือ", ["xnr"] = "Kangri", ["xns"] = "Kanashi", ["xnt"] = "Narragansett", ["xnu"] = "Nukunul", ["xny"] = "Nyiyaparli", ["xoc"] = "O'chi'chi'", ["xod"] = "Kokoda", ["xog"] = "Soga", ["xoi"] = "Kominimung", ["xok"] = "Xokleng", ["xom"] = "Komo", ["xon"] = "Konkomba", ["xoo"] = "Xukurú", ["xop"] = "Kopar", ["xor"] = "Korubo", ["xow"] = "Kowaki", ["xpa"] = "Pirriya", ["xpb"] = "Pyemmairre", ["xpc"] = "Pecheneg", ["xpd"] = "Paredarerme", ["xpe"] = "Liberia Kpelle", ["xpf"] = "Southeast Tasmanian", ["xpg"] = "Phrygian", ["xph"] = "Tyerrernotepanner", ["xpi"] = "Pictish", ["xpj"] = "Mpalitjanh", ["xpk"] = "Kulina", ["xpl"] = "Port Sorell", ["xpm"] = "Pumpokol", ["xpn"] = "Kapinawá", ["xpo"] = "Pochutec", ["xpp"] = "Puyo-Paekche", ["xpq"] = "Mohegan-Pequot", ["xpr"] = "พาร์เทีย", ["xps"] = "Pisidian", ["xpu"] = "Punic", ["xpv"] = "Tommeginne", ["xpw"] = "Peerapper", ["xpx"] = "Toogee", ["xpy"] = "Buyeo", ["xpz"] = "Bruny Island", ["xqa"] = "Karakhanid", ["xqt"] = "Qatabanian", ["xra"] = "Krahô", ["xrb"] = "Eastern Karaboro", ["xrd"] = "Gundungurra", ["xre"] = "Kreye", ["xrg"] = "Minang", ["xri"] = "Krikati-Timbira", ["xrm"] = "Armazic", ["xrn"] = "Arin", ["xrq"] = "Karranga", ["xrr"] = "Raetic", ["xrt"] = "Aranama-Tamique", ["xru"] = "Marriammu", ["xrw"] = "Karawa", ["xsa"] = "Sabaean", ["xsb"] = "ซัมบัล", ["xsc-pro"] = "Proto-Scythian", ["xsc-sak-pro"] = "Proto-Saka", ["xsc-sar-pro"] = "Proto-Sarmatian", ["xsc-skw-pro"] = "Proto-Saka-Wakhi", ["xsd"] = "Sidetic", ["xse"] = "Sempan", ["xsh"] = "Shamang", ["xsi"] = "Sio", ["xsj"] = "Subi", ["xsl"] = "South Slavey", ["xsm"] = "Kasem", ["xsn"] = "Sanga (Nigeria)", ["xso"] = "Solano", ["xsp"] = "Silopi", ["xsq"] = "Makhuwa-Saka", ["xsr"] = "เศรปา", ["xss"] = "Assan", ["xsu"] = "Sanumá", ["xsv"] = "ซูโดเวีย", ["xsy"] = "Saisiyat", ["xta"] = "Alcozauca Mixtec", ["xtb"] = "Chazumba Mixtec", ["xtc"] = "Kadugli", ["xtd"] = "Diuxi-Tilantongo Mixtec", ["xte"] = "Ketengban", ["xth"] = "Yitha Yitha", ["xti"] = "Sinicahua Mixtec", ["xtj"] = "San Juan Teita Mixtec", ["xtl"] = "Tijaltepec Mixtec", ["xtm"] = "Magdalena Peñasco Mixtec", ["xtn"] = "Northern Tlaxiaco Mixtec", ["xto"] = "โทแคเรียนเอ", ["xtp"] = "San Miguel Piedras Mixtec", ["xtq"] = "ตุมซุก", ["xtr"] = "Early Tripuri", ["xts"] = "Sindihui Mixtec", ["xtt"] = "Tacahua Mixtec", ["xtu"] = "Cuyamecalco Mixtec", ["xtv"] = "Thawa", ["xtw"] = "Tawandê", ["xty"] = "Yoloxochitl Mixtec", ["xua"] = "Alu Kurumba", ["xub"] = "Betta Kurumba", ["xud"] = "Umiida", ["xug"] = "คูนิงามิ", ["xuj"] = "Jennu Kurumba", ["xul"] = "Ngunawal", ["xum"] = "อุมเบรีย", ["xun"] = "Unggaranggu", ["xuo"] = "Kuo", ["xup"] = "Upper Umpqua", ["xur"] = "Urartian", ["xut"] = "Kuthant", ["xuu"] = "Khwe", ["xve"] = "Venetic", ["xvn"] = "วันดัล", ["xvo"] = "Volscian", ["xvs"] = "Vestinian", ["xwa"] = "Kwaza", ["xwc"] = "Woccon", ["xwd"] = "Wadi Wadi", ["xwe"] = "Xwela Gbe", ["xwg"] = "Kwegu", ["xwj"] = "Wajuk", ["xwk"] = "Wangkumara", ["xwl"] = "Western Xwla Gbe", ["xwo"] = "Written Oirat", ["xwr"] = "Kwerba Mamberamo", ["xww"] = "Wemba-Wemba", ["xxb"] = "Boro", ["xxk"] = "Ke'o", ["xxm"] = "Minkin", ["xxr"] = "Koropó", ["xxt"] = "Tambora", ["xya"] = "Yaygir", ["xyb"] = "Yandjibara", ["xyl"] = "Yalakalore", ["xyt"] = "Mayi-Thakurti", ["xyy"] = "Yorta Yorta", ["xzh"] = "Zhang-Zhung", ["xzm"] = "Semigallian", ["xzp"] = "Ancient Zapotec", ["yaa"] = "Yaminahua", ["yab"] = "Yuhup", ["yac"] = "Pass Valley Yali", ["yad"] = "Yagua", ["yae"] = "Pumé", ["yaf"] = "Yaka", ["yag"] = "ยามานา", ["yah"] = "Yazghulami", ["yai"] = "Yaghnobi", ["yaj"] = "Banda-Yangere", ["yak"] = "Yakima", ["yal"] = "Yalunka", ["yam"] = "Yamba", ["yan"] = "Mayangna", ["yao"] = "Yao", ["yap"] = "Yapese", ["yaq"] = "Yaqui", ["yar"] = "Yabarana", ["yas"] = "Gunu", ["yat"] = "Yambeta", ["yau"] = "Yuwana", ["yav"] = "Yangben", ["yaw"] = "Yawalapití", ["yay"] = "Agwagwune", ["yaz"] = "Lokaa", ["yba"] = "Yala", ["ybb"] = "Yemba", ["ybe"] = "Western Yugur", ["ybh"] = "Yakkha", ["ybi"] = "Yamphu", ["ybj"] = "Hasha", ["ybk"] = "Bokha", ["ybl"] = "Yukuben", ["ybm"] = "Yaben", ["ybn"] = "Yabaâna", ["ybo"] = "Yabong", ["ybx"] = "Yawiyo", ["yby"] = "Yaweyuha", ["ych"] = "Chesu", ["ycl"] = "Lolopo", ["ycn"] = "Yucuna", ["ycp"] = "Chepya", ["ycr"] = "Yilan Creole", ["yda"] = "Yanda", ["yde"] = "Yangum Dey", ["ydg"] = "Yidgha", ["ydk"] = "Yoidik", ["yea"] = "Ravula", ["yec"] = "Yenish", ["yee"] = "Yimas", ["yei"] = "Yeni", ["yej"] = "Yevanic", ["yen"] = "Yendang", ["yer"] = "Tarok", ["yes"] = "Yeskwa", ["yet"] = "Yetfa", ["yeu"] = "Yerukula", ["yev"] = "Yapunda", ["yey"] = "Yeyi", ["ygi"] = "Yiningayi", ["ygl"] = "Yangum Gel", ["ygm"] = "Yagomi", ["ygp"] = "Gepo", ["ygr"] = "Yagaria", ["ygs"] = "Yolngu Sign Language", ["ygu"] = "Yugul", ["ygw"] = "Yagwoia", ["yha"] = "Baha", ["yhl"] = "Hlepho Phowa", ["yi"] = "ยิดดิช", ["yia"] = "Yinggarda", ["yif"] = "Ache", ["yig"] = "Wusa", ["yii"] = "Yidiny", ["yij"] = "Yindjibarndi", ["yik"] = "Dongshanba Lalo", ["yil"] = "Yindjilandji", ["yim"] = "Yimchungru Naga", ["yin"] = "Yinchia", ["yip"] = "Pholo", ["yiq"] = "Micha", ["yir"] = "North Awyu", ["yis"] = "Yis", ["yit"] = "Eastern Lalu", ["yiu"] = "Lope", ["yiv"] = "Northern Nisu", ["yix"] = "Axi", ["yiy"] = "Yir-Yoront", ["yiz"] = "Azhe", ["yka"] = "ยากัน", ["ykg"] = "Northern Yukaghir", ["ykh"] = "Khamnigan Mongol", ["yki"] = "Yoke", ["ykk"] = "Yakaikeke", ["ykl"] = "Khlula", ["ykm"] = "Kap", ["ykn"] = "Kua-nsi", ["yko"] = "Yasa", ["ykr"] = "Yekora", ["ykt"] = "Kathu", ["yku"] = "Kuamasi", ["yky"] = "Yakoma", ["yla"] = "Yaul", ["ylb"] = "Yaleba", ["yle"] = "Yele", ["ylg"] = "Yelogu", ["yli"] = "Angguruk Yali", ["yll"] = "Yil", ["ylm"] = "Limi", ["yln"] = "Langnian Buyang", ["ylo"] = "Naruo", ["ylr"] = "Yalarnnga", ["ylu"] = "Aribwaung", ["yly"] = "Nyelâyu", ["ymb"] = "Yambes", ["ymc"] = "Southern Muji", ["ymd"] = "Muda", ["yme"] = "Yameo", ["ymg"] = "Yamongeri", ["ymh"] = "Mili", ["ymi"] = "Moji", ["ymk"] = "Makwe", ["yml"] = "Iamalele", ["ymm"] = "Maay", ["ymn"] = "Sunum", ["ymo"] = "Yangum Mon", ["ymp"] = "Yamap", ["ymq"] = "Qila Muji", ["ymr"] = "Malasar", ["yms"] = "Mysian", ["ymx"] = "Northern Muji", ["ymz"] = "Muzi", ["yna"] = "Aluo", ["ynb"] = "Yamben", ["ynd"] = "Yandruwandha", ["yne"] = "Lang'e", ["yng"] = "Yango", ["ynk"] = "Naukanski", ["ynl"] = "Yangulam", ["ynn"] = "Yana", ["yno"] = "ยอง", ["yns"] = "Yansi", ["ynu"] = "Yahuna", ["yo"] = "โยรูบา", ["yob"] = "Yoba", ["yog"] = "โยกัด", ["yoi"] = "โยนางูนิ", ["yok-bvy"] = "Buena Vista Yokuts", ["yok-dly"] = "Delta Yokuts", ["yok-gsy"] = "Gashowu Yokuts", ["yok-kry"] = "Kings River Yokuts", ["yok-nvy"] = "Northern Valley Yokuts", ["yok-ply"] = "Palewyami Yokuts", ["yok-svy"] = "Southern Valley Yokuts", ["yok-tky"] = "Tule-Kaweah Yokuts", ["yol"] = "ยอลอ", ["yom"] = "Yombe", ["yon"] = "Yongkom", ["yox"] = "โยรง", ["yoy"] = "Yoy", ["ypa"] = "Phala", ["ypb"] = "Labo Phowa", ["ypg"] = "Phola", ["yph"] = "Phupha", ["ypk-pro"] = "Proto-Yupik", ["ypm"] = "Phuma", ["ypn"] = "Ani Phowa", ["ypo"] = "Alo Phola", ["ypp"] = "Phupa", ["ypz"] = "Phuza", ["yra"] = "Yerakai", ["yrb"] = "Yareba", ["yre"] = "Yaouré", ["yri"] = "Yarí", ["yrk-for"] = "Forest Nenets", ["yrk-tun"] = "Tundra Nenets", ["yrl"] = "Nheengatu", ["yrn"] = "Yerong", ["yro"] = "Ỹaroamë", ["yrw"] = "Yarawata", ["yry"] = "Yarluyandi", ["ysc"] = "Jassic", ["ysd"] = "Samatao", ["ysg"] = "Sonaga", ["ysl"] = "Yugoslavian Sign Language", ["ysn"] = "Sani", ["yso"] = "Nisi", ["ysp"] = "Southern Lolopo", ["ysr"] = "Sirenik", ["yss"] = "Yessan-Mayo", ["ysy"] = "Sanie", ["yta"] = "Talu", ["ytl"] = "Toloza", ["ytp"] = "Thopho", ["ytw"] = "Yout Wam", ["yty"] = "Yatay", ["yua"] = "Yucatec Maya", ["yub"] = "Yugambal", ["yuc"] = "Yuchi", ["yue"] = "กวางตุ้ง", ["yuf"] = "Havasupai-Walapai-Yavapai", ["yug"] = "Yug", ["yui"] = "Yurutí", ["yuj"] = "Karkar-Yuri", ["yuk"] = "Yuki", ["yul"] = "Yulu", ["yum"] = "Yuma", ["yun"] = "Bena", ["yup"] = "Yukpa", ["yuq"] = "Yuqui", ["yur"] = "Yurok", ["yut"] = "Yopno", ["yuw"] = "Finisterre Yau", ["yux"] = "Southern Yukaghir", ["yuy"] = "East Yugur", ["yuz"] = "Yuracare", ["yva"] = "Yawa", ["yvt"] = "Yavitero", ["ywa"] = "Kalou", ["ywg"] = "Yinhawangka", ["ywl"] = "Western Lalu", ["ywn"] = "Yawanawa", ["ywq"] = "Nasu", ["ywr"] = "Yawuru", ["ywt"] = "Xishanba Lalo", ["ywu"] = "Wumeng", ["yww"] = "Yawarawarga", ["yxa"] = "Mayawali", ["yxg"] = "Yagara", ["yxl"] = "Yarli", ["yxm"] = "Yinwum", ["yxu"] = "Yuyu", ["yxy"] = "Yabula Yabula", ["yyu"] = "Torricelli Yau", ["yyz"] = "Ayizi", ["yzg"] = "E'ma Buyang", ["yzk"] = "Zokhuo", ["za"] = "จ้วง", ["zaa"] = "Sierra de Juárez Zapotec", ["zab"] = "San Juan Guelavía Zapotec", ["zac"] = "Ocotlán Zapotec", ["zad"] = "Cajonos Zapotec", ["zae"] = "Yareni Zapotec", ["zaf"] = "Ayoquesco Zapotec", ["zag"] = "Zaghawa", ["zah"] = "Zangwal", ["zai"] = "Isthmus Zapotec", ["zaj"] = "Zaramo", ["zak"] = "Zanaki", ["zal"] = "Zauzou", ["zam"] = "Central Mahuatlán Zapotec", ["zao"] = "Ozolotepec Zapotec", ["zap"] = "Zapotec", ["zaq"] = "Aloápam Zapotec", ["zar"] = "Rincón Zapotec", ["zas"] = "Santo Domingo Albarradas Zapotec", ["zat"] = "Tabaa Zapotec", ["zau"] = "ซันสการ์", ["zav"] = "Yatzachi Zapotec", ["zaw"] = "Mitla Zapotec", ["zax"] = "Xadani Zapotec", ["zay"] = "Zayse-Zergulla", ["zaz"] = "Zari", ["zbt"] = "Batui", ["zca"] = "Coatecas Altas Zapotec", ["zdj"] = "Ngazidja Comorian", ["zea"] = "Zealandic", ["zeg"] = "Zenag", ["zen"] = "Zenaga", ["zga"] = "Kinga", ["zgh"] = "Moroccan Amazigh", ["zgr"] = "Magori", ["zh"] = "จีน", ["zhb"] = "Zhaba", ["zhi"] = "Zhire", ["zhn"] = "จ้วงแบบหนง", ["zhw"] = "Zhoa", ["zhx-min-pro"] = "หมิ่นดั้งเดิม", ["zhx-sht"] = "Shaozhou Tuhua", ["zhx-sic"] = "เสฉวน", ["zhx-tai"] = "ห่อยซัน", ["zia"] = "Zia", ["zib"] = "Zimbabwe Sign Language", ["zik"] = "Zimakani", ["zil"] = "Zialo", ["zim"] = "Mesme", ["zin"] = "Zinza", ["zir"] = "Ziriya", ["ziw"] = "Zigula", ["ziz"] = "Zizilivakan", ["zka"] = "Kaimbulawa", ["zkb"] = "Koibal", ["zkd"] = "กะดู", ["zkg"] = "Goguryeo", ["zkh"] = "Khorezmian Turkic", ["zkk"] = "Karankawa", ["zko"] = "Kott", ["zkp"] = "São Paulo Kaingáng", ["zkr"] = "Zakhring", ["zkt"] = "คีตัน", ["zku"] = "Kaurna", ["zkv"] = "Krevinian", ["zkz"] = "Khazar", ["zle-ono"] = "Old Novgorodian", ["zle-ort"] = "รูซินเก่า", ["zls-chs"] = "Church Slavonic", ["zlw-ocs"] = "เช็กเก่า", ["zlw-opl"] = "โปแลนด์เก่า", ["zlw-osk"] = "สโลวักเก่า", ["zlw-slv"] = "สโลวินช์", ["zma"] = "Manda (Australia)", ["zmb"] = "Zimba", ["zmc"] = "Margany", ["zmd"] = "Maridan", ["zme"] = "Mangerr", ["zmf"] = "Mfinu", ["zmg"] = "Marti Ke", ["zmh"] = "Makolkol", ["zmi"] = "Negeri Sembilan Malay", ["zmj"] = "Maridjabin", ["zmk"] = "Mandandanyi", ["zml"] = "Madngele", ["zmm"] = "Marimanindji", ["zmn"] = "Mbangwe", ["zmo"] = "Molo", ["zmp"] = "Mbuun", ["zmq"] = "Mituku", ["zmr"] = "Maranungku", ["zms"] = "Mbesa", ["zmt"] = "Maringarr", ["zmu"] = "Muruwari", ["zmv"] = "Mbariman-Gudhinma", ["zmw"] = "Mbo (Congo)", ["zmx"] = "Bomitaba", ["zmy"] = "Mariyedi", ["zmz"] = "Mbandja", ["zna"] = "Zan Gula", ["zne"] = "Zande", ["zng"] = "Mang", ["znk"] = "Manangkari", ["zns"] = "Mangas", ["zoc"] = "Copainalá Zoque", ["zoh"] = "Chimalapa Zoque", ["zom"] = "Zou", ["zoo"] = "Asunción Mixtepec Zapotec", ["zoq"] = "Tabasco Zoque", ["zor"] = "Rayón Zoque", ["zos"] = "Francisco León Zoque", ["zpa"] = "Lachiguiri Zapotec", ["zpb"] = "Yautepec Zapotec", ["zpc"] = "Choapan Zapotec", ["zpd"] = "Southeastern Ixtlán Zapotec", ["zpe"] = "Petapa Zapotec", ["zpf"] = "San Pedro Quiatoni Zapotec", ["zpg"] = "Guevea de Humboldt Zapotec", ["zph"] = "Totomachapan Zapotec", ["zpi"] = "Santa María Quiegolani Zapotec", ["zpj"] = "Quiavicuzas Zapotec", ["zpk"] = "Tlacolulita Zapotec", ["zpl"] = "Lachixío Zapotec", ["zpm"] = "Mixtepec Zapotec", ["zpn"] = "Santa Inés Yatzechi Zapotec", ["zpo"] = "Amatlán Zapotec", ["zpp"] = "El Alto Zapotec", ["zpq"] = "Zoogocho Zapotec", ["zpr"] = "Santiago Xanica Zapotec", ["zps"] = "Coatlán Zapotec", ["zpt"] = "San Vicente Coatlán Zapotec", ["zpu"] = "Yalálag Zapotec", ["zpv"] = "Chichicapan Zapotec", ["zpw"] = "Zaniza Zapotec", ["zpx"] = "San Baltazar Loxicha Zapotec", ["zpy"] = "Mazaltepec Zapotec", ["zpz"] = "Texmelucan Zapotec", ["zra"] = "Gaya", ["zrg"] = "Mirgan", ["zrn"] = "Zirenkel", ["zro"] = "Záparo", ["zrs"] = "Mairasi", ["zsa"] = "Sarasira", ["zsk"] = "Kaskean", ["zsl"] = "Zambian Sign Language", ["zsr"] = "Southern Rincon Zapotec", ["zsu"] = "Sukurum", ["zte"] = "Elotepec Zapotec", ["ztg"] = "Xanaguía Zapotec", ["ztl"] = "Lapaguía-Guivini Zapotec", ["ztm"] = "San Agustín Mixtepec Zapotec", ["ztn"] = "Santa Catarina Albarradas Zapotec", ["ztp"] = "Loxicha Zapotec", ["ztq"] = "Quioquitani-Quierí Zapotec", ["zts"] = "Tilquiapan Zapotec", ["ztt"] = "Tejalapan Zapotec", ["ztu"] = "San Pablo Güilá Zapotec", ["ztx"] = "Zaachila Zapotec", ["zty"] = "Yatee Zapotec", ["zu"] = "ซูลู", ["zua"] = "Zeem", ["zuh"] = "Tokano", ["zum"] = "Kumzari", ["zun"] = "ซูนี", ["zuy"] = "Zumaya", ["zwa"] = "Zay", ["zyp"] = "ไซเฟ", ["zza"] = "ซาซา", ["zzj"] = "จ้วงแบบจั่วเจียง", } aspoad3nsd7bsklhjgyoyzwpte1neki มอดูล:languages/canonical names 828 211224 5775300 5771176 2026-10-14T10:35:17Z Octahedron80 267 5775300 Scribunto text/plain return { ["'Are'are"] = "alu", ["A'ou"] = "aou", ["A-Hmao"] = "hmd", ["A-Pucikwar"] = "apq", ["Aari"] = "aiw", ["Aasax"] = "aas", ["Aba"] = "utp", ["Abaga"] = "abg", ["Abai"] = "poz-abi", ["Abai Sungai"] = "abf", ["Abanyom"] = "abm", ["Abau"] = "aau", ["Abenaki"] = "abe", ["Abenlen Ayta"] = "abp", ["Abidji"] = "abi", ["Abinomn"] = "bsa", ["Abipón"] = "axb", ["Abishira"] = "ash", ["Abom"] = "aob", ["Abon"] = "abo", ["Aborlan Tagbanwa"] = "tbw", ["Abu"] = "ado", ["Abu'"] = "aah", ["Abua"] = "abn", ["Abui"] = "abz", ["Abun"] = "kgr", ["Abung"] = "abl", ["Abure"] = "abu", ["Abureni"] = "mgj", ["Acatepec Me'phaa"] = "tpx", ["Achagua"] = "aca", ["Achang"] = "acn", ["Ache"] = "yif", ["Acheron"] = "acz", ["Achi"] = "acr", ["Acholi"] = "ach", ["Achuar"] = "acu", ["Achumawi"] = "acv", ["Aché"] = "guq", ["Acroá"] = "acs", ["Adai"] = "xad", ["Adamorobe Sign Language"] = "ads", ["Adang"] = "adn", ["Adangbe"] = "adq", ["Adangme"] = "ada", ["Adap"] = "adp", ["Adasen"] = "tiu", ["Adele"] = "ade", ["Adhola"] = "adh", ["Adi"] = "adi", ["Adioukrou"] = "adj", ["Adithinngithigh"] = "dth", ["Adivasi Odia"] = "ort", ["Adiwasi Garasia"] = "gas", ["Adja"] = "ajg", ["Adnyamathanha"] = "adt", ["Adonara"] = "adr", ["Aduge"] = "adu", ["Adzera"] = "adz", ["Adûni"] = "art-adu", ["Aeka"] = "aez", ["Aekyom"] = "awi", ["Aequian"] = "xae", ["Aer"] = "aeq", ["Afade"] = "aal", ["Afghan Sign Language"] = "afg", ["Afitti"] = "aft", ["Afra"] = "ulf", ["Afrihili"] = "afh", ["Afro-Seminole Creole"] = "afs", ["Agarabi"] = "agd", ["Agariya"] = "agi", ["Agatu"] = "agc", ["Agavotaguerra"] = "avo", ["Agawam"] = "alg-aga", ["Aghem"] = "agq", ["Aghu"] = "ahh", ["Aghu Tharrnggala"] = "gtu", ["Aghul"] = "agx", ["Aghwan"] = "xag", ["Agi"] = "aif", ["Agob"] = "kit", ["Agoi"] = "ibm", ["Aguacateca"] = "agu", ["Aguano"] = "aga", ["Aguaruna"] = "agr", ["Aguna"] = "aug", ["Agusan Manobo"] = "msm", ["Agwagwune"] = "yay", ["Ahanta"] = "aha", ["Ahirani"] = "ahr", ["Ahtna"] = "aht", ["Ahwai"] = "nfd", ["Ai-Cham"] = "aih", ["Aighon"] = "aix", ["Aikanã"] = "tba", ["Aiklep"] = "mwg", ["Aimele"] = "ail", ["Aimol"] = "aim", ["Ainbai"] = "aic", ["Aiome"] = "aki", ["Airoran"] = "air", ["Aisi"] = "mmq", ["Aja"] = "aja", ["Ajawa"] = "ajw", ["Ajië"] = "aji", ["Ajyíninka Apurucayali"] = "cpc", ["Ak"] = "akq", ["Aka (Central Africa)"] = "axk", ["Aka (Sudan)"] = "soh", ["Aka-Bea"] = "abj", ["Aka-Bo"] = "akm", ["Aka-Cari"] = "aci", ["Aka-Kede"] = "akx", ["Aka-Kol"] = "aky", ["Aka-Kora"] = "ack", ["Akar-Bale"] = "acl", ["Akaselem"] = "aks", ["Akatek"] = "knj", ["Akawaio"] = "ake", ["Ake"] = "aik", ["Akebu"] = "keu", ["Akei"] = "tsr", ["Akeu"] = "aeu", ["Akhvakh"] = "akv", ["Akkala Sami"] = "sia", ["Akolet"] = "akt", ["Akoose"] = "bss", ["Akoye"] = "miw", ["Akpa"] = "akf", ["Akpes"] = "ibe", ["Akrukay"] = "afi", ["Akuku"] = "ayk", ["Akum"] = "aku", ["Akuntsu"] = "aqz", ["Akurio"] = "ako", ["Akuwagel"] = "bey", ["Akwa"] = "akw", ["Akyaung Ari"] = "nqy", ["Al-Sayyid Bedouin Sign Language"] = "syy", ["Alaba"] = "alw", ["Alabat Island Agta"] = "dul", ["Alacatlatzala Mixtec"] = "mim", ["Alago"] = "ala", ["Alagwa"] = "wbj", ["Alak"] = "alk", ["Alamblak"] = "amp", ["Alangan"] = "alj", ["Alanic"] = "xln", ["Alapmunte"] = "apv", ["Alas-Kluet Batak"] = "btz", ["Alawa"] = "alh", ["Alazapa"] = "nai-ala", ["Albanian Sign Language"] = "sqk", ["Alchuka"] = "tuw-alk", ["Alcozauca Mixtec"] = "xta", ["Alege"] = "alf", ["Alekano"] = "gah", ["Aleut"] = "ale", ["Algerian Sign Language"] = "asp", ["Algonquin"] = "alq", ["Ali"] = "aiy", ["Alladian"] = "ald", ["Allar"] = "all", ["Allentiac"] = "sai-all", ["Alngith"] = "aid", ["Alo Phola"] = "ypo", ["Alorese"] = "aol", ["Aloápam Zapotec"] = "zaq", ["Alsea"] = "aes", ["Alu"] = "mte", ["Alu Kurumba"] = "xua", ["Alugu"] = "aub", ["Alumu-Tesu"] = "aab", ["Alune"] = "alp", ["Alungul"] = "aus-alu", ["Aluo"] = "yna", ["Alur"] = "alz", ["Alutor"] = "alr", ["Alviri-Vidari"] = "avd", ["Alyawarr"] = "aly", ["Ama"] = "amm", ["Amahai"] = "amq", ["Amahuaca"] = "amc", ["Amaimon"] = "ali", ["Amal"] = "aad", ["Amanab"] = "amn", ["Amanayé"] = "ama", ["Amara"] = "aie", ["Amarakaeri"] = "amr", ["Amarasi"] = "aaz", ["Amarizana"] = "awd-ama", ["Amasi"] = "alv-ama", ["Amatlán Zapotec"] = "zpo", ["Amba"] = "rwm", ["Ambai"] = "amk", ["Ambakich"] = "aew", ["Ambala Ayta"] = "abc", ["Ambelau"] = "amv", ["Ambele"] = "ael", ["Amblong"] = "alm", ["Ambo"] = "amb", ["Ambonese Malay"] = "abs", ["Ambrak"] = "aag", ["Ambul"] = "apo", ["Ambulas"] = "abt", ["Amdang"] = "amj", ["Amele"] = "aey", ["Amganad Ifugao"] = "ifa", ["Ami"] = "amy", ["Amis"] = "ami", ["Amo"] = "amo", ["Amol"] = "alx", ["Amoltepec Mixtec"] = "mbz", ["Amondawa"] = "adw", ["Amorite"] = "sem-amo", ["Ampanang"] = "apg", ["Ampari Dogon"] = "aqd", ["Amri Karbi"] = "ajz", ["Amto"] = "amt", ["Amurdag"] = "amg", ["Ana Tinga Dogon"] = "dti", ["Anaang"] = "anw", ["Anakalangu"] = "akg", ["Anam"] = "pda", ["Anambé"] = "aan", ["Anamgura"] = "imi", ["Anasi"] = "bpo", ["Anauyá"] = "awd-ana", ["Ancient Ligurian"] = "xlg", ["Ancient Macedonian"] = "xmk", ["Ancient North Arabian"] = "xna", ["Ancient Zapotec"] = "xzp", ["Andai"] = "afd", ["Andajin"] = "ajn", ["Andaman Creole Hindi"] = "hca", ["Andaqui"] = "ana", ["Andarum"] = "aod", ["Andegerebinha"] = "adg", ["Andh"] = "anr", ["Andi"] = "ani", ["Andio"] = "bzb", ["Andjingith"] = "aus-and", ["Andoa"] = "anb", ["Andoque"] = "ano", ["Andoquero"] = "sai-and", ["Andra-Hus"] = "anx", ["Anem"] = "anz", ["Aneme Wake"] = "aby", ["Anfillo"] = "myo", ["Angaataha"] = "agm", ["Angaité"] = "aqt", ["Angal"] = "age", ["Angal Enen"] = "aoe", ["Angal Heneng"] = "akh", ["Angami"] = "njm", ["Angevin"] = "roa-ang", ["Angguruk Yali"] = "yli", ["Angika"] = "anp", ["Angkamuthi"] = "avm", ["Angkola Batak"] = "akb", ["Angkula"] = "aus-ang", ["Angloromani"] = "rme", ["Angolar"] = "aoa", ["Angor"] = "agg", ["Angoram"] = "aog", ["Angosturas Tunebo"] = "tnd", ["Anguthimri"] = "awg", ["Ani Phowa"] = "ypn", ["Anii"] = "blo", ["Animere"] = "anf", ["Anindilyakwa"] = "aoi", ["Anjam"] = "boj", ["Ankave"] = "aak", ["Anmatyerre"] = "amx", ["Annobonese"] = "fab", ["Anong"] = "nun", ["Anor"] = "anj", ["Anserma"] = "ans", ["Ansus"] = "and", ["Antakarinya"] = "ant", ["Antigua and Barbuda Creole English"] = "aig", ["Antillean Creole"] = "gcf", ["Anu"] = "anl", ["Anuak"] = "anu", ["Anufo"] = "cko", ["Anuki"] = "aui", ["Anus"] = "auq", ["Anuta"] = "aud", ["Anyi"] = "any", ["Anyin Morofo"] = "mtb", ["Anāl"] = "anm", ["Ao"] = "njo", ["Aoheng"] = "pni", ["Aore"] = "aor", ["Ap Ma"] = "kbx", ["Apabhramsa"] = "inc-apa", ["Apalachee"] = "xap", ["Apalaí"] = "apy", ["Apali"] = "ena", ["Apasco-Apoala Mixtec"] = "mip", ["Apatani"] = "apt", ["Apiaká"] = "api", ["Apinayé"] = "apn", ["Apma"] = "app", ["Apolista"] = "awd-apo", ["Apro"] = "ahp", ["Apurinã"] = "apu", ["Aputai"] = "apx", ["Aquitanian"] = "xaq", ["Arabana"] = "ard", ["Arabela"] = "arl", ["Araki"] = "akr", ["Arakwal"] = "rkw", ["Aralle-Tabulahan"] = "atq", ["Arammba"] = "stk", ["Aranadan"] = "aaf", ["Aranama-Tamique"] = "xrt", ["Arandai"] = "jbj", ["Araona"] = "aro", ["Arapaho"] = "arp", ["Arapaso"] = "arj", ["Arara-Karo"] = "arr", ["Ararandewára"] = "xaj", ["Araweté"] = "awt", ["Arawum"] = "awm", ["Arbore"] = "arv", ["Archi"] = "aqc", ["Are"] = "mwc", ["Areba"] = "aea", ["Arem"] = "aem", ["Argentine Sign Language"] = "aed", ["Argobba"] = "agj", ["Arguni"] = "agf", ["Arhuaco"] = "arh", ["Arhâ"] = "aqr", ["Arhö"] = "aok", ["Ari"] = "aac", ["Aribwatsa"] = "laz", ["Aribwaung"] = "ylu", ["Arifama-Miniafia"] = "aai", ["Arigidi"] = "aqg", ["Arikapú"] = "ark", ["Arikara"] = "ari", ["Arikem"] = "ait", ["Arin"] = "xrn", ["Aringa"] = "luc", ["Armazic"] = "xrm", ["Armenian Sign Language"] = "aen", ["Aromanian"] = "rup", ["Arop-Lokep"] = "apr", ["Arop-Sissano"] = "aps", ["Arosi"] = "aia", ["Arritinngithigh"] = "rrt", ["Arta"] = "atz", ["Arua"] = "aru", ["Aruamu"] = "msy", ["Aruek"] = "aur", ["Aruop"] = "lsr", ["Arutani"] = "atx", ["Aruá"] = "arx", ["Arára (Mato Grosso)"] = "axg", ["Arára (Pará)"] = "aap", ["As"] = "asz", ["Asaro'o"] = "mtv", ["Ashe"] = "ahs", ["Ashkun"] = "ask", ["Asho Chin"] = "csh", ["Ashraaf"] = "cus-ash", ["Asháninka"] = "cni", ["Ashéninka Pajonal"] = "cjo", ["Ashéninka Perené"] = "prq", ["Asilulu"] = "asl", ["Askopan"] = "eiv", ["Asoa"] = "asv", ["Assan"] = "xss", ["Assangori"] = "sjg", ["Assiniboine"] = "asb", ["อารามายาใหม่แบบอัสซีเรีย"] = "aii", ["Asu"] = "aum", ["Asue Awyu"] = "psa", ["Asumboa"] = "aua", ["Asunción Mixtepec Zapotec"] = "zoo", ["Asuri"] = "asr", ["Ata"] = "atm", ["Ata Manobo"] = "atd", ["Atakapa"] = "aqp", ["Atampaya"] = "amz", ["Atanques"] = "cba-ata", ["Atatláhuca Mixtec"] = "mib", ["Atauran"] = "adb", ["Atayal"] = "tay", ["Atemble"] = "ate", ["Ateso"] = "teo", ["Athpare"] = "aph", ["Ati"] = "atk", ["Atikamekw"] = "atj", ["Atohwaim"] = "aqm", ["Atong (Cameroon)"] = "ato", ["Atorada"] = "aox", ["Atsahuaca"] = "atc", ["Atsam"] = "cch", ["Atsugewi"] = "atw", ["Attapady Kurumba"] = "pkr", ["Attié"] = "ati", ["Au"] = "avt", ["Auhelawa"] = "kud", ["Aukan"] = "djk", ["Aulua"] = "aul", ["Aurá"] = "aux", ["Aushi"] = "auh", ["Aushiri"] = "avs", ["Auslan"] = "asf", ["Austral"] = "aut", ["Australian Aboriginal Sign Language"] = "asw", ["Australian Kriol"] = "rop", ["Austrian Sign Language"] = "asq", ["Austronesian Mari"] = "hob", ["Auwe"] = "smf", ["Auyana"] = "auy", ["Auye"] = "auu", ["Auyokawa"] = "auo", ["Avatime"] = "avn", ["Avau"] = "avb", ["Avava"] = "tmb", ["Avikam"] = "avi", ["Avokaya"] = "avu", ["Avá-Canoeiro"] = "avv", ["Awa (China)"] = "vwa", ["Awa (New Guinea)"] = "awb", ["Awa-Cuaiquer"] = "kwi", ["Awabakal"] = "awk", ["Awak"] = "awo", ["Awar"] = "aya", ["Awara"] = "awx", ["Awbono"] = "awh", ["Aweer"] = "bob", ["Awera"] = "awr", ["Awetí"] = "awe", ["Awing"] = "azo", ["Awjila"] = "auj", ["Awngi"] = "awn", ["Awngthim"] = "gwm", ["Awtuw"] = "kmn", ["Awun"] = "aww", ["Awutu"] = "afu", ["Awyi"] = "auw", ["Axamb"] = "ahb", ["Axi"] = "yix", ["Ayabadhu"] = "ayd", ["Ayautla Mazatec"] = "vmy", ["Ayere"] = "aye", ["Ayerrerenge"] = "axe", ["Ayi"] = "ayq", ["Ayizi"] = "yyz", ["Ayizo"] = "ayb", ["Ayomán"] = "sai-ayo", ["Ayoquesco Zapotec"] = "zaf", ["Ayoreo"] = "ayo", ["Ayu"] = "ayu", ["Ayutla Mixtec"] = "miy", ["Azha"] = "aza", ["Azhe"] = "yiz", ["Azoyú Me'phaa"] = "tpc", ["Baa"] = "kwb", ["Baagandji"] = "drl", ["Baan"] = "bvj", ["Baangi"] = "bqx", ["Baatonum"] = "bba", ["Baba"] = "bbw", ["Baba Malay"] = "mbf", ["Babango"] = "bbm", ["Babanki"] = "bbk", ["Babatana"] = "baa", ["Babine-Witsuwit'en"] = "bcr", ["Babole"] = "bvx", ["Babungo"] = "bav", ["Babuza"] = "bzg", ["Bacama"] = "bcy", ["Bacanese Malay"] = "btj", ["Bada"] = "bhz", ["Badanchi"] = "bau", ["Bade"] = "bde", ["Badeshi"] = "bdz", ["Badimaya"] = "bia", ["Baduy"] = "bac", ["Badyara"] = "pbp", ["Baeggu"] = "bvd", ["Baelelea"] = "bvc", ["Baenan"] = "sai-bae", ["Baetora"] = "btr", ["Bafanji"] = "bfj", ["Bafaw"] = "bwt", ["Bafia"] = "ksf", ["Bafut"] = "bfd", ["Baga Kaloum"] = "bqf", ["Baga Koga"] = "bgo", ["Baga Manduri"] = "bmd", ["Baga Pokur"] = "bcg", ["Baga Sitemu"] = "bsp", ["Baga Sobané"] = "bsv", ["Bagheli"] = "bfy", ["Bagirmi"] = "bmi", ["Bago-Kusuntu"] = "bqg", ["Bagri"] = "bgq", ["Bagua"] = "sai-bag", ["Bagupi"] = "bpi", ["Bagusa"] = "bqb", ["Bagvalal"] = "kva", ["Baha"] = "yha", ["Baham"] = "bdw", ["Bahamian Creole"] = "bah", ["Bahau"] = "bhv", ["Bahinemo"] = "bjh", ["Bahing"] = "bhj", ["Bahonsuai"] = "bsu", ["Bai (South Sudan)"] = "bdj", ["Baibai"] = "bbf", ["Baikeno"] = "bkx", ["Bailang"] = "tbq-blg", ["Baima"] = "bqh", ["Baimak"] = "bmx", ["Bainouk-Gunyaamolo"] = "bcz", ["Bainouk-Gunyuño"] = "bab", ["Bainouk-Samik"] = "bcb", ["Baiso"] = "bsw", ["Baissa Fali"] = "fah", ["Bajan"] = "bjs", ["Bajelani"] = "bjm", ["Bajjika"] = "vjk", ["Baka"] = "bkc", ["Bakairí"] = "bkq", ["Bakaka"] = "bqz", ["Bakhtiari"] = "bqi", ["Baki"] = "bki", ["Bakoko"] = "bkh", ["Bakole"] = "kme", ["Bakpinka"] = "bbs", ["Bakulung"] = "bbu", ["Bakumpai"] = "bkr", ["Bakung"] = "xkl", ["Bakwé"] = "bjw", ["Bala"] = "tuw-bal", ["Balaesang"] = "bls", ["Balangao"] = "blw", ["Balangingi"] = "sse", ["Balanta-Ganja"] = "bjt", ["Balanta-Kentohe"] = "ble", ["Balantak"] = "blz", ["Balau"] = "blg", ["Baldemu"] = "bdn", ["Bali"] = "bcp", ["Baliledo"] = "poz-bal", ["Balinese Malay"] = "mhp", ["Balkan Gagauz Turkish"] = "bgx", ["Balkan Romani"] = "rmn", ["Balo"] = "bqo", ["Baloi"] = "biz", ["Balong"] = "bnt-bal", ["Baltic Romani"] = "rml", ["Baluan-Pam"] = "blq", ["Bamako Sign Language"] = "bog", ["Bamali"] = "bbq", ["Bambalang"] = "bmo", ["Bambam"] = "ptu", ["Bambassi"] = "myf", ["Bambili-Bambui"] = "baw", ["Bamenyam"] = "bce", ["Bamu"] = "bcf", ["Bamukumbit"] = "bqt", ["Bamum"] = "bax", ["Bamunka"] = "bvm", ["Bamwe"] = "bmg", ["Ban Khor Sign Language"] = "bfk", ["Bana"] = "bcw", ["Banao Itneg"] = "bjx", ["Banaro"] = "byz", ["Banda"] = "bnd", ["Banda Malay"] = "bpq", ["Banda-Bambari"] = "liy", ["Banda-Banda"] = "bpd", ["Banda-Mbrès"] = "bqk", ["Banda-Ndélé"] = "bfl", ["Banda-Yangere"] = "yaj", ["Bandi"] = "bza", ["Bandial"] = "bqj", ["Bandjalang"] = "bdy", ["Bangala"] = "bxg", ["Bangandu"] = "bgf", ["Bangba"] = "bbe", ["Banggai"] = "bgz", ["Bangi"] = "bni", ["Bangime"] = "dba", ["Bangka"] = "mfb", ["Bangolan"] = "bgj", ["Bangru"] = "sit-ban", ["Bangubangu"] = "bnx", ["Bangwinji"] = "bsj", ["Baniva"] = "bvv", ["Baniwa"] = "bwi", ["Banka"] = "bxw", ["Bankan Tey Dogon"] = "dbw", ["Bankon"] = "abb", ["Banoni"] = "bcm", ["Bantawa"] = "bap", ["Bantayanon"] = "bfx", ["Bantik"] = "bnq", ["Banyumasan"] = "map-bms", ["Baoule"] = "bci", ["Baraamu"] = "brd", ["Barai"] = "bbb", ["Barakai"] = "baj", ["Baram Kayan"] = "kys", ["Barama"] = "bbg", ["Barambu"] = "brm", ["Baramu"] = "bmz", ["Barapasi"] = "brp", ["Baras"] = "brs", ["Barasana"] = "bsn", ["Barbareño"] = "boi", ["Barclayville Grebo"] = "gry", ["Bardi"] = "bcj", ["Barein"] = "bva", ["Bargam"] = "mlp", ["Bari"] = "bfa", ["Bariai"] = "bch", ["Bariji"] = "bjc", ["Barikanchi"] = "bxo", ["Barikewa"] = "jbk", ["Barngarla"] = "bjb", ["Barok"] = "bjk", ["Barombi"] = "bbi", ["Barranbinya"] = "aus-bra", ["Barro Negro Tunebo"] = "tbn", ["Barrow Point"] = "bpt", ["Baruga"] = "bjz", ["Barunggam"] = "aus-brm", ["Baruya"] = "byr", ["Barwe"] = "bwg", ["Barzani Jewish Neo-Aramaic"] = "bjf", ["Baré"] = "bae", ["Barí"] = "mot", ["Basa"] = "bzw", ["Basa-Gumna"] = "bsl", ["Basa-Gurmana"] = "buj", ["Basaa"] = "bas", ["Basap"] = "bdb", ["Basay"] = "byq", ["Bashkardi"] = "bsg", ["Basketo"] = "bst", ["Basque-Icelandic Pidgin"] = "crp-bip", ["Bassa"] = "bsq", ["Bassa-Kontagora"] = "bsr", ["Bassari"] = "bsc", ["Bassossi"] = "bsi", ["Bata"] = "bta", ["Bataan Ayta"] = "ayt", ["Batad Ifugao"] = "ifb", ["Batanga"] = "bnm", ["Batek"] = "btq", ["Bateri"] = "btv", ["Bathari"] = "bhm", ["Bati (Cameroon)"] = "btc", ["Bati (Indonesia)"] = "bvt", ["Batu"] = "btu", ["Batui"] = "zbt", ["Batuley"] = "bay", ["Bau"] = "bbd", ["Bau Bidayuh"] = "sne", ["Bauchi"] = "bsf", ["Baure"] = "brg", ["Bauria"] = "bge", ["Bauro"] = "bxa", ["Bauwaki"] = "bwk", ["Bauzi"] = "bvz", ["Bawm Chin"] = "bgr", ["Bay Miwok"] = "mkq", ["Bayali"] = "bjy", ["Baybayanon"] = "bvy", ["Baygo"] = "byg", ["Bayogoula"] = "nai-bay", ["Bayono"] = "byl", ["Bayot"] = "bda", ["Bayungu"] = "bxj", ["Bazigar"] = "bfr", ["Baïnounk Gubëeher"] = "alv-bgu", ["Beami"] = "beo", ["Beary"] = "dra-bry", ["Beaver"] = "bea", ["Beba"] = "bfp", ["Bebe"] = "bzv", ["Bebele"] = "beb", ["Bebeli"] = "bek", ["Bebil"] = "bxp", ["Bedik"] = "tnr", ["Bedjond"] = "bjv", ["Bedoanas"] = "bed", ["Beeke"] = "bkf", ["Beele"] = "bxq", ["Beembe"] = "beq", ["Beezen"] = "bnz", ["Befang"] = "bby", ["Begbere-Ejar"] = "bqv", ["Beja"] = "bej", ["Bekati'"] = "bei", ["Bekwarra"] = "bkv", ["Bekwel"] = "bkw", ["Belait"] = "beg", ["Belanda Bor"] = "bxb", ["Belanda Viri"] = "bvi", ["Belhariya"] = "byw", ["Beli"] = "blm", ["Belizean Creole"] = "bzj", ["Bella Coola"] = "blc", ["Bellari"] = "brw", ["Belter Creole"] = "art-bel", ["Bemba"] = "bem", ["Bembe"] = "bmb", ["Ben Tey"] = "dbt", ["Bena"] = "yun", ["Benabena"] = "bef", ["Bench"] = "bcq", ["Bende"] = "bdp", ["Bendi"] = "bct", ["Beneraf"] = "bnv", ["Beng"] = "nhb", ["Benga"] = "bng", ["Benggoi"] = "bgy", ["Bengkala Sign Language"] = "bqy", ["Bentong"] = "bnu", ["Benyadu'"] = "byd", ["Beothuk"] = "bue", ["Bepour"] = "bie", ["Bera"] = "brf", ["Berakou"] = "bxv", ["Berau Malay"] = "bve", ["Berbice Creole Dutch"] = "brc", ["Bergish"] = "gmw-bgh", ["Berik"] = "bkl", ["Berinomo"] = "bit", ["Berom"] = "bom", ["Berta"] = "wti", ["Berti"] = "byt", ["Besisi"] = "mhe", ["Besme"] = "bes", ["Besoa"] = "bep", ["Betaf"] = "bfe", ["Bete"] = "byf", ["Bete-Bendi"] = "btt", ["Betoi"] = "sai-bet", ["Betta Kurumba"] = "xub", ["Bezhta"] = "kap", ["Bhadrawahi"] = "bhd", ["Bhalay"] = "bhx", ["Bharia"] = "bha", ["Bhatri"] = "bgw", ["Bhattiyali"] = "bht", ["Bhaya"] = "bhe", ["Bhele"] = "bhy", ["Bhilali"] = "bhi", ["Bhili"] = "bhb", ["Bhoti Kinnauri"] = "nes", ["Bhunjia"] = "bhu", ["Biafada"] = "bif", ["Biage"] = "bdf", ["Biak"] = "bhw", ["Biali"] = "beh", ["Bian Marind"] = "bpv", ["Biangai"] = "big", ["Biao"] = "byk", ["Biao Mon"] = "bmt", ["Biao-Jiao Mien"] = "bje", ["Biatah Bidayuh"] = "bth", ["Bibaali"] = "bcn", ["Bibbulman"] = "xbp", ["Bidiyo"] = "bid", ["Bidyara"] = "bym", ["Bidyogo"] = "bjg", ["Biem"] = "bmc", ["Bierebo"] = "bnk", ["Bieria"] = "brj", ["Biete"] = "biu", ["Big Nambas"] = "nmb", ["Biga"] = "bhc", ["Bigambal"] = "xbe", ["Bih"] = "ibh", ["Bijori"] = "bix", ["Bikaru"] = "bic", ["Bikya"] = "byb", ["Bila"] = "bip", ["Bilakura"] = "bql", ["Bilaspuri"] = "kfs", ["Bilba"] = "bpz", ["Bilbil"] = "brz", ["Bile"] = "bil", ["Biliau"] = "bcu", ["Biloxi"] = "bll", ["Bilua"] = "blb", ["Bilur"] = "bxf", ["Bima"] = "bhp", ["Bimin"] = "bhl", ["Bimoba"] = "bim", ["Bina"] = "bmn", ["Binahari"] = "bxz", ["Binandere"] = "bhg", ["Binawa"] = "byj", ["Bindal"] = "xbd", ["Bine"] = "bon", ["Binji"] = "bpj", ["Binongan Itneg"] = "itb", ["Bintauna"] = "bne", ["Bintulu"] = "bny", ["Binumarien"] = "bjr", ["Bipi"] = "biq", ["Birao"] = "brr", ["Birgid"] = "brk", ["Birgit"] = "btf", ["Birhor"] = "biy", ["Biri"] = "bzr", ["Biritai"] = "bqq", ["Birri"] = "bvq", ["Birrpayi"] = "xbj", ["Birwa"] = "brl", ["Biseni"] = "ije", ["Bishnupriya Manipuri"] = "bpy", ["Bishuo"] = "bwh", ["Bisis"] = "bnw", ["Bisorio"] = "bir", ["Bissa"] = "bib", ["Bit"] = "bgk", ["Bitare"] = "brt", ["Bitur"] = "mcc", ["Biwat"] = "bwm", ["Biyo"] = "byo", ["Biyom"] = "bpm", ["Blablanga"] = "blp", ["Blackfoot"] = "bla", ["Blafe"] = "bfh", ["Blang"] = "blr", ["Blin"] = "byn", ["Bo"] = "bgl", ["Bo-Rukul"] = "mae", ["Bo-Ung"] = "mux", ["Boano (Maluku)"] = "bzn", ["Boano (Sulawesi)"] = "bzl", ["Bobongko"] = "bgb", ["Bobot"] = "bty", ["Bodo (Central Africa)"] = "boy", ["Bodo Gadaba"] = "gbj", ["Bodo Parja"] = "bdv", ["Bofi"] = "bff", ["Boga"] = "bvw", ["Bogaya"] = "boq", ["Boghom"] = "bux", ["Boguru"] = "bqu", ["Bohtan Neo-Aramaic"] = "bhn", ["Boikin"] = "bzf", ["Bokar"] = "sit-bok", ["Bokha"] = "ybk", ["Boko"] = "bqc", ["Bokobaru"] = "bus", ["Bokoto"] = "bdt", ["Bokyi"] = "bky", ["Bola"] = "bnp", ["Bolak"] = "art-blk", ["Bolango"] = "bld", ["Bolgo"] = "bvo", ["Bolia"] = "bli", ["Bolivian Sign Language"] = "bvl", ["Boloki"] = "bkt", ["Bolon"] = "bof", ["Bolondo"] = "bzm", ["Bolyu"] = "ply", ["Bom"] = "bmf", ["Boma Nkuu"] = "bnt-bon", ["Boma Yumu"] = "bnt-boy", ["Bomboli"] = "bml", ["Bomboma"] = "bws", ["Bomitaba"] = "zmx", ["Bomu"] = "bmq", ["Bomwali"] = "bmw", ["Bon Gula"] = "glc", ["Bonan"] = "peh", ["Bondei"] = "bou", ["Bondo"] = "bfw", ["Bondoukou Kulango"] = "kzc", ["Bondum Dom Dogon"] = "dbu", ["Bonerate"] = "bna", ["Bonggi"] = "bdg", ["Bonggo"] = "bpg", ["Bongili"] = "bui", ["Bongo"] = "bot", ["Bongu"] = "bpu", ["Bonjo"] = "bok", ["Bonkeng"] = "bvg", ["Bonkiman"] = "bop", ["Bookan"] = "bnb", ["Boon"] = "bnl", ["Boor"] = "bvf", ["Bora"] = "boa", ["Border Kuna"] = "kvn", ["Borei"] = "gai", ["Boro"] = "xxb", ["Borong"] = "ksr", ["Boruca"] = "brn", ["Borôro"] = "bor", ["Boselewa"] = "bwf", ["Bosngun"] = "bqs", ["Bote-Majhi"] = "bmj", ["Botlikh"] = "bph", ["Botolan Sambal"] = "sbl", ["Bouna Kulango"] = "nku", ["บูร์กีญง"] = "roa-brg", ["Bozaba"] = "bzo", ["Bragat"] = "aof", ["Brazilian Sign Language"] = "bzs", ["Brek Karen"] = "kvl", ["Brem"] = "buq", ["Breri"] = "brq", ["Bribri"] = "bzd", ["British Sign Language"] = "bfi", ["Brokkat"] = "bro", ["Brokpake"] = "sgt", ["Brokskat"] = "bkk", ["Broome Pearling Lugger Pidgin"] = "bpl", ["Brunei Bisaya"] = "bsb", ["Bruny Island"] = "xpz", ["Bu"] = "jid", ["Bu-Nao Bunu"] = "bwx", ["Bua"] = "bub", ["Bualkhaw Chin"] = "cbl", ["Buamu"] = "box", ["Bube"] = "bvb", ["Bubi"] = "buw", ["Bubia"] = "bbx", ["Budeh Stieng"] = "stt", ["Budibud"] = "btp", ["Budong-Budong"] = "bdx", ["Budu"] = "buu", ["Budukh"] = "bdk", ["Buduma"] = "bdm", ["Budza"] = "bja", ["Buena Vista Yokuts"] = "yok-bvy", ["Bugan"] = "bbh", ["Bughotu"] = "bgt", ["Buglere"] = "sab", ["Bugun"] = "bgg", ["Buhi'non Bikol"] = "ubl", ["Buhid"] = "bku", ["Buhutu"] = "bxh", ["Bujhyal"] = "byh", ["Bukar-Sadung Bidayuh"] = "sdo", ["Bukat"] = "bvk", ["Bukawa"] = "buk", ["Bukhari"] = "bhh", ["Bukit Malay"] = "bvu", ["Bukitan"] = "bkn", ["Bukiyip"] = "ape", ["Buksa"] = "tkb", ["Bukusu"] = "bxk", ["Bulgar"] = "xbo", ["Bulgarian Sign Language"] = "bqn", ["Bulgebi"] = "bmp", ["Buli (Ghana)"] = "bwu", ["Buli (Indonesia)"] = "bzq", ["Bulo Stieng"] = "sti", ["Bulu (Cameroon)"] = "bum", ["Bulu (New Guinea)"] = "bjl", ["Bulungan"] = "blj", ["Bum"] = "bmv", ["Bumaji"] = "byp", ["Bumang"] = "bvp", ["Bumbita Arapesh"] = "aon", ["Bumthangkha"] = "kjz", ["Bun"] = "buv", ["Buna"] = "bvn", ["Bunaba"] = "bck", ["Bunak"] = "bfn", ["Bunama"] = "bdd", ["Bundeli"] = "bns", ["Bung"] = "bqd", ["Bungain"] = "but", ["Bunganditj"] = "xbg", ["Bungku"] = "bkz", ["Bungu"] = "wun", ["Bunoge"] = "dgb", ["Bunun"] = "bnn", ["Buol"] = "blf", ["Bura"] = "bwr", ["Bura Mabang"] = "mde", ["Burak"] = "bys", ["Buraka"] = "bkg", ["Burarra"] = "bvr", ["Burate"] = "bti", ["Burduna"] = "bxn", ["Bure"] = "bvh", ["Burgundian"] = "gme-bur", ["Burji"] = "bji", ["Burmbar"] = "vrt", ["Burmeso"] = "bzu", ["Buru (Indonesia)"] = "mhs", ["Buru (Nigeria)"] = "bqw", ["Burui"] = "bry", ["Burumakok"] = "aip", ["Burun"] = "bdi", ["Burunge"] = "bds", ["Burushaski"] = "bsk", ["Burusu"] = "bqr", ["Buruwai"] = "asi", ["Busa"] = "bqp", ["Busam"] = "bxs", ["Busami"] = "bsm", ["Busang Kayan"] = "bfg", ["Bushoong"] = "buf", ["Buso"] = "bso", ["Busoa"] = "bup", ["Bussa"] = "dox", ["Busuu"] = "bju", ["Butbut Kalinga"] = "kyb", ["Butchulla"] = "xby", ["Butmas-Tur"] = "bnr", ["Butuanon"] = "btw", ["Buwal"] = "bhs", ["Buyeo"] = "xpy", ["Buyu"] = "byi", ["Buyuan Jino"] = "jiy", ["Bwa"] = "bww", ["Bwaidoka"] = "bwd", ["Bwala"] = "bnt-bwa", ["Bwanabwana"] = "tte", ["Bwatoo"] = "bwa", ["Bwe Karen"] = "bwe", ["Bwela"] = "bwl", ["Bwile"] = "bwc", ["Bwisi"] = "bwz", ["Byangsi"] = "bee", ["Byep"] = "mkk", ["Bädi Kanum"] = "khd", ["Caac"] = "msq", ["Cabiyarí"] = "cbb", ["Cabre"] = "awd-cab", ["Cabécar"] = "cjp", ["Cacaloxtepec Mixtec"] = "miu", ["Cacaopera"] = "ccr", ["Cacgia Roglai"] = "roc", ["Cacua"] = "cbv", ["Cacán"] = "sai-cac", ["Caddo"] = "cad", ["Cafundó"] = "ccd", ["Cahuarano"] = "cah", ["Cahuilla"] = "chl", ["Caijia"] = "sit-cai", ["Cajonos Zapotec"] = "zad", ["Caka"] = "ckx", ["Cakfem-Mushere"] = "cky", ["Calabrian Greek"] = "grk-cal", ["Calamian Tagbanwa"] = "tbk", ["Calusa"] = "nai-cal", ["Caluyanun"] = "clu", ["Caló"] = "rmq", ["Camarines Norte Agta"] = "abd", ["Cambodian Sign Language"] = "csx", ["Cameroon Mambila"] = "mcu", ["Cameroon Pidgin"] = "wes", ["Campalagian"] = "cml", ["Camsá"] = "kbh", ["Camtho"] = "cmt", ["Camunic"] = "xcc", ["Candoshi-Shapra"] = "cbu", ["Canela"] = "ram", ["Canichana"] = "caz", ["Cantabrian"] = "roa-can", ["Caolan"] = "mlc", ["Capanahua"] = "kaq", ["Capiznon"] = "cps", ["Cappadocian Greek"] = "cpg", ["Caquinte"] = "cot", ["Cara"] = "cfd", ["Carabayo"] = "cby", ["Caramanta"] = "crf", ["Caranqui"] = "sai-caq", ["Carapana"] = "cbc", ["Carian"] = "xcr", ["Cariay"] = "awd-kar", ["Carijona"] = "cbd", ["Carolina Algonquian"] = "crr", ["Carolinian"] = "cal", ["Carpathian Romani"] = "rmc", ["Carrier"] = "crx", ["Cashibo-Cacataibo"] = "cbr", ["Cashinahua"] = "cbs", ["Casiguran Dumagat Agta"] = "dgc", ["Casuarina Coast Asmat"] = "asc", ["Catacao"] = "sai-cat", ["Catalan Sign Language"] = "csc", ["Catawba"] = "chc", ["Catuquinaru"] = "sai-ctq", ["Catío Chibcha"] = "cba-cat", ["Cauca"] = "cca", ["Cavineña"] = "cav", ["Cayubaba"] = "cyb", ["Cayuga"] = "cay", ["Cayuse"] = "xcy", ["Cazcan"] = "azc-caz", ["Cañari"] = "sai-cnr", ["Cebaara Senoufo"] = "sef", ["Celtiberian"] = "xce", ["Cen"] = "cen", ["Central Asmat"] = "cns", ["Central Atlas Tamazight"] = "tzm", ["Central Awyu"] = "awu", ["Central Bai"] = "bca", ["Central Bontoc"] = "lbk", ["Central Cagayan Agta"] = "agt", ["ฟรังโกเนียตอนกลาง"] = "gmw-cfr", ["Central Grebo"] = "grv", ["Central Huasteca Nahuatl"] = "nch", ["Central Huishui Hmong"] = "hmc", ["Central Mahuatlán Zapotec"] = "zam", ["Central Mansi"] = "mns-cen", ["Central Masela"] = "mxz", ["Central Mashan Hmong"] = "hmm", ["Central Mazahua"] = "maz", ["Central Melanau"] = "mel", ["Central Ojibwa"] = "ojc", ["Central Pame"] = "pbs", ["Central Pomo"] = "poo", ["Central Puebla Nahuatl"] = "ncx", ["Central Siberian Yupik"] = "ess", ["Central Sierra Miwok"] = "csm", ["Central Subanen"] = "syb", ["Central Tagbanwa"] = "tgt", ["Central Tarahumara"] = "tar", ["Central Teke"] = "nzu", ["Central Tunebo"] = "tuf", ["Centúúm"] = "cet", ["Cerma"] = "cme", ["Ch'olti'"] = "myn-chl", ["Ch'orti'"] = "caa", ["Chaap Wuurong"] = "tjw", ["Chachi"] = "cbi", ["Chadian Arabic"] = "shu", ["Chadian Sign Language"] = "cds", ["Chadong"] = "cdy", ["Chaha"] = "sem-cha", ["Chaima"] = "ciy", ["Chairel"] = "sit-cha", ["Chak"] = "ckh", ["Chakali"] = "cli", ["Chala"] = "cll", ["Chaldean Neo-Aramaic"] = "cld", ["Chali"] = "tgf", ["Chamacoco"] = "ceg", ["Chamalal"] = "cji", ["Chamba Daka"] = "ccg", ["Chamba Leko"] = "ndi", ["Chambeali"] = "cdh", ["Chambri"] = "can", ["Chamicuro"] = "ccc", ["Chamling"] = "rab", ["ช็องปาญ"] = "roa-cha", ["Chang"] = "nbc", ["Changriwa"] = "cga", ["Changthang"] = "cna", ["Chantyal"] = "chx", ["Chaná"] = "sai-chn", ["Chané"] = "caj", ["Chapacura"] = "sai-chp", ["Chara"] = "cra", ["Charrua"] = "sai-chr", ["Chaudangsi"] = "cdn", ["Chaura"] = "crv", ["Chayahuita"] = "cbt", ["Chayuco Mixtec"] = "mih", ["Chazumba Mixtec"] = "xtb", ["Che"] = "ruk", ["Cheke Holo"] = "mrn", ["Chemakum"] = "xch", ["Chenapian"] = "cjn", ["Chenchu"] = "cde", ["Chenoua"] = "cnu", ["Chepang"] = "cdm", ["Chepya"] = "ycp", ["Cherepon"] = "cpn", ["Chesu"] = "ych", ["Chetco-Tolowa"] = "ctc", ["Chewong"] = "cwg", ["Chhattisgarhi"] = "hne", ["Chhintange"] = "ctn", ["Chhulung"] = "cur", ["Chiangmai Sign Language"] = "csd", ["Chiapanec"] = "cip", ["Chibcha"] = "chb", ["Chicahuaxtla Triqui"] = "trs", ["Chichicapan Zapotec"] = "zpv", ["Chichimeca-Jonaz"] = "pei", ["Chichonyi-Chidzihana-Chikauma"] = "coh", ["Chickasaw"] = "cic", ["Chicomuceltec"] = "cob", ["Chiduruma"] = "dug", ["Chigmecatitlán Mixtec"] = "mii", ["Chilcotin"] = "clc", ["Chilean Sign Language"] = "csg", ["Chilisso"] = "clh", ["Chiltepec Chinantec"] = "csa", ["Chimalapa Zoque"] = "zoh", ["Chimariko"] = "cid", ["Chimila"] = "cbg", ["Chimwiini"] = "bnt-cmw", ["Chinali"] = "cih", ["Chinbon Chin"] = "cnb", ["Chinese Pidgin English"] = "cpi", ["Chinese Sign Language"] = "csl", ["Chinook"] = "chh", ["Chinook Jargon"] = "chn", ["Chipaya"] = "cap", ["Chipewyan"] = "chp", ["Chiquihuitlán Mazatec"] = "maq", ["Chiquimulilla"] = "nai-chi", ["Chiquitano"] = "cax", ["Chiricahua"] = "apm", ["Chirino"] = "sai-chi", ["Chiripá"] = "nhd", ["Chiru"] = "cdf", ["Chitimacha"] = "ctm", ["Chitkuli Kinnauri"] = "cik", ["Chittagonian"] = "ctg", ["Chitwania Tharu"] = "the", ["Chiwere"] = "iow", ["Choapan Zapotec"] = "zpc", ["Chocangaca"] = "cgk", ["Chochotec"] = "coz", ["Choctaw"] = "cho", ["Chodri"] = "cdi", ["Chokri Naga"] = "nri", ["Chokwe"] = "cjk", ["Chol"] = "ctu", ["Cholón"] = "cht", ["Choni"] = "cda", ["Chono"] = "sai-cno", ["Chontal Maya"] = "chf", ["Chopi"] = "cce", ["Chothe Naga"] = "nct", ["Chrau"] = "crw", ["Chru"] = "cje", ["Chuabo"] = "chw", ["Chuanqiandian Cluster Miao"] = "cqd", ["Chuave"] = "cjv", ["Chug"] = "cvg", ["Chuj"] = "cac", ["Chuka"] = "cuh", ["Chukwa"] = "cuw", ["Chulym"] = "clw", ["Chumburung"] = "ncu", ["Churahi"] = "cdj", ["Church Slavonic"] = "zls-chs", ["Churuya"] = "sai-chu", ["Chut"] = "scb", ["Chuukese"] = "chk", ["Chuvan"] = "xcv", ["Chácobo"] = "cao", ["Ci Gbe"] = "cib", ["Cia-Cia"] = "cia", ["Cibak"] = "ckl", ["Cicipu"] = "awc", ["Ciguayo"] = "nai-cig", ["คิมบรี"] = "cim", ["Cinamiguin Manobo"] = "mkx", ["Cinda-Regi-Tiyal"] = "cdr", ["Cineni"] = "cie", ["Cinta Larga"] = "cin", ["Cishingini"] = "asg", ["Citak"] = "txt", ["Ciwogai"] = "tgd", ["แกลิกคลาสสิก"] = "ghc", ["Classical Mandaic"] = "myz", ["ซีรีแอกคลาสสิก"] = "syc", ["Coahuilteco"] = "xcw", ["Coast Miwok"] = "csi", ["Coastal Konjo"] = "kjc", ["Coatecas Altas Zapotec"] = "zca", ["Coatepec Nahuatl"] = "naz", ["Coatlán Mixe"] = "mco", ["Coatlán Zapotec"] = "zps", ["Coatzospan Mixtec"] = "miz", ["Cocama"] = "cod", ["Cochimi"] = "coj", ["Cocopa"] = "coc", ["Cocos Islands Malay"] = "coa", ["Coeruna"] = "sai-coe", ["Coeur d'Alene"] = "crd", ["Cofán"] = "con", ["Cogui"] = "kog", ["Col"] = "liw", ["Colombian Sign Language"] = "csn", ["Colonia Tovar German"] = "gct", ["Columbia-Wenatchi"] = "col", ["Colán"] = "sai-col", ["Comaltepec Chinantec"] = "cco", ["Comanche"] = "com", ["Comechingon"] = "sai-cmg", ["Comecrudo"] = "xcm", ["Communicationssprache"] = "art-com", ["Como Karim"] = "cfg", ["Comox"] = "coo", ["Con"] = "cno", ["Coos"] = "csz", ["Copainalá Zoque"] = "zoc", ["Copala Triqui"] = "trc", ["Copallén"] = "sai-cop", ["Coquille"] = "coq", ["Cora"] = "crn", ["Cori"] = "cry", ["Coroado Puri"] = "sai-crd", ["Cosoleacaque Nahuatl"] = "nhk", ["Costa Rican Sign Language"] = "csr", ["Cotabato Manobo"] = "mta", ["Cotoname"] = "xcn", ["Cowlitz"] = "cow", ["Coyaima"] = "coy", ["Coyotepec Popoloca"] = "pbf", ["Coyutla Totonac"] = "toc", ["Creek"] = "mus", ["Croatian Sign Language"] = "csq", ["Cross River Mbembe"] = "mfn", ["Crow"] = "cro", ["Cruzeño"] = "crz", ["Cua"] = "cua", ["Cuban Sign Language"] = "csf", ["Cubeo"] = "cub", ["Cueva"] = "sai-cva", ["Cuiba"] = "cui", ["Cuitlatec"] = "cuy", ["Culina"] = "cul", ["Culli"] = "sai-cul", ["Cumanagoto"] = "cuo", ["Cumbric"] = "xcb", ["Cung"] = "cug", ["Cupeño"] = "cup", ["Curonian"] = "xcu", ["Curripaco"] = "kpc", ["Cutchi-Swahili"] = "ccl", ["Cuvok"] = "cuv", ["Cuyamecalco Mixtec"] = "xtu", ["Cuyunon"] = "cyo", ["Cwi Bwamu"] = "bwy", ["Czech Sign Language"] = "cse", ["Cèmuhî"] = "cam", ["Cốông"] = "cnc", ["Da'a Kaili"] = "kzf", ["Daai Chin"] = "dao", ["Daakaka"] = "bpa", ["Daantanai'"] = "lni", ["Daasanach"] = "dsh", ["Daba"] = "dbq", ["Dabarre"] = "dbr", ["Dabe"] = "dbe", ["Dacian"] = "xdc", ["Dadanitic"] = "sem-dad", ["Dadi Dadi"] = "dda", ["Dadibi"] = "mps", ["Dadiya"] = "dbd", ["Daga"] = "dgz", ["Dagaari Dioula"] = "dgd", ["Dagba"] = "dgk", ["Dagbani"] = "dag", ["Dagik"] = "dec", ["Dagoman"] = "dgn", ["Dahalik"] = "dlk", ["Dahalo"] = "dal", ["Daho-Doo"] = "das", ["Dai"] = "dij", ["Dair"] = "drb", ["Dairi Batak"] = "btd", ["Dakka"] = "dkk", ["Dakpa"] = "dka", ["Daloa Bété"] = "bev", ["Dama (Nigeria)"] = "dmm", ["Dama (Sierra Leone)"] = "dmn-dam", ["Damakawa"] = "dam", ["Damal"] = "uhn", ["Dambi"] = "dac", ["Dameli"] = "dml", ["Dampelas"] = "dms", ["Dan"] = "dnj", ["Danaru"] = "dnr", ["Danau"] = "dnu", ["Dandami Maria"] = "daq", ["Dangaléat"] = "daa", ["Dangaura Tharu"] = "thl", ["Danish Sign Language"] = "dsl", ["Dano"] = "aso", ["Danu"] = "dnv", ["Danuwar"] = "dhw", ["Dao"] = "daz", ["Daonda"] = "dnd", ["Dar Daju Daju"] = "djc", ["Dar Fur Daju"] = "daj", ["Dar Sila Daju"] = "dau", ["Darai"] = "dry", ["Dargwa"] = "dar", ["Darkinjung"] = "xda", ["Darlong"] = "dln", ["Darmiya"] = "drd", ["Daro-Matu Melanau"] = "dro", ["Darumbal"] = "xgm", ["Dass"] = "dot", ["Datian Min"] = "nan-dat", ["Datooga"] = "tcc", ["Daungwurrung"] = "dgw", ["Daur"] = "dta", ["Davawenyo"] = "daw", ["Dawawa"] = "dww", ["Dawera-Daweloor"] = "ddw", ["Day"] = "dai", ["Dayi"] = "dax", ["Dazaga"] = "dzg", ["Deccani"] = "dcc", ["Dedua"] = "ded", ["Defaka"] = "afn", ["Defi Gbe"] = "gbh", ["Deg"] = "mzw", ["Deg Xinag"] = "ing", ["Degema"] = "deg", ["Degenan"] = "dge", ["Dehwari"] = "deh", ["Dela-Oenale"] = "row", ["Delo"] = "ntr", ["Delta Yokuts"] = "yok-dly", ["Dem"] = "dem", ["Dema"] = "dmx", ["Demisa"] = "dei", ["Demotic Egyptian"] = "egx-dem", ["Demta"] = "dmy", ["Dena'ina"] = "tfn", ["Dendi"] = "ddn", ["Dengese"] = "dez", ["Dengka"] = "dnk", ["Deno"] = "dbb", ["Denya"] = "anv", ["Dení"] = "dny", ["Deori"] = "der", ["Desano"] = "des", ["Desiya"] = "dso", ["Dewas Rai"] = "dwz", ["Dewoin"] = "dee", ["Dezfuli"] = "def", ["Dghwede"] = "dgh", ["Dhaiso"] = "dhs", ["Dhalandji"] = "dhl", ["Dhangu"] = "dhg", ["Dhanki"] = "dhn", ["Dhao"] = "nfa", ["Dharug"] = "xdk", ["Dhatki"] = "mki", ["Dhimal"] = "dhi", ["Dhodia"] = "dho", ["Dhofari Arabic"] = "adf", ["Dhudhuroa"] = "ddr", ["Dhundhari"] = "dhd", ["Dhungaloo"] = "dhx", ["Dhurga"] = "dhu", ["Dhuwal"] = "dwu", ["Dhuwaya"] = "dwy", ["Dia"] = "dia", ["Dibabawon Manobo"] = "mbd", ["Dibiyaso"] = "dby", ["Dibo"] = "dio", ["Dicamay Agta"] = "duy", ["Didinga"] = "did", ["Dieri"] = "dif", ["Digo"] = "dig", ["Dii"] = "dur", ["Dijim-Bwilim"] = "cfa", ["Dilling"] = "dil", ["Dima"] = "jma", ["Dimasa"] = "dis", ["Dimbong"] = "dii", ["Dime"] = "dim", ["Dinapigue Agta"] = "phi-din", ["Dineor"] = "mrx", ["Ding"] = "diz", ["Dinka"] = "din", ["Diodio"] = "ddi", ["Dirasha"] = "gdl", ["Diri"] = "dwa", ["Dirim"] = "dir", ["Disa"] = "dsi", ["Ditammari"] = "tbz", ["Ditidaht"] = "dtd", ["Diuwe"] = "diy", ["Diuxi-Tilantongo Mixtec"] = "xtd", ["Dixon Reef"] = "dix", ["Dizin"] = "mdx", ["Djadjawurrung"] = "dja", ["Djambarrpuyngu"] = "djr", ["Djangun"] = "djf", ["Djauan"] = "djn", ["Djawi"] = "djw", ["Djimini"] = "dyi", ["Djinang"] = "dji", ["Djinba"] = "djb", ["Djiwarli"] = "djl", ["Dobel"] = "kvo", ["Dobu"] = "dob", ["Doe"] = "doe", ["Doga"] = "dgg", ["Doghoro"] = "dgx", ["Dogoso"] = "dgs", ["Dogosé"] = "dos", ["Dogri"] = "doi", ["Dogrib"] = "dgr", ["Dogul Dom"] = "dbg", ["Doka"] = "dbi", ["Doko-Uyanga"] = "uya", ["Dolgan"] = "dlg", ["Dom"] = "doa", ["Domaaki"] = "dmk", ["Domari"] = "rmt", ["Dominican Sign Language"] = "doq", ["Dompo"] = "doy", ["Domu"] = "dof", ["Domung"] = "dev", ["Dondo"] = "dok", ["Dong"] = "doh", ["Dongo"] = "doo", ["Dongolawi"] = "kzh", ["Dongotono"] = "ddd", ["Dongshanba Lalo"] = "yik", ["Donno So Dogon"] = "dds", ["Doondo"] = "dde", ["Dorasque"] = "cba-dor", ["Dori'o"] = "dor", ["Dorig"] = "wwo", ["Doromu-Koki"] = "kqc", ["Dorze"] = "doz", ["Doso"] = "dol", ["Doteli"] = "dty", ["Dothraki"] = "art-dtk", ["Doura"] = "don", ["Doutai"] = "tds", ["Doyayo"] = "dow", ["Drehu"] = "dhv", ["Drung"] = "duu", ["Duala"] = "dua", ["Duano"] = "dup", ["Duau"] = "dva", ["Dubli"] = "dub", ["Dubu"] = "dmu", ["Dugun"] = "ndu", ["Duguri"] = "dbm", ["Dugwor"] = "dme", ["Duhwa"] = "kbz", ["Duit"] = "cba-dui", ["Duke"] = "nke", ["Dukhan"] = "trk-dkh", ["Dulbu"] = "dbo", ["Duli"] = "duz", ["Duma"] = "dma", ["Dumaitic"] = "sem-dum", ["Dumbea"] = "duf", ["Dumi"] = "dus", ["Dumpas"] = "dmv", ["Dumun"] = "dui", ["Duna"] = "duc", ["Dungmali"] = "raa", ["Dungra Bhil"] = "duh", ["Dungu"] = "dbv", ["Dupaningan Agta"] = "duo", ["Dura"] = "drq", ["Duri"] = "mvp", ["Duriankere"] = "dbn", ["Duruwa"] = "pci", ["Dusner"] = "dsn", ["Dusun Deyah"] = "dun", ["Dusun Malang"] = "duq", ["Dusun Witu"] = "duw", ["Dutch Low Saxon"] = "nds-nl", ["Dutch Sign Language"] = "dse", ["Duun"] = "dux", ["Duupa"] = "dae", ["Duvle"] = "duv", ["Duwai"] = "dbp", ["Duwet"] = "gve", ["Dwang"] = "nnu", ["Dyaabugay"] = "dyy", ["Dyaberdyaber"] = "dyb", ["Dyan"] = "dya", ["Dyangadi"] = "dyn", ["Dyirbal"] = "dbl", ["Dyugun"] = "dyd", ["Dyula"] = "dyu", ["Dza"] = "jen", ["Dzala"] = "dzl", ["Dzando"] = "dzn", ["Dzao Min"] = "bpn", ["Dzodinka"] = "add", ["Dzuun"] = "dnn", ["Dâw"] = "kwa", ["E"] = "eee", ["E'ma Buyang"] = "yzg", ["Early Old Oghuz"] = "trk-eog", ["Early Tripuri"] = "xtr", ["East Damar"] = "dmr", ["ฟรังโกเนียตะวันออก"] = "vmf", ["East Futuna"] = "fud", ["East Kewa"] = "kjs", ["East Limba"] = "lma", ["East Masela"] = "vme", ["East Nyala"] = "nle", ["East Tarangan"] = "tre", ["East Yugur"] = "yuy", ["Eastern Acipa"] = "acp", ["Eastern Arrernte"] = "aer", ["Eastern Bolivian Guarani"] = "gui", ["Eastern Bontoc"] = "ebk", ["Eastern Canadian Inuktitut"] = "ike", ["Eastern Durango Nahuatl"] = "azd", ["Eastern Gorkha Tamang"] = "tge", ["Eastern Gurung"] = "ggn", ["Eastern Highland Chatino"] = "cly", ["Eastern Highland Otomi"] = "otm", ["Eastern Huasteca Nahuatl"] = "nhe", ["Eastern Huishui Hmong"] = "hme", ["Eastern Karaboro"] = "xrb", ["Eastern Keres"] = "kee", ["Eastern Khanty"] = "kca-eas", ["Eastern Krahn"] = "kqo", ["Eastern Lalu"] = "yit", ["Eastern Magar"] = "mgp", ["Eastern Maninkakan"] = "emk", ["Eastern Mari"] = "mhr", ["Eastern Meohang"] = "emg", ["Eastern Mnong"] = "mng", ["Eastern Muria"] = "emu", ["Eastern Ngad'a"] = "nea", ["Eastern Nisu"] = "nos", ["Eastern Ojibwa"] = "ojg", ["Eastern Parbate Kham"] = "kif", ["Eastern Penan"] = "pez", ["Eastern Pomo"] = "peb", ["Eastern Qiandong Miao"] = "hmq", ["Eastern Subanun"] = "sfe", ["Eastern Tamang"] = "taj", ["Eastern Tawbuid"] = "bnj", ["Eastern Xiangxi Miao"] = "muq", ["Eastern Xwla Gbe"] = "gbx", ["Ebira"] = "igb", ["Eblaite"] = "xeb", ["Ebrié"] = "ebr", ["Ebughu"] = "ebg", ["Ecuadorian Sign Language"] = "ecs", ["Ede Cabe"] = "cbj", ["Ede Ica"] = "ica", ["Ede Idaca"] = "idd", ["Ede Ije"] = "ijj", ["Ede Nago"] = "nqg", ["Edera Awyu"] = "awy", ["Edo"] = "bin", ["Edolo"] = "etr", ["Edopi"] = "dbf", ["Efai"] = "efa", ["Efe"] = "efe", ["Efik"] = "efi", ["Efutop"] = "ofu", ["Ega"] = "ega", ["Eggon"] = "ego", ["Egyptian Sign Language"] = "esl", ["Ehueun"] = "ehu", ["Eipomek"] = "eip", ["Eitiep"] = "eit", ["Ejagham"] = "etu", ["Ejamat"] = "eja", ["Ekajuk"] = "eka", ["Ekari"] = "ekg", ["Ekele"] = "khy", ["Eki"] = "eki", ["Ekit"] = "eke", ["Ekpeye"] = "ekp", ["El Alto Zapotec"] = "zpp", ["El Hugeirat"] = "elh", ["El Molo"] = "elo", ["Elamite"] = "elx", ["Eleme"] = "elm", ["Elepi"] = "ele", ["Elip"] = "ekm", ["Elkei"] = "elk", ["Eloi"] = "art-elo", ["Elotepec Zapotec"] = "zte", ["Eloyi"] = "afo", ["Elseng"] = "mrf", ["Elu"] = "elu", ["Elymian"] = "xly", ["Emae"] = "mmw", ["Emai"] = "ema", ["Eman"] = "emn", ["Embaloh"] = "emb", ["Emberá-Baudó"] = "bdc", ["Emberá-Catío"] = "cto", ["Emberá-Chamí"] = "cmi", ["Emberá-Tadó"] = "tdc", ["Embu"] = "ebu", ["Emem"] = "enr", ["Emerillon"] = "eme", ["Emplawas"] = "emw", ["En"] = "enc", ["Enawené-Nawé"] = "unk", ["Ende"] = "end", ["Enga"] = "enq", ["Engenni"] = "enn", ["Enggano"] = "eno", ["Enlhet"] = "enl", ["Enrekang"] = "ptt", ["Enu"] = "enu", ["Enwan"] = "env", ["Enwang"] = "enw", ["Enxet"] = "enx", ["Enya"] = "gey", ["Eotile"] = "eot", ["Epena"] = "sja", ["Epi-Olmec"] = "xep", ["Epie"] = "epi", ["Epigraphic Mayan"] = "emy", ["Eravallan"] = "era", ["Erave"] = "kjy", ["Ere"] = "twp", ["Erie"] = "iro-ere", ["Eritai"] = "ert", ["Erokwanas"] = "erw", ["Erre"] = "err", ["Erromintxela"] = "emx", ["Ersu"] = "ers", ["Eruwa"] = "erh", ["Esan"] = "ish", ["Ese"] = "mcq", ["Ese Ejja"] = "ese", ["Esimbi"] = "ags", ["Eskayan"] = "esy", ["Esmeralda"] = "sai-esm", ["Esselen"] = "esq", ["Estado de México Otomi"] = "ots", ["Estonian Sign Language"] = "eso", ["Esuma"] = "esm", ["Etchemin"] = "etc", ["Etebi"] = "etb", ["Eten"] = "etx", ["Eteocretan"] = "ecr", ["Eteocypriot"] = "ecy", ["Ethiopian Sign Language"] = "eth", ["Etkywan"] = "ich", ["Eton (Cameroon)"] = "eto", ["Eton (Vanuatu)"] = "etn", ["Etulo"] = "utr", ["Evant"] = "bzz", ["Ewage-Notu"] = "nou", ["Ewarhuyana"] = "sai-ewa", ["Ewondo"] = "ewo", ["Eyak"] = "eya", ["Ezaa"] = "eza", ["Fagani"] = "faf", ["Faire Atta"] = "azt", ["Faita"] = "faj", ["Faiwol"] = "fai", ["Fakkanci"] = "gel", ["Falam Chin"] = "cfm", ["Fali"] = "fli", ["Fam"] = "fam", ["Fanagalo"] = "fng", ["Fanamaket"] = "bjp", ["Fang (Bantu)"] = "fan", ["Fang (Beboid)"] = "fak", ["Fania"] = "fni", ["Far Western Muria"] = "fmu", ["ฟราฟรา"] = "gur", ["Fas"] = "fqs", ["Fasu"] = "faa", ["Fataleka"] = "far", ["Fataluku"] = "ddg", ["Fayu"] = "fau", ["Fe'fe'"] = "fmp", ["Fedan"] = "pdn", ["Fembe"] = "agl", ["Fer"] = "kah", ["Feroge"] = "fer", ["Filomena Mata-Coahuitlán Totonac"] = "tlp", ["Fingallian"] = "gmw-fin", ["Finisterre Yau"] = "yuw", ["Finnish Sign Language"] = "fse", ["Finnish-Swedish Sign Language"] = "fss", ["Finongan"] = "fag", ["Fipa"] = "fip", ["Firan"] = "fir", ["Fiwaga"] = "fiw", ["Flemish Sign Language"] = "vgt", ["Flinders Island"] = "fln", ["Foau"] = "flh", ["Fogaha"] = "ber-fog", ["Foi"] = "foi", ["Foia Foia"] = "ffi", ["Folopa"] = "ppo", ["Foma"] = "fom", ["Fon"] = "fon", ["Fongoro"] = "fgr", ["Foodo"] = "fod", ["Forak"] = "frq", ["Fordata"] = "frd", ["Fore"] = "for", ["Forest Enets"] = "enf", ["Forest Nenets"] = "yrk-for", ["Fortsenal"] = "frt", ["Fox"] = "sac", ["ฟร็องช์-กงเต"] = "roa-fcm", ["Francisco León Zoque"] = "zos", ["French Belgian Sign Language"] = "sfb", ["French Sign Language"] = "fsl", ["Fuliiru"] = "flr", ["Fulniô"] = "fun", ["Fum"] = "fum", ["Fungwa"] = "ula", ["Fur"] = "fvr", ["Furu"] = "fuu", ["Futuna-Aniwa"] = "fut", ["Fuyug"] = "fuy", ["Fwe"] = "fwe", ["Fwâi"] = "fwa", ["Fyam"] = "pym", ["Fyer"] = "fie", ["Ga"] = "gaa", ["Ga'anda"] = "gqa", ["Ga'dang"] = "gdg", ["Gaa"] = "ttb", ["Gaam"] = "tbi", ["Gabadi"] = "kbt", ["Gabi"] = "gbw", ["Gabri"] = "gab", ["Gabrielino-Fernandeño"] = "xgf", ["Gadang"] = "gdk", ["Gaddang"] = "gad", ["Gaddi"] = "gbk", ["Gade"] = "ged", ["Gadjerawang"] = "gdh", ["Gadsup"] = "gaj", ["Gafat"] = "gft", ["Gagadu"] = "gbu", ["Gagnoa Bété"] = "btg", ["Gahri"] = "bfu", ["Gaikundi"] = "gbf", ["Gaina"] = "gcn", ["Gal"] = "gap", ["Galambu"] = "glo", ["Galatian"] = "xga", ["Galela"] = "gbi", ["Galeya"] = "gar", ["Galice"] = "gce", ["Galindian"] = "xgl", ["Gallaecian"] = "cel-gal", ["Gallo"] = "roa-gal", ["Gallo-Italic of Basilicata"] = "roa-gib", ["Gallo-Italic of Sicily"] = "roa-gis", ["Gallurese"] = "sdn", ["Galo"] = "adl", ["Galoli"] = "gal", ["Gamale Kham"] = "kgj", ["Gambera"] = "gma", ["Gamela"] = "sai-gam", ["Gamilaraay"] = "kld", ["Gamit"] = "gbl", ["Gamkonora"] = "gak", ["Gamo-Ningi"] = "bte", ["Gana"] = "gnq", ["Ganang"] = "gne", ["Gane"] = "gzn", ["Ganggalida"] = "gcd", ["Ganglau"] = "ggl", ["Gangte"] = "gnb", ["Gangulu"] = "gnl", ["Gants"] = "gao", ["Ganza"] = "gza", ["Ganzi"] = "gnz", ["Gao"] = "gga", ["Gapapaiwa"] = "pwg", ["Garawa"] = "wrk", ["Garhwali"] = "gbm", ["Garifuna"] = "cab", ["Garingbal"] = "xgi", ["Garre"] = "gex", ["Garus"] = "gyb", ["Garza"] = "xgr", ["Gashowu Yokuts"] = "yok-gsy", ["Gata'"] = "gaq", ["Gavak"] = "dmc", ["Gavar"] = "gou", ["Gavião do Jiparaná"] = "gvo", ["Gawar-Bati"] = "gwt", ["Gawwada"] = "gwd", ["Gaya"] = "zra", ["Gayil"] = "gyl", ["Gayo"] = "gay", ["Gayón"] = "sai-gay", ["Gbagyi"] = "gbr", ["Gban"] = "ggu", ["Gbanu"] = "gbv", ["Gbanziri"] = "gbg", ["Gbari"] = "gby", ["Gbaya-Bossangoa"] = "gbp", ["Gbaya-Bozoum"] = "gbq", ["Gbaya-Mbodomo"] = "gmm", ["Gbayi"] = "gyg", ["Gbesi Gbe"] = "gbs", ["Gbii"] = "ggb", ["Gbin"] = "xgb", ["Gbiri-Niragu"] = "grh", ["Gboloo Grebo"] = "gec", ["Gciriku"] = "diu", ["Gcwi"] = "gwj", ["Ge"] = "hmj", ["Geba Karen"] = "kvq", ["Gebe"] = "gei", ["Gedaged"] = "gdd", ["Gedeo"] = "drs", ["Geji"] = "gji", ["Geko Karen"] = "ghk", ["Gela"] = "nlg", ["Gele'"] = "sbc", ["Geme"] = "geq", ["Gen"] = "gej", ["Gende"] = "gaf", ["Gengle"] = "geg", ["Gepo"] = "ygp", ["Gera"] = "gew", ["Gerka"] = "gek", ["German Sign Language"] = "gsg", ["Geruma"] = "gea", ["Geser-Gorom"] = "ges", ["Geshiza"] = "ero-gsz", ["Gey"] = "guv", ["Ghadames"] = "gha", ["Ghanaian Sign Language"] = "gse", ["Ghandruk Sign Language"] = "gds", ["Ghanongga"] = "ghn", ["Ghari"] = "gri", ["Ghayavi"] = "bmk", ["Ghera"] = "ghr", ["Ghomala'"] = "bbj", ["Ghomara"] = "gho", ["Ghotuo"] = "aaa", ["Ghulfan"] = "ghl", ["Giangan"] = "bgi", ["Gibanawa"] = "gib", ["Gidar"] = "gid", ["Gikyode"] = "acd", ["Gilaki"] = "glk", ["Gilima"] = "gix", ["Gimi (Austronesian)"] = "gip", ["Gimi (Papuan)"] = "gim", ["Gimme"] = "kmp", ["Gimnime"] = "gmn", ["Ginuman"] = "gnm", ["Girawa"] = "bbr", ["Girirra"] = "gii", ["Giryama"] = "nyf", ["Githabul"] = "gih", ["Gitua"] = "ggt", ["Gitxsan"] = "git", ["Giyug"] = "giy", ["Gizrra"] = "tof", ["Glaro-Twabo"] = "glr", ["Glavda"] = "glw", ["Glio-Oubi"] = "oub", ["Glosa"] = "igs", ["Gnau"] = "gnu", ["Goa'uld"] = "art-gld", ["Goaria"] = "gig", ["Gobasi"] = "goi", ["Gobu"] = "gox", ["Godié"] = "god", ["Godoberi"] = "gdo", ["Godwari"] = "gdx", ["Goemai"] = "ank", ["Gogo"] = "gog", ["Gogodala"] = "ggw", ["Goguryeo"] = "zkg", ["Gojri"] = "gju", ["Gokana"] = "gkn", ["Gokhy"] = "tbq-gkh", ["Gola"] = "gol", ["Golin"] = "gvf", ["Golpa"] = "lja", ["Gone Dau"] = "goo", ["Gongduk"] = "goe", ["Gonja"] = "gjn", ["Goo"] = "gov", ["Gor"] = "gqr", ["Gorakor"] = "goc", ["Gorap"] = "goq", ["Goreng"] = "xgg", ["Gorontalo"] = "gor", ["Gorovu"] = "grq", ["Gorwaa"] = "gow", ["Gottscheerish"] = "gmw-gts", ["Goundo"] = "goy", ["Gourmanchéma"] = "gux", ["Gowlan"] = "goj", ["Gowro"] = "gwf", ["Gozarkhani"] = "goz", ["Grangali"] = "nli", ["Grass Koiari"] = "kbk", ["Greek Sign Language"] = "gss", ["Green Gelao"] = "giq", ["Grenadian Creole English"] = "gcl", ["Gresi"] = "grs", ["Groma"] = "gro", ["Gros Ventre"] = "ats", ["Gua"] = "gwx", ["Guachí"] = "sai-gua", ["Guahibo"] = "guh", ["Guajajára"] = "gub", ["Guajá"] = "gvj", ["Guambiano"] = "gum", ["Guamo"] = "sai-gmo", ["Guanano"] = "gvc", ["Guanche"] = "gnc", ["Guarayu"] = "gyr", ["Guatemalan Sign Language"] = "gsm", ["Guató"] = "gta", ["Guayabero"] = "guo", ["Guazacapán"] = "nai-guz", ["Gudang"] = "xgd", ["Gudanji"] = "nji", ["Gude"] = "gde", ["Gudu"] = "gdu", ["Guduf-Gava"] = "gdf", ["Guerrero Amuzgo"] = "amu", ["Guerrero Nahuatl"] = "ngu", ["Guevea de Humboldt Zapotec"] = "zpg", ["Gugadj"] = "ggd", ["Gugu Badhun"] = "gdc", ["Gugu Warra"] = "wrw", ["Guhu-Samane"] = "ghs", ["Guianese Creole"] = "gcr", ["Guiberoua Bété"] = "bet", ["Guinau"] = "awd-gnu", ["Guinea Kpelle"] = "gkp", ["Guinea-Bissau Sign Language"] = "lgs", ["Guinean Sign Language"] = "gus", ["Guiqiong"] = "gqi", ["Gula"] = "glu", ["Gula'alaa"] = "gmb", ["Gulay"] = "gvl", ["Gule"] = "gly", ["Gulf Arabic"] = "afb", ["Gullah"] = "gul", ["Gumalu"] = "gmu", ["Gumatj"] = "gnn", ["Gumawana"] = "gvs", ["Gumuz"] = "guk", ["Gun"] = "guw", ["Gundi"] = "gdi", ["Gunditjmara"] = "gjm", ["Gundungurra"] = "xrd", ["Gungabula"] = "gyf", ["Gungu"] = "rub", ["Guntai"] = "gnt", ["Gunu"] = "yas", ["Gunwinggu"] = "gup", ["Gunya"] = "gyy", ["Gupa-Abawa"] = "gpa", ["Gupapuyngu"] = "guf", ["Gur Lama"] = "las", ["Guragone"] = "gge", ["Guramalum"] = "grz", ["Gurani"] = "hac", ["Gureng Gureng"] = "gnr", ["Gurgula"] = "ggg", ["Guriaso"] = "grx", ["Gurindji"] = "gue", ["Gurindji Kriol"] = "gjr", ["Gurmana"] = "gvm", ["Guro"] = "goa", ["Guruntum"] = "grd", ["Gusan"] = "gsn", ["Gusii"] = "guz", ["Gusilay"] = "gsl", ["Gutnish"] = "gmq-gut", ["Guugu Yimidhirr"] = "kky", ["Guwa"] = "xgw", ["Guwamu"] = "gwu", ["Guwar"] = "aus-guw", ["Guya"] = "gka", ["Guyanese Creole English"] = "gyn", ["Guyani"] = "gvy", ["Guébie"] = "gie", ["Gvoko"] = "ngs", ["Gwa"] = "gwb", ["Gwahatike"] = "dah", ["Gwak"] = "jgk", ["Gwamhi-Wuri"] = "bga", ["Gwandara"] = "gwn", ["Gwara"] = "alv-gwa", ["Gweda"] = "grw", ["Gweno"] = "gwe", ["Gwere"] = "gwr", ["Gwich'in"] = "gwi", ["Gyalsumdo"] = "gyo", ["Gyele"] = "gyi", ["Gyem"] = "gye", ["Güenoa"] = "sai-gue", ["Habu"] = "hbu", ["Hachijō"] = "jpx-hcj", ["Hadiyya"] = "hdy", ["Hadoti"] = "hoj", ["Hadrami"] = "xhd", ["Hadza"] = "hts", ["Haeke"] = "aek", ["Hahon"] = "hah", ["Haida"] = "hai", ["Haigwai"] = "hgw", ["Hailufeng Min"] = "nan-hlh", ["Hainyaxo Bozo"] = "bzx", ["Haiphong Sign Language"] = "haf", ["Haisla"] = "has", ["Haitian Vodoun Culture Language"] = "hvc", ["Haiǁom"] = "hgm", ["Haji"] = "hji", ["Hakö"] = "hao", ["Halang"] = "hal", ["Halang Doan"] = "hld", ["Halbi"] = "hlb", ["Halia"] = "hla", ["Halkomelem"] = "hur", ["Hamap"] = "hmu", ["Hamba"] = "hba", ["Hamer-Banna"] = "amf", ["Hamtai"] = "hmt", ["Hanga"] = "hag", ["Hanga Hundi"] = "wos", ["Hani"] = "hni", ["Hanoi Sign Language"] = "hab", ["Harami"] = "xha", ["Harappan"] = "xiv", ["Harari"] = "har", ["Haraza"] = "nub-har", ["Harijan Kinnauri"] = "kjo", ["Haroi"] = "hro", ["Harsusi"] = "hss", ["Haruai"] = "tmd", ["Haruku"] = "hrk", ["หริยาณวี"] = "bgc", ["Harzani"] = "hrz", ["Hasaitic"] = "sem-has", ["Hasha"] = "ybj", ["Hassaniya Arabic"] = "mey", ["Hatam"] = "had", ["Hattic"] = "xht", ["Hausa Sign Language"] = "hsl", ["Haush"] = "sai-hau", ["Havasupai-Walapai-Yavapai"] = "yuf", ["Haveke"] = "hvk", ["Havu"] = "hav", ["Hawai'i Pidgin Sign Language"] = "hps", ["Hawaiian Creole"] = "hwc", ["Haya"] = "hay", ["Hdi"] = "xed", ["Hehe"] = "heh", ["Heiban"] = "hbn", ["Heiltsuk"] = "hei", ["Helong"] = "heg", ["Hema"] = "nix", ["Hemba"] = "hem", ["Herdé"] = "hed", ["Hermit"] = "llf", ["Hernican"] = "xhr", ["Hewa"] = "ham", ["Heyo"] = "auk", ["Hibito"] = "hib", ["Hidatsa"] = "hid", ["Higaonon"] = "mba", ["High Valyrian"] = "art-vlh", ["Highland Konjo"] = "kjk", ["Highland Oaxaca Chontal"] = "chd", ["Highland Popoluca"] = "poi", ["Highland Puebla Nahuatl"] = "azz", ["Highland Totonac"] = "tos", ["Hijuk"] = "hij", ["Hill Maria"] = "mrr", ["Himarimã"] = "hir", ["Hindi Dogri"] = "dgo", ["Hinduri"] = "hii", ["Hinukh"] = "gin", ["Hismaic"] = "sem-his", ["Hitchiti"] = "nai-hit", ["Hittite"] = "hit", ["Hitu"] = "htu", ["Hiw"] = "hiw", ["Hixkaryana"] = "hix", ["Hlepho Phowa"] = "yhl", ["Hlersu"] = "hle", ["Hmar"] = "hmr", ["Hmong Don"] = "hmf", ["Hmong Dô"] = "hmv", ["Hmong Shua"] = "hmz", ["Hmwaveke"] = "mrk", ["Ho"] = "hoc", ["Ho Chi Minh City Sign Language"] = "hos", ["Hoava"] = "hoa", ["Hobyót"] = "hoh", ["Hoia Hoia"] = "hhi", ["Holikachuk"] = "hoi", ["Holiya"] = "hoy", ["Holma"] = "hod", ["Holoholo"] = "hoo", ["Holu"] = "hol", ["Homa"] = "hom", ["Honduran Lenca"] = "len", ["Honduras Sign Language"] = "hds", ["Hone"] = "juh", ["Hong Kong Sign Language"] = "hks", ["Honi"] = "how", ["Hopi"] = "hop", ["Horned Miao"] = "hrm", ["Horo"] = "hor", ["Horom"] = "hoe", ["Hote"] = "hot", ["Hoti"] = "hti", ["Hovongan"] = "hov", ["Hoyahoya"] = "hhy", ["Hozo"] = "hoz", ["Hpon"] = "hpo", ["Hrangkhol"] = "hra", ["Hruso"] = "hru", ["Hrê"] = "hre", ["Hu"] = "huo", ["Huachipaeri"] = "hug", ["Huambisa"] = "hub", ["Huaorani"] = "auc", ["Huarijio"] = "var", ["Huaulu"] = "hud", ["Huautla Mazatec"] = "mau", ["Huave"] = "huv", ["Huaxcaleca Nahuatl"] = "nhq", ["Huba"] = "hbb", ["Huehuetla Tepehua"] = "tee", ["Huetar"] = "cba-hue", ["Huichol"] = "hch", ["Huilliche"] = "huh", ["Huitepec Mixtec"] = "mxs", ["Huizhou"] = "czh", ["Hukumina"] = "huw", ["Hula"] = "hul", ["Hulaulá"] = "huy", ["Huli"] = "hui", ["Hulung"] = "huk", ["Humburi Senni"] = "hmb", ["Humene"] = "huf", ["Hun"] = "uth", ["Hunde"] = "hke", ["Hung"] = "hnu", ["Hungana"] = "hum", ["Hungarian Sign Language"] = "hsh", ["Hungworo"] = "nat", ["Hunjara-Kaina Ke"] = "hkk", ["Hunnic"] = "xhc", ["Hunzib"] = "huz", ["Hupa"] = "hup", ["Hupdë"] = "jup", ["Hupla"] = "hap", ["Hutterisch"] = "geh", ["Hwana"] = "hwo", ["Hya"] = "hya", ["Hyam"] = "jab", ["Hän"] = "haa", ["Hértevin"] = "hrt", ["I-Wak"] = "iwk", ["Iaai"] = "iai", ["Iamalele"] = "yml", ["Iatmul"] = "ian", ["Iau"] = "tmu", ["Ibali Teke"] = "tek", ["Ibaloi"] = "ibl", ["Ibanag"] = "ibg", ["Ibani"] = "iby", ["Ibatan"] = "ivb", ["Iberian"] = "xib", ["Ibibio"] = "ibb", ["Ibino"] = "ibn", ["Iboko"] = "bkp", ["Ibu"] = "ibu", ["Ibuoro"] = "ibr", ["Icelandic Sign Language"] = "icl", ["Iceve-Maci"] = "bec", ["Idakho-Isukha-Tiriki"] = "ida", ["Idaté"] = "idt", ["Idere"] = "ide", ["Idesa"] = "ids", ["Idi"] = "idi", ["Idiom Neutral"] = "mis-idn", ["Idoma"] = "idu", ["Idon"] = "idc", ["Idu"] = "clk", ["Idun"] = "ldb", ["Iduna"] = "viv", ["Ifo"] = "iff", ["Ifè"] = "ife", ["Igala"] = "igl", ["Igana"] = "igg", ["Igede"] = "ige", ["Ignaciano"] = "ign", ["Igo"] = "ahl", ["Iguta"] = "nar", ["Igwe"] = "igw", ["Iha"] = "ihp", ["Ihievbe"] = "ihi", ["Ija-Zuba"] = "vki", ["Ik"] = "ikx", ["Ika"] = "ikk", ["Ikaranggal"] = "ikr", ["Ikizu"] = "ikz", ["Iko"] = "iki", ["Ikobi-Mena"] = "meb", ["Ikoma"] = "ntk", ["Ikpeng"] = "txi", ["Ikpeshi"] = "ikp", ["Ikposo"] = "kpo", ["Iku-Gora-Ankwa"] = "ikv", ["Ikulu"] = "ikl", ["Ikwere"] = "ikw", ["Ikwo"] = "iqw", ["Ila"] = "ilb", ["Ile Ape"] = "ila", ["Ilgar"] = "ilg", ["Ili Turki"] = "ili", ["Ili'uun"] = "ilu", ["Ilianen Manobo"] = "mbi", ["Illyrian"] = "xil", ["Ilongot"] = "ilk", ["Ilue"] = "ilv", ["Ilwana"] = "mlk", ["Imbongu"] = "imo", ["Imonda"] = "imn", ["Imroing"] = "imr", ["Inabaknon"] = "abx", ["Inapang"] = "mzu", ["Indanga"] = "bnt-ind", ["Indo-Portuguese"] = "idb", ["Indri"] = "idr", ["Indus Kohistani"] = "mvy", ["Inebu One"] = "oin", ["Ineseño"] = "inz", ["Inga"] = "inb", ["Inlaod Itneg"] = "iti", ["Inoke-Yate"] = "ino", ["Inonhan"] = "loc", ["Inor"] = "ior", ["Inpui Naga"] = "nkf", ["International Sign"] = "ils", ["Interslavic"] = "isv", ["Intha"] = "int", ["Inuinnaqtun"] = "esx-inq", ["Inuit Sign Language"] = "iks", ["Inuktun"] = "esx-ink", ["Inupiaq"] = "ik", ["Inuvialuktun"] = "ikt", ["Ipai"] = "nai-ipa", ["Ipalapa Amuzgo"] = "azm", ["Ipiko"] = "ipo", ["Ipili"] = "ipi", ["Ipulo"] = "ass", ["Iquito"] = "iqu", ["Ir"] = "irr", ["Irantxe"] = "irn", ["Iraqw"] = "irk", ["Irarutu"] = "irh", ["Iraya"] = "iry", ["Iresim"] = "ire", ["Iriga Bicolano"] = "bto", ["Irish Sign Language"] = "isg", ["Irula"] = "iru", ["Isabi"] = "isa", ["Isanzu"] = "isn", ["Isarog Agta"] = "agk", ["Isaurian"] = "mis-isa", ["Isconahua"] = "isc", ["Isebe"] = "igo", ["Ishkashimi"] = "isk", ["Isinai"] = "inn", ["Isirawa"] = "srl", ["Islander Creole English"] = "icr", ["Isnag"] = "isd", ["Isoko"] = "iso", ["Israeli Sign Language"] = "isr", ["Isthmus Mixe"] = "mir", ["Isthmus Zapotec"] = "zai", ["อิสโตร-โรมาเนีย"] = "ruo", ["Isu"] = "isu", ["Isubu"] = "szv", ["Italian Sign Language"] = "ise", ["Italiot Greek"] = "grk-ita", ["Itawit"] = "itv", ["Itelmen"] = "itl", ["Itene"] = "ite", ["Iteri"] = "itr", ["Itik"] = "itx", ["Ito"] = "itw", ["Itonama"] = "ito", ["Itsekiri"] = "its", ["Itu Mbon Uzo"] = "itm", ["Itundujia Mixtec"] = "mce", ["Itza'"] = "itz", ["Ivatan"] = "ivv", ["Iwaidja"] = "ibd", ["Iwal"] = "kbm", ["Iwam"] = "iwm", ["Iwur"] = "iwo", ["Ixcatec"] = "ixc", ["Ixcatlán Mazatec"] = "mzi", ["Ixil"] = "ixl", ["Ixtayutla Mixtec"] = "vmj", ["Ixtenco Otomi"] = "otz", ["Iyayu"] = "iya", ["Iyive"] = "uiv", ["Iyo"] = "nca", ["Iyo'wujwa Chorote"] = "crq", ["Iyojwa'ja Chorote"] = "crt", ["Izere"] = "izr", ["Izi"] = "izz", ["Izi-Ezaa-Ikwo-Mgbo"] = "izi", ["Izon"] = "ijc", ["Izora"] = "cbo", ["Iñapari"] = "inp", ["Jabem"] = "jae", ["Jabutí"] = "jbt", ["Jad"] = "jda", ["Jadgali"] = "jdg", ["Jah Hut"] = "jah", ["Jahanka"] = "jad", ["Jair Awyu"] = "awv", ["Jakaltek"] = "jac", ["Jakati"] = "jat", ["Jalapa de Díaz Mazatec"] = "maj", ["Jalkunan"] = "bxl", ["Jamaican Country Sign Language"] = "jcs", ["Jamaican Sign Language"] = "jls", ["Jamamadí"] = "jaa", ["Jambi Malay"] = "jax", ["Jamiltepec Mixtec"] = "mxt", ["Jaminjung"] = "djd", ["Jamsay"] = "djm", ["Jamtish"] = "gmq-jmk", ["Jandavra"] = "jnd", ["Janday"] = "jan", ["Jangkang"] = "djo", ["Jangshung"] = "jna", ["Janji"] = "jni", ["Japanese Sign Language"] = "jsl", ["Japhug"] = "sit-jap", ["Japrería"] = "jru", ["Jaqaru"] = "jqr", ["Jara"] = "jaf", ["Jarai"] = "jra", ["Jarawa"] = "anq", ["Jaru"] = "ddj", ["Jassic"] = "ysc", ["Jaunsari"] = "jns", ["Javindo"] = "jvd", ["Jawe"] = "jaz", ["Jaya"] = "jyy", ["Jebero"] = "jeb", ["Jeh"] = "jeh", ["Jehai"] = "jhi", ["Jeikó"] = "sai-jko", ["Jemez"] = "tow", ["Jenaama Bozo"] = "bze", ["Jeng"] = "jeg", ["Jennu Kurumba"] = "xuj", ["Jere"] = "jer", ["Jeri Kuo"] = "jek", ["Jersey Dutch"] = "gmw-jdt", ["Jeru"] = "akj", ["Jerung"] = "jee", ["Jhankot Sign Language"] = "jhs", ["Jiba"] = "juo", ["Jibu"] = "jib", ["Jicarilla"] = "apj", ["Jie"] = "mis-jie", ["Jiiddu"] = "jii", ["Jilbe"] = "jie", ["Jili"] = "mgi", ["Jilim"] = "jil", ["Jimi"] = "jmi", ["Jimjimen"] = "jim", ["Jina"] = "jia", ["Jingulu"] = "jig", ["Jiongnai Bunu"] = "pnu", ["Jirajara"] = "sai-jrj", ["Jirel"] = "jul", ["Jiru"] = "jrr", ["Jita"] = "jit", ["Jizhao"] = "mis-jzh", ["Jju"] = "kaj", ["Joba"] = "job", ["Jofotek-Bromnya"] = "jbr", ["Jola-Fonyi"] = "dyo", ["Jola-Kasa"] = "csk", ["Jonkor Bourmataguil"] = "jeu", ["Jordanian Sign Language"] = "jos", ["Jorá"] = "jor", ["Jowulu"] = "jow", ["Ju"] = "juu", ["Juang"] = "jun", ["Juba Arabic"] = "pga", ["Judeo-Italian"] = "itk", ["Judeo-Persian"] = "jpr", ["Judeo-Tat"] = "jdt", ["Jukun Takum"] = "jbu", ["Jumaytepeque"] = "nai-jum", ["Jumjum"] = "jum", ["Jumla Sign Language"] = "jus", ["Jumli"] = "jml", ["Jungle Inga"] = "inj", ["Juquila Mixe"] = "mxq", ["Jur Modo"] = "bex", ["Juray"] = "juy", ["Jurúna"] = "jur", ["Jutiapa"] = "nai-jtp", ["Jutish"] = "jut", ["Juwal"] = "mwb", ["Juxtlahuaca Mixtec"] = "vmc", ["Juǀ'hoan"] = "ktz", ["Jwira-Pepesa"] = "jwi", ["Júma"] = "jua", ["K'iche'"] = "quc", ["Kaamba"] = "xku", ["Kaan"] = "ldl", ["Kaang Chin"] = "ckn", ["Kaansa"] = "gna", ["Kaapor Sign Language"] = "uks", ["Kaba"] = "ksp", ["Kabalai"] = "kvf", ["Kabatei"] = "xkp", ["Kabba-Laka"] = "lap", ["Kabishiana"] = "tup-kab", ["Kabiyé"] = "kbp", ["Kabola"] = "klz", ["Kabore One"] = "onk", ["Kabras"] = "lkb", ["Kaburi"] = "uka", ["Kabutra"] = "kbu", ["Kabwa"] = "cwa", ["Kabwari"] = "kcw", ["Kachama-Ganjule"] = "kcx", ["Kachari"] = "xac", ["Kachi Koli"] = "gjk", ["Kacipo-Balesi"] = "koe", ["Kaco'"] = "xkk", ["Kadai"] = "kzd", ["Kadar"] = "kej", ["Kadara"] = "kad", ["Kadaru"] = "kdu", ["Kadiwéu"] = "kbc", ["Kado"] = "kdv", ["Kadugli"] = "xtc", ["Kaduo"] = "ktp", ["Kaera"] = "jka", ["Kafa"] = "kbr", ["Kafoa"] = "kpu", ["Kagate"] = "syw", ["Kagayanen"] = "cgc", ["Kagoma"] = "kdm", ["Kagoro"] = "xkg", ["Kagulu"] = "kki", ["Kahe"] = "hka", ["Kahua"] = "agw", ["Kaian"] = "kct", ["Kaibobo"] = "kzb", ["Kaidipang"] = "kzp", ["Kaiep"] = "kbw", ["Kaikadi"] = "kep", ["Kaike"] = "kzq", ["Kaiku"] = "kkq", ["Kaimbulawa"] = "zka", ["Kaimbé"] = "xai", ["Kaingang"] = "kgp", ["Kairak"] = "ckr", ["Kairiru"] = "kxa", ["Kairui-Midiki"] = "krd", ["Kais"] = "kzm", ["Kaitag"] = "xdq", ["Kaivi"] = "kce", ["Kaiwá"] = "kgk", ["Kaiy"] = "tcq", ["Kajakse"] = "ckq", ["Kajali"] = "xkj", ["Kajaman"] = "kag", ["Kakabai"] = "kqf", ["Kakabe"] = "kke", ["Kakanda"] = "kka", ["Kaki Ae"] = "tbd", ["Kakihum"] = "kxe", ["Kako"] = "kkj", ["Kakwa"] = "keo", ["Kala"] = "kcl", ["Kala Lagaw Ya"] = "mwp", ["Kalaamaya"] = "lkm", ["Kalabakan"] = "kve", ["Kalabari"] = "ijn", ["Kalabra"] = "kzz", ["Kalaktang Monpa"] = "kkf", ["Kalam"] = "kmh", ["Kalami"] = "gwc", ["Kalamsé"] = "knz", ["Kalanadi"] = "wkl", ["Kalanga"] = "kck", ["Kalao"] = "kly", ["Kalapuya"] = "kyl", ["Kalarko"] = "kba", ["Kalasha"] = "kls", ["Kalasuri"] = "xme-kls", ["Kalašma"] = "ine-kal", ["Kalenjin"] = "kln", ["Kalinago"] = "crb", ["Kalkatungu"] = "ktg", ["Kalkoti"] = "xka", ["Kallawaya"] = "caw", ["Kalo Finnish Romani"] = "rmf", ["Kalou"] = "ywa", ["Kaluli"] = "bco", ["Kalumpang"] = "kli", ["Kam"] = "kdx", ["Kamakan"] = "vkm", ["Kamang"] = "woi", ["Kamano"] = "kbq", ["Kamantan"] = "kci", ["Kamar"] = "keq", ["Kamara"] = "jmr", ["Kamarian"] = "kzx", ["Kamaru"] = "kgx", ["Kamasa"] = "klp", ["Kamasau"] = "kms", ["Kamassian"] = "xas", ["Kamayo"] = "kyk", ["Kamayurá"] = "kay", ["Kamba"] = "kam", ["Kambaata"] = "ktb", ["Kambaira"] = "kyy", ["Kamberataro"] = "kbv", ["Kamberau"] = "irx", ["Kambiwá"] = "xbw", ["Kami"] = "kmi", ["Kamkata-viri"] = "bsh", ["Kamo"] = "kcq", ["Kamoro"] = "kgq", ["Kamta"] = "rkt", ["Kamu"] = "xmu", ["Kamula"] = "xla", ["Kamwe"] = "hig", ["Kanakanabu"] = "xnb", ["Kanakuru"] = "kna", ["Kanamari"] = "knm", ["Kanashi"] = "xns", ["Kanasi"] = "soq", ["Kandas"] = "kqw", ["Kandawo"] = "gam", ["Kande"] = "kbs", ["Kang"] = "kyp", ["Kanga"] = "kcp", ["Kangean"] = "kkv", ["Kanggape"] = "igm", ["Kangjia"] = "kxs", ["Kango"] = "kty", ["Kango-Sua"] = "kzy", ["Kangri"] = "xnr", ["Kaniet"] = "ktk", ["Kanikkaran"] = "kev", ["Kaningdon-Nindem"] = "kdp", ["Kaningi"] = "kzo", ["Kaningra"] = "knr", ["Kaninuwa"] = "wat", ["Kanite"] = "kmu", ["Kanjari"] = "kft", ["Kanju"] = "kbe", ["Kannada Kurumba"] = "kfi", ["Kannauji"] = "bjj", ["Kanowit"] = "kxn", ["Kanoé"] = "kxo", ["Kansa"] = "ksk", ["Kantosi"] = "xkt", ["Kanu"] = "khx", ["Kanufi"] = "kni", ["Kanyok"] = "kny", ["Kao"] = "kax", ["Kaonde"] = "kqn", ["Kap"] = "ykm", ["Kapauri"] = "khp", ["Kapin"] = "tbx", ["Kapinawá"] = "xpn", ["Kapingamarangi"] = "kpg", ["Kapriman"] = "dju", ["Kaptiau"] = "kbi", ["Kapya"] = "klo", ["Kaqchikel"] = "cak", ["Kaqchikel-K'iche' Mixed Language"] = "ckz", ["Kara (New Guinea)"] = "leu", ["Kara (Tanzania)"] = "reg", ["Karachay-Balkar"] = "krc", ["Karadjeri"] = "gbd", ["Karaga Mandaya"] = "mry", ["Karaim"] = "kdr", ["Karajá"] = "kpj", ["Karakhanid"] = "xqa", ["Karami"] = "xar", ["Karamojong"] = "kdj", ["Karang"] = "kzr", ["Karanga"] = "kth", ["Karankawa"] = "zkk", ["Karas"] = "kgv", ["Karata"] = "kpt", ["Karawa"] = "xrw", ["Karbi"] = "mjw", ["Kare (Central Africa)"] = "kbn", ["Kare (New Guinea)"] = "kmf", ["Karekare"] = "kai", ["Karey"] = "kyd", ["Kari"] = "kbj", ["Kari'na"] = "car", ["Karingani"] = "kgn", ["Karipuna"] = "kuq", ["Karipúna Creole French"] = "kmv", ["Kariri"] = "kzw", ["Karitiâna"] = "ktn", ["Kariya"] = "kil", ["Kariyarra"] = "vka", ["Karkar-Yuri"] = "yuj", ["Karkin"] = "krb", ["Karko"] = "kko", ["Karnai"] = "bbv", ["Karo"] = "kxh", ["Karo Batak"] = "btx", ["Karok"] = "kyh", ["Karolanos"] = "kyn", ["Karon"] = "krx", ["Karon Dori"] = "kgw", ["Karore"] = "xkx", ["Karranga"] = "xrq", ["Karuwali"] = "rxw", ["Kasanga"] = "ccj", ["Kasem"] = "xsm", ["Kashaya"] = "kju", ["Kasiguranin"] = "ksn", ["Kaska"] = "kkz", ["Kaskean"] = "zsk", ["Kaskihá"] = "gva", ["Kassite"] = "mis-kas", ["Kassonke"] = "kao", ["Kasua"] = "khs", ["Kataang"] = "kgd", ["Katabaga"] = "ktq", ["Katawixi"] = "xat", ["Katembri"] = "sai-kat", ["Kathlamet"] = "nai-kat", ["Kathoriya Tharu"] = "tkt", ["Kathu"] = "ykt", ["Katkari"] = "kfu", ["Katla"] = "kcr", ["Kato"] = "ktw", ["Katso"] = "kaf", ["Katua"] = "kta", ["Katukina"] = "knt", ["Kaulong"] = "pss", ["Kaur"] = "vkk", ["Kaure"] = "bpp", ["Kaurna"] = "zku", ["Kauwera"] = "xau", ["Kavet"] = "krv", ["Kawacha"] = "kcb", ["Kawaiisu"] = "xaw", ["Kawe"] = "kgb", ["Kawishana"] = "awd-kaw", ["Kawésqar"] = "alc", ["Kaxararí"] = "ktx", ["Kaxuyana"] = "kbb", ["Kayabí"] = "kyz", ["Kayagar"] = "kyt", ["Kayan Mahakam"] = "xay", ["Kayan River Kayan"] = "xkn", ["Kayapa Kallahan"] = "kak", ["Kayapó"] = "txu", ["Kayardild"] = "gyd", ["Kayeli"] = "kzl", ["Kayong"] = "kxy", ["Kayort"] = "kyv", ["Kaytetye"] = "gbb", ["Kayupulau"] = "kzu", ["Kazukuru"] = "kzk", ["Ke'o"] = "xxk", ["Keak"] = "keh", ["Keapara"] = "khz", ["Kedang"] = "ksx", ["Keder"] = "kdy", ["Kehu"] = "khh", ["Kei"] = "kei", ["Keiga"] = "kec", ["Kein"] = "bmh", ["Keiyo"] = "eyo", ["Kela-Yela"] = "kel", ["Kelabit"] = "kzi", ["Kelantan Peranakan Hokkien"] = "mis-hkl", ["Keley-I Kallahan"] = "ify", ["Keliko"] = "kbo", ["Kelo"] = "xel", ["Kelon"] = "kyo", ["Kemak"] = "kem", ["Kembayan"] = "xem", ["Kemberano"] = "bzp", ["Kembra"] = "xkw", ["Kemezung"] = "dmo", ["Kemi Sami"] = "sjk", ["Kemiehua"] = "kfj", ["Kemtuik"] = "kmt", ["Kenaboi"] = "xbn", ["Kenati"] = "gat", ["Kendayan"] = "knx", ["Kendeje"] = "klf", ["Kendem"] = "kvm", ["Kenga"] = "kyq", ["Keningau Murut"] = "kxi", ["Keninjal"] = "knl", ["Kensiu"] = "kns", ["Kenswei Nsei"] = "ndb", ["Kenyan Sign Language"] = "xki", ["Kenyang"] = "ken", ["Kenyi"] = "lke", ["Keoru-Ahia"] = "xeu", ["Kepkiriwát"] = "kpn", ["Kepo'"] = "kuk", ["Kera"] = "ker", ["Kerak"] = "hhr", ["Kereho"] = "xke", ["Kerek"] = "krk", ["Kerewe"] = "ked", ["Kerewo"] = "kxz", ["Kerinci"] = "kvr", ["Kermanic"] = "xme-ker", ["Kesawai"] = "xes", ["Ket"] = "ket", ["Ketangalan"] = "kae", ["Kete"] = "kcv", ["Ketengban"] = "xte", ["Ketum"] = "ktt", ["Kewa"] = "kew", ["Keyagana"] = "kyg", ["Kgalagadi"] = "xkv", ["แฆแลจ"] = "klj", ["Khaling"] = "klr", ["Khamnigan Mongol"] = "ykh", ["Khamyang"] = "ksu", ["Khana"] = "ogo", ["Khandeshi"] = "khn", ["Khao"] = "xao", ["Kharam Naga"] = "kfw", ["Kharia"] = "khr", ["Kharia Thar"] = "ksy", ["Khayo"] = "lko", ["Khazar"] = "zkz", ["Khe"] = "kqg", ["Khehek"] = "tlx", ["Khengkha"] = "xkf", ["Khetrani"] = "xhe", ["Khezha Naga"] = "nkh", ["Khiamniungan Naga"] = "kix", ["Khinalug"] = "kjj", ["Khirwar"] = "kwx", ["Khisa"] = "kqm", ["Khlor"] = "llo", ["Khlula"] = "ykl", ["Khoekhoe"] = "naq", ["Khoibu Naga"] = "nkb", ["Khoini"] = "xkc", ["Kholok"] = "ktc", ["Kholosi"] = "inc-kho", ["Khorasani Turkish"] = "kmz", ["Khorezmian Turkic"] = "zkh", ["Khowar"] = "khw", ["Khroskyabs"] = "jiq", ["Khua"] = "xhv", ["Khuen"] = "khf", ["Khumi Chin"] = "cnk", ["Khvarshi"] = "khv", ["ฆวารัซม์"] = "xco", ["Khwe"] = "xuu", ["Kháng"] = "kjm", ["Kiautschou German Pidgin"] = "crp-kia", ["Kibala"] = "blv", ["Kibena"] = "bez", ["Kibet"] = "kie", ["Kibiri"] = "prm", ["Kichwa"] = "qwe-kch", ["Kickapoo"] = "kic", ["คิไก"] = "kzg", ["Kikami"] = "kcu", ["Kili"] = "tuw-kli", ["Kilit"] = "xme-klt", ["Kilivila"] = "kij", ["Kiliwa"] = "klb", ["Kilmeri"] = "kih", ["Kim"] = "kia", ["Kim Mun"] = "mji", ["Kimaama"] = "kig", ["Kimaragang"] = "kqr", ["Kimbu"] = "kiv", ["Kimbundu"] = "kmb", ["Kimki"] = "sbt", ["Kimré"] = "kqp", ["Kinabalian"] = "cbw", ["Kinalakna"] = "kco", ["Kinaray-a"] = "krj", ["Kinga"] = "zga", ["Kings River Yokuts"] = "yok-kry", ["Kinikinao"] = "gqn", ["Kinnauri"] = "kfk", ["Kintaq"] = "knq", ["Kinuku"] = "kkd", ["Kioko"] = "ues", ["Kiong"] = "kkm", ["Kiorr"] = "xko", ["Kiowa"] = "kio", ["Kipfokomo"] = "pkb", ["Kipsigis"] = "sgc", ["Kiput"] = "kyi", ["Kir-Balar"] = "kkr", ["Kire"] = "geb", ["Kirfi"] = "kks", ["Kirike"] = "okr", ["Kirikiri"] = "kiy", ["Kirya-Konzel"] = "fkk", ["Kis"] = "kis", ["Kisa"] = "lks", ["Kisan"] = "xis", ["Kisankasa"] = "kqh", ["Kisar"] = "kje", ["Kisi"] = "kiz", ["Kistane"] = "gru", ["Kita Maninkakan"] = "mwk", ["Kitanemuk"] = "azc-ktn", ["Kitembo"] = "tbt", ["Kitja"] = "gia", ["Kitsai"] = "kii", ["Kituba"] = "ktu", ["Kiunum"] = "wei", ["Kla"] = "lda", ["Klallam"] = "clm", ["Klamath-Modoc"] = "kla", ["Klao"] = "klu", ["Klias River Kadazan"] = "kqt", ["Klingon"] = "tlh", ["Knaanic"] = "czk", ["Ko"] = "fuj", ["Koalib"] = "kib", ["Koasati"] = "cku", ["Koba"] = "kpd", ["Kobiana"] = "kcj", ["Kobol"] = "kgu", ["Kobon"] = "kpw", ["Koch"] = "kdq", ["Kochila Tharu"] = "thq", ["Koda"] = "cdz", ["Kodaku"] = "ksz", ["Kodava"] = "kfa", ["Kodeoha"] = "vko", ["Kodi"] = "kod", ["Kodia"] = "kwp", ["Koenoem"] = "kcs", ["Kofa"] = "kso", ["Kofei"] = "kpi", ["Kofyar"] = "kwl", ["Kohin"] = "kkx", ["Kohistani Shina"] = "plk", ["Kohumono"] = "bcs", ["Koi"] = "kkt", ["Koibal"] = "zkb", ["Koireng"] = "nkd", ["Koitabu"] = "kqi", ["Koiwat"] = "kxt", ["Kok-Nar"] = "gko", ["Kok-Paponk"] = "okg", ["Kokata"] = "ktd", ["Koke"] = "kou", ["Koko-Bera"] = "kkp", ["Kokoda"] = "xod", ["Kokola"] = "kzn", ["Kokota"] = "kkk", ["Kol (Cameroon)"] = "biw", ["Kol (New Guinea)"] = "kol", ["Kola"] = "kvv", ["Kolami"] = "kfb", ["Kolbila"] = "klc", ["Kolhe"] = "ekl", ["Kolibugan Subanon"] = "skn", ["Kolom"] = "klm", ["Koluwawa"] = "klx", ["Kom (Cameroon)"] = "bkm", ["Kom (India)"] = "kmm", ["Koma"] = "kmy", ["Komba"] = "kpf", ["Kombai"] = "tyn", ["Kombio"] = "xbi", ["Komering"] = "kge", ["Komi-Permyak"] = "koi", ["Komi-Yazva"] = "urj-kya", ["Komi-Zyrian"] = "kpv", ["Kominimung"] = "xoi", ["Komo"] = "xom", ["Komodo"] = "kvh", ["Kompane"] = "kvp", ["Komyandaret"] = "kzv", ["Kon Keu"] = "kkn", ["Konabéré"] = "bbo", ["Konai"] = "kxw", ["Konda"] = "knd", ["Konda-Dora"] = "kfc", ["Kondekor"] = "gau", ["Koneraw"] = "kdw", ["Konkomba"] = "xon", ["Konni"] = "kma", ["Kono (Guinea)"] = "knu", ["Kono (Nigeria)"] = "klk", ["Kono (Sierra Leone)"] = "kno", ["Konomala"] = "koa", ["Konomihu"] = "nai-knm", ["Konongo"] = "kcz", ["Konso"] = "kxc", ["Konyak Naga"] = "nbe", ["Konyanka Maninka"] = "mku", ["Konzo"] = "koo", ["Koonzime"] = "ozm", ["Koorete"] = "kqy", ["Kopar"] = "xop", ["Kopkaka"] = "opk", ["Korafe-Yegha"] = "kpr", ["Korak"] = "koz", ["Korana"] = "kqz", ["Korandje"] = "kcy", ["Korean Sign Language"] = "kvk", ["Koreguaje"] = "coe", ["Koresh-e Rostam"] = "okh", ["Korku"] = "kfq", ["Korlai Creole Portuguese"] = "vkp", ["Koro (India)"] = "jkr", ["Koro (Vanuatu)"] = "krf", ["Koro (West Africa)"] = "kfo", ["Koromfé"] = "kfz", ["Koromira"] = "kqj", ["Koronadal Blaan"] = "bpr", ["Koroni"] = "xkq", ["Korop"] = "krp", ["Koropó"] = "xxr", ["Koroshi"] = "ktl", ["Korowai"] = "khe", ["Korra Koraga"] = "kfd", ["Korubo"] = "xor", ["Korupun-Sela"] = "kpq", ["Korwa"] = "kfp", ["Koryak"] = "kpy", ["Kosadle"] = "kiq", ["Kosarek Yale"] = "kkl", ["Koshin"] = "kid", ["Kosraean"] = "kos", ["Kota (Gabon)"] = "koq", ["Kota (India)"] = "kfe", ["Kota Bangun Kutai Malay"] = "mqg", ["Kota Marudu Talantang"] = "grm", ["Kota Marudu Tinagas"] = "ktr", ["Kotafon Gbe"] = "kqk", ["Koti"] = "eko", ["Kott"] = "zko", ["Kou"] = "snz", ["Kouya"] = "kyf", ["Kovai"] = "kqb", ["Kove"] = "kvc", ["Kowaki"] = "xow", ["Kowiai"] = "kwh", ["Koy Sanjaq Surat"] = "kqd", ["Koya"] = "kff", ["Koyaga"] = "kga", ["Koyo"] = "koh", ["Koyra Chiini"] = "khq", ["Koyraboro Senni"] = "ses", ["Koyukon"] = "koy", ["Kpagua"] = "kuw", ["Kpala"] = "kpl", ["Kpan"] = "kpk", ["Kpasam"] = "pbn", ["Kpati"] = "koc", ["Kpatili"] = "kym", ["Kpee"] = "cpo", ["Kpelle"] = "kpe", ["Kpessi"] = "kef", ["Kplang"] = "kph", ["Krache"] = "kye", ["Krahô"] = "xra", ["Kraol"] = "rka", ["Krenak"] = "kqq", ["Kresh"] = "krs", ["Krevinian"] = "zkv", ["Kreye"] = "xre", ["Krikati-Timbira"] = "xri", ["Krim"] = "krm", ["Krio"] = "kri", ["Krisa"] = "ksi", ["Kristang"] = "mcm", ["Krobu"] = "kxb", ["Krongo"] = "kgo", ["Kru'ng"] = "krr", ["Krymchak"] = "jct", ["Kryts"] = "kry", ["Ktunaxa"] = "kut", ["Kua"] = "tyu", ["Kua-nsi"] = "ykn", ["Kuamasi"] = "yku", ["Kuan"] = "uan", ["Kuanhua"] = "xnh", ["Kube"] = "kgf", ["Kubi"] = "kof", ["Kubo"] = "jko", ["Kubu"] = "kvb", ["Kucong"] = "lkc", ["Kudiya"] = "kfg", ["Kudmali"] = "kyw", ["Kudu-Camo"] = "kov", ["Kugama"] = "kow", ["Kugbo"] = "kes", ["Kugu-Muminh"] = "xmh", ["Kui (India)"] = "kxu", ["Kui (Indonesia)"] = "kvd", ["Kuijau"] = "dkr", ["Kuikúro"] = "kui", ["Kujarge"] = "vkj", ["Kuk"] = "kfn", ["Kukatja"] = "kux", ["Kukele"] = "kez", ["Kukkuzi"] = "urj-kuk", ["Kukna"] = "kex", ["Kuku-Mangk"] = "xmq", ["Kuku-Mu'inh"] = "xmp", ["Kuku-Thaypan"] = "typ", ["Kuku-Ugbanh"] = "ugb", ["Kuku-Uwanh"] = "uwa", ["Kuku-Yalanji"] = "gvn", ["Kula"] = "tpg", ["Kulaal"] = "glj", ["Kulere"] = "kul", ["Kulfa"] = "kxj", ["Kulina"] = "xpk", ["Kulisusu"] = "vkl", ["Kullu Pahari"] = "kfx", ["Kulon"] = "uon", ["Kulon-Pazeh"] = "uun", ["Kulung"] = "kle", ["Kumak"] = "nee", ["Kumalu"] = "ksl", ["Kumam"] = "kdi", ["Kuman"] = "kue", ["Kumaoni"] = "kfy", ["Kumarbhag Paharia"] = "kmj", ["Kumba"] = "ksm", ["Kumbainggar"] = "kgs", ["Kumbaran"] = "wkb", ["Kumbewaha"] = "xks", ["Kumeyaay"] = "nai-kum", ["Kumhali"] = "kra", ["Kumu"] = "kmw", ["Kumukio"] = "kuo", ["Kumzari"] = "zum", ["Kuna"] = "cuk", ["Kunama"] = "kun", ["Kunbarlang"] = "wlg", ["Kunda"] = "kdn", ["Kundal Shahi"] = "shd", ["Kunduvadi"] = "wku", ["Kung"] = "kfl", ["Kungarakany"] = "ggk", ["Kungardutyi"] = "gdt", ["Kunggari"] = "kgl", ["Kungkari"] = "lku", ["Kuni"] = "kse", ["Kuni-Boazi"] = "kvg", ["Kunimaipa"] = "kup", ["Kunja"] = "pep", ["Kunjen"] = "kjn", ["Kunyi"] = "njx", ["Kunza"] = "kuz", ["Kuo"] = "xuo", ["Kuot"] = "kto", ["Kupa"] = "kug", ["Kupang Malay"] = "mkn", ["Kupia"] = "key", ["Kupsabiny"] = "kpz", ["Kur"] = "kuv", ["Kura Ede Nago"] = "nqk", ["Kurama"] = "krh", ["Kuranko"] = "knk", ["Kuri"] = "nbn", ["Kuria"] = "kuj", ["Kurichiya"] = "kfh", ["Kurmukar"] = "kfv", ["Kurnai"] = "unn", ["Kurrama"] = "vku", ["Kurti"] = "ktm", ["Kurtjar"] = "gdj", ["Kurtöp"] = "xkz", ["Kurudu"] = "kjr", ["Kuruáya"] = "kyr", ["Kusaal"] = "kus", ["Kusaghe"] = "ksg", ["Kushi"] = "kuh", ["Kustenau"] = "awd-kus", ["Kusu"] = "ksv", ["Kusunda"] = "kgg", ["Kutang Ghale"] = "ght", ["Kutep"] = "kub", ["Kuthant"] = "xut", ["Kutto"] = "kpa", ["Kutu"] = "kdc", ["Kuturmi"] = "khj", ["Kuuk Thaayorre"] = "thd", ["Kuuk Yak"] = "uky", ["Kuuku-Ya'u"] = "kuy", ["Kuvale"] = "olu", ["Kuvi"] = "kxv", ["Kuwaa"] = "blh", ["Kuwaataay"] = "cwt", ["Kuwani"] = "paa-kwn", ["Kven"] = "fkv", ["Kw'adza"] = "wka", ["Kwa'"] = "bko", ["Kwaami"] = "ksq", ["Kwadi"] = "kwz", ["Kwaio"] = "kwd", ["Kwaja"] = "kdz", ["Kwak"] = "kwq", ["Kwak'wala"] = "kwk", ["Kwakum"] = "kwu", ["Kwalhioqua-Tlatskanai"] = "qwt", ["Kwama"] = "kmq", ["Kwambi"] = "kwm", ["Kwamera"] = "tnk", ["Kwami"] = "ktf", ["Kwamtim One"] = "okk", ["Kwang"] = "kvi", ["Kwanga"] = "kwj", ["Kwangali"] = "kwn", ["Kwanja"] = "knp", ["Kwanka"] = "bij", ["Kwanyama"] = "kj", ["Kwara'ae"] = "kwf", ["Kwasio"] = "nmg", ["Kwaya"] = "kya", ["Kwaza"] = "xwa", ["Kwegu"] = "xwg", ["Kwer"] = "kwr", ["Kwerba"] = "kwe", ["Kwerba Mamberamo"] = "xwr", ["Kwere"] = "cwe", ["Kwerisa"] = "kkb", ["Kwese"] = "kws", ["Kwesten"] = "kwt", ["Kwini"] = "gww", ["Kwinsu"] = "kuc", ["Kwinti"] = "kww", ["Kwoma"] = "kmo", ["Kwomtari"] = "kwo", ["Kyak"] = "bka", ["Kyaka"] = "kyc", ["Kyakala"] = "tuw-kkl", ["Kyan-Karyaw Naga"] = "nqq", ["Kyenele"] = "kql", ["Kyenga"] = "tye", ["Kyerung"] = "kgy", ["Kâte"] = "kmg", ["Kélé"] = "keb", ["Kómnzo"] = "paa-kom", ["La'bi"] = "lbi", ["Laal"] = "gdm", ["Laalaa"] = "cae", ["Laba"] = "lau", ["Label"] = "lbb", ["Labir"] = "jku", ["Labo"] = "mwi", ["Labo Phowa"] = "ypb", ["Labu"] = "lbu", ["Labuk-Kinabatangan Kadazan"] = "dtb", ["Lacandon"] = "lac", ["Lachi"] = "lbt", ["Lachiguiri Zapotec"] = "zpa", ["Lachixío Zapotec"] = "zpl", ["Ladji-Ladji"] = "llj", ["Laeko-Libuat"] = "lkl", ["Lafofa"] = "laf", ["Laghu"] = "lgb", ["Laghuu"] = "lgh", ["Lagwan"] = "kot", ["Laha (Indonesia)"] = "lhh", ["Laha (Vietnam)"] = "lha", ["Lahanan"] = "lhn", ["Lahnda"] = "lah", ["Lahta Karen"] = "kvt", ["Lahu"] = "lhu", ["Lahu Shi"] = "lhi", ["Lahul Lohar"] = "lhl", ["ฮาคา"] = "cnh", ["Laimbue"] = "lmx", ["Laitu Chin"] = "clj", ["Laiyolo"] = "lji", ["Laka"] = "lak", ["Lakalei"] = "lka", ["Lake Miwok"] = "lmw", ["Lakha"] = "lkh", ["Laki"] = "lki", ["Lakon"] = "lkn", ["Lakondê"] = "lkd", ["ลาโคตา"] = "lkt", ["Lakota Dida"] = "dic", ["Lala (New Guinea)"] = "nrz", ["Lala (South Africa)"] = "bnt-lal", ["Lala-Bisa"] = "leb", ["Lala-Roba"] = "lla", ["Lalana Chinantec"] = "cnl", ["Lama Bai"] = "lay", ["Lamaholot"] = "slp", ["Lamalera"] = "lmr", ["Lamang"] = "hia", ["Lamatuka"] = "lmq", ["Lamba"] = "lam", ["Lambadi"] = "lmn", ["Lambichhong"] = "lmh", ["Lambya"] = "lai", ["Lame"] = "bma", ["Lamenu"] = "lmu", ["Lamet"] = "lbn", ["Lamja-Dengsa-Tola"] = "ldh", ["Lamkang"] = "lmk", ["Lamma"] = "lev", ["Lamnso'"] = "lns", ["Lamogai"] = "lmg", ["Lampung Api"] = "ljp", ["Lamu"] = "llh", ["Lamu-Lamu"] = "lby", ["Lanas Lobu"] = "ruu", ["Landoma"] = "ldm", ["Lang'e"] = "yne", ["Langam"] = "lnm", ["Langbashe"] = "lna", ["Langi"] = "lag", ["Langnian Buyang"] = "yln", ["Lango (Sudan)"] = "lno", ["Lango (Uganda)"] = "laj", ["Lanima"] = "lnw", ["Lanoh"] = "lnh", ["Lao Naga"] = "nlq", ["Laomian"] = "lwm", ["Laopang"] = "lbg", ["Laos Sign Language"] = "lso", ["Lapaguía-Guivini Zapotec"] = "ztl", ["Lapine"] = "art-lap", ["Lapuyan Subanun"] = "laa", ["Laragia"] = "lrg", ["Larantuka Malay"] = "lrt", ["Lardil"] = "lbz", ["Larestani"] = "lrl", ["Larike-Wakasihu"] = "alo", ["Laro"] = "lro", ["Larteh"] = "lar", ["Laru"] = "lan", ["Larëvat"] = "lrv", ["Lasalimu"] = "llm", ["Lasgerdi"] = "lsa", ["Lashi"] = "lsi", ["Lasi"] = "lss", ["Latu"] = "ltu", ["Latundê"] = "ltn", ["Latvian Sign Language"] = "lsl", ["Lau"] = "llu", ["Laua"] = "luf", ["Lauan"] = "llx", ["Lauje"] = "law", ["Laura"] = "lur", ["Laurentian"] = "lre", ["Lavatbura-Lamusong"] = "lbv", ["Lave"] = "brb", ["Laven"] = "lbo", ["Lavukaleve"] = "lvk", ["Lawangan"] = "lbx", ["Lawi"] = "lvi", ["Lawu"] = "lwu", ["Lawunuia"] = "tgi", ["Layakha"] = "lya", ["Laze"] = "tbq-laz", ["Lealao Chinantec"] = "cle", ["Leco"] = "lec", ["Ledo Kaili"] = "lew", ["Leelau"] = "ldk", ["Lefa"] = "lfa", ["Lega-Mwenga"] = "lgm", ["Lega-Shabunda"] = "lea", ["Legbo"] = "agb", ["Legenyem"] = "lcc", ["Lehali"] = "tql", ["Leinong Naga"] = "lzn", ["Leipon"] = "lek", ["Lela"] = "dri", ["Lelak"] = "llk", ["Lele (Chad)"] = "lln", ["Lele (Congo)"] = "lel", ["Lele (Guinea)"] = "llc", ["Lele (New Guinea)"] = "lle", ["Lelemi"] = "lef", ["Lelepa"] = "lpa", ["Lembena"] = "leq", ["Lemerig"] = "lrz", ["Lemio"] = "lei", ["Lemnian"] = "xle", ["Lemolang"] = "ley", ["Lemoro"] = "ldj", ["Lenakel"] = "tnl", ["Lendu"] = "led", ["Lengilu"] = "lgi", ["Lengo"] = "lgr", ["Lengola"] = "lej", ["Lenje"] = "leh", ["Lenkau"] = "ler", ["Lenyima"] = "ldg", ["Lepcha"] = "lep", ["Lepki"] = "lpe", ["Lepontic"] = "xlp", ["Lere"] = "gnh", ["Lese"] = "les", ["Lesing-Gelimi"] = "let", ["Letemboi"] = "nms", ["Leti"] = "lti", ["Levuka"] = "lvu", ["Lewo"] = "lww", ["Lewo Eleng"] = "lwe", ["Lewotobi"] = "lwt", ["Leyigha"] = "ayi", ["Lhao Vo"] = "mhx", ["Lhokpu"] = "lhp", ["Li'o"] = "ljl", ["Liabuku"] = "lix", ["Liana-Seti"] = "ste", ["Liangmai Naga"] = "njn", ["Liberia Kpelle"] = "xpe", ["Liberian Kreyol"] = "lir", ["Libido"] = "liq", ["Libinza"] = "liz", ["Libon Bikol"] = "lbl", ["Liburnian"] = "xli", ["Libyan Sign Language"] = "lbs", ["Ligbi"] = "lig", ["Ligenza"] = "lgz", ["Lihir"] = "lih", ["Lika"] = "lik", ["Liki"] = "lio", ["Likila"] = "lie", ["Likuba"] = "kxx", ["Likum"] = "lib", ["Likwala"] = "kwc", ["Lilau"] = "lll", ["Lillooet"] = "lil", ["Limassa"] = "bme", ["Limbum"] = "lmp", ["Limi"] = "ylm", ["Limilngan"] = "lmc", ["Limos Kalinga"] = "kmk", ["Linear A"] = "lab", ["Lingkhim"] = "lii", ["Lingua Franca Nova"] = "lfn", ["Linngithigh"] = "lnj", ["Lipan"] = "apl", ["Lipo"] = "lpo", ["Lisabata-Nuniali"] = "lcs", ["Lisela"] = "lcl", ["Lish"] = "lsh", ["Lishana Deni"] = "lsd", ["Lishanid Noshan"] = "aij", ["Lishán Didán"] = "trg", ["Lithuanian Sign Language"] = "lls", ["Little Swanport"] = "aus-lsw", ["Litzlitz"] = "lzl", ["Lizu"] = "sit-liz", ["Lo-Toga"] = "lht", ["Loarki"] = "lrk", ["Lobala"] = "loq", ["Lobi"] = "lob", ["Lodhi"] = "lbm", ["Logba"] = "lgq", ["Logo"] = "log", ["Logol"] = "lof", ["Logooli"] = "rag", ["Logorik"] = "liu", ["Lokaa"] = "yaz", ["Loko"] = "lok", ["Lokoya"] = "lky", ["Lola"] = "lcd", ["Lolak"] = "llq", ["Lole"] = "llg", ["Lolo"] = "llb", ["Loloda"] = "loa", ["Lolopo"] = "ycl", ["Loma"] = "lom", ["Lomaiviti"] = "lmv", ["Lomakka"] = "loi", ["Lomavren"] = "rmi", ["Lombi"] = "lmi", ["Lombo"] = "loo", ["Lomwe"] = "ngl", ["Loncong"] = "lce", ["Long Phuri Naga"] = "lpn", ["Long Wat"] = "ttw", ["Longgu"] = "lgu", ["Longjia"] = "sit-lnj", ["Longto"] = "wok", ["Longuda"] = "lnu", ["Longyan Min"] = "nan-lnx", ["Loniu"] = "los", ["Lonwolwol"] = "crc", ["Loo"] = "ldo", ["Lopa"] = "lop", ["Lope"] = "yiu", ["Lopi"] = "lov", ["Lopit"] = "lpx", ["Lorang"] = "lrn", ["Lorediakarkar"] = "lnn", ["Lorrain"] = "roa-lor", ["Lote"] = "uvl", ["Lotha Naga"] = "njh", ["Lotud"] = "dtr", ["Lotuko"] = "lot", ["Lou"] = "loj", ["Loun"] = "lox", ["Loup A"] = "xlo", ["Loup B"] = "xlb", ["Lovono"] = "vnk", ["Lower Burdekin"] = "xbb", ["Lower Chehalis"] = "cea", ["Lower Grand Valley Dani"] = "dni", ["Lower Nossob"] = "nsb", ["Lower Southern Aranda"] = "axl", ["Lower Ta'oih"] = "tto", ["Lower Tanana"] = "taa", ["Lowland Oaxaca Chontal"] = "clo", ["Lowland Tarahumara"] = "tac", ["Loxicha Zapotec"] = "ztp", ["Lozi"] = "loz", ["Luang"] = "lex", ["Luba-Kasai"] = "lua", ["Luba-Katanga"] = "lu", ["Lubila"] = "kcc", ["Lubu"] = "lcf", ["Lubuagan Kalinga"] = "knb", ["Luchazi"] = "lch", ["Lucumí"] = "luq", ["Ludian"] = "lud", ["Lufu"] = "ldq", ["Lugbara"] = "lgg", ["Luguru"] = "ruf", ["Luhu"] = "lcq", ["Luhya"] = "luy", ["Luimbi"] = "lum", ["Luiseño"] = "lui", ["Lukpa"] = "dop", ["Lule"] = "ule", ["Lumba-Yakkha"] = "luu", ["Lumbee"] = "lmz", ["Lumbu"] = "lup", ["Lumun"] = "lmd", ["Lun Bawang"] = "lnd", ["Luna"] = "luj", ["Lunanakha"] = "luk", ["Lunda"] = "lun", ["Lungga"] = "lga", ["Luo"] = "luo", ["Luopohe Hmong"] = "hml", ["Luren"] = "sit-lrn", ["Luri (Nigeria)"] = "ldd", ["Lusengo"] = "lse", ["Lushootseed"] = "lut", ["Lusi"] = "khl", ["Lusitanian"] = "xls", ["Lutachoni"] = "lts", ["Lutos"] = "ndy", ["Lutuv"] = "clt", ["Luvale"] = "lue", ["Luwati"] = "luv", ["Luwian"] = "xlu", ["Luwo"] = "lwo", ["Luyana"] = "lyn", ["Lwalu"] = "lwa", ["Lwel"] = "lvl", ["Lycian"] = "xlc", ["Lydian"] = "xld", ["Lyélé"] = "lee", ["Láadan"] = "ldn", ["Láá Láá Bwamu"] = "bwj", ["Löyöp"] = "urr", ["Ma"] = "msj", ["Ma Manda"] = "skc", ["Ma'anyan"] = "mhy", ["Ma'di"] = "mhi", ["Ma'ya"] = "slz", ["Maaka"] = "mew", ["Maale"] = "mdy", ["Maay"] = "ymm", ["Maba"] = "mqa", ["Mabaale"] = "mmz", ["Mabaan"] = "mfz", ["Mabaka Valley Kalinga"] = "kkg", ["Mabire"] = "muj", ["Maca"] = "mca", ["Macaguaje"] = "mcl", ["Macaguán"] = "mbn", ["Macau Pidgin Portuguese"] = "crp-mpp", ["Machame"] = "jmc", ["Machiguenga"] = "mcb", ["Machinere"] = "mpd", ["Machinga"] = "mvw", ["Macoris"] = "nai-mac", ["Macuna"] = "myy", ["Macushi"] = "mbc", ["Mada (Cameroon)"] = "mxu", ["Mada (Nigeria)"] = "mda", ["Madagascar Sign Language"] = "mzc", ["Madak"] = "mmx", ["Maden"] = "xmx", ["Madhi Madhi"] = "dmd", ["Madi"] = "grg", ["Madngele"] = "zml", ["Madukayang Kalinga"] = "kmd", ["Madurese"] = "mad", ["Maeng Itneg"] = "itt", ["Mafa"] = "maf", ["Mag-Anchi Ayta"] = "sgb", ["Mag-Indi Ayta"] = "blx", ["Magahat"] = "mtw", ["Magdalena Peñasco Mixtec"] = "xtm", ["Magiyi"] = "gmg", ["Magoma"] = "gmx", ["Magori"] = "zgr", ["Magɨ"] = "gkd", ["Mahali"] = "mjx", ["Mahasu Pahari"] = "bfz", ["Mahican"] = "mjy", ["Mahongwe"] = "mhb", ["Mahou"] = "mxx", ["Maiadomu"] = "mzz", ["Maiani"] = "tnh", ["Maii"] = "mmm", ["Mailu"] = "mgu", ["Maindo"] = "cwb", ["Maipure"] = "awd-mpr", ["Mairasi"] = "zrs", ["Maisin"] = "mbq", ["Maiwa (Indonesia)"] = "wmm", ["Maiwa (New Guinea)"] = "mti", ["Maiwala"] = "mum", ["Majang"] = "mpe", ["Majera"] = "xmj", ["Majhi"] = "mjz", ["Majhwar"] = "mmj", ["Mak (China)"] = "mkg", ["Mak (Nigeria)"] = "pbl", ["Makaa"] = "mcp", ["Makah"] = "myh", ["Makalero"] = "mjb", ["Makasae"] = "mkz", ["Makassar Malay"] = "mfp", ["Makayam"] = "aup", ["Makhuwa"] = "vmw", ["Makhuwa-Marrevone"] = "xmc", ["Makhuwa-Meetto"] = "mgh", ["Makhuwa-Moniga"] = "mhm", ["Makhuwa-Saka"] = "xsq", ["Makhuwa-Shirima"] = "vmk", ["Maklew"] = "mgf", ["Makolkol"] = "zmh", ["Makonde"] = "kde", ["Maku"] = "xak", ["Maku'a"] = "lva", ["Makuri Naga"] = "jmn", ["Makuráp"] = "mpu", ["Makwe"] = "ymk", ["Makyan Naga"] = "umn", ["Mal Paharia"] = "mkb", ["Mala (New Guinea)"] = "ped", ["Mala (Nigeria)"] = "ruy", ["Mala Malasar"] = "ima", ["Malaccan Creole Malay"] = "ccm", ["Malalamai"] = "mmt", ["Malalí"] = "sai-mal", ["Malango"] = "mln", ["Malankuravan"] = "mjo", ["Malapandaram"] = "mjp", ["Malaryan"] = "mjq", ["Malas"] = "mkr", ["Malasanga"] = "mqz", ["Malasar"] = "ymr", ["Malavedan"] = "mjr", ["Malawi Lomwe"] = "lon", ["Malawian Sign Language"] = "lws", ["Malayic Dayak"] = "xdy", ["Malaynon"] = "mlz", ["Malaysian Sign Language"] = "xml", ["Malba Birifor"] = "bfo", ["Male"] = "mdc", ["Malecite-Passamaquoddy"] = "pqm", ["Maleng"] = "pkt", ["Maleu-Kilenge"] = "mgl", ["Malfaxal"] = "mlx", ["Malgana"] = "vml", ["Malgbe"] = "mxf", ["Mali"] = "gcc", ["Malibu"] = "sai-mlb", ["Malila"] = "mgq", ["Malimba"] = "mzd", ["Malimpung"] = "mli", ["Malinaltepec Tlapanec"] = "tcf", ["Malol"] = "mbk", ["Maltese Sign Language"] = "mdl", ["Malua Bay"] = "mll", ["Malvi"] = "mup", ["Maléku Jaíka"] = "gut", ["Mam"] = "mam", ["Mama"] = "mma", ["Mamaa"] = "mhf", ["Mamaindé"] = "wmd", ["Mamanwa"] = "mmn", ["Mamara Senoufo"] = "myk", ["Mamasa"] = "mqj", ["Mambae"] = "mgm", ["Mambai"] = "mcs", ["Mamboru"] = "mvd", ["Mambwe-Lungu"] = "mgr", ["Mampruli"] = "maw", ["Mamuju"] = "mqx", ["Mamulique"] = "emm", ["Mamusi"] = "kdf", ["Mamvu"] = "mdi", ["Man Met"] = "mml", ["มลายูแบบมานาโด"] = "xmm", ["Manam"] = "mva", ["Manambu"] = "mle", ["Manangba"] = "nmm", ["Manangkari"] = "znk", ["Manao"] = "awd-man", ["Manda (Australia)"] = "zma", ["Manda (India)"] = "mha", ["Manda (Tanzania)"] = "mgs", ["Mandahuaca"] = "mht", ["Mandaic"] = "mid", ["Mandailing Batak"] = "btm", ["Mandalorian"] = "art-man", ["Mandan"] = "mhq", ["Mandandanyi"] = "zmk", ["Mandar"] = "mdr", ["Mandara"] = "tbf", ["Mandari"] = "mqu", ["Mandeali"] = "mjl", ["Mander"] = "mqr", ["Mandingo"] = "man", ["Mandinka"] = "mnk", ["Mandjak"] = "mfv", ["Mandobo Atas"] = "aax", ["Mandobo Bawah"] = "bwp", ["Manem"] = "jet", ["Mang"] = "zng", ["Mangala"] = "mem", ["Mangarayi"] = "mpc", ["Mangarevan"] = "mrv", ["Mangas"] = "zns", ["Mangayat"] = "myj", ["Mangbetu"] = "mdj", ["Mangbutu"] = "mdk", ["Mangerr"] = "zme", ["Mangga Buang"] = "mmo", ["Manggarai"] = "mqy", ["Mangghuer"] = "mjg-mgr", ["Mango"] = "mge", ["Mangole"] = "mqc", ["Mangseng"] = "mbh", ["Manigri-Kambolé Ede Nago"] = "xkb", ["Manikion"] = "mnx", ["Manipa"] = "mqp", ["Mankanya"] = "knf", ["Mankiyali"] = "nlm", ["Manna-Dora"] = "mju", ["Mannan"] = "mjv", ["Mano"] = "mev", ["Manombai"] = "woo", ["Mansaka"] = "msk", ["Mansoanka"] = "msw", ["Manta"] = "myg", ["Mantsi"] = "nty", ["Manumanaw Karen"] = "kxf", ["Manus Koro"] = "kxr", ["Manusela"] = "wha", ["Manya"] = "mzj", ["Manyawa"] = "mny", ["Manza"] = "mzv", ["Mao Naga"] = "nbi", ["Maonan"] = "mmd", ["Maore Comorian"] = "swb", ["Mape"] = "mlh", ["Mapena"] = "mnm", ["Mapia"] = "mpy", ["Mapidian"] = "mpw", ["Mapos Buang"] = "bzh", ["Mapoyo"] = "mcg", ["Mapudungun"] = "arn", ["Mapun"] = "sjm", ["Mara"] = "mec", ["Mara Chin"] = "mrh", ["Marachi"] = "lri", ["Maraghei"] = "vmh", ["Maragus"] = "mrs", ["Maram Naga"] = "nma", ["Marama"] = "lrm", ["Maranungku"] = "zmr", ["Mararit"] = "mgb", ["Maratino"] = "sai-mar", ["Marau"] = "mvr", ["Marawan"] = "awd-mar", ["Marba"] = "mpg", ["Marenje"] = "vmr", ["Marfa"] = "mvu", ["Margany"] = "zmc", ["Marghi South"] = "mfm", ["Margi"] = "mrt", ["Maria"] = "mds", ["Mariaté"] = "awd-mrt", ["Maricopa"] = "mrc", ["Maridan"] = "zmd", ["Maridjabin"] = "zmj", ["Marik"] = "dad", ["Marimanindji"] = "zmm", ["Marind"] = "mrz", ["Maring"] = "mbw", ["Maring Naga"] = "nng", ["Maringarr"] = "zmt", ["Mariri"] = "mqi", ["Maritime Sign Language"] = "nsr", ["Maritsauá"] = "msp", ["Mariupol Greek"] = "grk-mar", ["Mariyedi"] = "zmy", ["Marka"] = "rkm", ["Markweeta"] = "enb", ["Marma"] = "rmz", ["Maroon Spirit Language"] = "crp-mar", ["Marovo"] = "mvo", ["Marrgu"] = "mhg", ["Marriammu"] = "xru", ["Marrithiyel"] = "mfr", ["Marrucinian"] = "umc", ["Marsian"] = "ims", ["Martha's Vineyard Sign Language"] = "mre", ["Marti Ke"] = "zmg", ["Martu Wangka"] = "mpj", ["Martuthunira"] = "vma", ["Marúbo"] = "mzr", ["Masaba"] = "myx", ["Masadiit Itneg"] = "tis", ["Masakará"] = "sai-msk", ["Masalit"] = "mls", ["Masana"] = "mcn", ["Masbate Sorsogon"] = "bks", ["Mashco Piro"] = "cuj", ["Mashi"] = "mho", ["Masimasi"] = "ism", ["Masiwang"] = "bnf", ["Maskelynes"] = "klv", ["Maslam"] = "msv", ["Masmaje"] = "mes", ["Massachusett"] = "wam", ["Massalat"] = "mdg", ["Massep"] = "mvs", ["Matagalpa"] = "mtn", ["Matal"] = "mfh", ["Matanawi"] = "sai-mat", ["Matbat"] = "xmt", ["Matengo"] = "mgv", ["Matepi"] = "mqe", ["Matigsalug Manobo"] = "mbt", ["Matipuhy"] = "mzo", ["Matlatzinca"] = "mat", ["Mato"] = "met", ["Mator"] = "mtm", ["Matsés"] = "mcf", ["Mattole"] = "mvb", ["Matukar"] = "mjk", ["Matumbi"] = "mgw", ["Matya Samo"] = "stj", ["Matís"] = "mpq", ["Maung"] = "mph", ["Mauritian Sign Language"] = "lsy", ["Mauwake"] = "mhl", ["Mav̋ea"] = "mkv", ["Mawa"] = "mcw", ["Mawak"] = "mjj", ["Mawan"] = "mcz", ["Mawayana"] = "mzx", ["Mawchi"] = "mke", ["Mawes"] = "mgk", ["Maxakalí"] = "mbl", ["Maxi Gbe"] = "mxl", ["Maya Samo"] = "sym", ["Mayaguduna"] = "xmy", ["Mayangna"] = "yan", ["Mayawali"] = "yxa", ["Maybrat"] = "ayz", ["Mayeka"] = "myc", ["Mayi-Thakurti"] = "xyt", ["Maykulan"] = "mnt", ["Maynas"] = "sai-mys", ["Mayo"] = "mfy", ["Mayogo"] = "mdm", ["Mayoyao Ifugao"] = "ifu", ["Mazagway"] = "dkx", ["Mazaltepec Zapotec"] = "zpy", ["Mazatlán Mazatec"] = "vmz", ["Mazatlán Mixe"] = "mzl", ["Mba"] = "mfc", ["Mbabaram"] = "vmb", ["Mbala"] = "mdp", ["Mbalanhu"] = "lnb", ["Mbandja"] = "zmz", ["Mbangala"] = "mxg", ["Mbangi"] = "mgn", ["Mbangwe"] = "zmn", ["Mbara (Australia)"] = "mvl", ["Mbara (Chad)"] = "mpk", ["Mbariman-Gudhinma"] = "zmv", ["Mbati"] = "mdn", ["Mbato"] = "gwa", ["Mbay"] = "myb", ["Mbe"] = "mfo", ["Mbe'"] = "mtk", ["Mbelime"] = "mql", ["Mbere"] = "mdt", ["Mbesa"] = "zms", ["Mbiywom"] = "aus-mbi", ["Mbo (Cameroon)"] = "mbo", ["Mbo (Congo)"] = "zmw", ["Mboi"] = "moi", ["Mboko"] = "mdu", ["Mbole"] = "mdq", ["Mbonga"] = "xmb", ["Mbongno"] = "bgu", ["Mbosi"] = "mdw", ["Mbowe"] = "mxo", ["Mbre"] = "mka", ["Mbu'"] = "muc", ["Mbudum"] = "xmd", ["Mbugu"] = "mhd", ["Mbugwe"] = "mgz", ["Mbuko"] = "mqb", ["Mbukushu"] = "mhw", ["Mbula"] = "mna", ["Mbula-Bwazza"] = "mbu", ["Mbule"] = "mlb", ["Mbulungish"] = "mbv", ["Mbum"] = "mdd", ["Mbunda"] = "mck", ["Mbunga"] = "mgy", ["Mburku"] = "bbt", ["Mbuun"] = "zmp", ["Mbwela"] = "mfu", ["Mbya Guarani"] = "gun", ["Me'en"] = "mym", ["Mebu"] = "mjn", ["Mecayapan Nahuatl"] = "nhx", ["Medebur"] = "mjm", ["Medefaidrin"] = "dmf", ["Media Lengua"] = "mue", ["Mednyj Aleut"] = "mud", ["Medumba"] = "byv", ["Mefele"] = "mfj", ["Megam"] = "mef", ["Megleno-Romanian"] = "ruq", ["Mehek"] = "nux", ["Mehináku"] = "mmh", ["Mehri"] = "gdq", ["Mekeo"] = "mek", ["Mekmek"] = "mvk", ["Mekwei"] = "msf", ["Mekéns"] = "skf", ["Mel-Khaonh"] = "hkn", ["Mele-Fila"] = "mxe", ["Melo"] = "mfx", ["Melpa"] = "med", ["Memoni"] = "mby", ["Mendalam Kayan"] = "xkd", ["Mendankwe-Nkwen"] = "mfd", ["Mende"] = "men", ["Mengaka"] = "xmg", ["Mengen"] = "mee", ["Mengisa"] = "leo", ["Menien"] = "sai-men", ["Menka"] = "mea", ["Menominee"] = "mez", ["Mentawai"] = "mwv", ["Menya"] = "mcr", ["Meoswar"] = "mvx", ["Mer"] = "mnu", ["Meramera"] = "mxm", ["Merei"] = "lmb", ["Merey"] = "meq", ["Meriam"] = "ulk", ["Meroitic"] = "xmr", ["Meru"] = "mer", ["Mesaka"] = "iyo", ["Mese"] = "mci", ["Mesme"] = "zim", ["Mesmes"] = "mys", ["Mesqan"] = "mvz", ["Messapic"] = "cms", ["Meta'"] = "mgo", ["Metlatónoc Mixtec"] = "mxv", ["Mewati"] = "wtm", ["Mexican Sign Language"] = "mfs", ["Meyah"] = "mej", ["Mezontla Popoloca"] = "pbe", ["Mezquital Otomi"] = "ote", ["Meänkieli"] = "fit", ["Mfinu"] = "zmf", ["Mfumte"] = "nfu", ["Mgbo"] = "gmz", ["Mi'kmaq"] = "mic", ["Miami"] = "mia", ["Mian"] = "mpt", ["Miani"] = "pla", ["Micha"] = "yiq", ["Michif"] = "crg", ["Michigamea"] = "cmm", ["Michoacán Mazahua"] = "mmc", ["Michoacán Nahuatl"] = "ncl", ["Mid Grand Valley Dani"] = "dnt", ["Mid-Southern Banda"] = "bjo", ["Middle Cornish"] = "cnx", ["Middle Kannada"] = "dra-mkn", ["Middle Median"] = "xme-mid", ["Middle Scots"] = "gmw-msc", ["Middle Watut"] = "mpl", ["Midob"] = "mei", ["Migaama"] = "mmy", ["Migabac"] = "mpp", ["Miji"] = "sjl", ["Miju"] = "mxj", ["Mikasuki"] = "mik", ["Milang"] = "tbq-mil", ["Mili"] = "ymh", ["Millcayac"] = "sai-mil", ["Miltu"] = "mlj", ["Miluk"] = "iml", ["Milyan"] = "imy", ["Mimi of Decorse"] = "mis-mmd", ["Mimi of Nachtigal"] = "mis-mmn", ["Mina"] = "hna", ["Minaean"] = "inm", ["Minang"] = "xrg", ["Minaveha"] = "mvn", ["Minderico"] = "drc", ["Mindiri"] = "mpn", ["Mingang Doso"] = "mko", ["Mingo"] = "iro-min", ["Minica Huitoto"] = "hto", ["Minidien"] = "wii", ["Minigir"] = "vmg", ["Minjungbal"] = "xjb", ["Minkin"] = "xxm", ["Minokok"] = "mqq", ["Minriq"] = "mnq", ["Mintil"] = "mzt", ["Miraya Bikol"] = "rbl", ["Mire"] = "mvh", ["Mirgan"] = "zrg", ["Miriti"] = "mmv", ["Miriwoong Sign Language"] = "rsm", ["Miriwung"] = "mep", ["Mirning"] = "gmr", ["Mirpur Panjabi"] = "pmu", ["Misantla Totonac"] = "tlc", ["Miship"] = "mjs", ["Misima-Paneati"] = "mpx", ["Mising"] = "mrg", ["Miskito"] = "miq", ["Mitla Zapotec"] = "zaw", ["Mitlatongo Mixtec"] = "vmm", ["Mittu"] = "mwu", ["Mituku"] = "zmq", ["Miu"] = "mpo", ["Miwa"] = "vmi", ["Mixed Great Andamanese"] = "gac", ["Mixifore"] = "mfg", ["Mixtepec Mixtec"] = "mix", ["Mixtepec Zapotec"] = "zpm", ["Miya"] = "mkf", ["Miyobe"] = "soy", ["Mlabri"] = "mra", ["Mlahsö"] = "lhs", ["Mlap"] = "kja", ["Mlomp"] = "mlo", ["Mmaala"] = "mmu", ["Mmani"] = "buy", ["Mmen"] = "bfm", ["Mo"] = "wkd", ["Mo'da"] = "gbn", ["Moba"] = "mfq", ["Mobilian"] = "mod", ["Mobu"] = "ahm", ["Mocana"] = "sai-mcn", ["Mochi"] = "old", ["Mochica"] = "omc", ["Mocho"] = "mhc", ["Mocoví"] = "moc", ["Modang"] = "mxd", ["Modole"] = "mqo", ["Moere"] = "mvq", ["Mofu-Gudur"] = "mif", ["Mogholi"] = "mhj", ["Mogum"] = "mou", ["Mohawk"] = "moh", ["Mohegan-Pequot"] = "xpq", ["Moi (Congo)"] = "mow", ["Moi (Indonesia)"] = "mxn", ["Moikodi"] = "mkp", ["Moingi"] = "mwz", ["Mojave"] = "mov", ["Moji"] = "ymi", ["Mok"] = "mqt", ["Moken"] = "mwt", ["Mokerang"] = "mft", ["Moklen"] = "mkm", ["Mokole"] = "mkl", ["Mokpwe"] = "bri", ["Molale"] = "mbe", ["Molbog"] = "pwm", ["Moldova Sign Language"] = "vsi", ["Molengue"] = "bxc", ["Molima"] = "mox", ["Molmo One"] = "aun", ["Molo"] = "zmo", ["Molof"] = "msl", ["Moloko"] = "mlw", ["Mom Jango"] = "ver", ["Moma"] = "myl", ["Momare"] = "msz", ["Mombo Dogon"] = "dmb", ["Mombum"] = "mso", ["Momina"] = "mmb", ["Momuna"] = "mqf", ["Monastic Sign Language"] = "mzg", ["Mondropolon"] = "npn", ["Mondé"] = "mnd", ["Mongghul"] = "mjg-mgl", ["Mongo"] = "lol", ["Mongol"] = "mgt", ["Mongolian Sign Language"] = "msr", ["Mongondow"] = "mog", ["Moni"] = "mnz", ["Monimbo"] = "mom", ["Mono (California)"] = "mnr", ["Mono (Cameroon)"] = "mru", ["Mono (Congo)"] = "mnh", ["Monom"] = "moo", ["Monsang Naga"] = "nmh", ["Montana Salish"] = "fla", ["Montol"] = "mtl", ["Monumbo"] = "mxk", ["Monzombo"] = "moj", ["Moo"] = "gwg", ["Moore"] = "mos", ["Moose Cree"] = "crm", ["Mopan Maya"] = "mop", ["Mor (Austronesian)"] = "mhz", ["Mor (Papuan)"] = "moq", ["Moraid"] = "msg", ["Moran"] = "tbq-mor", ["Morawa"] = "mze", ["Morelos Nahuatl"] = "nhm", ["Morerebi"] = "xmo", ["Moresada"] = "msx", ["Mori Atas"] = "mzq", ["Mori Bawah"] = "xmz", ["Morigi"] = "mdb", ["Moro"] = "mor", ["Moroccan Amazigh"] = "zgh", ["Moroccan Sign Language"] = "xms", ["Morokodo"] = "mgc", ["Morom"] = "bdo", ["Moronene"] = "mqn", ["Morori"] = "mok", ["Morouas"] = "mrp", ["Mortlockese"] = "mrl", ["Moru"] = "mgd", ["Mosimo"] = "mqv", ["Moskona"] = "mtj", ["Mota"] = "mtt", ["Motembo"] = "tmv", ["Motu"] = "meu", ["Mouk-Aria"] = "mwh", ["Mount Iraya Agta"] = "atl", ["Mount Iriga Agta"] = "agz", ["Mountain Koiari"] = "kpx", ["Mouwase"] = "jmw", ["Movima"] = "mzp", ["Moyadan Itneg"] = "ity", ["Moyon Naga"] = "nmo", ["Mozambican Sign Language"] = "mzy", ["Mozarabic"] = "mxi", ["Mpade"] = "mpi", ["Mpalitjanh"] = "xpj", ["Mpi"] = "mpz", ["Mpiemo"] = "mcx", ["Mpiin"] = "bnt-mpi", ["Mpinda"] = "pnd", ["Mpongmpong"] = "mgg", ["Mpoto"] = "mpa", ["Mpotovoro"] = "mvt", ["Mpuono"] = "bnt-mpu", ["Mpur"] = "akc", ["Mro Chin"] = "cmr", ["Mru"] = "mro", ["Mser"] = "kqx", ["Muak Sa-aak"] = "ukk", ["Mualang"] = "mtd", ["Mubami"] = "tsx", ["Mubi"] = "mub", ["Mucuchí"] = "sai-muc", ["Muda"] = "ymd", ["Mudburra"] = "dmw", ["Mudu Koraga"] = "vmd", ["Muduapa"] = "wiv", ["Muduga"] = "udg", ["Muellama"] = "sai-mue", ["Mufian"] = "aoj", ["Muher"] = "sem-mhr", ["Muinane"] = "bmr", ["Mukha-Dora"] = "mmk", ["Mukulu"] = "moz", ["Mulaha"] = "mfw", ["Mulam"] = "mlm", ["Mulao"] = "giu", ["Mullu Kurumba"] = "kpb", ["Mullukmulluk"] = "mpb", ["Muluridyi"] = "vmu", ["Mum"] = "kqa", ["Mumuye"] = "mzm", ["Muna"] = "mnb", ["Munda"] = "unx", ["Mundabli"] = "boe", ["Mundang"] = "mua", ["Mundani"] = "mnf", ["Mundari"] = "unr", ["Mundat"] = "mmf", ["Mundolinco"] = "art-mun", ["Mundurukú"] = "myu", ["Mungaka"] = "mhk", ["Mungbam"] = "mij", ["Munggui"] = "mth", ["Mungkip"] = "mpv", ["Muniche"] = "myr", ["Munit"] = "mtc", ["Munji"] = "mnj", ["Munsee"] = "umu", ["Mur Pano"] = "tkv", ["Muratayak"] = "asx", ["Murik (Malaysia)"] = "mxr", ["Murik (New Guinea)"] = "mtf", ["Murkim"] = "rmh", ["Murle"] = "mur", ["Murrinh-Patha"] = "mwf", ["Mursi"] = "muz", ["Murui Huitoto"] = "huu", ["Murupi"] = "mqw", ["Muruwari"] = "zmu", ["Musan"] = "mmp", ["Musar"] = "mmi", ["Musasa"] = "smm", ["Musey"] = "mse", ["Musgu"] = "mug", ["Muskum"] = "mje", ["Musom"] = "msu", ["Mussau-Emira"] = "emi", ["Muthuvan"] = "muv", ["Mutu"] = "tuc", ["Muya"] = "mvm", ["Muyang"] = "muy", ["Muyuw"] = "myw", ["Muzi"] = "ymz", ["Muzo"] = "sai-muz", ["Mvanip"] = "mcj", ["Mvuba"] = "mxh", ["Mwaghavul"] = "sur", ["Mwali Comorian"] = "wlc", ["Mwan"] = "moa", ["Mwatebu"] = "mwa", ["Mwera"] = "mwe", ["Mwerlap"] = "mrm", ["Mwimbi-Muthambi"] = "mws", ["Mwotlap"] = "mlv", ["Myene"] = "mye", ["Mysian"] = "yms", ["Mzieme Naga"] = "nme", ["Mághdì"] = "gmd", ["Mün Chin"] = "mwq", ["Mündü"] = "muh", ["N'Ko"] = "nqo", ["Na"] = "nbt", ["Naaba"] = "nao", ["Naba"] = "mne", ["Nabak"] = "naf", ["Nabi"] = "mty", ["Nachering"] = "ncd", ["Nadruvian"] = "ndf", ["Nadëb"] = "mbj", ["Nafaanra"] = "nfr", ["Nafi"] = "srf", ["Nafri"] = "nxx", ["Naga Pidgin"] = "nag", ["Nagarchal"] = "nbg", ["Nage"] = "nxe", ["Nagtipunan Agta"] = "phi-nag", ["Nagu"] = "ngr", ["Nagumi"] = "ngv", ["Nahali"] = "nlx", ["Nahari"] = "nhh", ["Nahavaq"] = "sns", ["Nai"] = "bio", ["Najdi Arabic"] = "ars", ["Naka'ela"] = "nae", ["Nakai"] = "nkj", ["Nakame"] = "nib", ["Nakanai"] = "nak", ["Nakara"] = "nck", ["Nake"] = "nbk", ["Naki"] = "mff", ["Nakwi"] = "nax", ["Nalca"] = "nlc", ["Nali"] = "nss", ["Nalik"] = "nal", ["Nalu"] = "naj", ["Nalögo"] = "nlz", ["Namakura"] = "nmk", ["Namat"] = "nkm", ["Nambikwara"] = "nab", ["Nambo"] = "ncm", ["Nambya"] = "nmq", ["Namia"] = "nnm", ["Namiae"] = "nvm", ["Namibian Sign Language"] = "nbs", ["Namla"] = "naa", ["Namo"] = "mxw", ["Namonuito"] = "nmt", ["Namosi-Naitasiri-Serua"] = "bwb", ["Namuyi"] = "nmy", ["Nanai"] = "gld", ["Nancere"] = "nnc", ["Nande"] = "nnb", ["Nandi"] = "niq", ["Nanerigé Sénoufo"] = "sen", ["Nanga Dama Dogon"] = "nzz", ["Nankina"] = "nnk", ["Nanti"] = "cox", ["Nanticoke"] = "nnt", ["Nanubae"] = "afk", ["Naolan"] = "nai-nao", ["Napu"] = "npy", ["Nar Phu"] = "npa", ["Nara"] = "nrb", ["Narak"] = "nac", ["Narango"] = "nrg", ["Narim"] = "loh", ["Naro"] = "nhr", ["Narom"] = "nrm", ["Narragansett"] = "xnt", ["Narua"] = "nru", ["Narungga"] = "nnr", ["Naruo"] = "ylo", ["Nasal"] = "nsy", ["Nasarian"] = "nvh", ["Nasioi"] = "nas", ["Naskapi"] = "nsk", ["Nasu"] = "ywq", ["Natagaimas"] = "nts", ["Natchez"] = "ncz", ["Nateni"] = "ntm", ["Natioro"] = "nti", ["Natú"] = "sai-nat", ["Natügu"] = "ntu", ["Nauete"] = "nxa", ["Naukanski"] = "ynk", ["Nauna"] = "ncn", ["Nauo"] = "nwo", ["Navut"] = "nsw", ["Nawaru"] = "nwr", ["Nawathinehena"] = "nwa", ["Nawdm"] = "nmz", ["Nawuri"] = "naw", ["Naxi"] = "nxq", ["Nayi"] = "noz", ["Naʼvi"] = "art-nav", ["Ncane"] = "ncr", ["Nchumbulu"] = "nlu", ["Nda'nda'"] = "nnz", ["Ndai"] = "gke", ["Ndaka"] = "ndk", ["Ndali"] = "ndh", ["Ndam"] = "ndm", ["Ndamba"] = "ndj", ["Ndambomo"] = "nxo", ["Ndasa"] = "nda", ["Ndau"] = "ndc", ["Nde-Gbite"] = "ned", ["Nde-Nsele-Nta"] = "ndd", ["Ndemli"] = "nml", ["Ndendeule"] = "dne", ["Ndengereko"] = "ndg", ["Nding"] = "eli", ["Ndjébbana"] = "djj", ["Ndo"] = "ndp", ["Ndobo"] = "ndw", ["Ndoe"] = "nbb", ["Ndogo"] = "ndz", ["Ndolo"] = "ndl", ["Ndom"] = "nqm", ["Ndombe"] = "ndq", ["Ndonga"] = "ng", ["Ndoola"] = "ndr", ["Ndrulo"] = "dno", ["Nduga"] = "ndx", ["Ndumu"] = "nmd", ["Ndunda"] = "nuh", ["Ndunga"] = "ndt", ["Ndut"] = "ndv", ["Ndyuka-Trio Pidgin"] = "njt", ["Ndzwani Comorian"] = "wni", ["Nedebang"] = "nec", ["Nefamese"] = "nef", ["Nefusa"] = "jbn", ["Negeri Sembilan Malay"] = "zmi", ["Negidal"] = "neg", ["Nehan"] = "nsn", ["Nek"] = "nif", ["Nekgini"] = "nkg", ["Neko"] = "nej", ["Neku"] = "nek", ["Neme"] = "nex", ["Nemi"] = "nem", ["Nen"] = "nqn", ["Nend"] = "anh", ["Nengone"] = "nen", ["Neo"] = "neu", ["Nepali Kurux"] = "kxl", ["Nete"] = "net", ["Neve'ei"] = "vnm", ["Neverver"] = "lgk", ["New River Shasta"] = "nai-nrs", ["New Zealand Sign Language"] = "nzs", ["Neyo"] = "ney", ["Nez Perce"] = "nez", ["Nga La"] = "hlt", ["Ngaanyatjarra"] = "ntj", ["Ngadha"] = "nxg", ["Ngadjunmaya"] = "nju", ["Ngadjuri"] = "jui", ["Ngaing"] = "nnf", ["งาจู"] = "nij", ["Ngala"] = "nud", ["Ngalakan"] = "nig", ["Ngalkbun"] = "ngk", ["Ngalum"] = "szb", ["Ngam"] = "nmc", ["Ngamambo"] = "nbv", ["Ngambay"] = "sba", ["Ngamini"] = "nmv", ["Ngamo"] = "nbh", ["Ngan'gityemerri"] = "nam", ["Nganakarti"] = "xnk", ["Nganasan"] = "nio", ["Ngandi"] = "nid", ["Ngando (Central African Republic)"] = "ngd", ["Ngando (Congo)"] = "nxd", ["Ngandyera"] = "nne", ["Ngangam"] = "gng", ["Ngantangarra"] = "ntg", ["Nganyaywana"] = "nyx", ["Ngardi"] = "rxd", ["Ngarigu"] = "xni", ["Ngarinman"] = "nbj", ["Ngarinyin"] = "ung", ["Ngarla"] = "nrk", ["Ngarluma"] = "nrl", ["Ngarrindjeri"] = "nay", ["Ngas"] = "anc", ["Ngasa"] = "nsg", ["Ngatik Men's Creole"] = "ngm", ["Ngawn Chin"] = "cnw", ["Ngawun"] = "nxn", ["Ngazidja Comorian"] = "zdj", ["Ngbaka"] = "nga", ["Ngbaka Ma'bo"] = "nbm", ["Ngbaka Manza"] = "ngg", ["Ngbee"] = "jgb", ["Ngbinda"] = "nbd", ["Ngbundu"] = "nuu", ["Ngelima"] = "agh", ["Ngemba"] = "nge", ["Ngen of Djonkro"] = "gnj", ["Ngendelengo"] = "nql", ["Ngeq"] = "ngt", ["Ngete"] = "nnn", ["Nggem"] = "nbq", ["Nggwahyi"] = "ngx", ["Ngie"] = "ngj", ["Ngiemboon"] = "nnh", ["Ngile"] = "jle", ["Ngindo"] = "nnq", ["Ngiti"] = "niy", ["Ngiyambaa"] = "wyb", ["Ngizim"] = "ngi", ["Ngkoth"] = "aus-ngk", ["Ngkâlmpw Kanum"] = "kcd", ["Ngochang"] = "tbq-ngo", ["Ngom"] = "nra", ["Ngomba"] = "jgo", ["Ngombale"] = "nla", ["Ngombe (Central African Republic)"] = "nmj", ["Ngombe (Congo)"] = "ngc", ["Ngong"] = "nnx", ["Ngongo"] = "noq", ["Ngoni"] = "ngo", ["Ngoreme"] = "ngq", ["Ngoshie"] = "nsh", ["Ngul"] = "nlo", ["Ngulu"] = "ngp", ["Nguluwan"] = "nuw", ["Ngumbi"] = "nui", ["Ngunawal"] = "xul", ["Ngundi"] = "ndn", ["Ngundu"] = "nue", ["Ngungwel"] = "ngz", ["Ngurmbur"] = "nrx", ["Ngwaba"] = "ngw", ["Ngwe"] = "nwe", ["Ngwo"] = "ngn", ["Ngäbere"] = "gym", ["Nhanda"] = "nha", ["Nheengatu"] = "yrl", ["Nhirrpi"] = "hrp", ["Nhuwala"] = "nhf", ["Nias"] = "nia", ["Nicaraguan Creole"] = "bzk", ["Nicaraguan Sign Language"] = "ncs", ["Nicola"] = "ath-nic", ["Niellim"] = "nie", ["Nigeria Mambila"] = "mzk", ["Nigerian Pidgin"] = "pcm", ["Nigerian Sign Language"] = "nsi", ["Nihali"] = "nll", ["Nii"] = "nii", ["Niksek"] = "gbe", ["Nila"] = "nil", ["Nilamba"] = "nim", ["Nimadi"] = "noe", ["Nimanbur"] = "nmp", ["Nimbari"] = "nmr", ["Nimboran"] = "nir", ["Nimi"] = "nis", ["Nimo"] = "niw", ["Nimoa"] = "nmw", ["Ninam"] = "shb", ["Nindi"] = "nxi", ["Ningera"] = "nby", ["Ninggerum"] = "nxr", ["Ningil"] = "niz", ["Ninia Yali"] = "nlk", ["Ninzo"] = "nin", ["Nipsan"] = "nps", ["Nisa"] = "njs", ["Nisenan"] = "nsz", ["Nisga'a"] = "ncg", ["Nisi"] = "yso", ["Niuafo'ou"] = "num", ["Niuatoputapu"] = "nkp", ["Nivaclé"] = "cag", ["Nivkh"] = "niv", ["Niwer Mil"] = "hrc", ["Niya Prakrit"] = "pra-niy", ["Njalgulgule"] = "njl", ["Njebi"] = "nzb", ["Njen"] = "njj", ["Njerep"] = "njr", ["Njyem"] = "njy", ["Nkami"] = "nkq", ["Nkangala"] = "nkn", ["Nkari"] = "nkz", ["Nkem-Nkum"] = "isi", ["Nkhumbi"] = "khu", ["Nkongho"] = "nkc", ["Nkonya"] = "nko", ["Nkoroo"] = "nkx", ["Nkoya"] = "nka", ["Nkukoli"] = "nbo", ["Nkutu"] = "nkw", ["Nnam"] = "nbp", ["Noakhali"] = "oak", ["Nobiin"] = "fia", ["Nobonob"] = "gaw", ["Nocamán"] = "nom", ["Nocte"] = "njb", ["Nogai"] = "nog", ["Noiri"] = "noi", ["Nokuku"] = "nkk", ["Nomaande"] = "lem", ["Nomane"] = "nof", ["Nomatsiguenga"] = "not", ["Nomlaki"] = "nol", ["Nomu"] = "noh", ["Nonuya"] = "noj", ["Nooksack"] = "nok", ["Noon"] = "snf", ["Noone"] = "nhu", ["Nootka"] = "nuk", ["Nopala Chatino"] = "cya", ["Noric"] = "nrc", ["Norn"] = "nrn", ["Norra"] = "nrr", ["North Ambrym"] = "mmg", ["North Asmat"] = "nks", ["North Awyu"] = "yir", ["North Babar"] = "bcd", ["North Boma"] = "boh", ["North Central Mixe"] = "neq", ["North Efate"] = "llp", ["North Fali"] = "fll", ["North Giziga"] = "gis", ["North Levantine Arabic"] = "apc", ["North Marquesan"] = "mrq", ["North Mofu"] = "mfk", ["มลายูแบบโมลุกกะเหนือ"] = "max", ["North Muyu"] = "kti", ["North Nuaulu"] = "nni", ["North Picene"] = "nrp", ["North Slavey"] = "scs", ["North Tairora"] = "tbg", ["North Tanna"] = "tnn", ["North Wahgi"] = "whg", ["North Watut"] = "una", ["Northeast Kiwai"] = "kiw", ["Northeast Maidu"] = "nmu", ["Northeast Malakula"] = "upv", ["Northeast Pashayi"] = "aee", ["Northeastern Dinka"] = "dip", ["Northeastern Pomo"] = "pef", ["Northern Alta"] = "aqn", ["Northern Bontoc"] = "rbk", ["Northern Catanduanes Bicolano"] = "cts", ["Northern Dagara"] = "dgi", ["Northern East Cree"] = "crl", ["Northern Emberá"] = "emp", ["Northern Ghale"] = "ghh", ["Northern Grebo"] = "gbo", ["Northern Guiyang Hmong"] = "huj", ["Northern Haida"] = "hdn", ["Northern Hindko"] = "hno", ["Northern Huishui Hmong"] = "hmi", ["Northern Kalapuya"] = "nrt", ["Northern Khanty"] = "kca-nor", ["Northern Kissi"] = "kqs", ["Northern Lorung"] = "lbr", ["Northern Luri"] = "lrc", ["Northern Mansi"] = "mns-nor", ["Northern Mashan Hmong"] = "hmp", ["Northern Muji"] = "ymx", ["Northern Ndebele"] = "nd", ["Northern Ngbandi"] = "ngb", ["Northern Nisu"] = "yiv", ["Northern Nuni"] = "nuv", ["Northern Oaxaca Nahuatl"] = "nhy", ["Northern Ohlone"] = "cst", ["Northern One"] = "onr", ["Northern Paiute"] = "pao", ["Northern Pame"] = "pmq", ["Northern Pomo"] = "pej", ["Northern Puebla Nahuatl"] = "ncj", ["Northern Pumi"] = "pmi", ["Northern Qiandong Miao"] = "hea", ["Northern Qiang"] = "cng", ["Northern Rengma Naga"] = "nnl", ["Northern Roglai"] = "rog", ["Northern Saharan Berber"] = "mzb", ["Northern Selkup"] = "sel-nor", ["Northern Sierra Miwok"] = "nsq", ["Northern Subanen"] = "stb", ["Northern Tarahumara"] = "thh", ["Northern Tepehuan"] = "ntp", ["Northern Tidung"] = "ntd", ["Northern Tlaxiaco Mixtec"] = "xtn", ["Northern Toussian"] = "tsp", ["Northern Tujia"] = "tji", ["Northern Tutchone"] = "ttm", ["Northern Valley Yokuts"] = "yok-nvy", ["Northern Yukaghir"] = "ykg", ["Northwest Gbaya"] = "gya", ["Northwest Maidu"] = "mjd", ["Northwest Oaxaca Mixtec"] = "mxa", ["Northwest Pashayi"] = "glh", ["Northwestern Dinka"] = "diw", ["Northwestern Fars"] = "faz", ["Northwestern Ojibwa"] = "ojb", ["Northwestern Tamang"] = "tmk", ["Norwegian Sign Language"] = "nsl", ["Notre"] = "bly", ["Notsi"] = "ncf", ["Nottoway"] = "ntw", ["Nottoway-Meherrin"] = "nwy", ["Noy"] = "noy", ["Nsari"] = "asj", ["Nsenga"] = "nse", ["Nshi"] = "nsc", ["Nsong"] = "soo", ["Nsongo"] = "nsx", ["Ntcham"] = "bud", ["Ntomba"] = "nto", ["Ntra'ngith"] = "dgt", ["Nubaca"] = "baf", ["Nubi"] = "kcn", ["Nuer"] = "nus", ["Nuguria"] = "nur", ["Nuk"] = "noc", ["Nukak Makú"] = "mbr", ["Nukna"] = "klt", ["Nukuini"] = "nuc", ["Nukumanu"] = "nuq", ["Nukunu"] = "nnv", ["Nukunul"] = "xnu", ["Nukuoro"] = "nkr", ["Numana"] = "nbr", ["Numanggang"] = "nop", ["Numbami"] = "sij", ["Nume"] = "tgs", ["Numidian"] = "nxm", ["Numèè"] = "kdk", ["Nungali"] = "nug", ["Nunggubuyu"] = "nuy", ["Nungon"] = "paa-nun", ["Nungu"] = "rin", ["Nupbikha"] = "npb", ["Nupe"] = "nup", ["Nusa Laut"] = "nul", ["Nusu"] = "nuf", ["Nutabe"] = "cba-nut", ["Nyabwa"] = "nwb", ["Nyagrong Minyag"] = "ero-nya", ["Nyaheun"] = "nev", ["Nyakyusa"] = "nyy", ["Nyali"] = "nlj", ["Nyam"] = "nmi", ["Nyamal"] = "nly", ["Nyambo"] = "now", ["Nyamusa-Molo"] = "nwm", ["Nyamwanga"] = "mwn", ["Nyamwezi"] = "nym", ["Nyaneka"] = "nyk", ["Nyang'i"] = "nyp", ["Nyanga (Congo)"] = "nyj", ["Nyanga (Togo)"] = "ayg", ["Nyanga-li"] = "nyc", ["Nyangatom"] = "nnj", ["Nyangbo"] = "nyb", ["Nyangga"] = "nny", ["Nyangumarta"] = "nna", ["Nyankole"] = "nyn", ["Nyarafolo Senoufo"] = "sev", ["Nyaturu"] = "rim", ["Nyawaygi"] = "nyt", ["Nyelâyu"] = "yly", ["Nyemba"] = "nba", ["Nyengo"] = "nye", ["Nyenkha"] = "neh", ["Nyeu"] = "nyl", ["Nyigina"] = "nyh", ["Nyiha"] = "nih", ["Nyika"] = "nkt", ["Nyimang"] = "nyi", ["Nyindrou"] = "lid", ["Nyindu"] = "nyg", ["Nyishi"] = "njz", ["Nyiyaparli"] = "xny", ["Nyokon"] = "nvo", ["Nyole (Kenya)"] = "nyd", ["Nyole (Uganda)"] = "nuj", ["Nyong"] = "muo", ["Nyoro"] = "nyo", ["Nyulnyul"] = "nyv", ["Nyunga"] = "nys", ["Nyungwe"] = "nyu", ["Nzadi"] = "nzd", ["Nzakambay"] = "nzy", ["Nzakara"] = "nzk", ["Nzanyi"] = "nja", ["Nzima"] = "nzi", ["Ná-Meo"] = "neo", ["Nüpode Huitoto"] = "hux", ["Nǀuu"] = "ngh", ["O'chi'chi'"] = "xoc", ["O'du"] = "tyh", ["O'odham"] = "ood", ["Obanliku"] = "bzy", ["Obispeño"] = "obi", ["Oblo"] = "obl", ["Obo Manobo"] = "obo", ["Obokuitai"] = "afz", ["Obolo"] = "ann", ["Obulom"] = "obu", ["Ocaina"] = "oca", ["Ocotepec Mixtec"] = "mie", ["Ocotlán Zapotec"] = "zac", ["Od"] = "odk", ["Odiai"] = "bhf", ["Odoodee"] = "kkc", ["Odual"] = "odu", ["Odut"] = "oda", ["Ofayé"] = "opy", ["Ofo"] = "ofo", ["Ogbah"] = "ogc", ["Ogbia"] = "ogb", ["Ogbogolo"] = "ogg", ["Ogbronuagum"] = "ogu", ["Ogea"] = "eri", ["Oirata"] = "oia", ["Ojitlán Chinantec"] = "chj", ["Okanagan"] = "oka", ["Okiek"] = "oki", ["Oko-Eni-Osayen"] = "oks", ["Oko-Juwoi"] = "okj", ["Okobo"] = "okb", ["Okodia"] = "okd", ["Okolod"] = "kqv", ["Okpamheri"] = "opa", ["Okpe (Northwestern Edo)"] = "okx", ["Okpe (Southwestern Edo)"] = "oke", ["Okpela"] = "atg", ["Oksapmin"] = "opm", ["Oku"] = "oku", ["Okwanuchu"] = "nai-okw", ["Old Awadhi"] = "inc-oaw", ["Old Gutnish"] = "gmq-ogt", ["Old Kannada"] = "dra-okn", ["Old Kentish Sign Language"] = "okl", ["Old Komi"] = "urj-koo", ["Old Median"] = "xme-old", ["นาวาร์โร-อารากอนเก่า"] = "roa-ona", ["Old Novgorodian"] = "zle-ono", ["Old Prussian"] = "prg", ["รูซินเก่า"] = "zle-ort", ["อาหรับใต้เก่า"] = "sem-srb", ["Old Sundanese"] = "osn", ["Old Tati"] = "xme-ott", ["Old Telugu"] = "dra-ote", ["Olekha"] = "ole", ["Ollari"] = "gdb", ["Olo"] = "ong", ["Oloma"] = "olm", ["Olrat"] = "olr", ["Olu'bo"] = "lul", ["Olukumi"] = "ulb", ["Olulumo-Ikom"] = "iko", ["Oluta Popoluca"] = "plo", ["Olutsotso"] = "lto", ["Omagua"] = "omg", ["Omaha-Ponca"] = "oma", ["Omba"] = "omb", ["Ombamba"] = "mbm", ["Ombo"] = "oml", ["Ometepec Nahuatl"] = "nht", ["Omi"] = "omi", ["Omok"] = "omk", ["Omotik"] = "omt", ["Omurano"] = "omu", ["Oneida"] = "one", ["Ong"] = "oog", ["Ongota"] = "bxe", ["Onin"] = "oni", ["Onjob"] = "onj", ["Ono"] = "ons", ["Onobasulu"] = "onn", ["Onondaga"] = "ono", ["Ontenu"] = "ont", ["Ontong Java"] = "ojv", ["Oorlams"] = "oor", ["Opao"] = "opo", ["Opata"] = "opt", ["Opuuo"] = "lgn", ["Opón"] = "sai-opo", ["Oraon Sadri"] = "sdr", ["Orejón"] = "ore", ["Oring"] = "org", ["Orizaba Nahuatl"] = "nlv", ["Orléanais"] = "roa-orl", ["Ormu"] = "orz", ["Ormuri"] = "oru", ["Oro"] = "orx", ["Oro Win"] = "orw", ["Oroch"] = "oac", ["Oroha"] = "ora", ["Orok"] = "oaa", ["Orokaiva"] = "okv", ["Oroko"] = "bdu", ["Orokolo"] = "oro", ["Oroqen"] = "orh", ["Orowe"] = "bpk", ["Oruma"] = "orr", ["Orya"] = "ury", ["Osage"] = "osa", ["Osamayi"] = "syx", ["Osatu"] = "ost", ["Oscan"] = "osc", ["Ososo"] = "oso", ["Ot Danum"] = "otd", ["Otank"] = "uta", ["Oti"] = "oti", ["Otomaco"] = "sai-oto", ["Otoro"] = "otr", ["Ottawa"] = "otw", ["Otuke"] = "otu", ["Ouma"] = "oum", ["Oune"] = "oue", ["Owa"] = "stn", ["Owenia"] = "wsr", ["Owiniga"] = "owi", ["Oy"] = "oyb", ["Oya'oya"] = "oyy", ["Oyda"] = "oyd", ["Ozolotepec Zapotec"] = "zao", ["Ozumacín Chinantec"] = "chz", ["Pa"] = "ppt", ["Pa Di"] = "pdi", ["Pa'a"] = "pqa", ["Pa-Hng"] = "pha", ["Paamese"] = "pma", ["Paasaal"] = "sig", ["Pacahuara"] = "pcp", ["Padoe"] = "pdo", ["Paelignian"] = "pgn", ["Paeonian"] = "ine-pae", ["Pagi"] = "pgi", ["Pagibete"] = "pae", ["Pagu"] = "pgu", ["Pahanan Agta"] = "apf", ["Pahari-Potwari"] = "phr", ["Pahi"] = "lgt", ["Pahlavani"] = "phv", ["Pai Tavytera"] = "pta", ["Paicî"] = "pri", ["Paikoneka"] = "awd-pai", ["Paipai"] = "ppi", ["Paite"] = "pck", ["Pajapan Nahuatl"] = "nhp", ["Pak-Tong"] = "pkg", ["Pakanha"] = "pkn", ["Pakistan Sign Language"] = "pks", ["Paku"] = "pku", ["Paku Karen"] = "jkp", ["Pal"] = "abw", ["Palaic"] = "plq", ["Palaka Senoufo"] = "plr", ["Palantla Chinantec"] = "cpa", ["Palawan Batak"] = "bya", ["Paleni"] = "pnl", ["Palenquero"] = "pln", ["Palewyami Yokuts"] = "yok-ply", ["Palikur"] = "plu", ["Paliyan"] = "pcf", ["Pallanganmiddang"] = "pmd", ["Palor"] = "fap", ["Palta"] = "sai-pal", ["Palu'e"] = "ple", ["Paluan"] = "plz", ["Palula"] = "phl", ["Palya Bareli"] = "bpx", ["Pam"] = "pmn", ["Pambia"] = "pmb", ["Pamigua"] = "sai-pam", ["Pamlico"] = "pmk", ["Pamona"] = "pmf", ["Pamosu"] = "hih", ["Pamplona Atta"] = "att", ["Pana (Central Africa)"] = "pnz", ["Pana (West Africa)"] = "pnq", ["Panamanian Sign Language"] = "lsp", ["Panamint"] = "par", ["Panare"] = "pbh", ["Panará"] = "kre", ["Panasuan"] = "psn", ["Panawa"] = "pwb", ["Pancana"] = "pnp", ["Panchpargania"] = "tdb", ["Pande"] = "bkj", ["Pangasinan"] = "pag", ["Pangseng"] = "pgs", ["Pangwa"] = "pbr", ["Panim"] = "pnr", ["Paniya"] = "pcg", ["Pankararé"] = "pax", ["Pankararú"] = "paz", ["Pankhu"] = "pkh", ["Pannei"] = "pnc", ["Panobo"] = "pno", ["Panyi Bai"] = "bfc", ["Panyjima"] = "pnw", ["Panzaleo"] = "sai-pnz", ["Pao"] = "ppa", ["Papantla Totonac"] = "top", ["Papapana"] = "ppn", ["Papar"] = "dpp", ["Papasena"] = "pas", ["Papel"] = "pbo", ["Papi"] = "ppe", ["Papora"] = "ppu", ["Papua New Guinean Sign Language"] = "pgz", ["Papuan Malay"] = "pmy", ["Papuma"] = "ppm", ["Para Naga"] = "pzn", ["Parachi"] = "prc", ["Paraguayan Sign Language"] = "pys", ["Parakanã"] = "pak", ["Paranan"] = "prf", ["Paranawát"] = "paf", ["Paratió"] = "sai-par", ["Paraujano"] = "pbg", ["Parauk"] = "prk", ["Parawen"] = "prw", ["Pardhan"] = "pch", ["Pardhi"] = "pcl", ["Pare"] = "asa", ["Pareci"] = "pab", ["Paredarerme"] = "xpd", ["Parenga"] = "pcj", ["Parkari Koli"] = "kvx", ["พาร์เทีย"] = "xpr", ["Parya"] = "paq", ["Pará Gavião"] = "gvp", ["Pasi"] = "psq", ["Pass Valley Yali"] = "yac", ["Pasé"] = "awd-pas", ["Patagón"] = "sai-ptg", ["Patamona"] = "pbc", ["Patani"] = "ptn", ["Pataxó Hã-Ha-Hãe"] = "pth", ["Patep"] = "ptp", ["Pathiya"] = "pty", ["Patpatar"] = "gfk", ["Pattani"] = "lae", ["Pattapu"] = "ptq", ["Patwin"] = "pwi", ["Paulohi"] = "plh", ["Paumarí"] = "pad", ["Paunaka"] = "pnk", ["Pauri Bareli"] = "bfb", ["Pauserna"] = "psm", ["Pawaia"] = "pwa", ["Pawnee"] = "paw", ["Payaguá"] = "sai-pyg", ["Paynamar"] = "pmr", ["Pazeh"] = "pzh", ["Pe"] = "pai", ["ปอร์"] = "pcb", ["Peba"] = "sai-peb", ["Pech"] = "pay", ["Pecheneg"] = "xpc", ["Peerapper"] = "xpw", ["Peere"] = "pfe", ["Pei"] = "ppq", ["Pekal"] = "pel", ["Pela"] = "bxd", ["Pele-Ata"] = "ata", ["Pemon"] = "aoc", ["Penang Sign Language"] = "psg", ["Penchal"] = "pek", ["Pendau"] = "ums", ["Pengo"] = "peg", ["Penobscot"] = "aaq", ["Penrhyn"] = "pnh", ["Pentlatch"] = "ptw", ["Perai"] = "wet", ["Peranakan Indonesian"] = "pea", ["Perema"] = "wom", ["Pericú"] = "nai-per", ["Pero"] = "pip", ["Persian Sign Language"] = "psc", ["Peruvian Sign Language"] = "prl", ["Petapa Zapotec"] = "zpe", ["Petats"] = "pex", ["Petjo"] = "pey", ["Peñoles Mixtec"] = "mil", ["Phai"] = "prt", ["Phala"] = "ypa", ["Phana'"] = "phq", ["Phangduwali"] = "phw", ["Phende"] = "pem", ["Philippine Sign Language"] = "psp", ["Philistine"] = "mis-phi", ["Phimbi"] = "phm", ["Phola"] = "ypg", ["Pholo"] = "yip", ["Phom"] = "nph", ["Phong-Kniang"] = "pnx", ["Phrygian"] = "xpg", ["Phudagi"] = "phd", ["Phuie"] = "pug", ["Phukha"] = "phh", ["Phuma"] = "ypm", ["Phuong"] = "phg", ["Phupa"] = "ypp", ["Phupha"] = "yph", ["Phuthi"] = "bnt-phu", ["Phuza"] = "ypz", ["Piamatsina"] = "ptr", ["Piame"] = "pin", ["Piapoco"] = "pio", ["Piaroa"] = "pid", ["Pichinglis"] = "fpe", ["Pichis Ashéninka"] = "cpu", ["Pictish"] = "xpi", ["Picuris"] = "nai-pic", ["Pidgin Delaware"] = "dep", ["Pidgin Iha"] = "ihb", ["Pidgin Onin"] = "onx", ["Pijao"] = "pij", ["Pije"] = "piz", ["Pijin"] = "pis", ["Pilagá"] = "plg", ["Pileni"] = "piv", ["Pima Bajo"] = "pia", ["Pimbwe"] = "piw", ["Pinai-Hagahai"] = "pnn", ["Pingelapese"] = "pif", ["Pini"] = "pii", ["Pinigura"] = "pnv", ["Pinjarup"] = "pnj", ["Pinji"] = "pic", ["Pinotepa Nacional Mixtec"] = "mio", ["Pintiini"] = "pti", ["Pintupi-Luritja"] = "piu", ["Pinyin"] = "pny", ["Pirahã"] = "myp", ["Piratapuyo"] = "pir", ["Pirlatapa"] = "bxi", ["Piro"] = "pie", ["Pirriya"] = "xpa", ["Pisabo"] = "pig", ["Pisaflores Tepehua"] = "tpp", ["Piscataway"] = "psy", ["Pisidian"] = "xps", ["Pitcairn-Norfolk"] = "pih", ["Piti"] = "pcn", ["Pitjantjatjara"] = "pjt", ["Pitta-Pitta"] = "pit", ["Piu"] = "pix", ["Piya-Kwonci"] = "piy", ["Plains Apache"] = "apk", ["Plains Cree"] = "crk", ["Plains Indian Sign Language"] = "psd", ["Plains Miwok"] = "pmw", ["Plapo Krumen"] = "ktj", ["Playero"] = "gob", ["Pochuri Naga"] = "npo", ["Pochutec"] = "xpo", ["Podoko"] = "pbi", ["Pogali"] = "hkh", ["Pogolo"] = "poy", ["Pohnpeian"] = "pon", ["ปัวเตอแว็ง-แซ็งต็องแฌ"] = "roa-poi", ["Pokangá"] = "pok", ["Poke"] = "pof", ["Pol"] = "pmm", ["Polabian"] = "pox", ["Polci"] = "plj", ["Polish Sign Language"] = "pso", ["Polonombauk"] = "plb", ["Pom"] = "pmo", ["Ponam"] = "ncc", ["Pongu"] = "png", ["Ponosakan"] = "pns", ["Pontic Greek"] = "pnt", ["Ponyo"] = "npg", ["Poqomam"] = "poc", ["Poqomchi'"] = "poh", ["Porohanon"] = "prh", ["Port Sandwich"] = "psw", ["Port Sorell"] = "xpl", ["Port Vato"] = "ptv", ["Portuguese Sign Language"] = "psr", ["Potawatomi"] = "pot", ["Potiguára"] = "pog", ["Poumei Naga"] = "pmx", ["Pouye"] = "bye", ["Powari"] = "pwr", ["Powhatan"] = "pim", ["Poyanáwa"] = "pyn", ["Prasuni"] = "prn", ["Pre-Samnite"] = "itc-psa", ["Primitive Irish"] = "pgl", ["Principense"] = "pre", ["Proto-Abkhaz-Abaza"] = "cau-abz-pro", ["Proto-Amuesha-Chamicuro"] = "awd-amc-pro", ["Proto-Andian"] = "cau-and-pro", ["Proto-Apachean"] = "apa-pro", ["Proto-Arawa"] = "auf-pro", ["Proto-Arawak"] = "awd-pro", ["Proto-Arnhem"] = "aus-arn-pro", ["Proto-Aroid"] = "omv-aro-pro", ["Proto-Aslian"] = "mkh-asl-pro", ["Proto-Atayalic"] = "map-ata-pro", ["Proto-Athabaskan"] = "ath-pro", ["Proto-Avaro-Andian"] = "cau-ava-pro", ["Proto-Bahnaric"] = "mkh-ban-pro", ["Proto-Bai"] = "sit-bai-pro", ["Proto-Batak"] = "btk-pro", ["Proto-Binanderean"] = "paa-bin-pro", ["Proto-Bodish"] = "sit-bdi-pro", ["Proto-Bongo-Bagirmi"] = "csu-bba-pro", ["Proto-Boran"] = "sai-bor-pro", ["Proto-Bua"] = "alv-bua-pro", ["Proto-Bungku-Tolaki"] = "poz-btk-pro", ["Proto-Burmish"] = "tbq-brm-pro", ["Proto-Caddoan"] = "cdd-pro", ["Proto-Cangin"] = "alv-cng-pro", ["Proto-Cariban"] = "sai-car-pro", ["Proto-Central Jê"] = "sai-cje-pro", ["Proto-Central Naga"] = "sit-aao-pro", ["Proto-Central New South Wales"] = "aus-cww-pro", ["Proto-Central Sudanic"] = "csu-pro", ["Proto-Central Togo"] = "alv-gtm-pro", ["Proto-Cerrado"] = "sai-cer-pro", ["Proto-Chatino"] = "omq-cha-pro", ["Proto-Chibchan"] = "cba-pro", ["Proto-Chimakuan"] = "chi-pro", ["Proto-Chinookan"] = "nai-ckn-pro", ["Proto-Chukotko-Kamchatkan"] = "qfa-cka-pro", ["Proto-Chumash"] = "nai-chu-pro", ["เซอร์แคสเซียนดั้งเดิม"] = "cau-cir-pro", ["Proto-Cupan"] = "azc-cup-pro", ["Proto-Cushitic"] = "cus-pro", ["Proto-Daju"] = "sdv-daj-pro", ["Proto-Daly"] = "aus-dal-pro", ["Proto-Dangari"] = "inc-dng-pro", ["Proto-Dargwa"] = "cau-drg-pro", ["Proto-Dizoid"] = "omv-diz-pro", ["Proto-Eastern Jebel"] = "sdv-eje-pro", ["Proto-Eastern Oti-Volta"] = "nic-eov-pro", ["Proto-Edekiri"] = "alv-edk-pro", ["Proto-Edoid"] = "alv-edo-pro", ["Proto-Ersuic"] = "sit-ers-pro", ["Proto-Eskimo-Aleut"] = "esx-pro", ["Proto-Fali"] = "alv-fli-pro", ["Proto-Gbaya"] = "gba-pro", ["Proto-Grassfields"] = "nic-grf-pro", ["Proto-Guang"] = "alv-gng-pro", ["Proto-Gurunsi"] = "nic-gns-pro", ["Proto-Halmahera-Cenderawasih"] = "poz-hce-pro", ["Proto-Heiban"] = "alv-hei-pro", ["Proto-Highland East Cushitic"] = "cus-hec-pro", ["Proto-Hrusish"] = "sit-hrs-pro", ["Proto-Hurro-Urartian"] = "qfa-hur-pro", ["Proto-Idomoid"] = "alv-ido-pro", ["Proto-Igboid"] = "alv-igb-pro", ["Proto-Ijoid"] = "ijo-pro", ["Proto-Iroquoian"] = "iro-pro", ["Proto-Iwaidjan"] = "aus-wdj-pro", ["Proto-Jukunoid"] = "nic-jkn-pro", ["Proto-Jê"] = "sai-jee-pro", ["Proto-Kadu"] = "qfa-kad-pro", ["Proto-Kalapuyan"] = "nai-klp-pro", ["Proto-Kam-Sui"] = "qfa-kms-pro", ["Proto-Kampa"] = "awd-kmp-pro", ["Proto-Kamta"] = "inc-krd-pro", ["Proto-Katuic"] = "mkh-kat-pro", ["Proto-Kham"] = "sit-kha-pro", ["Proto-Khanty"] = "kca-pro", ["Proto-Kho-Bwa"] = "sit-khb-pro", ["Proto-Khoe"] = "khi-kho-pro", ["Proto-Koman"] = "ssa-kom-pro", ["Proto-Komisenian"] = "ira-kms-pro", ["Proto-Kru"] = "kro-pro", ["Proto-Kuki-Chin"] = "tbq-kuk-pro", ["Proto-Kuliak"] = "ssa-klk-pro", ["Proto-Kwa"] = "alv-kwa-pro", ["Proto-Lalo"] = "tbq-lal-pro", ["Proto-Lezghian"] = "cau-lzg-pro", ["Proto-Lower Cross River"] = "nic-lcr-pro", ["Proto-Maidun"] = "nai-mdu-pro", ["Proto-Mande"] = "dmn-pro", ["Proto-Mangbetu"] = "csu-maa-pro", ["Proto-Mansi"] = "mns-pro", ["Proto-Mari"] = "chm-pro", ["Proto-Masa"] = "cdc-mas-pro", ["Proto-Mazatec"] = "omq-maz-pro", ["Proto-Medo-Parthian"] = "ira-mpr-pro", ["Proto-Mixe-Zoque"] = "nai-miz-pro", ["Proto-Mixtec"] = "omq-mxt-pro", ["Proto-Mixtecan"] = "omq-mix-pro", ["Proto-Mordvinic"] = "urj-mdv-pro", ["Proto-Mumuye"] = "alv-mum-pro", ["Proto-Munji-Yidgha"] = "ira-mny-pro", ["Proto-Muskogean"] = "nai-mus-pro", ["Proto-Na-Dene"] = "xnd-pro", ["Proto-Naish"] = "sit-nas-pro", ["Proto-Nawiki"] = "awd-nwk-pro", ["Proto-Nilo-Saharan"] = "ssa-pro", ["Proto-Nilotic"] = "sdv-nil-pro", ["Proto-North Halmahera"] = "paa-nha-pro", ["Proto-North Iroquoian"] = "iro-nor-pro", ["Proto-Northern Jê"] = "sai-nje-pro", ["Proto-Northern Naga"] = "sit-kon-pro", ["Proto-Numic"] = "azc-num-pro", ["Proto-Nupoid"] = "alv-nup-pro", ["Proto-Nuristani"] = "iir-nur-pro", ["Proto-Nyima"] = "sdv-nyi-pro", ["Proto-Nyulnyulan"] = "aus-nyu-pro", ["Proto-Ogoni"] = "nic-ogo-pro", ["Proto-Omotic"] = "omv-pro", ["Proto-Ongan"] = "qfa-ong-pro", ["Proto-Oti-Volta"] = "nic-ovo-pro", ["Proto-Oto-Manguean"] = "omq-pro", ["Proto-Oto-Pamean"] = "omq-otp-pro", ["Proto-Otomi"] = "oto-otm-pro", ["Proto-Otomian"] = "oto-pro", ["Proto-Pakanic"] = "mkh-pkn-pro", ["Proto-Pama-Nyungan"] = "aus-pam-pro", ["Proto-Paresi-Waura"] = "awd-prw-pro", ["แพริกดั้งเดิม"] = "mkh-pea-pro", ["Proto-Plateau"] = "nic-plt-pro", ["Proto-Plateau Penutian"] = "nai-plp-pro", ["Proto-Pomo"] = "nai-pom-pro", ["Proto-Puroik"] = "sit-khp-pro", ["Proto-Rukai"] = "dru-pro", ["รีวกีวอันดั้งเดิม"] = "jpx-ryu-pro", ["Proto-Sabaki"] = "bnt-sab-pro", ["Proto-Saka"] = "xsc-sak-pro", ["Proto-Saka-Wakhi"] = "xsc-skw-pro", ["Proto-Salish"] = "sal-pro", ["Proto-Samoyedic"] = "syd-pro", ["Proto-Sanglechi-Ishkashimi"] = "ira-sgi-pro", ["Proto-Sara"] = "csu-sar-pro", ["Proto-Sarmatian"] = "xsc-sar-pro", ["Proto-Scythian"] = "xsc-pro", ["Proto-Selkup"] = "sel-pro", ["Proto-Shughni-Roshani"] = "ira-shr-pro", ["Proto-Shughni-Yazghulami"] = "ira-shy-pro", ["Proto-Shughni-Yazghulami-Munji"] = "ira-sym-pro", ["Proto-Siouan"] = "sio-pro", ["Proto-Siouan-Catawban"] = "nai-sca-pro", ["Proto-Songhay"] = "son-pro", ["Proto-Sotho-Tswana"] = "bnt-sts-pro", ["Proto-South Cushitic"] = "cus-sou-pro", ["Proto-Southern Jê"] = "sai-sje-pro", ["Proto-Ta-Arawak"] = "awd-taa-pro", ["Proto-Takic"] = "azc-tak-pro", ["Proto-Taman"] = "sdv-tmn-pro", ["Proto-Tamangic"] = "sit-tam-pro", ["Proto-Tangkhulic"] = "sit-tng-pro", ["Proto-Tani"] = "sit-tan-pro", ["Proto-Taranoan"] = "sai-tar-pro", ["Proto-Tatic"] = "xme-ttc-pro", ["Proto-Totozoquean"] = "nai-tot-pro", ["Proto-Trans-New Guinea"] = "ngf-pro", ["Proto-Triqui"] = "omq-tri-pro", ["Proto-Tsezian"] = "cau-tsz-pro", ["Proto-Tsimshianic"] = "nai-tsi-pro", ["Proto-Tungusic"] = "tuw-pro", ["Proto-Ubangian"] = "nic-ubg-pro", ["Proto-Upper Cross River"] = "nic-ucr-pro", ["Proto-Utian"] = "nai-utn-pro", ["Proto-Western Kho-Bwa"] = "sit-khw-pro", ["Proto-Western Mande"] = "dmn-mdw-pro", ["Proto-Witotoan"] = "sai-wit-pro", ["Proto-Yeniseian"] = "qfa-yen-pro", ["Proto-Yukaghir"] = "qfa-yuk-pro", ["Proto-Yupik"] = "ypk-pro", ["Proto-Zapotec"] = "omq-zpc-pro", ["Proto-Zapotecan"] = "omq-zap-pro", ["Proto-Zaza-Gorani"] = "ira-zgr-pro", ["Providencia Sign Language"] = "prz", ["Psikye"] = "kvj", ["Puare"] = "pux", ["Pudtol Atta"] = "atp", ["Puebla Mazatec"] = "pbm", ["Puelche"] = "pue", ["Puerto Rican Sign Language"] = "psl", ["Puimei Naga"] = "npu", ["Puinave"] = "pui", ["Puiron"] = "sit-prn", ["Pulabu"] = "pup", ["Puluwat"] = "puw", ["Puma"] = "pum", ["Pumpokol"] = "xpm", ["Pumé"] = "yae", ["Punan Aput"] = "pud", ["Punan Bah-Biau"] = "pna", ["Punan Batu"] = "pnm", ["Punan Merah"] = "puf", ["Punan Merap"] = "puc", ["Punan Tubu"] = "puj", ["Punic"] = "xpu", ["Punu"] = "puu", ["Puoc"] = "puo", ["Puquina"] = "puq", ["Puragi"] = "pru", ["Purari"] = "iar", ["Purepecha"] = "pua", ["Puri"] = "prr", ["Purik"] = "prx", ["Purisimeño"] = "puy", ["Puroik"] = "suv", ["Puruborá"] = "pur", ["Puruhá"] = "sai-prh", ["Purukotó"] = "sai-pur", ["Purum"] = "pub", ["Putai"] = "mfl", ["Putoh"] = "put", ["Putukwam"] = "afe", ["Puyo-Paekche"] = "xpp", ["Puyuma"] = "pyu", ["Pwaamèi"] = "pme", ["Pwapwâ"] = "pop", ["Pyapun"] = "pcw", ["Pye Krumen"] = "pye", ["Pyemmairre"] = "xpb", ["Pyen"] = "pyy", ["Pykobjê"] = "sai-pyk", ["Pyu (New Guinea)"] = "pby", ["Páez"] = "pbb", ["Pááfang"] = "pfa", ["Päri"] = "lkr", ["Pémono"] = "pev", ["Pévé"] = "lme", ["Pökoot"] = "pko", ["Q'anjob'al"] = "kjb", ["Q'eqchi"] = "kek", ["Qabiao"] = "laq", ["Qaqet"] = "byx", ["Qatabanian"] = "xqt", ["Qau"] = "gqu", ["Qila Muji"] = "ymq", ["Qimant"] = "ahg", ["Quapaw"] = "qua", ["Quebec Sign Language"] = "fcs", ["Querétaro Otomi"] = "otq", ["Quetzaltepec Mixe"] = "pxm", ["Queyu"] = "qvy", ["Quiavicuzas Zapotec"] = "zpj", ["Quileute"] = "qui", ["Quimbaya"] = "sai-qmb", ["Quinault"] = "qun", ["Quinigua"] = "nai-qng", ["Quinqui"] = "quq", ["Quioquitani-Quierí Zapotec"] = "ztq", ["Quiotepec Chinantec"] = "chq", ["Quiripi"] = "qyp", ["Quitemo"] = "sai-qtm", ["Rabha"] = "rah", ["Rabona"] = "sai-rab", ["ระแด"] = "rad", ["Raetic"] = "xrr", ["Raga"] = "lml", ["Rahambuu"] = "raz", ["Rajah Kabunsuwan Manobo"] = "mqk", ["Raji"] = "rji", ["Rajong"] = "rjg", ["Rajput Garasia"] = "gra", ["Rakahanga-Manihiki"] = "rkh", ["Ralte"] = "ral", ["Rama"] = "rma", ["Ramandi"] = "tks", ["Ramanos"] = "sai-ram", ["Ramoaaina"] = "rai", ["Ramopa"] = "kjx", ["Rampi"] = "lje", ["Rana Tharu"] = "thr", ["Rang"] = "rax", ["Rangkas"] = "rgk", ["Ranglong"] = "rnl", ["Rao"] = "rao", ["Rapa"] = "ray", ["Rapoisi"] = "kyx", ["Rapting"] = "rpt", ["Rara Bakati'"] = "lra", ["Rasawa"] = "rac", ["Ratagnon"] = "btn", ["Ratahan"] = "rth", ["Rathawi"] = "rtw", ["Rathwi Bareli"] = "bgd", ["Raute"] = "rau", ["Ravula"] = "yea", ["Rawa"] = "rwo", ["Rawat"] = "jnl", ["Rawo"] = "rwa", ["Rayón Zoque"] = "zor", ["Razajerdi"] = "rat", ["Razihi"] = "rzh", ["Reang"] = "ria", ["Red Gelao"] = "gir", ["Reel"] = "atu", ["Rejang"] = "rej", ["Rejang Kayan"] = "ree", ["Reli"] = "rei", ["Rema"] = "bow", ["Rembarunga"] = "rmb", ["Rembong"] = "reb", ["Remo"] = "rem", ["Remontado Agta"] = "agv", ["Rempi"] = "rmp", ["Remun"] = "lkj", ["Rendille"] = "rel", ["Rengao"] = "ren", ["Rennellese"] = "mnv", ["Repanbitip"] = "rpn", ["Rer Bare"] = "rer", ["Rerau"] = "rea", ["Rerep"] = "pgk", ["Reshe"] = "res", ["Resígaro"] = "rgr", ["Retta"] = "ret", ["Reyesano"] = "rey", ["ฟรังโกเนียแบบไรน์"] = "gmw-rfr", ["Riang"] = "ril", ["Riantana"] = "ran", ["Ribun"] = "rir", ["Rigwe"] = "iri", ["Rikbaktsa"] = "rkb", ["Rincón Zapotec"] = "zar", ["Ringgou"] = "rgu", ["Ririo"] = "rri", ["Ritarungo"] = "rit", ["Riung"] = "riu", ["Riverain Sango"] = "snj", ["Rogo"] = "rod", ["Roma"] = "rmm", ["Romam"] = "rmx", ["Romanian Sign Language"] = "rms", ["Romano-Greek"] = "rge", ["Romano-Serbian"] = "rsb", ["Romanova"] = "rmv", ["Romblomanon"] = "rol", ["Rombo"] = "rof", ["Romkun"] = "rmk", ["Ron"] = "cla", ["Ronga"] = "rng", ["Rongga"] = "ror", ["Rongmei Naga"] = "nbu", ["Rongpo"] = "rnp", ["Ronji"] = "roe", ["Roon"] = "rnn", ["Roria"] = "rga", ["Roro"] = "rro", ["Rotokas"] = "roo", ["Rotuman"] = "rtm", ["Rouran"] = "mis-rou", ["Roviana"] = "rug", ["Ruching Palaung"] = "pce", ["Rudbari"] = "rdb", ["Rufiji"] = "rui", ["Ruga"] = "ruh", ["Rukai"] = "dru", ["Rukiga"] = "cgg", ["Ruma"] = "ruz", ["Rumai Palaung"] = "rbb", ["Rumu"] = "klq", ["Runga"] = "rou", ["Rungtu"] = "rtc", ["Rungus"] = "drg", ["Rungwa"] = "rnw", ["Russenorsk"] = "crp-rsn", ["Rutul"] = "rut", ["Ruuli"] = "ruc", ["Ruwund"] = "rnd", ["Rwa"] = "rwk", ["Rwandan Sign Language"] = "rsn", ["Réunion Creole French"] = "rcf", ["Sa"] = "sax", ["Sa'a"] = "apb", ["Sa'ban"] = "snv", ["Saafi-Saafi"] = "sav", ["Saam"] = "raq", ["Saamia"] = "lsm", ["Saanich"] = "str", ["Saare"] = "uss", ["Saaroa"] = "sxr", ["Saba"] = "saa", ["Sabaean"] = "xsa", ["Sabah Bisaya"] = "bsy", ["Sabah Malay"] = "msi", ["Sabanê"] = "sae", ["Sabaot"] = "spy", ["Sabine"] = "sbv", ["Sabir"] = "pml", ["Sabu"] = "hvn", ["Sabüm"] = "sbo", ["Sacapulteco"] = "quv", ["Sadri"] = "sck", ["Saep"] = "spd", ["Safaitic"] = "sem-saf", ["Safaliba"] = "saf", ["Safeyoka"] = "apz", ["Safwa"] = "sbk", ["Sagala"] = "sbm", ["Sagalla"] = "tga", ["Sahaptin"] = "nai-spt", ["Sahu"] = "saj", ["Saisiyat"] = "xsy", ["Sajau Basap"] = "sjb", ["Sakachep"] = "sch", ["Sakam"] = "skm", ["Sakao"] = "sku", ["Sakata"] = "skt", ["Sake"] = "sak", ["Sala"] = "shq", ["Salampasu"] = "slx", ["Salas"] = "sgu", ["Saleman"] = "sau", ["Saliba (Colombia)"] = "slc", ["Saliba (New Guinea)"] = "sbe", ["Salinan"] = "sln", ["Salt-Yui"] = "sll", ["Saluan"] = "loe", ["Salumá"] = "slj", ["Salvadoran Lenca"] = "nai-sln", ["Salvadoran Sign Language"] = "esn", ["Sam"] = "snx", ["Sama"] = "smd", ["Samalian"] = "sem-sam", ["Samaritan Aramaic"] = "sam", ["Samaritan Hebrew"] = "smp", ["Samarokena"] = "tmj", ["Samatao"] = "ysd", ["Samba"] = "smx", ["Sambalpuri"] = "spv", ["Sambe"] = "xab", ["Samberigi"] = "ssx", ["Samburu"] = "saq", ["Samei"] = "smh", ["Samo"] = "smq", ["Samoan Plantation Pidgin"] = "crp-spp", ["Samogitian"] = "sgs", ["Samosa"] = "swm", ["Sampang"] = "rav", ["Samtao"] = "stu", ["Samvedi"] = "smv", ["San Agustín Mixtepec Zapotec"] = "ztm", ["San Baltazar Loxicha Zapotec"] = "zpx", ["San Felipe Otlaltepec Popoloca"] = "pow", ["San Jerónimo Tecóatl Mazatec"] = "maa", ["San Juan Atzingo Popoloca"] = "poe", ["San Juan Colorado Mixtec"] = "mjc", ["San Juan Guelavía Zapotec"] = "zab", ["San Juan Quiahije Chatino"] = "omq-sjq", ["San Juan Teita Mixtec"] = "xtj", ["San Luís Temalacayuca Popoloca"] = "pps", ["San Marcos Tlalcoyalco Popoloca"] = "pls", ["San Martín Itunyoso Triqui"] = "trq", ["San Miguel Creole French"] = "scf", ["San Miguel Piedras Mixtec"] = "xtp", ["San Miguel el Grande Mixtec"] = "mig", ["San Pablo Güilá Zapotec"] = "ztu", ["San Pedro Amuzgos Amuzgo"] = "azg", ["San Pedro Quiatoni Zapotec"] = "zpf", ["San Vicente Coatlán Zapotec"] = "zpt", ["Sanapaná"] = "spn", ["Sanaviron"] = "sai-san", ["Sandawe"] = "sad", ["Sanga (Congo)"] = "sng", ["Sanga (Nigeria)"] = "xsn", ["Sanggau"] = "scg", ["Sangil"] = "snl", ["Sangir"] = "sxn", ["Sangisari"] = "sgr", ["Sangkong"] = "sgk", ["Sanglechi"] = "sgy", ["Sangtam Naga"] = "nsa", ["Sangu (Gabon)"] = "snq", ["Sangu (Tanzania)"] = "sbp", ["Sani"] = "ysn", ["Sanie"] = "ysy", ["Saniyo-Hiyewe"] = "sny", ["Sankaran Maninka"] = "msc", ["Sansi"] = "ssi", ["Santa Catarina Albarradas Zapotec"] = "ztn", ["Santa Inés Ahuatempan Popoloca"] = "pca", ["Santa Inés Yatzechi Zapotec"] = "zpn", ["Santa Lucía Monteverde Mixtec"] = "mdv", ["Santa María La Alta Nahuatl"] = "nhz", ["Santa María Quiegolani Zapotec"] = "zpi", ["Santa María Zacatepec Mixtec"] = "mza", ["Santa Teresa Cora"] = "cok", ["Santiago Xanica Zapotec"] = "zpr", ["Santo Domingo Albarradas Zapotec"] = "zas", ["Sanumá"] = "xsu", ["Sanxiang Min"] = "nan-zsh", ["Saparua"] = "spr", ["Sapará"] = "sai-sap", ["Sapo"] = "krn", ["Saponi"] = "spi", ["Saposa"] = "sps", ["Sapuan"] = "spu", ["Sapé"] = "spc", ["Sar"] = "mwm", ["Sara"] = "sre", ["Sara Kaba"] = "sbz", ["Sara Kaba Deme"] = "kwg", ["Sara Kaba Náà"] = "kwv", ["Saraiki"] = "skr", ["ซารามักกา"] = "srm", ["Sarangani Blaan"] = "bps", ["Sarangani Manobo"] = "mbs", ["Sarasira"] = "zsa", ["Saraveca"] = "sar", ["Sarawak Malay"] = "poz-sml", ["Sarikoli"] = "srh", ["Sarli"] = "sdf", ["Sartang"] = "onp", ["Sarua"] = "swy", ["Sarudu"] = "sdu", ["Saruga"] = "sra", ["Sasak"] = "sas", ["Sasaru"] = "sxs", ["Satawalese"] = "stw", ["Sateré-Mawé"] = "mav", ["Sathmar Swabian"] = "gmw-stm", ["Saudi Arabian Sign Language"] = "sdl", ["Saurashtra"] = "saz", ["Sauri"] = "srt", ["Sause"] = "sao", ["Sausi"] = "ssj", ["Savi"] = "sdg", ["Savosavo"] = "svs", ["Sawai"] = "szw", ["Saweru"] = "swr", ["Sawi"] = "saw", ["Sawila"] = "swt", ["Sawriya Paharia"] = "mjt", ["Saxwe Gbe"] = "sxw", ["Saya"] = "say", ["Sayula Popoluca"] = "pos", ["Scanian"] = "gmq-scy", ["Seba"] = "kdg", ["Sebat Bet Gurage"] = "sgw", ["Seberuang"] = "sbx", ["Sebop"] = "sib", ["Sebuyau"] = "snb", ["Sechelt"] = "sec", ["Sechura"] = "sai-sec", ["Secoya"] = "sey", ["Sedang"] = "sed", ["Sedoa"] = "tvw", ["Seenku"] = "sos", ["Segai"] = "sge", ["Segeju"] = "seg", ["Seget"] = "sbg", ["Sehwi"] = "sfw", ["Seim"] = "sim", ["Seimat"] = "ssg", ["Seit-Kaitetu"] = "hik", ["Sekani"] = "sek", ["Sekapan"] = "skp", ["Sekar"] = "skz", ["Seke"] = "skj", ["Sekele"] = "vaj", ["Seki"] = "syi", ["Seko Padang"] = "skx", ["Seko Tengah"] = "sko", ["Sekpele"] = "lip", ["Selangor Sign Language"] = "kgi", ["Selaru"] = "slu", ["Selayar"] = "sly", ["Selee"] = "snw", ["Selepet"] = "spl", ["Selk'nam"] = "ona", ["Selonian"] = "sxl", ["Selungai Murut"] = "slg", ["Seluwasan"] = "sws", ["Sema"] = "nsm", ["Semandang"] = "sdm", ["Semaq Beri"] = "szc", ["Sembakung Murut"] = "sbr", ["Semelai"] = "sza", ["Semigallian"] = "xzm", ["Semimi"] = "etz", ["Semnam"] = "ssm", ["Semnani"] = "smy", ["Sempan"] = "xse", ["Sena"] = "seh", ["Senara Sénoufo"] = "seq", ["Senaya"] = "syn", ["Sene"] = "sej", ["Seneca"] = "see", ["Sengele"] = "szg", ["Senggi"] = "snu", ["Sengo"] = "spk", ["Sengseng"] = "ssz", ["Senhaja de Srair"] = "sjs", ["Sensi"] = "sni", ["Sentani"] = "set", ["Senthang Chin"] = "sez", ["Sentinelese"] = "std", ["Sepa (Indonesia)"] = "spb", ["Sepa (New Guinea)"] = "spe", ["Sepen"] = "spm", ["Sepik Iwam"] = "iws", ["Sepik Mari"] = "mbx", ["Sera"] = "sry", ["Sere"] = "swf", ["Serer"] = "srr", ["Seri"] = "sei", ["Serili"] = "sve", ["Seroa"] = "kqu", ["Serrano"] = "ser", ["Seru"] = "szd", ["Serua"] = "srw", ["Serudung Murut"] = "srk", ["Serui-Laut"] = "seu", ["Seta"] = "stf", ["Setaman"] = "stm", ["Seti"] = "sbi", ["Severn Ojibwa"] = "ojs", ["Sewa Bay"] = "sew", ["Seychellois Creole"] = "crs", ["Seze"] = "sze", ["Sha"] = "scw", ["Shabak"] = "sdb", ["Shabo"] = "sbf", ["Shahmirzadi"] = "srz", ["Shahrudi"] = "shm", ["Shall-Zwall"] = "sha", ["Shama-Sambuga"] = "sqa", ["Shamang"] = "xsh", ["Shambala"] = "ksb", ["Shanenawa"] = "swo", ["Shanga"] = "sho", ["Shangzhai"] = "jih", ["Shaojiang Min"] = "sjc", ["Shaozhou Tuhua"] = "zhx-sht", ["Sharanahua"] = "mcd", ["Shark Bay"] = "ssv", ["Sharwa"] = "swq", ["Shasta"] = "sht", ["Shatt"] = "shj", ["Shau"] = "sqh", ["Shawnee"] = "sjw", ["She"] = "shx", ["Shebayo"] = "awd-she", ["Shehri"] = "shv", ["Shekkacho"] = "moy", ["Sheko"] = "she", ["Shelta"] = "sth", ["Shendu"] = "shl", ["Sheni"] = "scv", ["Sherbro"] = "bun", ["Sherdukpen"] = "sdp", ["Sheshi Kham"] = "kip", ["Shetland"] = "scz", ["Shi"] = "shr", ["Shihhi Arabic"] = "ssh", ["Shiki"] = "gua", ["Shilluk"] = "shk", ["Shina"] = "scl", ["Shinasha"] = "bwo", ["Shipibo-Conibo"] = "shp", ["Shixing"] = "sxg", ["Sholaga"] = "sle", ["Shom Peng"] = "sii", ["Shoo-Minda-Nye"] = "bcv", ["Shoshone"] = "shh", ["Shua"] = "shg", ["Shuar"] = "jiv", ["Shughni"] = "sgh", ["Shumashti"] = "sts", ["Shumcho"] = "scu", ["Shuswap"] = "shs", ["Shuwa-Zamani"] = "ksa", ["Shwai"] = "shw", ["Shwe Palaung"] = "pll", ["Sialum"] = "slw", ["Siamou"] = "sif", ["Sian"] = "spg", ["Siane"] = "snp", ["Siang"] = "sya", ["Siar-Lak"] = "sjr", ["Sibe"] = "nco", ["Siberian Tatar"] = "sty", ["Sibu Melanau"] = "sdx", ["Sicanian"] = "sxc", ["Sicel"] = "scx", ["Siculo-Arabic"] = "sqr", ["Sidetic"] = "xsd", ["Sie"] = "erg", ["Sierra Leone Sign Language"] = "sgx", ["Sierra Negra Nahuatl"] = "nsu", ["Sierra de Juárez Zapotec"] = "zaa", ["Sighu"] = "sxe", ["Sihan"] = "snr", ["Sika"] = "ski", ["Sikaritai"] = "tty", ["Sikiana"] = "sik", ["Sikule"] = "skh", ["Sila"] = "slt", ["Silacayoapan Mixtec"] = "mks", ["Sileibi"] = "sbq", ["Silimo"] = "wul", ["Siliput"] = "mkc", ["Silopi"] = "xsp", ["Silt'e"] = "stv", ["Simaa"] = "sie", ["Simalungun Batak"] = "bts", ["Simba"] = "sbw", ["Simbali"] = "smg", ["Simbari"] = "smb", ["Simbo"] = "sbb", ["Simeku"] = "smz", ["Simeulue"] = "smr", ["Simte"] = "smt", ["Sinacantán"] = "nai-sin", ["Sinagen"] = "siu", ["Sinasina"] = "sst", ["Sinaugoro"] = "snc", ["Sindhi Bhil"] = "sbn", ["Sindihui Mixtec"] = "xts", ["Singa"] = "sgm", ["Singapore Sign Language"] = "sls", ["Singpho"] = "sgp", ["Sinicahua Mixtec"] = "xti", ["Sininkere"] = "skq", ["Sinte Romani"] = "rmo", ["Sinyar"] = "sys", ["Sinúfana"] = "sai-sin", ["Sio"] = "xsi", ["Siona"] = "snn", ["Sipakapense"] = "qum", ["Sira"] = "swj", ["Siraya"] = "fos", ["Sirenik"] = "ysr", ["Siri"] = "sir", ["Siriano"] = "sri", ["Sirionó"] = "srq", ["Sirmauri"] = "srx", ["Siroi"] = "ssd", ["Sisaala"] = "sld", ["Sissano"] = "sso", ["Situ"] = "sit-sit", ["Siuslaw"] = "sis", ["Sivandi"] = "siy", ["Sivia Sign Language"] = "lsv", ["Siwai"] = "siw", ["Siwi"] = "siz", ["Siwu"] = "akp", ["Siyin Chin"] = "csy", ["Skagit"] = "ska", ["Skalvian"] = "svx", ["Ske"] = "ske", ["Skepi Creole Dutch"] = "skw", ["Skolt Sami"] = "sms", ["Skou"] = "skv", ["Slavomolisano"] = "svm", ["Slovakian Sign Language"] = "svk", ["Small Flowery Miao"] = "sfm", ["Smärky Kanum"] = "kxq", ["Snohomish"] = "sno", ["So'a"] = "ssq", ["Sobei"] = "sob", ["Sochiapam Chinantec"] = "cso", ["Soga"] = "xog", ["Sogdian"] = "sog", ["Sok"] = "skk", ["Sokna"] = "swn", ["Soko"] = "soc", ["Sokoro"] = "sok", ["Solano"] = "xso", ["Soli"] = "sby", ["Solombala English"] = "crp-slb", ["Solon"] = "tuw-sol", ["Solong"] = "aaw", ["Solos"] = "sol", ["Som"] = "smc", ["Somba-Siawari"] = "bmu", ["Somra"] = "ntx", ["Somrai"] = "sor", ["Somyev"] = "kgt", ["Sonaga"] = "ysg", ["Sonde"] = "shc", ["Songe"] = "sop", ["Songlai Chin"] = "csj", ["Songomeno"] = "soe", ["Songoora"] = "sod", ["Sonha"] = "soi", ["Sonia"] = "siq", ["Soninke"] = "snk", ["Sonsorolese"] = "sov", ["Soo"] = "teu", ["Sop"] = "urw", ["Soqotri"] = "sqt", ["Sora"] = "srb", ["Sori-Harengan"] = "sbh", ["Sorkhei"] = "sqo", ["Sorothaptic"] = "sxo", ["Sorsogon Ayta"] = "ays", ["Sos Kundi"] = "sdk", ["Sota Kanum"] = "krz", ["Sou"] = "sqq", ["South African Sign Language"] = "sfs", ["South Awyu"] = "aws", ["South Boma"] = "bnt-sbo", ["South Central Banda"] = "lnl", ["South Central Dinka"] = "dib", ["South Efate"] = "erk", ["South Fali"] = "fal", ["South Giziga"] = "giz", ["South Lembata"] = "lmf", ["South Marquesan"] = "mqm", ["South Muyu"] = "kts", ["South Nuaulu"] = "nxl", ["South Picene"] = "spx", ["South Slavey"] = "xsl", ["South Tairora"] = "omw", ["South Ucayali Ashéninka"] = "cpy", ["South Watut"] = "mcy", ["Southeast Ambrym"] = "tvk", ["Southeast Babar"] = "vbb", ["Southeast Ijo"] = "ijs", ["Southeast Pashayi"] = "psi", ["Southeast Tasmanian"] = "xpf", ["Southeastern Dinka"] = "dks", ["Southeastern Ixtlán Zapotec"] = "zpd", ["Southeastern Kolami"] = "nit", ["Southeastern Nochixtlán Mixtec"] = "mxy", ["Southeastern Pomo"] = "pom", ["Southeastern Puebla Nahuatl"] = "npl", ["Southeastern Tarahumara"] = "tcu", ["Southeastern Tepehuan"] = "stp", ["Southern Alta"] = "agy", ["Southern Bai"] = "bfs", ["Southern Birifor"] = "biv", ["Southern Bobo"] = "bwq", ["Southern Bontoc"] = "obk", ["Southern Carrier"] = "caf", ["Southern Catanduanes Bicolano"] = "bln", ["Southern Dagaare"] = "dga", ["Southern East Cree"] = "crj", ["Southern Ghale"] = "ghe", ["Southern Grebo"] = "grj", ["Southern Guiyang Hmong"] = "hmy", ["Southern Haida"] = "hax", ["Southern Hindko"] = "hnd", ["Southern Kalapuya"] = "sxk", ["Southern Kalinga"] = "ksc", ["Southern Khanty"] = "kca-sou", ["Southern Kissi"] = "kss", ["Southern Kiwai"] = "kjd", ["Southern Kurdish"] = "sdh", ["Southern Lolopo"] = "ysp", ["Southern Lorung"] = "lrr", ["Southern Luri"] = "luz", ["Southern Ma'di"] = "snm", ["Southern Mansi"] = "mns-sou", ["Southern Mashan Hmong"] = "hma", ["Southern Muji"] = "ymc", ["Southern Ndebele"] = "nr", ["Southern Ngbandi"] = "nbw", ["Southern Nicobarese"] = "nik", ["Southern Nisu"] = "nsd", ["Southern Nuni"] = "nnw", ["Southern Ohlone"] = "css", ["Southern One"] = "osu", ["Southern Pame"] = "pmz", ["Southern Pomo"] = "peq", ["Southern Puebla Mixtec"] = "mit", ["Southern Puget Sound Salish"] = "slh", ["Southern Pumi"] = "pmj", ["Southern Qiandong Miao"] = "hms", ["Southern Qiang"] = "qxs", ["Southern Rengma Naga"] = "nre", ["Southern Rincon Zapotec"] = "zsr", ["Southern Roglai"] = "rgs", ["Southern Sama"] = "ssb", ["Southern Samo"] = "sbd", ["Southern Selkup"] = "sel-sou", ["Southern Sierra Miwok"] = "skd", ["Southern Tidung"] = "itd", ["Southern Tiwa"] = "tix", ["Southern Toussian"] = "wib", ["Southern Tujia"] = "tjs", ["Southern Tutchone"] = "tce", ["Southern Valley Yokuts"] = "yok-svy", ["Southern Yukaghir"] = "yux", ["Southwest Gbaya"] = "gso", ["Southwest Pashayi"] = "psh", ["Southwest Tanna"] = "nwi", ["Southwestern Bontoc"] = "vbk", ["Southwestern Dinka"] = "dik", ["Southwestern Fars"] = "fay", ["Southwestern Guiyang Hmong"] = "hmg", ["Southwestern Huishui Hmong"] = "hmh", ["Southwestern Nisu"] = "nsv", ["Southwestern Tamang"] = "tsf", ["Southwestern Tarahumara"] = "twr", ["Southwestern Tepehuan"] = "tla", ["Southwestern Tlaxiaco Mixtec"] = "meh", ["Sowa"] = "sww", ["Sowanda"] = "sow", ["Soyaltepec Mazatec"] = "vmp", ["Soyaltepec Mixtec"] = "vmq", ["Spanish Sign Language"] = "ssp", ["Spiti Bhoti"] = "spt", ["Spokane"] = "spo", ["Squamish"] = "squ", ["ซรานันโตโง"] = "srn", ["Sri Lankan Creole Malay"] = "sci", ["Sri Lankan Sign Language"] = "sqs", ["Stau"] = "ero-tau", ["Stod Bhoti"] = "sbu", ["Stoney"] = "sto", ["Suabo"] = "szp", ["Suarmin"] = "seo", ["Suau"] = "swp", ["Suba"] = "sxb", ["Suba-Simbiti"] = "ssc", ["Subi"] = "xsj", ["Subiya"] = "sbs", ["Subtiaba"] = "sut", ["Sudest"] = "tgo", ["Suena"] = "sue", ["Suga"] = "sgi", ["Suganga"] = "sug", ["Sugut Dusun"] = "kzs", ["Suki"] = "sui", ["Suku"] = "sub", ["Sukuma"] = "suk", ["Sukur"] = "syk", ["Sukurum"] = "zsu", ["Sula"] = "szn", ["Sulka"] = "sua", ["Sulod"] = "srg", ["Suma"] = "sqm", ["Sumariup"] = "siv", ["Sumau"] = "six", ["Sumbawa"] = "smw", ["Sumbwa"] = "suw", ["Sumtu Chin"] = "csv", ["Sunam"] = "ssk", ["Sungwadaga"] = "mwo", ["Sungwadia"] = "mrb", ["Sunum"] = "ymn", ["Sunwar"] = "suz", ["Suoy"] = "syo", ["Supyire"] = "spp", ["Sur"] = "tdl", ["Surbakhal"] = "sbj", ["Suri"] = "suq", ["Surjapuri"] = "sjp", ["Sursurunga"] = "sgz", ["Suruahá"] = "swx", ["Surubu"] = "sde", ["Suruí"] = "sru", ["Suruí Do Pará"] = "mdz", ["Susquehannock"] = "sqn", ["Susu"] = "sus", ["Susuami"] = "ssu", ["Suundi"] = "sdj", ["Suwawa"] = "swu", ["Suyá"] = "suy", ["Svan"] = "sva", ["Swabian"] = "swg", ["Swampy Cree"] = "csw", ["สวาซี"] = "ss", ["Swedish Sign Language"] = "swl", ["Swiss-French Sign Language"] = "ssr", ["Swiss-German Sign Language"] = "sgg", ["Swiss-Italian Sign Language"] = "slf", ["Swo"] = "sox", ["Syenara Senoufo"] = "shz", ["Sácata"] = "sai-sac", ["São Paulo Kaingáng"] = "zkp", ["Sãotomense"] = "cri", ["Sìcìté Sénoufo"] = "sep", ["Sîshëë"] = "sih", ["Sô"] = "sss", ["T'en"] = "tct", ["Taabwa"] = "tap", ["Taba"] = "mky", ["Tabaa Zapotec"] = "zat", ["Tabancale"] = "sai-tab", ["Tabaru"] = "tby", ["Tabasco Nahuatl"] = "nhc", ["Tabasco Zoque"] = "zoq", ["Tabla"] = "tnm", ["Tabo"] = "knv", ["Tabriak"] = "tzx", ["Tacahua Mixtec"] = "xtt", ["Tacana"] = "tna", ["Tachawit"] = "shy", ["Tadaksahak"] = "dsq", ["Tadyawan"] = "tdy", ["Tae'"] = "rob", ["Tafi"] = "tcd", ["Tafreshi"] = "xme-taf", ["Tagabawa"] = "bgs", ["Tagal Murut"] = "mvv", ["Tagbu"] = "tbm", ["Tagdal"] = "tda", ["Tagish"] = "tgx", ["Tagoi"] = "tag", ["Tagwana Senoufo"] = "tgw", ["Tahltan"] = "tht", ["Tai"] = "taw", ["Tai Hang Tong"] = "thc", ["Tai Hongjin"] = "tiz", ["Tai Loi"] = "tlq", ["Tai Long"] = "thi", ["Tai Pao"] = "tpo", ["Tai Thanh"] = "tmm", ["Tai Ya"] = "cuu", ["Taiap"] = "gpn", ["Taikat"] = "aos", ["Taimyr Pidgin Russian"] = "crp-tpr", ["Tainae"] = "ago", ["Tairuma"] = "uar", ["Taita"] = "dav", ["Taivoan"] = "tvx", ["Taiwan Sign Language"] = "tss", ["Taje"] = "pee", ["Tajio"] = "tdj", ["Tajuasohn"] = "tja", ["Takelma"] = "tkm", ["Takia"] = "tbc", ["Takua"] = "tkz", ["Takuu"] = "nho", ["Takwane"] = "tke", ["Tal"] = "tal", ["Tala"] = "tak", ["Talaud"] = "tld", ["Taliabu"] = "tlv", ["Talieng"] = "tdf", ["Talinga-Bwisi"] = "tlj", ["Talise"] = "tlr", ["Tallán"] = "sai-tal", ["Talodi"] = "tlo", ["Taloki"] = "tlk", ["Talondo'"] = "tln", ["Talossan"] = "tzl", ["Talu"] = "yta", ["Talysh"] = "tly", ["Tama (Chad)"] = "tma", ["Tama (Colombia)"] = "ten", ["Tamagario"] = "tcg", ["Tamambo"] = "mla", ["Taman (Indonesia)"] = "tmn", ["Taman (Myanmar)"] = "tcl", ["Tamanaku"] = "tmz", ["Tamazola Mixtec"] = "vmx", ["Tambas"] = "tdk", ["Tambora"] = "xxt", ["Tambotalo"] = "tls", ["Tambunan Dusun"] = "kzt", ["Tami"] = "tmy", ["Tamki"] = "tax", ["Tamnim Citak"] = "tml", ["Tampias Lobu"] = "low", ["Tampuan"] = "tpu", ["Tampulma"] = "tpm", ["Tanacross"] = "tcb", ["Tanahmerah"] = "tcm", ["Tanapag"] = "tpv", ["Tanchangya"] = "tnv", ["Tandia"] = "tni", ["Tanema"] = "tnx", ["Tangale"] = "tan", ["Tanggu"] = "tgu", ["Tangkhul Naga"] = "nmf", ["Tangko"] = "tkx", ["Tangoa"] = "tgp", ["Tangsa"] = "nst", ["Tanguat"] = "tbs", ["Tangwang"] = "mis-tnw", ["Tanimbili"] = "tbe", ["Tanimuca-Retuarã"] = "tnc", ["Tanjijili"] = "uji", ["Tanudan Kalinga"] = "kml", ["Tanzanian Sign Language"] = "tza", ["Tapachultec"] = "nai-tap", ["Taparita"] = "sai-tpr", ["Tapayuna"] = "sai-tap", ["Tapeba"] = "tbb", ["Tapei"] = "afp", ["Tapieté"] = "tpj", ["Tapirapé"] = "taf", ["Tar Gula"] = "kcm", ["Tara Baka"] = "bdh", ["Tarairiú"] = "sai-trr", ["Tarantino"] = "roa-tar", ["Tarao"] = "tro", ["Taraon"] = "mhu", ["Tareng"] = "tgr", ["Tariana"] = "tae", ["Tarjumo"] = "txj", ["Tarok"] = "yer", ["Taroko"] = "trv", ["Tarpia"] = "tpf", ["Tartessian"] = "txr", ["Taruma"] = "tdm", ["Tasawaq"] = "twq", ["Tashelhit"] = "shi", ["Tasmate"] = "tmt", ["Tat"] = "ttt", ["Tataltepec Chatino"] = "cta", ["Tatana"] = "txx", ["Tataviam"] = "azc-tat", ["Tatuyo"] = "tav", ["Tauade"] = "ttd", ["Taulil"] = "tuh", ["Taungyo"] = "tco", ["Taupota"] = "tpa", ["Tause"] = "tad", ["Taushiro"] = "trr", ["Tauya"] = "tya", ["Taveta"] = "tvs", ["Tavoyan"] = "tvn", ["Tavringer Romani"] = "rmu", ["Tawala"] = "tbo", ["Tawandê"] = "xtw", ["Tawang Monpa"] = "twm", ["Tawasa"] = "nai-taw", ["Taworta"] = "tbp", ["Tawoyan"] = "twy", ["Tawr Chin"] = "tcp", ["Tay Khang"] = "tnu", ["Tayabas Ayta"] = "ayy", ["Taymanitic"] = "sem-tay", ["Tayo"] = "cks", ["Tchitchege"] = "tck", ["Tchumbuli"] = "bqa", ["Te'un"] = "tve", ["Teanu"] = "tkw", ["Tebul Sign Language"] = "tsy", ["Tebul Ure Dogon"] = "dtu", ["Tecpatlán Totonac"] = "tcw", ["Tedaga"] = "tuq", ["Tedim Chin"] = "ctd", ["Tee"] = "tkq", ["Tefaro"] = "tfo", ["Tegali"] = "ras", ["Tehit"] = "kps", ["Tehuelche"] = "teh", ["Teiwa"] = "twe", ["Tejalapan Zapotec"] = "ztt", ["Teke-Fuumu"] = "ifm", ["Teke-Kukuya"] = "kkw", ["Teke-Laali"] = "lli", ["Teke-Tege"] = "teg", ["Teke-Tsaayi"] = "tyi", ["Teke-Tyee"] = "tyx", ["Tektiteko"] = "ttc", ["Tela-Masbuar"] = "tvm", ["Telefol"] = "tlf", ["Teluti"] = "tlt", ["Tem"] = "kdh", ["Temascaltepec Nahuatl"] = "nhv", ["Tembé"] = "tqb", ["Teme"] = "tdo", ["Temein"] = "teq", ["Temi"] = "soz", ["Temne"] = "tem", ["Temoaya Otomi"] = "ott", ["Temoq"] = "tmo", ["Tempasuk Dusun"] = "tdu", ["Ten'edn"] = "tnz", ["Tenango Otomi"] = "otn", ["Tene Kan Dogon"] = "dtk", ["Tenggarong Kutai Malay"] = "vkt", ["Tengger"] = "tes", ["Tenharim"] = "pah", ["Tenino"] = "tqn", ["Tenis"] = "tns", ["Tennet"] = "tex", ["Teojomulco Chatino"] = "omq-teo", ["Teop"] = "tio", ["Teor"] = "tev", ["Tepecano"] = "tep", ["Tepetotutla Chinantec"] = "cnt", ["Tepeuxila Cuicatec"] = "cux", ["Tepinapa Chinantec"] = "cte", ["Tepo Krumen"] = "ted", ["Teposcolula Mixtec"] = "omq-tel", ["Tequistlatec"] = "nai-teq", ["Ter Sami"] = "sjt", ["Tera"] = "ttr", ["Terebu"] = "trb", ["Terei"] = "buo", ["Tereno"] = "ter", ["Teressa"] = "tef", ["Tereweng"] = "twg", ["Teribe"] = "tfr", ["Terik"] = "tec", ["Termanu"] = "twu", ["Ternateño"] = "tmg", ["Tese"] = "keg", ["Teshenawa"] = "twc", ["Tetela"] = "tll", ["Tetelcingo Nahuatl"] = "nhg", ["Tetete"] = "teb", ["Tetserret"] = "tez", ["Tetun Dili"] = "tdt", ["Teushen"] = "sai-teu", ["Teutila Cuicatec"] = "cut", ["Tewa"] = "tew", ["Texcatepec Otomi"] = "otx", ["Texistepec Popoluca"] = "poq", ["Texmelucan Zapotec"] = "zpz", ["Tezoatlán Mixtec"] = "mxb", ["Tha"] = "thy", ["Thachanadan"] = "thn", ["Thado Chin"] = "tcz", ["Thaiphum Chin"] = "cth", ["Thakali"] = "ths", ["Thamudic"] = "sem-tha", ["Thangal Naga"] = "nki", ["Thangmi"] = "thf", ["Thao"] = "ssf", ["Tharaka"] = "thk", ["Tharrgari"] = "dhr", ["Thawa"] = "xtv", ["Tho"] = "tou", ["Thompson"] = "thp", ["Thopho"] = "ytp", ["Thracian"] = "txh", ["Thu Lao"] = "tyl", ["Thulung"] = "tdh", ["Thurawal"] = "tbh", ["Thuri"] = "thu", ["Tiagba"] = "ahi", ["Tiale"] = "mnl", ["Tiang"] = "tbj", ["Tibea"] = "ngy", ["Tibetan Sign Language"] = "lsn", ["Ticuna"] = "tca", ["Tidaá Mixtec"] = "mtx", ["Tiemacèwè Bozo"] = "boo", ["Tiene"] = "tii", ["Tifal"] = "tif", ["Tigak"] = "tgc", ["Tigon Mbembe"] = "nza", ["Tii"] = "txq", ["Tijaltepec Mixtec"] = "xtl", ["Tikar"] = "tik", ["Tikopia"] = "tkp", ["Tilapa Otomi"] = "otl", ["Tillamook"] = "til", ["Tilquiapan Zapotec"] = "zts", ["Tilung"] = "tij", ["Tima"] = "tms", ["Timbe"] = "tim", ["Timor Pidgin"] = "tvy", ["Timote"] = "sai-tim", ["Timucua"] = "tjm", ["Timugon Murut"] = "tih", ["Tinani"] = "lbf", ["Tindi"] = "tin", ["Tingui-Boto"] = "tgv", ["Tinigua"] = "tit", ["Tinoc Kallahan"] = "tne", ["Tinputz"] = "tpz", ["Tinrin"] = "cir", ["Tipai"] = "nai-tip", ["Tippera"] = "tpe", ["Tira"] = "tic", ["Tirahi"] = "tra", ["Tiranige Diga Dogon"] = "tde", ["Tirax"] = "mme", ["Tita"] = "tdq", ["Titan"] = "ttv", ["Tiv"] = "tiv", ["Tiwa"] = "lax", ["Tiwi"] = "tiw", ["Tiéfo"] = "tiq", ["Tiéyaxo Bozo"] = "boz", ["Tjurruru"] = "tju", ["Tlachichilco Tepehua"] = "tpt", ["Tlacoapa Me'phaa"] = "tpl", ["Tlacoatzintepec Chinantec"] = "ctl", ["Tlacolulita Zapotec"] = "zpk", ["Tlahuica"] = "ocu", ["Tlahuitoltepec Mixe"] = "mxp", ["Tlamacazapa Nahuatl"] = "nuz", ["Tlazoyaltepec Mixtec"] = "mqh", ["Tlingit"] = "tli", ["To"] = "toz", ["To'abaita"] = "mlu", ["Toaripi"] = "tqo", ["Toba"] = "tob", ["Toba Batak"] = "bbc", ["Toba-Maskoy"] = "tmf", ["Tobagonian Creole English"] = "tgh", ["Tobanga"] = "tng", ["Tobati"] = "tti", ["Tobelo"] = "tlb", ["Tobian"] = "tox", ["Tobilung"] = "tgb", ["Tobo"] = "tbv", ["Tocantins Asurini"] = "asu", ["Tocho"] = "taz", ["Todrah"] = "tdr", ["Tofanma"] = "tlg", ["Tofin Gbe"] = "tfi", ["Togbo-Vara Banda"] = "tor", ["Togoyo"] = "tgy", ["Tojolabal"] = "toj", ["Toka-Leya"] = "dov", ["Tokano"] = "zuh", ["Tokelauan"] = "tkl", ["Toki Pona"] = "tok", ["Tol"] = "jic", ["Tolai"] = "ksd", ["Tolaki"] = "lbw", ["Tolomako"] = "tlm", ["Tolowa"] = "tol", ["Toloza"] = "ytl", ["Toma"] = "tod", ["Tomadino"] = "tdi", ["Tombelala"] = "ttp", ["Tombonuo"] = "txa", ["Tombulu"] = "tom", ["Tomini"] = "txm", ["Tommeginne"] = "xpv", ["Tommo So"] = "dto", ["Tomo Kan Dogon"] = "dtm", ["Tomoip"] = "tqp", ["Tondano"] = "tdn", ["Tonga (Malawi)"] = "tog", ["Tonga (Mozambique)"] = "toh", ["Tonga (Zambia)"] = "toi", ["Tongwe"] = "tny", ["Tonjon"] = "tjn", ["Tonkawa"] = "tqw", ["Tonsawang"] = "tnw", ["Tonsea"] = "txs", ["Tontemboan"] = "tnt", ["Toogee"] = "xpx", ["Tooro"] = "ttj", ["Topoiyo"] = "toy", ["Toposa"] = "toq", ["Toraja-Sa'dan"] = "sda", ["Toram"] = "trj", ["Torau"] = "ttu", ["Toro"] = "tdv", ["Toro So Dogon"] = "dts", ["Toro Tegu Dogon"] = "dtt", ["Toromono"] = "tno", ["Torona"] = "tqr", ["ครีโอลช่องแคบทอร์เรส"] = "tcs", ["Torricelli"] = "tei", ["Torricelli Yau"] = "yyu", ["Torwali"] = "trw", ["Torá"] = "trz", ["Tosu"] = "sit-tos", ["Totela"] = "ttl", ["Toto"] = "txo", ["Totoli"] = "txe", ["Totomachapan Zapotec"] = "zph", ["Totontepec Mixe"] = "mto", ["Totoro"] = "ttk", ["Touo"] = "tqu", ["Toura"] = "neb", ["Towei"] = "ttn", ["Transylvanian Saxon"] = "gmw-tsx", ["Traveller Danish"] = "rmd", ["Traveller Norwegian"] = "rmg", ["Traveller Scottish"] = "trl", ["Tregami"] = "trm", ["Tremembé"] = "tme", ["Trieng"] = "stg", ["Trimuris"] = "tip", ["Tring"] = "tgq", ["Tringgus"] = "trx", ["Trinidad and Tobago Sign Language"] = "lst", ["Trinidadian Creole English"] = "trf", ["Trinitario"] = "trn", ["Trió"] = "tri", ["Truká"] = "tka", ["Trumai"] = "tpy", ["Ts'ün-Lao"] = "tsl", ["Tsaangi"] = "tsa", ["Tsafiki"] = "cof", ["Tsakhur"] = "tkr", ["Tsakonian"] = "tsd", ["Tsakwambo"] = "kvz", ["Tsamai"] = "tsb", ["Tsat"] = "huq", ["Tsetsaut"] = "txc", ["Tsez"] = "ddo", ["Tshangla"] = "tsj", ["Tshobdun"] = "sit-tsh", ["Tshwa"] = "hio", ["Tsikimba"] = "kdl", ["Tsimané"] = "cas", ["Tsimshian"] = "tsi", ["Tsishingini"] = "tsw", ["Tso"] = "ldp", ["Tsogo"] = "tsv", ["Tsonga"] = "ts", ["Tsotsitaal"] = "fly", ["Tsucuba"] = "cbq", ["Tsum"] = "ttz", ["Tsuut'ina"] = "srs", ["Tsuvadi"] = "tvd", ["Tsuvan"] = "tsh", ["Tswa"] = "tsc", ["Tswapong"] = "two", ["Tuamotuan"] = "pmt", ["Tuareg"] = "tmh", ["Tubar"] = "tbu", ["Tucano"] = "tuo", ["Tugen"] = "tuy", ["Tugun"] = "tzn", ["Tugutil"] = "tuj", ["Tukang Besi North"] = "khc", ["Tukang Besi South"] = "bhq", ["Tuki"] = "bag", ["Tukpa"] = "tpq", ["Tukudede"] = "tkd", ["Tukumanféd"] = "tkf", ["Tula"] = "tul", ["Tule-Kaweah Yokuts"] = "yok-tky", ["Tulehu"] = "tlu", ["Tulishi"] = "tey", ["Tulu-Bohuai"] = "rak", ["Tulua"] = "aus-tul", ["Tuma-Irumu"] = "iou", ["Tumak"] = "tmc", ["Tumbuka"] = "tum", ["Tumi"] = "kku", ["Tumleo"] = "tmq", ["Tumtum"] = "tbr", ["Tumulung Sisaala"] = "sil", ["Tundra Enets"] = "enh", ["Tundra Nenets"] = "yrk-tun", ["Tunen"] = "tvu", ["Tungag"] = "lcm", ["Tunggare"] = "trt", ["Tunia"] = "tug", ["Tunica"] = "tun", ["Tunisian Sign Language"] = "tse", ["Tunjung"] = "tjg", ["Tunni"] = "tqq", ["Tunumiisut"] = "esx-tut", ["Tunzu"] = "dza", ["Tuoba"] = "mis-tuo", ["Tuotomb"] = "ttf", ["Tuparí"] = "tpr", ["Tupinambá"] = "tpn", ["Tupinikin"] = "tpk", ["Tupuri"] = "tui", ["Turaka"] = "trh", ["Turdetanian"] = "mis-tdt", ["Turdulian"] = "mis-tdl", ["Turi"] = "trd", ["Turiwára"] = "twt", ["Turka"] = "tuz", ["Turkana"] = "tuv", ["Turkish Sign Language"] = "tsm", ["Turks and Caicos Creole English"] = "tch", ["Turoyo"] = "tru", ["Turumsa"] = "tqm", ["Tuscarora"] = "tus", ["Tutelo"] = "tta", ["Tutong"] = "ttg", ["Tutsa Naga"] = "tvt", ["Tutuba"] = "tmi", ["Tututepec Mixtec"] = "mtu", ["Tututni"] = "tuu", ["Tuwali Ifugao"] = "ifk", ["Tuwari"] = "tww", ["Tuwuli"] = "bov", ["Tuxináwa"] = "tux", ["Tuxá"] = "tud", ["Tuyuca"] = "tue", ["Tuyuhun"] = "mis-tuh", ["Twana"] = "twa", ["Twendi"] = "twn", ["Tyap"] = "kcg", ["Tyaraity"] = "woa", ["Tyerrernotepanner"] = "xph", ["Tz'utujil"] = "tzj", ["Tzeltal"] = "tzh", ["Tày Tac"] = "tyt", ["Tây Bồi"] = "tas", ["Téén"] = "lor", ["Tübatulabal"] = "tub", ["U"] = "uuu", ["Uab Meto"] = "aoz", ["Uamué"] = "uam", ["Uare"] = "ksj", ["Ubaghara"] = "byc", ["Ubang"] = "uba", ["Ubi"] = "ubi", ["Ubir"] = "ubr", ["Ubykh"] = "uby", ["Ucayali-Yurúa Ashéninka"] = "cpb", ["Uda"] = "uda", ["Udihe"] = "ude", ["Uduk"] = "udu", ["Ufim"] = "ufi", ["Ugandan Sign Language"] = "ugn", ["Ughele"] = "uge", ["Uhami"] = "uha", ["Uisai"] = "uis", ["Ujir"] = "udj", ["Ukaan"] = "kcf", ["Ukhwejo"] = "ukh", ["Ukit"] = "umi", ["Ukpe-Bayobiri"] = "ukp", ["Ukpet-Ehom"] = "akd", ["Ukrainian Sign Language"] = "ukl", ["Ukue"] = "uku", ["Ukuriguma"] = "ukg", ["Ukwa"] = "ukq", ["Ukwuani-Aboh-Ndoni"] = "ukw", ["Ulau-Suain"] = "svb", ["Ulch"] = "ulc", ["Uldeme"] = "udl", ["Ulithian"] = "uli", ["Ullatan"] = "ull", ["Ulumanda'"] = "ulm", ["Ulwa"] = "ulw", ["Uma"] = "ppk", ["Uma' Lasan"] = "xky", ["Uma' Lung"] = "ulu", ["Umanakaina"] = "gdn", ["Umatilla"] = "uma", ["Umbindhamu"] = "umd", ["Umbu-Ungu"] = "ubu", ["Umbugarla"] = "umr", ["Umbundu"] = "umb", ["Umbuygamu"] = "umg", ["Ume Sami"] = "sju", ["Umeda"] = "upi", ["Umiida"] = "xud", ["Umiray Dumaget Agta"] = "due", ["Umon"] = "umm", ["Umotína"] = "umo", ["Umpila"] = "ump", ["Una"] = "mtg", ["Unami"] = "unm", ["Unde Kaili"] = "unz", ["Undetermined"] = "und", ["Uneapa"] = "bbn", ["Uneme"] = "une", ["Unggaranggu"] = "xun", ["Unggumi"] = "xgu", ["Unua"] = "onu", ["Unubahe"] = "unu", ["Uokha"] = "uok", ["Upper Chehalis"] = "cjh", ["Upper Grand Valley Dani"] = "dna", ["Upper Kuskokwim"] = "kuu", ["Upper Necaxa Totonac"] = "tku", ["Upper Ta'oih"] = "tth", ["Upper Tanana"] = "tau", ["Upper Taromi"] = "tov", ["Upper Umpqua"] = "xup", ["Ura (New Guinea)"] = "uro", ["Ura (Vanuatu)"] = "uur", ["Uradhi"] = "urf", ["Urali"] = "url", ["Urapmin"] = "urm", ["Urarina"] = "ura", ["Urartian"] = "xur", ["Urat"] = "urt", ["Urhobo"] = "urh", ["Uri"] = "uvh", ["Urigina"] = "urg", ["Urim"] = "uri", ["Urimo"] = "urx", ["Urningangg"] = "urc", ["Uru"] = "ure", ["Uru-Eu-Wau-Wau"] = "urz", ["Uru-Pa-In"] = "urp", ["Uruangnirin"] = "urn", ["Uruava"] = "urv", ["Urubú-Kaapor"] = "urb", ["Uruguayan Sign Language"] = "ugy", ["Urum"] = "uum", ["Urumi"] = "uru", ["Usaghade"] = "usk", ["Usan"] = "wnu", ["Usarufa"] = "usa", ["Ushojo"] = "ush", ["Usila Chinantec"] = "cuc", ["Uspanteco"] = "usp", ["Usui"] = "usi", ["Utarmbung"] = "omo", ["Ute"] = "ute", ["Utu"] = "utu", ["Uvbie"] = "evh", ["Uwinymil"] = "aus-uwi", ["Uya"] = "usu", ["Uyajitaya"] = "duk", ["Uzekwe"] = "eze", ["Vaagri Booli"] = "vaa", ["Vaghri"] = "vgr", ["Vaghua"] = "tva", ["Vagla"] = "vag", ["Vaiphei"] = "vap", ["Vale"] = "vae", ["Valencian Sign Language"] = "vsv", ["Valle Nacional Chinantec"] = "cvn", ["Valley Maidu"] = "vmv", ["Valman"] = "van", ["Valpei"] = "vlp", ["Vamale"] = "mkt", ["Vame"] = "mlr", ["Vangunu"] = "mpr", ["Vanimo"] = "vam", ["Vanji"] = "ira-wnj", ["Vanuma"] = "vau", ["Vao"] = "vao", ["Varhadi"] = "vah", ["Varisi"] = "vrs", ["Varli"] = "vav", ["Vasavi"] = "vas", ["Vayu"] = "vay", ["Veddah"] = "ved", ["Vehes"] = "val", ["Vemgo-Mabas"] = "vem", ["Venetic"] = "xve", ["Venezuelan Sign Language"] = "vsl", ["Ventureño"] = "veo", ["Vera'a"] = "vra", ["Vestinian"] = "xvs", ["Vidunda"] = "vid", ["Viemo"] = "vig", ["Vilela"] = "vil", ["Vili"] = "vif", ["Villa Viciosa Agta"] = "dyg", ["Vincentian Creole English"] = "svc", ["Virgin Islands Creole"] = "vic", ["Vishavan"] = "vis", ["Viti"] = "vit", ["Vitou"] = "vto", ["Viya"] = "gev", ["Vlax Romani"] = "rmy", ["Volscian"] = "xvo", ["Vono"] = "kch", ["Voro"] = "vor", ["Vumbu"] = "vum", ["Vunapu"] = "vnp", ["Vunjo"] = "vun", ["Vurës"] = "msn", ["Vute"] = "vut", ["Wa"] = "wbm", ["Wa'ema"] = "wag", ["Waama"] = "wwa", ["Waamwang"] = "wmn", ["Wab"] = "wab", ["Wabo"] = "wbb", ["Waboda"] = "kmx", ["Waci Gbe"] = "wci", ["Wadaginam"] = "wdg", ["Waddar"] = "wbq", ["Wadi Wadi"] = "xwd", ["Wadiyara Koli"] = "kxp", ["Wadjabangayi"] = "wdy", ["Wadjiginy"] = "wdj", ["Wadjigu"] = "wdu", ["Wae Rana"] = "wrx", ["Waffa"] = "waj", ["Wagawaga"] = "wgb", ["Wagaya"] = "wga", ["Wagdi"] = "wbr", ["Wageman"] = "waq", ["Wagi"] = "fad", ["Wahau Kayan"] = "whu", ["Wahau Kenyah"] = "whk", ["Wahgi"] = "wgi", ["Waigali"] = "wbk", ["Waigeo"] = "wgo", ["Waikuri"] = "nai-wai", ["Wailaki"] = "wlk", ["Wailapa"] = "wlr", ["Waima'a"] = "wmh", ["Waimaha"] = "bao", ["Waimiri-Atroari"] = "atr", ["Wainumá"] = "awd-wai", ["Waioli"] = "wli", ["Waitaká"] = "sai-wai", ["Waiwai"] = "waw", ["Waja"] = "wja", ["Wajarri"] = "wbv", ["Wajuk"] = "xwj", ["Waka"] = "wav", ["Wakawaka"] = "wkw", ["Wakhi"] = "wbl", ["Wakoná"] = "waf", ["Wala"] = "lgl", ["Walak"] = "wlw", ["Walangama"] = "nlw", ["Wali (Ghana)"] = "wlx", ["Wali (Sudan)"] = "wll", ["Waling"] = "wly", ["Walio"] = "wla", ["Walla Walla"] = "waa", ["Wallisian"] = "wls", ["Walmajarri"] = "wmt", ["Wam"] = "wmo", ["Wamas"] = "wmc", ["Wambaya"] = "wmb", ["Wambon"] = "wms", ["Wambule"] = "wme", ["Wamey"] = "cou", ["Wamin"] = "wmi", ["Wampar"] = "lbq", ["Wampur"] = "waz", ["Wan"] = "wan", ["Wanambre"] = "wnb", ["Wanap"] = "wnp", ["Wancho"] = "nnp", ["Wanda"] = "wbh", ["Wandala"] = "mfi", ["Wandamen"] = "wad", ["Wandarang"] = "wnd", ["Wandji"] = "wdd", ["Waneci"] = "wne", ["Wanga"] = "lwg", ["Wanggamala"] = "wnm", ["Wangganguru"] = "wgg", ["Wanggom"] = "wng", ["Wangkayutyuru"] = "wky", ["Wangkumara"] = "xwk", ["Wanham"] = "sai-wnm", ["Wanji"] = "wbi", ["Wanman"] = "wbt", ["Wannu"] = "jub", ["Wano"] = "wno", ["Wantoat"] = "wnc", ["Wanukaka"] = "wnk", ["Wanyi"] = "wny", ["Wané"] = "hwa", ["Wapan"] = "juk", ["Wapishana"] = "wap", ["Wappo"] = "wao", ["War-Jaintia"] = "aml", ["Wara"] = "wbf", ["Warapu"] = "wra", ["Waray Sorsogon"] = "srv", ["Wardaman"] = "wrr", ["Wardandi"] = "wxw", ["Warekena"] = "gae", ["Warembori"] = "wsa", ["Wari'"] = "pav", ["Waris"] = "wrs", ["Waritai"] = "wbe", ["Wariyangga"] = "wri", ["Warji"] = "wji", ["Warkay-Bipim"] = "bgv", ["Warlmanpa"] = "wrl", ["Warlpiri"] = "wbp", ["Warluwara"] = "wrb", ["Warnang"] = "wrn", ["Waropen"] = "wrp", ["Warray"] = "wrz", ["Warrgamay"] = "wgy", ["Warrwa"] = "wwr", ["Waru"] = "wru", ["Warumungu"] = "wrm", ["Waruna"] = "wrv", ["Warungu"] = "wrg", ["Warwar Feni"] = "hrw", ["Wasco-Wishram"] = "wac", ["Wasembo"] = "gsp", ["Washo"] = "was", ["Waskia"] = "wsk", ["Wastek"] = "hus", ["Wasu"] = "wsu", ["Watakataui"] = "wtk", ["Watam"] = "wax", ["Wathaurong"] = "wth", ["วาตีวา"] = "wtf", ["Watubela"] = "wah", ["Waube"] = "kop", ["Wauja"] = "wau", ["Wauyai"] = "wuy", ["Wawa"] = "www", ["Wawonii"] = "wow", ["Waxiang"] = "wxa", ["Wayampi"] = "oym", ["Wayana"] = "way", ["Wayanad Chetti"] = "ctt", ["Wayoró"] = "wyr", ["Wayumara"] = "sai-way", ["Wayuu"] = "guc", ["Wedau"] = "wed", ["Weh"] = "weh", ["Welaung"] = "weu", ["Weliki"] = "klh", ["Welsh Romani"] = "rmw", ["Wemale"] = "weo", ["Wemba-Wemba"] = "xww", ["Weme Gbe"] = "wem", ["Wendat"] = "wdt", ["Weri"] = "wer", ["Wersing"] = "kvw", ["West Albay Bikol"] = "fbl", ["West Ambae"] = "nnd", ["West Central Banda"] = "bbp", ["West Damar"] = "drn", ["West Flemish"] = "vls", ["West Greenlandic Pidgin"] = "crp-gep", ["West Lembata"] = "lmj", ["West Makian"] = "mqs", ["West Masela"] = "mss", ["West Tarangan"] = "txn", ["West Uvean"] = "uve", ["West-Central Limba"] = "lia", ["Western Arrernte"] = "are", ["Western Bolivian Guarani"] = "gnw", ["Western Bukidnon Manobo"] = "mbb", ["Western Dani"] = "dnw", ["Western Durango Nahuatl"] = "azn", ["Western Fijian"] = "wyy", ["Western Gurung"] = "gvr", ["Western Highland Chatino"] = "ctp", ["Western Huasteca Nahuatl"] = "nhw", ["Western Jicaque"] = "nai-wji", ["Western Juxtlahuaca Mixtec"] = "jmx", ["Western Karaboro"] = "kza", ["Western Keres"] = "kjq", ["Western Krahn"] = "krw", ["Western Lalu"] = "ywl", ["Western Magar"] = "mrd", ["Western Maninkakan"] = "mlq", ["Western Mari"] = "mrj", ["Western Mashan Hmong"] = "hmw", ["Western Meohang"] = "raf", ["Western Muria"] = "mut", ["Western Neo-Aramaic"] = "amw", ["Western Ojibwa"] = "ojw", ["Western Parbate Kham"] = "kjl", ["Western Penan"] = "pne", ["Western Sisaala"] = "ssl", ["Western Subanon"] = "suc", ["Western Tamang"] = "tdg", ["Western Tawbuid"] = "twb", ["Western Totonac"] = "tqt", ["Western Tunebo"] = "tnb", ["Western Xiangxi Miao"] = "mmr", ["Western Xwla Gbe"] = "xwl", ["Western Yugur"] = "ybe", ["Wewaw"] = "wea", ["Weyewa"] = "wew", ["Weyto"] = "woy", ["White Gelao"] = "giw", ["White Lachi"] = "lwh", ["Whitesands"] = "tnp", ["Wiarumus"] = "tua", ["Wichita"] = "wic", ["Wichí Lhamtés Güisnay"] = "mzh", ["Wichí Lhamtés Nocten"] = "mtp", ["Wichí Lhamtés Vejoz"] = "wlv", ["Wik-Epa"] = "wie", ["Wik-Iiyanh"] = "wij", ["Wik-Keyangan"] = "wif", ["Wik-Me'anha"] = "wih", ["Wik-Mungkan"] = "wim", ["Wik-Ngathana"] = "wig", ["Wikalkan"] = "wik", ["Wikngenchera"] = "wua", ["Wilawila"] = "wil", ["Wintu"] = "wnw", ["Winyé"] = "kst", ["Wipi"] = "gdr", ["Wiradjuri"] = "wrh", ["Wiraféd"] = "wir", ["Wirangu"] = "wgu", ["Wiru"] = "wiu", ["Wirö"] = "wpc", ["Wiwa"] = "mbp", ["Wiyot"] = "wiy", ["Wobé"] = "wob", ["Woccon"] = "xwc", ["Wogamusin"] = "wog", ["Wogeo"] = "woc", ["Woi"] = "wbw", ["Woiwurrung"] = "wyi", ["Wojenaka"] = "jod", ["Wolane"] = "wle", ["Wolani"] = "wod", ["Wolaytta"] = "wal", ["Woleaian"] = "woe", ["Wolio"] = "wlo", ["Womo"] = "wmx", ["Wong-gie"] = "aus-won", ["Wongo"] = "won", ["Woods Cree"] = "cwd", ["Woria"] = "wor", ["Worimi"] = "kda", ["Worodougou"] = "jud", ["Worora"] = "wro", ["Wotapuri-Katarqalai"] = "wsv", ["Wotu"] = "wtw", ["Woun Meu"] = "noa", ["Written Oirat"] = "xwo", ["Wudu"] = "wud", ["Wuhuan"] = "mis-wuh", ["Wulguru"] = "aus-wul", ["Wuliwuli"] = "wlu", ["Wulna"] = "wux", ["Wumboko"] = "bqm", ["Wumbvu"] = "wum", ["Wumeng"] = "ywu", ["Wunai Bunu"] = "bwn", ["Wunambal"] = "wub", ["Wurrugu"] = "wur", ["Wusa"] = "yig", ["Wushi"] = "bse", ["Wusi"] = "wsi", ["Wutung"] = "wut", ["Wutunhua"] = "wuh", ["Wuvulu-Aua"] = "wuv", ["Wyandot"] = "wya", ["Wára"] = "tci", ["Wãpha"] = "juw", ["Wè Southern"] = "gxx", ["Wè Western"] = "wec", ["Xadani Zapotec"] = "zax", ["Xakriabá"] = "xkr", ["Xamtanga"] = "xan", ["Xanaguía Zapotec"] = "ztg", ["Xavante"] = "xav", ["Xerénte"] = "xer", ["Xetá"] = "xet", ["Xianbei"] = "mis-xbi", ["Xibe"] = "sjo", ["Xicotepec de Juárez Totonac"] = "too", ["Xinca"] = "xin", ["Xingú Asuriní"] = "asn", ["Xiongnu"] = "mis-xnu", ["Xipaya"] = "xiy", ["Xiri"] = "xii", ["Xiriâna"] = "xir", ["Xishanba Lalo"] = "ywt", ["Xocó"] = "sai-xoc", ["Xokleng"] = "xok", ["Xukurú"] = "xoo", ["Xwela Gbe"] = "xwe", ["Xârâgurè"] = "axx", ["Yaa"] = "iyx", ["Yaaku"] = "muu", ["Yabarana"] = "yar", ["Yabaâna"] = "ybn", ["Yaben"] = "ybm", ["Yabong"] = "ybo", ["Yabula Yabula"] = "yxy", ["Yace"] = "ekr", ["Yafi"] = "wfg", ["Yagara"] = "yxg", ["Yagaria"] = "ygr", ["Yaghnobi"] = "yai", ["Yagomi"] = "ygm", ["Yagua"] = "yad", ["Yagwoia"] = "ygw", ["Yahadian"] = "ner", ["Yahang"] = "rhp", ["Yahuna"] = "ynu", ["Yaka"] = "yaf", ["Yakaikeke"] = "ykk", ["Yakima"] = "yak", ["Yakkha"] = "ybh", ["Yakoma"] = "yky", ["Yala"] = "yba", ["Yalahatan"] = "jal", ["Yalakalore"] = "xyl", ["Yalarnnga"] = "ylr", ["Yale"] = "nce", ["Yaleba"] = "ylb", ["Yalunka"] = "yal", ["Yalálag Zapotec"] = "zpu", ["Yamap"] = "ymp", ["Yamba"] = "yam", ["Yamben"] = "ynb", ["Yambes"] = "ymb", ["Yambeta"] = "yat", ["Yamdena"] = "jmd", ["Yameo"] = "yme", ["Yaminahua"] = "yaa", ["Yamongeri"] = "ymg", ["Yamphu"] = "ybi", ["Yan-nhangu"] = "jay", ["Yana"] = "ynn", ["Yanda"] = "yda", ["Yanda Dogon"] = "dym", ["Yandjibara"] = "xyb", ["Yandruwandha"] = "ynd", ["Yanesha'"] = "ame", ["Yangben"] = "yav", ["Yangkaal"] = "aus-ynk", ["Yangkam"] = "bsx", ["Yangman"] = "jng", ["Yango"] = "yng", ["Yangulam"] = "ynl", ["Yangum Dey"] = "yde", ["Yangum Gel"] = "ygl", ["Yangum Mon"] = "ymo", ["Yankunytjatjara"] = "kdd", ["Yanomam"] = "wca", ["Yanomamö"] = "guu", ["Yansi"] = "yns", ["Yanyuwa"] = "jao", ["Yao"] = "yao", ["Yao (South America)"] = "sai-yao", ["Yaosakor Asmat"] = "asy", ["Yaouré"] = "yre", ["Yapese"] = "yap", ["Yapunda"] = "yev", ["Yaqay"] = "jaq", ["Yaqui"] = "yaq", ["Yarawata"] = "yrw", ["Yareba"] = "yrb", ["Yareni Zapotec"] = "zae", ["Yarli"] = "yxl", ["Yarluyandi"] = "yry", ["Yarumá"] = "sai-yar", ["Yarí"] = "yri", ["Yasa"] = "yko", ["Yatay"] = "yty", ["Yatee Zapotec"] = "zty", ["Yatzachi Zapotec"] = "zav", ["Yaul"] = "yla", ["Yaur"] = "jau", ["Yautepec Zapotec"] = "zpb", ["Yavitero"] = "yvt", ["Yawa"] = "yva", ["Yawalapití"] = "yaw", ["Yawanawa"] = "ywn", ["Yawarawarga"] = "yww", ["Yaweyuha"] = "yby", ["Yawijibaya"] = "jbw", ["Yawiyo"] = "ybx", ["Yawuru"] = "ywr", ["Yaygir"] = "xya", ["Yazghulami"] = "yah", ["Ye'kwana"] = "mch", ["Yei"] = "jei", ["Yekhee"] = "ets", ["Yekora"] = "ykr", ["Yele"] = "yle", ["Yelmek"] = "jel", ["Yelogu"] = "ylg", ["Yemaek"] = "hmk", ["Yemba"] = "ybb", ["Yemsa"] = "jnj", ["Yendang"] = "yen", ["Yeni"] = "yei", ["Yenish"] = "yec", ["Yerakai"] = "yra", ["Yeretuar"] = "gop", ["Yerong"] = "yrn", ["Yerukula"] = "yeu", ["Yeskwa"] = "yes", ["Yessan-Mayo"] = "yss", ["Yetfa"] = "yet", ["Yevanic"] = "yej", ["Yeyi"] = "yey", ["Yidgha"] = "ydg", ["Yidiny"] = "yii", ["Yil"] = "yll", ["Yilan Creole"] = "ycr", ["Yimas"] = "yee", ["Yimchungru Naga"] = "yim", ["Yinbaw Karen"] = "kvu", ["Yinchia"] = "yin", ["Yindjibarndi"] = "yij", ["Yindjilandji"] = "yil", ["Yine"] = "pib", ["Yinggarda"] = "yia", ["Yinhawangka"] = "ywg", ["Yiningayi"] = "ygi", ["Yintale Karen"] = "kvy", ["Yinwum"] = "yxm", ["Yir-Yoront"] = "yiy", ["Yirandali"] = "ljw", ["Yis"] = "yis", ["Yitha Yitha"] = "xth", ["Yoba"] = "yob", ["Yocoboué Dida"] = "gud", ["Yoidik"] = "ydk", ["Yoke"] = "yki", ["Yolmo"] = "scp", ["Yolngu Sign Language"] = "ygs", ["Yoloxochitl Mixtec"] = "xty", ["Yom"] = "pil", ["Yombe"] = "yom", ["Yongkom"] = "yon", ["Yopno"] = "yut", ["Yora"] = "mts", ["Yorta Yorta"] = "xyy", ["Yosondúa Mixtec"] = "mpm", ["Youle Jino"] = "jiu", ["Younuo Bunu"] = "buh", ["Yout Wam"] = "ytw", ["Yoy"] = "yoy", ["Yuanga"] = "nua", ["Yucatec Maya"] = "yua", ["Yucatec Maya Sign Language"] = "msd", ["Yuchi"] = "yuc", ["Yucuañe Mixtec"] = "mvg", ["Yucuna"] = "ycn", ["Yug"] = "yug", ["Yugambal"] = "yub", ["Yugoslavian Sign Language"] = "ysl", ["Yugul"] = "ygu", ["Yuhup"] = "yab", ["Yuki"] = "yuk", ["Yukpa"] = "yup", ["Yukuben"] = "ybl", ["Yulu"] = "yul", ["Yuma"] = "yum", ["Yumana"] = "awd-yum", ["Yupiltepeque"] = "nai-yup", ["Yupua"] = "sai-yup", ["Yuqui"] = "yuq", ["Yuracare"] = "yuz", ["Yuri"] = "sai-yri", ["Yurok"] = "yur", ["Yuru"] = "ljx", ["Yurumanguí"] = "sai-yur", ["Yurutí"] = "yui", ["Yutanduchi Mixtec"] = "mab", ["Yuwana"] = "yau", ["Yuyu"] = "yxu", ["Zaachila Zapotec"] = "ztx", ["Zabana"] = "kji", ["Zacatepec Chatino"] = "ctz", ["Zacatlán-Ahuacatlán-Tepetzintla Nahuatl"] = "nhi", ["Zaghawa"] = "zag", ["Zaiwa"] = "atb", ["Zakhring"] = "zkr", ["Zambian Sign Language"] = "zsl", ["Zan Gula"] = "zna", ["Zanaki"] = "zak", ["Zande"] = "zne", ["Zangwal"] = "zah", ["Zaniza Zapotec"] = "zpw", ["Zapotec"] = "zap", ["Zaramo"] = "zaj", ["Zari"] = "zaz", ["Zarma"] = "dje", ["Zauzou"] = "zal", ["Zay"] = "zwa", ["Zayein Karen"] = "kxk", ["Zayse-Zergulla"] = "zay", ["Zazao"] = "jaj", ["Zbu"] = "sit-zbu", ["Zealandic"] = "zea", ["Zeem"] = "zua", ["Zemba"] = "dhm", ["Zeme Naga"] = "nzm", ["Zenag"] = "zeg", ["Zenaga"] = "zen", ["Zenzontepec Chatino"] = "czn", ["Zhaba"] = "zhb", ["Zhang-Zhung"] = "xzh", ["Zhenan Min"] = "nan-zhe", ["Zhire"] = "zhi", ["Zhoa"] = "zhw", ["Zhár"] = "jjr", ["Zia"] = "zia", ["Zialo"] = "zil", ["Zigula"] = "ziw", ["Zimakani"] = "zik", ["Zimba"] = "zmb", ["Zimbabwe Sign Language"] = "zib", ["Zinza"] = "zin", ["Zirenkel"] = "zrn", ["Ziriya"] = "zir", ["Zizilivakan"] = "ziz", ["Zo'é"] = "pto", ["Zokhuo"] = "yzk", ["Zoogocho Zapotec"] = "zpq", ["Zotung Chin"] = "czt", ["Zou"] = "zom", ["Zulgo-Gemzek"] = "gnd", ["Zumaya"] = "zuy", ["Zumbun"] = "jmb", ["Zuwara"] = "ber-zuw", ["ไซเฟ"] = "zyp", ["Záparo"] = "zro", ["Àhàn"] = "ahn", ["Áncá"] = "acb", ["Äiwoo"] = "nfl", ["Äynu"] = "aib", ["Ömie"] = "aom", ["Önge"] = "oon", ["ǀXam"] = "xam", ["ǁAni"] = "hnh", ["ǁGana"] = "gnk", ["ǁXegwi"] = "xeg", ["ǂHoan"] = "huc", ["ǃKung"] = "khi-kun", ["ǃXóõ"] = "nmn", ["กงกัณ"] = "kok", ["กบาลัน"] = "ckv", ["กรีก"] = "el", ["กรีกแบบไมซีนี"] = "gmy", ["กรีกโบราณ"] = "grc", ["กรีนแลนด์"] = "kl", ["กวางตุ้ง"] = "yue", ["กอกบอรอก"] = "trp", ["กอท"] = "got", ["กอทแบบไครเมีย"] = "gme-cgo", ["กอล"] = "cel-gau", ["กะดู"] = "zkd", ["กะตูตะวันตก"] = "kuf", ["กะตูตะวันออก"] = "ktv", ["กะยัน"] = "pdu", ["กะยาตะวันตก"] = "kyu", ["กะยาตะวันออก"] = "eky", ["กะเหรี่ยงดั้งเดิม"] = "kar-pro", ["กะเหรี่ยงปะโอ"] = "blk", ["กะเหรี่ยงสะกอ"] = "ksw", ["กะเหรี่ยงโปตะวันตก"] = "pwo", ["กะเหรี่ยงโปตะวันออก"] = "kjp", ["กะเหรี่ยงโปเหนือ"] = "pww", ["กะเหรี่ยงโปแพร่"] = "kjt", ["กะไบล์"] = "kab", ["กัจฉ์"] = "kfr", ["กันกานาอือ"] = "kne", ["กันกานาอือเหนือ"] = "xnn", ["กันนฑะ"] = "kn", ["กัมเบอรา"] = "xbr", ["กัวรานีคลาสสิก"] = "gn-cls", ["กัวรานีแบบปารากวัย"] = "gug", ["กั้น"] = "gan", ["กากาอุซ"] = "gag", ["กาดาซันชายฝั่ง"] = "kzj", ["กาตาลา"] = "ca", ["กาตาลาเก่า"] = "roa-oca", ["กานูรี"] = "kr", ["กาปัมปางัน"] = "pam", ["กามรูป"] = "inc-kam", ["การากัลปัก"] = "kaa", ["กาลากัน"] = "kqe", ["กาลากันแบบกากัน"] = "kll", ["กาลากันแบบตากาเกาลู"] = "klg", ["กาลิเซีย"] = "gl", ["กาลิเซีย-โปรตุเกสเก่า"] = "roa-opt", ["กาเรา"] = "kyj", ["กาโม"] = "gmv", ["กาโร"] = "grt", ["กิลเบิร์ต"] = "gil", ["กีนาบาตางันตอนบน"] = "dmg", ["กืออึซ"] = "gez", ["กุรุข"] = "kru", ["กูนียันดี"] = "gni", ["กูย"] = "kdt", ["กูร์ดั้งเดิม"] = "nic-gur-pro", ["กฺ๋อง"] = "ugo", ["กเบดั้งเดิม"] = "alv-gbe-pro", ["ขมุ"] = "kjg", ["ขมุอิกดั้งเดิม"] = "mkh-khm-pro", ["ขร้า-ไทดั้งเดิม"] = "qfa-tak-pro", ["ขร้าดั้งเดิม"] = "qfa-kra-pro", ["ครี"] = "cr", ["ครีโอลกาบูเวร์ดี"] = "kea", ["ครีโอลกินี-บิสเซา"] = "pov", ["ครีโอลจาเมกา"] = "jam", ["ครีโอลมอริเชียส"] = "mfe", ["ครีโอลมาเก๊า"] = "mzs", ["ครีโอลลุยเซียนา"] = "lou", ["ครีโอลเฮติ"] = "ht", ["คองโก"] = "kg", ["คอปติก"] = "cop", ["คอร์ซิกา"] = "co", ["คอร์นวอลล์"] = "kw", ["คอร์นวอลล์เก่า"] = "oco", ["คอเคเซียนตะวันตกเฉียงเหนือดั้งเดิม"] = "cau-nwc-pro", ["คอเคเซียนตะวันออกเฉียงเหนือดั้งเดิม"] = "cau-nec-pro", ["คังรังชือ"] = "ane", ["คัลมึค"] = "xal", ["คาคัส"] = "kjh", ["คาชุบ"] = "csb", ["คาซัค"] = "kk", ["คาซี"] = "kha", ["คานธาระ"] = "pgd", ["คาบาร์เดีย"] = "kbd", ["คาร์ทเวเลียนดั้งเดิม"] = "ccs-pro", ["คาร์นิโคบาร์"] = "caq", ["คาลาเมียนดั้งเดิม"] = "phi-kal-pro", ["คาเซียนดั้งเดิม"] = "aav-khs-pro", ["คาเรเลีย"] = "krl", ["คำตี้"] = "kht", ["คำเมือง"] = "nod", ["คีตัน"] = "zkt", ["คีร์กีซ"] = "ky", ["คุชราต"] = "gu", ["คุชราตกลาง"] = "inc-mgu", ["คุชราตเก่า"] = "inc-ogu", ["คูนิงามิ"] = "xug", ["คูมาน"] = "qwm", ["คูมุก"] = "kum", ["งูนีดั้งเดิม"] = "bnt-ngu-pro", ["จอร์เจีย"] = "ka", ["จอร์เจียน-แซนดั้งเดิม"] = "ccs-gzn-pro", ["จอร์เจียเก่า"] = "oge", ["จักมา"] = "ccp", ["จามตะวันตก"] = "cja", ["จามตะวันออก"] = "cjm", ["จามิกดั้งเดิม"] = "cmc-pro", ["จิ่งเผาะ"] = "kac", ["จิ้น"] = "cjy", ["จีน"] = "zh", ["จีนกลาง"] = "cmn", ["จีนยุคกลาง"] = "ltc", ["จีนวรรณกรรม"] = "lzh", ["จีนเก่า"] = "och", ["จุน"] = "cuq", ["จูร์เชน"] = "juc", ["จ้วง"] = "za", ["จ้วงแบบจั่วเจียง"] = "zzj", ["จ้วงแบบหนง"] = "zhn", ["ชวา"] = "jv", ["ชวาเก่า"] = "kaw", ["ชวาแบบนิวแคลิโดเนีย"] = "jas", ["ชวาแบบแคริบเบียน"] = "jvn", ["ชอง"] = "cog", ["ชากาทาย"] = "chg", ["ชาดิกดั้งเดิม"] = "cdc-pro", ["ชาดิกตอนกลางดั้งเดิม"] = "cdc-cbm-pro", ["ชาบากาโน"] = "cbk", ["ชามอร์โร"] = "ch", ["ชุกชี"] = "ckt", ["ชูวัช"] = "cv", ["ซอกดิกดั้งเดิม"] = "ira-sgc-pro", ["ซองคา"] = "dz", ["ซอร์เบียตอนบน"] = "hsb", ["ซอร์เบียตอนล่าง"] = "dsb", ["ซังโก"] = "sg", ["ซันสการ์"] = "zau", ["ซัมบัล"] = "xsb", ["ซัสซารี"] = "sdc", ["ซากีซายา"] = "szy", ["ซาคา"] = "sah", ["ซาซา"] = "zza", ["ซาปา"] = "tys", ["ซามัว"] = "sm", ["ซามาตอนกลาง"] = "sml", ["ซามาแบบปางูตารัน"] = "slm", ["ซามิกดั้งเดิม"] = "smi-pro", ["ซามีเหนือ"] = "se", ["ซามีแบบกิลดิน"] = "sjd", ["ซามีแบบปีเต"] = "sje", ["ซามีแบบลูเล"] = "smj", ["ซามีแบบอีนารี"] = "smn", ["ซามีใต้"] = "sma", ["ซาราวักเหนือดั้งเดิม"] = "poz-swa-pro", ["ซาร์ดิเนีย"] = "sc", ["ซาลาร์"] = "slr", ["ซาโฮ"] = "ssy", ["ซิซิลี"] = "scn", ["ซินดาริน"] = "sjn", ["ซีกายานา"] = "sky", ["ซีดามา"] = "sid", ["ซิโน-ทิเบตันดั้งเดิม"] = "sit-pro", ["ซุนดา"] = "su", ["ซูก"] = "tsg", ["ซูทู"] = "st", ["ซูทูเหนือ"] = "nso", ["ซูนี"] = "zun", ["ซูรีเกาโนน"] = "sgd", ["ซูลาเวซีใต้ดั้งเดิม"] = "poz-ssw-pro", ["ซูลู"] = "zu", ["ซูเมอร์"] = "sux", ["ซูโดเวีย"] = "xsv", ["ญัฮกุร"] = "cbn", ["ญี่ปุ่น"] = "ja", ["ญี่ปุ่นเก่า"] = "ojp", ["ญ้อ"] = "nyw", ["ดราวิเดียนดั้งเดิม"] = "dra-pro", ["ดราวิเดียนตอนกลางดั้งเดิม"] = "dra-cen-pro", ["ดราวิเดียนเหนือดั้งเดิม"] = "dra-nor-pro", ["ดราวิเดียนใต้ดั้งเดิม"] = "dra-sou-pro", ["ดราวิเดียนใต้ที่สองดั้งเดิม"] = "dra-sdt-pro", ["ดราวิเดียนใต้ที่หนึ่งดั้งเดิม"] = "dra-sdo-pro", ["ดัตช์"] = "nl", ["ดัตช์กลาง"] = "dum", ["ดัตช์เก่า"] = "odt", ["ดาโคตา"] = "dak", ["ดุงกาน"] = "dng", ["ดูซุนตอนกลาง"] = "dtp", ["ตงเซียง"] = "sce", ["ตองงา"] = "to", ["ตังกุต"] = "txg", ["ตันดากาโนน"] = "tgn", ["ตั่ย"] = "tyz", ["ตากาล็อก"] = "tl", ["ตางัม"] = "sit-tgm", ["ตาตาร์"] = "tt", ["ตาตาร์แบบไครเมีย"] = "crh", ["ตาอีโน"] = "tnq", ["ตาฮีตี"] = "ty", ["ตีรูไร"] = "tiy", ["ตีโดเร"] = "tvo", ["ตุมซุก"] = "xtq", ["ตุรกี"] = "tr", ["ตุรกีแบบออตโตมัน"] = "ota", ["ตุรกีแบบอานาโตเลียเก่า"] = "trk-oat", ["ตุรุง"] = "try", ["ตูปี-กัวรานีดั้งเดิม"] = "tup-gua-pro", ["ตูปีเก่า"] = "tpw", ["ตูลู"] = "tcy", ["ตูวา"] = "tyv", ["ตูวาลู"] = "tvl", ["ตูเปียนดั้งเดิม"] = "tup-pro", ["ตโบลี"] = "tbl", ["ต้งเหนือ"] = "doc", ["ต้งใต้"] = "kmc", ["ทมิฬ"] = "ta", ["ทมิฬเก่า"] = "oty", ["ตอกปีซิน"] = "tpi", ["ทะวืง"] = "thm", ["ทาจิก"] = "tg", ["ทาบาซารัน"] = "tab", ["ทิเบต"] = "bo", ["ทิเบตคลาสสิก"] = "xct", ["ทิเบตเก่า"] = "otb", ["ทือกรึญญา"] = "ti", ["ทือเกร"] = "tig", ["นอซู"] = "ii", ["นอร์มัน"] = "nrf", ["นอร์สดั้งเดิม"] = "gmq-pro", ["นอร์สเก่า"] = "non", ["นอร์เวย์"] = "no", ["นอร์เวย์กลาง"] = "gmq-mno", ["นอร์เวย์แบบนือนอสก์"] = "nn", ["นอร์เวย์แบบบุ๊กมอล"] = "nb", ["นัคดั้งเดิม"] = "cau-nkh-pro", ["นาวัตล์"] = "nah", ["นาวัตล์คลาสสิก"] = "nci", ["นาวัตล์ตอนกลาง"] = "nhn", ["นาวันดั้งเดิม"] = "azc-nah-pro", ["นาวาโฮ"] = "nv", ["นาอูรู"] = "na", ["นาโปลี"] = "nap", ["นิวเคลียร์พอลินีเชียนดั้งเดิม"] = "poz-pnp-pro", ["นิวเบียเก่า"] = "onw", ["นิโคบารีสดั้งเดิม"] = "aav-nic-pro", ["นิโคบาร์กลาง"] = "ncb", ["นีวเว"] = "niu", ["นุง"] = "nut", ["นูเบียนดั้งเดิม"] = "nub-pro", ["บราฮุอี"] = "brh", ["บริทอนิกดั้งเดิม"] = "cel-bry-pro", ["บรูตะวันตก"] = "brv", ["บรูตะวันออก"] = "bru", ["บลาการ์"] = "beu", ["บอลโต-สลาวิกดั้งเดิม"] = "ine-bsl-pro", ["บะห์นัร"] = "bdq", ["บัตส์"] = "bbl", ["บันจาร์"] = "bjn", ["บัมบารา"] = "bm", ["บัลติ"] = "bft", ["บัลแกเรีย"] = "bg", ["บาลี"] = "pi", ["บาวาเรีย"] = "bar", ["บาสก์"] = "eu", ["บาสก์ดั้งเดิม"] = "euq-pro", ["บาหลี"] = "ban", ["บาเจาแบบอินโดนีเซีย"] = "bdl", ["บาเจาแบบเวสต์โคสต์"] = "bdr", ["บาโลจ"] = "bal", ["บิสลามา"] = "bi", ["บีซู"] = "bzi", ["บีนูกิด"] = "bkd", ["บีโคลตอนกลาง"] = "bcl", ["บูกิส"] = "bug", ["บูร์บอแน-แบรีชง"] = "roa-bbn", ["บูร์ยัต"] = "bua", ["ปนัร"] = "pbv", ["ปนัร-คาซี-ลึงงัมดั้งเดิม"] = "aav-pkl-pro", ["ปยู"] = "pyx", ["ปรากฤต"] = "pra", ["ปรากฤตแบบอโศก"] = "inc-ash", ["ปะหล่องดั้งเดิม"] = "mkh-pal-pro", ["ปักษ์ใต้"] = "sou", ["ปังควาฬฺ"] = "pgg", ["ปัญจาบ"] = "pa", ["ปัญจาบเก่า"] = "inc-opa", ["ปาทาน"] = "ps", ["ปาทานดั้งเดิม"] = "ira-pat-pro", ["ปาลาวาโนตอนกลาง"] = "plc", ["ปาลาวาโนตะวันตกเฉียงใต้"] = "plv", ["ปาลาวาโนแบบบรูกส์พอยต์"] = "plw", ["ปาเปียเมนตู"] = "pap", ["ปาเลา"] = "pau", ["ปาโกะห์"] = "pac", ["ปีการ์"] = "pcd", ["ปีปิล"] = "ppl", ["ปีเยมอนเต"] = "pms", ["ปูกาปูกา"] = "pkp", ["ปู้อี"] = "pcc", ["ผิงเหนือ"] = "cnp", ["ผิงใต้"] = "csp", ["ผูเซียน"] = "cpx", ["ผู้น้อย"] = "pho", ["ผู้ไท"] = "pht", ["ฝรั่งเศส"] = "fr", ["ฝรั่งเศสกลาง"] = "frm", ["ฝรั่งเศสเก่า"] = "fro", ["พทคะ"] = "bfq", ["พม่า"] = "my", ["พม่าเก่า"] = "obr", ["พรัช"] = "bra", ["พวน"] = "phu", ["พอลินีเชียนดั้งเดิม"] = "poz-pol-pro", ["พอลินีเชียนตะวันออกดั้งเดิม"] = "poz-pep-pro", ["พิหาร"] = "bh", ["พ่าเก"] = "phk", ["ฟรียูลี"] = "fur", ["ฟรีเชียตะวันตก"] = "fy", ["ฟรีเชียเก่า"] = "ofs", ["ฟรีเชียเหนือ"] = "frr", ["ฟรีเชียแบบซาเทอร์ลันท์"] = "stq", ["ฟร็องโก-พรอว็องส์"] = "frp", ["ฟาลา"] = "fax", ["ฟาลิสกุส"] = "xfa", ["ฟินนิกดั้งเดิม"] = "urj-fin-pro", ["ฟินิเชีย"] = "phn", ["ฟินแลนด์"] = "fi", ["ฟิลิปปินส์ดั้งเดิม"] = "phi-pro", ["ฟีจี"] = "fj", ["ฟูลา"] = "ff", ["มคหะ"] = "mag", ["มงตาแญ"] = "moe", ["มณีปุระ"] = "mni", ["มณีปุระเก่า"] = "omp", ["มนองตอนกลาง"] = "cmo", ["มนองใต้"] = "mnn", ["มราฐี"] = "mr", ["มราฐีเก่า"] = "omr", ["มลยาฬัม"] = "ml", ["มลายูตอนกลาง"] = "pse", ["มลายูแบบตรังกานู"] = "poz-ter", ["มลายูแบบบรูไน"] = "kxd", ["มลายูแบบปัตตานี"] = "mfa", ["มลายูแบบเกอดะฮ์"] = "meo", ["มวานี"] = "wmw", ["มอกชา"] = "mdf", ["มองโกลิกดั้งเดิม"] = "xgn-pro", ["มองโกเลีย"] = "mn", ["มองโกเลียกลาง"] = "xng", ["มองโกเลียคลาสสิก"] = "cmg", ["มอญ"] = "mnw", ["มอญ-เขมรดั้งเดิม"] = "mkh-pro", ["มอญกลาง"] = "mkh-mmn", ["มอญดั้งเดิม"] = "mkh-mnc-pro", ["มอญเก่า"] = "omx", ["มอญแบบไทย"] = "mnw-tha", ["มอลตา"] = "mt", ["มอแซนแดรอน"] = "mzn", ["มัล"] = "mlf", ["มัลดีฟส์"] = "dv", ["มัสบาเต"] = "msb", ["มากัซซาร์"] = "mak", ["มากินดาเนา"] = "mdh", ["มาซิโดเนีย"] = "mk", ["มายันดั้งเดิม"] = "myn-pro", ["มาราเนา"] = "mrw", ["มาร์วาร์"] = "mwr", ["มาร์แชลล์"] = "mh", ["มาลากาซี"] = "mg", ["มาลาโย-จามิกดั้งเดิม"] = "poz-mcm-pro", ["มาลาโย-ซุมบาวันดั้งเดิม"] = "poz-msa-pro", ["มาลาโย-พอลินีเชียนดั้งเดิม"] = "poz-pro", ["มาลาโย-พอลินีเชียนตอนกลาง-ตะวันออกดั้งเดิม"] = "poz-cet-pro", ["มาลาโย-พอลินีเชียนตะวันออกดั้งเดิม"] = "pqe-pro", ["มาวรี"] = "mi", ["มาเลย์อิกดั้งเดิม"] = "poz-mly-pro", ["มาเลเซีย"] = "ms", ["มาไซ"] = "mas", ["มิงเกรเลีย"] = "xmf", ["มิยาโกะ"] = "mvi", ["มีนังกาเบา"] = "min", ["มีรังดา"] = "mwl", ["มีโซ"] = "lus", ["มือรัสเซีย"] = "rsl", ["มืออินเดีย"] = "ins", ["มืออินโดนีเซีย"] = "inl", ["มืออเมริกัน"] = "ase", ["มือเนปาล"] = "nsp", ["มือไทย"] = "tsq", ["มุนดาดั้งเดิม"] = "mun-pro", ["มูซี"] = "mui", ["ม้ง-เมี่ยนดั้งเดิม"] = "hmx-pro", ["ม้งขาว"] = "mww", ["ม้งดั้งเดิม"] = "hmn-pro", ["ม้งเขียว"] = "hnj", ["ยอง"] = "yno", ["ยอลอ"] = "yol", ["ยะไข่"] = "rki", ["ยากัน"] = "yka", ["ยามานา"] = "yag", ["ยามี"] = "tao", ["ยาเอยามะ"] = "rys", ["ยิดดิช"] = "yi", ["ยุปปิก"] = "esu", ["ยูกริกดั้งเดิม"] = "urj-ugr-pro", ["ยูการิต"] = "uga", ["ยูราลิกดั้งเดิม"] = "urj-pro", ["ยูเครน"] = "uk", ["ยูโต-แอซเทกันดั้งเดิม"] = "azc-pro", ["รวันดา-รุนดี"] = "rw", ["รัสเซีย"] = "ru", ["ราชพังสี"] = "rjs", ["ราปานูอี"] = "rap", ["ราโรโตงา"] = "rar", ["ริฟ"] = "rif", ["รูซินแบบคาร์พาเทีย"] = "rue", ["รูซินแบบพันโนเนีย"] = "rsk", ["ร่วม"] = "mul", ["ลอมบาร์ด"] = "lmo", ["ละติน"] = "la", ["ลัก"] = "lbe", ["ลักเกีย"] = "lbc", ["ลักเซมเบิร์ก"] = "lb", ["ลัซ"] = "lzz", ["ลัตเกล"] = "ltg", ["ลัตเวีย"] = "lv", ["ลัมปุงกิกดั้งเดิม"] = "poz-lgx-pro", ["ลัมโบยา"] = "lmy", ["ลาดัก"] = "lbj", ["ลาดิน"] = "lld", ["ลาดิโน"] = "lad", ["ลาว"] = "lo", ["ลิงกาลา"] = "ln", ["ลิทัวเนีย"] = "lt", ["ลิทัวเนียเก่า"] = "olt", ["ลินดู"] = "klw", ["ลิมบู"] = "lif", ["ลิมเบิร์ก"] = "li", ["ลิววี"] = "olo", ["ลีกูเรีย"] = "lij", ["ลีสู่"] = "lis", ["ลีโวเนีย"] = "liv", ["ลึงงัม"] = "lyg", ["ลูกันดา"] = "lg", ["ลูอิชดั้งเดิม"] = "sit-luu-pro", ["วอลตา-คองโกดั้งเดิม"] = "nic-vco-pro", ["วอลตา-ไนเจอร์ดั้งเดิม"] = "alv-von-pro", ["วันดัล"] = "xvn", ["วัลลูน"] = "wa", ["วาราโอ"] = "wba", ["วาไร"] = "war", ["วินเนอเบโก"] = "win", ["วีลามอวิตแซ"] = "wym", ["สกอต"] = "sco", ["สมราย"] = "smu", ["สลาวอนิกคริสตจักรเก่า"] = "cu", ["สลาวิกดั้งเดิม"] = "sla-pro", ["สลาวิกตะวันออกเก่า"] = "orv", ["สวานา"] = "tn", ["สวาฮีลี"] = "sw", ["สวีเดน"] = "sv", ["สวีเดนเก่า"] = "gmq-osw", ["สะโอจ"] = "scq", ["สันถาลี"] = "sat", ["สันสกฤต"] = "sa", ["ซำเร"] = "sxm", ["สิกขิม"] = "sip", ["สิงหล"] = "si", ["สินธ์"] = "sd", ["สิเลฏ"] = "syl", ["สุ่ย"] = "swi", ["สเปน"] = "es", ["สเปนเก่า"] = "osp", ["สโลวัก"] = "sk", ["สโลวักเก่า"] = "zlw-osk", ["สโลวินช์"] = "zlw-slv", ["สโลวีเนีย"] = "sl", ["หง่วน"] = "nuo", ["หมะ"] = "cma", ["หมิ่นดั้งเดิม"] = "zhx-min-pro", ["หมิ่นตอนกลาง"] = "czo", ["หมิ่นตะวันออก"] = "cdo", ["หมิ่นเหนือ"] = "mnp", ["หมิ่นใต้"] = "nan", ["หล่อยแอว๋"] = "luh", ["ห่อยซัน"] = "zhx-tai", ["อวัธ"] = "awa", ["ออสซีเซีย"] = "os", ["ออสโตรนีเซียนดั้งเดิม"] = "map-pro", ["ออสโตรเอเชียติกดั้งเดิม"] = "aav-pro", ["ออโรโม"] = "om", ["อะดีเกยา"] = "ady", ["อะตง"] = "aot", ["อะวาร์"] = "av", ["อะวาร์เก่า"] = "oav", ["อะแพชีตะวันตก"] = "apw", ["อักลัน"] = "akl", ["อังกฤษ"] = "en", ["อังกฤษกลาง"] = "enm", ["อังกฤษเก่า"] = "ang", ["อับคาเซีย"] = "ab", ["อัมฮารา"] = "am", ["อัมโมน"] = "sem-amm", ["อัลไตเหนือ"] = "atv", ["อัลไตใต้"] = "alt", ["อัสตูเรียส"] = "ast", ["อัสสัม"] = "as", ["อัสสัมกลาง"] = "inc-mas", ["อัสสัมช่วงต้น"] = "inc-oas", ["อากูตายา"] = "agn", ["อาข่า"] = "ahk", ["อาคัน"] = "ak", ["อาซี"] = "bno", ["อานาโตเลียนดั้งเดิม"] = "ine-ana-pro", ["อาบาซา"] = "abq", ["อาฟรีกานส์"] = "af", ["อาฟาร์"] = "aa", ["อามามิโอชิมะเหนือ"] = "ryn", ["อามามิโอชิมะใต้"] = "ams", ["อารากอน"] = "an", ["อารามายา"] = "arc", ["อาร์มีเนีย"] = "hy", ["อาร์มีเนียกลาง"] = "axm", ["อาร์มีเนียนดั้งเดิม"] = "hyx-pro", ["อาร์มีเนียเก่า"] = "xcl", ["อาลูตีก"] = "ems", ["อาหม"] = "aho", ["อาหรับ"] = "ar", ["อาหรับแบบซูดาน"] = "apd", ["อาหรับแบบตูนิเซีย"] = "aeb", ["อาหรับแบบทาจิกิสถาน"] = "abh", ["อาหรับแบบบาห์เรน"] = "abv", ["อาหรับแบบลิเบีย"] = "ayl", ["อาหรับแบบลิแวนต์ใต้"] = "ajp", ["อาหรับแบบอันดะลุส"] = "xaa", ["อาหรับแบบอิรัก"] = "acm", ["อาหรับแบบอียิปต์"] = "arz", ["อาหรับแบบอุซเบกิสถาน"] = "auz", ["อาหรับแบบฮิญาซ"] = "acw", ["อาหรับแบบเมโสโปเตเมียเหนือ"] = "ayp", ["อาหรับแบบเยเมน"] = "ayn", ["อาหรับแบบแอลจีเรีย"] = "arq", ["อาหรับแบบโมร็อกโก"] = "ary", ["อาหรับแบบโอมาน"] = "acx", ["อาหรับแบบไซปรัส"] = "acy", ["อาเจะฮ์"] = "ace", ["อาเซอร์ไบจาน"] = "az", ["อาเนตยูม"] = "aty", ["อาเบ"] = "aba", ["อิกโบ"] = "ig", ["อิงกุช"] = "inh", ["อิงเกรีย"] = "izh", ["อิตาลิกดั้งเดิม"] = "itc-pro", ["อิตาลี"] = "it", ["อินุกติตุต"] = "iu", ["อินุอิตดั้งเดิม"] = "esx-inu-pro", ["อินเทร์ลิงเกว"] = "ie", ["อินเทอร์ลิงกวา"] = "ia", ["อินโด-ยูโรเปียนดั้งเดิม"] = "ine-pro", ["อินโด-อารยันดั้งเดิม"] = "inc-pro", ["อินโด-อิเรเนียนดั้งเดิม"] = "iir-pro", ["อินโดนีเซีย"] = "id", ["อิสเตรีย"] = "ist", ["อิเรเนียนดั้งเดิม"] = "ira-pro", ["อีดาอัน"] = "dbj", ["อีทรัสคัน"] = "ett", ["อีบัน"] = "iba", ["อียิปต์"] = "egy", ["อีรานุน"] = "ill", ["อีสาน"] = "tts", ["อีโด"] = "io", ["อีโลกาโน"] = "ilo", ["อุซเบก"] = "uz", ["อุดมูร์ต"] = "udm", ["อุตซิตา"] = "oc", ["อุตซิตาเก่า"] = "pro", ["อุนเซอร์ด็อยทช์"] = "uln", ["อุมเบรีย"] = "xum", ["อุยกูร์"] = "ug", ["อุยกูร์เก่า"] = "oui", ["อูดี"] = "udi", ["อูรดู"] = "ur", ["อูรักลาโวยจ"] = "urk", ["อู๋"] = "wuu", ["อเวสตะ"] = "ae", ["อ่ายตน"] = "aio", ["ฮกเกี้ยน"] = "nan-hbl", ["ฮังการี"] = "hu", ["ฮังการีเก่า"] = "ohu", ["ฮาชอง"] = "haj", ["ฮานูโนโอ"] = "hnn", ["ฮาวาย"] = "haw", ["ฮินดี"] = "hi", ["ฮินดีเก่า"] = "inc-ohi", ["ฮินดีแบบฟีจี"] = "hif", ["ฮินดูสตานีแบบแคริบเบียน"] = "hns", ["ฮีบรู"] = "he", ["ฮีรีโมตู"] = "ho", ["ฮีลีไกโนน"] = "hil", ["ฮุนสริก"] = "hrx", ["ฮูร์เรีย"] = "xhu", ["เกชวนดั้งเดิม"] = "qwe-pro", ["เกชัว"] = "qu", ["เกชัวคลาสสิก"] = "qwc", ["เกรตอันดามันนีสดั้งเดิม"] = "qfa-adm-pro", ["เกอฮอ"] = "kpm", ["เกาหลี"] = "ko", ["เกาหลีกลาง"] = "okm", ["เกาหลีดั้งเดิม"] = "qfa-kor-pro", ["เกาหลีเก่า"] = "oko", ["เกาหลีใหม่ช่วงต้น"] = "ko-ear", ["เกโกโย"] = "ki", ["เขมร"] = "km", ["เขมรกลาง"] = "xhm", ["เขมรดั้งเดิม"] = "mkh-kmr-pro", ["เขมรเก่า"] = "okz", ["เขมรเหนือ"] = "kxm", ["เขิน"] = "kkh", ["เคลติกดั้งเดิม"] = "cel-pro", ["เควนยา"] = "qya", ["เคอร์ดิชดั้งเดิม"] = "ku-pro", ["เคิร์ดตอนกลาง"] = "ckb", ["เคิร์ดเหนือ"] = "kmr", ["เจวา"] = "ny", ["เจอร์แมนิกดั้งเดิม"] = "gem-pro", ["เจอร์แมนิกตะวันตกดั้งเดิม"] = "gmw-pro", ["เจียเม่า"] = "jio", ["เฉ่าเหมียว"] = "cov", ["เชจู"] = "jje", ["เชเชน"] = "ce", ["เชเยนน์"] = "chy", ["เชโรกี"] = "chr", ["เช็ก"] = "cs", ["เช็กเก่า"] = "zlw-ocs", ["เซบัวโน"] = "ceb", ["เซมิติกดั้งเดิม"] = "sem-pro", ["เซมิติกตะวันตกดั้งเดิม"] = "sem-wes-pro", ["เซอร์โบ-โครเอเชีย"] = "sh", ["เซอไม"] = "sea", ["เซียง"] = "hsn", ["เดนมาร์ก"] = "da", ["เดนมาร์กเก่า"] = "gmq-oda", ["เดาโร"] = "dwr", ["เตตุน"] = "tet", ["เตลูกู"] = "te", ["เตอร์กิกดั้งเดิม"] = "trk-pro", ["เตอร์กิกเก่า"] = "otk", ["เตอร์นาเต"] = "tft", ["เตอเมียร์"] = "tea", ["เติร์กเมน"] = "tk", ["เทาส์"] = "twf", ["เนปาล"] = "ne", ["เนวาร์"] = "new", ["เนวาร์กลาง"] = "nwx", ["เนวาร์คลาสสิก"] = "nwc", ["เนเกอร์ฮอลันดส์"] = "dcr", ["เบ"] = "onb", ["เบ-ไทดั้งเดิม"] = "qfa-bet-pro", ["เบงกอล"] = "bn", ["เบงกอลกลาง"] = "inc-mbn", ["เบงกอลเก่า"] = "inc-obn", ["เบดั้งเดิม"] = "qfa-onb-pro", ["เบนูเอ-คองโกดั้งเดิม"] = "nic-bco-pro", ["เบรอตง"] = "br", ["เบรอตงกลาง"] = "xbm", ["เบรอตงเก่า"] = "obt", ["เบลารุส"] = "be", ["เบอตาวี"] = "bew", ["เบอราวัน"] = "lod", ["เบอร์เบอร์ดั้งเดิม"] = "ber-pro", ["เบอร์เบอร์แบบตูนีเซีย"] = "sds", ["เปอร์มิกดั้งเดิม"] = "urj-prm-pro", ["เปอร์เซีย"] = "fa", ["เปอร์เซียกลาง"] = "pal", ["เปอร์เซียเก่า"] = "peo", ["เพลาท์ดิทช์"] = "pdt", ["เมวาร์"] = "mtr", ["อิ้วเมี่ยน"] = "ium", ["เมี่ยนดั้งเดิม"] = "hmx-mie-pro", ["เยอรมัน"] = "de", ["เยอรมันตอนกลางตะวันออก"] = "gmw-ecg", ["เยอรมันต่ำ"] = "nds", ["เยอรมันต่ำกลาง"] = "gml", ["เยอรมันต่ำแบบเยอรมนี"] = "nds-de", ["เยอรมันสูงกลาง"] = "gmh", ["เยอรมันสูงเก่า"] = "goh", ["เยอรมันแบบซิพเซอร์"] = "gmw-zps", ["เยอรมันแบบว็อลกา"] = "gmw-vog", ["เยอรมันแบบอลามันเนีย"] = "gsw", ["เยอรมันแบบเพนซิลเวเนีย"] = "pdc", ["เรอหวั่ง"] = "raw", ["เลซกี"] = "lez", ["เลออน"] = "roa-leo", ["เลออนเก่า"] = "roa-ole", ["เลอเวือะตะวันตก"] = "lcp", ["เลอเวือะตะวันออก"] = "lwl", ["เวนดา"] = "ve", ["เวปส์"] = "vep", ["เวลส์"] = "cy", ["เวลส์กลาง"] = "wlm", ["เวลส์เก่า"] = "owl", ["เวอโร"] = "vro", ["เวียดนาม"] = "vi", ["เวียดนามกลาง"] = "mkh-mvi", ["เวียตติกดั้งเดิม"] = "mkh-vie-pro", ["เวเนโต"] = "vec", ["เศรปา"] = "xsr", ["เสฉวน"] = "zhx-sic", ["เหมื่อง"] = "mtq", ["เอชเตฮาร์ด"] = "esh", ["เอซเตรมาดูรา"] = "ext", ["เอมีเลีย"] = "egl", ["เอร์เซีย"] = "myv", ["เอสกิโมดั้งเดิม"] = "esx-esk-pro", ["เอสเปรันโต"] = "eo", ["เอสโตเนีย"] = "et", ["เอเว"] = "ee", ["เอเวนค์"] = "evn", ["เอเว็น"] = "eve", ["เอโดม"] = "xdm", ["เฮลเลนิกดั้งเดิม"] = "grk-pro", ["เฮาซา"] = "ha", ["เฮเรโร"] = "hz", ["แกลิกแบบสกอตแลนด์"] = "gd", ["แคชเมียร์"] = "ks", ["แคะ"] = "hak", ["แจพอนิกดั้งเดิม"] = "jpx-pro", ["แซกซันเก่า"] = "osx", ["แดลเมเชีย"] = "dlm", ["แต้จิ๋ว"] = "nan-tws", ["แบกเตรีย"] = "xbc", ["แบชเคียร์"] = "ba", ["แบนทอยด์ดั้งเดิม"] = "nic-bod-pro", ["แบนทูดั้งเดิม"] = "bnt-pro", ["แบล็กสปีช"] = "art-bsp", ["แพ็กเจ"] = "pkc", ["แฟโร"] = "fo", ["แมงซ์"] = "gv", ["แมนจู"] = "mnc", ["แสก"] = "skb", ["แอกแคด"] = "akk", ["แอตแลนติก-คองโกดั้งเดิม"] = "alv-pro", ["แอฟโฟรเอเชียติกดั้งเดิม"] = "afa-pro", ["แอลกองเคียนดั้งเดิม"] = "alg-pro", ["แอลจิกดั้งเดิม"] = "aql-pro", ["แอลฟ์ดาเลิน"] = "ovd", ["แอละแบมา"] = "akz", ["แอลเบเนีย"] = "sq", ["แอลเบเนียนดั้งเดิม"] = "sqj-pro", ["โกฟา"] = "gof", ["โคซา"] = "xh", ["โคณฑี"] = "gon", ["โคตาน"] = "kho", ["โคทาวา"] = "avk", ["โจว"] = "tsu", ["โชนา"] = "sn", ["โชร์"] = "cjs", ["โซตซิล"] = "tzo", ["โซมาลอยด์ดั้งเดิม"] = "cus-som-pro", ["โซมาลี"] = "so", ["โซ่ง"] = "soa", ["โตทา"] = "tcx", ["โตฟา"] = "kim", ["โทกูโนชิมะ"] = "tkn", ["โทแคเรียนดั้งเดิม"] = "ine-toc-pro", ["โทแคเรียนบี"] = "txb", ["โทแคเรียนเอ"] = "xto", ["โนเวียล"] = "nov", ["โบล"] = "bol", ["โบลีเนา"] = "smk", ["โบโด"] = "brx", ["โบโด-กาโรดั้งเดิม"] = "tbq-bdg-pro", ["โปรตุเกส"] = "pt", ["โปแลนด์"] = "pl", ["โปแลนด์เก่า"] = "zlw-opl", ["โภชปุระ"] = "bho", ["โมกิล"] = "mkj", ["โมอับ"] = "obm", ["โมเชโน"] = "mhn", ["โยกัด"] = "yog", ["โยนางูนิ"] = "yoi", ["โยรง"] = "yox", ["โยรูบอยด์ดั้งเดิม"] = "alv-yrd-pro", ["โยรูบา"] = "yo", ["โยรูบาดั้งเดิม"] = "alv-yor-pro", ["โรมัญญา"] = "rgn", ["รูมันช์"] = "rm", ["โรมานี"] = "rom", ["โรมาเนีย"] = "ro", ["โรฮีนจา"] = "rhg", ["โลจบาน"] = "jbo", ["โลโกโน"] = "arw", ["โลโล-เบอร์มีซดั้งเดิม"] = "tbq-lob-pro", ["โลโลอิชดั้งเดิม"] = "tbq-lol-pro", ["โวต"] = "vot", ["โวลอฟ"] = "wo", ["โวลาปุก"] = "vo", ["โอกินาวะ"] = "ryu", ["โอกิโนเอราบุ"] = "okn", ["โอจิบเว"] = "oj", ["โอซิง"] = "osi", ["โอริยา"] = "or", ["โอริยากลาง"] = "inc-mor", ["โอริยาเก่า"] = "inc-oor", ["โอเชียนิกดั้งเดิม"] = "poz-oce-pro", ["ไซลีเซีย"] = "szl", ["ไทขาว"] = "twh", ["ไทดั้งเดิม"] = "tai-pro", ["ไทดำ"] = "blt", ["ไทตะวันตกเฉียงใต้ดั้งเดิม"] = "tai-swe-pro", ["ไทย"] = "th", ["ไทลื้อ"] = "khb", ["ไทแดง"] = "tyr", ["ไทแมน"] = "tyj", ["ไทแหล่ง"] = "tjl", ["ไทใต้คง"] = "tdd", ["ไทใหญ่"] = "shn", ["ไนเจอร์-คองโกดั้งเดิม"] = "nic-pro", ["ไปวัน"] = "pwn", ["ไมถิลี"] = "mai", ["ไมนอส"] = "omn", ["ไมยา"] = "sks", ["ไมโครนีเซียนดั้งเดิม"] = "poz-mic-pro", ["ไว"] = "vai", ["ไหล"] = "lic", ["ไหลดั้งเดิม"] = "qfa-lic-pro", ["ไหหลำ"] = "hnm", ["ไอซ์แลนด์"] = "is", ["ไอนุ"] = "ain", ["ไอย์มารา"] = "ay", ["ไอริช"] = "ga", ["ไอริชกลาง"] = "mga", ["ไอริชเก่า"] = "sga", ["Ỹaroamë"] = "yro", } jjcl2mmux9l2g84d0nngtdy4hx1npzx มอดูล:scripts/code to canonical name 828 211722 5775262 5719256 2026-10-14T09:54:06Z Octahedron80 267 5775262 Scribunto text/plain return { ["Adlm"] = "อัดลัม", ["Afak"] = "อาฟากา", ["Aghb"] = "Caucasian Albanian", ["Ahom"] = "อาหม", ["Arab"] = "อาหรับ", ["Armi"] = "แอราเมอิกจักรวรรดิ", ["Armn"] = "อาร์มีเนีย", ["Avst"] = "อเวสตะ", ["Bali"] = "บาหลี", ["Bamu"] = "บามุม", ["Bass"] = "บัสซา", ["Batk"] = "บาตัก", ["Beng"] = "เบงกอล", ["Bhks"] = "ไภกษุกี", ["Blis"] = "Blissymbolic", ["Bopo"] = "จู้อิน", ["Brah"] = "พราหมี", ["Brai"] = "เบรลล์", ["Bugi"] = "ลนตารา", ["Buhd"] = "บูฮิด", ["Cakm"] = "จักมา", ["Cans"] = "Canadian syllabic", ["Cari"] = "คาเรีย", ["Cham"] = "จาม", ["Cher"] = "เชโรกี", ["Chis"] = "Chisoi", ["Chrs"] = "Khwarezmian", ["Copt"] = "คอปติก", ["Cpmn"] = "Cypro-Minoan", ["Cprt"] = "ไซปรัส", ["Cyrl"] = "ซีริลลิก", ["Cyrs"] = "ซีริลลิกเก่า", ["Deva"] = "เทวนาครี", ["Diak"] = "ดิเวส อกุรุ", ["Dogr"] = "โฑครี", ["Dsrt"] = "Deseret", ["Dupl"] = "Duployan", ["Egyd"] = "Demotic", ["Egyh"] = "Hieratic", ["Egyp"] = "ไฮเออโรกลีฟอียิปต์", ["Elba"] = "เอลบาซาน", ["Elym"] = "Elymaic", ["Ethi"] = "เอธิโอเปีย", ["Gara"] = "Garay", ["Geok"] = "Khutsuri", ["Geor"] = "จอร์เจีย", ["Glag"] = "กลาโกลิติก", ["Gong"] = "คุญชลาโคณฑี", ["Gonm"] = "มสรามโคณฑี", ["Goth"] = "กอท", ["Gran"] = "ครันถะ", ["Grek"] = "กรีก", ["Gujr"] = "คุชราต", ["Gukh"] = "Khema", ["Guru"] = "คุรมุขี", ["Hang"] = "ฮันกึล", ["Hani"] = "จีน", ["Hano"] = "ฮานูโนโอ", ["Hans"] = "จีนตัวย่อ", ["Hant"] = "จีนตัวเต็ม", ["Hatr"] = "Hatran", ["Hebr"] = "ฮีบรู", ["Hira"] = "ฮิรางานะ", ["Hluw"] = "ไฮเออโรกลีฟอานาโตเลีย", ["Hmng"] = "Pahawh Hmong", ["Hmnp"] = "Nyiakeng Puachue Hmong", ["Hrkt"] = "คานะ", ["Hung"] = "ฮังการีเก่า", ["Ibrnn"] = "ไอบีเรียตะวันออกเฉียงเหนือ", ["Ibrns"] = "ไอบีเรียตะวันออกเฉียงใต้", ["Image"] = "Image-rendered", ["Inds"] = "สินธุ", ["Ipach"] = "สัทอักษรสากล", ["Ital"] = "อิตาลิกเก่า", ["Java"] = "ชวา", ["Jpan"] = "ญี่ปุ่น", ["Jurc"] = "จูร์เชน", ["Kali"] = "กะยา", ["Kana"] = "คาตากานะ", ["Kawi"] = "กวิ", ["Khar"] = "ขโรษฐี", ["Khmr"] = "เขมร", ["Khoj"] = "Khojki", ["Khomt"] = "ขอมไทย", ["Kitl"] = "คีตันใหญ่", ["Kits"] = "คีตันเล็ก", ["Knda"] = "กันนฑะ", ["Kore"] = "เกาหลี", ["Kpel"] = "Kpelle", ["Krai"] = "Kirat Rai", ["Kthi"] = "ไกถี", ["Kulit"] = "Kulitan", ["Lana"] = "ไทธรรม", ["Laoo"] = "ลาว", ["Latf"] = "ฟรักทูร์", ["Latg"] = "แกลิก", ["Latn"] = "ละติน", ["Leke"] = "Leke", ["Lepc"] = "เลปชา", ["Limb"] = "ลิมบู", ["Lina"] = "ลิเนียร์เอ", ["Linb"] = "ลิเนียร์บี", ["Lisu"] = "ลีสู่", ["Loma"] = "Loma", ["Lyci"] = "ลิเชีย", ["Lydi"] = "ลิเดีย", ["Mahj"] = "มหาชนี", ["Maka"] = "มากัซซาร์", ["Mand"] = "มันดาอิก", ["Mani"] = "Manichaean", ["Marc"] = "Marchen", ["Maya"] = "มายา", ["Medf"] = "Medefaidrin", ["Mend"] = "Mende", ["Merc"] = "Meroitic cursive", ["Mero"] = "Meroitic hieroglyphic", ["Mlym"] = "มลยาฬัม", ["Modi"] = "โมฑี", ["Mong"] = "มองโกเลีย", ["Moon"] = "Moon", ["Morse"] = "รหัสมอร์ส", ["Mroo"] = "มรู", ["Mtei"] = "มณีปุระ", ["Mult"] = "Multani", ["Music"] = "สัญกรณ์ดนตรี", ["Mymr"] = "พม่า", ["Nagm"] = "Mundari Bani", ["Nand"] = "นันทินาครี", ["Narb"] = "Ancient North Arabian", ["Nbat"] = "Nabataean", ["Newa"] = "Newa", ["Nkdb"] = "Dongba", ["Nkgb"] = "Geba", ["Nkoo"] = "N'Ko", ["None"] = "ไม่ระบุ", ["Nshu"] = "Nüshu", ["Ogam"] = "Ogham", ["Olck"] = "อ็อลจิกิ", ["Onao"] = "Ol Onal", ["Orkh"] = "Old Turkic", ["Orya"] = "โอริยา", ["Osge"] = "Osage", ["Osma"] = "อุสมาน", ["Ougr"] = "อุยกูร์เก่า", ["Palm"] = "Palmyrene", ["Pauc"] = "Pau Cin Hau", ["Pcun"] = "Proto-Cuneiform", ["Pelm"] = "Proto-Elamite", ["Perm"] = "Old Permic", ["Phag"] = "Phags-pa", ["Phli"] = "ปาห์ลาวีจารึก", ["Phlp"] = "ปาห์ลาวีเพลงสดุดี", ["Phlv"] = "ปาห์ลาวีคัมภีร์", ["Phnx"] = "ฟินิเชีย", ["Plrd"] = "Pollard", ["Polyt"] = "กรีก", ["Prti"] = "Inscriptional Parthian", ["Psin"] = "Proto-Sinaitic", ["Ranj"] = "Ranjana", ["Rjng"] = "Rejang", ["Rohg"] = "ฮานิฟ", ["Roro"] = "Rongorongo", ["Rumin"] = "Rumi numerals", ["Runr"] = "รูน", ["Samr"] = "Samaritan", ["Sarb"] = "Ancient South Arabian", ["Saur"] = "Saurashtra", ["Semap"] = "สัญญาณธง", ["Sgnw"] = "SignWriting", ["Shaw"] = "Shavian", ["Shrd"] = "ศารทา", ["Shui"] = "Sui", ["Sidd"] = "สิทธัม", ["Sidt"] = "Sidetic", ["Sind"] = "ขุทาพาที", ["Sinh"] = "สิงหล", ["Sogd"] = "ซอกเดีย", ["Sogo"] = "ซอกเดียเก่า", ["Sora"] = "Sorang Sompeng", ["Soyo"] = "Soyombo", ["Sund"] = "ซุนดา", ["Sunu"] = "Sunuwar", ["Sylo"] = "สิเลฏินาครี", ["Syrc"] = "ซีรีแอก", ["Tagb"] = "ตักบันวา", ["Takr"] = "ฏากรี", ["Tale"] = "ไทใต้คง", ["Talu"] = "ไทลื้อใหม่", ["Taml"] = "ทมิฬ", ["Tang"] = "ตังกุต", ["Tavt"] = "ไทเวียด", ["Tayo"] = "ลายตัย", ["Telu"] = "เตลูกู", ["Teng"] = "เทงกวาร์", ["Tfng"] = "ทิฟินาค", ["Tglg"] = "ไบบายิน", ["Thaa"] = "ทานะ", ["Thai"] = "ไทย", ["Tibt"] = "ทิเบต", ["Tirh"] = "ติรหุตา", ["Tnsa"] = "Tangsa", ["Todr"] = "Todhri", ["Tols"] = "Tolong Siki", ["Toto"] = "Toto", ["Tutg"] = "Tigalari", ["Ugar"] = "ยูการิต", ["Vaii"] = "ไว", ["Visp"] = "Visible Speech", ["Vith"] = "Vithkuqi", ["Wara"] = "วรังจิติ", ["Wcho"] = "Wancho", ["Wole"] = "Woleai", ["Xpeo"] = "เปอร์เซียเก่า", ["Xsux"] = "คูนิฟอร์ม", ["Yezi"] = "Yezidi", ["Yiii"] = "อี๋", ["Zanb"] = "Zanabazar Square", ["Zmth"] = "mathematical notation", ["Zname"] = "Znamenny musical notation", ["Zsym"] = "สัญลักษณ์", ["Zyyy"] = "ยังไม่กำหนด", ["Zzzz"] = "ยังไม่มีรหัส", ["as-Beng"] = "อัสสัม", ["fa-Arab"] = "อาหรับ", ["kk-Arab"] = "อาหรับ", ["ks-Arab"] = "อาหรับ", ["ku-Arab"] = "อาหรับ", ["mnc-Mong"] = "แมนจู", ["ms-Arab"] = "อาหรับ", ["mzn-Arab"] = "อาหรับ", ["ota-Arab"] = "อาหรับ", ["pa-Arab"] = "ศาหมุขี", ["pal-Avst"] = "Pazend", ["pjt-Latn"] = "ละติน", ["ps-Arab"] = "อาหรับ", ["sd-Arab"] = "อาหรับ", ["sit-tam-Tibt"] = "ตัมยิก", ["sjo-Mong"] = "ซิเบ", ["tt-Arab"] = "อาหรับ", ["ug-Arab"] = "อาหรับ", ["ur-Arab"] = "อาหรับ", ["xwo-Mong"] = "Clear Script", } qogc0gqs5uazqgopfquw3amsp1gasr3 5775292 5775262 2026-10-14T10:20:44Z Octahedron80 267 5775292 Scribunto text/plain return { ["Adlm"] = "อัดลัม", ["Afak"] = "อาฟากา", ["Aghb"] = "Caucasian Albanian", ["Ahom"] = "อาหม", ["Arab"] = "อาหรับ", ["Armi"] = "แอราเมอิกจักรวรรดิ", ["Armn"] = "อาร์มีเนีย", ["Avst"] = "อเวสตะ", ["Bali"] = "บาหลี", ["Bamu"] = "บามุม", ["Bass"] = "บัสซา", ["Batk"] = "บาตัก", ["Beng"] = "เบงกอล", ["Bhks"] = "ไภกษุกี", ["Blis"] = "Blissymbolic", ["Bopo"] = "จู้อิน", ["Brah"] = "พราหมี", ["Brai"] = "เบรลล์", ["Bugi"] = "ลนตารา", ["Buhd"] = "บูฮิด", ["Cakm"] = "จักมา", ["Cans"] = "Canadian syllabic", ["Cari"] = "คาเรีย", ["Cham"] = "จาม", ["Cher"] = "เชโรกี", ["Chis"] = "Chisoi", ["Chrs"] = "Khwarezmian", ["Copt"] = "คอปติก", ["Cpmn"] = "Cypro-Minoan", ["Cprt"] = "ไซปรัส", ["Cyrl"] = "ซีริลลิก", ["Cyrs"] = "ซีริลลิกเก่า", ["Deva"] = "เทวนาครี", ["Diak"] = "ดิเวส อกุรุ", ["Dogr"] = "โฑครี", ["Dsrt"] = "Deseret", ["Dupl"] = "Duployan", ["Egyd"] = "Demotic", ["Egyh"] = "Hieratic", ["Egyp"] = "ไฮเออโรกลีฟอียิปต์", ["Elba"] = "เอลบาซาน", ["Elym"] = "Elymaic", ["Ethi"] = "เอธิโอเปีย", ["Gara"] = "Garay", ["Geok"] = "Khutsuri", ["Geor"] = "จอร์เจีย", ["Glag"] = "กลาโกลิติก", ["Gong"] = "คุญชลาโคณฑี", ["Gonm"] = "มสรามโคณฑี", ["Goth"] = "กอท", ["Gran"] = "ครันถะ", ["Grek"] = "กรีก", ["Gujr"] = "คุชราต", ["Gukh"] = "Khema", ["Guru"] = "คุรมุขี", ["Hang"] = "ฮันกึล", ["Hani"] = "จีน", ["Hano"] = "ฮานูโนโอ", ["Hans"] = "จีนตัวย่อ", ["Hant"] = "จีนตัวเต็ม", ["Hatr"] = "Hatran", ["Hebr"] = "ฮีบรู", ["Hira"] = "ฮิรางานะ", ["Hluw"] = "ไฮเออโรกลีฟอานาโตเลีย", ["Hmng"] = "Pahawh Hmong", ["Hmnp"] = "Nyiakeng Puachue Hmong", ["Hrkt"] = "คานะ", ["Hung"] = "ฮังการีเก่า", ["Ibrnn"] = "ไอบีเรียตะวันออกเฉียงเหนือ", ["Ibrns"] = "ไอบีเรียตะวันออกเฉียงใต้", ["Image"] = "Image-rendered", ["Inds"] = "สินธุ", ["Ipach"] = "สัทอักษรสากล", ["Ital"] = "อิตาลิกเก่า", ["Java"] = "ชวา", ["Jpan"] = "ญี่ปุ่น", ["Jurc"] = "จูร์เชน", ["Kali"] = "กะยา", ["Kana"] = "คาตากานะ", ["Kawi"] = "กวิ", ["Khar"] = "ขโรษฐี", ["Khmr"] = "เขมร", ["Khoj"] = "Khojki", ["Khomt"] = "ขอมไทย", ["Kitl"] = "คีตันใหญ่", ["Kits"] = "คีตันเล็ก", ["Knda"] = "กันนฑะ", ["Kore"] = "เกาหลี", ["Kpel"] = "Kpelle", ["Krai"] = "Kirat Rai", ["Kthi"] = "ไกถี", ["Kulit"] = "Kulitan", ["Lana"] = "ไทธรรม", ["Laoo"] = "ลาว", ["Latf"] = "ฟรักทูร์", ["Latg"] = "แกลิก", ["Latn"] = "ละติน", ["Leke"] = "Leke", ["Lepc"] = "เลปชา", ["Limb"] = "ลิมบู", ["Lina"] = "ลิเนียร์เอ", ["Linb"] = "ลิเนียร์บี", ["Lisu"] = "ลีสู่", ["Loma"] = "Loma", ["Lyci"] = "ลิเชีย", ["Lydi"] = "ลิเดีย", ["Mahj"] = "มหาชนี", ["Maka"] = "มากัซซาร์", ["Mand"] = "มันดาอิก", ["Mani"] = "Manichaean", ["Marc"] = "Marchen", ["Maya"] = "มายา", ["Medf"] = "Medefaidrin", ["Mend"] = "Mende", ["Merc"] = "Meroitic cursive", ["Mero"] = "Meroitic hieroglyphic", ["Mlym"] = "มลยาฬัม", ["Modi"] = "โมฑี", ["Mong"] = "มองโกเลีย", ["Moon"] = "Moon", ["Morse"] = "รหัสมอร์ส", ["Mroo"] = "มรู", ["Mtei"] = "มณีปุระ", ["Mult"] = "Multani", ["Music"] = "สัญกรณ์ดนตรี", ["Mymr"] = "พม่า", ["Nagm"] = "Mundari Bani", ["Nand"] = "นันทินาครี", ["Narb"] = "Ancient North Arabian", ["Nbat"] = "Nabataean", ["Newa"] = "Newa", ["Nkdb"] = "Dongba", ["Nkgb"] = "Geba", ["Nkoo"] = "N'Ko", ["None"] = "ไม่ระบุ", ["Nshu"] = "Nüshu", ["Ogam"] = "Ogham", ["Olck"] = "อ็อลจิกิ", ["Onao"] = "Ol Onal", ["Orkh"] = "Old Turkic", ["Orya"] = "โอริยา", ["Osge"] = "Osage", ["Osma"] = "อุสมาน", ["Ougr"] = "อุยกูร์เก่า", ["Palm"] = "Palmyrene", ["Pauc"] = "Pau Cin Hau", ["Pcun"] = "Proto-Cuneiform", ["Pelm"] = "Proto-Elamite", ["Perm"] = "Old Permic", ["Phag"] = "Phags-pa", ["Phli"] = "ปาห์ลาวีจารึก", ["Phlp"] = "ปาห์ลาวีเพลงสดุดี", ["Phlv"] = "ปาห์ลาวีคัมภีร์", ["Phnx"] = "ฟินิเชีย", ["Plrd"] = "Pollard", ["Polyt"] = "กรีก", ["Prti"] = "พาร์เทียจารึก", ["Psin"] = "Proto-Sinaitic", ["Ranj"] = "Ranjana", ["Rjng"] = "Rejang", ["Rohg"] = "ฮานิฟ", ["Roro"] = "Rongorongo", ["Rumin"] = "Rumi numerals", ["Runr"] = "รูน", ["Samr"] = "Samaritan", ["Sarb"] = "Ancient South Arabian", ["Saur"] = "Saurashtra", ["Semap"] = "สัญญาณธง", ["Sgnw"] = "SignWriting", ["Shaw"] = "Shavian", ["Shrd"] = "ศารทา", ["Shui"] = "Sui", ["Sidd"] = "สิทธัม", ["Sidt"] = "Sidetic", ["Sind"] = "ขุทาพาที", ["Sinh"] = "สิงหล", ["Sogd"] = "ซอกเดีย", ["Sogo"] = "ซอกเดียเก่า", ["Sora"] = "Sorang Sompeng", ["Soyo"] = "Soyombo", ["Sund"] = "ซุนดา", ["Sunu"] = "Sunuwar", ["Sylo"] = "สิเลฏินาครี", ["Syrc"] = "ซีรีแอก", ["Tagb"] = "ตักบันวา", ["Takr"] = "ฏากรี", ["Tale"] = "ไทใต้คง", ["Talu"] = "ไทลื้อใหม่", ["Taml"] = "ทมิฬ", ["Tang"] = "ตังกุต", ["Tavt"] = "ไทเวียด", ["Tayo"] = "ลายตัย", ["Telu"] = "เตลูกู", ["Teng"] = "เทงกวาร์", ["Tfng"] = "ทิฟินาค", ["Tglg"] = "ไบบายิน", ["Thaa"] = "ทานะ", ["Thai"] = "ไทย", ["Tibt"] = "ทิเบต", ["Tirh"] = "ติรหุตา", ["Tnsa"] = "Tangsa", ["Todr"] = "Todhri", ["Tols"] = "Tolong Siki", ["Toto"] = "Toto", ["Tutg"] = "Tigalari", ["Ugar"] = "ยูการิต", ["Vaii"] = "ไว", ["Visp"] = "Visible Speech", ["Vith"] = "Vithkuqi", ["Wara"] = "วรังจิติ", ["Wcho"] = "Wancho", ["Wole"] = "Woleai", ["Xpeo"] = "เปอร์เซียเก่า", ["Xsux"] = "คูนิฟอร์ม", ["Yezi"] = "Yezidi", ["Yiii"] = "อี๋", ["Zanb"] = "Zanabazar Square", ["Zmth"] = "mathematical notation", ["Zname"] = "Znamenny musical notation", ["Zsym"] = "สัญลักษณ์", ["Zyyy"] = "ยังไม่กำหนด", ["Zzzz"] = "ยังไม่มีรหัส", ["as-Beng"] = "อัสสัม", ["fa-Arab"] = "อาหรับ", ["kk-Arab"] = "อาหรับ", ["ks-Arab"] = "อาหรับ", ["ku-Arab"] = "อาหรับ", ["mnc-Mong"] = "แมนจู", ["ms-Arab"] = "อาหรับ", ["mzn-Arab"] = "อาหรับ", ["ota-Arab"] = "อาหรับ", ["pa-Arab"] = "ศาหมุขี", ["pal-Avst"] = "Pazend", ["pjt-Latn"] = "ละติน", ["ps-Arab"] = "อาหรับ", ["sd-Arab"] = "อาหรับ", ["sit-tam-Tibt"] = "ตัมยิก", ["sjo-Mong"] = "ซิเบ", ["tt-Arab"] = "อาหรับ", ["ug-Arab"] = "อาหรับ", ["ur-Arab"] = "อาหรับ", ["xwo-Mong"] = "Clear Script", } iroakltgl1mercfljqgnxkbz5x30laf 5775297 5775292 2026-10-14T10:31:59Z Octahedron80 267 5775297 Scribunto text/plain return { ["Adlm"] = "อัดลัม", ["Afak"] = "อาฟากา", ["Aghb"] = "Caucasian Albanian", ["Ahom"] = "อาหม", ["Arab"] = "อาหรับ", ["Armi"] = "แอราเมอิกจักรวรรดิ", ["Armn"] = "อาร์มีเนีย", ["Avst"] = "อเวสตะ", ["Bali"] = "บาหลี", ["Bamu"] = "บามุม", ["Bass"] = "บัสซา", ["Batk"] = "บาตัก", ["Beng"] = "เบงกอล", ["Bhks"] = "ไภกษุกี", ["Blis"] = "Blissymbolic", ["Bopo"] = "จู้อิน", ["Brah"] = "พราหมี", ["Brai"] = "เบรลล์", ["Bugi"] = "ลนตารา", ["Buhd"] = "บูฮิด", ["Cakm"] = "จักมา", ["Cans"] = "Canadian syllabic", ["Cari"] = "คาเรีย", ["Cham"] = "จาม", ["Cher"] = "เชโรกี", ["Chis"] = "Chisoi", ["Chrs"] = "ฆวารัซม์", ["Copt"] = "คอปติก", ["Cpmn"] = "Cypro-Minoan", ["Cprt"] = "ไซปรัส", ["Cyrl"] = "ซีริลลิก", ["Cyrs"] = "ซีริลลิกเก่า", ["Deva"] = "เทวนาครี", ["Diak"] = "ดิเวส อกุรุ", ["Dogr"] = "โฑครี", ["Dsrt"] = "Deseret", ["Dupl"] = "Duployan", ["Egyd"] = "Demotic", ["Egyh"] = "Hieratic", ["Egyp"] = "ไฮเออโรกลีฟอียิปต์", ["Elba"] = "เอลบาซาน", ["Elym"] = "Elymaic", ["Ethi"] = "เอธิโอเปีย", ["Gara"] = "Garay", ["Geok"] = "Khutsuri", ["Geor"] = "จอร์เจีย", ["Glag"] = "กลาโกลิติก", ["Gong"] = "คุญชลาโคณฑี", ["Gonm"] = "มสรามโคณฑี", ["Goth"] = "กอท", ["Gran"] = "ครันถะ", ["Grek"] = "กรีก", ["Gujr"] = "คุชราต", ["Gukh"] = "Khema", ["Guru"] = "คุรมุขี", ["Hang"] = "ฮันกึล", ["Hani"] = "จีน", ["Hano"] = "ฮานูโนโอ", ["Hans"] = "จีนตัวย่อ", ["Hant"] = "จีนตัวเต็ม", ["Hatr"] = "Hatran", ["Hebr"] = "ฮีบรู", ["Hira"] = "ฮิรางานะ", ["Hluw"] = "ไฮเออโรกลีฟอานาโตเลีย", ["Hmng"] = "Pahawh Hmong", ["Hmnp"] = "Nyiakeng Puachue Hmong", ["Hrkt"] = "คานะ", ["Hung"] = "ฮังการีเก่า", ["Ibrnn"] = "ไอบีเรียตะวันออกเฉียงเหนือ", ["Ibrns"] = "ไอบีเรียตะวันออกเฉียงใต้", ["Image"] = "Image-rendered", ["Inds"] = "สินธุ", ["Ipach"] = "สัทอักษรสากล", ["Ital"] = "อิตาลิกเก่า", ["Java"] = "ชวา", ["Jpan"] = "ญี่ปุ่น", ["Jurc"] = "จูร์เชน", ["Kali"] = "กะยา", ["Kana"] = "คาตากานะ", ["Kawi"] = "กวิ", ["Khar"] = "ขโรษฐี", ["Khmr"] = "เขมร", ["Khoj"] = "Khojki", ["Khomt"] = "ขอมไทย", ["Kitl"] = "คีตันใหญ่", ["Kits"] = "คีตันเล็ก", ["Knda"] = "กันนฑะ", ["Kore"] = "เกาหลี", ["Kpel"] = "Kpelle", ["Krai"] = "Kirat Rai", ["Kthi"] = "ไกถี", ["Kulit"] = "Kulitan", ["Lana"] = "ไทธรรม", ["Laoo"] = "ลาว", ["Latf"] = "ฟรักทูร์", ["Latg"] = "แกลิก", ["Latn"] = "ละติน", ["Leke"] = "Leke", ["Lepc"] = "เลปชา", ["Limb"] = "ลิมบู", ["Lina"] = "ลิเนียร์เอ", ["Linb"] = "ลิเนียร์บี", ["Lisu"] = "ลีสู่", ["Loma"] = "Loma", ["Lyci"] = "ลิเชีย", ["Lydi"] = "ลิเดีย", ["Mahj"] = "มหาชนี", ["Maka"] = "มากัซซาร์", ["Mand"] = "มันดาอิก", ["Mani"] = "Manichaean", ["Marc"] = "Marchen", ["Maya"] = "มายา", ["Medf"] = "Medefaidrin", ["Mend"] = "Mende", ["Merc"] = "Meroitic cursive", ["Mero"] = "Meroitic hieroglyphic", ["Mlym"] = "มลยาฬัม", ["Modi"] = "โมฑี", ["Mong"] = "มองโกเลีย", ["Moon"] = "Moon", ["Morse"] = "รหัสมอร์ส", ["Mroo"] = "มรู", ["Mtei"] = "มณีปุระ", ["Mult"] = "Multani", ["Music"] = "สัญกรณ์ดนตรี", ["Mymr"] = "พม่า", ["Nagm"] = "Mundari Bani", ["Nand"] = "นันทินาครี", ["Narb"] = "Ancient North Arabian", ["Nbat"] = "Nabataean", ["Newa"] = "Newa", ["Nkdb"] = "Dongba", ["Nkgb"] = "Geba", ["Nkoo"] = "N'Ko", ["None"] = "ไม่ระบุ", ["Nshu"] = "Nüshu", ["Ogam"] = "Ogham", ["Olck"] = "อ็อลจิกิ", ["Onao"] = "Ol Onal", ["Orkh"] = "Old Turkic", ["Orya"] = "โอริยา", ["Osge"] = "Osage", ["Osma"] = "อุสมาน", ["Ougr"] = "อุยกูร์เก่า", ["Palm"] = "Palmyrene", ["Pauc"] = "Pau Cin Hau", ["Pcun"] = "Proto-Cuneiform", ["Pelm"] = "Proto-Elamite", ["Perm"] = "Old Permic", ["Phag"] = "Phags-pa", ["Phli"] = "ปาห์ลาวีจารึก", ["Phlp"] = "ปาห์ลาวีเพลงสดุดี", ["Phlv"] = "ปาห์ลาวีคัมภีร์", ["Phnx"] = "ฟินิเชีย", ["Plrd"] = "Pollard", ["Polyt"] = "กรีก", ["Prti"] = "พาร์เทียจารึก", ["Psin"] = "Proto-Sinaitic", ["Ranj"] = "Ranjana", ["Rjng"] = "Rejang", ["Rohg"] = "ฮานิฟ", ["Roro"] = "Rongorongo", ["Rumin"] = "Rumi numerals", ["Runr"] = "รูน", ["Samr"] = "Samaritan", ["Sarb"] = "Ancient South Arabian", ["Saur"] = "Saurashtra", ["Semap"] = "สัญญาณธง", ["Sgnw"] = "SignWriting", ["Shaw"] = "Shavian", ["Shrd"] = "ศารทา", ["Shui"] = "Sui", ["Sidd"] = "สิทธัม", ["Sidt"] = "Sidetic", ["Sind"] = "ขุทาพาที", ["Sinh"] = "สิงหล", ["Sogd"] = "ซอกเดีย", ["Sogo"] = "ซอกเดียเก่า", ["Sora"] = "Sorang Sompeng", ["Soyo"] = "Soyombo", ["Sund"] = "ซุนดา", ["Sunu"] = "Sunuwar", ["Sylo"] = "สิเลฏินาครี", ["Syrc"] = "ซีรีแอก", ["Tagb"] = "ตักบันวา", ["Takr"] = "ฏากรี", ["Tale"] = "ไทใต้คง", ["Talu"] = "ไทลื้อใหม่", ["Taml"] = "ทมิฬ", ["Tang"] = "ตังกุต", ["Tavt"] = "ไทเวียด", ["Tayo"] = "ลายตัย", ["Telu"] = "เตลูกู", ["Teng"] = "เทงกวาร์", ["Tfng"] = "ทิฟินาค", ["Tglg"] = "ไบบายิน", ["Thaa"] = "ทานะ", ["Thai"] = "ไทย", ["Tibt"] = "ทิเบต", ["Tirh"] = "ติรหุตา", ["Tnsa"] = "Tangsa", ["Todr"] = "Todhri", ["Tols"] = "Tolong Siki", ["Toto"] = "Toto", ["Tutg"] = "Tigalari", ["Ugar"] = "ยูการิต", ["Vaii"] = "ไว", ["Visp"] = "Visible Speech", ["Vith"] = "Vithkuqi", ["Wara"] = "วรังจิติ", ["Wcho"] = "Wancho", ["Wole"] = "Woleai", ["Xpeo"] = "เปอร์เซียเก่า", ["Xsux"] = "คูนิฟอร์ม", ["Yezi"] = "Yezidi", ["Yiii"] = "อี๋", ["Zanb"] = "Zanabazar Square", ["Zmth"] = "mathematical notation", ["Zname"] = "Znamenny musical notation", ["Zsym"] = "สัญลักษณ์", ["Zyyy"] = "ยังไม่กำหนด", ["Zzzz"] = "ยังไม่มีรหัส", ["as-Beng"] = "อัสสัม", ["fa-Arab"] = "อาหรับ", ["kk-Arab"] = "อาหรับ", ["ks-Arab"] = "อาหรับ", ["ku-Arab"] = "อาหรับ", ["mnc-Mong"] = "แมนจู", ["ms-Arab"] = "อาหรับ", ["mzn-Arab"] = "อาหรับ", ["ota-Arab"] = "อาหรับ", ["pa-Arab"] = "ศาหมุขี", ["pal-Avst"] = "Pazend", ["pjt-Latn"] = "ละติน", ["ps-Arab"] = "อาหรับ", ["sd-Arab"] = "อาหรับ", ["sit-tam-Tibt"] = "ตัมยิก", ["sjo-Mong"] = "ซิเบ", ["tt-Arab"] = "อาหรับ", ["ug-Arab"] = "อาหรับ", ["ur-Arab"] = "อาหรับ", ["xwo-Mong"] = "Clear Script", } bpmgz5wx6lqd2f9iobg74ddnx01xfow มอดูล:de-noun 828 238602 5775177 4615568 2026-10-14T03:04:26Z Octahedron80 267 5775177 Scribunto text/plain local export = {} --[=[ Authorship: <benwing2> ]=] --[=[ TERMINOLOGY: -- "slot" = A particular combination of case/number. Example slot names for nouns are "voc_s" (vocative singular) and "gen_p" (genitive plural). Each slot is filled with zero or more forms. -- "form" = The declined German form representing the value of a given slot. -- "lemma" = The dictionary form of a given German term. Generally the nominative masculine singular, but may occasionally be another form if the nominative masculine singular is missing. ]=] --[=[ FIXME: 1. Qualifiers in genders should appear as footnotes on the articles. 2. Support notation like <g:f> on feminine/diminutive/masculine, e.g. used for [[Gespons]] (neuter with the meaning "wife", masculine with the meaning "husband"). 3. Fix CSS gender-specific class in table. 4. Support adjectival nouns and adjective-noun combinations. (DONE) 5. Allow period and comma in forms e.g. for [[Eigent.-Whg.]], [[Eigt.-Whg.]] (using a backslash). (DONE) 6. Allow embedded links in genitive/plural/feminine/diminutive/masculine specs, e.g. 'f=![[weiblich]]er Geschäftspartner'. 7. Add 'prop' indicator to indicate proper nouns and suppress the indefinite article. 8. Add 'surname' indicator to indicate surnames, decline appropriately and include both masc and fem variants in the table. (DONE) 9. Add 'langname' indicator to indicate langnames and decline appropriately with its own table with two alternatives. (DONE) ]=] local M = require("Module:module loader").init { require = { com = "Module:de-common", de_adjective = "Module:de-adjective", iut = "Module:inflection utilities", inflection_table = "Module:inflection-table", JSON = "Module:JSON", debug = "Module:debug", links = "Module:links", parameters = "Module:parameters", put = "Module:parse utilities", string_utilities = "Module:string utilities", table = "Module:table", utilities = "Module:utilities", }, loadData = { headword_data = "Module:headword/data", }, lazy = { lang = function() return require("Module:languages").getByCode("de") end, }, } local pretend_from_headword = false -- may be set during debugging local force_cat = false -- may be set during debugging local u = M.string_utilities.char local rfind = M.string_utilities.find local rmatch = M.string_utilities.match local rsubn = M.string_utilities.gsub local unpack = unpack or table.unpack -- Lua 5.2 compatibility local usub = M.string_utilities.sub local SUB_ESCAPED_PERIOD = u(0xFFF0) local SUB_ESCAPED_COMMA = u(0xFFF1) local archaic_dative_note = "[ปัจจุบันพบได้ยาก, [[Wiktionary:German entry guidelines#Dative_singular_-e_in_noun_declension|ดูหมายเหตุ]]]" -- version of rsubn() that discards all but the first return value local function rsub(term, foo, bar) local retval = rsubn(term, foo, bar) return retval end local function track(page) M.debug.track("de-noun/" .. page) return true end local states = { "str", "wk", "mix" } local definitenesses = { "ind", "def" } local cases_with_abl_voc = { "nom", "gen", "dat", "acc", "abl", "voc" } local basic_cases = { "nom", "gen", "dat", "acc" } local numbers = { "s", "p" } local gender_spec_to_full_gender = { m = "เพศชาย", f = "เพศหญิง", n = "เพศกลาง", } local case_set_with_abl_voc = M.table.listToSet(cases_with_abl_voc) local function add_equiv(slot_list) table.insert(slot_list, {"m_equiv", "-"}) -- masculine equivalent of a feminine or neuter noun table.insert(slot_list, {"f_equiv", "-"}) -- feminine equivalent of a masculine or neuter noun table.insert(slot_list, {"n_equiv", "-"}) -- neuter equivalent of a masculine or feminine noun end -- Construct noun slots. local noun_slot_list = {} add_equiv(noun_slot_list) local noun_slot_set = {} for _, number in ipairs(numbers) do for _, case in ipairs(number == "s" and cases_with_abl_voc or basic_cases) do local slot = case .. "_" .. number local accel = case .. "|" .. number table.insert(noun_slot_list, {slot, accel}) noun_slot_set[slot] = true end end -- Construct noun surname slots. local surname_slot_list = { } local surname_slot_set = {} local surname_endings = { {"m_s", "m|s"}, {"f_s", "f|s"}, {"p", "p"}, } for _, case in ipairs(basic_cases) do for _, ending_and_accel in ipairs(surname_endings) do local ending, ending_accel = unpack(ending_and_accel) local slot = case .. "_" .. ending local accel = case .. "|" .. ending_accel table.insert(surname_slot_list, {slot, accel}) surname_slot_set[slot] = true end end -- Construct noun langname slots. local langname_slot_list = { } local langname_slot_set = {} for _, case in ipairs(basic_cases) do for _, number in ipairs(numbers) do for _, is_alt in ipairs { false, true } do local slot = case .. "_" .. number .. (is_alt and "_alt" or "") -- FIXME: We should add accelerators for the alternative forms, but this requires hacking the accelerator -- code in [[Module:inflection utilities]] to specify the alternative lemma; e.g. genitive singular -- ''Deutschen'' needs to have lemma [[Deutsche]] not [[Deutsch]]. local accel = is_alt and "-" or case .. "|" .. number table.insert(langname_slot_list, {slot, accel}) langname_slot_set[slot] = true end end end -- Construct adjectival slots. local adjectival_slot_list = {} add_equiv(adjectival_slot_list) local adjectival_slot_set = {} for _, state in ipairs(states) do for _, case in ipairs(basic_cases) do for _, number in ipairs(numbers) do local slot = state .. "_" .. case .. "_" .. number local accel = state .. "|" .. case .. "|" .. number table.insert(adjectival_slot_list, {slot, accel}) adjectival_slot_set[slot] = true end end end -- Construct expanded slot lists including linked variants. local noun_slot_list_with_linked = M.table.shallowCopy(noun_slot_list) table.insert(noun_slot_list_with_linked, {"nom_s_linked", "nom|s"}) table.insert(noun_slot_list_with_linked, {"nom_p_linked", "nom|p"}) local surname_slot_list_with_linked = M.table.shallowCopy(surname_slot_list) table.insert(surname_slot_list_with_linked, {"nom_m_s_linked", "nom|m|s"}) local langname_slot_list_with_linked = M.table.shallowCopy(langname_slot_list) table.insert(langname_slot_list_with_linked, {"nom_s_linked", "nom|s"}) local adjectival_slot_list_with_linked = M.table.shallowCopy(adjectival_slot_list) table.insert(adjectival_slot_list_with_linked, {"str_nom_s_linked", "str|nom|s"}) table.insert(adjectival_slot_list_with_linked, {"str_nom_p_linked", "str|nom|p"}) -- Construct expanded slot lists including linked variants and articles. local function add_slot_articles(slot_list, cases, numbers) for _, case in ipairs(cases) do for _, number in ipairs(numbers) do for _, def in ipairs(definitenesses) do local slotaccel = {"art_" .. def .. "_" .. case .. "_" .. number, "-"} table.insert(slot_list, slotaccel) end end end end local noun_slot_list_with_linked_and_articles = M.table.shallowCopy(noun_slot_list_with_linked) add_slot_articles(noun_slot_list_with_linked_and_articles, cases_with_abl_voc, numbers) local surname_slot_list_with_linked_and_articles = M.table.shallowCopy(surname_slot_list_with_linked) add_slot_articles(surname_slot_list_with_linked_and_articles, basic_cases, {"m_s", "f_s", "p"}) local langname_slot_list_with_linked_and_articles = M.table.shallowCopy(langname_slot_list_with_linked) add_slot_articles(langname_slot_list_with_linked_and_articles, basic_cases, {"s"}) local adjectival_slot_list_with_linked_and_articles = M.table.shallowCopy(adjectival_slot_list_with_linked) add_slot_articles(adjectival_slot_list_with_linked_and_articles, basic_cases, numbers) -- Return true if `prop` is a recognized indicator that can be specified on adjectives in [[Module:de-adjective]]. local function is_adjectival_decl_indicator(prop) return prop == "ss" or prop == "sync_n" or prop == "sync_mn" or prop == "sync_mns" end local function skip_slot(number, slot) return number == "sg" and rfind(slot, "_p$") or number == "pl" and rfind(slot, "_s$") end local function combine_stem_ending(props, stem, ending) if ending:find("^%^") then -- Umlaut requested ending = rsub(ending, "^%^", "") stem = M.com.apply_umlaut(stem) end if props.ss and stem:find("ß$") and rfind(ending, "^" .. M.com.V) then stem = rsub(stem, "ß$", "ss") end return stem .. ending end -- Add a form (a combination of `stem` and `ending`, where either may be a single string, a list of strings, or a -- list of objects of the form {form=FORM, footnotes=FOOTNOTES}, where FOOTNOTES can be nil or a list of strings) -- to the given slot `slot`. `gender` specifies the gender of the resulting form ("m", "f" or "n") or nil. (This is -- used to ensure that the correct article is attached to the form when there are multiple forms with differing -- genders. If `gender` is nil, articles of all relevant genders will be included. `gender` should only be nil -- when the slot is plural or when the gender cannot be determined, e.g. in overrides.) `footnotes` specifies -- any extra footnotes to add to the resulting form, and should be either nil or a list of strings. -- `process_combined_stem_ending` is a function to process the resulting form before it is inserted. (This is used -- currently to add an -n to the dative plural.) local function add(base, slot, stem, ending, gender, footnotes, process_combined_stem_ending) if not ending or skip_slot(base.number, slot) then return end local function do_combine_stem_ending(stem, ending) local retval = combine_stem_ending(base.props, stem, ending) if process_combined_stem_ending then retval = process_combined_stem_ending(retval) end -- For now, don't do this. -- If gender specified, add a special character to the beginning of the value to indicate the -- gender. This gets propagated to the end and used in [[Module:de-headword]]. -- if gender then -- retval = gender_to_gender_char[gender] .. retval -- end return retval end footnotes = M.iut.combine_footnotes(base.footnotes, footnotes) local ending_obj = M.iut.combine_form_and_footnotes(ending, footnotes) -- If we're declining an adjectival noun or adjective-noun combination, and the slot is a noun slot, convert it to -- the equivalent adjective slots (e.g. gen_s -> str_gen_s/wk_gen_s/mix_gen_s). But don't do that for "m_equiv", -- "f_equiv", "n_equiv", which are the same in nouns and adjectives. if base.props.overall_adj and noun_slot_set[slot] and not rfind(slot, "equiv$") then for _, state in ipairs(states) do M.iut.add_forms(base.forms, state .. "_" .. slot, stem or base.lemma, ending_obj, do_combine_stem_ending) end else M.iut.add_forms(base.forms, slot, stem or base.lemma, ending_obj, do_combine_stem_ending) end end -- Process an ending spec such as "s", "(e)s", "^er", "^lein", "!Pizzen", etc. as might be found in the genitive, -- plural, an override, the value of dim=/m=/f=/n=, etc. `endings` is a list of such specs, where each entry of the -- list is of the form {form=FORM, footnotes=FOOTNOTES} where FOOTNOTES is either nil or {FOOTNOTE, FOOTNOTE, ...}. If -- `literal_endings` is given, the FORM values should be interpreted literally (i.e. as full forms) rather than as -- ending specs. `default` is what to substitute if an ending spec is "+", and should be either in the same format as -- `endings` or something that can be converted to that format, e.g. a string. `literal_default`, if given, indicates -- that the FORM values in `default` should be interpreted literally, similar to `literal_endings`. `desc` is an -- English description of what kind of spec is being processed, for error messages. `process` is called for each -- generated form and is a function of two arguments, STEM and ENDING. If the spec is a full form, STEM will be that -- form (in the form of an object {form=FORM, footnotes=FOOTNOTES}) and ENDING will be an empty string; otherwise, STEM -- will be nil and ENDING will be the the ending to process in the form {form=FORM, footnotes=FOOTNOTES}. Note that -- umlauts are not handled in process_spec(); if the spec passed in specifies an umlaut, e.g. "^chen", process() -- will be called with a FORM beginning with "^", and must handle the umlaut itself. (Umlauts are properly handled -- inside of add().) local function process_spec(endings, literal_endings, default, literal_default, desc, process) for _, ending in ipairs(endings) do local function sub_form(form) return {form = form, footnotes = ending.footnotes} end if ending.form == "--" then -- do nothing elseif ending.form == "+" then if not default then -- Could happen if e.g. gen is given as -- and then a gen_s override with + is specified, or with n= for neuter, -- where no default is available. error("Form '+' found for " .. desc .. " but no default is available") end process_spec(M.iut.convert_to_general_list_form(default, ending.footnotes), literal_default, nil, nil, desc, process) else local full_eform if literal_endings or rfind(ending.form, "^" .. M.com.CAP) then full_eform = true elseif rfind(ending.form, "^!") then full_eform = true ending = sub_form(rsub(ending.form, "^!", "")) end if full_eform then process(ending, "") else local expanded_endings local umlaut = rmatch(ending.form, "^(%^?)%(e%)s$" ) if umlaut then expanded_endings = {"es", "s"} end if not umlaut then umlaut = rmatch(ending.form, "^(%^?)%(s%)$") if umlaut then expanded_endings = {"s", ""} end end if not umlaut then umlaut = rmatch(ending.form, "^(%^?)%(es%)$") if umlaut then expanded_endings = {"es", ""} end end if expanded_endings then local new_endings = {} for _, expanded_ending in ipairs(expanded_endings) do table.insert(new_endings, sub_form(umlaut .. expanded_ending)) end process(nil, new_endings) else if ending.form == "-" then ending = sub_form("") end process(nil, ending) end end end end end -- Add an ending spec such as "s", "(e)s", "^er", "^lein", "!Pizzen", etc. as might be found in the genitive, plural, -- an override, the value of dim=/m=/f=/n=, etc., to the slot `slot` (e.g. "gen_s"). `endings` is a list of such specs, -- where each entry of the list is of the form {form=FORM, footnotes=FOOTNOTES} where FOOTNOTES is either nil or -- {FOOTNOTE, FOOTNOTE, ...}. For the meaning of `gender`, `footnotes` and `process_combined_stem_ending`, see add(). -- For the meaning of `default` and `literal_default`, see process_spec(). local function add_spec(base, slot, endings, gender, default, literal_default, footnotes, process_combined_stem_ending) local function do_add(stem, ending) add(base, slot, stem, ending, gender, footnotes, process_combined_stem_ending) end process_spec(endings, nil, default, literal_default, "slot '" .. slot .. "'", do_add) end local function process_slot_overrides(base) for slot, overrides in pairs(base.overrides) do if skip_slot(base.number, slot) then error("Override specified for invalid slot '" .. slot .. "' due to '" .. base.number .. "' number restriction") end local origforms = base.forms[slot] base.forms[slot] = nil -- Gender is not given by the user. add_spec(base, slot, overrides, nil, origforms, "literal default") end end local function add_archaic_dative_singular(base, gender, def_gen) for _, ending in ipairs(base.gens) do local dat_ending local ending_form = ending.form if ending_form == "+" then ending_form = def_gen end if ending_form == "es" or ending_form == "(e)s" then dat_ending = "e" elseif ending_form == "ses" then dat_ending = "se" elseif base.props.dat_with_e then dat_ending = "e" end if dat_ending then add(base, "dat_s", nil, dat_ending, gender, M.iut.combine_footnotes(ending.footnotes, {archaic_dative_note})) end end end local function get_n_ending(base, stem, is_sg) if rfind(stem, "e$") then -- typical feminine or weak masculine in -e return "n" elseif rfind(stem, "e[lr]$") and not rfind(stem, M.com.NV .. "[ei]e[lr]$") then -- [[Kammer]], [[Feier]], [[Leier]], but not [[Spur]], [[Beer]], [[Manier]], [[Schmier]] or [[Vier]] -- similarly, [[Achsel]], [[Gabel]], [[Tafel]], etc. but not [[Ziel]] return "n" elseif base.props.weak_n then -- ''des Nachbarn'', ''des Herrn'', ''des Satyrn'', etc. return "n" elseif rfind(stem, "[^aeAE]in$") then -- [[Chinesin]], [[Doktorin]], etc.; but not words in -ein or -ain such as [[Pein]] return "nen" else return "en" end end local function get_default_gen(base, gender) if gender == "f" then return "" elseif base.props.weak then return get_n_ending(base, base.lemma, "is singular") elseif rfind(base.lemma, "nis$") then -- neuter like [[Erlebnis]], [[Geheimnis]] or occasional masculine like [[Firnis]], [[Penis]] return "ses" elseif rfind(base.lemma, M.com.NV .. "us$") then -- [[Euphemismus]], [[Exitus]], [[Exodus]], etc. return "" elseif rfind(base.lemma, "[sßxz]$") then return "es" else return "s" end end local function get_default_pl(base, gender) if rfind(base.lemma, "nis$") then -- neuter like [[Erlebnis]], [[Geheimnis]] or feminine like [[Kenntnis]], [[Wildnis]], -- or occasional masculine like [[Firnis]], [[Penis]] return "se" elseif gender == "f" or base.props.weak then return get_n_ending(base, base.lemma) elseif rfind(base.lemma, "e$") then track("default-pl-e-not-f-or-weak") -- FIXME: This should return "s" return get_n_ending(base, base.lemma) elseif gender == "n" and rfind(base.lemma, "lein$") then -- Diminutives in -lein (those in -chen will automatically get a null ending from -en below) return "" elseif gender == "n" and rfind(base.lemma, "um$") then -- [[Museum]] -> [[Museen]], [[Vakuum]] -> [[Vakuen]]; not masculine [[Baum]] (plural [[Bäume]]) -- or [[Reichtum]] (plural [[Reichtümer]]) return "!" .. rsub(base.lemma, "um$", "en") elseif rfind(base.lemma, "mus$") then -- Algorithmus -> Algorithmen, Aphorismus -> Aphorismen return "!" .. rsub(base.lemma, "us$", "en") elseif rfind(base.lemma, M.com.NV .. "us$") then -- [[Abakus]] -> [[Abakusse]], [[Zirkus]] -> [[Zirkusse]], [[Autobus]] -> [[Autobusse]]; -- not [[Applaus]] (plural [[Applause]]) return "se" elseif rfind(base.lemma, "e[lmnr]$") and not rfind(base.lemma, M.com.NV .. "[ei]e[lnmr]$") then -- check for weak ending -el, -em, -en, -er, e.g. [[Adler]], [[Meier]], [[Riedel]]; but exclude [[Heer]], -- [[Bier]], [[Ziel]], which take -e by default return "" else return "e" end end local function decline_singular(base, gender, def_gen) add(base, "nom_s", nil, "", gender) add_spec(base, "gen_s", base.gens, gender, def_gen) if base.props.weak then local ending = get_n_ending(base, base.lemma, "is singular") add(base, "dat_s", nil, ending, gender) add(base, "acc_s", nil, gender == "m" and ending or "", gender) else add(base, "dat_s", nil, "", gender) add_archaic_dative_singular(base, gender, def_gen) add(base, "acc_s", nil, "", gender) end end local function decline_plural(base, def_pl) local function process_nom_pl_for_decl_type(stem_ending) if base.props.saw_mn and base.number ~= "pl" then if base.props.weak then M.table.insertIfNot(base.decl_type, "อ่อน") elseif stem_ending == base.lemma .. "n" or stem_ending == base.lemma .. "en" then M.table.insertIfNot(base.decl_type, "ผสม") else M.table.insertIfNot(base.decl_type, "แข็ง") end end return stem_ending end local function process_dat_pl_to_add_n(stem_ending) if base.props.nodatpln then return stem_ending elseif rfind(stem_ending, "e[lr]?$") or rfind(stem_ending, "erl$") then return stem_ending .. "n" else return stem_ending end end add_spec(base, "nom_p", base.pls, nil, def_pl, nil, nil, process_nom_pl_for_decl_type) add_spec(base, "gen_p", base.pls, nil, def_pl) add_spec(base, "dat_p", base.pls, nil, def_pl, nil, nil, process_dat_pl_to_add_n) add_spec(base, "acc_p", base.pls, nil, def_pl) end local function decline_noun(base) if base.number == "pl" then decline_plural(base, "") if rfind(base.lemma, "innen$") then --- Ends in -innen, likely feminine. Chop off, and convert e.g. Chinesinnen -> Chinesen. local masc = rsub(base.lemma, "innen$", "") if rfind(masc, "es$") then masc = masc .. "en" end -- No need to specify gender for *_equiv; will be handled correctly in [[Module:de-headword]]. add(base, "m_equiv", masc, "") else -- Likely masculine. Try to convert Chinesen -> Chinesinnen, and -er -> -erinnen. local femstem = rsub(base.lemma, "en$", "") add(base, "f_equiv", femstem, "innen") end else base.decl_type = {} for _, genderspec in ipairs(base.genders) do local gender = genderspec.form decline_singular(base, gender, get_default_gen(base, gender)) decline_plural(base, get_default_pl(base, gender)) if gender == "m" then add(base, "f_equiv", rsub(base.lemma, "e$", ""), "in") -- feminine elseif gender == "f" then -- Try (sort of) to get the masculine. Remove final -in, and if the result ends in -es, convert to -ese -- (e.g. Chinesin -> Chinese). local masc = rsub(base.lemma, "in$", "") if rfind(masc, "es$") then masc = masc .. "e" end add(base, "m_equiv", masc, "") end -- do nothing for neuter end end end local function decline_surname(base) -- We don't specify gender here. There are always two genders, m and f, which will be handled correctly in -- [[Module:de-headword]]. add(base, "nom_m_s", nil, "") add(base, "nom_f_s", nil, "") local gen_m_s if rfind(base.lemma, "[sxzß]$") or rfind(base.lemma, "ce$") then -- [[Marx]], [[Engels]], [[Weiß]], [[Schulz]] -- also names with silent -s or -x like [[Delacroix]] gen_m_s = "'" else gen_m_s = "s" end add_spec(base, "gen_m_s", base.gens, nil, gen_m_s) add(base, "gen_m_s", nil, "", nil, {"[มีคำกำกับนาม]"}) add(base, "gen_f_s", nil, "") add(base, "dat_m_s", nil, "") add(base, "dat_f_s", nil, "") add(base, "acc_m_s", nil, "") add(base, "acc_f_s", nil, "") local pl_ending if rfind(base.lemma, "[sxß]$") then -- [[Marx]], [[Engels]], [[Weiß]] pl_ending = {"", "ens"} elseif rfind(base.lemma, "z$") then -- [[Schulz]], [[Schmitz]] pl_ending = {"", "es", "ens"} elseif rfind(base.lemma, "ce$") then pl_ending = {"", "ns"} elseif rfind(base.lemma, "e[nlr]?$") then -- [[Müller]], [[Goethe]], [[Dürer]], [[Schlegel]], [[Münchhausen]] pl_ending = {"s", ""} else -- [[Schmidt]], [[Bergmann]], [[Brentano]] pl_ending = {"s"} end add_spec(base, "nom_p", base.pls, nil, pl_ending) add_spec(base, "gen_p", base.pls, nil, pl_ending) add_spec(base, "dat_p", base.pls, nil, pl_ending) add_spec(base, "acc_p", base.pls, nil, pl_ending) end local function decline_toponym(base) -- We don't specify gender here, which is always neuter. add(base, "nom_s", nil, "") local gen_s local null_footnote if rfind(base.lemma, "[sxzß]$") then gen_s = "'" null_footnote = "[มีคำกำกับนาม]" else gen_s = "s" null_footnote = "[อาจมีคำกำกับนาม]" end add_spec(base, "gen_s", base.gens, nil, gen_s) add(base, "gen_s", nil, "", nil, {null_footnote}) add(base, "dat_s", nil, "") add(base, "acc_s", nil, "") if base.number == "both" then -- only with explicitly given plural add_spec(base, "nom_p", base.pls) add_spec(base, "gen_p", base.pls) add_spec(base, "dat_p", base.pls) add_spec(base, "acc_p", base.pls) end end local function decline_langname(base) -- We don't specify gender here, which is always neuter. add(base, "nom_s", nil, "") add(base, "gen_s", nil, "") -- If explicit genitive singular given, add it (in addition to the null genitive singular), otherwise default to -s. add_spec(base, "gen_s", base.gens, nil, "s") add(base, "dat_s", nil, "") add(base, "acc_s", nil, "") add(base, "nom_s_alt", nil, "e") add(base, "gen_s_alt", nil, "en") add(base, "dat_s_alt", nil, "en") add(base, "acc_s_alt", nil, "e") end local function decline_adjective(base) -- Construct an equivalent call to {{de-adecl}} based on the adjective indicators we fetched. local adj_spec_parts = {} local function ins(val) table.insert(adj_spec_parts, val) end local function ins_dot() if #adj_spec_parts > 0 then ins(".") end end local function insert_footnotes(footnotes) if footnotes then for _, footnote in ipairs(footnotes) do ins(footnote) end end end if base.adj_stem then ins("stem") for _, stem in ipairs(base.adj_stem) do ins(":") ins(stem.form) insert_footnotes(stem.footnotes) end end if base.adj_suppress then ins_dot() ins("suppress:") ins(base.adj_suppress) end if base.footnotes then ins_dot() insert_footnotes(base.footnotes) end for prop, _ in pairs(base.props) do if is_adjectival_decl_indicator(prop) then ins_dot() ins(prop) end end local adj_alternant_multiword_spec = M.de_adjective.do_generate_forms( {base.lemma .. "<" .. table.concat(adj_spec_parts) .. ">"} ) local function copy(from_slot, to_slot) base.forms[to_slot] = adj_alternant_multiword_spec.forms[from_slot] end local function copy_gender_forms(gender) local number = gender == "p" and "p" or "s" for _, state in ipairs(states) do for _, case in ipairs(basic_cases) do copy(state .. "_" .. case .. "_" .. gender, state .. "_" .. case .. "_" .. number) end end end if base.number == "pl" then copy_gender_forms("p") -- No need to specify gender for *_equiv; will be handled correctly in [[Module:de-headword]]. add(base, "m_equiv", base.lemma, "e") add(base, "f_equiv", base.lemma, "e") add(base, "n_equiv", base.lemma, "e") else -- Normally there should be only one gender. for _, genderspec in ipairs(base.genders) do local gender = genderspec.form copy_gender_forms(gender) -- No need to specify gender for *_equiv; will be handled correctly in [[Module:de-headword]]. add(base, "m_equiv", base.lemma, "er") -- masculine add(base, "f_equiv", base.lemma, "e") -- feminine add(base, "n_equiv", base.lemma, "es") -- neuter end if base.number ~= "sg" then copy_gender_forms("p") end end end -- Return the slots that may contain a lemma, in the order they should be checked. `props` is a property table, -- coming either from `base` or `alternant_multiword_spec`. local function get_lemma_slots(props) if props.surname then return {"nom_m_s"} elseif props.overall_adj then return {"str_nom_s", "str_nom_p"} else return {"nom_s", "nom_p"} end end -- Return the lemmas for this term. The return value is a list of {form = FORM, footnotes = FOOTNOTES}. -- If `linked_variant` is given, return the linked variants (with embedded links if specified that way by the user), -- otherwies return variants with any embedded links removed. If `remove_footnotes` is given, remove any -- footnotes attached to the lemmas. function export.get_lemmas(alternant_multiword_spec, linked_variant, remove_footnotes) local slots_to_fetch = get_lemma_slots(alternant_multiword_spec.props) local linked_suf = linked_variant and "_linked" or "" for _, slot in ipairs(slots_to_fetch) do if alternant_multiword_spec.forms[slot .. linked_suf] then local lemmas = alternant_multiword_spec.forms[slot .. linked_suf] if remove_footnotes then local lemmas_no_footnotes = {} for _, lemma in ipairs(lemmas) do table.insert(lemmas_no_footnotes, {form = lemma.form}) end return lemmas_no_footnotes else return lemmas end end end return {} end local function handle_derived_slots_and_overrides(base) process_slot_overrides(base) -- Compute linked versions of potential lemma slots, for use in {{de-noun}}. -- We substitute the original lemma (before removing links) for forms that -- are the same as the lemma, if the original lemma has links. for _, slot in ipairs(get_lemma_slots(base.props)) do M.iut.insert_forms(base.forms, slot .. "_linked", M.iut.map_forms(base.forms[slot], function(form) if form == base.orig_lemma_no_links and rfind(base.orig_lemma, "%[%[") then return base.orig_lemma else return form end end)) end end -- Like M.put.split_alternating_runs_and_strip_spaces(), but ensure that backslash-escaped commas and periods are not -- treated as separators. local function split_alternating_runs_with_escapes(segments, splitchar) for i, segment in ipairs(segments) do segments[i] = rsub(segment, "\\,", SUB_ESCAPED_COMMA) segments[i] = rsub(segment, "\\%.", SUB_ESCAPED_PERIOD) end local separated_groups = M.put.split_alternating_runs_and_strip_spaces(segments, splitchar) for _, separated_group in ipairs(separated_groups) do for i, segment in ipairs(separated_group) do separated_group[i] = rsub(segment, SUB_ESCAPED_COMMA, ",") separated_group[i] = rsub(segment, SUB_ESCAPED_PERIOD, ".") end end return separated_groups end --[=[ Parse an indicator spec (text consisting of angle brackets and zero or more dot-separated indicators within them). Return value is an object of the form { overrides = { SLOT = {OVERRIDE, OVERRIDE, ...}, ... }, -- where OVERRIDE is {form = FORM, footnotes = FOOTNOTES}; same as `forms` table; FORM can be a full form (only if beginning with a capital letter or !), otherwise an ending; "-" for an ending means a null ending, while "--" suppresses the slot entirely, i.e. it is defective gens = {GEN_SG_SPEC, GEN_SG_SPEC, ...}, same form as OVERRIDE above pls = {PL_SPEC, PL_SPEC, ...}, same form as OVERRIDE above forms = {}, -- forms for a single spec alternant; see `forms` below props = { PROP = true, PROP = true, ... }, -- misc Boolean properties: "weak" (weak noun); "adj" (adjectival noun; set using "+"); "ss" (lemma in -ß changes to -ss- before endings beginning with a vowel; pre-1996 spelling); "nodatpln" (suppress automatic addition of 'n' in the dative plural after '-e', '-er', '-el') number = "NUMBER", -- "sg", "pl", "both"; may be missing adj = true, -- may be missing -- The following additional fields are added by other functions: orig_lemma = "ORIGINAL-LEMMA", -- as given by the user or taken from pagename orig_lemma_no_links = "ORIGINAL-LEMMA-NO-LINKS", -- links removed lemma = "LEMMA", -- `orig_lemma_no_links`, forms = { SLOT = { { form = "FORM", footnotes = {"FOOTNOTE", "FOOTNOTE", ...} -- may be missing }, ... }, ... }, } ]=] local function parse_indicator_spec(angle_bracket_spec, lemma, pagename, proper_noun) if lemma == "" then lemma = pagename end local base = {forms = {}, overrides = {}, props = {prop = proper_noun}} base.orig_lemma = lemma base.orig_lemma_no_links = M.links.remove_links(lemma) base.lemma = base.orig_lemma_no_links local inside = rmatch(angle_bracket_spec, "^<(.*)>$") assert(inside) local function parse_err(msg) error(msg .. ": <" .. inside .. ">") end --[=[ Parse a single override spec and return three values: the slot the override applies to, the original indicator spec used to specify the slot, and the override specs. The input is a list where the footnotes have been separated out. For example, given the spec 'dat:-[referring to a card suit, as a term of endearment, and generally in speech]:en[in most cases in writing]', the input will be a list {"dat:-", "[referring to a card suit, as a term of endearment, and generally in speech]", ":en", "[in most cases in writing]", ""} ]=] local function parse_override(segments) local part = segments[1] local offset = 4 local case = usub(part, 1, 3) if not case_set_with_abl_voc[case] then parse_err("Internal error: unrecognized case in override: '" .. table.concat(segments) .. "'") end local indicator = case local rest = usub(part, offset) local slot if rfind(rest, "^pl") then rest = rsub(rest, "^pl", "") slot = case .. "_p" indicator = indicator .. "pl" else slot = case .. "_s" end if rfind(rest, "^:") then rest = rsub(rest, "^:", "") else parse_err("Slot indicator '" .. indicator .. "' must be followed by a colon: '" .. table.concat(segments) .. "'") end if not noun_slot_set[slot] then parse_err("Unrecognized slot indicator '" .. indicator .. "': '" .. table.concat(segments) .. "'") end segments[1] = rest return slot, indicator, M.com.fetch_specs(segments, ":", "override", nil, parse_err) end if inside ~= "" then local segments = M.put.parse_balanced_segment_run(inside, "[", "]") local dot_separated_groups = split_alternating_runs_with_escapes(segments, "%.") for i, dot_separated_group in ipairs(dot_separated_groups) do local part = dot_separated_group[1] if i == 1 then local comma_separated_groups = split_alternating_runs_with_escapes(dot_separated_group, ",") base.genders = M.com.fetch_specs(comma_separated_groups[1], ":", "gender", nil, parse_err) local saw_sg = false local saw_pl = false local saw_gendered_pl = false local saw_non_gendered_pl = false local saw_adj = false local special_variant = nil for _, genderspec in ipairs(base.genders) do local g = genderspec.form if g == "m" or g == "n" then -- Set this on `base.props` as it's used in various other places. base.props.saw_mn = true saw_sg = true elseif g == "f" then saw_sg = true elseif g == "p" then saw_pl = true saw_non_gendered_pl = true elseif rfind(g, "^[mfn]p$") then saw_pl = true saw_gendered_pl = true elseif g == "+" or g == "p+" or g == "+p" then if #base.genders > 1 then parse_err("Can't specify multiple genders with adjectival declension") end saw_adj = true if g ~= "+" then saw_pl = true end elseif g == "surname" or g == "toponym" or g == "langname" then if #base.genders > 1 then parse_err("Can't specify multiple genders with " .. g .. " declension") end special_variant = g else parse_err("Unrecognized gender spec '" .. g .. "'") end end if saw_sg and saw_pl then parse_err("Can't specify both singular and plural gender specs") end if saw_gendered_pl and saw_non_gendered_pl then parse_err("Can't specify both 'p' and gendered plural specs") end local gen_index = (base.props.saw_mn or special_variant) and 2 or 1 local pl_index = (saw_adj or saw_pl) and 1 or (base.props.saw_mn or special_variant == "surname" or special_variant == "toponym") and 3 or 2 if #comma_separated_groups > pl_index then if saw_adj then parse_err("Can't specify plurals or genitives with adjectival declension") elseif saw_pl then parse_err("Can't specify plurals or genitives with plural-only nouns") elseif base.props.saw_mn then parse_err("Can specify at most three comma-separated specs when the gender is masculine or " .. "neuter (gender, genitive, plural)") elseif special_variant == "surname" or special_variant == "toponym" then parse_err("Can specify at most three comma-separated specs with '" .. special_variant .. "' " .. "nouns ('" .. special_variant .. "', genitive, plural)") elseif special_variant == "langname" then parse_err("Can specify at most two comma-separated specs with 'langname' " .. " ('langname', genitive)") else parse_err("Can specify at most two comma-separated specs when the gender is feminine " .. "(gender, plural)") end end if #comma_separated_groups >= gen_index and gen_index > 1 then base.gens = M.com.fetch_specs(comma_separated_groups[gen_index], ":", "genitive", "allow blank", parse_err) end if #comma_separated_groups >= pl_index and pl_index > gen_index then base.pls = M.com.fetch_specs(comma_separated_groups[pl_index], ":", "plural", "allow blank", parse_err) end if special_variant then if #base.genders > 1 then parse_err("Internal error: More than one gender spec for '" .. special_variant .. "'") else base.props[special_variant] = true if special_variant == "surname" then -- FIXME, does it make sense to put the footnotes on the feminine gender (they appear after the gender)? base.genders = {{form = "m"}, {form = "f", footnotes = base.genders[1].footnotes}} else base.genders = {{form = "n", footnotes = base.genders[1].footnotes}} end end elseif saw_adj then if #base.genders > 1 then parse_err("Internal error: More than one gender spec for adjectival declension") else base.props.adj = true if saw_pl then base.number = "pl" base.genders = {{form = "p", footnotes = base.genders[1].footnotes}} else -- Stash the footnotes into `adj_footnotes`; we will put them onto the autodetected gender -- in determine_adjectival_genders(), which will set base.genders appropriately. base.adj_footnotes = base.genders[1].footnotes base.genders = {} end end elseif saw_pl then -- Convert 'mp' to 'm-p', 'fp' to 'f-p', etc. as that's what [[Module:gender and number]] expects. for _, genderspec in ipairs(base.genders) do local gender = rmatch(genderspec.form, "^([mfn])p$") if gender then genderspec.form = gender .. "-p" end end base.number = "pl" end elseif base.props.adj and part:find("^stem:") then dot_separated_group[1] = rsub(part, "^stem:", "") base.adj_stem = M.com.fetch_specs(dot_separated_group, ":", "adjectival stem", nil, parse_err) elseif base.props.adj and part:find("^suppress:") then if #dot_separated_group > 1 then parse_err("Can't specify footnotes with suppress: '" .. table.concat(dot_separated_group) .. "'") end -- No need to parse or validate more. Will happen in [[Module:de-adjective]]. base.adj_suppress = rsub(part, "suppress:", "") elseif part == "" then if #dot_separated_group == 1 then parse_err("Blank indicator") end base.footnotes = M.com.fetch_footnotes(dot_separated_group, parse_err) elseif part:find(":") then -- override -- FIXME: Handle adjectival overrides local case_prefix = usub(part, 1, 3) if case_set_with_abl_voc[case_prefix] then local slot, slot_indicator, override = parse_override(dot_separated_group) if base.overrides[slot] then parse_err("Can't specify override twice for slot '" .. slot_indicator .. "'") else base.overrides[slot] = override end else parse_err("Unrecognized indicator '" .. part .. "'") end elseif #dot_separated_group > 1 then local errmsg if base.props.adj then errmsg = "Footnotes only allowed with slot overrides, 'stem:' or by themselves" else errmsg = "Footnotes only allowed with genitive, plural, slot overrides or by themselves" end parse_err(errmsg .. ": '" .. table.concat(dot_separated_group) .. "'") elseif part == "sg" or part == "both" then if base.number then if base.number ~= part then parse_err("Can't specify '" .. part .. "' along with '" .. base.number .. "'") else parse_err("Can't specify '" .. part .. "' twice") end end base.number = part elseif not base.props.adj and (part == "weak" or part == "weak_n" or part == "ss" or part == "nodatpln" or part == "article" or part == "dat_with_e") then if base.props[part] then parse_err("Can't specify '" .. part .. "' twice") end base.props[part] = true if part == "weak_n" then -- weak_n implies weak base.props.weak = true end elseif base.props.adj and (part == "article" or is_adjectival_decl_indicator(part)) then if base.props[part] then parse_err("Can't specify '" .. part .. "' twice") end base.props[part] = true else parse_err("Unrecognized indicator '" .. part .. "'") end end end return base end -- For an adjectival lemma, synthesize the predicative (lemma) form. It doesn't have to be perfect in that the -- predicative form itself isn't used, so we don't have to try to convert -abler -> -abel or anything like that. local function synthesize_adj_lemma(base) local stem, ending = rmatch(base.lemma, "^(.*)(e[rs]?)$") if not stem then error("Unrecognized adjectival lemma, should end in '-er', '-e' or '-es': '" .. base.lemma .. "'") end base.lemma = stem -- Will be ignored if number == "pl" if ending == "er" then base.autodetected_gender = "m" elseif ending == "e" then base.autodetected_gender = "f" else base.autodetected_gender = "n" end end local function detect_indicator_spec(alternant_multiword_spec, base) if base.props.article then alternant_multiword_spec.props.article = true end for _, prop in ipairs {"surname", "toponym", "langname"} do if alternant_multiword_spec.props[prop] == nil then alternant_multiword_spec.props[prop] = base.props[prop] elseif alternant_multiword_spec.props[prop] ~= base.props[prop] then -- We do this because we have a special table with its own slots for each of these special variants. -- FIXME: Consider supporting adjectives with these variants. That requires that we copy the adjectival -- declensions to the appropriate per-variant slots. error("If some alternants set '" .. prop .. "', all must do so") end end if base.props.adj then alternant_multiword_spec.props.overall_adj = true synthesize_adj_lemma(base) else -- Set default values. base.number = base.number or base.props.surname and "both" or base.pls and "both" or (alternant_multiword_spec.props.is_proper or base.props.toponym or base.props.langname) and "sg" or "both" if not base.props.surname then if base.number == "pl" then if base.gens then error("Internal error: With plural-only noun, no genitive singular specs should be allowed") end if base.pls then error("Internal error: With plural-only noun, no plural specs should be allowed") end end if base.pls and base.number == "sg" then error("Can't specify explicit plural specs along with explicit '.sg'") end end base.gens = base.gens or {{form = "+"}} base.pls = base.pls or {{form = "+"}} end end local function detect_all_indicator_specs(alternant_multiword_spec) M.iut.map_word_specs(alternant_multiword_spec, function(base) detect_indicator_spec(alternant_multiword_spec, base) end) -- Now propagate some properties downwards. M.iut.map_word_specs(alternant_multiword_spec, function(base) base.props.overall_adj = alternant_multiword_spec.props.overall_adj end) end local propagate_multiword_properties local function propagate_alternant_properties(alternant_spec, property, mixed_value, nouns_only) local seen_property for _, multiword_spec in ipairs(alternant_spec.alternants) do propagate_multiword_properties(multiword_spec, property, mixed_value, nouns_only) if seen_property == nil then seen_property = multiword_spec[property] elseif multiword_spec[property] and seen_property ~= multiword_spec[property] then seen_property = mixed_value end end alternant_spec[property] = seen_property end propagate_multiword_properties = function(multiword_spec, property, mixed_value, nouns_only) local seen_property = nil local last_seen_nounal_pos = 0 local word_specs = multiword_spec.alternant_or_word_specs or multiword_spec.word_specs for i = 1, #word_specs do local is_nounal if word_specs[i].alternants then propagate_alternant_properties(word_specs[i], property, mixed_value) is_nounal = not not word_specs[i][property] elseif nouns_only then is_nounal = not word_specs[i].props.adj else is_nounal = not not word_specs[i][property] end if is_nounal then if not word_specs[i][property] then error("Internal error: noun-type word spec without " .. property .. " set") end for j = last_seen_nounal_pos + 1, i - 1 do word_specs[j][property] = word_specs[j][property] or word_specs[i][property] end last_seen_nounal_pos = i if seen_property == nil then seen_property = word_specs[i][property] elseif seen_property ~= word_specs[i][property] then seen_property = mixed_value end end end if last_seen_nounal_pos > 0 then for i = last_seen_nounal_pos + 1, #word_specs do word_specs[i][property] = word_specs[i][property] or word_specs[last_seen_nounal_pos][property] end end multiword_spec[property] = seen_property end local function propagate_properties_downward(alternant_multiword_spec, property, default_propval) local propval1 = alternant_multiword_spec[property] or default_propval alternant_multiword_spec[property] = propval1 for _, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do local propval2 = alternant_or_word_spec[property] or propval1 if alternant_or_word_spec.alternants then for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do local propval3 = multiword_spec[property] or propval2 for _, word_spec in ipairs(multiword_spec.word_specs) do local propval4 = word_spec[property] or propval3 if propval4 == "mixed" then error("Attempt to assign mixed " .. property .. " to word") end word_spec[property] = propval4 end end else if propval2 == "mixed" then error("Attempt to assign mixed " .. property .. " to word") end alternant_or_word_spec[property] = propval2 end end end --[=[ Propagate `property` ("genders" or "number") from nouns to adjacent adjectives. We proceed as follows: 1. We assume the properties in question are already set on all nouns. This should happen in parse_indicator_spec(). 2. We first propagate properties upwards and sideways. We recurse downwards from the top. When we encounter a multiword spec, we proceed left to right looking for a noun. When we find a noun, we fetch its property (recursing if the noun is an alternant), and propagate it to any adjectives to its left, up to the next noun to the left. When we have processed the last noun, we also propagate its property value to any adjectives to the right. Finally, we set the property value for the multiword spec itself by combining all the non-nil properties of the individual elements. If all non-nil properties have the same value, the result is that value, otherwise it is `mixed_value` (which is "mixed" gender, but "both" for number). 3. When we encounter an alternant spec in this process, we recursively process each alternant (which is a multiword spec) using the previous step, and combine any non-nil properties we encounter the same way as for multiword specs. 4. The effect of steps 2 and 3 is to set the property of each alternant and multiword spec based on its children or its neighbors. ]=] local function propagate_properties(alternant_multiword_spec, property, default_propval, mixed_value) propagate_multiword_properties(alternant_multiword_spec, property, mixed_value, "nouns only") propagate_multiword_properties(alternant_multiword_spec, property, mixed_value, false) propagate_properties_downward(alternant_multiword_spec, property, default_propval) end -- Set the gender of adjectives and adjectival nouns to the gender autodetected during synthesize_adj_lemma(), -- unless the form is plural. We don't just set the gender directly in synthesize_adj_lemma() because we don't know -- until later (i.e. when propagate_properties() is called) whether an adjectival form in -e is feminine or plural. -- We set the footnotes (i.e. qualifiers) of the gender to the footnotes (if any) specified directly after '+'. local function determine_adjectival_genders(alternant_multiword_spec) M.iut.map_word_specs(alternant_multiword_spec, function(base) if base.props.adj and #base.genders == 0 then base.genders = {{form = base.number == "pl" and "p" or base.autodetected_gender, footnotes = base.adj_footnotes}} end end) end -- Find the first noun in a multiword expression and set alternant_multiword_spec.first_noun -- to the index of that noun. Also find the first adjective and set alternant_multiword_spec.first_adj -- similarly. If there is a first noun, we use its properties to determine the overall expression's -- properties; otherwise we use the first adjective's properties, otherwise the first word's properties. -- If the "word" located this way is not an alternant spec, we just use its properties directly, otherwise -- we use the properties of the first noun (or failing that the first adjective, or failing that the -- first word) in each alternative alternant in the alternant spec. For this reason, we need to set the -- the .first_noun of and .first_adj of each multiword expression embedded in the first noun alternant spec, -- and the .first_adj of each multiword expression in each adjective alternant spec leading up to the -- first noun alternant spec. local function determine_noun_status(alternant_multiword_spec) for i, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do if alternant_or_word_spec.alternants then local alternant_type for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do for j, word_spec in ipairs(multiword_spec.word_specs) do if not word_spec.props.adj then multiword_spec.first_noun = j alternant_type = "noun" break elseif not multiword_spec.first_adj then multiword_spec.first_adj = j if not alternant_type then alternant_type = "adj" end end end end if alternant_type == "noun" then alternant_multiword_spec.first_noun = i return elseif alternant_type == "adj" and not alternant_multiword_spec.first_adj then alternant_multiword_spec.first_adj = i end else if not alternant_or_word_spec.props.adj then alternant_multiword_spec.first_noun = i return elseif not alternant_multiword_spec.first_adj then alternant_multiword_spec.first_adj = i end end end end local function decline_noun_or_adjective(base) if base.props.surname then decline_surname(base) elseif base.props.toponym then decline_toponym(base) elseif base.props.langname then decline_langname(base) elseif base.props.adj then decline_adjective(base) else decline_noun(base) end handle_derived_slots_and_overrides(base) end -- Set the overall articles. We can't do this using the normal inflection code as it will produce e.g. -- '[[der]] [[und]] [[der]]' for conjoined nouns. local function compute_non_surname_articles(alternant_multiword_spec) if alternant_multiword_spec.number ~= "pl" then M.iut.map_word_specs(alternant_multiword_spec, function(base) for _, genderspec in ipairs(base.genders) do for _, case in ipairs(cases_with_abl_voc) do for _, def in ipairs(definitenesses) do M.iut.insert_form(alternant_multiword_spec.forms, "art_" .. def .. "_" .. case .. "_s", {form = M.com.articles[genderspec.form][def .. "_" .. case]}) end end end end) end for _, case in ipairs(basic_cases) do for _, def in ipairs(definitenesses) do M.iut.insert_form(alternant_multiword_spec.forms, "art_" .. def .. "_" .. case .. "_p", {form = M.com.articles.p[def .. "_" .. case]}) end end end -- Set the overall surname articles. We can't do this using the normal inflection code as it will produce e.g. -- '[[der]] [[und]] [[der]]' for conjoined nouns. local function compute_surname_articles(alternant_multiword_spec) for _, gender in ipairs {"m", "f"} do for _, case in ipairs(basic_cases) do for _, def in ipairs(definitenesses) do M.iut.insert_form(alternant_multiword_spec.forms, "art_" .. def .. "_" .. case .. "_" .. gender .. "_s", {form = "([[" .. M.com.articles[gender][def .. "_" .. case] .. "]])"}) end end end for _, case in ipairs(basic_cases) do M.iut.insert_form(alternant_multiword_spec.forms, "art_def_" .. case .. "_p", {form = "([[" .. M.com.articles.p["def_" .. case] .. "]])"}) end end local function compute_articles(alternant_multiword_spec) if alternant_multiword_spec.props.surname then compute_surname_articles(alternant_multiword_spec) else compute_non_surname_articles(alternant_multiword_spec) end end -- Call a function `fun` over the first noun in the `alternant_multiword_spec`, or over the first noun in each -- alternant if there is more than one alternant. If there are no nouns, use the first adjective (in the case of an -- adjectival noun). local function map_first_noun(alternant_multiword_spec, fun) local key_entry = alternant_multiword_spec.first_noun or alternant_multiword_spec.first_adj or 1 if #alternant_multiword_spec.alternant_or_word_specs >= key_entry then local alternant_or_word_spec = alternant_multiword_spec.alternant_or_word_specs[key_entry] if alternant_or_word_spec.alternants then for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do key_entry = multiword_spec.first_noun or multiword_spec.first_adj or 1 if #multiword_spec.word_specs >= key_entry then fun(multiword_spec.word_specs[key_entry]) end end else fun(alternant_or_word_spec) end end end -- Compute the categories to add the noun to, as well as the annotation to display in the -- declension title bar. We combine the code to do these functions as both categories and -- title bar contain similar information. local function compute_categories_and_annotation(alternant_multiword_spec) alternant_multiword_spec.categories = {} alternant_multiword_spec.decl_type = {} local function insert(cattype) cattype = rsub(cattype, "~", alternant_multiword_spec.pos) M.table.insertIfNot(alternant_multiword_spec.categories, cattype .. "ภาษาเยอรมัน") end if not alternant_multiword_spec.props.is_proper and alternant_multiword_spec.number == "sg" then insert("~นับไม่ได้") elseif alternant_multiword_spec.number == "pl" then insert("~พหูพจน์เท่านั้น") end local annotation local annparts = {} local genderdescs = {} local decldescs = {} if alternant_multiword_spec.number == "sg" then table.insert(annparts, "เอกพจน์เท่านั้น") elseif alternant_multiword_spec.number == "pl" and alternant_multiword_spec.genders[1].spec ~= "p" then -- If the gender is just 'p', we use "pl-only" below as a substitute for the gender and hook any qualifiers -- onto it. Note that when 'p' is the gender, there can be only one gender. table.insert(annparts, "พหูพจน์เท่านั้น") end for i, genderspec in ipairs(alternant_multiword_spec.genders) do local genderdesc_parts = {} local gender = genderspec.spec if gender == "p" then table.insert(genderdesc_parts, "พหูพจน์เท่านั้น") else gender = rsub(gender, "%-p$", "") table.insert(genderdesc_parts, gender_spec_to_full_gender[gender]) end if genderspec.q then table.insert(genderdesc_parts, " ''(") table.insert(genderdesc_parts, table.concat(genderspec.q, ", ")) table.insert(genderdesc_parts, ")''") end table.insert(genderdescs, table.concat(genderdesc_parts)) end local function insert_decl_type(decl_type) M.table.insertIfNot(decldescs, decl_type) M.table.insertIfNot(alternant_multiword_spec.decl_type, decl_type) end local function do_word_spec(base) if base.props.surname then M.table.insertIfNot(decldescs, "นามสกุล") elseif base.props.toponym then M.table.insertIfNot(decldescs, "ภูมินาม") elseif base.props.langname then M.table.insertIfNot(decldescs, "ชื่อภาษา") elseif base.decl_type and #base.decl_type > 0 then -- strong/weak/mixed declension type; should only be present on masculine or neuter nouns with a plural for _, decl_type in ipairs(base.decl_type) do if decl_type == "อ่อน" then insert("~อ่อน") elseif decl_type == "ผสม" then insert("~ผสม") end insert_decl_type(decl_type) end elseif base.props.saw_mn then -- For singular-only masculine or neuter nouns, we can still classify as strong or weak. -- We don't try to classify plural-only nouns. Even for nouns in -n or -en, we have no idea if they are -- strong (-en is part of the stem), mixed or weak. if base.props.weak then insert("~อ่อน") insert_decl_type("อ่อน") else insert_decl_type("แข็ง") end end end -- Use the surname/toponym/langname/weak/strong properties of the noun(s). map_first_noun(alternant_multiword_spec, do_word_spec) if #genderdescs > 0 then table.insert(annparts, table.concat(genderdescs, " // ")) end if #decldescs > 0 then table.insert(annparts, table.concat(decldescs, " // ")) end if not alternant_multiword_spec.first_noun and alternant_multiword_spec.first_adj then insert("~เชิงคุณศัพท์") table.insert(annparts, "adjectival") end if alternant_multiword_spec.props.langname then insert("specially-declined language names") end alternant_multiword_spec.annotation = table.concat(annparts, ", ") end local function compute_headword_genders(alternant_multiword_spec) alternant_multiword_spec.genders = {} -- Compute the genders based on the nouns. We don't want to use the adjectives in adjective-noun combinations -- because that will cause issues in plural-only expressions like [[Kanarische Inseln]], where ''Inseln'' may be -- 'f-p' but ''Kanarische'' will be just 'p', and we'd end up with both genders. map_first_noun(alternant_multiword_spec, function(base) for _, genderspec in ipairs(base.genders) do -- Create the new spec to insert. local spec = {spec = genderspec.form, q = genderspec.footnotes} -- See if the gender of the spec is already present; if so, combine qualifiers. local saw_existing = false for _, existing_spec in ipairs(alternant_multiword_spec.genders) do if existing_spec.spec == spec.spec then existing_spec.q = M.iut.combine_footnotes(existing_spec.q, spec.q) saw_existing = true break end end -- If not, add gender. if not saw_existing then table.insert(alternant_multiword_spec.genders, spec) end end end) -- Now convert the footnotes in the gender specs to qualifiers. This involves removing brackets and expanding any -- footnote abbreviations. for _, genderspec in ipairs(alternant_multiword_spec.genders) do if genderspec.q then local processed_q = {} for _, q in ipairs(genderspec.q) do M.table.insertIfNot(processed_q, M.iut.expand_footnote_or_references(q, "return raw", "no parse refs")) end genderspec.q = processed_q end end end -- Process the specs in `arg_specs` given for dim=, m=, f=, n= or sg= and store the results in `slot` in -- `alternant_multiword_spec.forms`. `arg_specs` is a list of specs, each of which is a comma-separated or -- colon-separated string of specs, where each spec may be a suffix like "in", or a suffix with umlaut like -- "^chen", or a full form beginning with a capital letter or exclamation point. Suffixes are added onto the lemma -- with -e removed if present. `default` is the default value to use if "+" is given as a spec, and `literal_default`, -- if given, indicates that `default` is always a literal (full) form; otherwise, it `default` begins with a -- lowercase letter, it is taken as a suffix. (This is used in cases like the feminine of [[ordenlicher Professor]], -- which is generated as "ordentiche Professorin"; we don't want this interpreted as a suffix.) `desc` is an English -- description of the form whose specs are being processed, for display in error messages. local function process_dim_m_f_n(alternant_multiword_spec, arg_specs, default, literal_default, slot, desc) -- We don't want footnotes attached to a lemma to end up in the output. These footnotes typically get there if the -- syntax `.[footnote]` is used, which attaches a footnote to every form. local lemmas = export.get_lemmas(alternant_multiword_spec, nil, "remove footnotes") lemmas = M.iut.map_forms(lemmas, function(form) return rsub(form, "e$", "") end) for _, spec in ipairs(arg_specs) do local function parse_err(msg) error(msg .. ": " .. spec) end local segments = M.put.parse_balanced_segment_run(spec, "[", "]") -- Allow comma (preferred) or colon as separator. local ending_specs = M.com.fetch_specs(segments, "[,:]", desc, nil, parse_err) -- FIXME, this should propagate the 'ss' property upwards local props = {} local function do_combine_stem_ending(stem, ending) return combine_stem_ending(props, stem, ending) end local function process(stem, ending) M.iut.add_forms(alternant_multiword_spec.forms, slot, stem or lemmas, ending, do_combine_stem_ending) end process_spec(ending_specs, nil, default, literal_default, desc, process) end end local function show_forms(alternant_multiword_spec) local lemmas = export.get_lemmas(alternant_multiword_spec) local props = { lang = M.lang, lemmas = lemmas, slot_list = alternant_multiword_spec.props.surname and surname_slot_list_with_linked_and_articles or alternant_multiword_spec.props.langname and langname_slot_list_with_linked_and_articles or alternant_multiword_spec.props.overall_adj and adjectival_slot_list_with_linked_and_articles or noun_slot_list_with_linked_and_articles, } M.iut.show_forms(alternant_multiword_spec.forms, props) end local noun_template_both = [=[ ! class="outer" style="width:15%" | ! colspan=3 class="outer" style="width:46%" | เอกพจน์ ! colspan=2 class="outer" style="width:39%" | พหูพจน์ |- ! ! style="width:7%" | ไม่ชี้เฉพาะ ! style="width:7%" | ชี้เฉพาะ ! style="width:32%" | คำนาม ! style="width:7%" | ชี้เฉพาะ ! style="width:32%" | คำนาม |- ! กรรตุการก | class="secondary" | {art_ind_nom_s} | class="secondary" | {art_def_nom_s} | data-accel-col=1 | {nom_s} | class="secondary" | {art_def_nom_p} | data-accel-col=3 | {nom_p} |- ! สัมพันธการก | class="secondary" | {art_ind_gen_s} | class="secondary" | {art_def_gen_s} | data-accel-col=1 | {gen_s} | class="secondary" | {art_def_gen_p} | data-accel-col=3 | {gen_p} |- ! สัมปทานการก | class="secondary" | {art_ind_dat_s} | class="secondary" | {art_def_dat_s} | data-accel-col=1 | {dat_s} | class="secondary" | {art_def_dat_p} | data-accel-col=3 | {dat_p} |- ! กรรมการก | class="secondary" | {art_ind_acc_s} | class="secondary" | {art_def_acc_s} | data-accel-col=1 | {acc_s} | class="secondary" | {art_def_acc_p} | data-accel-col=3 | {acc_p} ]=] local noun_template_both_no_indef = [=[ ! class="outer" style="width:15%" | ! colspan=2 class="outer" style="width:39%" | เอกพจน์ ! colspan=2 class="outer" style="width:39%" | พหูพจน์ |- ! ! style="width:7%" | ชี้เฉพาะ ! style="width:32%" | คำนาม ! style="width:7%" | ชี้เฉพาะ ! style="width:32%" | คำนาม |- ! กรรตุการก | class="secondary" | {art_def_nom_s} | data-accel-col=1 | {nom_s} | class="secondary" | {art_def_nom_p} | data-accel-col=3 | {nom_p} |- ! สัมพันธการก | class="secondary" | {art_def_gen_s} | data-accel-col=1 | {gen_s} | class="secondary" | {art_def_gen_p} | data-accel-col=3 | {gen_p} |- ! สัมปทานการก | class="secondary" | {art_def_dat_s} | data-accel-col=1 | {dat_s} | class="secondary" | {art_def_dat_p} | data-accel-col=3 | {dat_p} |- ! กรรมการก | class="secondary" | {art_def_acc_s} | data-accel-col=1 | {acc_s} | class="secondary" | {art_def_acc_p} | data-accel-col=3 | {acc_p} ]=] local noun_template_abl_voc = [=[ |- ! อปาทานการก | class="secondary" | {art_ind_abl_s} | class="secondary" | {art_def_abl_s} | data-accel-col=1 | {abl_s} |- ! สัมโพธนการก | class="secondary" | {art_ind_voc_s} | class="secondary" | {art_def_voc_s} | data-accel-col=1 | {voc_s}]=] local noun_template_sg = [=[ ! class="outer" style="width:24.6%" | ! colspan=3 class="outer" | เอกพจน์ |- ! ! style="width:11.5%" | ไม่ชี้เฉพาะ ! style="width:11.5%" | ชี้เฉพาะ ! style="width:52.5%" | คำนาม |- ! กรรตุการก | class="secondary" | {art_ind_nom_s} | class="secondary" | {art_def_nom_s} | data-accel-col=1 | {nom_s} |- ! สัมพันธการก | class="secondary" | {art_ind_gen_s} | class="secondary" | {art_def_gen_s} | data-accel-col=1 | {gen_s} |- ! สัมปทานการก | class="secondary" | {art_ind_dat_s} | class="secondary" | {art_def_dat_s} | data-accel-col=1 | {dat_s} |- ! กรรมการก | class="secondary" | {art_ind_acc_s} | class="secondary" | {art_def_acc_s} | data-accel-col=1 | {acc_s}{abl_voc_clause} ]=] local noun_template_sg_no_indef = [=[ ! class="outer" style="width:24.6%" | ! colspan=2 class="outer" | เอกพจน์ |- ! ! style="width:11.5%" | ชี้เฉพาะ ! style="width:52.5%" | คำนาม |- ! กรรตุการก | class="secondary" | {art_def_nom_s} | data-accel-col=1 | {nom_s} |- ! สัมพันธการก | class="secondary" | {art_def_gen_s} | data-accel-col=1 | {gen_s} |- ! สัมปทานการก | class="secondary" | {art_def_dat_s} | data-accel-col=1 | {dat_s} |- ! กรรมการก | class="secondary" | {art_def_acc_s} | data-accel-col=1 | {acc_s}{abl_voc_clause} ]=] local noun_template_pl = [=[ ! class="outer" style="width:24.6%" | ! colspan=2 class="outer" | พหูพจน์ |- ! ! style="width:11.5%" | ชี้เฉพาะ ! style="width:52.5%" | คำนาม |- ! กรรตุการก | class="secondary" | {art_def_nom_p} | data-accel-col=3 | {nom_p} |- ! สัมพันธการก | class="secondary" | {art_def_gen_p} | data-accel-col=3 | {gen_p} |- ! สัมปทานการก | class="secondary" | {art_def_dat_p} | data-accel-col=3 | {dat_p} |- ! กรรมการก | class="secondary" | {art_def_acc_p} | data-accel-col=3 | {acc_p} ]=] local noun_template_surname = [=[ ! rowspan=2 class="outer" style="width:11%" | ! colspan=6 class="outer" | เอกพจน์ ! colspan=2 rowspan=2 class="outer" | พหูพจน์ |- ! colspan=3 class="outer" | เพศชาย ! colspan=3 class="outer" | เพศหญิง |- ! ! style="width:4%" | ไม่ชี้เฉพาะ ! style="width:4%" | ชี้เฉพาะ ! style="width:23%" | คำนาม ! style="width:4%" | ไม่ชี้เฉพาะ ! style="width:4%" | ชี้เฉพาะ ! style="width:23%" | คำนาม ! style="width:4%" | ชี้เฉพาะ ! style="width:23%" | คำนาม |- ! กรรตุการก | class="secondary" | {art_ind_nom_m_s} | class="secondary" | {art_def_nom_m_s} | data-accel-col=1 | {nom_m_s} | class="secondary" | {art_ind_nom_f_s} | class="secondary" | {art_def_nom_f_s} | data-accel-col=2 | {nom_f_s} | class="secondary" | {art_def_nom_p} | data-accel-col=3 | {nom_p} |- ! สัมพันธการก | class="secondary" | {art_ind_gen_m_s} | class="secondary" | {art_def_gen_m_s} | data-accel-col=1 | {gen_m_s} | class="secondary" | {art_ind_gen_f_s} | class="secondary" | {art_def_gen_f_s} | data-accel-col=2 | {gen_f_s} | class="secondary" | {art_def_gen_p} | data-accel-col=3 | {gen_p} |- ! สัมปทานการก | class="secondary" | {art_ind_dat_m_s} | class="secondary" | {art_def_dat_m_s} | data-accel-col=1 | {dat_m_s} | class="secondary" | {art_ind_dat_f_s} | class="secondary" | {art_def_dat_f_s} | data-accel-col=2 | {dat_f_s} | class="secondary" | {art_def_dat_p} | data-accel-col=3 | {dat_p} |- ! กรรมการก | class="secondary" | {art_ind_acc_m_s} | class="secondary" | {art_def_acc_m_s} | data-accel-col=1 | {acc_m_s} | class="secondary" | {art_ind_acc_f_s} | class="secondary" | {art_def_acc_f_s} | data-accel-col=2 | {acc_f_s} | class="secondary" | {art_def_acc_p} | data-accel-col=3 | {acc_p} ]=] local noun_template_langname = [=[ ! class="outer" style="width:15%" | ! colspan=4 class="outer" style="width:85%" | เอกพจน์ &nbsp; ''([[Wiktionary:German entry guidelines#Declension of language names|explanation of the use and meaning of the forms]])'' |- ! ! style="width:14%" | (ปกติไม่มีคำกำกับนาม) ! style="width:32%" | คำนาม ! style="width:7%" | ชี้เฉพาะ ! style="width:32%" | คำนาม |- ! กรรตุการก | class="secondary" | ({art_def_nom_s}) | data-accel-col=1 | {nom_s} | class="secondary" | {art_def_nom_s} | data-accel-col=1 | {nom_s_alt} |- ! สัมพันธการก | class="secondary" | ({art_def_gen_s}) | data-accel-col=1 | {gen_s} | class="secondary" | {art_def_gen_s} | data-accel-col=1 | {gen_s_alt} |- ! สัมปทานการก | class="secondary" | ({art_def_dat_s}) | data-accel-col=1 | {dat_s} | class="secondary" | {art_def_dat_s} | data-accel-col=1 | {dat_s_alt} |- ! กรรมการก | class="secondary" | ({art_def_acc_s}) | data-accel-col=1 | {acc_s} | class="secondary" | {art_def_acc_s} | data-accel-col=1 | {acc_s_alt} ]=] local adjectival_template_both = [=[ ! style="width:15%" | ! colspan=2 | เอกพจน์ ! colspan=2 | พหูพจน์ |- data-wikt-palette-p="blue" ! class="outer" | {gender} ! colspan=4 class="outer" | การผันรูปแบบแข็ง |- data-wikt-palette-p="blue" ! กรรตุการก | colspan=2 data-accel-col=1 | {str_nom_s} | colspan=2 data-accel-col=3 | {str_nom_p} |- data-wikt-palette-p="blue" ! สัมพันธการก | colspan=2 data-accel-col=1 | {str_gen_s} | colspan=2 data-accel-col=3 | {str_gen_p} |- data-wikt-palette-p="blue" ! สัมปทานการก | colspan=2 data-accel-col=1 | {str_dat_s} | colspan=2 data-accel-col=3 | {str_dat_p} |- data-wikt-palette-p="blue" ! กรรมการก | colspan=2 data-accel-col=1 | {str_acc_s} | colspan=2 data-accel-col=3 | {str_acc_p} |- data-wikt-palette-p="green" ! class="outer" | ! colspan=4 class="outer" | การผันรูปแบบอ่อน |- data-wikt-palette-p="green" ! กรรตุการก | class="secondary" style="width:5em" | {art_def_nom_s} | data-accel-col=4 | {wk_nom_s} | class="secondary" style="width:5em" | {art_def_nom_p} | data-accel-col=5 | {wk_nom_p} |- data-wikt-palette-p="green" ! สัมพันธการก | class="secondary" style="width:5em" | {art_def_gen_s} | data-accel-col=4 | {wk_gen_s} | class="secondary" style="width:5em" | {art_def_gen_p} | data-accel-col=5 | {wk_gen_p} |- data-wikt-palette-p="green" ! สัมปทานการก | class="secondary" style="width:5em" | {art_def_dat_s} | data-accel-col=4 | {wk_dat_s} | class="secondary" style="width:5em" | {art_def_dat_p} | data-accel-col=5 | {wk_dat_p} |- data-wikt-palette-p="green" ! กรรมการก | class="secondary" style="width:5em" | {art_def_acc_s} | data-accel-col=4 | {wk_acc_s} | class="secondary" style="width:5em" | {art_def_acc_p} | data-accel-col=5 | {wk_acc_p} |- data-wikt-palette-p="orange" ! class="outer" | ! colspan=4 class="outer" | การผันรูปแบบผสม |- data-wikt-palette-p="orange" ! กรรตุการก | class="secondary" style="width:5em" | {art_ind_nom_s} | data-accel-col=6 | {mix_nom_s} | class="secondary" style="width:5em" | {art_ind_nom_p} | data-accel-col=7 | {mix_nom_p} |- data-wikt-palette-p="orange" ! สัมพันธการก | class="secondary" style="width:5em" | {art_ind_gen_s} | data-accel-col=6 | {mix_gen_s} | class="secondary" style="width:5em" | {art_ind_gen_p} | data-accel-col=7 | {mix_gen_p} |- data-wikt-palette-p="orange" ! สัมปทานการก | class="secondary" style="width:5em" | {art_ind_dat_s} | data-accel-col=6 | {mix_dat_s} | class="secondary" style="width:5em" | {art_ind_dat_p} | data-accel-col=7 | {mix_dat_p} |- data-wikt-palette-p="orange" ! กรรมการก | class="secondary" style="width:5em" | {art_ind_acc_s} | data-accel-col=6 | {mix_acc_s} | class="secondary" style="width:5em" | {art_ind_acc_p} | data-accel-col=7 | {mix_acc_p} ]=] local adjectival_template_sg = [=[ ! style="width:15%" | ! colspan=2 | เอกพจน์ |- data-wikt-palette-p="blue" ! class="outer" | {gender} ! colspan=2 class="outer" | การผันรูปแบบแข็ง |- data-wikt-palette-p="blue" ! กรรตุการก | colspan=2 data-accel-col=1 | {str_nom_s} |- data-wikt-palette-p="blue" ! สัมพันธการก | colspan=2 data-accel-col=1 | {str_gen_s} |- data-wikt-palette-p="blue" ! สัมปทานการก | colspan=2 data-accel-col=1 | {str_dat_s} |- data-wikt-palette-p="blue" ! กรรมการก | colspan=2 data-accel-col=1 | {str_acc_s} |- data-wikt-palette-p="green" ! class="outer" | ! colspan=2 class="outer" | การผันรูปแบบอ่อน |- data-wikt-palette-p="green" ! กรรตุการก | class="secondary" style="width:5em" | {art_def_nom_s} | data-accel-col=4 | {wk_nom_s} |- data-wikt-palette-p="green" ! สัมพันธการก | class="secondary" style="width:5em" | {art_def_gen_s} | data-accel-col=4 | {wk_gen_s} |- data-wikt-palette-p="green" ! สัมปทานการก | class="secondary" style="width:5em" | {art_def_dat_s} | data-accel-col=4 | {wk_dat_s} |- data-wikt-palette-p="green" ! กรรมการก | class="secondary" style="width:5em" | {art_def_acc_s} | data-accel-col=4 | {wk_acc_s} |- data-wikt-palette-p="orange" ! class="outer" | ! colspan=2 class="outer" | การผันรูปแบบผสม |- data-wikt-palette-p="orange" ! กรรตุการก | class="secondary" style="width:5em" | {art_ind_nom_s} | data-accel-col=6 | {mix_nom_s} |- data-wikt-palette-p="orange" ! สัมพันธการก | class="secondary" style="width:5em" | {art_ind_gen_s} | data-accel-col=6 | {mix_gen_s} |- data-wikt-palette-p="orange" ! สัมปทานการก | class="secondary" style="width:5em" | {art_ind_dat_s} | data-accel-col=6 | {mix_dat_s} |- data-wikt-palette-p="orange" ! กรรมการก | class="secondary" style="width:5em" | {art_ind_acc_s} | data-accel-col=6 | {mix_acc_s} ]=] local adjectival_template_pl = [=[ ! style="width:15%" | ! colspan=2 | พหูพจน์ |- data-wikt-palette-p="blue" ! class="outer" | {gender} ! colspan=2 class="outer" | การผันรูปแบบแข็ง |- data-wikt-palette-p="blue" ! กรรตุการก | colspan=2 data-accel-col=3 | {str_nom_p} |- data-wikt-palette-p="blue" ! สัมพันธการก | colspan=2 data-accel-col=3 | {str_gen_p} |- data-wikt-palette-p="blue" ! สัมปทานการก | colspan=2 data-accel-col=3 | {str_dat_p} |- data-wikt-palette-p="blue" ! กรรมการก | colspan=2 data-accel-col=3 | {str_acc_p} |- data-wikt-palette-p="green" ! class="outer" | ! colspan=2 class="outer" | การผันรูปแบบอ่อน |- data-wikt-palette-p="green" ! กรรตุการก | class="secondary" style="width:5em" | {art_def_nom_p} | data-accel-col=5 | {wk_nom_p} |- data-wikt-palette-p="green" ! สัมพันธการก | class="secondary" style="width:5em" | {art_def_gen_p} | data-accel-col=5 | {wk_gen_p} |- data-wikt-palette-p="green" ! สัมปทานการก | class="secondary" style="width:5em" | {art_def_dat_p} | data-accel-col=5 | {wk_dat_p} |- data-wikt-palette-p="green" ! กรรมการก | class="secondary" style="width:5em" | {art_def_acc_p} | data-accel-col=5 | {wk_acc_p} |- data-wikt-palette-p="orange" ! class="outer" | ! colspan=2 class="outer" | การผันรูปแบบผสม |- data-wikt-palette-p="orange" ! กรรตุการก | class="secondary" style="width:5em" | {art_ind_nom_p} | data-accel-col=7 | {mix_nom_p} |- data-wikt-palette-p="orange" ! สัมพันธการก | class="secondary" style="width:5em" | {art_ind_gen_p} | data-accel-col=7 | {mix_gen_p} |- data-wikt-palette-p="orange" ! สัมปทานการก | class="secondary" style="width:5em" | {art_ind_dat_p} | data-accel-col=7 | {mix_dat_p} |- data-wikt-palette-p="orange" ! กรรมการก | class="secondary" style="width:5em" | {art_ind_acc_p} | data-accel-col=7 | {mix_acc_p} ]=] local function make_table(alternant_multiword_spec) local forms = alternant_multiword_spec.forms if alternant_multiword_spec.title then forms.title = alternant_multiword_spec.title else forms.title = 'การผันรูปของ <i lang="de" class="Latn">' .. forms.lemma .. '</i>' end local annotation = alternant_multiword_spec.annotation if annotation == "" then forms.annotation = "" else forms.annotation = " [<span style=\"font-size: smaller;\">" .. annotation .. "</span>]" end local table_spec if alternant_multiword_spec.props.surname then table_spec = noun_template_surname elseif alternant_multiword_spec.props.langname then table_spec = noun_template_langname elseif alternant_multiword_spec.props.overall_adj then table_spec = alternant_multiword_spec.number == "sg" and adjectival_template_sg or alternant_multiword_spec.number == "pl" and adjectival_template_pl or adjectival_template_both if alternant_multiword_spec.number == "pl" then forms.gender = "" else local genderdesc_parts = {} for _, gender in ipairs(alternant_multiword_spec.genders) do table.insert(genderdesc_parts, gender_spec_to_full_gender[gender.spec]) end forms.gender = "''" .. table.concat(genderdesc_parts, " or ") .. " gender ''" end else local no_indef = alternant_multiword_spec.props.toponym or alternant_multiword_spec.props.article table_spec = alternant_multiword_spec.number == "sg" and (no_indef and noun_template_sg_no_indef or noun_template_sg) or alternant_multiword_spec.number == "pl" and noun_template_pl or (no_indef and noun_template_both_no_indef or noun_template_both) if forms.abl_s ~= "—" or forms.voc_s ~= "—" then forms.abl_voc_clause = M.string_utilities.format(noun_template_abl_voc, forms) else forms.abl_voc_clause = "" end end return M.string_utilities.format(M.inflection_table.make_top { lang = "de", title = "{title}{annotation}", palette = "blue", tall = "yes", } .. table_spec .. M.inflection_table.make_bottom { notes = forms.footnote ~= "" and "{footnote}" or nil, }, forms) end -- Externally callable function to parse and decline a noun given user-specified arguments. Return value is -- ALTERNANT_MULTIWORD_SPEC, an object where the declined forms are in `ALTERNANT_MULTIWORD_SPEC.forms` for each slot. -- If there are no values for a slot, the slot key will be missing. The value for a given slot is a list of objects -- {form=FORM, footnotes=FOOTNOTES}. function export.do_generate_forms(parent_args, pos, from_headword, is_proper, def) local params = { [1] = {required = true, default = "Haus<n,es,^er>"}, pagename = true, json = {type = "boolean"}, -- for bot use } if from_headword or pretend_from_headword then params["head"] = {list = true} params["f"] = {list = true} params["m"] = {list = true} params["n"] = {list = true} params["dim"] = {list = true} params["sg"] = {list = true} params["id"] = {} params["sort"] = {} params["splithyph"] = {type = "boolean"} params["nolinkhead"] = {type = "boolean"} end local args = M.parameters.process(parent_args, params) local arg1 = M.com.add_angle_brackets(args[1]) local pagename = args.pagename or M.headword_data.pagename local function do_parse_indicator_spec(angle_bracket_spec, lemma) return parse_indicator_spec(angle_bracket_spec, lemma, pagename) end local parse_props = { parse_indicator_spec = do_parse_indicator_spec, allow_default_indicator = true, allow_blank_lemma = true, } local alternant_multiword_spec = M.iut.parse_inflected_text(arg1, parse_props) alternant_multiword_spec.args = args alternant_multiword_spec.props = {} alternant_multiword_spec.props.is_proper = is_proper detect_all_indicator_specs(alternant_multiword_spec) local default_number = (alternant_multiword_spec.props.is_proper or alternant_multiword_spec.props.toponym) and "sg" or "both" propagate_properties(alternant_multiword_spec, "number", default_number, "both") -- FIXME, maybe should check that noun genders match adjective genders determine_adjectival_genders(alternant_multiword_spec) determine_noun_status(alternant_multiword_spec) local inflect_props = { skip_slot = function(slot) return skip_slot(alternant_multiword_spec.number, slot) end, slot_list = alternant_multiword_spec.props.surname and surname_slot_list_with_linked or alternant_multiword_spec.props.langname and langname_slot_list_with_linked or alternant_multiword_spec.props.overall_adj and adjectival_slot_list_with_linked or noun_slot_list_with_linked, inflect_word_spec = decline_noun_or_adjective, } M.iut.inflect_multiword_or_alternant_multiword_spec(alternant_multiword_spec, inflect_props) compute_articles(alternant_multiword_spec) compute_headword_genders(alternant_multiword_spec) if not pos then -- Compute part of speech for categories. Fetch the first lemma, or failing that (which would only happen -- if the user overrides the nom_sg and nom_p to be missing) the pagename. If it begins with a hyphen, -- it's a suffix, else a noun (proper nouns get categorized like nouns). local lemmas = export.get_lemmas(alternant_multiword_spec) local first_lemma = #lemmas > 0 and lemmas[1].form or pagename pos = rfind(first_lemma, "^%-") and "ปัจจัย" or "คำนาม" end alternant_multiword_spec.pos = pos compute_categories_and_annotation(alternant_multiword_spec) if from_headword or pretend_from_headword then process_dim_m_f_n(alternant_multiword_spec, args.dim, "^chen", nil, "dim", "ตัวบอกความเล็ก") process_dim_m_f_n(alternant_multiword_spec, args.f, alternant_multiword_spec.forms.f_equiv, "literal default", "f", "เพศหญิงเทียบเท่า") process_dim_m_f_n(alternant_multiword_spec, args.m, alternant_multiword_spec.forms.m_equiv, "literal default", "m", "เพศชายเทียบเท่า") process_dim_m_f_n(alternant_multiword_spec, args.n, alternant_multiword_spec.forms.n_equiv, "literal default", "n", "เพศกลางเทียบเท่า") process_dim_m_f_n(alternant_multiword_spec, args.sg, nil, nil, "sg", "เอกพจน์") end if args.json then return M.JSON.toJSON(alternant_multiword_spec) end return alternant_multiword_spec end -- Entry point for {{de-ndecl}}. Template-callable function to parse and decline a noun given -- user-specified arguments and generate a displayable table of the declined forms. function export.show(frame) local parent_args = frame:getParent().args local alternant_multiword_spec = export.do_generate_forms(parent_args) if type(alternant_multiword_spec) == "string" then -- json=1 specified return alternant_multiword_spec end show_forms(alternant_multiword_spec) return make_table(alternant_multiword_spec) .. M.utilities.format_categories( alternant_multiword_spec.categories, M.lang, nil, nil, force_cat) end return export m89bvjwjmdml5byh1ly9u8eicwdtlyn มอดูล:nyms 828 240768 5775203 5686047 2026-10-14T04:59:43Z Octahedron80 267 5775203 Scribunto text/plain local export = {} local debug_track_module = "Module:debug/track" local decorations_module = "Module:decorations" local labels_module = "Module:labels" local links_module = "Module:links" local parameter_utilities_module = "Module:parameter utilities" local parse_utilities_module = "Module:parse utilities" local function track(page) return require(debug_track_module)("nyms/" .. page) end local function wrap_span(text, lang, sc) return '<span class="' .. sc .. '" lang="' .. lang .. '">' .. text .. '</span>' end local function term_already_linked(term) -- optimization to avoid unnecessarily loading [[Module:parse utilities]] return term:find("[<{]") and require(parse_utilities_module).term_already_linked(term) end function export.nyms(frame) local parent_args = frame:getParent().args -- FIXME: Temporary error message and tracking. for arg, _ in pairs(parent_args) do if type(arg) == "string" and arg:find("^lb[0-9]*$") then local llarg = arg:gsub("^lb", "ll") error(("%s= is deprecated; use %s= instead, per the documentation"):format(arg, llarg)) end if arg == "q" or arg == "qq" then track(arg) end if type(arg) == "string" and arg:find("^tag[0-9]*$") then local larg = arg:gsub("^tag", "l") local llarg = arg:gsub("^tag", "ll") error(("Use %s= (on the left) or %s= (on the right) instead of %s="):format(larg, llarg, arg)) end end local params = { [1] = {required = true, type = "language", default = "und"}, [2] = {list = true, allow_holes = true, required = true}, } local m_param_utils = require(parameter_utilities_module) local param_mods = m_param_utils.construct_param_mods { {group = {"link", "ref", "l"}}, -- For compatibility, we don't distinguish q= from q1= and qq= from q1=. FIXME: Maybe we should change this. {group = "q", separate_no_index = false}, {param = "lb", deprecated = true}, } local special_separators = mw.clone(m_param_utils.default_special_separators) special_separators["<"] = " < " local items, args = m_param_utils.parse_list_with_inline_modifiers_and_separate_params { params = params, param_mods = param_mods, raw_args = parent_args, termarg = 2, parse_lang_prefix = true, track_module = "nyms", lang = 1, special_separators = special_separators, sc = "sc.default", -- because we show them ourselves below so e.g. we can handle already-linked terms no_show_decorations = true, } local nym_type = frame.args[1] local nym_type_class = string.gsub(nym_type, "%s", "-") local lang = args[1] local langcode = lang:getCode() local data = { lang = lang, items = items, sc = args.sc.default, l = args.l.default, ll = args.ll.default, q = args.q.default, qq = args.qq.default, } local parts = {} local thesaurus_parts = {} for i, item in ipairs(data.items) do if item.lb then error("Inline modifier <lb:...> is deprecated; use <ll:...> per the documentation") end local explicit_item_lang = item.lang item.lang = item.lang or data.lang item.sc = item.sc or data.sc local text local is_thesaurus if item.term and item.term:find("^อรรถาภิธาน:") then is_thesaurus = true for k, _ in pairs(item) do if m_param_utils.item_key_is_property(k) and k ~= "lang" and k ~= "sc" and k ~= "q" and k ~= "qq" and k ~= "l" and k ~= "ll" and k ~= "refs" then error(("You cannot use most named parameters and inline modifiers with Thesaurus links, but saw %s%s= or its equivalent inline modifier <%s:...>"):format( k, item.itemno, k)) end end local term = item.term:match("^อรรถาภิธาน:(.*)$") -- Chop off fragment term = term:match("^(.-)#.*$") or term local lang = item.lang local sccode = (item.sc or lang:findBestScript(term)):getCode() -- FIXME: I assume it's better to include full-language codes in the CSS rather than etym-language codes, -- which are generally specific to Wiktionary. However, we should probably instead be using the functions -- from [[Module:script utilities]] in preference to rolling our own. text = "[[" .. item.term .. "#" .. lang:getFullName() .. "|อรรถาภิธาน:" .. wrap_span(term, lang:getFullCode(), sccode) .. "]]" else if thesaurus_parts[1] then error("Links to the Thesaurus must follow all non-Thesaurus links") end local raw_term = item.alt or item.term if raw_term and term_already_linked(raw_term) then text = raw_term else text = require(links_module).full_link(item) end end local qq = item.qq -- If a separate language code was given for the term, display the language name as a right qualifier. -- Otherwise it may not be obvious that the term is in a separate language (e.g. if the main language is 'zh' -- and the term language is a Chinese lect such as Min Nan). But don't do this for Translingual terms, which -- are often added to the list of English and other-language terms. if explicit_item_lang then local explicit_code = explicit_item_lang:getCode() if explicit_code ~= langcode and explicit_code ~= "mul" then qq = mw.clone(qq) or {} table.insert(qq, 1, explicit_item_lang:getCanonicalName()) end end if item.q and item.q[1] or qq and qq[1] or item.l and item.l[1] or item.ll and item.ll[1] or item.refs and item.refs[1] then text = require(decorations_module).format_decorations { lang = item.lang, text = text, q = item.q, qq = qq, l = item.l, ll = item.ll, refs = item.refs, } end local insert_place = is_thesaurus and thesaurus_parts or parts -- Don't include the separator if this is the first item of this class that we're inserting. table.insert(insert_place, insert_place[1] and item.separator or "") table.insert(insert_place, text) end local text = table.concat(parts) local thesaurus_text = table.concat(thesaurus_parts) local caption = "<span style=\"font-size: smaller\">" .. mw.getContentLanguage():ucfirst(nym_type) .. ((#items > 1 or thesaurus_text ~= "") and "s" or "") .. ":</span> " text = caption .. text local function decoration_error_if_no_terms() if not parts[1] then error("Cannot specify overall decorations if no non-Thesaurus terms given") end end if data.q and data.q[1] or data.qq and data.qq[1] or data.l and data.l[1] then decoration_error_if_no_terms() text = require(decorations_module).format_decorations { lang = data.lang, text = text, q = data.q, qq = data.qq, l = data.l, -- ll handled specially for compatibility's sake } end if data.ll and data.ll[1] then decoration_error_if_no_terms() text = text .. " &mdash; " .. require(labels_module).show_labels { lang = data.lang, labels = data.ll, nocat = true, open = false, close = false, no_track_already_seen = true, } end if thesaurus_text ~= "" then local thesaurus_intro = parts[1] and "; ''ดูเพิ่ม'' " or "''ดูที่'' " text = text .. thesaurus_intro .. thesaurus_text end return "<span class=\"nyms " .. nym_type_class .. "\">" .. text .. "</span>" end return export 8hvx4e5t87w3k3mzz2gdzoun227st69 5775204 5775203 2026-10-14T05:01:45Z Octahedron80 267 5775204 Scribunto text/plain local export = {} local debug_track_module = "Module:debug/track" local decorations_module = "Module:decorations" local labels_module = "Module:labels" local links_module = "Module:links" local parameter_utilities_module = "Module:parameter utilities" local parse_utilities_module = "Module:parse utilities" local function track(page) return require(debug_track_module)("nyms/" .. page) end local function wrap_span(text, lang, sc) return '<span class="' .. sc .. '" lang="' .. lang .. '">' .. text .. '</span>' end local function term_already_linked(term) -- optimization to avoid unnecessarily loading [[Module:parse utilities]] return term:find("[<{]") and require(parse_utilities_module).term_already_linked(term) end function export.nyms(frame) local parent_args = frame:getParent().args -- FIXME: Temporary error message and tracking. for arg, _ in pairs(parent_args) do if type(arg) == "string" and arg:find("^lb[0-9]*$") then local llarg = arg:gsub("^lb", "ll") error(("%s= is deprecated; use %s= instead, per the documentation"):format(arg, llarg)) end if arg == "q" or arg == "qq" then track(arg) end if type(arg) == "string" and arg:find("^tag[0-9]*$") then local larg = arg:gsub("^tag", "l") local llarg = arg:gsub("^tag", "ll") error(("Use %s= (on the left) or %s= (on the right) instead of %s="):format(larg, llarg, arg)) end end local params = { [1] = {required = true, type = "language", default = "und"}, [2] = {list = true, allow_holes = true, required = true}, } local m_param_utils = require(parameter_utilities_module) local param_mods = m_param_utils.construct_param_mods { {group = {"link", "ref", "l"}}, -- For compatibility, we don't distinguish q= from q1= and qq= from q1=. FIXME: Maybe we should change this. {group = "q", separate_no_index = false}, {param = "lb", deprecated = true}, } local special_separators = mw.clone(m_param_utils.default_special_separators) special_separators["<"] = " < " local items, args = m_param_utils.parse_list_with_inline_modifiers_and_separate_params { params = params, param_mods = param_mods, raw_args = parent_args, termarg = 2, parse_lang_prefix = true, track_module = "nyms", lang = 1, special_separators = special_separators, sc = "sc.default", -- because we show them ourselves below so e.g. we can handle already-linked terms no_show_decorations = true, } local nym_type = frame.args[1] local nym_type_class = string.gsub(nym_type, "%s", "-") local lang = args[1] local langcode = lang:getCode() local data = { lang = lang, items = items, sc = args.sc.default, l = args.l.default, ll = args.ll.default, q = args.q.default, qq = args.qq.default, } local parts = {} local thesaurus_parts = {} for i, item in ipairs(data.items) do if item.lb then error("Inline modifier <lb:...> is deprecated; use <ll:...> per the documentation") end local explicit_item_lang = item.lang item.lang = item.lang or data.lang item.sc = item.sc or data.sc local text local is_thesaurus if item.term and item.term:find("^อรรถาภิธาน:") then is_thesaurus = true for k, _ in pairs(item) do if m_param_utils.item_key_is_property(k) and k ~= "lang" and k ~= "sc" and k ~= "q" and k ~= "qq" and k ~= "l" and k ~= "ll" and k ~= "refs" then error(("You cannot use most named parameters and inline modifiers with Thesaurus links, but saw %s%s= or its equivalent inline modifier <%s:...>"):format( k, item.itemno, k)) end end local term = item.term:match("^อรรถาภิธาน:(.*)$") -- Chop off fragment term = term:match("^(.-)#.*$") or term local lang = item.lang local sccode = (item.sc or lang:findBestScript(term)):getCode() -- FIXME: I assume it's better to include full-language codes in the CSS rather than etym-language codes, -- which are generally specific to Wiktionary. However, we should probably instead be using the functions -- from [[Module:script utilities]] in preference to rolling our own. text = "[[" .. item.term .. "#" .. lang:getFullName() .. "|อรรถาภิธาน:" .. wrap_span(term, lang:getFullCode(), sccode) .. "]]" else if thesaurus_parts[1] then error("Links to the Thesaurus must follow all non-Thesaurus links") end local raw_term = item.alt or item.term if raw_term and term_already_linked(raw_term) then text = raw_term else text = require(links_module).full_link(item) end end local qq = item.qq -- If a separate language code was given for the term, display the language name as a right qualifier. -- Otherwise it may not be obvious that the term is in a separate language (e.g. if the main language is 'zh' -- and the term language is a Chinese lect such as Min Nan). But don't do this for Translingual terms, which -- are often added to the list of English and other-language terms. if explicit_item_lang then local explicit_code = explicit_item_lang:getCode() if explicit_code ~= langcode and explicit_code ~= "mul" then qq = mw.clone(qq) or {} table.insert(qq, 1, explicit_item_lang:getCanonicalName()) end end if item.q and item.q[1] or qq and qq[1] or item.l and item.l[1] or item.ll and item.ll[1] or item.refs and item.refs[1] then text = require(decorations_module).format_decorations { lang = item.lang, text = text, q = item.q, qq = qq, l = item.l, ll = item.ll, refs = item.refs, } end local insert_place = is_thesaurus and thesaurus_parts or parts -- Don't include the separator if this is the first item of this class that we're inserting. table.insert(insert_place, insert_place[1] and item.separator or "") table.insert(insert_place, text) end local text = table.concat(parts) local thesaurus_text = table.concat(thesaurus_parts) local caption = "<span style=\"font-size: smaller\">" .. mw.getContentLanguage():ucfirst(nym_type) .. --[[((#items > 1 or thesaurus_text ~= "") and "s" or "") ..]] ":</span> " text = caption .. text local function decoration_error_if_no_terms() if not parts[1] then error("Cannot specify overall decorations if no non-Thesaurus terms given") end end if data.q and data.q[1] or data.qq and data.qq[1] or data.l and data.l[1] then decoration_error_if_no_terms() text = require(decorations_module).format_decorations { lang = data.lang, text = text, q = data.q, qq = data.qq, l = data.l, -- ll handled specially for compatibility's sake } end if data.ll and data.ll[1] then decoration_error_if_no_terms() text = text .. " &mdash; " .. require(labels_module).show_labels { lang = data.lang, labels = data.ll, nocat = true, open = false, close = false, no_track_already_seen = true, } end if thesaurus_text ~= "" then local thesaurus_intro = parts[1] and "; ''ดูเพิ่ม'' " or "''ดูที่'' " text = text .. thesaurus_intro .. thesaurus_text end return "<span class=\"nyms " .. nym_type_class .. "\">" .. text .. "</span>" end return export 1zr0d4ogugro9cg6kuqvtr4kwls54gl açúcar 0 240772 5775280 2187918 2026-10-14T10:10:03Z Octahedron80 267 5775280 wikitext text/x-wiki == ภาษาโปรตุเกส == {{wikipedia|lang=pt}} === รูปแบบอื่น === * {{alt|pt|açucre||ล้าสมัย|ภาษาถิ่น}} === รากศัพท์ === {{bor+|pt|ar|سكر|السُكَّر}},<ref>{{R:pt:Infopédia}}</ref><ref>{{R:pt:Priberam}}</ref> จาก{{der|pt|fa|شکر|tr=šakar}}, จาก{{der|pt|pal|𐭱𐭪𐭥}}, จาก{{der|pt|sa|शर्करा|t=น้ำตาลทราย, น้ำตาลกรวด}}, เดิมทีหมายถึง "กรวด", จาก{{der|pt|ine-pro|*ḱorkeh₂|t=กรวด, ก้อนหิน}} === การออกเสียง === {{pt-IPA|pt=+,açúcār|br=+}} * {{IPA|pt|/ɐˈsu.kɐɹ/|a=Rural Central Brazil}} * {{hyphenation|pt|a|çú|car}} * {{audio|pt|Pt-açúcar.ogg|a=Portugal}} === คำนาม === {{pt-noun|m}} # {{lb|pt|uncountable}} [[น้ำตาล]] # {{lb|pt|countable|chemistry}} น้ำตาล ==== ลูกคำ ==== {{der4|pt|açucarzinho|açúcar mascavo|açúcar mascavado|açúcar em caixa|açúcar-cande|açucarar|açucarado|cana-de-açúcar}} ==== คำสืบทอด ==== * {{desc|bor=1|kgp|asuka}} * {{desc|bor=1|pap|suku}} === อ้างอิง === <references/> 47md0tjizsfgrrpat43owjtcf3q04ox filter 0 241085 5775169 2000346 2026-10-13T14:52:18Z Alifshinobi 397 /* คำนาม */ 5775169 wikitext text/x-wiki {{also/auto}} == ภาษาอังกฤษ == === การออกเสียง === * {{IPA|en|/ˈfɪltə/|a=RP}} * {{IPA|en|/ˈfɪltɚ/|[ˈfɪɫɾɚ]|a=GenAm}} * {{audio|en|en-us-filter.ogg|a=US}} * {{rhymes|ɪltə(r)|lang=en}} === คำนาม === {{en-noun}} # [[อุปกรณ์]][[กรอง]][[ของเหลว]][[หรือ]][[สารละลาย]][[เป็นต้น]], [[ตัวกรอง]] # อุปกรณ์กรอง[[สัญญาณ]] # [[กลไก]][[อื่น ๆ]] [[ที่]][[ทำ]][[หน้าที่]][[คล้าย]][[การกรอง]] [[เช่น]][[ตัว]]กรอง[[อีเมล]] === คำกริยา === {{en-verb}} # กรอง extq9u1wqrwq1dwbhmvttyrfnv9iint teal 0 241115 5775240 1549746 2026-10-14T08:49:56Z Ziv 18038 ([[c:GR|GR]]) [[File:Sarcelleh 1.jpg]] → [[File:Sarcelle d'hiver (Anas Crecca).jpg]] [[c:COM:FR#FR2|Criterion 2]] · More precise 5775240 wikitext text/x-wiki == ภาษาอังกฤษ == [[Image:Sarcelle d'hiver (Anas Crecca).jpg|thumb|teal (1)]] === รากศัพท์ === จาก{{etyl|enm|en}} {{m|enm|tele}} === การออกเสียง === * {{IPA|en|/ˈtɪːl/}} === คำนาม === {{en-noun|~}} # {{lb|en|countable}} [[นกเป็ดน้ำ]] # {{lb|en|countable|uncountable}} [[สี]][[น้ำเงิน]]อม[[เขียว]]เข้ม #: {{color panel|008888}} === คำคุณศัพท์ === {{en-adj}} # มี[[สี]][[น้ำเงิน]]อม[[เขียว]]เข้ม {{topics|en|สี}} gnolurwfbyiay5phcgai8ou6o5c37k8 Chine 0 255161 5775281 3221870 2026-10-14T10:10:41Z Octahedron80 267 5775281 wikitext text/x-wiki {{also/auto}} == ภาษาฝรั่งเศส == === รากศัพท์ === จาก{{der|fr|fa|چینی|tr=čini||ชาวจีน}}, เทียบ {{m|fa|چین|tr=čin||จีน}} กับปัจจัย {{m|fa|ی|ـی|tr=-i}}, จาก{{der|fr|pal|𐭰𐭩𐭭||จีน}}, จาก{{der|fr|sa|चीन}} === การออกเสียง === * {{fr-IPA}} === คำวิสามานยนาม === {{fr-proper noun|f}} # [[จีน]] ==== คำที่เกี่ยวข้อง ==== * {{l|fr|chinois}}, {{l|fr|Chinois}} === การสลับอักษร === * {{l|fr|chien}}, {{l|fr|niche}}, {{l|fr|niché}} qe547sj6ej7fm39mdzwgu32rtdeyjkm มอดูล:IPA/tracking 828 270317 5775270 1107759 2026-10-14T10:00:47Z Octahedron80 267 5775270 Scribunto text/plain local export = {} --[[ symb is what is tracked. It can be a literal symbol or a Lua pattern. If it is a table, tracking is added for any of the symbols in the list. cat is the subtemplate that is added to the default path "IPA/" + language code + "/". ]] local U = require("Module:string/char") local syllabic = U(0x329) -- The validity of this table is checked by documentation function -- in [[Module:User:Erutuon/sandbox]]. export.tracking = { en = { { symb = "iə", cat = "ambig", }, { symb = { "ɪi", "ʊu", "ɪj", "ʊw" }, cat = "eeoo", }, { symb = { "r" }, cat = "plain r", }, }, cs = { { symb = "[mnrl]" .. syllabic, cat = "syllabic-consonant", }, }, ps = { { symb = "ɤ", cat = "Pashto", }, }, fa = { { symb = "ʔ", cat = "glottal-stop", }, }, { { symb = "", cat = "", }, }, } function export.run_tracking(IPA, lang) if not IPA or IPA == "" then return end lang = lang:getCode() if not export.tracking[lang] then return end for i, arguments in ipairs(export.tracking[lang]) do local symbols = arguments.symb local category = arguments.cat if type(symbols) == "string" then symbols = { symbols } end for _, symbol in pairs(symbols) do if mw.ustring.find(IPA, symbol) then require("Module:debug/track")("IPA/" .. lang .. "/" .. category) end end end end return export se04v8uivjuynpeoe4fnp0a4t96kccz มอดูล:inflection utilities 828 287871 5775182 5770990 2026-10-14T03:28:55Z Octahedron80 267 5775182 Scribunto text/plain local export = {} local m_links = require("Module:links") local m_str_utils = require("Module:string utilities") local m_table = require("Module:table") local put = require("Module:parse utilities") local headword_data_module = "Module:headword/data" local script_utilities_module = "Module:script utilities" local pages_module = "Module:pages" local template_parser_module = "Module:template parser" local is_callable = require("Module:fun").is_callable local split = m_str_utils.split local ufind = m_str_utils.find local usub = m_str_utils.sub local ucfirst = m_str_utils.ucfirst local unpack = unpack or table.unpack -- Lua 5.2 compatibility local dump = mw.dumpObject local new_title = mw.title.new local insert = table.insert local concat = table.concat local insert_if_not = m_table.insertIfNot local shallow_copy = m_table.shallowCopy local function track(page) require("Module:debug/track")("inflection utilities/" .. page) return true end local footnote_abbrevs = { ["a"] = "archaic", ["c"] = "colloquial", ["d"] = "dialectal", ["fp"] = "folk-poetic", ["l"] = "literary", ["lc"] = "low colloquial", ["p"] = "poetic", ["pej"] = "pejorative", ["r"] = "rare", } --[==[ intro: The following code is used in building up the inflection of terms in inflected languages, where a term can potentially consist of several inflected words, each surrounded by fixed text, and a given slot (e.g. accusative singular) of a given word can potentially consist of multiple possible inflected forms. In addition, each form may be associated with a manual transliteration and/or a list of footnotes (or qualifiers, in the case of headword lines). The following terminology is helpful to understand: * A '''term''' is a word or multiword expression that can be inflected. A multiword term may in turn consist of several single-word inflected terms with surrounding fixed text. A term belongs to a particular '''part of speech''' (e.g. noun, verb, adjective, etc.). * An '''inflection dimension''' is a particular dimension over which a term may be inflected, such as case, number, gender, person, tense, mood, voice, aspect, etc. * The '''lemma''' is the particular form of a term under which the term is entered into a dictionary. For example, for verbs, it is most commonly the infinitive, but this differs for some languages: e.g. Latin, Greek and Bulgarian use the first-person singular present indicative (active voice in the case of Latin and Greek); Sanskrit and Macedonian use the third-person singular present indicative (active voice in the case of Sanskrit); Hebrew and Arabic use the third-person singular masculine past (aka "perfect"); etc. For nouns, the lemma form is most commonly the nominative singular, but e.g. for Old French it is the objective singular and for Sanskrit it is the root. * A '''slot''' is a particular combination of inflection dimensions. An example might be "accusative plural" for a noun, or "first-person singular present indicative" for a verb. Slots are named in a language-specific fashion. For example, the slot "accusative plural" might have a name `accpl`, while "first-person singular present indicative" might be variously named `pres1s`, `pres_ind_1_sg`, etc. Each slot is filled with zero or more '''forms'''. * A '''form''' is a particular inflection of a slot for a particular term. Note that a given slot may (and often does) have more than one associated form; these different forms are termed '''variants'''. An example is {{m+|de|Bug||bow (of a ship)}}, which has two genitive singular forms ''Buges'' and ''Bugs''; two plural forms in all cases, e.g. nominative plural ''Buge'' and ''Büge''; and two dative singular forms ''Bug'' and rare/archaic ''Buge''. The form variants for a given slot are ordered, and generally should have the more common and/or preferred variants first, along with rare, archaic or obsolete variants last (if they are included at all). * Forms are described using '''form objects''', which are Lua objects taking the form `{form="``form_value``", translit="``manual_translit``", footnotes={"``footnote``", "``footnote``", ...}}`. (Additional '''metadata''' may be present in a form object, although the support for preserving such metadata when transformations are applied to form objects isn't yet complete.) ``form_value`` is a '''form value''' specifying the value of the form itself in the term's script. ``manual_translit`` specifies optional manual transliteration for the form, in case (a) the form value is in a different script; and (b) either the form's automatic transliteration is incorrect and needs to be overridden, or the language of the term has no automatic transliteration (e.g. in the case of Persian and Hebrew). ``footnote`` is a footnote to be attached to the form in question, and should be e.g. {"[archaic]"} or {"[only in the meaning 'to succeed (an officeholder)']"}, i.e. the string must be surrounded by brackets and should begin with a lowercase letter and not end in a period/full stop. When such footnotes are converted to actual footnotes in a table of inflected forms, the brackets will be removed, the first letter will be capitalized and a period/full stop will be added to the end. (However, when such footnotes are used as qualifiers in headword lines, only the brackets will be removed, with no capitalization or final period.) Note that only ``form_value`` is mandatory. * A list of zero or more form objects is termed a '''form object list''', or usually just a '''form list'''. Such lists are ordered and go into form tables (see below). * A '''form table''' is a Lua table (i.e. a dictionary) describing all the possible inflections of a given term. The keys in such a table are slots (strings) and the values are form lists. '''NOTE:''' All inflection code assumes and maintains the invariant that no two slots, and no two forms in a single slot, share the same form object (by reference, i.e. the Lua object describing a form object should never be shared in two places). This allows for safely side-effecting form objects in certain sorts of operations. This same invariant necessarily applies to the Lua list objects containing the form objects, but does '''NOT''' apply to metadata inside of form objects. In particular, a list of footnotes may well be shared among different form objects. This means it is '''NOT''' safe to side-effect such lists, and in fact no code in this module that manipulates footnote lists will ever side-effect such lists; they are treated as immutable. * Some functions, to save memory, accept and work with abbreviated forms of form objects and/or form lists. Specifically, an '''abbreviated form object''' is either a form object or a string, the latter corresponding to a form object whose form value is the string and all other properties are nil. Similarly, an '''abbreviated form list''' is either a single abbreviated form object or a list of such objects, i.e. any of a string, form object or list of strings and/or form objects. Functions that do not accept such abbreviated structures may be said to insist on being passed form objects in '''full form''', or form lists in '''full list form'''. * Each slot is associated with an '''accelerator tag set''', which is a list of inflection tags that are used when generating an accelerator entry for the forms in the slot (see [[WT:ACCEL]]). For example, the first singular present indicative of a verb might have slot name `pres_1sg` and corresponding accelerator tag set `1|s|pres|ind`. As shown, the accelerator tag set is a string consisting of inflection tags (as used in {{tl|inflection of}}) separated by `|`. Despite the terminology ''tag set'', the tags in a tag set are ordered, although the same tag should never occur twice. * Some inflected terms are '''multiword''', i.e. they consist of multiple '''words''', where each word is generally separated by spaces or sometimes hyphens. In such a term, some of the words inflect, while others remain fixed. Words that inflect are termed '''inflecting words''' (or more correctly '''inflecting parts''', since in some circumstances, parts of a word can inflect). The '''fixed text''' is all the parts of a multiword term that do not inflect. * The descriptor that describes how a given term inflects is called an '''inflection spec''', and consists of the lemma form of the term itself, annotated with an '''angle bracket spec''' after each inflecting word. As the name implies, an angle bracket spec is surrounded by angle brackets (`<...>`). A simple example is {{m+|de|Feder||feather}}, whose inflection spec looks like `Feder<f>`, where `f` specifies the feminine gender. In this case, although there are several properties that could be specified between angle brackets, all except the gender are optional and have been left out, indicating that defaults should be used. Another example is {{m+|de|Baske|Basque person}}, whose inflection spec looks like `Baske<m.weak>`, where `m` specifies the masculine gender and `weak` specifies the weak inflection. Note that individual components of an angle bracket spec like `m` and `weak` are termed '''indicators''' and are separated by periods/full stops. A slightly more complex example is {{m+|de|Zeitgeist||zeitgeist}}, whose inflection spec looks like `Zeitgeist<m,es:s,er>` and which specifies three things in a single '''compound indicator''': `m` (the masculine gender); `es:s` (the genitive singular, which can end in either ''-es'' or ''-s''); and `er` (the nomininative plural, which ends in ''-er''). * If there are several inflecting words in a term, each one will be followed by its own angle bracket spec. An example is {{m+|de|schwarzes Loch||black hole}}, whose inflection spec looks like `schwarzes<+> Loch<n,es:s,^er>`. Here, the adjective ''schwarzes'' (the nominative neuter singular of {{m|de|schwarz||black}}) is followed by the angle bracket spec `<+>` specifying that it inflects as an adjective, and the noun ''Loch'' has the angle bracket spec `<n,es:s,^er>`, indicating (similarly to the above example) that it is neuter, has a genitive singular in either ''-es'' or ''-s'', and has a nominative plural in ''-er'' with umlaut, hence ''Löcher'' (the `^` specifies that the form requires umlaut). * Sometimes a given term has multiple ways of inflecting that differ in ways that can't be specified using a single angle bracket spec. This is supported using '''alternants''', which are specified using double parentheses. (This is so that terms that themselves contain parentheses can be specified without interference.) An example is {{m+|uk|русин||Rusyn}}, which can be stressed either as ''ру́син'' (stress on the first syllable and following accent paradigm ''a'', hence genitive singular ''ру́сина'') or ''руси́н'' (stress on the second syllable and following accent paradigm ''b'', hence genitive singular ''русина́''; note how the stress moves onto the ending, in accordance with the accent paradigm). This is specified using `((ру́син<pr>,руси́н<b.pr>))`, i.e. each separate the alternants with a comma and surround them with double parentheses. (Here, `pr` means that the terms belong to the personal animacy class, and `b` specifies the accent paradigm; paradigm ''a'' is the default and hence is omitted.) * Note that occasionally, parts of a single space-delimited word can inflect separately. An example is {{m+|la|rōsmarīnus||rosemary}}, which is a compound of {{m+|la|rōs||dew}} and {{m|la|marīnus||marine, of the sea}}. In this compound, both parts of the compound can inflect separately; hence genitive singular ''rōrismarīnī'', accusative singular ''rōremmarīnum'', etc. Alternatively, only the second part inflects; hence genitive singular ''rōsmarīnī'', accusative singular ''rōsmarīnum'', etc. This is specified as `((rōs/rōr<3.M>marīnus<2>,rōsmarīnus<2>))`. Here, the term {{m|la|rōs}} by itself would have inflection spec `rōs/rōr<3.M>` (indicating that it is third declension masculine with a non-nominative-singular stem ''rōr-'') and the term {{m|la|marīnus}} would have inflection spec `<2>` (indicating that it is second declension; the masculine gender is inferred from the ''-us'' ending). When combined in a single inflection spec, the doubly-inflecting alternant is written `rōs/rōr<3.M>marīnus<2>`, with each inflecting part followed by its corresponding angle bracket spec, and the singly-inflecting alternant is written `rōsmarīnus<2>`. As this example shows, the two alternants need not correspond in how many inflecting parts there are. It should also be noted that fixed text can surround an alternant and it is even possible to supply multiple alternants in a single inflection spec (e.g. if the term has two words in it and each word requires an alternant to inflect). * The result of parsing a single angle bracket spec is stored into a '''word spec'''. The structure of a word spec is fairly arbitrary and is determined by the user-written `parse_indicator_spec` function, but always contains a form table under the `forms` key that is populated during inflection (see below). A parameter or local variable that holds a word spec is conventionally named `base` for historical reasons. Word specs are grouped together into a structure termed a '''multiword spec''', which describes one or more word specs along with the fixed text in between and around the inflected words. Multiword specs are in turn grouped into structures termed '''alternant specs''', indicating the distinct alternants and the words in each alternant. Finally, multiword specs and alternant specs are grouped into an '''alternant multiword spec''', which is the top-level object describing an inflection spec. Each of these different specs has a form table in it stored in the `forms` key that is populated during the inflection process and contains the form objects that specify the inflections of this part of the full multiword term. (It should be noted that the term '''spec''' is overloaded to mean two different things: the user-specified descriptor that specifies the lemma form of the term and associated inflection, and the associated internal Lua object that encapsulates all information derived from the descriptor, along with later-generated information on how to inflect the term(s) being described.) * Among these various "spec" structures, the two most important are the top-level alternant multiword spec and the bottom-level word spec or "base". You will rarely find it necessary to manipulate the intermediate structures or concern yourself with the details of their formation. * The term ''form'' is unfortunately overloaded in various modules to mean several things. In particular, for historical reasons, the form value inside of a form object is stored using the key `form`; the form table inside of an alternant multiword spec, a word spec (or "base") and the intermediate structures is stored using the key `forms`; and the accelerator tag set is internally referred to in [[WT:ACCEL]] as a "form". To avoid confusion, the following conventions are followed in code in this module, and should be followed for code in invoking modules as well: *# Functions that accept form objects often name the relevant parameter `form` (if a single form object is required) or `forms` (if a list of form objects, aka form list, is required). *# Functions that accept abbreviated form objects should (but don't always) indicate this by naming the parameter `abform` (for a single abbreviated form object) or `abforms` (for an abbreviated form list). *# Functions that accept a form value (the native-script string portion of a form object, stored for historical reasons in the `.form` property) should '''not''' call such a parameter `form`, but instead use something that makes clear that a form value is required, such as `formval` or sometimes just `val`. *# Similarly, functions that accept a form table should '''not''' call such a parameter `forms` (although for historical reasons the form table in an alternant multiword spec is stored in the field `forms`). Instead, use `formtable` or `formtab`, or similar name that makes clear that the value is a form table (i.e. a map from slot to form list). ====Footnote handling==== Each form can have one or more attached footnotes. The form of a footnote as specified by the user and stored in form values is e.g. {"[archaic]"} or {"[only in the meaning 'to succeed (an officeholder)']"}, i.e. the string must be surrounded by brackets and should begin with a lowercase letter and not end in a period/full stop. When such footnotes are converted to actual footnotes in a table of inflected forms, the brackets will be removed, the first letter will be capitalized and a period/full stop will be added to the end. (However, when such footnotes are used as qualifiers in headword lines, only the brackets will be removed, with no capitalization or final period.) When merging two forms into one, such as when concatenating the form objects of two inflected words in a multiword term or deduplicating form objects sharing the same form value during `show_forms()`, the footnotes are generally combined as well. This means that if one form object has footnotes and the other doesn't, the resulting form object inherits the footnotes of the object that has them, and if both form objects have footnotes, the resulting form object gets all footnotes from both source form objects, with duplicates removed. However, when inserting a form into a form table slot that already has a form whose form value and translit are identical to the new form, the behavior is different. In under normal circumstances the footnotes of the new form are ''not'' incorporated into those of the existing form (if any), but are simply dropped. To understand why this makes sense, consider a term that has two possible forms of its lemma (e.g. two forms differing in stress or in vowel length), where the second form is archaic, rare, colloquial or the like, and has an attached footnote indicating this. An example of this is {{m+|ru|кожух||sheepskin coat; bullet shell}}, where the form ''кожу́х'' with accent pattern ''b'' is more common overall but the form ''ко́жух'' with accent pattern ''c(1)'' is more common among professionals. On first glance, this could be indicated using `((кожу́х&lt;b>,ко́жух<c(1).[professional usage only]>))`. But some forms of these two declensions are the same (in particular, the genitive, dative, instrumental and prepositional plural). If for these slots, the footnotes of the duplicate forms were combined (i.e. the footnotes of the second declension pattern were added to the already-existing form taken from the first declension pattern), these forms would wrongly be labeled as ''professional usage only''. For this reason, it makes more sense to drop the footnotes of the second form when deduplicating. The same sort of behavior makes sense when a single lemma can have two different declensions, the second of which requires a footnote and where some forms in the two declensions are shared. An example of this is {{m+|uk|окови́та||strong, high-quality liquor}}, which can be inflected adjectivally or (rarely) nominally. This would be indicated as `((окови́та<sg.+>,окови́та<sg.[rare]>))` where the `+` indicates adjectival declension and the `sg` indicates that this term only exists in the singular. Here, the two declensions differ in the genitive, dative/locative and vocative (respectively, adjectival ''окови́тої'', ''окови́тій'', ''окови́та'' vs. nominal ''окови́ти'', ''окови́ті'', ''окови́то'') but are the same in the accusative (''окови́ту'') and instrumental (''окови́тою''). Again, dropping the footnotes of the second form when deduplicating is correct and including them would be wrong. This behavior can be changed by attaching a '''footnote modifier''' to the footnote associated the second form. A footnote modifier is a symbol attached to the beginning of a footnote, directly following the opening bracket. The following modifiers are currently recognized: * `!` or `+`: If placed on a footnote of the second form, combine that footnote with those of the first form (if any) rather than dropping it. * `*`: If placed on a footnote of the first form, drop that footnote when merging a second form with any footnotes. An example where the `*` modifier makes sense is a modification of the above example with {{m+|ru|кожух}}. If we notated it as `((кожу́х<b.[more common among laymen]>,ко́жух<c(1).[more common among professionals]>))`, the shared forms would wrongly have the footnote ''more common among laymen'' when in fact they are the only possible forms. If instead we used `((кожу́х<b.[*more common among laymen]>,ко́жух<c(1).[more common among professionals]>))`, the shared forms would correctly have no footnote. ]==] local function extract_footnote_modifiers(footnote) local footnote_mods, footnote_without_mods = footnote:match("^%[([!*+]?)(.*)%]$") if not footnote_mods then error("Saw footnote '" .. footnote .. "' not surrounded by brackets") end return footnote_mods, footnote_without_mods end function export.deduplicate_footnotes(existing, new) if not new then return existing end -- Check to see if there are existing footnotes with *; if so, remove them. if existing then local any_footnotes_with_asterisk = false for _, footnote in ipairs(existing) do local footnote_mods, _ = extract_footnote_modifiers(footnote) if footnote_mods:find("%*") then any_footnotes_with_asterisk = true break end end if any_footnotes_with_asterisk then local filtered_footnotes = {} for _, footnote in ipairs(existing) do local footnote_mods, _ = extract_footnote_modifiers(footnote) if not footnote_mods:find("%*") then insert(filtered_footnotes, footnote) end end if #filtered_footnotes > 0 then existing = filtered_footnotes else existing = nil end end end -- The behavior here has changed; track cases where the old behavior might -- be needed by adding ! to the footnote. track("combining-footnotes") local any_footnotes_with_bang = false for _, footnote in ipairs(new) do local footnote_mods, _ = extract_footnote_modifiers(footnote) if footnote_mods:find("[!+]") then any_footnotes_with_bang = true break end end if any_footnotes_with_bang then if not existing then existing = {} else existing = shallow_copy(existing) end for _, footnote in ipairs(new) do local already_seen = false local footnote_mods, footnote_without_mods = extract_footnote_modifiers(footnote) if footnote_mods:find("[!+]") then for _, existing_footnote in ipairs(existing) do local _, existing_footnote_without_mods = extract_footnote_modifiers(existing_footnote) if existing_footnote_without_mods == footnote_without_mods then already_seen = true break end end if not already_seen then insert(existing, footnote) end end end end return existing end --[==[ Combine two sets of footnotes. If either is {nil}, just return the other, and if both are {nil}, return {nil}. ]==] function export.combine_footnotes(notes1, notes2) if not notes1 and not notes2 then return nil end if not notes1 then return notes2 end if not notes2 then return notes1 end local combined = shallow_copy(notes1) for _, note in ipairs(notes2) do insert_if_not(combined, note) end return combined end function export.combine_formobj_lists(list1, list2, metadata) if not list1 and not list2 then return nil end if not list1 then return list2 end if not list2 then return list1 end return export.insert_formobjs_into_list { existing_list = list1, formobjs = list2, metadata = metadata, } end function export.looks_like_formobj_list(list) if list == nil then return true end if type(list) ~= "table" then return false end for k, v in pairs(list) do if type(k) ~= "number" then return false end if type(v) ~= "table" then return false end if type(v.form) ~= "string" then return false end end return true end function export.looks_like_boolean(object) return object == nil or type(object) == "boolean" end function export.default_fallback_deduplicate_handler(_prop, existing, _new) return existing, true end function export.default_fallback_combine_handler(_prop, existing, new) if export.looks_like_formobj_list(existing) and export.looks_like_formobj_list(new) then return export.combine_formobj_lists(existing, new), true end if export.looks_like_boolean(existing) and export.looks_like_boolean(new) then return existing or new, true end if existing ~= nil then return existing, true end return new, true end --[=[ Default handler functions for working with form object metadata. The user-specific metadata structure has the same format. The key is a specific metadata field and the value is an object with fields specifying handlers and other properties of the metadata. The recognized per-metadata fields are: * `deduplicate`: Handler to deduplicate form objects when inserting a form object into an existing list (e.g. in a form table) and two form objects have the same form value and translit. It is passed two values, respectively the metadata property from the existing list object and the new object being inserted into the list. The default and most common behavior here is to preserve the property from the existing object, even if {nil}, and discard the value from the new object. This is what is done in footnotes, for example, unless the existing footnote is preceded by a * (meaning to discard both values) or the new one is preceded by a + (meaning to preserve and combine both values, which is not currrently used anywhere and may not make sense). This ensures, for example, that if a noun can be declined according to two paradigms and the second one is rare, the {"rare"} footnote on the second one gets discarded whenever the two paradigms coincide. * `combine`: Handler to combine form objects when deriving a new object from an existing one. This is conceptually very different from deduplication. In this situation, the existing and new objects may or may not match in form value and translit, and since the new form object is a derivative of the existing one, it should combine the metadata properties of both, preserving as much information as possible and not discarding anything. Combining footnotes, for example, means taking the footnotes from either one, and if both exising and new objects have footnotes, merging the two lists and removing duplicates. (The only exception is if a new footnote is preceded by !, which causes existing footnotes to be discarded. This is termed an ''override'', and allows e.g. for one form of a paradigm to override the footnote associated with the paradigm as a whole, as for example if a paradigm is archaic except for one of the forms.) The default `combine` strategy is as follows: *# If both the existing and new objects are {nil} or "look like" form object lists (i.e. are tables where all keys are numeric and all values are tables with a `form` key whose value is a string), combine them using `insert_formobjs_into_list`. *# If both the existing and new objects are {nil} or boolean, combine them using `A or B`. *# Otherwise, take the existing value if it is non-{nil}, and if not, take the new value. Note that this means that the existing value will be kept even if it is {false} (which would not happen if the new value were {true}); this allows for metadata where {false} is a special signal and keys are otherwise strings or objects. However, in general if the metadata objects are not boolean and not form object lists, they will need a custom combine handler. Some keys in the metadata handler object are special: * `*`: This provides "fallback" handlers. These handlers are called for all metadata fields without dedicated handlers. These handlers work similar to normal `deduplicate` and `combine` handlers except (a) they are passed three values, first the metadata property name and then the existing and new values; (b) they should return two values, first the appropriate merged value and second a boolean indicating whether the property was handled. (If {false} is returned, the first value is ignored and should be {nil}.) Dedicated and fallback handlers are called in the following order: (1) dedicated user-specified handlers; (2) dedicated default handlers; (3) fallback user-specified handlers; (4) fallback default handlers. This ensures that standard dedicated default handlers (particularly for footnotes) get run prior to the user-specified fallback handlers; otherwise, practically all such fallback handlers will need to check for and ignore footnotes. * `_side_effect_formobjs`: If true, existing form objects can be side-effected during deduplication or combining. This requires that two logically distinct form objects in a given form table never have the same reference. (Generally two such objects should be in different slots. If two references to the same object occur in different positions in the list of form objects under a single slot, it indicates an internal error in the deduplication mechanism.) This means that user code that generates form objects for insertion into a table must take care never to reuse existing form objects (e.g. when setting one slot equal to another slot), but must always shallow-copy the form objects. This is not the default for this reason, and in fact it's not clear that implementing this results in any efficiency gain (it may actually be the reverse, since form objects have to be copied that could otherwise be shared). ]=] local default_metadata_handlers = { footnotes = { deduplicate = export.deduplicate_footnotes, combine = export.combine_footnotes, }, ["*"] = { deduplicate = export.default_fallback_deduplicate_handler, combine = export.default_fallback_combine_handler, } } local function is_non_metadata_property(k) return k == "form" or k == "translit" end -- Call the appropriate handler for merging the metadata for property `prop` with values `existing_val` and `new_val`, -- using method `method` (either "deduplicate" or "combine"). `metadata` is the structure describing all properties of -- all metadata in a given form object. This looks for, in order: -- 1. A handler for the specific property in `metadata`. -- 2. A default handler for the specific property. -- 3. A fallback handler in `metadata`. -- 4. A default fallback handler. -- Return the new value. local function call_merge_metadata_handler(metadata, prop, method, existing_val, new_val) local function try_specific(handler_struct, prop) if handler_struct and handler_struct[prop] and handler_struct[prop][method] then return (handler_struct[prop][method](existing_val, new_val)), true end return nil, false end local function try_fallback(handler_struct, prop) if handler_struct and handler_struct["*"] and handler_struct["*"][method] then -- Two values are returned from the handler, the new value and whether the property was handled. return handler_struct["*"][method](prop, existing_val, new_val) end return nil, false end local retval, matched = try_specific(metadata, prop) if matched then return retval end retval, matched = try_specific(default_metadata_handlers, prop) if matched then return retval end retval, matched = try_fallback(metadata, prop) if matched then return retval end retval, matched = try_fallback(default_metadata_handlers, prop) return retval end -- Merge the metadata of two form objects using `method` (either "deduplicate" or "combine"). `metadata` is the -- structure describing all properties of all metadata in a given form object. Metadata is merged between -- `existing_formobj` (on the left) and `new_formobj` (on the right), and is normally stored into `existing_formobj`, -- but will be stored into `new_formobj` if `merge_into_new` is given. If `metadata` allows for side-effecting, the -- changes will be made by side-effecting the object to be changed; otherwise, a shallow copy of the object will be -- made if necessary (only if the new value is not equal to the existing value). Returns two values, the changed form -- object (which may be the same as one of the passed-in objects) and a flag which is true if a copy was made of a form -- object. local function merge_metadata(metadata, method, existing_formobj, new_formobj, merge_into_new) local props_of_either = nil for k, _ in pairs(existing_formobj) do if not is_non_metadata_property(k) then props_of_either = props_of_either or {} props_of_either[k] = true end end for k, _ in pairs(new_formobj) do if not is_non_metadata_property(k) then props_of_either = props_of_either or {} props_of_either[k] = true end end local store_object = merge_into_new and new_formobj or existing_formobj if props_of_either then if metadata._side_effect_formobjs then for k, _ in pairs(props_of_either) do store_object[k] = call_merge_metadata_handler(metadata, k, method, existing_formobj[k], new_formobj[k]) end else local need_to_copy = false local new_values = {} for k, _ in pairs(props_of_either) do local newval = call_merge_metadata_handler(metadata, k, method, existing_formobj[k], new_formobj[k]) if newval ~= store_object[k] and not m_table.deepEquals(newval, store_object[k]) then need_to_copy = true end new_values[k] = newval end if need_to_copy then store_object = shallow_copy(store_object) for k, _ in pairs(props_of_either) do store_object[k] = new_values[k] end end return store_object, need_to_copy end else return store_object, false end end --[==[ Insert a form object (see above) into a list of such objects. If the form is already present (i.e. both the form value and translit, if any, match), the footnotes of the existing and new form might be combined (specifically, footnotes in the new form beginning with `+` will be combined). ]==] function export.insert_formobj_into_list(data) local existing_list = data.existing_list local formobj = data.formobj -- Don't do anything if the form object is nil. if not formobj then return existing_list end for i, existing_formobj in ipairs(existing_list) do if existing_formobj.form == formobj.form and existing_formobj.translit == formobj.translit then local new_existing, copied = merge_metadata(data.metadata, "deduplicate", existing_formobj, formobj) if copied then local list_copy = shallow_copy(existing_list) list_copy[i] = new_existing return list_copy end return existing_list end end -- Form not found. insert(existing_list, formobj) return existing_list end --[==[ Insert a list of form objects (see above) into an existing list of such objects. ``forms`` can be {nil}, in which case nothing happens. This is equivalent to calling `insert_form_into_list` for each form object. ]==] function export.insert_formobjs_into_list(data) local existing_list = data.existing_list local formobjs = data.formobjs if not formobjs then return existing_list end for _, formobj in ipairs(formobjs) do existing_list = export.insert_formobj_into_list { existing_list = existing_list, formobj = formobj, metadata = data.metadata, } end return existing_list end --[==[ Insert a form object (see above) into the given slot in the given form table. On input, `data` is an object with the following fields: * `formtable`: A form table (see above). * `slot`: A slot in the form table, i.e. a key whose value is a form object list. * `formobj`: A form object to be inserted into the slot. Can be {nil}, in which case nothing happens. * `metadata`: The metadata structure describing how to combine user-specified metadata. Can be {nil} if there is no user-specified metadata or the default fallback metadata handlers are sufficient (which is usually the case if the all user-specified metadata is boolean). ]==] function export.insert_formobj_into_form_table(data) local formtable, slot, formobj, metadata = data.formtable, data.slot, data.formobj, data.metadata -- Don't do anything if the form object is nil. if not formobj then return end if not formtable[slot] then formtable[slot] = {} end formtable[slot] = export.insert_formobj_into_list { existing_list = formtable[slot], formobj = formobj, metadata = metadata, } end --[==[ Insert a list of form objects (see above) into the given slot in the given form table. On input, `data` is an object with the following fields: * `formtable`: A form table (see above). * `slot`: A slot in the form table, i.e. a key whose value is a form object list. * `formobjs`: A list of form objects (in "full list" form) to be inserted into the slot. Can be {nil} or an empty list, in which case nothing happens. * `metadata`: The metadata structure describing how to combine user-specified metadata. Can be {nil} if there is no user-specified metadata or the default fallback metadata handlers are sufficient (which is usually the case if the all user-specified metadata is boolean). ]==] function export.insert_formobjs_into_form_table(data) local formtable, slot, formobjs, metadata = data.formtable, data.slot, data.formobjs, data.metadata if not formobjs or not formobjs[1] then return end if not formtable[slot] then formtable[slot] = {} end formtable[slot] = export.insert_formobjs_into_list { existing_list = formtable[slot], formobjs = formobjs, metadata = metadata, } end --[==[ Flatmap a function over a list of form objects and return the (potentially flattened) results. Flatmapping is like a normal mapping operation except that the function being called may return a list, and all values returned are concatenated into a single flattened list. On input, `data` is an object with the following fields: * `formobjs`: A list of form objects to map over, in "full list form" (see above). * `fn`: The function to map over each form object. If a form object's value is {"?"}, the form object is preserved on output and the function is not called. Otherwise, the function is called with one argument, the form object, and should {nil} (equivalent to returning an empty list) or an abbreviated form list (i.e. anything that is convertible into a full list form, such as a single form value, a list of form values, a form object or a list of form objects). For each form object in the return value, the metadata of the object mapped over is combined with the metadata of the returned object, unless `no_combine_metadata` is given. The resulting form objects from all function calls are flattened into a list and deduplicated in the same fashion as `insert_formobj_into_list()` (in case two different form objects map to the same form value). * `metadata`: The metadata structure describing how to combine user-specified metadata. Can be {nil} if there is no user-specified metadata or the default fallback metadata handlers are sufficient (which is usually the case if the all user-specified metadata is boolean). * `no_combine_metadata`: See `fn` above. If given, metadata from objects mapped over will not be combined into the returned objects. NOTE: There is no `map_formobjs` function provided because `flatmap_formobjs` will correctly handle functions that return single form objects. ]==] function export.flatmap_formobjs(data) local formobjs, fn, metadata, no_combine_metadata = data.formobjs, data.fn, data.metadata, data.no_combine_metadata if not formobjs then return nil end local retval = {} for _, formobj in ipairs(formobjs) do local funret if formobj.form == "?" then if metadata and metadata._side_effect_formobjs then funret = shallow_copy(formobj) else funret = formobj end else funret = fn(formobj) end if funret then funret = export.convert_to_full_list_form(funret) for _, fr in ipairs(funret) do if not no_combine_metadata then fr = merge_metadata(metadata, "combine", formobj, fr, "merge_into_new") end retval = export.insert_formobj_into_list { existing_list = retval, formobj = fr, metadata = metadata, } end end end return retval end --[==[ Insert a form object (see above) into a list of such objects. If the form is already present (i.e. both the form value and translit, if any, match), the footnotes of the existing and new form might be combined (specifically, footnotes in the new form beginning with `!` will be combined). ]==] function export.insert_form_into_list(list, form) -- Don't do anything if the form object or the form inside it is nil. This simplifies -- form insertion in the presence of inflection generating functions that may return nil, -- such as generate_noun_vocative() and generate_noun_count_form(). if not form or not form.form then return end for _, listform in ipairs(list) do if listform.form == form.form and listform.translit == form.translit then listform.footnotes = export.deduplicate_footnotes(listform.footnotes, form.footnotes) return end end -- Form not found. insert(list, form) end --[==[ Insert a list of form objects (see above) into an existing list of such objects. ``forms`` can be {nil}, in which case nothing happens. This is equivalent to calling `insert_form_into_list` for each form object. ]==] function export.insert_forms_into_list(list, forms) if not forms then return end for _, form in ipairs(forms) do export.insert_form_into_list(list, form) end end --[==[ Insert a form object (see above) into the given slot in the given form table. ``form`` can be {nil}, in which case nothing happens. ]==] function export.insert_form(formtable, slot, form) -- Don't do anything if the form object or the form inside it is nil. This simplifies -- form insertion in the presence of inflection generating functions that may return nil, -- such as generate_noun_vocative() and generate_noun_count_form(). if not form or not form.form then return end if not formtable[slot] then formtable[slot] = {} end export.insert_form_into_list(formtable[slot], form) end --[==[ Insert a list of form objects (see above) into the given slot in the given form table. ``forms`` can be {nil}, in which case nothing happens. ]==] function export.insert_forms(formtable, slot, forms) if not forms then return end for _, form in ipairs(forms) do export.insert_form(formtable, slot, form) end end --[==[ Identity mapping function. ]==] function export.identity(formval, translit) return formval, translit end local function form_value_transliterable(formval) return formval ~= "?" and formval ~= "—" end local function call_map_function_str(str, fun) if str == "?" then return "?" end local newformval, newtranslit = fun(str) if newtranslit then return {form=newformval, translit=newtranslit} else return newformval end end -- FIXME: This doesn't correctly handle metadata. local function call_map_function_obj(form, fun) if form.form == "?" then return {form = "?", footnotes = form.footnotes} end local newformval, newtranslit = fun(form.form, form.translit) return {form = newformval, translit = newtranslit, footnotes = form.footnotes} end --[==[ Map a function over the form values in ``forms`` (a list of form objects in "full list form"; see above). If an input form value is {"?"}, it is preserved on output and the function is not called. Otherwise, the function is called with two arguments, the original form and manual translit; if manual translit isn't relevant, it's fine to declare the function with only one argument. The return value is either a single value (the new form) or two values (the new form and new manual translit). The footnotes (if any) from the input form objects are preserved on output. Uses `insert_form_into_list()` to insert the resulting form objects into the returned list in case two different forms map to the same thing. FIXME: Expand this to correctly handle metadata, or create a variant that correctly handles metadata. ]==] function export.map_forms(forms, fun) if not forms then return nil end local retval = {} for _, form in ipairs(forms) do export.insert_form_into_list(retval, call_map_function_obj(form, fun)) end return retval end --[==[ Map a list-returning function over the form values in ``forms`` (a list of form objects in "full list form"; see above). If an input form value is {"?"}, it is preserved on output and the function is not called. Otherwise, the function is called with two arguments, the original form and manual translit; if manual translit isn't relevant, it's fine to declare the function with only one argument. The return value of the function can be {nil} or an abbreviated form list (i.e. anything that is convertible into a full list form, such as a single form value, a list of form values, a form object or a list of form objects). For each form object in the return value, the footnotes of that form object (if any) are combined with any footnotes from the input form object, and the result inserted into the returned list using `insert_form_into_list()` in case two different forms map to the same thing. ]==] function export.flatmap_forms(forms, fun) if not forms then return nil end local retval = {} for _, form in ipairs(forms) do local funret = form.form == "?" and form or fun(form.form, form.translit) if funret then funret = export.convert_to_general_list_form(funret) for _, fr in ipairs(funret) do local newform = { form = fr.form, translit = fr.translit, footnotes = export.combine_footnotes(form.footnotes, fr.footnotes) } export.insert_form_into_list(retval, newform) end end end return retval end --[==[ Map a function over the form values in ``abforms`` (an abbreviated form list). If the input form value is {"?"}, it is preserved on output and the function is not called. If ``first_only`` is given and ``abforms`` is a list, only map over the first element. Return value is of the same form as ``abforms``, unless ``abforms`` is a string and the function returns both form value and manual translit (in which case the return value is a form object). The function is called with two arguments, the original form value and manual translit; if manual translit isn't relevant, it's fine to declare the function with only one argument. The return value is either a single value (the new form value) or two values (the new form value and new manual translit). The footnotes (if any) from the input form objects are preserved on output. FIXME: This function is used only in [[Module:bg-verb]] and should be moved into that module. ]==] function export.map_form_or_forms(abforms, fun, first_only) if not abforms then return nil elseif type(abforms) == "string" then return call_map_function_str(abforms, fun) elseif abforms.form then return call_map_function_obj(abforms, fun) else local retval = {} for i, abform in ipairs(abforms) do if first_only then return export.map_form_or_forms(abform, fun) end insert(retval, export.map_form_or_forms(abform, fun)) end return retval end end --[==[ Expand a given footnote (as specified by the user, including the surrounding brackets) into the form to be inserted into the final generated table. If ``no_parse_refs`` is not given and the footnote is a reference (of the form {"[ref:...]"}), parse and return the specified reference(s). Two values are returned, `footnote_string` (the expanded footnote, or nil if the second value is present) and `references` (a list of objects of the form `{text = ``text``, name = ``name``, group = ``group``}` if the footnote is a reference and ``no_parse_refs`` is not given, otherwise {nil}). Unless ``return_raw`` is given, the returned footnote string is capitalized and has a final period added. ]==] function export.expand_footnote_or_references(note, return_raw, no_parse_refs) local _, notetext = extract_footnote_modifiers(note) if not no_parse_refs and notetext:find("^ref:") then -- a reference notetext = notetext:gsub("^ref:", "") local parsed_refs = require("Module:references").parse_references(notetext) for i, ref in ipairs(parsed_refs) do if type(ref) == "string" then parsed_refs[i] = {text = ref} end end return nil, parsed_refs end if footnote_abbrevs[notetext] then notetext = footnote_abbrevs[notetext] track("footnote-whole-abbrev") else local split_notes = split(notetext, "<(.-)>") for i, split_note in ipairs(split_notes) do if i % 2 == 0 then split_notes[i] = footnote_abbrevs[split_note] track("footnote-angle-bracket-abbrev") if not split_notes[i] then -- Don't error for now, because HTML might be in the footnote. -- Instead we should switch the syntax here to e.g. <<a>> to avoid -- conflicting with HTML. split_notes[i] = "<" .. split_note .. ">" track("footnote-unrecognized-angle-bracket-abbrev") --error("Unrecognized footnote abbrev: <" .. split_note .. ">") else track("footnote-recognized-angle-bracket-abbrev") end end end notetext = concat(split_notes) end return return_raw and notetext or ucfirst(notetext) .. "." end --[==[ Convert a list of foonotes to qualifiers and references for use in [[Module:headword]] or similar. Returns two values, a list of qualifiers (possibly {nil}) and a list of reference structures (possibly {nil}), following the structure defined in [[Module:references]]). ]==] function export.convert_footnotes_to_qualifiers_and_references(footnotes) if not footnotes then return nil end local quals, refs for _, qualifier in ipairs(footnotes) do local this_footnote, this_refs = export.expand_footnote_or_references(qualifier, "return raw") if this_refs then if not refs then refs = this_refs else for _, ref in ipairs(this_refs) do insert(refs, ref) end end else if not quals then quals = {this_footnote} else insert(quals, this_footnote) end end end return quals, refs end --[==[ Combine an abbreviated form object (either a string or a table) with additional footnotes, possibly replacing the form value and/or translit in the process. Normally called in one of two ways: (1) `combine_form_and_footnotes(``form_obj``, ``addl_footnotes``, ``new_form``, ``new_translit``)` where ``form_obj`` is an existing abbreviated form object; ``addl_footnotes`` is either {nil}, a single string (a footnote) or a list of footnotes; ``new_formval`` is either {nil} or the new form value to substitute; and ``new_translit`` is either {nil} or the new translit string to substitute. (2) `combine_form_and_footnotes(``form_value``, ``footnotes``)`, where ``form_value`` is a form value (a string) and ``footnotes`` is either {nil}, a single string (a footnote) or a list of footnotes. In either case, a form object is returned, preserving as many properties as possible from any existing form object in ``abform``. Do the minimal amount of work; e.g. if ``abform`` is a form object and ``addl_footnotes``, ``new_formval`` and ``new_translit`` are all {nil}, the same object as passed in is returned. Under no circumstances is the existing form object side-effected. '''FIXME:''' This does not correctly preserve metadata. ]==] function export.combine_form_and_footnotes(abform, addl_footnotes, new_formval, new_translit) if type(addl_footnotes) == "string" then addl_footnotes = {addl_footnotes} end if not addl_footnotes and not new_formval and not new_translit then return abform end if type(abform) == "string" then new_formval = new_formval or abform return {form = new_formval, translit = new_translit, footnotes = addl_footnotes} end abform = shallow_copy(abform) if new_formval then abform.form = new_formval end if new_translit then abform.translit = new_translit end if addl_footnotes then abform.footnotes = export.combine_footnotes(abform.footnotes, addl_footnotes) end return abform end --[==[ Convert an abbreviated form list (either a string, form object, or list of either) into full list form. This function does not side-effect any of the objects passed into ``abforms``, but will return ``abforms`` unchanged if already in full list form. ]==] function export.convert_to_full_list_form(abforms) if type(abforms) == "string" then return {{form = abforms}} elseif abforms.form then return {abforms} else -- Check if already in full list form and return directly if so. local must_convert = false for _, form in ipairs(abforms) do if type(form) == "string" then must_convert = true break end end if not must_convert then return abforms end end local retval = {} for _, form in ipairs(abforms) do if type(form) == "string" then insert(retval, {form = form}) else insert(retval, form) end end return retval end --[==[ Convert an abbreviated form list (either a string, form object, or list of either) into full list form. If ``footnotes`` is supplied, then for each form in the form list, combine the form's footnotes with ``footnotes``. This function does not side-effect any of the objects passed into ``abforms``, but will return ``abforms`` unchanged if already in full list form and ``footnotes`` is {nil}. '''FIXME:''' This does not correctly preserve metadata. ]==] function export.convert_to_general_list_form(abforms, footnotes) if type(footnotes) == "string" then footnotes = {footnotes} end if type(abforms) == "string" then return {{form = abforms, footnotes = footnotes}} elseif abforms.form then return {export.combine_form_and_footnotes(abforms, footnotes)} elseif not footnotes then -- Check if already in full list form and return directly if so. local must_convert = false for _, form in ipairs(abforms) do if type(form) == "string" then must_convert = true break end end if not must_convert then return abforms end end local retval = {} for _, form in ipairs(abforms) do if type(form) == "string" then insert(retval, {form = form, footnotes = footnotes}) else insert(retval, export.combine_form_and_footnotes(form, footnotes)) end end return retval end local function is_table_of_strings(forms) for k, v in pairs(forms) do if type(k) ~= "number" or type(v) ~= "string" then return false end end return true end local function lang_or_func_transliterate(func, lang, text) local retval if func then retval = func(text) else retval = (lang:transliterate(text)) end -- FIXME! Hack to work around bug in ...:transliterate(). Remove me as soon as this bug is fixed. if not retval and (text == " " or text == "-" or text == "-́" or text == "?" or text == "-∅") then retval = text end if not retval then error(("Unable to transliterate text '%s'"):format(text)) end return retval end --[==[ Combine ``stems`` and ``endings`` and store into slot ``slot`` of form table ``formtable``. Either of ``stems`` and ``endings`` can be {nil} or an abbreviated form list. The combination of a given stem and ending happens using ``combine_stem_ending``, which takes two parameters (stem and ending, each a string) and returns one value (a string). If manual transliteration is present in either ``stems`` or ``endings``, ``lang`` (a language object or a function of one argument to transliterate a string) along with ``combine_stem_ending_tr`` (a function for combining manual transliterations that works much like ``combine_stem_ending``) must be given. ``footnotes``, if specified, is a list of additional footnotes to attach to the resulting inflections (stem+ending combinations). The resulting inflections are inserted into the form table using `insert_form()`, in case of duplication. ]==] function export.add_forms(formtable, slot, stems, endings, combine_stem_ending, lang, combine_stem_ending_tr, footnotes) if stems == nil or endings == nil then return end local function combine(stem, ending) if stem == "?" or ending == "?" then return "?" end return combine_stem_ending(stem, ending) end local function transliterate(text) return lang_or_func_transliterate(is_callable(lang) and lang or nil, lang, text) end if type(stems) == "string" and type(endings) == "string" then export.insert_form(formtable, slot, {form = combine(stems, endings), footnotes = footnotes}) elseif type(stems) == "string" and is_table_of_strings(endings) then for _, ending in ipairs(endings) do export.insert_form(formtable, slot, {form = combine(stems, ending), footnotes = footnotes}) end else stems = export.convert_to_general_list_form(stems) endings = export.convert_to_general_list_form(endings, footnotes) for _, stem in ipairs(stems) do for _, ending in ipairs(endings) do local new_footnotes = nil if stem.footnotes and ending.footnotes then new_footnotes = shallow_copy(stem.footnotes) for _, footnote in ipairs(ending.footnotes) do insert_if_not(new_footnotes, footnote) end elseif stem.footnotes then new_footnotes = stem.footnotes elseif ending.footnotes then new_footnotes = ending.footnotes end local new_form = combine(stem.form, ending.form) local new_translit if new_form ~= "?" and (stem.translit or ending.translit) then if not lang or not combine_stem_ending_tr then error("Internal error: With manual translit, 'lang' and 'combine_stem_ending_tr' must be passed to 'add_forms'") end local stem_tr = stem.translit or transliterate(m_links.remove_links(stem.form)) local ending_tr = ending.translit or transliterate(m_links.remove_links(ending.form)) new_translit = combine_stem_ending_tr(stem_tr, ending_tr) end export.insert_form(formtable, slot, {form = new_form, translit = new_translit, footnotes = new_footnotes}) end end end end --[==[ Combine any number of form components and store into slot ``slot`` of form table ``formtable``. ``components`` is a list of abbreviated form lists which should be concatenated similarly to how `add_forms()` does it, and stored in ``slot`` along with any footnotes in ``footnotes``. More specifically: # If there are no components, nothing happens. # If there is one component, it is converted to full list form and `insert_forms()` called. # If there are two components, they are treated as stems and endings respectively and `add_forms()` is called. # If there are three or more components, they are concatenated left-to-right in the manner of a `reduce()` operation: the first two components are combined using `add_forms()` and stored into a temporary table, then the next component is combined with the result of the previous operation, etc. In the last combination, footnotes in `footnotes` are combined in, and the result stored into `formtable`. This should generally be used when you are likely to have three or more components, as in [[Module:ar-verb]] (prefixes, stems and endings) and [[Module:de-verb]] (which in some situations has five components combined together). ``combine_stem_ending``, ``lang``, ``combine_stem_ending_tr`` and ``footnotes`` are as in `add_forms()`. ]==] function export.add_multiple_forms(formtable, slot, components, combine_stem_ending, lang, combine_stem_ending_tr, footnotes) if #components == 0 then return elseif #components == 1 then local forms = export.convert_to_general_list_form(components[1], footnotes) export.insert_forms(formtable, slot, forms) elseif #components == 2 then local stems = components[1] local endings = components[2] export.add_forms(formtable, slot, stems, endings, combine_stem_ending, lang, combine_stem_ending_tr, footnotes) else local prev = components[1] for i=2, #components do local temptable = {} export.add_forms(temptable, slot, prev, components[i], combine_stem_ending, lang, combine_stem_ending_tr, i == #components and footnotes or nil) prev = temptable[slot] end export.insert_forms(formtable, slot, prev) end end local function iterate_slot_list_or_table(props, do_slot) if props.slot_list then for _, slot_and_accel_tag_set in ipairs(props.slot_list) do local slot, accel_tag_set = unpack(slot_and_accel_tag_set) do_slot(slot, accel_tag_set) end else for slot, accel_tag_set in pairs(props.slot_table) do do_slot(slot, accel_tag_set) end end end function export.default_split_bracketed_runs_into_words(bracketed_runs, data) -- If the text begins with a hyphen, include the hyphen in the set of allowed characters -- for an inflected segment. This way, e.g. conjugating "-ir" is treated as a regular -- -ir verb rather than a hyphen + irregular [[ir]]. local is_suffix = (not data or data.text_index == 1) and bracketed_runs[1]:find("^%-") local split_pattern = is_suffix and " " or "[ %-]" return put.split_alternating_runs(bracketed_runs, split_pattern, "preserve splitchar") end local function props_transliterate(props, text) return lang_or_func_transliterate(props.transliterate, props.lang, text) end local function parse_before_or_post_text(data) -- NOTE: data.text_index is also used (in `default_split_bracketed_runs_into_words`). local props, text, segments, lemma_is_last = data.props, data.text, data.segments, data.lemma_is_last -- Call parse_balanced_segment_run() to keep multiword links together. local bracketed_runs = put.parse_balanced_segment_run(text, "[", "]") -- Split normally on space or hyphen (but customizable). Use preserve_splitchar so we know whether the separator was -- a space or hyphen. local space_separated_groups if props.split_bracketed_runs_into_words then space_separated_groups = props.split_bracketed_runs_into_words(bracketed_runs) end if not space_separated_groups then space_separated_groups = export.default_split_bracketed_runs_into_words(bracketed_runs, data) end local parsed_components = {} local parsed_components_translit = {} local saw_manual_translit = false local lemma for j, space_separated_group in ipairs(space_separated_groups) do local component = concat(space_separated_group) if lemma_is_last and j == #space_separated_groups then lemma = component if lemma == "" and not props.allow_blank_lemma then error("Word is blank: '" .. concat(segments) .. "'") end elseif component:find("//") then -- Manual translit or respelling specified. if not props.lang then error("Manual translit not allowed for this language; if this is incorrect, 'props.lang' must be set internally") end saw_manual_translit = true local split_components = split(component, "//", "plain") if #split_components ~= 2 then error("Term with translit or respelling should have only one // in it: " .. component) end local translit component, translit = unpack(split_components) if props.transliterate_respelling then translit = props.transliterate_respelling(translit) end insert(parsed_components, component) insert(parsed_components_translit, translit) else insert(parsed_components, component) insert(parsed_components_translit, false) -- signal that it may need later transliteration end end if saw_manual_translit then for j, parsed_component in ipairs(parsed_components) do if not parsed_components_translit[j] then parsed_components_translit[j] = props_transliterate(props, m_links.remove_links(parsed_component)) end end end text = concat(parsed_components) local translit if saw_manual_translit then translit = concat(parsed_components_translit) end return text, translit, lemma end --[=[ Parse a segmented multiword spec such as "[[медичний|меди́чна]]<+> [[сестра́]]<*,*#.pr>" (in Ukrainian). "Segmented" here means it is broken up on <...> segments using parse_balanced_segment_run(text, "<", ">"), e.g. the above text would be passed in as {"[[медичний|меди́чна]]", "<+>", " [[сестра́]]", "<*,*#.pr>", ""}. The return value is a table of the form { word_specs = {``word_spec``, ``word_spec``, ...}, post_text = "``text-at-end``", post_text_no_links = "``text-at-end-no-links``", post_text_translit = "``manual-translit-of-text-at-end``" or nil (if no manual translit or respelling was specified in the post-text) } where ``word_spec`` describes an individual inflected word and "``text-at-end``" is any raw text that may occur after all inflected words. Individual words or linked text (including multiword text) may be given manual transliteration or respelling in languages that support this using ``text``//``translit`` or ``text``//``respelling``. Each ``word_spec`` is of the form returned by parse_indicator_spec(): { lemma = "``lemma``", before_text = "``text-before-word``", before_text_no_links = "``text-before-word-no-links``", before_text_translit = "``manual-translit-of-text-before-word``" or nil (if no manual translit or respelling was specified in the before-text) -- Fields as described in parse_indicator_spec() ... } For example, the return value for "[[медичний|меди́чна]]<+> [[сестра́]]<*,*#.pr>" is { word_specs = { { lemma = "[[медичний|меди́чна]]", overrides = {}, adj = true, before_text = "", before_text_no_links = "", forms = {}, }, { lemma = "[[сестра́]]", overrides = {}, stresses = { { reducible = true, genpl_reversed = false, }, { reducible = true, genpl_reversed = true, }, }, animacy = "pr", before_text = " ", before_text_no_links = " ", forms = {}, }, }, post_text = "", post_text_no_links = "", } ]=] local function parse_multiword_spec(segments, props, disable_allow_default_indicator) local multiword_spec = { word_specs = {} } if not disable_allow_default_indicator then if #segments == 1 then if props.allow_default_indicator then insert(segments, "<>") insert(segments, "") elseif props.angle_brackets_omittable then segments[1] = "<" .. segments[1] .. ">" insert(segments, 1, "") insert(segments, "") end end end -- Loop over every other segment. The even-numbered segments are angle-bracket specs while -- the odd-numbered segments are the text between them. for i = 2, #segments - 1, 2 do local before_text, before_text_translit, lemma = parse_before_or_post_text { props = props, text = segments[i - 1], text_index = i - 1, segments = segments, lemma_is_last = true } local base = props.parse_indicator_spec(segments[i], lemma) base.before_text = before_text base.before_text_no_links = m_links.remove_links(base.before_text) base.before_text_translit = before_text_translit base.lemma = base.lemma or lemma insert(multiword_spec.word_specs, base) end multiword_spec.post_text, multiword_spec.post_text_translit = parse_before_or_post_text { props = props, text = segments[#segments], text_index = #segments, segments = segments, lemma_is_last = false } multiword_spec.post_text_no_links = m_links.remove_links(multiword_spec.post_text) return multiword_spec end --[=[ Parse an alternant, e.g. "((родо́вий,родови́й))" or "((ру́син<pr>,руси́н<b.pr>))" (both in Ukrainian). The return value is a table of the form { alternants = {``multiword_spec``, ``multiword_spec``, ...} } where ``multiword_spec`` describes a given alternant and is as returned by parse_multiword_spec(). ]=] local function parse_alternant(alternant, props) local alternant_text = alternant:match("^%(%((.*)%)%)$") local segments = put.parse_balanced_segment_run(alternant_text, "<", ">") local comma_separated_groups = put.split_alternating_runs(segments, "%s*,%s*") local alternant_spec = {alternants = {}} for _, comma_separated_group in ipairs(comma_separated_groups) do insert(alternant_spec.alternants, parse_multiword_spec(comma_separated_group, props)) end return alternant_spec end --[==[ Top-level parsing function. Parse text describing one or more inflected words. `text` is the inflected text to parse, which generally has `<...>` specs following words to be inflected, and may have alternants indicated using double parens. Examples: * {"[[медичний|меди́чна]]<+> [[сестра́]]<*,*#.pr>"} (Ukrainian, for {{m|uk|меди́чна сестра́||nurse|lit=medical sister}}); * {"((ру́син<pr>,руси́н<b.pr>))"} (Ukrainian, for {{m|uk|русин||Rusyn}}, with two possible stress patterns); * {"पंचायती//पंचाय*ती राज<M>"} (Hindi, for {{m|hi|पंचायती राज||village council}}, with phonetic respelling in the before-text component); * {"((<M>,<M.plstem:फ़तूह.dirpl:फ़तूह>))"} (Hindi, for {{m|hi|फ़तह||win, victory}} when used on that page, where the lemma is omitted and taken from the pagename); * {""} (for any number of Hindi adjectives, where the lemma is omitted and taken from the pagename, and the angle bracket spec <> is assumed); * {"काला<+>धन<M>"} (Hindi, for {{m|hi|कालाधन||black money}}, showing that closed compounds where each part is declined can be correctly handled). `props` is an object specifying properties used during parsing, as follows: ```{ parse_indicator_spec = __function__(``angle_bracket_spec``, ``lemma``) `''(required)''`, lang = __lang object__, transliterate_respelling = __function__(``respelling_or_translit``) `''(optional)''`, split_bracketed_runs_into_words = __function__(``bracket_split_runs``) `''(optional)''`, allow_default_indicator = __boolean__, angle_brackets_omittable = __boolean__, allow_blank_lemma = __boolean__, }``` `parse_indicator_spec` is a required function that takes two arguments, a string surrounded by angle brackets and the lemma, and should return an arbitrary object containing properties describing the indicators inside of the angle brackets). This object is often called a '''base''' and given the argument name `base` in inflection code. `lang` is the language object for the language in question; only needed if manual translit or respelling may be present using `//`. `transliterate_respelling` is a function that is only needed if respelling is allowed in place of manual translit after `//`. It takes one argument, the respelling or translit, and should return the transliteration of any respelling but return any translit unchanged. `split_bracketed_runs_into_words` is an optional function to split the passed-in text into words. It is used, for example, to determine what text constitutes a word when followed by an angle-bracket spec, i.e. what the lemma to be inflected is vs. surrounding fixed text. It takes one argument, the result of splitting the original text on brackets, and should return alternating runs of words and split characters, or nil to apply the default algorithm. Specifically, the value passed in is the result of calling `parse_balanced_segment_run(``text``, "[", "]")` from [[Module:parse utilities]] on the original text, and the default version of this function calls `split_alternating_runs(``bracketed_runs``, ``pattern``, "preserve splitchar")`, where ``bracketed_runs`` is the value passed in and ``pattern`` splits on either spaces or hyphens (unless the text begins with a hyphen, in which case splitting is only on spaces, so that suffixes can be inflected). `allow_default_indicator` should be {true} if an empty indicator in angle brackets `<>` can be omitted and should be automatically added at the end of the multiword text (if no alternants) or at the end of each alternant (if alternants present). `angle_brackets_omittable` should be {true} if angle brackets can be omitted around a non-empty indicator in the presence of a blank lemma. In this case, if the combined indicator spec has no angle brackets, they will be added around the indicator (or around all indicators, if alternants are present). This only makes sense when `allow_blank_lemma` is specified. `allow_blank_lemma` should be {true} of if a blank lemma is allowed; in such a case, the calling function should substitute a default lemma, typically taken from the pagename. The return value is a table referred to as an '''alternant multiword spec''', and is of the form ```{ alternant_or_word_specs = {``alternant_or_word_spec``, ``alternant_or_word_spec``, ...}, post_text = "``text_at_end``", post_text_no_links = "``text_at_end_no_links``", post_text_translit = "``translit_of_text_at_end``" `(or nil)`, }``` where `alternant_or_word_spec` is either an '''alternant spec''' as returned by `parse_alternant()` or a '''multiword spec''' as described in the comment above `parse_multiword_spec()`. An alternant spec looks as follows: ```{ alternants = {``multiword_spec``, ``multiword_spec``, ...}, before_text = "``text_before_alternant``", before_text_no_links = "``text_before_alternant``", before_text_translit = "``translit_of_text_before_alternant``" `(or nil)`, }``` i.e. it is like what is returned by `parse_alternant()` but has extra `before_text` and `before_text_no_links` fields. ]==] function export.parse_inflected_text(text, props) if props.angle_brackets_omittable and not props.allow_blank_lemma then error("If 'angle_brackets_omittable' is specified, so should 'allow_blank_lemma'") end local alternant_multiword_spec = {alternant_or_word_specs = {}} local alternant_segments = split(text, "(%(%(.-%)%))") local last_post_text, last_post_text_no_links, last_post_text_translit for i = 1, #alternant_segments do if i % 2 == 1 then local segments = put.parse_balanced_segment_run(alternant_segments[i], "<", ">") -- Disable allow_default_indicator if alternants are present and we're processing -- the non-alternant text. Otherwise we will try to treat the non-alternant text -- surrounding the alternants as an inflected word rather than as raw text. local multiword_spec = parse_multiword_spec(segments, props, #alternant_segments ~= 1) for _, word_spec in ipairs(multiword_spec.word_specs) do insert(alternant_multiword_spec.alternant_or_word_specs, word_spec) end last_post_text = multiword_spec.post_text last_post_text_no_links = multiword_spec.post_text_no_links last_post_text_translit = multiword_spec.post_text_translit else local alternant_spec = parse_alternant(alternant_segments[i], props) alternant_spec.before_text = last_post_text alternant_spec.before_text_no_links = last_post_text_no_links alternant_spec.before_text_translit = last_post_text_translit insert(alternant_multiword_spec.alternant_or_word_specs, alternant_spec) end end alternant_multiword_spec.post_text = last_post_text alternant_multiword_spec.post_text_no_links = last_post_text_no_links alternant_multiword_spec.post_text_translit = last_post_text_translit -- Save boolean properties from `props`. We need at least `allow_default_indicator` when implementing -- `reconstruct_original_spec()`. alternant_multiword_spec.allow_default_indicator = props.allow_default_indicator alternant_multiword_spec.angle_brackets_omittable = props.angle_brackets_omittable alternant_multiword_spec.allow_blank_lemma = props.allow_blank_lemma return alternant_multiword_spec end -- Inflect alternants in ``alternant_spec`` (an object as returned by parse_alternant()). -- This sets the form values in ```alternant_spec``.forms` for all slots. -- (If a given slot has no values, it will not be present in ```alternant_spec``.forms`). local function inflect_alternants(alternant_spec, props) alternant_spec.forms = {} for _, multiword_spec in ipairs(alternant_spec.alternants) do export.inflect_multiword_or_alternant_multiword_spec(multiword_spec, props) iterate_slot_list_or_table(props, function(slot) if not props.skip_slot or not props.skip_slot(slot) then export.insert_forms(alternant_spec.forms, slot, multiword_spec.forms[slot]) end end) end end --[=[ Subfunction of `inflect_multiword_or_alternant_multiword_spec()`. This is used in building up the inflections of multiword expressions. The basic purpose of this function is to append a set of forms representing the inflections of a given inflected term in a given slot onto the existing forms for that slot. Given a multiword expression potentially consisting of several inflected terms along with fixed text in between, we work iteratively from left to right, adding the new forms onto the existing ones. Normally, all combinations of new and existing forms are created, meaning if there are M existing forms and N new ones, we will end up with M*N forms. However, some of these combinations can be rejected using the variant mechanism (see the description of get_variants below). Specifically, `formtable` is a table of per-slot forms, where the key is a slot and the value is a list of form objects (objects of the form {form=``form``, translit=``manual_translit``, footnotes=``footnotes``}). `slot` is the slot in question. `forms` specifies the forms to be appended onto the existing forms, and is likewise a list of form objects. `props` is the same as in `inflect_multiword_or_alternant_multiword_spec()`. `before_text` is the fixed text that goes before the forms to be added. `before_text_no_links` is the same as `before_text` but with any links (i.e. hyperlinks of the form [[``term``]] or [[``term``|``display``]]) converted into raw terms using remove_links() in [[Module:links]], and `before_text_translit` is optional manual translit of `before_text_no_links`. Note that the value "?" in a form is "infectious" in that if either the existing or new form has the value "?", the resulting combination will also be "?". This allows "?" to be used to mean "unknown". ]=] local function append_forms(props, formtable, slot, forms, before_text, before_text_no_links, before_text_translit) if not forms then return end local old_forms = formtable[slot] or {{form = ""}} local ret_forms = {} for _, old_form in ipairs(old_forms) do for _, form in ipairs(forms) do local old_form_vars = props.get_variants and props.get_variants(old_form.form) or "" local form_vars = props.get_variants and props.get_variants(form.form) or "" if old_form_vars ~= "" and form_vars ~= "" and old_form_vars ~= form_vars then -- Reject combination due to non-matching variant codes. else local new_formval local new_translit if old_form.form == "?" or form.from == "?" then new_formval = "?" else new_formval = old_form.form .. before_text .. form.form if old_form.translit or before_text_translit or form.translit then if not props.lang then error("Internal error: If manual translit is given, 'props.lang' must be set") end if not before_text_translit then before_text_translit = props_transliterate(props, before_text_no_links) or "" end local old_translit = old_form.translit or props_transliterate(props, m_links.remove_links(old_form.form)) or "" local translit = form.translit or props_transliterate(props, m_links.remove_links(form.form)) or "" new_translit = old_translit .. before_text_translit .. translit end end local new_formobj local new_footnotes = export.combine_footnotes(old_form.footnotes, form.footnotes) if new_formval == form.form and new_translit == form.translit then -- Automatically preserve metadata when possible. new_formobj = shallow_copy(form) new_formobj.footnotes = new_footnotes else new_formobj = {form=new_formval, translit=new_translit, footnotes=new_footnotes} if props.combine_metadata then props.combine_metadata { slot = slot, dest_form = new_formobj, form1 = old_form, form2 = form, between_text = before_text, between_text_no_links = before_text_no_links, between_text_translit = before_text_translit, } end end insert(ret_forms, new_formobj) end end end formtable[slot] = ret_forms end --[==[ Top-level inflection function. Create the inflections of a noun, verb, adjective or similar. `alternant_multiword_spec` is as returned by `parse_inflected_text` and describes the properties of the term to be inflected, including all the user-provided inflection specifications (e.g. the number, gender, conjugation/declension/etc. of each word) and the surrounding text. `props` indicates how to do the actual inflection (see below). The resulting inflected forms are stored into the `.forms` property of `multiword_spec`. This property holds a table whose keys are slots (i.e. ID's of individual inflected forms, such as "pres_1sg" for the first-person singular present indicative tense of a verb) and whose values are lists of the form `{ form = ``form``, translit = ``manual_translit_or_nil``, footnotes = ``footnote_list_or_nil``}`, where ``form`` is a string specifying the value of the form (e.g. "ouço" for the first-person singular present indicative of the Portuguese verb [[ouvir]]); ``manual_translit_or_nil`` is the corresponding manual transliteration if needed (i.e. if the form is in a non-Latin script and the automatic transliteration is incorrect or unavailable), otherwise nil; and ``footnote_list_or_nil`` is a list of footnotes to be attached to the form, or nil for no footnotes. Note that currently footnotes must be surrounded by brackets, e.g "[archaic]", and should not begin with a capital letter or end with a period. (Conversion from "[archaic]" to "Archaic." happens automatically.) This function has no return value, but modifies `multiword_spec` in-place, adding the `forms` table as described above. After calling this function, call show_forms() on the `forms` table to convert the forms and footnotes given in this table to strings suitable for display. `props` is an object specifying properties used during inflection, as follows: ```{ slot_list = {{"``slot``", "``accel``"}, {"``slot``", "``accel``"}, ...}, slot_table = {``slot`` = "``accel``", ``slot`` = "``accel``", ...}, skip_slot = nil `or` __function__(slot), lang = nil `or` __lang_object__, inflect_word_spec = __function__(base), get_variants = nil 'or` __function__(formval), combine_metadata = nil `or` __function__(data), include_user_specified_links = __boolean__, }``` `slot_list` is a list of two-element lists of slots and associated accelerator tags. ``slot`` is arbitrary but should correspond with slot names as generated by `inflect_word_spec`. ``accel`` is the corresponding accelerator tags; e.g. if ``slot`` is "pres_1sg", ``accel`` might be "1|s|pres|ind". ``accel`` is actually unused during inflection, but is used during `show_forms()`, which takes the same `slot_list` as a property upon input. `slot_table` is a table mapping slots to associated accelerator tags and serves the same function as `slot_list`. Only one of `slot_list` or `slot_table` must be given. For new code it is preferable to use `slot_list` because this allows you to control the order of processing slots, which may occasionally be important. `skip_slot` is a function of one argument, a slot name, and should return a boolean indicating whether to skip the given slot during inflection. It can be used, for example, to skip singular slots if the overall term being inflected is plural-only, and vice-versa. `lang` is a language object. This is only used to generate manual transliteration. If the language is written in the Latin script or manual transliteration cannot be specified in the input to parse_inflected_text(), this can be omitted. (Manual transliteration is allowed if the `lang` object is set in the `props` passed to parse_inflected_text().) `inflect_word_spec` is the function to do the actual inflection. It is passed a single argument, which is a ``word_spec`` object describing the word to be inflected and the user-provided inflection specifications. It is exactly the same as was returned by the `parse_indicator_spec` function provided in the `props` sent on input to `parse_inflected_text`, but has additional fields describing the word to be inflected and the surrounding text, as follows: ```{ lemma = "``lemma``", before_text = "``text-before-word``", before_text_no_links = "``text-before-word-no-links``", before_text_translit = "``manual-translit-of-text-before-word``" or nil (if no manual translit or respelling was specified in the before-text) -- Fields as described in parse_indicator_spec() ... }``` Here ``lemma`` is the word to be inflected as specified by the user (including any links if so given), and the `before_text*` fields describe the raw text preceding the word to be inflected. Any other fields in this object are as set by `parse_inflected_text`, and describe things like the gender, number, conjugation/declension, etc. as specified by the user in the <...> spec following the word to be inflected. `inflect_word_spec` should initialize the `.forms` property of the passed-in ``word_spec`` object to the inflected forms of the word in question. The value of this property is a table of the same format as the `.forms` property that is ultimately generated by inflect_multiword_or_alternant_multiword_spec() and described above near the top of this documentation: i.e. a table whose keys are slots and whose values are lists of the form `{ form = ``form``, translit = ``manual_translit_or_nil``, footnotes = ``footnote_list_or_nil``}`. `get_variants` is either {nil} or a function of one argument (a string, a form value). The purpose of this function is to ensure that in a multiword term where a given slot has more than one possible variant, the final output has only parallel variants in it. For example, feminine nouns and adjectives in Russian have two possible endings, one typically in -ой (-oj) and the other in -ою (-oju). If we have a feminine adjective-noun combination (or a hyphenated feminine noun-noun combination, or similar), and we don't specify `get_variants`, we'll end up with four values for the instrumental singular: one where both adjective and noun end in -ой, one where both end in -ою, and two where one of the words ends in -ой and the other in -ою. In general if we have N words each with K variants, we'll end up with an explosion of N^K possibilities. `get_variants` avoids this by returning a variant code (an arbitary string) for each variant. If two words each have a non-empty variant code, and the variant codes disagree, the combination will be rejected. If `get_variants` is not provided, or either variant code is an empty string, or the variant codes agree, the combination is allowed. The recommended way to use `get_variants` is as follows: 1. During inflection in `inflect_word_spec`, add a special character or string to each of the variants generated for a given slot when there is more than one. (As an optimization, do this only when there is more than one word being inflected.) Special Unicode characters can be used for this purpose, e.g. U+FFF0, U+FFF1, ..., U+FFFD, which have no meaning in Unicode. 2. Specify `get_variants` as a function that pulls out and returns the special character(s) or string included in the variant forms. 3. When calling show_forms(), specify a `canonicalize` function that removes the variant code character(s) or string from each form before converting to the display form. See [[Module:hi-verb]] and [[Module:hi-common]] for an example of doing this in a generalized fashion. (Look for add_variant_codes(), get_variants() and remove_variant_codes().) `combine_metadata` is a function that is invoked when combining two form objects along along with in-between text and storing into a destination form object. When this happens, if the the form value and translit in the first form object is empty and the in-between text is likewise empty (which regularly happens when appending the form object describing the first word in a multiword expression to empty base text), the second form object is simply shallow-copied along with all of its metadata, and any footnotes are combined appropriately (normally the first form object is such a case won't have footnotes). Otherwise, a new form object is constructed by combining the form values, translit and footnotes from the two objects and in-between text, and calling `combine_metadata` to combine any other metadata. Leave this unspecified if there is no additional metadata or if you don't want any metadata carried over. (Examples of metadata that should generally not be carried over are glosses of individual words, sense ID's and similar word-level properties that can't easily be combined to generate a multiword equivalent. Examples of metadata that should be carried over and combined are qualifiers, labels and certain boolean properties such as an uncertainty flag indicating that a given form is uncertain. For some metadata, it is more complex; for example, if both source words have the same gender or part of speech, the destination should keep that value, but if they differ, it may be safest to leave the field blank.) This function, if specified, is called with a single argument as follows: ```{ slot = "__string__", dest_form = __formobj__, form1 = __formobj__, form2 = __formobj__, between_text = "__string__", between_text_no_links = "__string__", between_text_translit = "__string__" `or` nil }``` Here, `slot` is the slot whose forms are being constructed. `dest_form` is the destination form object into which the combined metadata should be written, and is pre-populated with appropriate `form`, `translit` and `footnotes` fields. `form1` and `form2` are the two source forms being combined, and `between_text` is the text to be inserted between the two source forms. `between_text_no_links` is the same as `between_text` but with double-bracket links removed, and `between_text_translit` is the manual transliteration of `between_text_no_links`, if specified. The function should return nothing, but should side-effect `dest_form` as appropriate. `include_user_specified_links`, if given, ensures that user-specified links in the raw text surrounding a given word are preserved in the output. If omitted or set to false, such links will be removed and the whole multiword expression will be linked. ]==] function export.inflect_multiword_or_alternant_multiword_spec(multiword_spec, props) multiword_spec.forms = {} local is_alternant_multiword = not not multiword_spec.alternant_or_word_specs for _, word_spec in ipairs(is_alternant_multiword and multiword_spec.alternant_or_word_specs or multiword_spec.word_specs) do if word_spec.alternants then inflect_alternants(word_spec, props) else props.inflect_word_spec(word_spec) end iterate_slot_list_or_table(props, function(slot) if not props.skip_slot or not props.skip_slot(slot) then append_forms(props, multiword_spec.forms, slot, word_spec.forms[slot], (slot:find("linked") or props.include_user_specified_links) and word_spec.before_text or word_spec.before_text_no_links, word_spec.before_text_no_links, word_spec.before_text_translit ) end end) end if multiword_spec.post_text ~= "" then local pseudoform = {{form=""}} iterate_slot_list_or_table(props, function(slot) -- If slot is empty or should be skipped, don't try to append post-text. if (not props.skip_slot or not props.skip_slot(slot)) and multiword_spec.forms[slot] then append_forms(props, multiword_spec.forms, slot, pseudoform, (slot:find("linked") or props.include_user_specified_links) and multiword_spec.post_text or multiword_spec.post_text_no_links, multiword_spec.post_text_no_links, multiword_spec.post_text_translit ) end end) end end function export.map_word_specs(alternant_multiword_spec, fun) for _, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do if alternant_or_word_spec.alternants then for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do for _, word_spec in ipairs(multiword_spec.word_specs) do fun(word_spec) end end else fun(alternant_or_word_spec) end end end function export.create_footnote_obj() return { notes = {}, seen_notes = {}, noteindex = 1, seen_refs = {}, } end function export.get_footnote_text(footnotes, footnote_obj) if not footnotes then return "" end local link_indices = {} local all_refs = {} for _, footnote in ipairs(footnotes) do local refs footnote, refs = export.expand_footnote_or_references(footnote) if footnote then local this_noteindex = footnote_obj.seen_notes[footnote] if not this_noteindex then -- Generate a footnote index. this_noteindex = footnote_obj.noteindex footnote_obj.noteindex = footnote_obj.noteindex + 1 insert(footnote_obj.notes, '<sup style="color: var(--wikt-palette-red, red)">' .. this_noteindex .. '</sup>' .. footnote) footnote_obj.seen_notes[footnote] = this_noteindex end insert_if_not(link_indices, this_noteindex) end if refs then for _, ref in ipairs(refs) do if not ref.name then local this_refhash = footnote_obj.seen_refs[ref.text] if not this_refhash then -- Different text needs to have different auto-generated names, globally across the entire page, -- including across different invocations of {{it-verb}} or {{it-conj}}. The easiest way to accomplish -- this is to use a message-digest hashing function. It does not have to be cryptographically secure -- (MD5 is insecure); it just needs to have low probability of collisions. this_refhash = mw.hash.hashValue("md5", ref.text) footnote_obj.seen_refs[ref.text] = this_refhash end ref.autoname = this_refhash end -- I considered using "n" as the default group rather than nothing, to more clearly distinguish regular -- footnotes from references, but this requires referencing group "n" as <references group="n"> below, -- which is non-obvious. insert_if_not(all_refs, ref) end end end table.sort(link_indices) local function sort_refs(r1, r2) -- FIXME, we are now sorting on an arbitrary hash. Should we keep track of the order we -- saw the autonamed references and sort on that? if r1.autoname and r2.name then return true elseif r1.name and r2.autoname then return false elseif r1.name and r2.name then return r1.name < r2.name else return r1.autoname < r2.autoname end end table.sort(all_refs, sort_refs) for i, ref in ipairs(all_refs) do local refargs = {name = ref.name or ref.autoname, group = ref.group} all_refs[i] = mw.getCurrentFrame():extensionTag("ref", ref.text, refargs) end local link_text if #link_indices > 0 then link_text = '<sup style="color: var(--wikt-palette-red, red)">' .. concat(link_indices, ",") .. '</sup>' else link_text = "" end local ref_text = concat(all_refs) if link_text ~= "" and ref_text ~= "" then return link_text .. "<sup>,</sup>" .. ref_text else return link_text .. ref_text end end --[==[ Add links around words in a term. If multiword_only, do it only in multiword terms. ]==] function export.add_links(form, multiword_only) if form == "" or form == " " then return form end if not form:find("%[%[") then if ufind(form, "[%s%p]") then --optimization to avoid loading [[Module:headword]] on single-word forms local m_headword = require("Module:headword") if m_headword.head_is_multiword(form) then form = m_headword.add_multiword_links(form) end end if not multiword_only and not form:find("%[%[") then form = "[[" .. form .. "]]" end end return form end --[==[ Remove redundant link surrounding entire term. ]==] function export.remove_redundant_links(term) return (term:gsub("^%[%[([^%[%]|]*)%]%]$", "%1")) end --[==[ Add links to all before and after text; for use in inflection modules that preserve links in multiword lemmas and include links in non-lemma forms rather than allowing the entire form to be a link. If `remember_original`, remember the original user-specified before/after text so we can reconstruct the original spec later. `add_links` is a function of one argument to add links to a given piece of text; if unspecified, it defaults to `export.add_links`. ]==] function export.add_links_to_before_and_after_text(alternant_multiword_spec, remember_original, add_links) add_links = add_links or export.add_links local function add_links_remember_original(object, field) if remember_original then object["user_specified_" .. field] = object[field] end object[field] = add_links(object[field]) end for _, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do add_links_remember_original(alternant_or_word_spec, "before_text") if alternant_or_word_spec.alternants then for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do for _, word_spec in ipairs(multiword_spec.word_specs) do add_links_remember_original(word_spec, "before_text") end add_links_remember_original(multiword_spec, "post_text") end end end add_links_remember_original(alternant_multiword_spec, "post_text") end --[==[ Reconstruct the original overall spec from the output of parse_inflected_text(), so we can use it in the language-specific acceleration module in the implementation of {{tl|pt-verb form of}} and the like. `props` is an optional table of properties. Currently only `preprocess_angle_bracket_spec` is recognized, and is an optional function of one argument that is called to process an angle-bracket spec before inserting into the reconstructed spec. ]==] function export.reconstruct_original_spec(alternant_multiword_spec, props) local parts = {} props = props or {} local function ins(txt) insert(parts, txt) end local function insert_angle_bracket_spec(spec) if props.preprocess_angle_bracket_spec then spec = props.preprocess_angle_bracket_spec(spec) end ins(spec) end for _, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do ins(alternant_or_word_spec.user_specified_before_text) if alternant_or_word_spec.alternants then ins("((") for i, multiword_spec in ipairs(alternant_or_word_spec.alternants) do if i > 1 then ins(",") end for _, word_spec in ipairs(multiword_spec.word_specs) do ins(word_spec.user_specified_before_text) ins(word_spec.user_specified_lemma) insert_angle_bracket_spec(word_spec.angle_bracket_spec) end ins(multiword_spec.user_specified_post_text) end ins("))") else ins(alternant_or_word_spec.user_specified_lemma) insert_angle_bracket_spec(alternant_or_word_spec.angle_bracket_spec) end end ins(alternant_multiword_spec.user_specified_post_text) local retval = concat(parts) if alternant_multiword_spec.allow_default_indicator then -- As a special case, if we see e.g. "amar<>", remove the <>. Don't do this if there are spaces or alternants. if not retval:find(" ") and not retval:find("%(%(") then local retval_no_angle_brackets = retval:match("^(.*)<>$") if retval_no_angle_brackets then return retval_no_angle_brackets end end end return retval end --[==[ Convert the forms in ``formtable`` (a form table, whose keys are slots and whose values are lists of form objects, each of which is a table of the form `form = ``form``, translit = ``manual_translit_or_nil``, footnotes = ``footnote_list_or_nil``, no_accel = ``true_to_suppress_accelerators``, ... `) into strings. The form table is side-effected. Each form list turns into a string consisting of a comma-separated list of linked forms, with accelerators (unless `no_accel` is set in a given form object). If `include_translit` is specified, each string consists of a comma-separated list of form values (each formatted as a link), an HTML `&lt;br/>`, and a comma-separated list of transliterations. `props` is a table used in generating the strings, as follows: ```{ lang = __lang_object__, lemmas = {"``lemma``", "``lemma``", ...}, slot_list = {{"``slot``", "``accel``"}, {"``slot``", "``accel``"}, ...}, slot_table = {``slot`` = "``accel``", ``slot`` = "``accel``", ...}, include_translit = __boolean__, create_footnote_obj = nil `or` __function__(), canonicalize = nil or __function__(formval), preprocess_forms = nil `or` __function__(data), no_deduplicate_forms = __boolean__, combine_metadata_during_dedup = nil `or` __function__(data), transform_accel_obj = nil `or` __function__(slot, form, accel_obj), format_forms = nil `or` __function__(data), generate_link = nil `or` __function__(data), format_tr = nil `or` __function__(data), join_spans = nil `or` __function__(data), footnotes = nil or {"``extra_footnote``", "``extra_footnote``", ...}, }``` `lemmas` is the list of lemmas, used in the accelerators. `slot_list` is a list of two-element lists of slots and associated accelerator tag sets. ``slot`` should correspond to slots generated during `inflect_multiword_or_alternant_multiword_spec()`. ``accel`` is the corresponding accelerator tag set; e.g. if ``slot`` is "pres_1sg", ``accel`` might be "1|s|pres|ind". ``accel`` is used in generating entries for accelerator support (see [[WT:ACCEL]]). `slot_table` is a table mapping slots to associated accelerator tag sets and serves the same function as `slot_list`. Only one of `slot_list` or `slot_table` must be given. For new code it is preferable to use `slot_list` because this allows you to control the order of processing slots, which may occasionally be important. `include_translit`, if given, causes transliteration to be included in the generated strings. The function works as follows: # Create an object to hold footnotes (customizable using `create_footnote_obj`). # Generate the comma-separated lemma form values and store in `.lemma` in the form table. # Loop over the slots specified using `slot_list` or `slot_table`. For each slot: ## Canonicalize the form values (customizable using `canonicalize`; by default does nothing). ## Preprocess the forms (customizable using `preprocess_forms`; by default does nothing). ## Unless `no_deduplicate_forms` is set, deduplicate forms in a slot sharing the same form value but possibly different transliteration. (This happens e.g. in Russian, where it is relatively common for a given form to have two possible transliterations, one reflecting a more nativized pronunciation where Cyrillic е triggers palatalization of the preceding consonant, and one reflecting a more "foreign" pronunciation where this palatalization does not happen. In such a case, the automatic transliteration would normally suffice for the more nativized pronunciation but the more "foreign" pronunciation will need manual transliteration.) As part of deduplication, footnotes will be combined using `combine_footnotes`; distinct manual transliterations will be combined into a list (meaning the `translit` field of form objects in some subsequent `props` functions may hold a list; this will be noted when possible); and any remaining metadata will be combined using the `combine_metadata_during_dedup` method, if provided. ## Add acceleration to all forms. The acceleration tag set associated with a given form comes from `slot_list` or `slot_table`, i.e. all forms in a given slot have the same tag set. However, different forms will have different associated transliterations stored into the accelerator object associated with the form, as well as possibly different lemmas. In particular, when there are multiple lemma forms, this is often due to alternative ways to pronounce the lemma (e.g. alternative stress positions or vowel lengths), and there are often associated non-lemma forms that match each lemma. An example given in the introduction is {{m+|uk|русин||Rusyn}}, stressed in the lemma as ''ру́син'' or ''руси́н'' with associated genitive singulars ''ру́сина'' and ''русина́''. We would like the auto-generated accelerator entry for {{m|uk|русина}} to show the variant ''ру́сина'' as having lemma ''ру́син'' and the variant ''русина́'' as having the lemma ''руси́н'', rather than showing both variants as having both lemmas, which is less accurate. As a result, the code that generates acceleration objects for forms matches up forms and lemmas one-to-one if possible. If this is not possible, the matching is usually one lemma to many forms, as in {{m+|uk|міст||bridge}} with genitive singular ''мо́сту'' or ''моста́'' (in which case all forms get the same lemma), or many lemmas to one form, as in {{m+|uk|черга||turn, queue}} stressed either ''че́рга'' or ''черга́'' with nominative singular only ''че́рги'' (in which case the single form gets assicated all lemmas). If there are multiple lemmas and multiple forms, the algorithm attempts to align them as evenly as possible (e.g. two lemma variants to four forms means the first two forms get assigned the first lemma variant and the last two forms get assigned the second lemma variant); this is often going to be incorrect, but (a) there's unlikely to be a single algorithm that works in all such circumstances, and (b) these cases are very rare. Finally, note the following: ##* No acceleration is assigned to a form if any of the following apply: (a) there are no lemmas given in `props.lemmas`; (b) the `no_accel` key in the form object has a non-falsy value; (c) the form value of the form is {"?"} or an em-dash ({"—"}); (d) the accelerator tag set is given as a hyphen {"-"}); or (e) the form value contains an internal link. ##* The accelerator code sets the `formval_for_link` key in each form object to the version of the form value that should be passed to `full_link()` in [[Module:links]]. This is usually the same as the passed-in form value, but differs when the entire form value is surrounded with a redundant internal link (which is removed). ##* The resulting accelerator object can be modified (or replaced entirely) by the `transform_accel_obj` function. This is used, for example, in [[Module:es-verb]], [[Module:pt-verb]] and other Romance-language verb conjugation modules (likewise [[Module:ar-verb]]) to replace the tag set with the original verb spec used to generate the verb, so that the accelerator code can generate the appropriate call to {{tl|es-verb form of}}, {{tl|pt-verb form of}} or the like, which computes the inflections, instead of directly listing the inflections. ## Format the forms into strings. The entire default process can be replaced using `format_forms`; otherwise the default algorithm works as follows: ### Generate the '''form value spans''', with one entry (a linked HTML-ized version of the form value) per form. This can be customized using `generate_link`. (Various modules do this. For example, the Arabic verb module includes qualifiers, labels, ID's and the like that can be specified by the user; the Portuguese and reintegrated Galician verb modules italicize certain superseded or otherwise less-desirable forms instead of linking them normally; the German verb module adds {{m|de|dass}} to subjunctive forms and optional pronouns to imperative forms; and the German adjective module adds articles to adjective forms normally accompanied by articles and the equivalent of "he/she is" etc. to predicate forms.) The default uses `full_link()` in [[Module:links]] (with transliteration generation disabled) concatenated with the appropriate footnote symbol(s) (if any). ### Generate the '''transliteration spans''', with one entry per distinct translit, auto-generated if manual translit isn't available. Note that, due to the earlier form value deduplication step, there may be multiple translits per form object. These translits are themselves deduplicated to get the list of spans. (Such duplication can happen, for example, in Arabic with terms containing a glottal stop in them; there may be multiple ways of spelling the glottal stop or ''hamza'' in Arabic, but only one way of transliterating it.) Each span consists of an object specifying the translit and the list of footnotes. These objects are then converted to formatted strings, either using `format_tr` if supplied or else calling `tag_translit()` in [[Module:script utilities]] and concatenating the appropriate footnote symbol(s) (if any). ### Combine the form value and transliteration spans. If `join_spans` is supplied, use it; otherwise, concatenate the form value spans (comma-separated) and (if available) transliteration spans (comma-separated), and (if appropriate) combine them using {<br />}. `create_footnote_obj` is an optional function of no arguments to create the footnote object used to track footnotes; see `create_footnote_obj()`. Customizing it is useful to prepopulate the footnote table using `get_footnote_text()`. `canonicalize` is an optional function of one argument (a form value) to canonicalize each form before processing; it can return nil for no change. The most common purpose of this function is to remove variant codes from the form value. See the documentation for `inflect_multiword_or_alternant_multiword_spec()` for a description of variant codes and their purpose. `preprocess_forms` is an optional function of one argument (a table of properties) to preprocess the form objects as a whole. It runs after `canonicalize` (meaning that the form values passed in are canonicalized) and before deduplication and the addition of acceleration info. The property table passed in has the following properties: * `slot`: The slot being processed. * `forms`: The list of form objects for this slot. * `accel_tag_set`: The accelerator tag set for this slot, taken from `slot_list` or `slot_table`. * `footnote_obj`: The footnote object returned by the `create_footnote_obj` property or the default `create_footnote_obj()` function. `preprocess_forms` should return a list of preprocessed form objects, or {nil} to use the passed-in `forms`. If this function does deduplication, you should set `no_deduplicate_forms` to disable the default deduplication process. `no_deduplicate_forms`, if set, disables the deduplication step (see above). `combine_metadata_during_dedup` is an optional function of one argument (a table of properties) to combine the metadata of deduplicated form objects. The property table passed in has the following properties: * `slot`: The slot being processed. * `existing_form`: The existing form object into which a duplicated form is being combined. * `dup_form`: The duplicated form being combined into `existing_form`. * `existing_form_pos`: The one-based position of the existing form in the deduplicated form list (not necessarily its original position). * `dup_form_pos`: The one-based position of the duplicated form in its original list. The following should be noted about the form objects passed in: # The form values in `.form` have been canonicalized using `.canonicalize`, if provided. # The form values in `existing_form` and `dup_form` are always the same. # The footnotes in `existing_form` have already been combined with those in `dup_form`. # If there was manual translit either in `existing_form` (prior to deduplication) or in `dup_form`, there will be manual translit in `existing_form.translit` that is a list and combines any previous accumulated translits in `existing_form` as well as the translit in `dup_form` (even if one of them was specified as {nil} indicating an automatic translit). This means that the translit in `existing_form.translit` is always either {nil} or a list of strings (and the same applies to `dup_form.translit`). `transform_accel_obj` is an optional function of three arguments (``slot``, ``formobj``, ``accel_obj``) to transform the default constructed accelerator object in ``accel_obj`` into an object that should be passed to `full_link()` in [[Module:links]]. It should return the new accelerator object, or {nil} for no acceleration. (If {nil} is returned, the corresponding form has no acceleration; this is unlike most customization functions, where returning {nil} causes the default algorithm to be invoked.) The function can destructively modify the accelerator object passed in. '''NOTE''': This is called even when the passed-in ``accel_obj`` is {nil} (see the (a) through (e) reasons above why no acceleration may be assigned to a form). Thus, your code needs to do something sensible in this case. The description above of how `show_forms()` works inclues various examples of modules that supply a `transform_accel_obj` function and the reasons for doing so. `format_forms`, if supplied, is a function that entirely replaces the formatting portion of `show_forms()`. An example of why you might want to do this is to get a different layout than the default, e.g. one where translit is displayed next to each form value instead of the form values and translits grouped and displayed on separate lines. Under normal circumstances, you should not do this, but instead customize the functions that replace specific parts of the default formatting algorithm (see below). This function is passed one argument (a table of properties) and should return a string (the formatted forms, ready to store into the slot in the form table) or {nil} to proceed with the default algorithm (see above). The property table passed in has the following properties: * `slot`: The slot being processed. * `forms`: The list of form objects, deduplicated and with accelerator info added. * `footnote_obj`: The footnote object returned by the `create_footnote_obj` property or the default `create_footnote_obj()` function. The following should be noted about the form objects in `forms`: # There are extra fields `formval_for_link` and `accel_obj`. The first one is as described above under the paragraph beginning "Add acceleration to all forms" under "The function works as follows". The second one is the accelerator object in the format expected by [[Module:links]]. # The `translit` field, if non-{nil}, is a list of transliterations rather than a single transliteration; this is due to the form value deduplication step. `generate_link` is an optional function to generate the link text for a given form value. It is passed a single argument (a table of properties) and should return a string, the formatted link. If it returns {nil}, the default algorithm (see above) is invoked. The property table passed in has the following properties: * `slot`: The slot being processed. * `form`: The form to be converted to a formatted link. As with the `format_forms` function described above, the form objects passed in contain extra fields `formval_for_link` and `accel_obj` (both of which will normally be used), and the `translit` field, if non-{nil}, is a list. * `pos`: The one-based position of the form being processed, in the list of form value spans. Rarely used. * `footnote_obj`: The footnote object returned by the `create_footnote_obj` property or the default `create_footnote_obj()` function. Normally used in order to get the footnote symbol associated with any footnotes in `footnotes`. The description above of how `show_forms()` works inclues various examples of modules that supply a `generate_link` function and the reasons for doing so. `format_tr` is an optional function to generate the formatted text for a given transliteration. It is passed a single argument (a table of properties) and should return a string, the formatted transliteration text. If it returns {nil}, the default algorithm (see above) is invoked. The property table passed in has the following properties: * `slot`: The slot being processed. * `tr_for_tag`: The transliteration to process. * `pos`: The one-based position of the transliteration being processed, in the list of transliteration spans. Rarely used. * `footnotes`: The list of footnotes associated with all form objects with this transliteration. (If there were multiple form objects with the same transliteration, the list of footnotes will have been generated using `combine_footnotes()`.) * `footnote_obj`: The footnote object returned by the `create_footnote_obj` property or the default `create_footnote_obj()` function. Normally used in order to get the footnote symbol associated with any footnotes in `footnotes`. `join_spans` is an optional function to join the processed form value and transliteration spans into a formatted string. It is passed a single argument (a table of properties) and should return the final string to store into the form table slot. If it returns {nil}, the default algorithm (see above) is invoked. The property table passed in has the following properties: * `slot`: The slot being processed. * `formval_spans`: A list of strings, the formatted form value spans. * `tr_spans`: A list of strings, the formatted transliteration spans. If there is no transliteration, this will be an empty list. A custom `join_spans` is provided by [[Module:de-verb]], which concatenates the form value spans vertically (using {"<br />"}) instead of horizontally using a comma, as is normal; this is because there is no translit and the form values are often long, containing extra words attached during `generate_link()`. The only exception is the `aux` slot holding the auxiliaries, which is concatenated horizontally using {" or "}. [[Module:de-adjective]] similarly provides a custom `join_spans` function that concatenates the form value spans vertically. ]==] function export.show_forms(formtable, props) local footnote_obj = props.create_footnote_obj and props.create_footnote_obj() or export.create_footnote_obj() local function fetch_formval_and_translit(entry, remove_links) local formval, translit if type(entry) == "table" then formval, translit = entry.form, entry.translit else formval = entry end if remove_links then formval = m_links.remove_links(formval) end return formval, translit end local lemma_formvals = {} for _, lemma in ipairs(props.lemmas) do local lemma_formval, _ = fetch_formval_and_translit(lemma) insert_if_not(lemma_formvals, lemma_formval) end formtable.lemma = #lemma_formvals > 0 and concat(lemma_formvals, ", ") or mw.loadData(headword_data_module).pagename -- For safety, since we in-place modify `lemmas` usually before processing a given slot, make a copy. local props_lemmas = shallow_copy(props.lemmas) for i, lemma in ipairs(props_lemmas) do props_lemmas[i] = shallow_copy(lemma) end local function do_slot(slot, accel_tag_set) local formobjs = formtable[slot] if formobjs then if type(formobjs) ~= "table" then error("Internal error: For slot '" .. slot .. "', expected table but saw " .. dump(formobjs)) end -- Maybe canonicalize the form values (e.g. remove variant codes and monosyllabic accents). if props.canonicalize then for _, form in ipairs(formobjs) do form.form = props.canonicalize(form.form) or form.form end end -- Preprocess the forms as a whole if called for. if props.preprocess_forms then formobjs = props.preprocess_forms { slot = slot, forms = formobjs, accel_tag_set = accel_tag_set, footnote_obj = footnote_obj, } or formobjs end -- Maybe deduplicate form values (happens e.g. in Russian with two terms with the same Russian form but -- different translits). if not props.no_deduplicate_forms then local deduped_formobjs = {} for i, form in ipairs(formobjs) do local function combine_forms(existing_form, dup_form, pos) assert(existing_form.form == dup_form.form) -- Combine footnotes. existing_form.footnotes = export.combine_footnotes(existing_form.footnotes, dup_form.footnotes) -- If translit is being generated, and there's manual translit associated with either form, we -- need to generate any missing translits and combine them, taking into account the fact that a -- translit value may actually be a list of translits (particularly with the existing form if we -- already combined an item with manual translit into it). if props.include_translit and form_value_transliterable(existing_form.form) and ( existing_form.translit or dup_form.translit) then local combined_translit if not existing_form.translit then combined_translit = { props_transliterate(props, m_links.remove_links(existing_form.form)) } elseif type(existing_form.translit) == "string" then combined_translit = {existing_form.translit} else combined_translit = existing_form.translit end local dup_form_translit = dup_form.translit if not dup_form_translit then -- dup_form.form is the same as existing_form.form (see assert above), but this is -- defensive programming in case that changes dup_form_translit = {props_transliterate(props, m_links.remove_links(dup_form.form))} elseif type(dup_form_translit) == "string" then dup_form_translit = {dup_form_translit} end for _, translit in ipairs(dup_form_translit) do insert_if_not(combined_translit, translit) end existing_form.translit = combined_translit end if props.combine_metadata_during_dedup then props.combine_metadata_during_dedup { slot = slot, existing_form = existing_form, existing_form_pos = pos, dup_form = dup_form, dup_form_pos = i, } end end insert_if_not(deduped_formobjs, form, { key = function(formobj) return formobj.form end, combine = combine_forms, }) end formobjs = deduped_formobjs end -- Add acceleration info to form objects. for i, form in ipairs(formobjs) do local formval = form.form if not form_value_transliterable(formval) then form.formval_for_link = formval else local formval_for_link formval_for_link = formval -- remove redundant link surrounding entire form formval_for_link = export.remove_redundant_links(formval_for_link) form.formval_for_link = formval_for_link -------------------- Compute the accelerator object. ----------------- local accel_obj if props_lemmas[1] and not form.no_accel and accel_tag_set ~= "-" then -- If there is more than one form or more than one lemma, things get tricky. Often, there are -- the same number of forms as lemmas, e.g. for Ukrainian [[зимовий]] "wintry; winter (rel.)", -- which can be stressed зимо́вий or зимови́й with corresponding masculine/neuter genitive -- singulars зимо́вого or зимово́го etc. In this case, usually the forms and lemmas match up so -- we do this. If there are different numbers of forms than lemmas, it's usually one lemma -- against several forms e.g. Ukrainian [[міст]] "bridge" with genitive singular мо́сту or моста́ -- (accent patterns b or c) or [[ложка|ло́жка]] "spoon" with nominative plural ло́жки or ложки́ -- (accent patterns a or c). Here, we should assign the same lemma to both forms. The opposite -- can happen, e.g. [[черга]] "turn, queue" stressed че́рга or черга́ with nominative plural only -- че́рги (accent patterns a or d). Here we should assign both lemmas to the same form. In more -- complicated cases, with more than one lemma and form and different numbers of each, we try -- to align them as much as possible, e.g. if there are somehow eight forms and three lemmas, -- we assign lemma 1 to forms 1-3, lemma 2 to forms 4-6 and lemma 3 to forms 7 and 8, and -- conversely if there are somehow three forms and eight lemmas. This is likely to be wrong, but -- (a) there's unlikely to be a single algorithm that works in all such circumstances, and (b) -- these cases are vanishingly rare or nonexistent. Properly we should try to remember which -- form was generated by which lemma, but that is significant extra work for little gain. local first_lemma, last_lemma if #formobjs >= #props_lemmas then -- More forms than lemmas. Try to even out the forms assigned per lemma. local forms_per_lemma = math.ceil(#formobjs / #props_lemmas) first_lemma = math.floor((i - 1) / forms_per_lemma) + 1 last_lemma = first_lemma else -- More lemmas than forms. Try to even out the lemmas assigned per form. local lemmas_per_form = math.ceil(#props_lemmas / #formobjs) first_lemma = (i - 1) * lemmas_per_form + 1 last_lemma = math.min(first_lemma + lemmas_per_form - 1, #props_lemmas) end local accel_lemma, accel_lemma_translit if first_lemma == last_lemma then accel_lemma, accel_lemma_translit = fetch_formval_and_translit(props_lemmas[first_lemma], "remove links") else accel_lemma = {} accel_lemma_translit = {} for j=first_lemma, last_lemma do local this_accel_lemma, this_accel_lemma_translit = fetch_formval_and_translit(props_lemmas[j], "remove links") -- Do not use insert() especially for the translit because it may be nil and in -- that case we want gaps in the array. accel_lemma[j - first_lemma + 1] = this_accel_lemma accel_lemma_translit[j - first_lemma + 1] = this_accel_lemma_translit end end local accel_translit if props.include_translit and form.translit then if type(form.translit) == "table" then accel_translit = concat(form.translit, ", ") elseif type(form.translit) == "string" then accel_translit = form.translit else error(("Internal error: For slot '%s', form translit is not a table or string: %s"): format(slot, dump(form.translit))) end end accel_obj = { form = accel_tag_set, translit = accel_translit, lemma = accel_lemma, lemma_translit = props.include_translit and accel_lemma_translit or nil, } end -- Postprocess if requested. if props.transform_accel_obj then accel_obj = props.transform_accel_obj(slot, form, accel_obj) end form.accel_obj = accel_obj end end -- Format the form objects into a string for insertion into the table. local formatted_forms if props.format_forms then formatted_forms = props.format_forms { slot = slot, forms = formobjs, footnote_obj = footnote_obj, } end if not formatted_forms then -- Default algorithm: Separate form values and translits and concatenate on separate lines. -- Form values have already been deduplicated but we may need to deduplicate translits (this happens -- e.g. in Arabic where there may be multiple ways of spelling a hamza in the Arabic script but only -- one way in transliteration). local formval_spans = {} local tr_spans = {} for i, form in ipairs(formobjs) do local link if props.generate_link then link = props.generate_link { slot = slot, pos = i, form = form, footnote_obj = footnote_obj, } end if not link then link = m_links.full_link { lang = props.lang, term = form.formval_for_link, tr = "-", accel = form.accel_obj } .. export.get_footnote_text(form.footnotes, footnote_obj) end formval_spans[i] = link if props.include_translit then local translits = form.translit or props_transliterate(props, m_links.remove_links(form.form)) if type(translits) == "string" then translits = {translits} end for _, tr in ipairs(translits) do local tr_for_tag = tr -- formerly may have differed due to old-style footnote symbols insert_if_not(tr_spans, { tr_for_tag = tr_for_tag, footnotes = form.footnotes, }, { key = function(trobj) return trobj.tr_for_tag end, combine = function(trobj, newtrobj) -- Combine footnotes. trobj.footnotes = export.combine_footnotes(trobj.footnotes, newtrobj.footnotes) end, }) end end end for i, tr_span in ipairs(tr_spans) do local formatted_tr if props.format_tr then formatted_tr = props.format_tr { slot = slot, pos = i, tr_for_tag = tr_span.tr_for_tag, footnotes = tr_span.footnotes, footnote_obj = footnote_obj, } end if not formatted_tr then formatted_tr = require(script_utilities_module).tag_translit(tr_span.tr_for_tag, props.lang, "default", " style=\"color: var(--wikt-palette-grey-8,#888);\"") .. export.get_footnote_text(tr_span.footnotes, footnote_obj) end tr_spans[i] = formatted_tr end if props.join_spans then formatted_forms = props.join_spans { slot = slot, formval_spans = formval_spans, tr_spans = tr_spans, } end if not formatted_forms then local formval_span = concat(formval_spans, ", ") local tr_span if #tr_spans > 0 then tr_span = concat(tr_spans, ", ") end if tr_span then formatted_forms = formval_span .. "<br />" .. tr_span else formatted_forms = formval_span end end end formtable[slot] = formatted_forms else formtable[slot] = "—" end end iterate_slot_list_or_table(props, do_slot) formtable.footnote = concat(footnote_obj.notes, "<br />") end --[==[ Find the inflection spec for a given lemma by scraping the contents of the section for the lemma's language, looking for the appropriate inflection template (which may in some cases be a headword template with inflections specified in the headword, as for {{tl|en-verb}}). An inflection ID can be given; in that case, the value of the appropriate ID param (usually `id=`) must match the specified ID; otherwise, any inflection template call of the right name will work. If anything goes wrong in the process, a string is returned describing the error message; otherwise an object is returned, where `template` is the template found (an object of the form returned by `find_templates` in [[Module:template parser]]). There is one param, an object with the following fields: * `lemma`: The page to scrape for inflection calls. * `langname`: The name of the language of the lemma (e.g. {"English"} or {"Icelandic"}). Only that section is scraped. * `infltemp`: A string specifying the name of the template containing the inflections, or a function of one argument, the template object (an object of the form returned by `find_templates` in [[Module:template parser]]), which should return true if the given object specifies an appropriate inflection template. * `idparam`: The name of the parameter holding the inflection ID used to disambiguate lemmas with multiple inflections. * `inflid`: The inflection ID that must match the value of the `idparam` parameter specified by the inflection template. If not specified, there must be exactly one matching inflection template present, and it must not have a value given for the `idparam` parameter. Otherwise, all matching inflection templates must have a value specified for the `idparam` parameter of the template, and there must be exactly one template whose `idparam` parameter value is the same as the value of `inflid`. Find the inflection spec by scraping the contents of the Icelandic section of `lemma`, looking for `infltemp` calls (where template is e.g. "is-ndecl", "is-adecl" or "is-conj") If `inflid` is given, it must match the value of the |id= param specified to the inflection template; otherwise, any inflection template call will work. If anything goes wrong in the process, a string is returned describing the error message; otherwise a table of inflections is returned, each containing a field `infl` with the inflection spec. The inflection spec comes from the |deriv=, |deriv2=, etc. params in the inflection template if specified and `is_deriv` is given; otherwise from 1=, 2=, etc. If `allow_empty_infl` is given, a missing inflection spec in 1= is allowed and converted to an empty string; otherwise, an error is signaled. ]==] function export.scrape_inflection(data) local langname, lemma, infltemp, inflid, idparam = data.langname, data.lemma, data.infltemp, data.inflid, data.idparam local lang_lemma = ("%s base lemma '[[%s]]'"):format(langname, lemma) local title = new_title(lemma) if title then local content = title:getContent() if content then local langtext = require(pages_module).get_section(content, langname) if langtext then local inflobjs_by_id = {} local inflobjs_without_id = {} local ordered_seen_ids = {} for template in require(template_parser_module).find_templates(langtext) do local matches if type(infltemp) == "string" then matches = template:get_name() == infltemp else matches = infltemp(template) end if matches then local inflobj = {template = template} local args = template:get_arguments() local tid = args[idparam] if tid then if inflobjs_by_id[tid] then return ("For %s base lemma '[[%s]]', saw id='%s' twice"):format( langname, lemma, tid ) end inflobjs_by_id[tid] = inflobj insert_if_not(ordered_seen_ids, tid) else insert(inflobjs_without_id, inflobj) end end end local function concat_ordered_seen_ids() local quoted_seen_ids = {} for _, seen_id in ipairs(ordered_seen_ids) do insert(quoted_seen_ids, "'" .. seen_id .. "'") end return m_table.serialCommaJoin(quoted_seen_ids, {dontTag = true}) end local formatted_template = type(infltemp) == "string" and ("[[Template:%s]]"):format(infltemp) or "inflection template" local formatted_template_sing_article = type(infltemp) == "string" and ("[[Template:%s]]"):format(infltemp) or "an inflection template" local formatted_template_plural = type(infltemp) == "string" and "'s" or "s" if ordered_seen_ids[1] and inflobjs_without_id[1] then return ("For %s, saw %s %s%s with id=%s as well as %s %s%s without ID; this is not allowed; " .. "with multiple %s calls, all must have id= params"):format(lang_lemma, #ordered_seen_ids, formatted_template, ordered_seen_ids[2] and formatted_template_plural or "", concat_ordered_seen_ids(), #inflobjs_without_id, formatted_template, inflobjs_without_id[2] and formatted_template_plural or "", formatted_template) elseif not ordered_seen_ids[1] and not inflobjs_without_id[1] then return ("For %s, found %s section but couldn't find any calls to %s"):format( lang_lemma, langname, formatted_template_sing_article) elseif #inflobjs_without_id > 1 then return ("For %s, found %s %s%s without ID's; this is not allowed; with multiple %s calls, " .. "all must have id= params"):format(lang_lemma, #inflobjs_without_id, formatted_template, formatted_template_plural, formatted_template) elseif inflid then if inflobjs_by_id[inflid] then return inflobjs_by_id[inflid] elseif ordered_seen_ids[1] then return ("For %s, found %s section but couldn't find any inflections matching ID '%s'; " .. "instead found ID's %s; you may have misspelled the ID"):format(lang_lemma, langname, inflid, concat_ordered_seen_ids()) else return ("For %s, found %s section with a single %s without ID, but ID requirement '%s' " .. "specified; consider removing the ID requirement"):format(lang_lemma, langname, formatted_template, inflid) end elseif inflobjs_without_id[1] then -- only one infl template, and it doesn't have an id=; return it return inflobjs_without_id[1] elseif ordered_seen_ids[2] then return ("For %s, saw %s %s%s with id=%s but with a request to return the inflection without " .. "ID; consider adding an ID restriction to the scrape request, e.g. '@@:%s' for " .. "self-scraping or '@%s:%s' for scraping from another page"):format(lang_lemma, #ordered_seen_ids, formatted_template, formatted_template_plural, concat_ordered_seen_ids(), ordered_seen_ids[1], usub(lemma, 1, 1), ordered_seen_ids[1]) else return ("For %s, found %s section with a single %s with id=%s, but the scrape request didn't " .. "specify an ID; consider removing the id= param from the %s call unless you expect to add " .. "more such calls to the page in the future (in which case add the ID to the scrape request, " .. "e.g. '@@:%s' for self-scraping or '@%s:%s' for scraping from another page"):format(lang_lemma, langname, formatted_template, concat_ordered_seen_ids(), formatted_template, ordered_seen_ids[1], usub(lemma, 1, 1), ordered_seen_ids[1]) end else return ("For %s, page exists but has no %s section"):format(lang_lemma, langname) end else return ("For %s, couldn't fetch contents for page; page may not exist"):format(lang_lemma) end else return ("Bad %s; couldn't create title object"):format(lang_lemma) end end return export jrmbo5eurcdrihtq1wcd55wbbadred9 حوت 0 298289 5775232 2188590 2026-10-14T08:11:44Z Octahedron80 267 /* ภาษาอาหรับ */ เก็บกวาด 5775232 wikitext text/x-wiki == ภาษาอาหรับ == === รากศัพท์ 1 === {{unk|ar|nocap=1}}; สันนิษฐานว่ามีต้นกำเนิดเก่าแก่เป็นพิเศษ ซึ่งอาจเปิดทางให้เกิดการสืบสร้างมาจากราก {{ar-root|ح ي و|nocat=1}} “มีชีวิต”<ref name="ELA">{{R:xaa:ELA|II|pages=378–379}}</ref> หรือ {{ar-root|ح و ي|nocat=1}} “บิดงอ, ขด; บรรจุ”—อันเป็นการสืบสร้างที่ไม่สามารถอธิบายได้ด้วยไวยากรณ์อาหรับที่สืบทอดกันมา อย่างไรก็ตาม ปัจจัยสร้างคำนาม{{cog|sem-nwe|-}} ''-ūt'' ที่เติมลงในคำยืมจาก{{bor|ar|arc|𐤇𐤅𐤄|tr=ḥwh|pos=สถานะสัมบูรณ์}} / {{m|arc|𐡇𐡅𐡉𐡄}} / {{m|arc|חויא|tr=ḥiwyā}} / {{m|syc|ܚܘܝܐ|tr=ḥewyā|t=งู}} (ซึ่งเป็นคำที่สืบเชื้อสายร่วมกับ{{cog|ar|حَيَّة|t=งู}} จากรากหลัง) [[ภาคผนวก:Aramaic terms only witnessed by borrowings|อาจทำให้เกิด]]รูปคำอาหรับที่เป็นไปได้คือ {{m|ar||*حِوُوت|t=งูทะเล}} ซึ่งเมื่อเป็นไปตามกฎทางเสียงของอาหรับที่ห้ามลำดับเสียง ''iwū'' และ ''iyū'' โดยเปลี่ยนไปใช้ ''ū'' แทน (เช่นในรูปบุรุษที่สามเพศชายพหูพจน์อดีตกาลของ {{m|ar|نَسِيَ}}) จึงถูกลดทอนรูปกลายมาเป็น {{m-self|ar|حُوت}} ส่วนกรณีของที่มาจากนอก{{cog|sem|-}} มีการพิจารณาว่าอาจมาจาก{{bor|ar|egy|-}} โดยพิจารณาจากชื่อปลา {{m|egy|hwn}}, {{m|egy|hwṯn}} และ {{m|egy|ḥmt}}<ref name="ELA"/> แต่ยังไม่ได้ข้อสรุปใด ๆ ==== คำนาม ==== {{ar-noun|حُوت|m|pl=حِيتَان,أَحْوَات}} # [[ปลา]] #: {{syn|ar|سَمَك}} #* {{RQ:ar:Lévi-Provençal:1934|page=235 (٤٣) 4}} #* {{quote-book|year=1154|ar|author={{w|al-Idrisi|al-ʾIdrīsīy}}|title=ar:نزهة المشتاقใน اختراق الآفاق|section=[[wikisource:ar:نزهة المشتاق في اختراق الآفاق/الإقليم الثالث/الجزء الثاني|{{lang|ar|الإقليم الأول - الجزء الثاني}}]]|passage=وهذه البحيرة من أعاجيب الدنيا وذلك أن بها اثني عشر نوعاً من السمك يوجد منهاใน كل شهر نوع واحد لا يمتزج بغيره من أصناف السمك فإذا تم الشهر لم يوجد شيء من ذلك النوع في الشهر الآتي ثم يوجد في الشهر الآتي صنف من السمك آخر غير الصنف الأول لا يمتزج بغيره هكذا لكل شهر نوع من السمك لا يمتزج بسمك غيره إلى كمال السنة هكذا في كل عام وهذه الاثنا عشر نوع من ال'''حوت''' التي ذكرناها هي البوري والقاجوج والمحل والطلنط والاشبلينيات والشلبة والقاروض واللاج والجوجة والكحلاء والطنفلو والقلا.|t=ทะเลสาบแห่งนี้เป็นหนึ่งในสิ่งมหัศจรรย์ของโลก ด้วยเหตุว่ามีปลาอยู่สิบสองชนิด ในแต่ละเดือนจะพบเพียงชนิดเดียวโดยไม่ปะปนกับปลาชนิดอื่น และเมื่อสิ้นเดือน ก็จะไม่พบปลาชนิดนั้นในเดือนถัดไปเลย จากนั้นในเดือนถัดไปก็จะพบปลาอีกชนิดหนึ่งซึ่งต่างจากชนิดแรกและไม่ปะปนกับชนิดอื่นเช่นกัน เป็นเช่นนี้ทุกเดือนที่มีปลาหนึ่งชนิดไม่ปะปนกับปลาอื่นจนครบหนึ่งปี และเป็นเช่นนี้ในทุก ๆ ปี ซึ่งปลาทั้งสิบสองชนิดนี้ที่พวกเราได้กล่าวถึง ได้แก่ [มีรายชื่อปลาตามมา ซึ่งบางชนิดไม่สามารถระบุได้แน่ชัด]}} # [[วาฬ]] # {{lb|ar|มักใช้ร่วมกับคำนำหน้านามชี้เฉพาะ}} [[กลุ่มดาวปลา]], [[ราศีมีน]] ===== การผันรูป ===== {{ar-decl-noun|حُوت|pl=حِيتَان|pl2=أَحْوَات}} ===== ลูกคำ ===== * {{l|ar|فَم الحُوت|t=[[ดาวโฟมัลฮอต]]}} * {{l|ar|حَوَّات}}, {{l|ar|حَاوِت|t=[[คนหาปลา]], [[ชาวประมง]]}} ===== คำสืบทอด ===== * {{desc|xaa|حوت|tr=ḥūt, ḥawt}} * {{desc|arq|حوت|tr=ḥūt}} * {{desc|ayl|حوت|tr=ḥūt}} * {{desc|mt|ħut}} * {{desc|ary|حوت|tr=ḥūt}} * {{desc|aeb|حوت|tr=ḥūt}} * {{desc|kk|құт|t=February {{q|obsolete}}|bor=1}} * {{desc|sw|Hutu|t=Pisces|bor=1}} === รากศัพท์ 2 === {{nonlemma}} ==== คำกริยา ==== {{ar-verb form|+حوى<I>}} === อ้างอิง === {{reflist}} 07npazdaescave01yvzqortx4nlj8kt 5775233 5775232 2026-10-14T08:13:34Z Octahedron80 267 /* คำนาม */ 5775233 wikitext text/x-wiki == ภาษาอาหรับ == === รากศัพท์ 1 === {{unk|ar|nocap=1}}; สันนิษฐานว่ามีต้นกำเนิดเก่าแก่เป็นพิเศษ ซึ่งอาจเปิดทางให้เกิดการสืบสร้างมาจากราก {{ar-root|ح ي و|nocat=1}} “มีชีวิต”<ref name="ELA">{{R:xaa:ELA|II|pages=378–379}}</ref> หรือ {{ar-root|ح و ي|nocat=1}} “บิดงอ, ขด; บรรจุ”—อันเป็นการสืบสร้างที่ไม่สามารถอธิบายได้ด้วยไวยากรณ์อาหรับที่สืบทอดกันมา อย่างไรก็ตาม ปัจจัยสร้างคำนาม{{cog|sem-nwe|-}} ''-ūt'' ที่เติมลงในคำยืมจาก{{bor|ar|arc|𐤇𐤅𐤄|tr=ḥwh|pos=สถานะสัมบูรณ์}} / {{m|arc|𐡇𐡅𐡉𐡄}} / {{m|arc|חויא|tr=ḥiwyā}} / {{m|syc|ܚܘܝܐ|tr=ḥewyā|t=งู}} (ซึ่งเป็นคำที่สืบเชื้อสายร่วมกับ{{cog|ar|حَيَّة|t=งู}} จากรากหลัง) [[ภาคผนวก:Aramaic terms only witnessed by borrowings|อาจทำให้เกิด]]รูปคำอาหรับที่เป็นไปได้คือ {{m|ar||*حِوُوت|t=งูทะเล}} ซึ่งเมื่อเป็นไปตามกฎทางเสียงของอาหรับที่ห้ามลำดับเสียง ''iwū'' และ ''iyū'' โดยเปลี่ยนไปใช้ ''ū'' แทน (เช่นในรูปบุรุษที่สามเพศชายพหูพจน์อดีตกาลของ {{m|ar|نَسِيَ}}) จึงถูกลดทอนรูปกลายมาเป็น {{m-self|ar|حُوت}} ส่วนกรณีของที่มาจากนอก{{cog|sem|-}} มีการพิจารณาว่าอาจมาจาก{{bor|ar|egy|-}} โดยพิจารณาจากชื่อปลา {{m|egy|hwn}}, {{m|egy|hwṯn}} และ {{m|egy|ḥmt}}<ref name="ELA"/> แต่ยังไม่ได้ข้อสรุปใด ๆ ==== คำนาม ==== {{ar-noun|حُوت|m|pl=حِيتَان,أَحْوَات}} # [[ปลา]] #: {{syn|ar|سَمَك}} #* {{RQ:ar:Lévi-Provençal:1934|page=235 (٤٣) 4}} #* {{quote-book|year=1154|ar|author={{w|al-Idrisi|al-ʾIdrīsīy}}|title=ar:نزهة المشتاقใน اختراق الآفاق|section=[[wikisource:ar:نزهة المشتاق في اختراق الآفاق/الإقليم الثالث/الجزء الثاني|{{lang|ar|الإقليم الأول - الجزء الثاني}}]]|passage=وهذه البحيرة من أعاجيب الدنيا وذلك أن بها اثني عشر نوعاً من السمك يوجد منهاใน كل شهر نوع واحد لا يمتزج بغيره من أصناف السمك فإذا تم الشهر لم يوجد شيء من ذلك النوع في الشهر الآتي ثم يوجد في الشهر الآتي صنف من السمك آخر غير الصنف الأول لا يمتزج بغيره هكذا لكل شهر نوع من السمك لا يمتزج بسمك غيره إلى كمال السنة هكذا في كل عام وهذه الاثنا عشر نوع من ال'''حوت''' التي ذكرناها هي البوري والقاجوج والمحل والطلنط والاشبلينيات والشلبة والقاروض واللاج والجوجة والكحلاء والطنفلو والقلا.|t=ทะเลสาบแห่งนี้เป็นหนึ่งในสิ่งมหัศจรรย์ของโลก ด้วยเหตุว่ามีปลาอยู่สิบสองชนิด ในแต่ละเดือนจะพบเพียงชนิดเดียวโดยไม่ปะปนกับปลาชนิดอื่น และเมื่อสิ้นเดือน ก็จะไม่พบปลาชนิดนั้นในเดือนถัดไปเลย จากนั้นในเดือนถัดไปก็จะพบปลาอีกชนิดหนึ่งซึ่งต่างจากชนิดแรกและไม่ปะปนกับชนิดอื่นเช่นกัน เป็นเช่นนี้ทุกเดือนที่มีปลาหนึ่งชนิดไม่ปะปนกับปลาอื่นจนครบหนึ่งปี และเป็นเช่นนี้ในทุก ๆ ปี ซึ่งปลาทั้งสิบสองชนิดนี้ที่พวกเราได้กล่าวถึง ได้แก่ [มีรายชื่อปลาตามมา ซึ่งบางชนิดไม่สามารถระบุได้แน่ชัด]}} # [[วาฬ]] # {{lb|ar|มักใช้ร่วมกับคำกำกับนามชี้เฉพาะ}} [[กลุ่มดาวปลา]], [[ราศีมีน]] ===== การผันรูป ===== {{ar-decl-noun|حُوت|pl=حِيتَان|pl2=أَحْوَات}} ===== ลูกคำ ===== * {{l|ar|فَم الحُوت|t=[[ดาวโฟมัลฮอต]]}} * {{l|ar|حَوَّات}}, {{l|ar|حَاوِت|t=[[คนหาปลา]], [[ชาวประมง]]}} ===== คำสืบทอด ===== * {{desc|xaa|حوت|tr=ḥūt, ḥawt}} * {{desc|arq|حوت|tr=ḥūt}} * {{desc|ayl|حوت|tr=ḥūt}} * {{desc|mt|ħut}} * {{desc|ary|حوت|tr=ḥūt}} * {{desc|aeb|حوت|tr=ḥūt}} * {{desc|kk|құт|t=February {{q|obsolete}}|bor=1}} * {{desc|sw|Hutu|t=Pisces|bor=1}} === รากศัพท์ 2 === {{nonlemma}} ==== คำกริยา ==== {{ar-verb form|+حوى<I>}} === อ้างอิง === {{reflist}} 01xdzq1ivr1gopeli2laa7fh7uor203 5775234 5775233 2026-10-14T08:14:36Z Octahedron80 267 /* คำสืบทอด */ 5775234 wikitext text/x-wiki == ภาษาอาหรับ == === รากศัพท์ 1 === {{unk|ar|nocap=1}}; สันนิษฐานว่ามีต้นกำเนิดเก่าแก่เป็นพิเศษ ซึ่งอาจเปิดทางให้เกิดการสืบสร้างมาจากราก {{ar-root|ح ي و|nocat=1}} “มีชีวิต”<ref name="ELA">{{R:xaa:ELA|II|pages=378–379}}</ref> หรือ {{ar-root|ح و ي|nocat=1}} “บิดงอ, ขด; บรรจุ”—อันเป็นการสืบสร้างที่ไม่สามารถอธิบายได้ด้วยไวยากรณ์อาหรับที่สืบทอดกันมา อย่างไรก็ตาม ปัจจัยสร้างคำนาม{{cog|sem-nwe|-}} ''-ūt'' ที่เติมลงในคำยืมจาก{{bor|ar|arc|𐤇𐤅𐤄|tr=ḥwh|pos=สถานะสัมบูรณ์}} / {{m|arc|𐡇𐡅𐡉𐡄}} / {{m|arc|חויא|tr=ḥiwyā}} / {{m|syc|ܚܘܝܐ|tr=ḥewyā|t=งู}} (ซึ่งเป็นคำที่สืบเชื้อสายร่วมกับ{{cog|ar|حَيَّة|t=งู}} จากรากหลัง) [[ภาคผนวก:Aramaic terms only witnessed by borrowings|อาจทำให้เกิด]]รูปคำอาหรับที่เป็นไปได้คือ {{m|ar||*حِوُوت|t=งูทะเล}} ซึ่งเมื่อเป็นไปตามกฎทางเสียงของอาหรับที่ห้ามลำดับเสียง ''iwū'' และ ''iyū'' โดยเปลี่ยนไปใช้ ''ū'' แทน (เช่นในรูปบุรุษที่สามเพศชายพหูพจน์อดีตกาลของ {{m|ar|نَسِيَ}}) จึงถูกลดทอนรูปกลายมาเป็น {{m-self|ar|حُوت}} ส่วนกรณีของที่มาจากนอก{{cog|sem|-}} มีการพิจารณาว่าอาจมาจาก{{bor|ar|egy|-}} โดยพิจารณาจากชื่อปลา {{m|egy|hwn}}, {{m|egy|hwṯn}} และ {{m|egy|ḥmt}}<ref name="ELA"/> แต่ยังไม่ได้ข้อสรุปใด ๆ ==== คำนาม ==== {{ar-noun|حُوت|m|pl=حِيتَان,أَحْوَات}} # [[ปลา]] #: {{syn|ar|سَمَك}} #* {{RQ:ar:Lévi-Provençal:1934|page=235 (٤٣) 4}} #* {{quote-book|year=1154|ar|author={{w|al-Idrisi|al-ʾIdrīsīy}}|title=ar:نزهة المشتاقใน اختراق الآفاق|section=[[wikisource:ar:نزهة المشتاق في اختراق الآفاق/الإقليم الثالث/الجزء الثاني|{{lang|ar|الإقليم الأول - الجزء الثاني}}]]|passage=وهذه البحيرة من أعاجيب الدنيا وذلك أن بها اثني عشر نوعاً من السمك يوجد منهاใน كل شهر نوع واحد لا يمتزج بغيره من أصناف السمك فإذا تم الشهر لم يوجد شيء من ذلك النوع في الشهر الآتي ثم يوجد في الشهر الآتي صنف من السمك آخر غير الصنف الأول لا يمتزج بغيره هكذا لكل شهر نوع من السمك لا يمتزج بسمك غيره إلى كمال السنة هكذا في كل عام وهذه الاثنا عشر نوع من ال'''حوت''' التي ذكرناها هي البوري والقاجوج والمحل والطلنط والاشبلينيات والشلبة والقاروض واللاج والجوجة والكحلاء والطنفلو والقلا.|t=ทะเลสาบแห่งนี้เป็นหนึ่งในสิ่งมหัศจรรย์ของโลก ด้วยเหตุว่ามีปลาอยู่สิบสองชนิด ในแต่ละเดือนจะพบเพียงชนิดเดียวโดยไม่ปะปนกับปลาชนิดอื่น และเมื่อสิ้นเดือน ก็จะไม่พบปลาชนิดนั้นในเดือนถัดไปเลย จากนั้นในเดือนถัดไปก็จะพบปลาอีกชนิดหนึ่งซึ่งต่างจากชนิดแรกและไม่ปะปนกับชนิดอื่นเช่นกัน เป็นเช่นนี้ทุกเดือนที่มีปลาหนึ่งชนิดไม่ปะปนกับปลาอื่นจนครบหนึ่งปี และเป็นเช่นนี้ในทุก ๆ ปี ซึ่งปลาทั้งสิบสองชนิดนี้ที่พวกเราได้กล่าวถึง ได้แก่ [มีรายชื่อปลาตามมา ซึ่งบางชนิดไม่สามารถระบุได้แน่ชัด]}} # [[วาฬ]] # {{lb|ar|มักใช้ร่วมกับคำกำกับนามชี้เฉพาะ}} [[กลุ่มดาวปลา]], [[ราศีมีน]] ===== การผันรูป ===== {{ar-decl-noun|حُوت|pl=حِيتَان|pl2=أَحْوَات}} ===== ลูกคำ ===== * {{l|ar|فَم الحُوت|t=[[ดาวโฟมัลฮอต]]}} * {{l|ar|حَوَّات}}, {{l|ar|حَاوِت|t=[[คนหาปลา]], [[ชาวประมง]]}} ===== คำสืบทอด ===== * {{desc|xaa|حوت|tr=ḥūt, ḥawt}} * {{desc|arq|حوت|tr=ḥūt}} * {{desc|ayl|حوت|tr=ḥūt}} * {{desc|mt|ħut}} * {{desc|ary|حوت|tr=ḥūt}} * {{desc|aeb|حوت|tr=ḥūt}} * {{desc|kk|құт|t=กุมภาพันธ์ {{q|เลิกใช้}}|bor=1}} * {{desc|sw|Hutu|t=กลุ่มดาวปลา, ราศีมีน|bor=1}} === รากศัพท์ 2 === {{nonlemma}} ==== คำกริยา ==== {{ar-verb form|+حوى<I>}} === อ้างอิง === {{reflist}} 16a0a8xi6vihs0d8brpzhe02gc0pojp 5775257 5775234 2026-10-14T09:20:59Z Octahedron80 267 /* รากศัพท์ 1 */ 5775257 wikitext text/x-wiki == ภาษาอาหรับ == === รากศัพท์ 1 === {{unk|ar|nocap=1}}; สันนิษฐานว่ามีต้นกำเนิดเก่าแก่เป็นพิเศษ ซึ่งอาจเปิดทางให้เกิดการสืบสร้างมาจากราก {{ar-root|ح ي و|nocat=1}} “มีชีวิต”<ref name="ELA">{{R:xaa:ELA|II|pages=378–379}}</ref> หรือ {{ar-root|ح و ي|nocat=1}} “บิดงอ, ขด; บรรจุ”—อันเป็นการสืบสร้างที่ไม่สามารถอธิบายได้ด้วยไวยากรณ์อาหรับที่สืบทอดกันมา อย่างไรก็ตาม ปัจจัยสร้างคำนาม{{cog|sem-nwe|-}} ''-ūt'' ที่เติมลงในคำยืมจาก{{bor|ar|arc|𐤇𐤅𐤄|pos=สถานะสัมบูรณ์}} / {{m|arc|𐡇𐡅𐡉𐡄}} / {{m|arc|חויא|tr=ḥiwyā}} / {{m|syc|ܚܘܝܐ|tr=ḥewyā|t=งู}} (ซึ่งเป็นคำที่สืบเชื้อสายร่วมกับ{{cog|ar|حَيَّة|t=งู}} จากรากหลัง) [[ภาคผนวก:Aramaic terms only witnessed by borrowings|อาจทำให้เกิด]]รูปคำอาหรับที่เป็นไปได้คือ {{m|ar||*حِوُوت|t=งูทะเล}} ซึ่งเมื่อเป็นไปตามกฎทางเสียงของอาหรับที่ห้ามลำดับเสียง ''iwū'' และ ''iyū'' โดยเปลี่ยนไปใช้ ''ū'' แทน (เช่นในรูปบุรุษที่สามเพศชายพหูพจน์อดีตกาลของ {{m|ar|نَسِيَ}}) จึงถูกลดทอนรูปกลายมาเป็น {{m-self|ar|حُوت}} ส่วนกรณีของที่มาจากนอก{{cog|sem|-}} มีการพิจารณาว่าอาจมาจาก{{bor|ar|egy|-}} โดยพิจารณาจากชื่อปลา {{m|egy|hwn}}, {{m|egy|hwṯn}} และ {{m|egy|ḥmt}}<ref name="ELA"/> แต่ยังไม่ได้ข้อสรุปใด ๆ ==== คำนาม ==== {{ar-noun|حُوت|m|pl=حِيتَان,أَحْوَات}} # [[ปลา]] #: {{syn|ar|سَمَك}} #* {{RQ:ar:Lévi-Provençal:1934|page=235 (٤٣) 4}} #* {{quote-book|year=1154|ar|author={{w|al-Idrisi|al-ʾIdrīsīy}}|title=ar:نزهة المشتاقใน اختراق الآفاق|section=[[wikisource:ar:نزهة المشتاق في اختراق الآفاق/الإقليم الثالث/الجزء الثاني|{{lang|ar|الإقليم الأول - الجزء الثاني}}]]|passage=وهذه البحيرة من أعاجيب الدنيا وذلك أن بها اثني عشر نوعاً من السمك يوجد منهاใน كل شهر نوع واحد لا يمتزج بغيره من أصناف السمك فإذا تم الشهر لم يوجد شيء من ذلك النوع في الشهر الآتي ثم يوجد في الشهر الآتي صنف من السمك آخر غير الصنف الأول لا يمتزج بغيره هكذا لكل شهر نوع من السمك لا يمتزج بسمك غيره إلى كمال السنة هكذا في كل عام وهذه الاثنا عشر نوع من ال'''حوت''' التي ذكرناها هي البوري والقاجوج والمحل والطلنط والاشبلينيات والشلبة والقاروض واللاج والجوجة والكحلاء والطنفلو والقلا.|t=ทะเลสาบแห่งนี้เป็นหนึ่งในสิ่งมหัศจรรย์ของโลก ด้วยเหตุว่ามีปลาอยู่สิบสองชนิด ในแต่ละเดือนจะพบเพียงชนิดเดียวโดยไม่ปะปนกับปลาชนิดอื่น และเมื่อสิ้นเดือน ก็จะไม่พบปลาชนิดนั้นในเดือนถัดไปเลย จากนั้นในเดือนถัดไปก็จะพบปลาอีกชนิดหนึ่งซึ่งต่างจากชนิดแรกและไม่ปะปนกับชนิดอื่นเช่นกัน เป็นเช่นนี้ทุกเดือนที่มีปลาหนึ่งชนิดไม่ปะปนกับปลาอื่นจนครบหนึ่งปี และเป็นเช่นนี้ในทุก ๆ ปี ซึ่งปลาทั้งสิบสองชนิดนี้ที่พวกเราได้กล่าวถึง ได้แก่ [มีรายชื่อปลาตามมา ซึ่งบางชนิดไม่สามารถระบุได้แน่ชัด]}} # [[วาฬ]] # {{lb|ar|มักใช้ร่วมกับคำกำกับนามชี้เฉพาะ}} [[กลุ่มดาวปลา]], [[ราศีมีน]] ===== การผันรูป ===== {{ar-decl-noun|حُوت|pl=حِيتَان|pl2=أَحْوَات}} ===== ลูกคำ ===== * {{l|ar|فَم الحُوت|t=[[ดาวโฟมัลฮอต]]}} * {{l|ar|حَوَّات}}, {{l|ar|حَاوِت|t=[[คนหาปลา]], [[ชาวประมง]]}} ===== คำสืบทอด ===== * {{desc|xaa|حوت|tr=ḥūt, ḥawt}} * {{desc|arq|حوت|tr=ḥūt}} * {{desc|ayl|حوت|tr=ḥūt}} * {{desc|mt|ħut}} * {{desc|ary|حوت|tr=ḥūt}} * {{desc|aeb|حوت|tr=ḥūt}} * {{desc|kk|құт|t=กุมภาพันธ์ {{q|เลิกใช้}}|bor=1}} * {{desc|sw|Hutu|t=กลุ่มดาวปลา, ราศีมีน|bor=1}} === รากศัพท์ 2 === {{nonlemma}} ==== คำกริยา ==== {{ar-verb form|+حوى<I>}} === อ้างอิง === {{reflist}} 4vywtfysgcejh5akf5kqxz2pzoooafm ख़ून 0 300739 5775282 5685188 2026-10-14T10:11:50Z Octahedron80 267 5775282 wikitext text/x-wiki {{also/auto}} == ภาษาฮินดี == === รูปแบบอื่น === * {{l|hi|खून}} === รากศัพท์ === {{bor+|hi|fa|خون|tr=xun}}, จาก{{der|hi|pal|𐭧𐭥𐭭}} === การออกเสียง === * {{hi-IPA}} * {{rhyme|hi|uːn}} * {{audio|hi|LL-Q1568 (hin)-AryamanA-ख़ून.wav}} === คำนาม === {{hi-noun|m|ur=خون}} # [[เลือด]] #: {{hi-x|भेड़िये के मुँह को इंसान का '''ख़ून''' लगा है।|The wolf has cultivated a taste for human '''blood'''.}} #: {{syn|hi|लहू|रक्त|रुधिर|लोह|लोहू|अस्र}} # [[การฆ่า]], [[ฆาตกรรม]] #: {{hi-x|'''ख़ून''' कल इसी सड़क पे हुआ था।|The '''murder''' happened in this street yesterday.}} #: {{syn|hi|हत्या|क़त्ल}} ==== การผันรูป ==== {{hi-ndecl|<M>}} ==== ลูกคำ ==== * {{l|hi|ख़ून करना}} * {{l|hi|ख़ूनी}} fc5ks1gmhck3oz6e6mjwl1tabtzcodd มอดูล:template parser 828 306082 5775192 4615346 2026-10-14T04:21:44Z Octahedron80 267 5775192 Scribunto text/plain --[==[ Parse wikitext into an editable node tree using [[Module:parser]]. This is a preprocessor-style structural parser, not a complete wikitext renderer. Template names, arguments and other values are expanded only when the caller requests them; native preprocessing is used where expansion is required. Main entry points: parse(text, transcluded) returns a "wikitext" root node. Its default is non-transclusion mode. find_templates(text, not_transcluded) and find_parameters(text, not_transcluded) default to transclusion mode. find_headings(text, i, j) uses non-transclusion mode and optional inclusive heading-level bounds. The find_* iterators return node, parent and key, including nested matches. They do not return a normalised name, argument table, raw text and index. Nodes inherit traversal, cloning and stringification from [[Module:parser]]. This module adds expansion and classes for wikitext, templates (including magic words), parameters, explicit arguments, recognised tags and headings. Template and parameter nodes retain their original source in .raw and their one-based byte position in .index; these snapshots do not follow edits. The parser uses backtracking and memoised failed routes. Keep performance changes grounded in profiling: callers may parse many pages in one run. Compatibility cases, such as exact lowercase onlyinclude delimiters, are intentional. Recheck them against MediaWiki before changing their behaviour. See the documentation subpage for the public API and its limitations. ]==] local export = {} local data_module = "Module:template parser/data" local load_module = "Module:load" local magic_words_data_module = "Module:data/magic words" local pages_module = "Module:pages" local parser_extension_tags_data_module = "Module:data/parser extension tags" local parser_module = "Module:parser" local scribunto_module = "Module:Scribunto" local string_pattern_escape_module = "Module:string/patternEscape" local string_replacement_escape_module = "Module:string/replacementEscape" local string_utilities_module = "Module:string utilities" local table_length_module = "Module:table/length" local table_shallow_copy_module = "Module:table/shallowCopy" local table_sorted_pairs_module = "Module:table/sortedPairs" local title_is_title_module = "Module:title/isTitle" local title_make_title_module = "Module:title/makeTitle" local title_new_title_module = "Module:title/newTitle" local title_redirect_target_module = "Module:title/redirectTarget" local require = require local m_parser = require(parser_module) local mw = mw local mw_title = mw.title local mw_uri = mw.uri local string = string local table = table local anchor_encode = mw_uri.anchorEncode local build_template -- defined as export.buildTemplate below local class_else_type = m_parser.class_else_type local concat = table.concat local encode_uri = mw_uri.encode local find = string.find local format = string.format local gsub = string.gsub local html_create = mw.html.create local insert = table.insert local is_node = m_parser.is_node local lower = string.lower local match = string.match local next = next local pairs = pairs local parse -- defined as export.parse below local parse_template_name -- defined below local pcall = pcall local rep = string.rep local select = select local sub = string.sub local title_equals = mw_title.equals local tostring = m_parser.tostring local type = type local umatch = mw.ustring.match --[==[ Lazy function imports. Each wrapper replaces its local binding with the imported function on first use, so later calls invoke that function directly. ]==] local function decode_entities(...) decode_entities = require(string_utilities_module).decode_entities return decode_entities(...) end local function encode_entities(...) encode_entities = require(string_utilities_module).encode_entities return encode_entities(...) end local function get_link_target(...) get_link_target = require(pages_module).get_link_target return get_link_target(...) end local function is_title(...) is_title = require(title_is_title_module) return is_title(...) end local function load_data(...) load_data = require(load_module).load_data return load_data(...) end local function make_title(...) make_title = require(title_make_title_module) return make_title(...) end local function new_title(...) new_title = require(title_new_title_module) return new_title(...) end local function pattern_escape(...) pattern_escape = require(string_pattern_escape_module) return pattern_escape(...) end local function php_htmlspecialchars(...) php_htmlspecialchars = require(scribunto_module).php_htmlspecialchars return php_htmlspecialchars(...) end local function php_ltrim(...) php_ltrim = require(scribunto_module).php_ltrim return php_ltrim(...) end local function php_trim(...) php_trim = require(scribunto_module).php_trim return php_trim(...) end local function redirect_target(...) redirect_target = require(title_redirect_target_module) return redirect_target(...) end local function replacement_escape(...) replacement_escape = require(string_replacement_escape_module) return replacement_escape(...) end local function scribunto_parameter_key(...) scribunto_parameter_key = require(scribunto_module).scribunto_parameter_key return scribunto_parameter_key(...) end local function shallow_copy(...) shallow_copy = require(table_shallow_copy_module) return shallow_copy(...) end local function sorted_pairs(...) sorted_pairs = require(table_sorted_pairs_module) return sorted_pairs(...) end local function split(...) split = require(string_utilities_module).split return split(...) end local function table_len(...) table_len = require(table_length_module) return table_len(...) end local function uupper(...) uupper = require(string_utilities_module).upper return uupper(...) end --[==[ Lazy object imports. Use "object or get_object()": the first call saves the object and clears its loader, and subsequent accesses use the saved object. ]==] local data local function get_data() data, get_data = load_data(data_module), nil return data end local frame local function get_frame() frame, get_frame = mw.getCurrentFrame(), nil return frame end local magic_words local function get_magic_words() magic_words, get_magic_words = load_data(magic_words_data_module), nil return magic_words end local parser_extension_tags local function get_parser_extension_tags() parser_extension_tags, get_parser_extension_tags = load_data(parser_extension_tags_data_module), nil return parser_extension_tags end ------------------------------------------------------------------------------------ -- -- Nodes -- ------------------------------------------------------------------------------------ local Node = m_parser.node() local new_node = Node.new --[==[ Public as export.expand(obj, frame_args). Expand a recognised parser node using its own method; return any other value unchanged. frame_args is an optional table for resolving parameter nodes, not a Scribunto frame object. ]==] local function expand(obj, frame_args) return is_node(obj) and obj:expand(frame_args) or obj end export.expand = expand --[==[ Default expansion concatenates the expanded array children. Subclasses override this where delimiters or native preprocessing are required. ]==] function Node:expand(frame_args) local output = {} for i = 1, #self do output[i] = expand(self[i], frame_args) end return concat(output) end local Wikitext = Node:new_class("wikitext") --[==[ Coalesce plain-text fragments and avoid unnecessary wrapper nodes. With force_node, retain a wikitext wrapper even around a string or another class; export.parse uses this so its root is always a wikitext node. ]==] function Wikitext:new(this, force_node) if type(this) ~= "table" then return force_node and new_node(self, {this}) or this elseif #this == 1 then local this1 = this[1] return force_node and class_else_type(this1) ~= "wikitext" and new_node(self, this) or this1 end local success, str = pcall(concat, this) if success then return force_node and new_node(self, {str}) or str end return new_node(self, this) end --[==[ Parameter slots: [1] is the name and [2] is the optional default, each a string or node. Extra pipe-separated fields are discarded from the tree: {{{a|b|c}}} has default "b". The complete invocation still survives in .raw. ]==] local Parameter = Node:new_class("parameter") --[==[ An equals sign in a default is literal parameter text, so undo any explicit argument wrapper produced by the shared template/parameter parsing route. ]==] function Parameter:new(this) local this2 = this[2] if class_else_type(this2) == "argument" then insert(this2, 2, "=") this2 = Wikitext:new(this2) end if this[3] == nil then this[2] = this2 else this = {this[1], this2} end return new_node(self, this) end function Parameter:__tostring() local output = {} for i = 1, #self do output[i] = tostring(self[i]) end return "{{{" .. concat(output, "|") .. "}}}" end --[==[ Expand the name and apply Scribunto's parameter-key conversion, including trimming and conversion of suitable numeric names to numeric table keys. ]==] function Parameter:get_name(frame_args) return scribunto_parameter_key(expand(self[1], frame_args)) end --[==[ Return the expanded default without consulting this parameter's supplied value. With no default, return unresolved {{{name}}} text, as in template expansion. ]==] function Parameter:get_default(frame_args) local default = self[2] if default ~= nil then return expand(default, frame_args) end return "{{{" .. expand(self[1], frame_args) .. "}}}" end --[==[ Look up the expanded name in frame_args, if supplied. A non-nil value, including an empty string, overrides the default. Without a supplied value, expand the default or retain unresolved {{{name}}} text. Parameter nodes do not automatically read the current frame's arguments. ]==] function Parameter:expand(frame_args) if frame_args == nil then return self:get_default() end local name = expand(self[1], frame_args) local val = frame_args[scribunto_parameter_key(name)] -- Parameter in use. if val ~= nil then return val end val = self[2] -- Default. if val ~= nil then return expand(val, frame_args) end return "{{{" .. name .. "}}}" end --[==[ Only explicitly keyed template arguments have an argument wrapper, with [1] as the key and [2] as the value. Implicit arguments remain strings or other nodes. Stringification and expansion reinsert the equals sign. ]==] local Argument = Node:new_class("argument") function Argument:new(this) local key = this._parse_data.key this = Wikitext:new(this) if key == nil then return this end return new_node(self, {Wikitext:new(key), this}) end function Argument:__tostring() return tostring(self[1]) .. "=" .. tostring(self[2]) end function Argument:expand(frame_args) return expand(self[1], frame_args) .. "=" .. expand(self[2], frame_args) end --[==[ All double-brace invocations use the template class, including parser functions, parser variables, missing pages and syntactically invalid names. [1] holds the unexpanded invocation name; [2] onwards hold arguments in source order. Normalisation and validation are deferred to the methods. ]==] local Template = Node:new_class("template") function Template:__tostring() local output = {} for i = 1, #self do output[i] = tostring(self[i]) end return "{{" .. concat(output, "|") .. "}}" end --[==[ Resolve and classify expanded names lazily. The templates cache stores a canonical template/function name, or false for a failed name. Parser-variable names have a separate cache; parser_functions stores each function's first (colon) argument separately, not a combined name/argument table. Caches are keyed by expanded name only, not by argument presence or redirect lookup mode. In particular, a cached pipe-bearing use of a parser-variable name can affect a later pipe-less use; redirect modes also share results. FIXME: Some parser functions have special argument handling (e.g. {{#SWITCH:}}). ]==] do local templates, parser_variables, parser_functions = {}, {}, {} local function retrieve_magic_word_data(chunk) local mgw_data = (magic_words or get_magic_words())[chunk] if mgw_data then return mgw_data end local normalized = uupper(chunk) mgw_data = magic_words[normalized] if mgw_data and not mgw_data.case_sensitive then return mgw_data end end -- Returns the name required to transclude the title object `title` using -- template {{ }} syntax. If the `shortcut` flag is set, then any calls -- which require a namespace prefix will use the abbreviated form where one -- exists (e.g. "Template:PAGENAME" becomes "T:PAGENAME"). local function get_template_invocation_name(title, shortcut) if not (is_title(title) and not title.isExternal) then error("Template invocations require a valid page title, which cannot contain an interwiki prefix.") end local namespace = title.namespace -- If not in the template namespace, include the prefix (or ":" if -- mainspace). if namespace ~= 10 then return get_link_target(title, shortcut) end -- If in the template namespace and it shares a name with a magic word, -- it needs the prefix "Template:". local text, fragment = title.text, title.fragment if fragment and fragment ~= "" then text = text .. "#" .. fragment end local colon = find(text, ":", nil, true) if not colon then local mgw_data = retrieve_magic_word_data(text) return mgw_data and mgw_data.parser_variable and get_link_target(title, shortcut) or text end local mgw_data = retrieve_magic_word_data(sub(text, 1, colon - 1)) if mgw_data and (mgw_data.parser_function or mgw_data.transclusion_modifier) then return get_link_target(title, shortcut) end -- Also if "Template:" is necessary for disambiguation (e.g. -- "Template:Category:Foo" can't be called with "Category:Foo"). local check = new_title(text, namespace) return check and title_equals(title, check) and text or get_link_target(title, shortcut) end export.getTemplateInvocationName = get_template_invocation_name --[==[ Return modifier/name chunks, the semantic subclass, and any colon argument. Recognise modifier priorities and magic-word case rules from the data. For page transclusions, validate and normalise the title and resolve local redirects unless disabled. Title validity does not require page existence. fragment controls whether a page fragment survives in the returned name. ]==] function parse_template_name(name, has_args, fragment, force_transclusion) local chunks, colon, start, n, p = {}, find(name, ":", nil, true), 1, 0, 0 while colon do local mgw_data = retrieve_magic_word_data(php_ltrim(sub(name, start, colon - 1))) if not mgw_data then break end local priority = mgw_data.priority if not (priority and priority > p) then local pf = mgw_data.parser_function and mgw_data.name or nil if pf then n = n + 1 chunks[n] = pf .. ":" return chunks, "parser function", sub(name, colon + 1) end break end n = n + 1 chunks[n] = mgw_data.name .. ":" start, p = colon + 1, priority colon = find(name, ":", start, true) end if start > 1 then name = sub(name, start) end name = php_trim(name) -- Parser variables can only take SUBST:/SAFESUBST: as modifiers. if not has_args and p <= 1 then local mgw_data = retrieve_magic_word_data(name) local pv = mgw_data and mgw_data.parser_variable and mgw_data.name or nil if pv then n = n + 1 chunks[n] = pv return chunks, "parser variable" end end -- Get the template title with the custom new_title() function in -- [[Module:title/newTitle]], with `allowOnlyFragment` set to false -- (e.g. "{{#foo}}" is invalid) and `allowRelative` set to true, for -- relative links for namespaces with subpages (e.g. "{{/foo}}"). local title = new_title(name, 10, false, true) if not (title and not title.isExternal) then return nil end if force_transclusion ~= "no_redirect" then -- Resolve any redirects. If the redirect target is an interwiki link, -- the template won't fail, but the redirect does not get resolved (i.e. -- the redirect page itself gets transcluded, so the template name -- should not be normalized to the target). local redirect = redirect_target(title, force_transclusion) if redirect and not redirect.isExternal then title = redirect end end -- If `fragment` is not true, unset it from the title object to prevent -- it from being included by get_template_invocation_name. if not fragment then title.fragment = "" end chunks[n + 1] = get_template_invocation_name(title) return chunks, "template" end --[==[ Return canonical name, semantic subclass and colon argument, or nil when validation fails or a recognised SUBST: modifier is present. SAFESUBST: and other remaining modifiers are omitted from the analytical name; MSGNW: is represented as a parser function with the page name as its first argument. force_transclusion is passed to redirectTarget on an uncached lookup; true selects its transclusion-based redirect check. "no_redirect" bypasses that check entirely. All modes share the name cache, so this is not a per-call guarantee once another lookup has populated the cache. ]==] local function process_name(self, frame_args, force_transclusion) local name = expand(self[1], frame_args) local has_args, norm = #self > 1 if not has_args then norm = parser_variables[name] if norm then return norm, "parser variable" end end norm = templates[name] if norm then local pf_arg1 = parser_functions[name] return norm, pf_arg1 and "parser function" or "template", pf_arg1 elseif norm == false then return nil end local chunks, subclass, pf_arg1 = parse_template_name(name, has_args, nil, force_transclusion) -- Fail if invalid. if not chunks then templates[name] = false return nil end local chunk1 = chunks[1] -- Fail on SUBST:. if chunk1 == "SUBST:" then templates[name] = false return nil -- Any modifiers are ignored. elseif subclass == "parser function" then local pf = chunks[#chunks] templates[name] = pf parser_functions[name] = pf_arg1 return pf, "parser function", pf_arg1 end -- Ignore SAFESUBST:, and treat MSGNW: as a parser function with the pagename as its first argument (ignoring any RAW: that comes after). if chunks[chunk1 == "SAFESUBST:" and 2 or 1] == "MSGNW:" then pf_arg1 = chunks[#chunks] local pf = "MSGNW:" templates[name] = pf parser_functions[name] = pf_arg1 return pf, "parser function", pf_arg1 end -- Ignore any remaining modifiers, as they've done their job. local output = chunks[#chunks] if subclass == "parser variable" then parser_variables[name] = output else templates[name] = output end return output, subclass end --[==[ Return only the canonical name (or nil). Parser-function names include the trailing colon, but not their first argument. This method may expand nested nodes and perform redirect lookups; parsing alone does neither. ]==] function Template:get_name(frame_args, force_transclusion) -- Only return the first return value. return (process_name(self, frame_args, force_transclusion)) end --[==[ Return a fresh table of fully expanded arguments, or nil for a failed name. Ordinary templates: implicit arguments count independently from 1 and retain whitespace; explicit keys use Scribunto conversion and explicit values use PHP trimming. Later assignments to the same key win, including assignments where an implicit argument overwrites an explicit numeric argument. Parser functions: [1] is the colon argument and [2] onwards are expanded pipe arguments in source order, without trimming or interpreting key=value. Parser variables return an empty table. Function-specific conventions and lazy branch selection (e.g. #switch) are not implemented by this method. ]==] function Template:get_arguments(frame_args) local name, subclass, pf_arg1 = process_name(self, frame_args) if name == nil then return nil elseif subclass == "parser variable" then return {} end local template_args = {} if subclass == "parser function" then template_args[1] = pf_arg1 for i = 2, #self do template_args[i] = expand(self[i], frame_args) -- Not trimmed. end return template_args end local implicit = 0 for i = 2, #self do local arg = self[i] if class_else_type(arg) == "argument" then template_args[scribunto_parameter_key(expand(arg[1], frame_args))] = php_trim((expand(arg[2], frame_args))) else implicit = implicit + 1 template_args[implicit] = expand(arg, frame_args) -- Not trimmed. end end return template_args end --[==[ Expand via the current Scribunto frame. Invalid/SUBST: invocations remain braced text with their children expanded. Parser variables are preprocessed by canonical name; ordinary templates by reconstructed, expanded children. Parser functions use the original reconstructed invocation so MediaWiki retains its own argument rules and lazy evaluation; when frame_args is supplied, try a child frame carrying those arguments first. BIG TODO: manual template expansion. ]==] function Template:expand(frame_args) local name, subclass, pf_arg1 = process_name(self, frame_args) if name == nil then local output = {} for i = 1, #self do output[i] = expand(self[i], frame_args) end return "{{" .. concat(output, "|") .. "}}" elseif subclass == "parser variable" then return (frame or get_frame()):preprocess("{{" .. name .. "}}") elseif subclass == "parser function" then local f = frame or get_frame() if frame_args ~= nil then local success, new_f = pcall(f.newChild, f, {args = frame_args}) if success then f = new_f end end return f:preprocess(tostring(self)) end local output = {} for i = 1, #self do output[i] = expand(self[i], frame_args) end return (frame or get_frame()):preprocess("{{" .. concat(output, "|") .. "}}") end end --[==[ Recognised tag nodes have .name (ASCII-lowercased), .attributes (raw text or a cached table), .self_closing and .index. Their body is opaque string data: the tree does not expose templates or parameters inside it. Inclusion tags and comments are consumed by the parser instead of becoming tag nodes. ]==] local Tag = Node:new_class("tag") --[==[ Rebuild the tag using normalised names and parsed/escaped attributes. Attribute order follows table iteration, so this is not source-preserving. ]==] function Tag:__tostring() local open_tag, attributes, n = {"<", self.name}, self:get_attributes(), 2 for attr, value in next, attributes do n = n + 1 open_tag[n] = " " .. php_htmlspecialchars(attr) .. "=\"" .. php_htmlspecialchars(value, "compat") .. "\"" end if self.self_closing then return concat(open_tag) .. "/>" end return concat(open_tag) .. ">" .. concat(self) .. "</" .. self.name .. ">" end do local valid_attribute_name local function get_valid_attribute_name() valid_attribute_name, get_valid_attribute_name = (data or get_data()).valid_attribute_name, nil return valid_attribute_name end --[==[ Parse attributes on demand and cache the returned table on this node. Validate names, lowercase ASCII names, normalise value whitespace and decode entities; a valueless attribute has value "". Later duplicate names win. The returned table is live: changes affect subsequent stringification. ]==] function Tag:get_attributes() local raw = self.attributes if not raw then self.attributes = {} return self.attributes elseif type(raw) == "table" then return raw end if sub(raw, -1) == "/" then raw = sub(raw, 1, -2) end local attributes, head = {}, 1 -- Semi-manual implementation of the native regex. while true do local name, loc = match(raw, "([^\t\n\f\r />][^\t\n\f\r /=>]*)()", head) if not name then break end head = loc local value loc = match(raw, "^[\t\n\f\r ]*=[\t\n\f\r ]*()", head) if loc then head = loc -- Either "", '' or the value ends on a space/at the end. Missing -- end quotes are repaired by closing the value at the end. value, loc = match(raw, "^\"([^\"]*)\"?()", head) if not value then value, loc = match(raw, "^'([^']*)'?()", head) if not value then value, loc = match(raw, "^([^\t\n\f\r ]*)()", head) end end head = loc end -- valid_attribute_name is a pattern matching a valid attribute name. -- Defined in the data due to its length - see there for more info. if umatch(name, valid_attribute_name or get_valid_attribute_name()) then -- Sanitizer applies PHP strtolower (ASCII-only). attributes[lower(name)] = value and decode_entities( php_trim((gsub(value, "[\t\n\r ]+", " "))) ) or "" end end self.attributes = attributes return attributes end end --[==[ Preprocess the reconstructed tag in the current frame. Unlike parameter and template expansion, this override does not use a frame_args argument. ]==] function Tag:expand() return (frame or get_frame()):preprocess(tostring(self)) end --[==[ A heading stores its unexpanded body in array children, plus .level, .index and .section. section is this parse's top-level heading counter, not a guarantee of MediaWiki's final section-edit numbering. ]==] local Heading = Node:new_class("heading") function Heading:new(this) if #this > 1 then local success, str = pcall(concat, this) if success then return new_node(self, { str, level = this.level, section = this.section, index = this.index }) end end return new_node(self, this) end do local node_tostring = Node.__tostring function Heading:__tostring() local eq = rep("=", self.level) return eq .. node_tostring(self) .. eq end end do local expand_node = Node.expand --[==[ Reject newline-containing expanded bodies for name/anchor inspection. The heading node and its already-assigned local section counter remain intact. ]==] local function validate_name(self, frame_args) local name = expand_node(self, frame_args) if find(name, "\n", nil, true) then return nil end return name end --[==[ Expand and PHP-trim the heading body, without its delimiting equals signs. Return nil if the expanded body contains a newline. ]==] function Heading:get_name(frame_args) local name = validate_name(self, frame_args) return name ~= nil and php_trim(name) or nil end --[==[ Encode the expanded body as an anchor and decode entities; return nil for a body containing a newline. No duplicate-anchor suffix is added, and this does not attempt to reproduce every stage of rendered-heading processing. FIXME: account for anchor disambiguation. ]==] function Heading:get_anchor(frame_args) local name = validate_name(self, frame_args) return name ~= nil and decode_entities(anchor_encode(name)) or nil end --[==[ Rebuild the heading with an expanded body. Unlike get_name/get_anchor, expansion itself does not reject newlines in that body. ]==] function Heading:expand(frame_args) local eq = rep("=", self.level) return eq .. expand_node(self, frame_args) .. eq end end ------------------------------------------------------------------------------------ -- -- Parser -- ------------------------------------------------------------------------------------ local Parser = m_parser.string_parser() --[==[ Parse brace runs from the innermost invocation outwards. Three opening and closing braces prefer a parameter; a pair of closing braces can force an inner template even when three or more opening braces remain. Lone braces are literal. For example, {{{{foo}}}} is a parameter inside literal braces, while {{{{{foo}}}}} is a parameter inside a template. The shared route protects delimiters inside recognised tags and nested brace, wikilink, heading and language-conversion blocks. It records .raw and .index from the original input after constructing each retained node. ]==] do -- Handlers. local handle_name local handle_argument local handle_value local function do_template_or_parameter(self, inner_node) self:push_sublayer(handle_name) self:set_pattern("[\n<[{|}]") -- If a node has already been parsed, nest it at the start of the new -- outer node (e.g. when parsing"{{{{foo}}bar}}", the template "{{foo}}" -- is parsed first, since it's the innermost, and becomes the first -- node of the outer template. if inner_node then self:emit(inner_node) end end local function pipe(self) self:emit(Wikitext:new(self:pop_sublayer())) self:push_sublayer(handle_argument) self:set_pattern("[\n<=[{|}]") end local function rbrace(self, this) if self:read(1) == "}" then self:emit(Wikitext:new(self:pop_sublayer())) return self:pop() end self:emit(this) end function handle_name(self, ...) handle_name = self:switch(handle_name, { ["\n"] = Parser.heading_block, ["<"] = Parser.tag, ["["] = Parser.wikilink_block, ["{"] = Parser.braces, ["|"] = pipe, ["}"] = rbrace, [""] = Parser.fail_route, [false] = Parser.emit }) return handle_name(self, ...) end function handle_argument(self, ...) handle_argument = self:switch(handle_argument, { ["\n"] = function(self, this) return self:heading_block(this, "==") end, ["<"] = Parser.tag, ["="] = function(self) local key = self:pop_sublayer() self:push_sublayer(handle_value) self:set_pattern("[\n<[{|}]") self.current_layer._parse_data.key = key end, ["["] = Parser.wikilink_block, ["{"] = Parser.braces, ["|"] = pipe, ["}"] = rbrace, [""] = Parser.fail_route, [false] = Parser.emit }) return handle_argument(self, ...) end function handle_value(self, ...) handle_value = self:switch(handle_value, { ["\n"] = Parser.heading_block, ["<"] = Parser.tag, ["["] = Parser.wikilink_block, ["{"] = Parser.braces, ["|"] = function(self) self:emit(Argument:new(self:pop_sublayer())) self:push_sublayer(handle_argument) self:set_pattern("[\n<=[{|}]") end, ["}"] = function(self, this) if self:read(1) == "}" then self:emit(Argument:new(self:pop_sublayer())) return self:pop() end self:emit(this) end, [""] = Parser.fail_route, [false] = Parser.emit }) return handle_value(self, ...) end function Parser:template_or_parameter() local text, head, node_to_emit, failed = self.text, self.head -- Comments/tags interrupt the brace count. local braces = match(text, "^{+()", head) - head self:advance(braces) while true do local success, node = self:try(do_template_or_parameter, node_to_emit) -- Fail means no "}}" or "}}}" was found, so emit any remaining -- unmatched opening braces before any templates/parameters that -- were found. if not success then self:emit(rep("{", braces)) failed = true break -- If there are 3+ opening and closing braces, it's a parameter. elseif braces >= 3 and self:read(2) == "}" then self:advance(3) braces = braces - 3 node = Parameter:new(node) -- Otherwise, it's a template. else self:advance(2) braces = braces - 2 node = Template:new(node) end local index = head + braces node.index = index node.raw = sub(text, index, self.head - 1) node_to_emit = node -- Terminate once not enough braces remain for further matches. if braces == 0 then break -- Emit any stray opening brace before any matched nodes. elseif braces == 1 then self:emit("{") break end end if node_to_emit then self:emit(node_to_emit) end return braces, failed end end --[==[ Parse only tag names with an end-tag pattern in the data. Bodies are opaque, and the first matching end tag closes the block. Unsupported or malformed tag syntax falls back to literal text. Inclusion mode decides whether to remove only the delimiter or the entire block; comments are discarded. ]==] do local end_tags local function get_end_tags() end_tags, get_end_tags = (data or get_data()).end_tags, nil return end_tags end -- Handlers. local handle_start local handle_tag local function do_tag(self) local layer = self.current_layer layer._parse_data.handler, layer.index = handle_start, self.head self:set_pattern("[%s/>]") self:advance() end local function is_ignored_tag(self, this) if self.transcluded then return this == "includeonly" end return this == "noinclude" or this == "onlyinclude" end local function ignored_tag(self, text, head) local loc = find(text, ">", head, true) if not loc then return self:fail_route() end self:jump(loc) local tag = self:pop() tag.ignored = true return tag end function handle_start(self, this) if this == "/" then local text, head = self.text, self.head + 1 local this = match(text, "^[^%s/>]+", head) if this and is_ignored_tag(self, lower(this)) then head = head + #this if not match(text, "^/[^>]", head) then return ignored_tag(self, text, head) end end return self:fail_route() elseif this == "" then return self:fail_route() end -- Tags are only case-insensitive with ASCII characters. local raw_name = this this = lower(this) local end_tag_pattern = (end_tags or get_end_tags())[this] if not end_tag_pattern then -- Validity check. return self:fail_route() end local layer = self.current_layer local pdata = layer._parse_data local text, head = self.text, self.head + pdata.step if match(text, "^/[^>]", head) then return self:fail_route() elseif is_ignored_tag(self, this) then return ignored_tag(self, text, head) -- If an onlyinclude tag is not ignored (and cannot be active since it -- would have triggered special handling earlier), it must be plaintext. elseif this == "onlyinclude" then return self:fail_route() elseif this == "noinclude" or this == "includeonly" then layer.ignored = true -- Ignored block. layer.raw_name = raw_name end layer.name, pdata.handler, pdata.end_tag_pattern = this, handle_tag, end_tag_pattern self:set_pattern(">") end function handle_tag(self, this) if this == "" then return self:fail_route() end local layer = self.current_layer if this ~= ">" then layer.attributes = this return elseif self:read(-1) == "/" then layer.self_closing = true return self:pop() end local text, head = self.text, self.head + 1 local loc1, loc2 = find(text, layer._parse_data.end_tag_pattern, head) if loc1 then if loc1 > head then self:emit(sub(text, head, loc1 - 1)) end self:jump(loc2) return self:pop() -- Compatibility case: an excluded lowercase noinclude/includeonly block -- can extend to EOF without a closing tag. Mixed-case names must have a -- closing tag, even though matching that closing tag is case-insensitive. elseif layer.ignored then local raw_name = layer.raw_name if raw_name == "noinclude" or raw_name == "includeonly" then self:jump(#text) return self:pop() end end return self:fail_route() end function Parser:tag() -- HTML comment. if self:read(1, 3) == "!--" then local text = self.text self:jump(select(2, find(text, "-->", self.head + 4, true)) or #text) -- onlyinclude tags (which must be lowercase with no whitespace). elseif self.onlyinclude and self:read(1, 13) == "/onlyinclude>" then local text = self.text self:jump(select(2, find(text, "<onlyinclude>", self.head + 14, true)) or #text) else local success, tag = self:try(do_tag) if not success then self:emit("<") elseif not tag.ignored then self:emit(Tag:new(tag)) end end end end --[==[ Parse heading syntax at the start of the input or immediately after a top-level newline. Retain levels 1-6 and move excess equals signs into the body. Increment this parser's section counter for each accepted heading. Heading blocks nested in other constructs protect delimiters but do not become heading nodes or increment that counter. Expansion may introduce a newline and prevent a structurally accepted heading from rendering. ]==] do -- Handlers. local handle_start local handle_body local handle_possible_end local function do_heading(self) local layer, head = self.current_layer, self.head layer._parse_data.handler, layer.index = handle_start, head self:set_pattern("[\t\n ]") -- Comments/tags interrupt the equals count. local eq = match(self.text, "^=+()", head) - head layer.level = eq self:advance(eq) end local function do_heading_possible_end(self) self.current_layer._parse_data.handler = handle_possible_end self:set_pattern("[\n<]") end function handle_start(self, ...) -- ===== is "=" as an L2; ======== is "==" as an L3 etc. local function newline(self) local layer = self.current_layer local eq = layer.level if eq <= 2 then return self:fail_route() end -- Calculate which equals signs determine the heading level. local level_eq = eq - (2 - eq % 2) level_eq = level_eq > 12 and 12 or level_eq -- Emit the excess. self:emit(rep("=", eq - level_eq)) layer.level = level_eq / 2 return self:pop() end local function whitespace(self) local success, possible_end = self:try(do_heading_possible_end) if success then self:emit(Wikitext:new(possible_end)) self.current_layer._parse_data.handler = handle_body self:set_pattern("[\n<=[{]") return self:consume() end return newline(self) end handle_start = self:switch(handle_start, { ["\t"] = whitespace, ["\n"] = newline, [" "] = whitespace, [""] = newline, [false] = function(self) -- Emit any excess = signs once we know it's a conventional heading. Up till now, we couldn't know if the heading is just a string of = signs (e.g. ========), so it wasn't guaranteed that the heading text starts after the 6th. local layer = self.current_layer local eq = layer.level if eq > 6 then self:emit(1, rep("=", eq - 6)) layer.level = 6 end layer._parse_data.handler = handle_body self:set_pattern("[\n<=[{]") return self:consume() end }) return handle_start(self, ...) end function handle_body(self, ...) handle_body = self:switch(handle_body, { ["\n"] = Parser.fail_route, ["<"] = Parser.tag, ["="] = function(self) -- Comments/tags interrupt the equals count. local eq = match(self.text, "^=+", self.head) local eq_len = #eq self:advance(eq_len) local success, possible_end = self:try(do_heading_possible_end) if success then self:emit(eq) self:emit(Wikitext:new(possible_end)) return self:consume() end local layer = self.current_layer local level = layer.level if eq_len > level then self:emit(rep("=", eq_len - level)) elseif level > eq_len then layer.level = eq_len self:emit(1, rep("=", level - eq_len)) end return self:pop() end, ["["] = Parser.wikilink_block, ["{"] = function(self, this) return self:braces(this, true) end, [""] = Parser.fail_route, [false] = Parser.emit }) return handle_body(self, ...) end function handle_possible_end(self, ...) handle_possible_end = self:switch(handle_possible_end, { ["\n"] = Parser.fail_route, ["<"] = function(self) if self:read(1, 3) ~= "!--" then return self:pop() end local head = select(2, find(self.text, "-->", self.head + 4, true)) if not head then return self:pop() end self:jump(head) end, [""] = Parser.fail_route, [false] = function(self, this) if not match(this, "^[\t ]+()$") then return self:pop() end self:emit(this) end }) return handle_possible_end(self, ...) end function Parser:heading() local success, heading = self:try(do_heading) if success then local section = self.section + 1 heading.section = section self.section = section self:emit(Heading:new(heading)) return self:consume() else self:emit("=") end end end ------------------------------------------------------------------------------------ -- -- Block handlers -- ------------------------------------------------------------------------------------ --[==[ Nested preprocessor blocks shield pipes, equals signs and closing braces from an outer invocation. The inner block must end before those characters can close or split its parent. For example, {{example|[[}} stays unclosed, but {{example|[[ ]]}} can close. The inner text need not ultimately render as a valid link. Wikilink, heading and language-conversion blocks can reach the end of input without explicit closure; templates/parameters backtrack when their required closing braces are absent. These blocks retain their text/nodes within wikitext wrappers rather than adding separate classes. ]==] --[==[ Language-conversion block: -{ ... }-. Brace invocations take priority over a -{{ prefix. This route protects outer delimiters even though language conversion is not enabled as a rendering feature on the English Wiktionary. ]==] do --Handler. local handle_language_conversion_block local function do_language_conversion_block(self) self.current_layer._parse_data.handler = handle_language_conversion_block self:set_pattern("[\n<[{}]") end function handle_language_conversion_block(self, ...) handle_language_conversion_block = self:switch(handle_language_conversion_block, { ["\n"] = Parser.heading_block, ["<"] = Parser.tag, ["["] = Parser.wikilink_block, ["{"] = Parser.braces, ["}"] = function(self, this) if self:read(1) == "-" then self:emit("}-") self:advance() return self:pop() end self:emit(this) end, [""] = Parser.pop, [false] = Parser.emit }) return handle_language_conversion_block(self, ...) end function Parser:braces(this, fail_on_unclosed_braces) local language_conversion_block = self:read(-1) == "-" if self:read(1) == "{" then local braces, failed = self:template_or_parameter() -- Headings will fail if they contain an unclosed brace block. if failed and fail_on_unclosed_braces then return self:fail_route() -- Language conversion blocks cannot begin "-{{", but can begin -- "-{{{" iff parsed as "-{" + "{{". elseif not (language_conversion_block and braces == 1) then return self:consume() end else self:emit(this) if not language_conversion_block then return end self:advance() end self:emit(Wikitext:new(self:get(do_language_conversion_block))) end end --[==[ Nested heading block: a newline followed by =, ending at a newline or EOF. Unlike a top-level heading, it need not have closing equals signs and is retained as wikitext rather than a heading node. Within an argument that has not yet seen an equals sign, only == starts this block: a single = is instead its key/value separator. Thus {{example|key\n=value}} can close, while {{example|key\n==}} cannot close before the heading block ends. ]==] do --Handler. local handle_heading_block local function do_heading_block(self) self.current_layer._parse_data.handler = handle_heading_block self:set_pattern("[\n<[{]") end function handle_heading_block(self, ...) handle_heading_block = self:switch(handle_heading_block, { ["\n"] = function(self) self:newline() return self:pop() end, ["<"] = Parser.tag, ["["] = Parser.wikilink_block, ["{"] = Parser.braces, [""] = Parser.pop, [false] = Parser.emit }) return handle_heading_block(self, ...) end function Parser:heading_block(this, nxt) self:newline() this = this .. (nxt or "=") local loc = #this - 1 while self:read(0, loc) == this do self:advance() self:emit(Wikitext:new(self:get(do_heading_block))) end end end -- Wikilink block. -- Opens with "[[" and closes with "]]". do -- Handler. local handle_wikilink_block local function do_wikilink_block(self) self.current_layer._parse_data.handler = handle_wikilink_block self:set_pattern("[\n<[%]{]") end function handle_wikilink_block(self, ...) handle_wikilink_block = self:switch(handle_wikilink_block, { ["\n"] = Parser.heading_block, ["<"] = Parser.tag, ["["] = Parser.wikilink_block, ["]"] = function(self, this) if self:read(1) == "]" then self:emit("]]") self:advance() return self:pop() end self:emit(this) end, ["{"] = Parser.braces, [""] = Parser.pop, [false] = Parser.emit }) return handle_wikilink_block(self, ...) end function Parser:wikilink_block() if self:read(1) == "[" then self:emit("[[") self:advance(2) self:emit(Wikitext:new(self:get(do_wikilink_block))) else self:emit("[") end end end -- Lines which only contain comments, " " and "\t" are eaten, so long as -- they're bookended by "\n" (i.e. not the first or last line). function Parser:newline() local text, head = self.text, self.head while true do repeat local loc = match(text, "^[\t ]*<!%-%-()", head + 1) if not loc then break end loc = select(2, find(text, "-->", loc, true)) head = loc or head until not loc -- Fail if no comments found. if head == self.head then break end head = match(text, "^[\t ]*()\n", head + 1) if not head then break end self:jump(head) end self:emit("\n") end do -- Handlers. local handle_start local main_handler -- If `transcluded` is true, then the text is checked for a pair of -- onlyinclude tags. If these are found (even if they're in the wrong -- order), then the start of the page is treated as though it is preceded -- by a closing onlyinclude tag. -- Note 1: unlike other parser extension tags, onlyinclude tags are case- -- sensitive and cannot contain whitespace. -- Note 2: onlyinclude tags *can* be implicitly closed by the end of the -- text, but the hard requirement above means this can only happen if -- either the tags are in the wrong order or there are multiple onlyinclude -- blocks. local function do_parse(self, transcluded) self.current_layer._parse_data.handler = handle_start self:set_pattern(".") self.section = 0 if not transcluded then return end self.transcluded = true local text = self.text if find(text, "</onlyinclude>", nil, true) then local head = find(text, "<onlyinclude>", nil, true) if head then self.onlyinclude = true self:jump(head + 13) end end end -- If the first character is "=", try parsing it as a heading. function handle_start(self, this) self.current_layer._parse_data.handler = main_handler self:set_pattern("[\n<{]") if this == "=" then return self:heading() end return self:consume() end function main_handler(self, ...) main_handler = self:switch(main_handler, { ["\n"] = function(self) self:newline() if self:read(1) == "=" then self:advance() return self:heading() end end, ["<"] = Parser.tag, ["{"] = function(self, this) if self:read(1) == "{" then self:template_or_parameter() return self:consume() end self:emit(this) end, [""] = Parser.pop, [false] = Parser.emit }) return main_handler(self, ...) end --[==[ Parse a string (or a number converted to a string) and always return a wikitext root. By default read non-transcluded page text; a truthy second argument selects transclusion rules. Expansion and name validity checks are deferred. Unmatched markup is handled by the parsing routes rather than causing an otherwise valid string input to return nil. ]==] function export.parse(text, transcluded) local text_type = type(text) return (select(2, Parser:parse{ text = text_type == "string" and text or text_type == "number" and tostring(text) or error("bad argument #1 (string expected, got " .. text_type .. ")"), node = {Wikitext, true}, route = {do_parse, transcluded} })) end parse = export.parse end --[==[ Iterate over all retained template-class nodes, outer before inner, returning node, parent and key. Includes magic words and invalid names. Default to transclusion mode; a truthy not_transcluded selects ordinary page mode. ]==] function export.find_templates(text, not_transcluded) return parse(text, not not_transcluded):iterate_nodes("template") end do local link_parameter_1, link_parameter_2 local function get_link_parameter_1() link_parameter_1, get_link_parameter_1 = (data or get_data()).template_link_param_1, nil return link_parameter_1 end local function get_link_parameter_2() link_parameter_2, get_link_parameter_2 = (data or get_data()).template_link_param_2, nil return link_parameter_2 end -- Generate a link. If the target title doesn't have a fragment, use "#top" -- (which is an implicit anchor at the top of every page), as this ensures -- self-links still display as links, since bold display is distracting and -- unintuitive for template links. local function link_page(title, display) local fragment = title.fragment if fragment == "" then fragment = "top" end return format( "[[:%s|%s]]", encode_uri(title.prefixedText .. "#" .. fragment, "WIKI"), display ) end -- pf_arg1 or pf_arg2 may need to be linked if a given parser function -- treats them as a pagename. If a key exists in `namespace`, the value is -- the namespace for the page: if not 0, then the namespace prefix will -- always be added to the input (e.g. {{#invoke:}} can only target the -- Module: namespace, so inputting "Template:foo" gives -- "Module:Template:foo", and "Module:foo" gives "Module:Module:foo"). -- However, this isn't possible with mainspace (namespace 0), so prefixes -- are respected. make_title() handles all of this automatically. local function finalize_arg(pagename, namespace) if namespace == nil then return pagename end local title = make_title(namespace, pagename) return title and not title.isExternal and link_page(title, pagename) or pagename end local function render_title(name, args) -- parse_template_name returns a table of transclusion modifiers plus -- the normalized template/magic word name, which will be used as link -- targets. The third return value pf_arg1 is the first argument of a -- parser function, which comes after the colon (e.g. "foo" in -- "{{#IF:foo|bar|baz}}"). This means args[1] (i.e. the first argument -- that comes after a pipe is actually argument 2, and so on. Note: the -- second parameter of parse_template_name checks if there are any -- arguments, since parser variables cannot take arguments (e.g. -- {{CURRENTYEAR}} is a parser variable, but {{CURRENTYEAR|foo}} -- transcludes "Template:CURRENTYEAR"). In such cases, the returned -- table explicitly includes the "Template:" prefix in the template -- name. The third parameter instructs it to retain any fragment in the -- template name in the returned table, if present. local chunks, subclass, pf_arg1 = parse_template_name( name, args and pairs(args)(args) ~= nil, true ) if chunks == nil then return name, args end local chunks_len = #chunks -- Additionally, generate the corresponding table `rawchunks`, which -- is a list of colon-separated chunks in the raw input. This is used -- to retrieve the display forms for each chunk. local rawchunks = split(name, ":") for i = 1, chunks_len - 1 do chunks[i] = format( "[[%s|%s]]", encode_uri((magic_words or get_magic_words())[sub(chunks[i], 1, -2)].transclusion_modifier, "WIKI"), rawchunks[i] ) end local chunk = chunks[chunks_len] -- If it's a template, return a link to it with link_page, concatenating -- the remaining chunks in `rawchunks` to form the display text. -- Use new_title with the default namespace 10 (Template:) to generate -- a target title, which is the same setting used for retrieving -- templates (including those in other namespaces, as prefixes override -- the default). if subclass == "template" then chunks[chunks_len] = link_page( new_title(chunk, 10), concat(rawchunks, "&#58;", chunks_len) -- : ) return concat(chunks, "&#58;"), args -- : elseif subclass == "parser variable" then chunks[chunks_len] = format( "[[%s|%s]]", encode_uri((magic_words or get_magic_words())[chunk].parser_variable, "WIKI"), rawchunks[chunks_len] ) return concat(chunks, "&#58;"), args -- : end -- Otherwise, it must be a parser function. local mgw_data = (magic_words or get_magic_words())[sub(chunk, 1, -2)] local link = mgw_data.parser_function or mgw_data.transclusion_modifier local pf_arg2 = args and args[1] or nil -- Some magic words have different links, depending on whether argument -- 2 is specified (e.g. "baz" in {{foo:bar|baz}}). if type(link) == "table" then link = pf_arg2 and link[2] or link[1] end chunks[chunks_len] = format( "[[%s|%s]]", encode_uri(link, "WIKI"), rawchunks[chunks_len] ) -- #TAG: has special handling, because documentation links for parser -- extension tags come from [[Module:data/parser extension tags]]. if chunk == "#TAG:" then -- Tags are only case-insensitive with ASCII characters. local tag = (parser_extension_tags or get_parser_extension_tags())[lower(php_trim(pf_arg1))] if tag then pf_arg1 = format( "[[%s|%s]]", encode_uri(tag, "WIKI"), pf_arg1 ) end -- Otherwise, finalize pf_arg1 and add it to `chunks`. else pf_arg1 = finalize_arg(pf_arg1, (link_parameter_1 or get_link_parameter_1())[chunk]) end chunks[chunks_len + 1] = pf_arg1 -- Finalize pf_arg2 (if applicable), then return. if pf_arg2 then args = shallow_copy(args) -- Avoid destructively modifying args. args[1] = finalize_arg(pf_arg2, (link_parameter_2 or get_link_parameter_2())[chunk]) end return concat(chunks, "&#58;"), args -- : end function export.buildTemplate(title, args) local output = {title} if not args then return output end --[==[ Return invocation parts, not a string: {title, arg1, "key=value", ...}. Numeric keys precede other keys in sorted order. Positive integer keys within the contiguous 1-based prefix can be implicit; all others retain their explicit key. Neither normalisation nor delimiter escaping occurs. Callers assembling executable wikitext must handle literal = and | where necessary; a raw = in an implicit value may change its interpretation. TODO: handle "=" and "|" in params/values. ]==] local implicit for k, v in sorted_pairs(args) do if type(k) == "number" and k >= 1 and k % 1 == 0 then if implicit == nil then implicit = table_len(args) end insert(output, k <= implicit and v or k .. "=" .. v) else insert(output, k .. "=" .. v) end end return output end build_template = export.buildTemplate --[==[ Return display wikitext in a <code style="white-space: pre-wrap"> element. Unless no_link is truthy, link the title/modifiers/magic words and selected page-name arguments to their targets or documentation. title contains any parser function's colon argument; args contains only the pipe arguments. Escape braces and equals signs for display, then join parts with encoded pipe/braces. This displays an invocation; it does not execute it or escape every kind of wikitext in caller-supplied argument values. ]==] function export.templateLink(title, args, no_link) if not no_link then title, args = render_title(title, args) end local output = build_template(title, args) for i = 1, #output do output[i] = encode_entities(output[i], "={}", true, true) end return tostring(html_create("code") :css("white-space", "pre-wrap") :wikitext("&#123;&#123;" .. concat(output, "&#124;") .. "&#125;&#125;") -- {{ | }} ) end end do --[==[ As find_templates, but select retained parameter nodes. Return node, parent and key; default to transclusion mode. Discarded extra parameter fields and opaque tag bodies are not traversed. ]==] function export.find_parameters(text, not_transcluded) return parse(text, not not_transcluded):iterate_nodes("parameter") end --[==[ Display {{{name}}} or {{{name|default}}} in a wrapping <code> element. nil default omits the pipe; "" retains an empty default. Only the surrounding delimiters are encoded: caller-supplied name/default wikitext is not escaped. ]==] function export.displayParameter(name, default) return tostring(html_create("code") :css("white-space", "pre-wrap") :wikitext("&#123;&#123;&#123;" .. concat({name, default}, "&#124;") .. "&#125;&#125;&#125;") -- {{{ | }}} ) end end do local function check_level(level) if type(level) ~= "number" then error("Heading levels must be numbers.") elseif level < 1 or level > 6 or level % 1 ~= 0 then error("Heading levels must be integers between 1 and 6.") end return level end --[==[ Iterate over heading nodes in non-transclusion mode, returning node, parent and key. Optional truthy bounds must be integer levels 1-6; omitted/falsy bounds default to 1 and 6. The range is inclusive; a reversed range is empty. Structurally accepted headings remain in the iterator even if later expansion introduces a newline and their name/anchor methods return nil. .section is a local counter; do not assume it reproduces native edit links. TODO: section numbers for edit links seem to also include headings nested inside templates and parameters (but apparently not those in parser extension tags - need to test this more). If we ever want to add section edit links manually, this will need to be accounted for. ]==] function export.find_headings(text, i, j) local parsed = parse(text) if i == nil and j == nil then return parsed:iterate_nodes("heading") end i = i and check_level(i) or 1 j = j and check_level(j) or 6 return parsed:iterate(function(v) if class_else_type(v) == "heading" then local level = v.level return level >= i and level <= j end end) end end do local function make_tag(tag) return tostring(html_create("code") :css("white-space", "pre-wrap") :wikitext("&lt;" .. tag .. "&gt;") ) end --[==[ Display one tag using input without the outer < and >, e.g. "ref name=foo", "/ref" or "nowiki/". Link recognised tag names to documentation. Invalid or unknown forms still display in <code>, without an automatically added link. Inclusion tags have their own documentation link and onlyinclude requires exact lowercase spelling with no remainder. Input is display wikitext; the helper encodes the outer angle brackets, not all caller-supplied markup. ]==] function export.wikitagLink(tag) -- ">" can't appear in tags (including attributes) since the parser -- unconditionally treats ">" as the end of a tag. if find(tag, ">", nil, true) then return make_tag(tag) end -- Tags must start "<tagname..." or "</tagname...", with no whitespace -- after "<" or "</". local slash, tagname, remainder = match(tag, "^(/?)([^/%s]+)(.*)$") if not tagname then return make_tag(tag) end -- Tags are only case-insensitive with ASCII characters. local link = lower(tagname) if ( -- onlyinclude tags must be lowercase and are whitespace intolerant. link == "onlyinclude" and (link ~= tagname or remainder ~= "") or -- Closing wikitags (except onlyinclude) can only have whitespace -- after the tag name. slash == "/" and not match(remainder, "^%s*()$") or -- Tagnames cannot be followed immediately by "/", unless it comes -- at the end (e.g. "<nowiki/>", but not "<nowiki/ >"). remainder ~= "/" and sub(remainder, 1, 1) == "/" ) then -- Output with no link. return make_tag(tag) end -- Partial transclusion tags aren't in the table of parser extension -- tags. if link == "noinclude" or link == "includeonly" or link == "onlyinclude" then link = "mw:Transclusion#Partial transclusion" else link = (parser_extension_tags or get_parser_extension_tags())[link] end if link then tag = gsub(tag, pattern_escape(tagname), "[[" .. replacement_escape(encode_uri(link, "WIKI")) .. "|%0]]", 1) end return make_tag(tag) end end --[==[ Re-export the base parser's class_else_type(value): return a registered node class name (including nodes from other Module:parser clients), otherwise Lua's ordinary type(value). Plain tables therefore return "table". ]==] export.class_else_type = class_else_type return export 4enaqwfj6shypm4s4e65zg11de3n3ha มอดูล:de-common 828 311763 5775178 1582117 2026-10-14T03:05:28Z Octahedron80 267 5775178 Scribunto text/plain local export = {} --[=[ Authorship: <benwing2> ]=] local M = require("Module:module loader").init { require = { put = "Module:parse utilities", }, } local rmatch = mw.ustring.match local vowels = "aeiouyäöüAEIOUYÄÖÜ" local capletters = "A-ZÄÖÜ" export.CAP = "[" .. capletters .. "]" export.V = "[" .. vowels .. "]" export.NV = "[^" .. vowels .. "]" export.articles = { ["m"] = { ind_nom = "ein", def_nom = "der", ind_gen = "eines", def_gen = "des", ind_dat = "einem", def_dat = "dem", ind_acc = "einen", def_acc = "den", ind_abl = "einen", def_abl = "den", ind_voc = "einen", def_voc = "den", }, ["f"] = { ind_nom = "eine", def_nom = "die", ind_gen = "einer", def_gen = "der", ind_dat = "einer", def_dat = "der", ind_acc = "eine", def_acc = "die", ind_abl = "eine", def_abl = "die", ind_voc = "eine", def_voc = "die", }, ["n"] = { ind_nom = "ein", def_nom = "das", ind_gen = "eines", def_gen = "des", ind_dat = "einem", def_dat = "dem", ind_acc = "ein", def_acc = "das", ind_abl = "ein", def_abl = "das", ind_voc = "ein", def_voc = "das", }, ["p"] = { ind_nom = "([[keine]])", def_nom = "die", ind_gen = "([[keiner]])", def_gen = "der", ind_dat = "([[keinen]])", def_dat = "den", ind_acc = "([[keine]])", def_acc = "die", ind_abl = "?", def_abl = "?", ind_voc = "?", def_voc = "?", }, } local umlaut = { ["a"] = "ä", ["A"] = "Ä", ["o"] = "ö", ["O"] = "Ö", ["u"] = "ü", ["U"] = "Ü", } function export.apply_umlaut(term, origterm) local stem, after = term:match("^(.*[^e])(e[lmnr]?)$") if stem then -- Nagel -> Nägel, Garten -> Gärten return export.apply_umlaut(stem, term) .. after end -- Haus -> Häuschen local before_v, v, after_v = rmatch(term, "^(.*)([Aa])([Uu]" .. export.NV .. "-)$") if not before_v then -- Haar -> Härchen before_v, v, after_v = rmatch(term, "^(.*)([Aa])[Aa](" .. export.NV .. "-)$") end if not before_v then -- Boot -> Bötchen before_v, v, after_v = rmatch(term, "^(.*)([Oo])[Oo](" .. export.NV .. "-)$") end if not before_v then -- regular umlaut before_v, v, after_v = rmatch(term, "^(.*)([AaOouU])(" .. export.NV .. "-)$") end if before_v then return before_v .. umlaut[v] .. after_v end error("Can't umlaut " .. (origterm or term) .. " because the last vowel isn't a, o, u or au") end function export.fetch_footnotes(separated_group, parse_err) local footnotes for j = 2, #separated_group - 1, 2 do if separated_group[j + 1] ~= "" then parse_err("Extraneous text after bracketed footnotes: '" .. table.concat(separated_group) .. "'") end if not footnotes then footnotes = {} end table.insert(footnotes, separated_group[j]) end return footnotes end function export.fetch_specs(segments, separator, spectype, allow_blank, parse_err) local separated_groups = M.put.split_alternating_runs_and_strip_spaces(segments, separator) if allow_blank and #separated_groups == 1 and #separated_groups[1] == 1 and separated_groups[1][1] == "" then return nil end local specs = {} for _, separated_group in ipairs(separated_groups) do local form = separated_group[1] if form == "" then parse_err("Blank form not allowed here, but saw '" .. table.concat(segments) .. "'") end local new_spec = {form = form, footnotes = export.fetch_footnotes(separated_group, parse_err)} for _, existing_spec in ipairs(specs) do if existing_spec.form == new_spec.form then parse_err("Duplicate " .. spectype .. " spec '" .. table.concat(separated_group) .. "'") end end table.insert(specs, new_spec) end return specs end function export.add_angle_brackets(text) local function add_to_one(txt) if txt:find("<") then local segments = M.put.parse_multi_delimiter_balanced_segment_run(txt, {{"<", ">"}, {"[", "]"}}) for i = 2, #segments, 2 do if segments[i]:find("^<.*>$") then return txt end end end return "<" .. txt .. ">" end if not text:find("%(%(") then return add_to_one(text) end return (text:gsub("%(%((.-)%)%)", function(alternants) local segments = M.put.parse_multi_delimiter_balanced_segment_run(alternants, {{"<", ">"}, {"[", "]"}}) local comma_separated_groups = M.put.split_alternating_runs_and_strip_spaces(segments, ",") local parts = {} for _, group in ipairs(comma_separated_groups) do table.insert(parts, add_to_one(table.concat(group))) end return "((" .. table.concat(parts, ",") .. "))" end)) end return export f347bqbpaksqhj5hm5pnpjf5bd28z3z มอดูล:parse utilities 828 311764 5775187 4615780 2026-10-14T04:10:45Z Octahedron80 267 5775187 Scribunto text/plain local export = {} local fun_is_callable_module = "Module:fun/isCallable" local languages_module = "Module:languages" local parameters_module = "Module:parameters" local string_char_module = "Module:string/char" local string_utilities_module = "Module:string utilities" local table_insert_if_not_module = "Module:table/insertIfNot" local assert = assert local concat = table.concat local dump = mw.dumpObject local error = error local insert = table.insert local ipairs = ipairs local list_to_text = mw.text.listToText local pairs = pairs local require = require local sort = table.sort local type = type local ugsub = mw.ustring.gsub local function convert_val(...) convert_val = require(parameters_module).convert_val return convert_val(...) end local function get_lang(...) get_lang = require(languages_module).getByCode return get_lang(...) end local function insert_if_not(...) insert_if_not = require(table_insert_if_not_module) return insert_if_not(...) end local function is_callable(...) is_callable = require(fun_is_callable_module) return is_callable(...) end local function split(...) split = require(string_utilities_module).split return split(...) end local function u(...) u = require(string_char_module) return u(...) end local function umatch(...) umatch = require(string_utilities_module).match return umatch(...) end --[==[ intro: In order to understand the following parsing code, you need to understand how inflected text specs work. They are intended to work with inflected text where individual words to be inflected may be followed by inflection specs in angle brackets. The format of the text inside of the angle brackets is up to the individual language and part-of-speech specific implementation. A real-world example is as follows: `<nowiki>[[медичний|меди́чна]]<+> [[сестра́]]<*,*#.pr></nowiki>`. This is the inflection of the Ukrainian multiword expression {{m|uk|меди́чна сестра́||nurse|lit=medical sister}}, consisting of two words: the adjective {{m|uk|меди́чна||medical|pos=feminine singular}} and the noun {{m|uk|сестра́||sister}}. The specs in angle brackets follow each word to be inflected; for example, `<+>` means that the preceding word should be declined as an adjective. The code below works in terms of balanced expressions, which are bounded by delimiters such as `< >` or `[ ]`. The intention is to allow separators such as spaces to be embedded inside of delimiters; such embedded separators will not be parsed as separators. For example, Ukrainian noun specs allow footnotes in brackets to be inserted inside of angle brackets; something like `меди́чна<+> сестра́<pr.[this is a footnote]>` is legal, as is `<nowiki>[[медичний|меди́чна]]<+> [[сестра́]]<pr.[this is an <i>italicized footnote</i>]></nowiki>`, and the parsing code should not be confused by the embedded brackets, spaces or angle brackets. The parsing is done by two functions, which work in close concert: {parse_balanced_segment_run()} and {split_alternating_runs()}. To illustrate, consider the following: {parse_balanced_segment_run("foo<M.proper noun> bar<F>", "<", ">")} =<br /> { {"foo", "<M.proper noun>", " bar", "<F>", ""}} then {split_alternating_runs({"foo", "<M.proper noun>", " bar", "<F>", ""}, " ")} =<br /> { {{"foo", "<M.proper noun>", ""}, {"bar", "<F>", ""}}} Here, we start out with a typical inflected text spec `foo<M.proper noun> bar<F>`, call {parse_balanced_segment_run()} on it, and call {split_alternating_runs()} on the result. The output of {parse_balanced_segment_run()} is a list where even-numbered segments are bounded by the bracket-like characters passed into the function, and odd-numbered segments consist of the surrounding text. {split_alternating_runs()} is called on this, and splits '''only''' the odd-numbered segments, grouping all segments between the specified character. Note that the inner lists output by {split_alternating_runs()} are themselves in the same format as the output of {parse_balanced_segment_run()}, with bracket-bounded text in the even-numbered segments. Hence, such lists can be passed again to {split_alternating_runs()}. ]==] --[==[ Parse a string containing matched instances of parens, brackets or the like. Return a list of strings, alternating between textual runs not containing the open/close characters and runs beginning and ending with the open/close characters. For example, {parse_balanced_segment_run("foo(x(1)), bar(2)", "(", ")") = {"foo", "(x(1))", ", bar", "(2)", ""}} ]==] function export.parse_balanced_segment_run(segment_run, open, close) return split(segment_run, "(%b" .. open .. close .. ")") end -- The following is an equivalent, older implementation that does not use %b (written before I was aware of %b). --[=[ function export.parse_balanced_segment_run(segment_run, open, close) local break_on_open_close = split(segment_run, "([%" .. open .. "%" .. close .. "])") local text_and_specs = {} local level = 0 local seg_group = {} for i, seg in ipairs(break_on_open_close) do if i % 2 == 0 then if seg == open then insert(seg_group, seg) level = level + 1 else assert(seg == close) insert(seg_group, seg) level = level - 1 if level < 0 then error("Unmatched " .. close .. " sign: '" .. segment_run .. "'") elseif level == 0 then insert(text_and_specs, concat(seg_group)) seg_group = {} end end elseif level > 0 then insert(seg_group, seg) else insert(text_and_specs, seg) end end if level > 0 then error("Unmatched " .. open .. " sign: '" .. segment_run .. "'") end return text_and_specs end ]=] --[==[ Like parse_balanced_segment_run() but accepts multiple sets of delimiters. For example, {parse_multi_delimiter_balanced_segment_run("foo[bar(baz[bat])], quux<glorp>", {{"[", "]"}, {"(", ")"}, {"<", ">"}}) = {"foo", "[bar(baz[bat])]", ", quux", "<glorp>", ""}}. Each element in the list of delimiter pairs is a string specifying an equivalence class of possible delimiter characters. You can use this, for example, to allow either "[" or "&amp;#91;" to be treated equivalently, with either one closed by either "]" or "&amp;#93;". To do this, first replace "&amp;#91;" and "&amp;#93;" with single Unicode characters such as U+FFF0 and U+FFF1, and then specify a two-character string containing "[" and U+FFF0 as the opening delimiter, and a two-character string containing "]" and U+FFF1 as the corresponding closing delimiter. If `no_error_on_unmatched` is given and an error is found during parsing, a string is returned containing the error message instead of throwing an error. ]==] function export.parse_multi_delimiter_balanced_segment_run(segment_run, delimiter_pairs, no_error_on_unmatched) local escaped_delimiter_pairs = {} local open_to_close_map = {} local open_close_items = {} local open_items = {} for _, open_close in ipairs(delimiter_pairs) do local open, close = open_close[1], open_close[2] open = open:gsub("([%[%]%%%%-])", "%%%1") close = close:gsub("([%[%]%%%%-])", "%%%1") insert(open_close_items, open) insert(open_close_items, close) insert(open_items, open) open = "[" .. open .. "]" close = "[" .. close .. "]" open_to_close_map[open] = close insert(escaped_delimiter_pairs, {open, close}) end local open_close_pattern = "([" .. concat(open_close_items) .. "])" local open_pattern = "([" .. concat(open_items) .. "])" local break_on_open_close = split(segment_run, open_close_pattern) local text_and_specs = {} local level = 0 local seg_group = {} local open_at_level_zero for i, seg in ipairs(break_on_open_close) do if i % 2 == 0 then insert(seg_group, seg) if level == 0 then if not umatch(seg, open_pattern) then local errmsg = "Unmatched close sign " .. seg .. ": '" .. segment_run .. "'" if no_error_on_unmatched then return errmsg else error(errmsg) end end assert(open_at_level_zero == nil) for _, open_close in ipairs(escaped_delimiter_pairs) do local open = open_close[1] if umatch(seg, open) then open_at_level_zero = open break end end if open_at_level_zero == nil then error(("Internal error: Segment %s didn't match any open regex"):format(seg)) end level = level + 1 elseif umatch(seg, open_at_level_zero) then level = level + 1 elseif umatch(seg, open_to_close_map[open_at_level_zero]) then level = level - 1 assert(level >= 0) if level == 0 then insert(text_and_specs, concat(seg_group)) seg_group = {} open_at_level_zero = nil end end elseif level > 0 then insert(seg_group, seg) else insert(text_and_specs, seg) end end if level > 0 then local errmsg = "Unmatched open sign " .. open_at_level_zero .. ": '" .. segment_run .. "'" if no_error_on_unmatched then return errmsg else error(errmsg) end end return text_and_specs end --[==[ Check whether a term contains top-level HTML. We want to distinguish inline modifiers from HTML. We assume an inline modifier is either a boolean modifier like `<bor>` or a prefix modifier like `<tr:Miryem>`. All other things inside of angle brackets, e.g. `<nowiki><span class="foo"></nowiki>`, `<nowiki></span></nowiki>`, `<nowiki><br/></nowiki>`, etc., should be flagged as HTML (typically caused by wrapping an argument in {{tl|m|...}}, {{tl|af|...}} or similar, but sometimes specified directly, e.g. `<nowiki><sup>6</sup></nowiki>`). By default, we assume the tag in an inline modifier contains either letters, numbers, hyphens or underscore (but not spaces), and must either stand alone or be followed by a colon, leading to a default HTML-checking pattern of {"<[%w_%-]*[^%w_%-:>]"}. But this can be modified; e.g. [[Module:tl-pronunciation]] allows modifiers of the form `<<var>pos</var>^<var>defn</var>>` or `<<var>pos</var>,<var>pos</var>,<var>pos</var>^<var>defn</var>>`, and would need to use its own HTML pattern. It's important we restrict the check for HTML to top-level to allow for generated HTML inside of e.g. qualifier tags, such as `<nowiki>foo<q:similar to {{m|fr|bar}}></nowiki>`. ]==] function export.term_contains_top_level_html(term, html_pattern) html_pattern = html_pattern or "<[%w_%-]*[^%w_%-:>]" -- If no HTML anywhere, the answer is no. if not term:find(html_pattern) then return false end -- Otherwise, we have to call parse_balanced_segment_run() and check alternate runs at top level. local runs = export.parse_balanced_segment_run(term, "<", ">") for i = 2, #runs, 2 do if runs[i]:find("^" .. html_pattern) then return true end end return false end --[==[ Check whether a term appears to have already been passed through `full_link()`. Passing it again will mangle it in various ways; at best it will have unnecessary lang/script wrapping, which might do nothing but might result in overly large fonts or other issues. We also check for uses of {{tl|ja-r/args}}, {{tl|ryu-r/args}} or {{tl|ko-l/args}}, which will be manged by `full_link()`. If this check succeeds, use the text raw instead of passing through `full_link()`. ]==] function export.term_already_linked(term) return term:find("<span") or term:find("{{ja%-r|") or term:find("{{ryu%-r|") or term:find("{{ko%-l|") end --[==[ Split a list of alternating textual runs of the format returned by `parse_balanced_segment_run` on `splitchar`. This only splits the odd-numbered textual runs (the portions between the balanced open/close characters). The return value is a list of lists, where each list contains an odd number of elements, where the even-numbered elements of the sublists are the original balanced textual run portions. For example, if we do {parse_balanced_segment_run("foo<M.proper noun> bar<F>", "<", ">") = {"foo", "<M.proper noun>", " bar", "<F>", ""}} then {split_alternating_runs({"foo", "<M.proper noun>", " bar", "<F>", ""}, " ") = {{"foo", "<M.proper noun>", ""}, {"bar", "<F>", ""}}} Note that we did not touch the text "<M.proper noun>" even though it contains a space in it, because it is an even-numbered element of the input list. This is intentional and allows for embedded separators inside of brackets/parens/etc. Note also that the inner lists in the return value are of the same form as the input list (i.e. they consist of alternating textual runs where the even-numbered segments are balanced runs), and can in turn be passed to split_alternating_runs(). If `preserve_splitchar` is passed in, the split character is included in the output, as follows: {split_alternating_runs({"foo", "<M.proper noun>", " bar", "<F>", ""}, " ", true) = {{"foo", "<M.proper noun>", ""}, {" "}, {"bar", "<F>", ""}}} Consider what happens if the original string has multiple spaces between brackets, and multiple sets of brackets without spaces between them. {parse_balanced_segment_run("foo[dated][low colloquial] baz-bat quux xyzzy[archaic]", "[", "]") = {"foo", "[dated]", "", "[low colloquial]", " baz-bat quux xyzzy", "[archaic]", ""}} then {split_alternating_runs({"foo", "[dated]", "", "[low colloquial]", " baz-bat quux xyzzy", "[archaic]", ""}, "[ %-]") = {{"foo", "[dated]", "", "[low colloquial]", ""}, {"baz"}, {"bat"}, {"quux"}, {"xyzzy", "[archaic]", ""}}} If `preserve_splitchar` is passed in, the split character is included in the output, as follows: {split_alternating_runs({"foo", "[dated]", "", "[low colloquial]", " baz bat quux xyzzy", "[archaic]", ""}, "[ %-]", true) = {{"foo", "[dated]", "", "[low colloquial]", ""}, {" "}, {"baz"}, {"-"}, {"bat"}, {" "}, {"quux"}, {" "}, {"xyzzy", "[archaic]", ""}}} As can be seen, the even-numbered elements in the outer list are one-element lists consisting of the separator text. ]==] function export.split_alternating_runs(segment_runs, splitchar, preserve_splitchar) local grouped_runs = {} local run = {} for i, seg in ipairs(segment_runs) do if i % 2 == 0 then insert(run, seg) else local parts = split(seg, preserve_splitchar and "(" .. splitchar .. ")" or splitchar) insert(run, parts[1]) for j=2,#parts do insert(grouped_runs, run) run = {parts[j]} end end end if #run > 0 then insert(grouped_runs, run) end return grouped_runs end --[==[ After calling `parse_multi_delimiter_balanced_segment_run()`, rejoin delimiter-bounded textual runs (i.e. textual runs surrounded by certain matched delimiters) with the runs on either side. This can be used when some of the matched delimiters are specified only in order to ensure that delimiters inside of other delimiters aren't parsed. As an example, [[Module:object usage]] calls {m_parse_utilities.parse_multi_delimiter_balanced_segment_run(object, {{"[", "]"}, {"(", ")"}, {"<", ">"}})} but the actual syntax of {{tl|+obj}} only uses parens and angle brackets as delimiters. Square brackets are included so that internal links are treated as units (i.e. parens and angle brackets occurring inside of them aren't parsed), but beyond that we don't treat square brackets as delimiters, so we want to rejoin square-bracket-delimited textual runs with adjacent runs before further parsing. There are two primary workflows when using this function: # If you only care about balanced delimiters occurring inside of other balanced delimiters (e.g. in the above example with [[Module:object usage]], you can call `rejoin_delimited_runs()` directly after `parse_multi_delimiter_balanced_segment_run()`. # However, if you care about single delimiters such as commas and slashes occurring inside of balanced delimiters (e.g. if you allow multiple comma-separated terms, e.g. of which can have associated inline modifiers, and you don't want commas inside of internal links to be treated as delimiters), you need to call `rejoin_delimited_runs()` ''after'' calling `split_alternating_runs()`. This is used, for example, in `parse_inline_modifiers()` for exactly this reason, when a `splitchar` is provided. `data` is an object of properties. Currently there are two: `runs` (the output of calling `parse_multi_delimiter_balanced_segment_run()`, i.e. a list of textual runs, where even-numbered elements begin and end with a matched delimiter and odd-numbered elements are surrounding text) and `delimiter_pattern` (a Lua pattern matching delimited textual runs that we want to rejoin with the surrounding text). `delimiter_pattern` should normally be anchored at the beginning; e.g. {"^%["} would be the correct pattern to use when rejoining square-bracket-delimited textual runs, as described above. ]==] function export.rejoin_delimited_runs(data) local joined_runs = {} local i = 1 while i <= #data.runs do local run = data.runs[i] if i % 2 == 0 and run:find(data.delimiter_pattern) then joined_runs[#joined_runs] = joined_runs[#joined_runs] .. run .. data.runs[i + 1] i = i + 2 else insert(joined_runs, run) i = i + 1 end end return joined_runs end function export.strip_spaces(text) return (ugsub(text, "^%s*(.-)%s*$", "%1")) end --[==[ Apply an arbitrary function `frob` to the "raw-text" segments in a split run set (the output of split_alternating_runs()). We leave alone stuff within balanced delimiters (footnotes, inflection specs and the like), as well as splitchars themselves if present. `preserve_splitchar` indicates whether splitchars are present in the split run set. `frob` is a function of one argument (the string to frob) and should return one argument (the frobbed string). We operate by only frobbing odd-numbered segments, and only in odd-numbered runs if preserve_splitchar is given. ]==] function export.frob_raw_text_alternating_runs(split_run_set, frob, preserve_splitchar) for i, run in ipairs(split_run_set) do if not preserve_splitchar or i % 2 == 1 then for j, segment in ipairs(run) do if j % 2 == 1 then run[j] = frob(segment) end end end end end --[==[ Like split_alternating_runs() but applies an arbitrary function `frob` to "raw-text" segments in the result (i.e. not stuff within balanced delimiters such as footnotes and inflection specs, and not splitchars if present). `frob` is a function of one argument (the string to frob) and should return one argument (the frobbed string). ]==] function export.split_alternating_runs_and_frob_raw_text(run, splitchar, frob, preserve_splitchar) local split_runs = export.split_alternating_runs(run, splitchar, preserve_splitchar) export.frob_raw_text_alternating_runs(split_runs, frob, preserve_splitchar) return split_runs end --[==[ FIXME: Older entry point. Call `split_alternating_runs_and_frob_raw_text()` in [[Module:parse utilities]] directly. Like `split_alternating_runs()` but strips spaces from both ends of the odd-numbered elements (only in odd-numbered runs if `preserve_splitchar` is given). Effectively we leave alone the footnotes and splitchars themselves, but otherwise strip extraneous spaces. Spaces in the middle of an element are also left alone. ]==] function export.split_alternating_runs_and_strip_spaces(segment_runs, splitchar, preserve_splitchar) return export.split_alternating_runs_and_frob_raw_text(segment_runs, splitchar, export.strip_spaces, preserve_splitchar) end --[==[ Split the non-modifier parts of an alternating run (after parse_balanced_segment_run() is called) on a Lua pattern, but not on certain sequences involving characters in that pattern (e.g. comma+whitespace). `splitchar` is the pattern to split on; `preserve_splitchar` indicates whether to preserve the delimiter and is the same as in split_alternating_runs(). `escape_fun` is called beforehand on each run of raw text and should return two values: the escaped run and whether unescaping is needed. If any call to `escape_fun` indicates that unescaping is needed, `unescape_fun` will be called on each run of raw text after splitting on `splitchar`. The return value of this function is as in split_alternating_runs(). ]==] function export.split_alternating_runs_escaping(run, splitchar, preserve_splitchar, escape_fun, unescape_fun) -- First replace comma with a temporary character in comma+whitespace sequences. local need_unescape = false for i in ipairs(run) do if i % 2 == 1 and escape_fun then local this_need_unescape run[i], this_need_unescape = escape_fun(run[i]) need_unescape = need_unescape or this_need_unescape end end if need_unescape then return export.split_alternating_runs_and_frob_raw_text(run, splitchar, unescape_fun, preserve_splitchar) else return export.split_alternating_runs(run, splitchar, preserve_splitchar) end end --[==[ Replace comma with a temporary char in comma + whitespace. ]==] function export.escape_comma_whitespace(run, tempcomma) tempcomma = tempcomma or u(0xFFF0) local escaped = false if run:find("\\,") then -- FIXME: we should probably convert literal \\ to \ to allow people to put a backslash before a comma that -- should be passed through; but maybe it's enough to use an HTML escape for the comma or backslash. run = (run:gsub("\\,", tempcomma)) -- discard backslash before comma, doing its duty to protect the comma escaped = true end if run:find(",%s") then run = (run:gsub(",(%s)", tempcomma .. "%1")) escaped = true end return run, escaped end --[==[ Undo the replacement of comma with a temporary char. ]==] function export.unescape_comma_whitespace(run, tempcomma) tempcomma = tempcomma or u(0xFFF0) return (run:gsub(tempcomma, ",")) end --[==[ Split the non-modifier parts of an alternating run (after parse_balanced_segment_run() is called) on comma, but not on comma+whitespace. See `split_on_comma()` above for more information and the meaning of `tempcomma`. ]==] function export.split_alternating_runs_on_comma(run, tempcomma) tempcomma = tempcomma or u(0xFFF0) -- Replace comma with a temporary char in comma + whitespace. local function escape_comma_whitespace(seg) return export.escape_comma_whitespace(seg, tempcomma) end -- Undo replacement of comma with a temporary char in comma + whitespace. local function unescape_comma_whitespace(seg) return export.unescape_comma_whitespace(seg, tempcomma) end return export.split_alternating_runs_escaping(run, ",", false, escape_comma_whitespace, unescape_comma_whitespace) end --[==[ Split text on a Lua pattern, but not on certain sequences involving characters in that pattern (e.g. comma+whitespace). `splitchar` is the pattern to split on; `preserve_splitchar` indicates whether to preserve the delimiter between split segments. `escape_fun` is called beforehand on the text and should return two values: the escaped run and whether unescaping is needed. If the call to `escape_fun` indicates that unescaping is needed, `unescape_fun` will be called on each run of text after splitting on `splitchar`. The return value of this a list of runs, interspersed with delimiters if `preserve_splitchar` is specified. ]==] function export.split_escaping(text, splitchar, preserve_splitchar, escape_fun, unescape_fun) if not umatch(text, splitchar) then return {text} end -- If there are square or angle brackets, we don't want to split on delimiters inside of them. To effect this, we -- use parse_multi_delimiter_balanced_segment_run() to parse balanced brackets, then do delimiter splitting on the -- non-bracketed portions of text using split_alternating_runs_escaping(), and concatenate back to a list of -- strings. When calling parse_multi_delimiter_balanced_segment_run(), we make sure not to throw an error on -- unbalanced brackets; in that case, we fall through to the code below that handles the case without brackets. if text:find("[%[<]") then local runs = export.parse_multi_delimiter_balanced_segment_run(text, {{"[", "]"}, {"<", ">"}}, "no error on unmatched") if type(runs) ~= "string" then local split_runs = export.split_alternating_runs_escaping(runs, splitchar, preserve_splitchar, escape_fun, unescape_fun) for i = 1, #split_runs do split_runs[i] = concat(split_runs[i]) end return split_runs end end -- First escape sequences we don't want to count for splitting. local need_unescape if escape_fun then text, need_unescape = escape_fun(text) end local parts = split(text, preserve_splitchar and "(" .. splitchar .. ")" or splitchar) if need_unescape then for i = 1, #parts, (preserve_splitchar and 2 or 1) do parts[i] = unescape_fun(parts[i]) end end return parts end --[==[ Split text on comma, but not on comma+whitespace. This is similar to `mw.text.split(text, ",")` but will not split on commas directly followed by whitespace, to handle embedded commas in terms (which are almost always followed by a space). `tempcomma` is the Unicode character to temporarily use when doing the splitting; normally U+FFF0, but you can specify a different character if you use U+FFF0 for some internal purpose. ]==] function export.split_on_comma(text, tempcomma) -- Don't do anything if no comma. Note that split_escaping() has a similar check at the beginning, so if there's a -- comma we effectively do this check twice, but this is worth it to optimize for the common no-comma case. if not text:find(",") then return {text} end tempcomma = tempcomma or u(0xFFF0) -- Replace comma with a temporary char in comma + whitespace. local function escape_comma_whitespace(run) return export.escape_comma_whitespace(run, tempcomma) end -- Undo replacement of comma with a temporary char in comma + whitespace. local function unescape_comma_whitespace(run) return export.unescape_comma_whitespace(run, tempcomma) end return export.split_escaping(text, ",", false, escape_comma_whitespace, unescape_comma_whitespace) end --[==[ Ensure that Wikicode (template calls, bracketed links, HTML, bold/italics, etc.) displays literally in error messages by inserting a Unicode word-joiner symbol after all characters that may trigger Wikicode interpretation. Replacing with equivalent HTML escapes doesn't work because they are displayed literally. I could not get this to work using <nowiki>...</nowiki> (those tags display literally), using using {{#tag:nowiki|...}} (same thing) or using mw.getCurrentFrame():extensionTag("nowiki", ...) (everything gets converted to a strip marker `UNIQ--nowiki-00000000-QINU` or similar). FIXME: This is a massive hack; there must be a better way. ]==] function export.escape_wikicode(term) term = term:gsub("([%[<'{])", "%1" .. u(0x2060)) return term end function export.make_parse_err(arg_gloss) return function(msg, stack_frames_to_ignore) error(export.escape_wikicode(("%s: %s"):format(msg, arg_gloss)), stack_frames_to_ignore) end end -- Parse a term that may include a link '[[LINK]]' or a two-part link '[[LINK|DISPLAY]]'. FIXME: Doesn't currently -- handle embedded links like '[[FOO]] [[BAR]]' or [[FOO|BAR]] [[BAZ]]' or '[[FOO]]s'; if they are detected, it returns -- the term unchanged and `nil` for the display form. local function parse_bracketed_term(term, parse_err) local inside = term:match("^%[%[(.*)%]%]$") if inside then if inside:find("%[%[") or inside:find("%]%]") then -- embedded links, e.g. '[[FOO]] [[BAR]]'; FIXME: we should process them properly return term, nil end local parts = split(inside, "|") if #parts > 2 then parse_err("Saw more than two parts inside a bracketed link") end return parts[1], parts[2] end return term, nil end --[==[ Parse a term that may have a language code (or possibly multiple plus-separated language codes, if `data.allow_multiple` is given) preceding it (e.g. {la:minūtia} or {grc:[[σκῶρ|σκατός]]} or {nan-hbl+hak:[[毋]][[知]]}). Return five arguments: # the original prefixed term; in the case of a Wikipedia or Wikisource prefix followed by a two-part link, it is a two-part link with the Wikipedia/Wikisource prefix moved inside the link; in the case of a Wikipedia or Wikisource prefix followed by a redundant one-part link, the brackets are removed; # the language object corresponding to the language code (possibly a family object if `data.allow_family` is given), or a list of such objects if `data.allow_multiple` is given; # the link if the unprefixed term is of the form <code>[[<var>link</var>|<var>display</var>]]</code> or of the form <code>[[<var>link</var>]]</code>, otherwise the full unprefixed term; # the display part if the term is of the form <code>[[<var>link</var>|<var>display</var>]]</code> or has a Wikipedia or Wikisource prefix (in which case the part minus the prefix and any following language code will be returned, with redundant brackets stripped), else {nil}; # {true} if the term has a Wikipedia/Wikisource prefix, else {false}. Etymology-only languages are always allowed. This function also correctly handles Wikipedia prefixes (e.g. {w:Abatemarco} or {w:it:Colle Val d'Elsa} or {lw:ru:Филарет}) and Wikisource prefixes (e.g. {s:Twelve O'Clock} or {s:[[Walden/Chapter XVIII|Walden]]} or {s:fr:Perceval ou le conte du Graal} or {s:ro:[[Domnul Vucea|Mr. Vucea]]} or {ls:ko:이상적 부인} or {ls:ko:[[조선 독립의 서#一. 槪論|조선 독립의 서]]}) and converts them into two-part links, with the display form not including the Wikipedia or Wikisource prefix unless it was explicitly specified using a two-part link as in {lw:ru:[[Филарет (Дроздов)|Митрополи́т Филаре́т]]} or {ls:ko:[[조선 독립의 서#一. 槪論|조선 독립의 서]]}. The difference between {w:} ("Wikipedia") and {lw:} ("Wikipedia link") is that the latter requires a language code and returns the corresponding language object; same for the difference between {s:} ("Wikisource") and {ls:} ("Wikisource link"). NOTE: Embedded links are not correctly handled currently. If an embedded link is detected, the whole term is returned as the link part (third argument), and the display part is nil. If you construct your own link from the link and display parts, you must check for this. The calling convention is to pass in a single argument `data` containing the following fields: * `term`: The term to parse. * `parse_err`: An optional function of one or two arguments to display an error. (The second argument to the function is the number of stack frames to ignore when calling error(); if you declare your error function with only one argument, things will still work fine.) * `paramname`: If `parse_err` is omitted, this should be a string naming a parameter to display in the error message, along with the term in question, and will be used to generate a `parse_err` function using `make_parse_err()`. (If `paramname` is omitted, just the term itself appears in the error message.) * `allow_multiple`: Allow multiple plus-separated language codes, e.g. {nan-hbl+hak:[[毋]][[知]]}. See above. * `allow_family`: Allow family objects to appear in place of language codes. * `allow_bad`: Don't throw an error on invalid language code prefixes; instead, include the prefix and colon as part of the term. Note that if a prefix doesn't look like a language code (e.g. if it's a number), the code won't even try to parse it as a language code, regardless of the `allow_bad` setting, but will always include it in the term. * `lang_cache`: A table mapping language codes to language objects, where invalid language codes are indicated by the value `false`. If this field is specified, the cache will be consulted before calling `getByCode()` in [[Module:languages]], and the result cached. If not specified, no cache will be used. ]==] function export.parse_term_with_lang(data) local term = data.term local parse_err = data.parse_err or data.paramname and export.make_parse_err(("%s=%s"):format(data.paramname, term)) or export.make_parse_err(term) -- Parse off an initial language code (e.g. 'la:minūtia' or 'grc:[[σκῶρ|σκατός]]'). First check for Wikipedia -- prefixes ('w:Abatemarco' or 'w:it:Colle Val d'Elsa' or 'lw:zh:邹衡') and Wikisource prefixes -- ('s:ro:[[Domnul Vucea|Mr. Vucea]]' or 'ls:ko:이상적 부인'). Wikipedia/Wikisource language codes follow a similar -- format to Wiktionary language codes (see below). Here and below we don't parse if there's a space after the -- colon (happens e.g. if the user uses {{desc|...}} inside of {{col}}, grrr ...). local termlang, foreign_wiki, actual_term = term:match("^(l?[ws]):([a-z][a-z][a-z-]*):([^ ].*)$") if not termlang then termlang, actual_term = term:match("^([ws]):([^ ].*)$") end if termlang then local wiki_links = termlang:find("^l") local base_wiki_prefix = termlang:find("w$") and "w:" or "s:" local wiki_prefix = base_wiki_prefix .. (foreign_wiki and foreign_wiki .. ":" or "") local link, display = parse_bracketed_term(actual_term, parse_err) if link:find("%[%[") or display and display:find("%[%[") then -- FIXME, this should be handlable with the right parsing code parse_err("Cannot have embedded brackets following a Wikipedia (w:... or lw:...) link; expand the term to a fully bracketed term w:[[LINK|DISPLAY]] or similar") end local lang = wiki_links and get_lang(foreign_wiki, parse_err, "allow etym") or nil local prefixed_link = wiki_prefix .. link if display then return ("[[%s|%s]]"):format(prefixed_link, display), lang, prefixed_link, display, true else -- Return the link minus any language codes as the fourth term (display form). Previously we returned `actual_term` -- but this causes problems with redundant Wikipedia links of the form `w:[[Dragon Ball Z]]`. Don't generate a -- two-part link so you can specify a display form in 3=. Note that the fourth and fifth params are currently only -- used in [[Module:quote]]. return prefixed_link, lang, prefixed_link, link, true end end -- Wiktionary language codes are in one of the following formats, where 'x' is a lowercase letter and 'X' an -- uppercase letter: -- xx -- xxx -- xxx-xxx -- xxx-xxx-xxx (esp. for protolanguages) -- xx-xxx (for etymology-only languages) -- xx-xxx-xxx (maybe? for etymology-only languages) -- xx-XX (for etymology-only languages, where XX is a country code, e.g. en-US) -- xxx-XX (for etymology-only languages, where XX is a country code) -- xx-xxx-XX (for etymology-only languages, where XX is a country code) -- xxx-xxx-XX (for etymology-only langauges, where XX is a country code, e.g. nan-hbl-PH) -- Things like xxx-x+ (e.g. cmn-pinyin, cmn-tongyong) -- VL., LL., etc. -- -- We check the for nonstandard Latin etymology language codes separately, and otherwise make only the following -- assumptions: -- (1) There are one to three hyphen-separated components. -- (2) The last component can consist of two uppercase ASCII letters; otherwise, all components contain only -- lowercase ASCII letters. -- (3) Each component must have at least two letters. -- (4) The first component must have two or three letters. local function is_possible_lang_code(code) -- Special hack for Latin variants, which can have nonstandard etym codes, e.g. VL., LL. if code:find("^[A-Z]L%.$") then return true end return code:find("^([a-z][a-z][a-z]?)$") or code:find("^[a-z][a-z][a-z]?%-[A-Z][A-Z]$") or code:find("^[a-z][a-z][a-z]?%-[a-z][a-z]+$") or code:find("^[a-z][a-z][a-z]?%-[a-z][a-z]+%-[A-Z][A-Z]$") or code:find("^[a-z][a-z][a-z]?%-[a-z][a-z]+%-[a-z][a-z]+$") end local function get_by_code(code, allow_bad) local lang if data.lang_cache then lang = data.lang_cache[code] end if lang == nil then lang = get_lang(code, not allow_bad and parse_err or nil, "allow etym", data.allow_family) if data.lang_cache then data.lang_cache[code] = lang or false end end return lang or nil end if data.allow_multiple then local termlang_spec termlang_spec, actual_term = term:match("^([a-zA-Z.,+-]+):([^ ].*)$") if termlang_spec then termlang = split(termlang_spec, "[,+]") local all_possible_code = true for _, code in ipairs(termlang) do if not is_possible_lang_code(code) then all_possible_code = false break end end if all_possible_code then local saw_nil = false for i, code in ipairs(termlang) do termlang[i] = get_by_code(code, data.allow_bad) if not termlang[i] then saw_nil = true end end if saw_nil then termlang = nil else term = actual_term end else termlang = nil end end else termlang, actual_term = term:match("^([a-zA-Z.-]+):([^ ].*)$") if termlang then if is_possible_lang_code(termlang) then termlang = get_by_code(termlang, data.allow_bad) if termlang then term = actual_term end else termlang = nil end end end local link, display = parse_bracketed_term(term, parse_err) return term, termlang, link, display, false end --[==[ Maybe parse any language prefix off of a given term and store the term and language(s) into a new or existing object. This function is useful for implementing a `generate_obj` handler of `parse_inline_modifiers` that can support terms with prefixed language(s). This wraps `parse_term_with_lang` and has the same handling of language prefixes as that function. The calling convention is to pass in a single argument `data` containing the following fields (NOTE: you must set `parse_lang_prefix` to get language-prefix-parsing behavior): * `term`: The term to parse. This is the only required parameter. * `parse_lang_prefix`: This must be specified in order for language prefixes to be recognized and parsed off. * `termobj`: The existing object to store results into. If unspecified, a new object is created. * `term_dest`: The field in `termobj` into which the term itself (minus any language prefix) is stored. If unspecified, defaults to {"term"}. * `parse_err`: An optional function of one or two arguments to display an error. (The second argument to the function is the number of stack frames to ignore when calling error(); if you declare your error function with only one argument, things will still work fine.) * `paramname`: If `parse_err` is omitted, this should be a string naming a parameter to display in the error message, along with the term in question, and will be used to generate a `parse_err` function using `make_parse_err()`. (If `paramname` is omitted, just the term itself appears in the error message.) * `allow_multiple_lang_prefixes`: Allow multiple plus-separated language codes, e.g. {nan-hbl+hak:[[毋]][[知]]}. See `parse_term_with_lang` for more information. * `allow_bad_lang_prefix`: Don't throw an error on invalid language code prefixes; instead, include the prefix and colon as part of the term. Note that if a prefix doesn't look like a language code (e.g. if it's a number), the code won't even try to parse it as a language code, regardless of this setting, but will always include it in the term. * `allow_family_as_lang_prefix`: Allow family objects to appear in place of language codes. * `lang_cache`: A table mapping language codes to language objects, where invalid language codes are indicated by the value `false`. If this field is specified, the cache will be consulted before calling `getByCode()` in [[Module:languages]], and the result cached. If not specified, no cache will be used. The return value is the object in `data.termobj` (if non-{nil}) or a newly-created object (otherwise), with the parsed-off term stored in the field named by the `data.term_dest` property (normally `.term`). If `data.allow_multiple_lang_prefixes` was not given and a language prefix was parsed off, the corresponding language object is stored into both `lang` and `termlang` (the storage into `termlang` is so that the presence of a language prefix can specifically be determined in the event that `lang` is already set in an existing termobj). If `data.allow_multiple_lang_prefixes` was given and one or more language prefixes were parsed off, the first such language object is stored into `lang`, and the list of all language objects stored into `termlangs. ]==] function export.generate_obj_maybe_parsing_lang_prefix(data) local term = data.term local term_dest = data.term_dest or "term" local termobj = data.termobj or {} if data.parse_lang_prefix and term:find(":", nil, true) then local actual_term, termlangs = export.parse_term_with_lang { term = term, parse_err = data.parse_err, paramname = data.paramname, allow_bad = data.allow_bad_lang_prefix, allow_multiple = data.allow_multiple_lang_prefixes, allow_family = data.allow_family_as_lang_prefix, lang_cache = data.lang_cache, } termobj[term_dest] = actual_term ~= "" and actual_term or nil if termlangs then -- If we couldn't parse a language code, don't overwrite an existing setting in `lang` -- that may have originated from a separate |langN= param. if data.allow_multiple_lang_prefixes then termobj.termlangs = termlangs termobj.lang = termlangs and termlangs[1] or nil else termobj.termlang = termlangs termobj.lang = termlangs end end else termobj[term_dest] = term ~= "" and term or nil end return termobj end --[==[ Parse a term that may have inline modifiers attached (e.g. {rifiuti<q:plural-only>} or {rinfusa<t:bulk cargo><lit:resupplying><qq:more common in the plural {{m|it|rinfuse}}>}). * `arg` is the term to parse. * `props` is an object holding further properties controlling how to parse the term (only `param_mods` and `generate_obj` are required): ** `paramname` is the name of the parameter where `arg` comes from, or nil if this isn't available (it is used only in error messages). ** `param_mods` is a table describing the allowed inline modifiers (see below). ** `generate_obj` is a function of one or two arguments that should parse the argument minus the inline modifiers and return a corresponding parsed object (into which the inline modifiers will be rewritten). If declared with one argument, that will be the raw value to parse; if declared with two arguments, the second argument will be the `parse_err` function (see below). ** `parse_err` is an optional function of one argument (an error message) and should display the error message, along with any desired contextual text (e.g. the argument name and value that triggered the error). If omitted, a default function will be generated which displays the error along with the original value of `arg` (passed through {escape_wikicode()} above to ensure that Wikicode (such as links) is displayed literally). ** `splitchar` is a Lua pattern. If specified, `arg` can consist of multiple delimiter-separated terms, each of which may be followed by inline modifiers, and the return value will be a list of parsed objects instead of a single object. Note that splitting on delimiters will not happen in certain protected sequences (by default comma+whitespace; see below). The algorithm to split on delimiters is sensitive to inline modifier syntax and will not be confused by delimiters inside of inline modifiers, which do not trigger splitting (whether or not contained within protected sequences). ** `outer_container`, if specified, is used when multiple delimiter-separated terms are possible, and is the object into which the list of per-term objects is stored (into the `terms` field) and into which any modifiers that are given the `overall` property (see below) will be stored. If given, this value will be returned as the value of {parse_inline_modifiers()}. If `outer_container` is not given, {parse_inline_modifiers()} will return the list of per-term objects directly, and no modifier may have an `overall` property. ** `preserve_splitchar`, if specified, causes the actual delimiter matched by `splitchar` to be returned in the parsed object describing the element that comes after the delimiter. The delimiter is stored in a key whose name is controlled by `delimiter_key`, which defaults to "delimiter". ** `delimiter_key` controls the key into which the actual delimiter is written when `preserve_splitchar` is used. See above. ** `escape_fun` and `unescape_fun` are as in split_escaping() and split_alternating_runs_escaping() above and control the protected sequences that won't be split. By default, `escape_comma_whitespace` and `unescape_comma_whitespace` are used, so that comma+whitespace sequences won't be split. Set to `false` to disable escaping/unescaping. ** `pre_normalize_modifiers`, if specified, is a function of one argument, which can be used to "normalize" modifiers prior to further parsing. This is used, for example, in [[Module:tl-pronunciation]] to convert modifiers of the form `<noun^expectation; hope>` to `<t:noun^expectation; hope>`, so they can be processed as standard modifiers. It is also used in [[Module:ar-verb]] to convert footnotes of the form `[rare]` to `<footnote:[rare]>`, to allow for mixing bracketed footnotes and inline modifiers when overriding verbal nouns and such. It could similarly be used to handle boolean modifiers like `<slb>` in {{tl|desc}} and convert them to a standard form `<slb:1>`. It runs just before parsing out the modifier prefix and value, and is passed an object containing fields `modtext` (the un-normalized modifier text, including surrounding angle brackets, or in some cases, text surrounded by other delimiters such as square brackets, if `parse_inline_modifiers_from_segments()` is being called and the caller did their own parsing of balanced segment runs) and `parse_err` (the passed-in or autogenerated function to signal an error during parsing; a function of one argument, a message, which throws an error displaying that message). It should return a single value, the normalized value of `modtext`, including surrounding angle brackets. `param_mods` is a table describing allowed modifiers. The keys of the table are modifier prefixes and the values are tables describing how to parse and store the associated modifier values. Here is a typical example, for an item that takes the standard modifiers associated with `full_link()` in [[Module:links]], as well as left and right qualifiers and labels: { local param_mods = { alt = {}, t = { -- [[Module:links]] expects the gloss in "gloss". item_dest = "gloss", }, gloss = {}, tr = {}, ts = {}, g = { -- [[Module:links]] expects the genders in "g". `sublist = true` automatically splits on comma (optionally -- with surrounding whitespace). item_dest = "genders", sublist = true, }, pos = {}, lit = {}, id = {}, sc = { -- Automatically parse as a script code and convert to a script object. type = "script", }, -- Qualifiers and labels q = { type = "qualifier", }, qq = { type = "qualifier", }, l = { type = "labels", }, ll = { type = "labels", }, } } In the table values: * `item_dest` specifies the destination key to store the object into (if not the same as the modifier key itself). * `type`, `set`, `sublist` and `convert` have the same meaning as in [[Module:parameters]] and are used for converting the object from the string form given by the user into the form needed for further processing. Note that `type` makes use of additional properties that may be specified. Specifically, if {type = "language"}, the properties `family` and `method` are also examined, and if {type = "family"} or {type = "script"}, the property `method` is examined. * `store` describes how to store the converted modifier value into the parsed object. If omitted, the converted value is simply written into the parsed object under the appropriate key; but an error is generated if the key already has a value. (This means that multiple occurrences of a given modifier are allowed if `store` is given, but not otherwise.) `store` can be one of the following: ** {"insert"}: the converted value is appended to the key's value using {insert()}; if the key has no value, it is first converted to an empty list; ** {"insertIfNot"}: is similar but appends the value using {insertIfNot()} in [[Module:table]]; ** {"insert-flattened"}, the converted value is assumed to be a list and the objects are appended one-by-one into the key's existing value using {insert()}; ** {"insertIfNot-flattened"} is similar but appends using {insertIfNot()} in [[Module:table]]; (WARNING: When using {"insert-flattened"} and {"insertIfNot-flattened"}, if there is no existing value for the key, the converted value is just stored directly. This means that future appends will side-effect that value, so make sure that the return value of the conversion function for this key generates a fresh list each time.) ** a function of one argument, an object with the following properties: *** `dest`: the object to write the value into; *** `key`: the field where the value should be written; *** `converted`: the (converted) value to write; *** `raw_val`: the raw, user-specified value (a string); *** `parse_err`: a function of one argument (an error string), which signals an error, and includes extra context in the message about the modifier in question, the angle-bracket spec that includes the modifier in it, the overall value, and (if `paramname` was given) the parameter holding the overall value. * `overall` only applies if `splitchar` is given. In this case, the modifier applies to the entire argument rather than to an individual term in the argument, and must occur after the last item separated by `splitchar`, instead of being allowed to occur after any of them. The modifier will be stored into the outer container object, which must exist (i.e. `outer_container` must have been given). The return value of {parse_inline_modifiers()} depends on whether `splitchar` and `outer_container` have been given. If neither is given, the return value is the object returned by `generate_obj`. If `splitchar` but not `outer_container` is given, the return value is a list of per-term objects, each of which is generated by `generate_obj`. If both `splitchar` and `outer_container` are given, the return value is the value of `outer_container` and the per-term objects are stored into the `terms` field of this object. ]==] function export.parse_inline_modifiers(arg, props) local segments local function rejoin_bracket_delimited_runs(segments) return export.rejoin_delimited_runs { runs = segments, delimiter_pattern = "^%[.*%]$", } end local rejoin_square_brackets_after_split = false -- The following is an optimization. If we see a square bracket (normally a double square bracket internal link -- [[...]]), we want to not treat delimiter characters inside (either <...> balanced delimiters or separators such -- as commas) as delimiters. But this requires a more sophisticated and slower algorithm, and most of the time it -- isn't needed because there are no square brackets. So we check for a square bracket and fall back to a simpler -- algorithm otherwise (which, since it involves only a single balanced delimiter, can use the built-in %b() Lua -- pattern syntax, which AFAIK is implemented in C). if arg:find("%[") then segments = export.parse_multi_delimiter_balanced_segment_run(arg, {{"[", "]"}, {"<", ">"}}) if not props.splitchar then segments = rejoin_bracket_delimited_runs(segments) else rejoin_square_brackets_after_split = true end else segments = export.parse_balanced_segment_run(arg, "<", ">") end local function verify_no_overall() for _, mod_props in pairs(props.param_mods) do if mod_props.overall then error("Internal caller error: Can't specify `overall` for a modifier in `param_mods` unless `outer_container` property is given") end end end if not props.splitchar then if props.outer_container then error("Internal caller error: Can't specify `outer_container` property unless `splitchar` is given") end verify_no_overall() return export.parse_inline_modifiers_from_segments { group = segments, group_index = nil, separated_groups = nil, arg = arg, props = props, } else local terms = {} if props.outer_container then props.outer_container.terms = terms else verify_no_overall() end local escape_fun = props.escape_fun if escape_fun == nil then escape_fun = export.escape_comma_whitespace end local unescape_fun = props.unescape_fun if unescape_fun == nil then unescape_fun = export.unescape_comma_whitespace end local separated_groups = export.split_alternating_runs_escaping(segments, props.splitchar, props.preserve_splitchar, escape_fun, unescape_fun) for j = 1, #separated_groups, (props.preserve_splitchar and 2 or 1) do if rejoin_square_brackets_after_split then separated_groups[j] = rejoin_bracket_delimited_runs(separated_groups[j]) end local parsed = export.parse_inline_modifiers_from_segments { group = separated_groups[j], group_index = j, separated_groups = separated_groups, arg = arg, props = props, } if props.preserve_splitchar and j > 1 then parsed[props.delimiter_key or "delimiter"] = separated_groups[j - 1][1] end insert(terms, parsed) end if props.outer_container then return props.outer_container else return terms end end end --[==[ Parse a single term that may have inline modifiers attached. This is a helper function of {parse_inline_modifiers()} but is exported separately in case the caller needs to make their own call to {parse_balanced_segment_run()} (as in [[Module:quote]], which splits on several matched delimiters simultaneously). It takes only a single argument, `data`, which is an object with the following fields: * `group`: A list of segments as output by {parse_balanced_segment_run()} (see the overall comment at the top of [[Module:parse utilities]]), or one of the lists returned by calling {split_alternating_runs()}. * `separated_groups`: The list of groups (each of which is of the form of `group`) describing all the terms in the argument parsed by {parse_inline_modifiers()}, or {nil} if this isn't applicable (i.e. multiple terms aren't allowed in the argument). Currently used only the check the number of groups in the list against `group_index`. * `group_index`: The index into `separated_groups` where `group` can be found, or {nil} if not applicable (see below). * `arg`: The original user-specified argument being parsed; used only for error messages and only when `props.parse_err` is not specified. * `props`: The `props` argument to {parse_inline_modifiers()}. The return value is the object created by `generate_obj`, with properties filled in describing the modifiers of the term in question. Note that `props.outer_container` and the `overall` setting of the `props.param_mods` structure are respected, but `props.splitchar` is ignored because the splitting happens in the caller. Specifically, if there are any modifiers with the `overall` setting, `props.separated_groups` and `props.group_index` must be given so that the function is able to determine if the modifier is indeed attached to the last term, and `props.outer_container` must be given because that is where such modifiers are stored. Otherwise, none of these settings need be given. ]==] function export.parse_inline_modifiers_from_segments(data) local props = data.props local group = data.group local function get_valid_prefixes() local valid_prefixes = {} for param_mod, mod_props in pairs(props.param_mods) do if not mod_props.deprecated then insert(valid_prefixes, param_mod) end end sort(valid_prefixes) return valid_prefixes end local function get_arg_gloss() if props.paramname then return ("%s=%s"):format(props.paramname, data.arg) else return data.arg end end local parse_err = props.parse_err or export.make_parse_err(get_arg_gloss()) local term_obj = props.generate_obj(group[1], parse_err) for k = 2, #group - 1, 2 do if group[k + 1] ~= "" then parse_err("Extraneous text '" .. group[k + 1] .. "' after modifier") end local group_k = group[k] if props.pre_normalize_modifiers then -- FIXME: For some use cases, we might have to pass more information. group_k = props.pre_normalize_modifiers { modtext = group_k, parse_err = parse_err } end local modtext = group_k:match("^<(.*)>$") if not modtext then parse_err("Internal error: Modifier '" .. group_k .. "' isn't surrounded by angle brackets") end local prefix, val = modtext:match("^([a-zA-Z0-9+_-]+):(.*)$") if not prefix then local valid_prefixes = get_valid_prefixes() for i, valid_prefix in ipairs(valid_prefixes) do valid_prefixes[i] = "'" .. valid_prefix .. ":'" end parse_err(("Modifier %s%s lacks a prefix, should begin with one of %s"):format( group_k, group_k ~= group[k] and (" (normalized from %s)"):format(group[k]) or "", list_to_text(valid_prefixes))) end local prefix_parse_err if props.parse_err then prefix_parse_err = function(msg, stack_frames_to_ignore) props.parse_err(("%s: modifier prefix '%s' in %s"):format(msg, prefix, group[k]), stack_frames_to_ignore) end else prefix_parse_err = export.make_parse_err(("modifier prefix '%s' in %s in %s"):format( prefix, group[k], get_arg_gloss())) end if props.param_mods[prefix] then local mod_props = props.param_mods[prefix] if mod_props.replaced_by == false then prefix_parse_err( ("Prefix has been removed and is no longer valid%s%s"):format( mod_props.reason and ", " .. mod_props.reason or "", mod_props.instead and "; instead, " .. mod_props.instead or "") ) elseif mod_props.replaced_by then prefix_parse_err( ("Prefix has been replaced by '%s'%s"):format( mod_props.replaced_by, mod_props.reason and ", " .. mod_props.reason or "") ) end local key = mod_props.item_dest or prefix local dest if mod_props.overall then if not data.separated_groups then prefix_parse_err("Internal error: `data.separated_groups` not given when `overall` is seen") end if not props.outer_container then -- This should have been caught earlier during validation in parse_inline_modifiers(). prefix_parse_err("Internal error: `props.outer_container` not given when `overall` is seen") end if data.group_index ~= #data.separated_groups then prefix_parse_err("Prefix should occur after the last comma-separated term") end dest = props.outer_container else dest = term_obj end local converted = val if mod_props.type or mod_props.set or mod_props.sublist or mod_props.convert then -- WARNING: Here as an optimization we embed some knowledge of convert_val() in [[Module:parameters]], -- specifically that if none of `type`, `set`, `sublist` and `convert` are set, the conversion is an -- identity operation and can be skipped. (convert_val() also makes use of the fields `method` and -- `family`, but only if `type` is set to certain values such as "language", "family" or "script", and -- makes use of the field `required`, but only if `set` is set.) If this becomes problematic, consider -- removing the optimization. converted = convert_val(converted, prefix_parse_err, mod_props) end local store = props.param_mods[prefix].store if not store then if dest[key] then prefix_parse_err("Prefix occurs twice") end dest[key] = converted elseif store == "insert" then if not dest[key] then dest[key] = {converted} else insert(dest[key], converted) end elseif store == "insertIfNot" then if not dest[key] then dest[key] = {converted} else insert_if_not(dest[key], converted) end elseif store == "insert-flattened" then if not dest[key] then dest[key] = converted else for _, obj in ipairs(converted) do insert(dest[key], obj) end end elseif store == "insertIfNot-flattened" then if not dest[key] then dest[key] = converted else for _, obj in ipairs(converted) do insert_if_not(dest[key], obj) end end elseif type(store) == "string" then prefix_parse_err(("Internal caller error: Unrecognized value '%s' for `store` property"):format(store)) elseif not is_callable(store) then prefix_parse_err(("Internal caller error: Unrecognized type for `store` property %s"):format(dump(store))) else store{ dest = dest, key = key, converted = converted, raw = val, parse_err = prefix_parse_err } end else local valid_prefixes = get_valid_prefixes() for i, valid_prefix in ipairs(valid_prefixes) do valid_prefixes[i] = "'" .. valid_prefix .. "'" end prefix_parse_err("Unrecognized prefix, should be one of " .. list_to_text(valid_prefixes)) end end return term_obj end return export 7m7efxuys6lveo94nvwye55dw2c55on มอดูล:de-adjective 828 311778 5775184 5728570 2026-10-14T03:54:04Z Octahedron80 267 5775184 Scribunto text/plain local export = {} --[=[ Authorship: <benwing2> ]=] --[=[ TERMINOLOGY: -- "slot" = A particular combination of state/case/gender/number. Example slot names for adjectives are "mix_nom_m" (mixed nominative masculine singular), "sup_str_dat_p" (superlative strong dative plural) and "comp_pred" (comparative predicative). Each slot is filled with zero or more forms. -- "form" = The declined German form representing the value of a given slot. -- "lemma" = The dictionary form of a given German term. Generally the predicative, but may be the strong nominative masculine singular or other form. ]=] local M = require("Module:module loader").init { require = { com = "Module:de-common", iut = "Module:inflection utilities", inflection_table = "Module:inflection-table", JSON = "Module:JSON", links = "Module:links", parameters = "Module:parameters", put = "Module:parse utilities", string_utilities = "Module:string utilities", table = "Module:table", utilities = "Module:utilities", }, loadData = { headword_data = "Module:headword/data", }, lazy = { lang = function() return require("Module:languages").getByCode("de") end, }, } local pretend_from_headword = false local u = mw.ustring.char local rsplit = mw.text.split local rfind = mw.ustring.find local rmatch = mw.ustring.match local rgmatch = mw.ustring.gmatch local rsubn = mw.ustring.gsub local ulen = mw.ustring.len local unpack = unpack or table.unpack -- Lua 5.2 compatibility local uupper = mw.ustring.upper local OMITTED_E = u(0xFFF0) -- version of rsubn() that discards all but the first return value local function rsub(term, foo, bar) local retval = rsubn(term, foo, bar) return retval end -- version of rsubn() that returns a 2nd argument boolean indicating whether -- a substitution was made. local function rsubb(term, foo, bar) local retval, nsubs = rsubn(term, foo, bar) return retval, nsubs > 0 end local function link_term(term, face) return M.links.full_link({ lang = M.lang, term = term }, face) end local endings = { ["str"] = { ["m"] = {"er", "en", "em", "en"}, ["f"] = {"e", "er", "er", "e"}, ["n"] = {"es", "en", "em", "es"}, ["p"] = {"e", "er", "en", "e"}, }, ["wk"] = { ["m"] = {"e", "en", "en", "en"}, ["f"] = {"e", "en", "en", "e"}, ["n"] = {"e", "en", "en", "e"}, ["p"] = {"en", "en", "en", "en"}, }, ["mix"] = { ["m"] = {"er", "en", "en", "en"}, ["f"] = {"e", "en", "en", "e"}, ["n"] = {"es", "en", "en", "es"}, ["p"] = {"en", "en", "en", "en"}, }, } local cases = { "nom", "gen", "dat", "acc" } local genders = { "m", "f", "n", "p" } local states = { "str", "wk", "mix" } local comps = { "", "comp", "sup" } local adjective_slot_list_positive = { -- We generate the lemma into `the_lemma` because `lemma` is special-cased in M.iut.show_forms() -- to hold the lemma string. {"the_lemma", "-"}, } local adjective_slot_list_comparative = { } local adjective_slot_list_superlative = { } for _, comp in ipairs(comps) do local slot_list = comp == "" and adjective_slot_list_positive or comp == "comp" and adjective_slot_list_comparative or adjective_slot_list_superlative local compsup = comp ~= "" and comp .. "_" or "" for _, state in ipairs(states) do for _, gender in ipairs(genders) do local case_endings = endings[state][gender] for i, case in ipairs(cases) do local slot = compsup .. state .. "_" .. case .. "_" .. gender local comp_ending = comp == "comp" and "er" or comp == "sup" and "st" or "" local accel = "adj-form-" .. comp_ending .. case_endings[i] table.insert(slot_list, {slot, accel}) end end end table.insert(slot_list, {compsup .. "pred", "-"}) for _, gender in ipairs(genders) do table.insert(slot_list, {compsup .. "pred_" .. gender, "-"}) end end local adjective_slot_set = {} local all_adjective_slot_list = {} local function add_slots(slot_list) for _, slot_accel in ipairs(slot_list) do table.insert(all_adjective_slot_list, slot_accel) local slot, accel = unpack(slot_accel) if slot ~= "the_lemma" then adjective_slot_set[slot] = true end end end add_slots(adjective_slot_list_positive) add_slots(adjective_slot_list_comparative) add_slots(adjective_slot_list_superlative) local function add(base, slot, stem, ending, footnotes) if not ending or base.suppress and base.suppress[ending] then return end local function do_combine_stem_ending(stem, ending) return rsub(stem, OMITTED_E, "") .. ending end if ending == "en" and (base.props.sync_n or base.props.sync_mn or base.props.sync_mns) then ending = "n" elseif ending == "em" and (base.props.sync_mn or base.props.sync_mns) then ending = "m" elseif ending == "es" and base.props.sync_mns then ending = "s" end footnotes = M.iut.combine_footnotes(base.footnotes, footnotes) local ending_obj = M.iut.combine_form_and_footnotes(ending, footnotes) M.iut.add_forms(base.forms, slot, stem, ending_obj, do_combine_stem_ending) end local function decline(base, stem, compsup) for _, state in ipairs(states) do local prefix = compsup .. state .. "_" for _, gender in ipairs(genders) do for i, case in ipairs(cases) do add(base, prefix .. case .. "_" .. gender, stem, endings[state][gender][i]) end end end end local function parse_indicator_spec(angle_bracket_spec, lemma, pagename) if lemma == "" then lemma = pagename end local base = {forms = {}, stems = {}, overrides = {}, props = {}} base.orig_lemma = lemma base.orig_lemma_no_links = M.links.remove_links(lemma) base.lemma = base.orig_lemma_no_links local inside = rmatch(angle_bracket_spec, "^<(.*)>$") assert(inside) local function parse_err(msg) error(msg .. ": <" .. inside .. ">") end --[=[ Parse a single override spec and return two values: the slot the override applies to and the override specs. The input is a list where the footnotes have been separated out, i.e. parse_balanced_segment_run(..., "[", "]") has already been called. ]=] local function parse_override(segments) local part = segments[1] local slot, rest = rmatch(part, "^(.-):(.*)$") if not adjective_slot_set[slot] then parse_err("Unrecognized slot '" .. slot .. "' in override: '" .. table.concat(segments) .. "'") end segments[1] = rest return slot, M.com.fetch_specs(segments, ":", "override", nil, parse_err) end if inside ~= "" then local segments = M.put.parse_balanced_segment_run(inside, "[", "]") local dot_separated_groups = M.put.split_alternating_runs_and_strip_spaces(segments, "%.") for i, dot_separated_group in ipairs(dot_separated_groups) do local part = dot_separated_group[1] if part == "comp" then part = "comp:+" end if rfind(part, "^comp:") or rfind(part, "^sup:") or rfind(part, "^stem:") then local spectype, rest = rmatch(part, "^(.-):(.*)$") if base[spectype] then parse_err("Can't specify value for '" .. spectype .. "' twice") end dot_separated_group[1] = rest base[spectype] = M.com.fetch_specs(dot_separated_group, ":", spectype, nil, parse_err) elseif rfind(part, "^state:") then if #dot_separated_group > 1 then parse_err("Can't specify footnotes with 'state': '" .. table.concat(dot_separated_group) .. "'") end if base.state then parse_err("Can't specify value for 'state:' twice") end local state = rsub(part, "state:", "") if not M.table.contains(states, state) then parse_err("Unrecognized state '" .. state .. "', should be one of " .. table.concat(states, "/")) end base.state = state elseif rfind(part, "^suppress:") then if #dot_separated_group > 1 then parse_err("Can't specify footnotes with 'suppress': '" .. table.concat(dot_separated_group) .. "'") end if base.suppress then parse_err("Can't specify value for 'suppress:' twice") end part = rsub(part, "suppress:", "") local endings = rsplit(part, "%s*:%s*") local suppress = {} for _, ending in ipairs(endings) do if ending ~= "e" and ending ~= "em" and ending ~= "en" and ending ~= "er" and ending ~= "es" then parse_err("Unrecognized ending '" .. ending .. "' in suppress: spec: '" .. table.concat(dot_separated_group) .. "'") end suppress[ending] = true end base.suppress = suppress elseif part:find(":") then local slot, override = parse_override(dot_separated_group) if base.overrides[slot] then parse_err("Can't specify override twice for slot '" .. slot .. "'") else base.overrides[slot] = override end elseif part == "" then if #dot_separated_group == 1 then parse_err("Blank indicator") end base.footnotes = M.com.fetch_footnotes(dot_separated_group, parse_err) elseif #dot_separated_group > 1 then parse_err("Footnotes only allowed with slot overrides, comp:, sup:, stem: or by themselves: '" .. table.concat(dot_separated_group) .. "'") elseif part == "ss" or part == "indecl" or part == "predonly" or part == "sync_n" or part == "sync_mn" or part == "sync_mns" then if base.props[part] then parse_err("Can't specify '" .. part .. "' twice") end base.props[part] = true else parse_err("Unrecognized indicator '" .. part .. "'") end end end return base end local function generate_default_stem(base) if base.props.ss then return rsub(base.lemma, "ß$", "ss") end if base.lemma:find("e$") then return rsub(base.lemma, "e$", "") end return rsub(base.lemma, "([ai])bel$", "%1b" .. OMITTED_E .. "l") end local function generate_default_comp(base, stem) return stem .. "er" end local function generate_default_sup(base, stem) if rfind(stem, OMITTED_E .. "[lmnr]$") then -- If we omitted -e- in the stem, put it back. E.g. [[simpel]], stem ''simpl-'', comparative -- ''simpler'', superlative ''simpelst-'', or [[abgeschlossen]], comparative ''abgeschlossener'' or -- ''abgeschlossner'', superlative just ''abgeschlossenst-''. local non_ending, ending = rmatch(stem, "^(.*)" .. OMITTED_E .. "([lmnr])$") if base.props.ss then -- [[abgeschlossen]] -> ''abgeschloßner'' -> ''abgeschlossenst'' (pre-1996 spelling) non_ending = rsub(non_ending, "ß$", "ss") end return non_ending .. "e" .. ending .. "st" elseif rfind(stem, "gr[oö]ß$") or rfind(stem, "gr[oö]ss$") then -- Write this way so we can be called either on positive or comparative stem. return stem .. "t" elseif rfind(stem, "h[oö]h$") then -- [[hoch]], [[ranghoch]], etc. -- Write this way so we can be called either on positive or comparative stem. return rsub(stem, "..$", "öchst") elseif rfind(stem, "n[aä]h$") then -- [[nah]], [[äquatornah]], [[bahnhofsnah]], [[bodennah]], [[citynah]], [[hautnah]] (has no comp in dewikt), -- [[körpernah]], [[zeitnah]], etc. -- NOTE: [[erdnah]], [[praxisnah]] can be either regular (like [[froh]] below) or following [[nah]]. -- Write this way so we can be called either on positive or comparative stem. return rsub(stem, "..$", "ächst") elseif rfind(stem, "[aeiouäöü]h$") then -- [[froh]], [[farbenfroh]], [[lebensfroh]], [[schadenfroh]], [[früh]], [[jäh]] (has only jähest in dewikt), [[rauh]], -- [[roh]], [[weh]], [[zäh]] return {stem .. "st", stem .. "est"} elseif rfind(stem, "e[rl]?nd$") then -- Present participles; non-present-participles like [[elend]], [[behend]]/[[behende]], [[horrend]] need special-casing return stem .. "st" elseif rfind(stem, "[^wi]e[rl]?t$") then -- Most adjectives in -et, -elt, -ert (past participles specifically), but not adjectives in -iert, -ielt or -- non-past-participles in -wert; other non-past-participles like [[alert]], [[inert]], [[concret]], [[discret]], -- [[obsolet]] need special-casing return stem .. "st" elseif rfind(stem, "[^e]igt$") then -- Most adjectives in -igt (past participles specifically), but not adjectives in -eigt such as [[abgeneigt]]; -- exceptions like [[gewitzigt]], [[gerechtfertigt]] need special-casing return stem .. "st" elseif rfind(stem, "[ae][iuwy]$") then -- Those ending in diphthongs can take either -est -or -st (scheu, neu, schlau, frei, blau/blaw, etc.) return {stem .. "est", stem .. "st"} elseif rfind(stem, "[szxßdt]$") then if base.props.ss and rfind(stem, "ß$") then return rsub(stem, ".$", "ssest") end return stem .. "est" elseif rfind(stem, "sk$") then -- [[burlesk]], [[chevaleresk]], [[dantesk]], [[grotesk]], [[pittoresk]], [[pythonesk]]; also [[brüsk]], [[promisk]] return stem .. "est" elseif rfind(stem, "[^i]sch$") then -- Adjectives in -sch where it is not an adjective-forming ending typically can take either -est or -st; examples -- are [[barsch]], [[falsch]], [[fesch]], [[forsch]]/[[nassforsch]], [[frisch]]/[[taufrisch]], [[harsch]], -- [[keusch]]/[[unkeusch]], [[lasch]], [[morsch]], [[rasch]], [[wirsch]]/[[unwirsch]]; maybe [[krüsch]]/[[krütsch]]? -- (dewikt says sup. only ''krüschst'', Duden says only ''krüschest''); a few can take only -est per dewikt -- [[deutsch]]/[[süddeutsch]]/[[teutsch]], [[hübsch]], [[krosch]], [[resch]], [[rösch]] -- Cases where -sch without -isch occurs that is an adjective-forming ending need special-casing, e.g. -- [[figelinsch]] return {stem .. "est", stem .. "st"} else return stem .. "st" end end local function process_spec(base, destforms, slot, specs, base_stem, form_default) local function do_form_default(form) local retval = form_default(base, form) if type(retval) ~= "table" then retval = {retval} end return retval end specs = specs or {{form = "+"}} for _, spec in ipairs(specs) do local forms if spec.form == "-" then -- Skip "-"; effectively, no forms get inserted into output.comp. elseif spec.form == "+" then forms = M.iut.flatmap_forms(base_stem, do_form_default) elseif rfind(spec.form, "^%+") then local ending = rsub(spec.form, "^%+", "") forms = M.iut.map_forms(base_stem, function(form) return form .. ending end) elseif spec.form == "^" then forms = M.iut.flatmap_forms(base_stem, function(form) return do_form_default(M.com.apply_umlaut(form)) end) elseif rfind(spec.form, "^%^") then local ending = rsub(spec.form, "^%^", "") forms = M.iut.map_forms(base_stem, function(form) return M.com.apply_umlaut(form) .. ending end) elseif spec.form == "-e" then forms = M.iut.flatmap_forms(base_stem, function(form) local non_ending, ending = rmatch(form, "^(.*)e([lmnr])$") if not non_ending then error("Can't use '-e' with stem '" .. form .. "'; should end in -el, -em, -en or -er") end if base.props.ss then non_ending = rsub(non_ending, "ss$", "ß") end return do_form_default(non_ending .. OMITTED_E .. ending) end) else M.iut.insert_form(destforms, slot, spec) end if forms then forms = M.iut.convert_to_general_list_form(forms, spec.footnotes) M.iut.insert_forms(destforms, slot, forms) end end end local function detect_indicator_spec(alternant_multiword_spec, base) -- First generate the stem(s), substituting + with the default formed from the lemma. process_spec(base, base.stems, "stem", base.stem, {{form = generate_default_stem(base)}}, function(base, stem) return stem end) -- Next process the superative, if specified. We do this first so that if there is a superative and no -- comparative specified, we add a comparative; but if sup:- is given, we don't add a comparative. if base.sup then process_spec(base, base.stems, "sup", base.sup, base.stems.stem, generate_default_sup) if base.stems.sup and not base.comp then base.comp = {{form = "+"}} end end -- Next process the comparative, if specified (or defaulted because a superlative was specified). if base.comp then process_spec(base, base.stems, "comp", base.comp, base.stems.stem, generate_default_comp) end -- Next, if comparative specified but not superlative, derive the superlative(s) from the comparative(s). if base.stems.comp and not base.sup then local sups = M.iut.flatmap_forms(base.stems.comp, function(form) if not rfind(form, "er$") then error("Don't know how to derive superlative from comparative '" .. form .. "' because it doesn't end in -er; specify the superlative explicitly using sup:...") end local retval = generate_default_sup(base, rsub(form, "er$", "")) if type(retval) ~= "table" then retval = {retval} end return retval end) M.iut.insert_forms(base.stems, "sup", sups) end -- Make sure all alternants agree in having a comparative and/or superlative. for _, compsup in ipairs { {"comp", "has_comp", "comparative"}, {"sup", "has_sup", "superlative"} } do local stem, altprop, desc = unpack(compsup) local has_stem = not not base.stems[stem] if alternant_multiword_spec.props[altprop] == nil then alternant_multiword_spec.props[altprop] = has_stem elseif alternant_multiword_spec.props[altprop] ~= has_stem then error("If one alternant has a " .. desc .. ", all must") end end if base.props.predonly then base.props.indecl = true end if base.overrides.pred and base.overrides.pred[1].form == "-" then base.props.nopred = true end if base.props.predonly and base.props.nopred then error("Can't be both 'predonly' and 'pred:-'") end -- Make sure all alternants agree in 'state' if specified. local stateval = base.state or false if alternant_multiword_spec.state == nil then alternant_multiword_spec.state = stateval elseif alternant_multiword_spec.state ~= stateval then error("All alternants must agree in the value of 'state', if specified") end -- Make sure all alternants agree in various properties. for _, propdesc in ipairs { {"indecl"}, {"nopred", "pred:-"}, {"predonly"} } do local prop, desc = unpack(propdesc) desc = desc or prop local val = not not base.props[prop] if alternant_multiword_spec.props[prop] == nil then alternant_multiword_spec.props[prop] = val elseif alternant_multiword_spec.props[prop] ~= val then error("If one alternant specifies '" .. desc .. "', all must") end end end local function detect_all_indicator_specs(alternant_multiword_spec) M.iut.map_word_specs(alternant_multiword_spec, function(base) detect_indicator_spec(alternant_multiword_spec, base) end) end local function process_slot_overrides(base) for slot, overrides in pairs(base.overrides) do local origforms = base.forms[slot] base.forms[slot] = nil process_spec(base, base.forms, slot, overrides, origforms, function(base, form) return form end) end end local function decline_adjective(base) decline(base, base.stems.stem, "") if base.stems.comp then decline(base, base.stems.comp, "comp_") end if base.stems.sup then decline(base, base.stems.sup, "sup_") end add(base, "the_lemma", base.orig_lemma, "") add(base, "pred", base.lemma, "") if base.stems.comp and not base.props.nopred then add(base, "comp_pred", base.stems.comp, "") end if base.stems.sup and not base.props.nopred then add(base, "sup_pred", M.iut.map_forms(base.stems.sup, function(form) if not rfind(form, "[%[%]]") then form = "[[" .. form .. "en]]" elseif rfind(form, "%]%]$") then form = rsub(form, "%]%]$", "en]]") else form = form .. "en" end return "[[am]] " .. form end), "") end process_slot_overrides(base) end -- Compute the categories to add the adjective to, as well as the annotation to display in the -- declension title bar. We combine the code to do these functions as both categories and -- title bar contain similar information. local function compute_categories_and_annotation(alternant_multiword_spec) alternant_multiword_spec.categories = {} local function insert(cattype) cattype = rsub(cattype, "~", alternant_multiword_spec.pos) M.table.insertIfNot(alternant_multiword_spec.categories, cattype .. "ภาษาเยอรมัน") end local annotation local annparts = {} if not alternant_multiword_spec.props.has_comp then table.insert(annparts, "เปรียบเทียบไม่ได้") insert("~เปรียบเทียบไม่ได้") end if not alternant_multiword_spec.forms.pred then table.insert(annparts, "ไม่มีภาคแสดง") insert("~ไม่มีภาคแสดง") end alternant_multiword_spec.annotation = table.concat(annparts, ", ") end local function show_forms(alternant_multiword_spec) local lemmas = alternant_multiword_spec.forms.the_lemma or {} local function generate_link(data) local slot = data.slot local link = M.links.full_link { lang = M.lang, term = data.form.formval_for_link, tr = "-", accel = data.form.accel_obj } local footnote_text = M.iut.get_footnote_text(data.form.footnotes, data.footnote_obj) link = link .. footnote_text if rfind(slot, "pred_m$") then return link_term("[[er]] [[ist]]") .. " " .. link elseif rfind(slot, "pred_f$") then return link_term("[[sie]] [[ist]]") .. " " .. link elseif rfind(slot, "pred_n$") then return link_term("[[es]] [[ist]]") .. " " .. link elseif rfind(slot, "pred_p$") then return link_term("[[sie]] [[sind]]") .. " " .. link elseif rfind(slot, "wk_") then local case, gender = rmatch(slot, ".*wk_(.*)_([mfnp])$") return link_term(M.com.articles[gender]["def_" .. case]) .. " " .. link elseif rfind(slot, "mix_") then local case, gender = rmatch(slot, ".*mix_(.*)_([mfnp])$") return link_term(M.com.articles[gender]["ind_" .. case]) .. " " .. link else return link end end local function join_spans(data) return table.concat(data.formval_spans, "<br />") end local function copy_predicate_forms(compsup) for _, gender in ipairs(genders) do alternant_multiword_spec.forms[compsup .. "pred_" .. gender] = alternant_multiword_spec.forms[compsup .. "pred"] end end copy_predicate_forms("") copy_predicate_forms("comp_") copy_predicate_forms("sup_") local props = { lang = M.lang, lemmas = lemmas, generate_link = generate_link, join_spans = join_spans, } props.slot_list = adjective_slot_list_positive M.iut.show_forms(alternant_multiword_spec.forms, props) alternant_multiword_spec.footnote_positive = alternant_multiword_spec.forms.footnote if alternant_multiword_spec.props.has_comp then props.slot_list = adjective_slot_list_comparative M.iut.show_forms(alternant_multiword_spec.forms, props) alternant_multiword_spec.footnote_comparative = alternant_multiword_spec.forms.footnote end if alternant_multiword_spec.props.has_sup then props.slot_list = adjective_slot_list_superlative M.iut.show_forms(alternant_multiword_spec.forms, props) alternant_multiword_spec.footnote_superlative = alternant_multiword_spec.forms.footnote end end local single_state_table_spec = [=[ ! กรรตุการก | {COMPSUPSTATE_nom_m} | {COMPSUPSTATE_nom_f} | {COMPSUPSTATE_nom_n} | {COMPSUPSTATE_nom_p} |- data-wikt-palette-p="PALETTE" ! สัมพันธการก | {COMPSUPSTATE_gen_m} | {COMPSUPSTATE_gen_f} | {COMPSUPSTATE_gen_n} | {COMPSUPSTATE_gen_p} |- data-wikt-palette-p="PALETTE" ! สัมปทานการก | {COMPSUPSTATE_dat_m} | {COMPSUPSTATE_dat_f} | {COMPSUPSTATE_dat_n} | {COMPSUPSTATE_dat_p} |- data-wikt-palette-p="PALETTE" ! กรรมการก | {COMPSUPSTATE_acc_m} | {COMPSUPSTATE_acc_f} | {COMPSUPSTATE_acc_n} | {COMPSUPSTATE_acc_p} ]=] local function state_rows(state, palette) return rsub(rsub(single_state_table_spec, "PALETTE", palette), "STATE", state) end local function make_table(alternant_multiword_spec) if alternant_multiword_spec.props.indecl then if alternant_multiword_spec.props.predonly then return "ผันรูปไม่ได้, ภาคแสดงเท่านั้น" elseif alternant_multiword_spec.props.nopred then return "ผันรูปไม่ได้, ไม่มีภาคแสดง" else return "ผันรูปไม่ได้" end end local forms = alternant_multiword_spec.forms local table_spec = (alternant_multiword_spec.state and -- Single-state table ([=[ ! rowspan="2" class="outer" | พจน์ / เพศ ! colspan="3" class="outer" | เอกพจน์ ! rowspan="2" class="outer" | พหูพจน์ |- ! class="outer" | เพศชาย ! class="outer" | เพศหญิง ! class="outer" | เพศกลาง |- data-wikt-palette-p="indigo" ]=] .. state_rows(alternant_multiword_spec.state, "indigo")) or not alternant_multiword_spec.args.truncate and -- Normal (non-truncated) table ([=[ ! colspan="2" rowspan="2" class="outer" | พจน์ / เพศ ! colspan="3" class="outer" | เอกพจน์ ! rowspan="2" class="outer" | พหูพจน์ |- ! class="outer" | เพศชาย ! class="outer" | เพศหญิง ! class="outer" | เพศกลาง |- data-wikt-palette-p="yellow" ! colspan="2" class="outer" | ภาคแสดง | {COMPSUPpred_m} | {COMPSUPpred_f} | {COMPSUPpred_n} | {COMPSUPpred_p} |- data-wikt-palette-p="blue" ! rowspan="4" class="outer" | การผันรูปแบบแข็ง <br/> (ไม่มีคำกำกับนาม) ]=] .. state_rows("str", "blue") .. [=[ |- data-wikt-palette-p="green" ! rowspan="4" class="outer" | การผันรูปแบบอ่อน <br/> (มีคำกำกับนามชี้เฉพาะ) ]=] .. state_rows("wk", "green") .. [=[ |- data-wikt-palette-p="orange" ! rowspan="4" class="outer" | การผันรูปแบบผสม <br/> (มีคำกำกับนามไม่ชี้เฉพาะ) ]=] .. state_rows("mix", "orange")) or -- Truncated table, for the documentation page where otherwise we'd hit the template expand limit [=[ ! colspan="2" rowspan="2" class="outer" | พจน์ / เพศ ! colspan="3" class="outer" | เอกพจน์ ! rowspan="2" class="outer" | พหูพจน์ |- ! class="outer" | เพศชาย ! class="outer" | เพศหญิง ! class="outer" | เพศกลาง |- data-wikt-palette-p="yellow" ! colspan="2" class="outer" | ภาคแสดง | {COMPSUPpred_m} | {COMPSUPpred_f} | {COMPSUPpred_n} | {COMPSUPpred_p} |- data-wikt-palette-p="blue" ! rowspan="2" class="outer" | การผันรูปแบบแข็ง <br/> (ไม่มีคำกำกับนาม) ! กรรตุการก | {COMPSUPstr_nom_m} | {COMPSUPstr_nom_f} | {COMPSUPstr_nom_n} | {COMPSUPstr_nom_p} |- data-wikt-palette-p="blue" ! ... | colspan="4" | ... |- data-wikt-palette-p="green" ! rowspan="2" class="outer" | การผันรูปแบบอ่อน <br/> (มีคำกำกับนามชี้เฉพาะ) ! กรรตุการก | {COMPSUPwk_nom_m} | {COMPSUPwk_nom_f} | {COMPSUPwk_nom_n} | {COMPSUPwk_nom_p} |- data-wikt-palette-p="green" ! ... | colspan="4" | ... |- data-wikt-palette-p="orange" ! rowspan="2" class="outer" | การผันรูปแบบผสม <br/> (มีคำกำกับนามไม่ชี้เฉพาะ) ! กรรตุการก | {COMPSUPmix_nom_m} | {COMPSUPmix_nom_f} | {COMPSUPmix_nom_n} | {COMPSUPmix_nom_p} |- data-wikt-palette-p="orange" ! ... | colspan="4" | ... ]=]) local ital_lemma = '<i lang="de" class="Latn">' .. forms.lemma .. "</i>" local annotation = alternant_multiword_spec.annotation if annotation ~= "" then annotation = " (<span style=\"font-size: smaller;\">" .. annotation .. "</span>)" end local function format_degree(compsup, title, footnote) forms.footnote = footnote return M.string_utilities.format(M.inflection_table.make_top { lang = "de", title = title .. "ของ " .. ital_lemma .. annotation, tall = "yes", } .. rsub(table_spec, "COMPSUP", compsup) .. M.inflection_table.make_bottom { notes = footnote ~= "" and "{footnote}" or nil, }, forms) end local degrees = {format_degree("", "รูปเชิงบวก", alternant_multiword_spec.footnote_positive)} if alternant_multiword_spec.props.has_comp then table.insert(degrees, format_degree("comp_", "รูปขั้นกว่า", alternant_multiword_spec.footnote_comparative)) end if alternant_multiword_spec.props.has_sup then table.insert(degrees, format_degree("sup_", "รูปขั้นสุด", alternant_multiword_spec.footnote_superlative)) end if #degrees == 1 then return degrees[1] end return M.inflection_table.make_box({ title = "-", flow = "vertical", }, "\n" .. table.concat(degrees, "\n") .. "\n") end local boolean_param = {type = "boolean"} -- Externally callable function to parse and decline a noun given user-specified arguments. -- Return value is ALTERNANT_MULTIWORD_SPEC, an object where the declined forms are in `ALTERNANT_MULTIWORD_SPEC.forms` -- for each slot. If there are no values for a slot, the slot key will be missing. The value for a given slot is a -- list of objects {form=FORM, footnotes=FOOTNOTES}. function export.do_generate_forms(parent_args, pos, from_headword, def) local params = { [1] = true, pagename = true, -- If truncate=1 is specified, truncate the tables to reduce the template expansion size, -- for use on documentation and test pages. truncate = boolean_param, json = boolean_param, -- for bot use } if from_headword or pretend_from_headword then params["head"] = {list = true} params["id"] = true params["sort"] = true params["splithyph"] = boolean_param params["nolinkhead"] = boolean_param params["new"] = boolean_param end local args = M.parameters.process(parent_args, params) local pagename = args.pagename or M.headword_data.pagename if not args[1] then if pagename == "de-adecl" then args[1] = def or "lang<comp:^>" else args[1] = "" end end local arg1 = M.com.add_angle_brackets(args[1]) local function do_parse_indicator_spec(angle_bracket_spec, lemma) return parse_indicator_spec(angle_bracket_spec, lemma, pagename) end local parse_props = { parse_indicator_spec = do_parse_indicator_spec, allow_default_indicator = true, allow_blank_lemma = true, } local alternant_multiword_spec = M.iut.parse_inflected_text(arg1, parse_props) alternant_multiword_spec.pos = pos or "คำคุณศัพท์" alternant_multiword_spec.args = args alternant_multiword_spec.forms = {} alternant_multiword_spec.props = {} detect_all_indicator_specs(alternant_multiword_spec) local inflect_props = { lang = M.lang, skip_slot = function(slot) return false end, slot_list = all_adjective_slot_list, inflect_word_spec = decline_adjective, } M.iut.inflect_multiword_or_alternant_multiword_spec(alternant_multiword_spec, inflect_props) compute_categories_and_annotation(alternant_multiword_spec) if args.json then return M.JSON.toJSON(alternant_multiword_spec) end return alternant_multiword_spec end -- Entry point for {{de-adecl}}. Template-callable function to parse and decline -- an adjective given user-specified arguments and generate a displayable table -- of the declined forms. function export.show(frame) local parent_args = frame:getParent().args local alternant_multiword_spec = export.do_generate_forms(parent_args) if type(alternant_multiword_spec) == "string" then -- json=1 specified return alternant_multiword_spec end show_forms(alternant_multiword_spec) return make_table(alternant_multiword_spec) .. M.utilities.format_categories( alternant_multiword_spec.categories, M.lang) end return export tfks494qgcmsat8ejpgpoj8mlx6yb2l มอดูล:languages/data/3/x/extra 828 312276 5775299 5715035 2026-10-14T10:34:40Z Octahedron80 267 5775299 Scribunto text/plain local m = {} m["xaa"] = { aliases = {"อาหรับแบบมัวร์", "อาหรับแบบสเปน", "Andalusian Arabic", "Andalusi Arabic", "Moorish Arabic", "Spanish Arabic"}, } m["xab"] = { } m["xac"] = { } m["xad"] = { } m["xae"] = { } m["xag"] = { other_names = {"Caucasian Albanian", "Old Udi"}, } m["xai"] = { } m["xaj"] = { } m["xak"] = { other_names = {"Mako", "Máku", "Mácu", "Macú", "Macu", "Máko", "Maku-Auari"}, } m["xal"] = { other_names = {"โอยรัต", "Kalmyk", "Oirat", "Modern Oirat"}, } m["xam"] = { other_names = {"ǀKham"}, } m["xan"] = { } m["xao"] = { } m["xap"] = { } m["xaq"] = { } m["xar"] = { } m["xas"] = { } m["xat"] = { } m["xau"] = { } m["xav"] = { } m["xaw"] = { } m["xay"] = { } m["xbb"] = { } m["xbc"] = { aliases = {"Bactrian", "Greco-Bactrian", "Kushan", "Kushano-Bactrian"}, } m["xbd"] = { } m["xbe"] = { } m["xbg"] = { } m["xbi"] = { } m["xbj"] = { } m["xbm"] = { aliases = {"Middle Breton"}, } m["xbn"] = { } m["xbo"] = { other_names = {"Bolğar", "Bulghar", "Bolghar", "Bolgarian", "Bolgar"}, } m["xbp"] = { } m["xbr"] = { other_names = {"ซุมบาตะวันออก", "ซุมบา", "Kambera", "East Sumbanese", "Sumbanese"}, } m["xbw"] = { } m["xby"] = { other_names = {"Badjala"}, -- contrast gbw } m["xcb"] = { } m["xcc"] = { } m["xce"] = { } m["xch"] = { } m["xcl"] = { aliases = {"อาร์มีเนียคลาสสิก", "กราบาร์", "Old Armenian", "Classical Armenian", "Liturgical Armenian", "Grabar"}, } m["xcm"] = { } m["xcn"] = { } m["xco"] = { aliases = {"โคแรสเมีย", "Chorasmian", "Khwarazmian", "Khorezmian"}, } m["xcr"] = { } m["xct"] = { aliases = {"Classical Tibetan"}, } m["xcu"] = { } m["xcv"] = { other_names = {"Chuvantsy", "Chuvansky"}, } m["xcw"] = { } m["xcy"] = { } m["xda"] = { } m["xdc"] = { } m["xdk"] = { other_names = {"Darug", "Dharruk", "Dharuk", "Eora", "Iora", "Iyora", "Sydney"}, } m["xdm"] = { aliases = {"Edomite"}, } m["xdy"] = { } m["xeb"] = { } m["xed"] = { } m["xeg"] = { } m["xel"] = { } m["xem"] = { } m["xep"] = { } m["xer"] = { other_names = {"Koromu", "Cherente", "Sherenté"}, } m["xes"] = { other_names = {"Koromu"}, } m["xet"] = { } m["xeu"] = { } m["xfa"] = { aliases = {"ฟาลิสกัน", "Faliscan"}, } m["xga"] = { } m["xgb"] = { } m["xgd"] = { } m["xgf"] = { other_names = {"Tongva", "Gabrielino", "Gabrieleño", "Fernandeño"}, } m["xgg"] = { } m["xgi"] = { } m["xgl"] = { } m["xgm"] = { other_names = {"Darambal", "Dharumbal", "Dharambal", "Guwinmal", "Kuinmabara", "Karunbara", "Rakiwara", "Wapabara"}, } m["xgr"] = { } m["xgu"] = { } m["xgw"] = { } m["xha"] = { } m["xhc"] = { other_names = {"Hunnish"}, } m["xhd"] = { } m["xhe"] = { other_names = {"Khetranki"}, } m["xhm"] = { aliases = {"Middle Khmer"}, } m["xhr"] = { } m["xht"] = { other_names = {"Hattian"}, } m["xhu"] = { aliases = {"Hurrian"}, } m["xhv"] = { } m["xib"] = { } m["xii"] = { } m["xin"] = { } m["xil"] = { } m["xir"] = { } m["xis"] = { } m["xiv"] = { } m["xiy"] = { other_names = {"Shipaja", "Xipaia"}, } m["xjb"] = { } m["xka"] = { } m["xkb"] = { } m["xkc"] = { aliases = {"Xoini, Kho'ini"}, } m["xkd"] = { } m["xke"] = { other_names = {"Krio Dayak", "Krio", "Keriu", "Seputan"}, } m["xkf"] = { other_names = {"Kheng"}, } m["xkg"] = { } m["xki"] = { } m["xkj"] = { } m["xkk"] = { other_names = {"Lamam"}, -- Lamam is a Kaco' clan name, formerly encoded by the ISO as 'lmm' } m["xkl"] = { other_names = {"Mainstream Kenyah", "Kenyah"}, } m["xkn"] = { } m["xko"] = { } m["xkp"] = { } m["xkq"] = { } m["xkr"] = { other_names = {"Chacriabá", "Chacriaba", "Chakriaba", "Xakriaba", "Xacriabá", "Xacriaba", "Chikriaba", "Xikriabá", "Xikriaba", "Shacriaba", "Xicriabá", "Xicriaba", "Chicriabá", "Chicriaba"}, } m["xks"] = { } m["xkt"] = { } m["xku"] = { } m["xkv"] = { } m["xkw"] = { } m["xkx"] = { } m["xky"] = { } m["xkz"] = { other_names = {"Kurtop", "Kurtö", "Kurtopkha", "Kurtokha", "Zhâke"}, } m["xla"] = { } m["xlb"] = { } m["xlc"] = { } m["xld"] = { } m["xle"] = { } m["xlg"] = { } m["xli"] = { } --xln is etymology-only m["xlo"] = { } m["xlp"] = { } m["xls"] = { } m["xlu"] = { other_names = {"Cuneiform Luwian", "Hieroglyphic Luwian"}, } m["xly"] = { } m["xmb"] = { other_names = {"Mboa"}, } m["xmc"] = { } m["xmd"] = { } m["xmf"] = { other_names = {"เมเกรเลีย", "Mingrelian", "Megrelian", "Mingrel", "Megrel"}, } m["xmg"] = { } m["xmh"] = { other_names = {"Kuku-Muminh", "Wik-Muminh", "Kugu-Nganhcara", "Wik-Nganhcara", "Wikngenchera"}, } m["xmj"] = { } m["xmk"] = { } m["xml"] = { } m["xmm"] = { aliases = {"มานาโด", "Manado Malay", "Manadonese"}, } m["xmo"] = { } m["xmp"] = { } m["xmq"] = { } m["xmr"] = { other_names = {"Kushite"}, } m["xms"] = { } m["xmt"] = { } m["xmu"] = { } m["xmx"] = { } m["xmy"] = { } m["xmz"] = { } m["xna"] = { } m["xnb"] = { other_names = {"Kanakanavu"}, } m["xng"] = { aliases = {"Middle Mongolian"}, } m["xnh"] = { } m["xni"] = { } m["xnk"] = { } m["xnn"] = { aliases = {"Northern Kankanaey", "Northern Kankana-ey"}, } m["xnr"] = { } m["xns"] = { } m["xnt"] = { } m["xnu"] = { } m["xny"] = { other_names = {"Nyiyabali", "Njijabali", "Nijadali"}, } m["xoc"] = { } m["xod"] = { } m["xog"] = { other_names = {"Lusoga"}, } m["xoi"] = { } m["xok"] = { } m["xom"] = { other_names = {"Komo (Sudan)", "Madiin"}, } m["xon"] = { } m["xoo"] = { -- contrast kzw, sai-kat, sai-xoc other_names = {"Xukuru", "Xucurú", "Xucuru", "Shukurú", "Shukuru", "Shucurú", "Shucuru", "Kirirí", "Kiriri", "Kirirí-Xokó"}, } m["xop"] = { } m["xor"] = { } m["xow"] = { } m["xpa"] = { } m["xpb"] = { other_names = {"Northeastern Tasmanian", "North East Tasmanian", "Plangermaireener", "Plangamerina", "Cape Portland", "Ben Lomond", "Pipers River"}, } m["xpc"] = { } m["xpd"] = { other_names = {"Oyster Bay", "Oyster Bay Tasmanian", "Paritarami", "Big River", "Big River Tasmanian", "Lairmairrener", "Lemerina"}, } m["xpe"] = { } m["xpf"] = { other_names = {"Mainland Southeast Tasmanian", "Nuenonne", "Nyunoni"}, } m["xpg"] = { } m["xph"] = { other_names = {"North Midland Tasmanian", "North Midlands Tasmanian", "Cheranotipana"}, } m["xpi"] = { } m["xpj"] = { other_names = {"Luthigh"}, } m["xpk"] = { other_names = {"Kulina Pano"}, } m["xpl"] = { aliases = {"Port Sorell Tasmanian"}, } m["xpm"] = { } m["xpn"] = { } m["xpo"] = { } m["xpp"] = { } m["xpq"] = { } m["xpr"] = { aliases = {"Parthian"}, } m["xps"] = { } m["xpu"] = { } m["xpv"] = { other_names = {"Tommeeginnee", "Northern Tasmanian"}, } m["xpw"] = { other_names = {"Pirapa", "Northwestern Tasmanian"}, } m["xpx"] = { other_names = {"Southwestern Tasmanian"}, } m["xpy"] = { } m["xpz"] = { other_names = {"Bruny Island Tasmanian", "Nuenonne", "Nyunoni"}, } m["xqa"] = { } m["xqt"] = { } m["xra"] = { } m["xrb"] = { } m["xrd"] = { } m["xre"] = { } m["xrg"] = { } m["xri"] = { } m["xrm"] = { } m["xrn"] = { } m["xrq"] = { other_names = {"Karrangpurru"}, } m["xrr"] = { other_names = {"Rhaetic", "Rhaetian"}, } m["xrt"] = { } m["xru"] = { } m["xrw"] = { } m["xsa"] = { } m["xsb"] = { aliases = {"ซัมบาลี", "Sambali", "Sambal", "Tina Sambal", "Tina"}, } m["xsd"] = { } m["xse"] = { } m["xsh"] = { } m["xsi"] = { } m["xsj"] = { } m["xsl"] = { } m["xsm"] = { other_names = {"Kassena"}, } m["xsn"] = { other_names = {"Sanga"}, } m["xso"] = { } m["xsp"] = { } m["xsq"] = { } m["xsr"] = { aliases = {"Sherpa", "Sharpa", "Sharwa", "Xiaerba"}, } m["xss"] = { } m["xsu"] = { } m["xsv"] = { other_names = {"Sudovian", "Jatvingian"}, } m["xsy"] = { } m["xta"] = { } m["xtb"] = { } m["xtc"] = { other_names = {"Katcha-Kadugli-Miri"}, } m["xtd"] = { } m["xte"] = { } m["xth"] = { } m["xti"] = { } m["xtj"] = { } m["xtl"] = { } m["xtm"] = { } m["xtn"] = { } m["xto"] = { aliases = {"โทคาเรียนเอ", "โทแคเรียนตะวันออก", "โทคาเรียนตะวันออก", "Tocharian A", "East Tocharian", "Agnean", "Turfanian"}, } m["xtp"] = { } m["xtq"] = { aliases = {"Tumshuqese"}, } m["xtr"] = { } m["xts"] = { } m["xtt"] = { } m["xtu"] = { } m["xtv"] = { } m["xtw"] = { } m["xty"] = { } m["xua"] = { } m["xub"] = { } m["xud"] = { } m["xug"] = { aliases = {"Kunigami"}, } m["xuj"] = { } m["xul"] = { } m["xum"] = { aliases = {"อัมเบรีย", "Umbrian"}, } m["xun"] = { } m["xuo"] = { } m["xup"] = { } m["xur"] = { other_names = {"Urartean"}, } m["xut"] = { } m["xuu"] = { other_names = {"Kxoe"}, } m["xve"] = { } -- m["xvi"] = { "Kamviri", 1193495, "nur-nor", scripts = {"Arab"} } moved to etym-only code m["xvn"] = { aliases = {"แวนดัล", "Vandal"}, } m["xvo"] = { } m["xvs"] = { } m["xwa"] = { } m["xwc"] = { } m["xwd"] = { } m["xwe"] = { } m["xwg"] = { } m["xwj"] = { } m["xwk"] = { other_names = {"Wanggumara", "Karenggapa", "Punthamara"}, } m["xwl"] = { } m["xwo"] = { } m["xwr"] = { } m["xww"] = { other_names = {"Wemba Wemba", "Wamba-Wamba", "Wamba Wamba", "Wembawemba", "Baraba-Baraba", "Barababaraba", "Nari-Nari", "Wergaia", "Wotjobaluk"}, } m["xxb"] = { other_names = {"Boro (Ghana)"}, } m["xxk"] = { } m["xxm"] = { } m["xxr"] = { } m["xxt"] = { } m["xya"] = { } m["xyb"] = { } m["xyl"] = { } m["xyt"] = { } m["xyy"] = { other_names = {"Yotayota", "Yortayorta", "Bangerang", "Kailtheban", "Wollithiga", "Jotijota", "Joti-Jota", "Jodajoda"}, } m["xzh"] = { } m["xzm"] = { other_names = {"Semigallian", "Semigalian"}, } m["xzp"] = { } return m 4s5twq1re06nqy5yde58v0hbgmey9ok มอดูล:etymology/specialized 828 312470 5775201 5713621 2026-10-14T04:55:53Z Octahedron80 267 5775201 Scribunto text/plain local export = {} local m_str_utils = require("Module:string utilities") local en_utilities_module = "Module:en-utilities" local etymology_module = "Module:etymology" local gsub = m_str_utils.gsub local insert = table.insert local pluralize = require(en_utilities_module).pluralize local upper = m_str_utils.upper -- This function handles all the messiness of different types of specialized borrowings. It should insert any -- borrowing-type-specific categories into `categories` unless `nocat` is given, and return the text to display -- before the source + term (or "" for no text). local function get_specialized_borrowing_text_insert_cats(data) local bortype, categories, lang, terms, source, nocap, nocat, senseid = data.bortype, data.categories, data.lang, data.terms, data.source, data.nocap, data.nocat, data.senseid local function inscat(cat) if not nocat then local display, sourcedisp = require(etymology_module).get_display_and_cat_name(source, "raw") if cat:find("DISPLAY") then cat = cat:gsub("DISPLAY", display) elseif cat:find("SOURCE") then cat = cat:gsub("SOURCE", sourcedisp) else --cat = cat .. " " .. sourcedisp cat = cat .. sourcedisp end --insert(categories, lang:getFullName() .. " " .. cat) insert(categories, "ศัพท์" .. lang:getCategoryName() .. mw.ustring.sub(cat, 6)) end end -- `text` is the display text for the borrowing type, which gets converted -- into a link. -- `appendix` is a the glossary anchor, which defaults to `text` -- `prep` is the preposition between the borrowing type and the language -- name (e.g. "of", "from") -- `pos` is the part of speech for the borrowing type ("noun" or -- "adjective"; defaults to "noun") -- `plural` is the plural form of the borrowing type; if not specified, -- the pluralize function is used local text, appendix, prep, pos, plural if bortype == "calque" then text, prep = "แปลตรงตัว", "จาก" inscat("ศัพท์ที่แปลตรงตัวจาก") elseif bortype == "partial-calque" then text, prep = "แปลตรงตัวบางส่วน", "จาก" inscat("ศัพท์ที่แปลตรงตัวบางส่วนจาก") elseif bortype == "semantic-loan" then text, prep = "ยืมความหมาย", "จาก" inscat("ศัพท์ที่ยืมความหมายจาก") elseif bortype == "transliteration" then text, prep = "ถอดอักษร", "จาก" inscat("ศัพท์ที่ยืมมาจาก") inscat("ศัพท์ที่ถอดอักษรจาก") elseif bortype == "phono-semantic-matching" then text, prep = "เข้าคู่เสียง-ความหมาย", "จาก" inscat("ศัพท์ที่เข้าคู่เสียง-ความหมายจาก") else local langcode = lang:getCode() local lang_is_source = langcode == source:getCode() if lang_is_source then -- Track, because this shouldn't be happening. A language can only have itself as a source further up the chain after a borrowing, which is always "derived". require("Module:debug/track"){ "etymology/specialized/self-as-source", "etymology/specialized/self-as-source/" .. langcode } inscat("ศัพท์ที่ยืมกลับไปยัง") else inscat("ศัพท์ที่ยืมมาจาก") --if bortype ~= "borrowing" then -- inscat(bortype .. " borrowings from") --end end if bortype == "borrowing" then text, appendix, prep, pos = "ยืมมา", "loanword", "จาก", "คำคุณศัพท์" --[=[ elseif ( bortype == "learned" or bortype == "semi-learned" or bortype == "orthographic" or bortype == "unadapted" ) then text, prep = bortype .. " borrowing", "จาก" --]=] elseif bortype == "learned" then text, prep = "ยืมโดยเรียนรู้", "จาก" inscat("ศัพท์ที่ยืมโดยเรียนรู้จาก") elseif bortype == "semi-learned" then text, prep = "ยืมโดยกึ่งเรียนรู้", "จาก" inscat("ศัพท์ที่ยืมโดยกึ่งเรียนรู้จาก") elseif bortype == "orthographic" then text, prep = "ยืมอักขรวิธี", "จาก" inscat("ศัพท์ที่ยืมอักขรวิธีจาก") elseif bortype == "unadapted" then text, prep = "ยืมโดยไม่ดัดแปลง", "จาก" inscat("ศัพท์ที่ยืมโดยไม่ดัดแปลงจาก") elseif bortype == "adapted" then text, prep = "ยืมโดยดัดแปลง", "จาก" inscat("ศัพท์ที่ยืมโดยดัดแปลงจาก") else error("Internal error: Unrecognized bortype: " .. bortype) end end -- If the term is suppressed, the preposition should always be "from": -- "Calque of Chinese 中國". -- "Calque from Chinese" (not "Calque of Chinese"). if terms[1].term == "-" then prep = "จาก" end appendix = "ภาคผนวก:อภิธานศัพท์#" .. (appendix or text) if senseid then local senseids, output = mw.text.split(senseid, '!!'), {} for i, id in ipairs(senseids) do -- FIXME: This should be done via a function. insert(output, mw.getCurrentFrame():preprocess('{{senseno|' .. lang:getCode() .. '|' .. id .. (i == 1 and not nocap and "|uc=1" or "") .. '}}')) end local link if senseid:find('!!') then link, text = "are", pos == "คำคุณศัพท์" and text or plural or pluralize(text) else link = pos == "คำคุณศัพท์" and "is" or "is a" end text = mw.text.listToText(output) .. " " .. link .. " " .. '[[' .. appendix .. '|' .. text .. ']]' else text = "[[" .. appendix .. "|" .. (nocap and text or gsub(text, "^.", upper)) .. "]]" end return text .. prep end function export.specialized_borrowing(data) local lang, sources, terms = data.lang, data.sources, data.terms local categories = {} local text for _, source in ipairs(sources) do text = get_specialized_borrowing_text_insert_cats { bortype = data.bortype, categories = categories, lang = lang, terms = terms, source = source, nocap = data.nocap, nocat = data.nocat, senseid = data.senseid, } end for _, source in ipairs(sources) do require(etymology_module).insert_source_cat_get_display { lang = lang, source = source, raw = true, categories = categories, nocat = data.nocat, } end text = data.notext and "" or text local sourcetext = require(etymology_module).format_sources { lang = lang, sources = sources, terms = terms, sort_key = data.sort_key, nocat = true, sourceconj = data.sourceconj, } return text .. require(etymology_module).format_links(terms, data.conj, "etymology/specialized", sourcetext), categories end return export 6349bb4u4hkbqd3xlo3wptdcvtmlm9x มอดูล:es-verb 828 314624 5775176 5729353 2026-10-14T02:19:45Z Octahedron80 267 5775176 Scribunto text/plain local export = {} --[=[ Authorship: Ben Wing <benwing2> ]=] --[=[ TERMINOLOGY: -- "slot" = A particular combination of tense/mood/person/number/etc. Example slot names for verbs are "pres_1s" (present indicative first-person singular), "pres_sub_2sv" (present subjunctive second-person singular voseo form) "impf_sub_ra_3p" (imperfect subjunctive -ra form third-person plural), "imp_1p_comb_lo" (imperative first-person plural combined with clitic [[lo]]). Each slot is filled with zero or more forms. -- "form" = The conjugated Spanish form representing the value of a given slot. -- "lemma" = The dictionary form of a given Spanish term. For Spanish, always the infinitive. ]=] --[=[ FIXME: 1. Implement no_pres_stressed for aterir, garantir. (NOTE: Per RAE, garantir used in all forms in Argentina/Uruguay.) [DONE] 2. Support concluyo. [DONE] 3. Fixes for veo -> ve vs. preveo -> prevé. [DONE] 4. Various more irregular verbs, e.g. predecir, redecir, bendecir, maldecir. [DONE] 5. Raising of e -> i, o -> u before -iendo, -ió, etc. occurs only in -ir verbs. [DONE] 6. Raising of e -> i, o -> u happens before subjunctive -amos, -áis in -ir verbs. [DONE] 7. Implement reflexive verbs. [DONE] 8. Implement categories. [DONE] 9. Implement show_forms. [DONE] 10. Reconcile stems.vowel_alt from irregular verbs with vowel_alt from indicators. May require moving the irregular-verb handling code in construct_stems() into detect_indicator_spec(). [DONE] 11. Implement make_table. [DONE] 12. Vowel alternation should show u-ue (jugar), i-ie (adquirir), e-í (reír) alternations specially. [DONE] 13. Handle linking of multiword forms as is done in [[Module:es-headword]]. [DONE] 14. Implement comparison against previous module. [DONE] 15. Implement categorization of irregularities for individual tenses. 16. Support nocomb=1. [DONE] 17. (Possibly) display irregular forms in a different color, as with the old module. 18. (Possibly) display a "rule" description indicating the types of alternations. 19. Implement replace_reflexive_indicators(). [NOT DONE; SUPPORT REMOVED] 20. Implement verbs with attached clitics e.g. [[pasarlo]], [[corrérsela]]. [DONE] 21. When footnote + tú/vos notation, add a space before tú/vos. 22. Fix [[erguir]] so ie-i vowel alternation produces ye- at beginning of word, similarly for errar. Also allow multiple vowel alternation specs in irregular verbs, for errar. Finally, ie should show as e-ye for errar and as e-ye-i for erguir. [DONE] 23. Figure out why red links in combined forms show up as black not red. 24. Consider including alternative superseded forms of verbs like [[ciar]] (e.g. pret_3s = cio, ció with footnote). 25. Allow conjugation of suffixes e.g. -ir, -ecer; need to fix in [[Module:inflection utilities]]. [DONE] 26. Allow specification of stems esp. so that footnotes can be hung off them; use + for the default. 27. Don't remove monosyllabic accents when conjugating suffixes. [DONE] 28. If multiword expression with no <>, add <> after first word, as with [[Module:es-headword]]. [DONE] 29. (Possibly) link the parts of a reflexive or cliticized infinitive, as done in [[Module:es-headword]]. [DONE] 30. Final fixes to allow [[Module:es-headword]] to use this module. [DONE] --]=] local lang = require("Module:languages").getByCode("es") local m_string_utilities = require("Module:string utilities") local m_links = require("Module:links") local m_table = require("Module:table") local iut = require("Module:inflection utilities") local put = require("Module:parse utilities") local com = require("Module:es-common") local force_cat = false -- set to true for debugging local check_for_red_links = false -- set to false for debugging local rfind = mw.ustring.find local rmatch = mw.ustring.match local rsplit = mw.text.split local rsub = com.rsub local unpack = unpack or table.unpack -- Lua 5.2 compatibility local function link_term(term) return m_links.full_link({ lang = lang, term = term }, "term") end local V = com.V -- vowel regex class local AV = com.AV -- accented vowel regex class local C = com.C -- consonant regex class local tuteo_link = link_term("tuteo") local voseo_link = link_term("voseo") local fut_sub_note = "[พบได้ยาก; ปัจจุบันในภาษาทางกฎหมายเป็นหลัก]" local pres_sub_voseo_note = "[" .. voseo_link .. " ในอาร์เจนตินาและอุรุกวัย นิยมใช้รูป " .. tuteo_link .. " สำหรับมาลาสมมุติปัจจุบันกาล]" local vowel_alternants = m_table.listToSet({"ie", "ie-i", "ye", "ye-i", "ue", "ue-u", "hue", "i", "í", "ú", "+"}) local vowel_alternant_to_desc = { ["ie"] = "e-ie", ["ie-i"] = "e-ie-i", ["ye"] = "e-ye", ["ye-i"] = "e-ye-i", ["ue"] = "o-ue", ["ue-u"] = "o-ue-u", ["hue"] = "o-hue", ["i"] = "e-i", ["í"] = "i-í", ["ú"] = "u-ú", } local raise_vowel = {["e"] = "i", ["o"] = "u"} local all_persons_numbers = { ["1s"] = "1|s", ["2s"] = "2|s", ["2sv"] = "2|s|voseo", ["3s"] = "3|s", ["1p"] = "1|p", ["2p"] = "2|p", ["3p"] = "3|p", } local person_number_list_basic = {"1s", "2s", "3s", "1p", "2p", "3p"} local person_number_list_voseo = {"1s", "2s", "2sv", "3s", "1p", "2p", "3p"} local imp_person_number_list = {"2s", "2sv", "3s", "1p", "2p", "3p"} local neg_imp_person_number_list = {"2s", "3s", "1p", "2p", "3p"} person_number_to_reflexive_pronoun = { ["1s"] = "me", ["2s"] = "te", ["2sv"] = "te", ["3s"] = "se", ["1p"] = "nos", ["2p"] = "os", ["3p"] = "se", } local indicator_flags = m_table.listToSet { "no_pres_stressed", "no_pres1_and_sub", "only3s", "only3sp", "only3p", "no_built_in", } -- Initialize all the slots for which we generate forms. The particular slots may depend on whether we're generating -- combined slots (`not alternant_multiword_spec.nocomb`, which is always false if we're dealing with a verb with an -- attached clitic, such as [[hincarla]], or a reflexive or partly-reflexive verb, where a partly-reflexive verb is -- a conjoined term made up of two or more verbs, where some but not all are reflexive). It may also depend on whether -- we're being requested to generate some double-combined forms, such as [[llevándoselo]]; see the comment below for -- `verb_slot_double_combined_rows`. local function add_slots(alternant_multiword_spec) -- "Basic" slots: All slots that go into the regular table (not the combined-form table). alternant_multiword_spec.verb_slots_basic = { {"infinitive", "inf"}, {"infinitive_linked", "inf"}, {"gerund", "ger"}, {"pp_ms", "m|s|past|part"}, {"pp_fs", "f|s|past|part"}, {"pp_mp", "m|p|past|part"}, {"pp_fp", "f|p|past|part"}, } -- Slots that go into the combined-form table, along with double-combined slots (e.g. [[llevándoselo]]) that are -- requested for use with {{es-verb form of}}. alternant_multiword_spec.verb_slots_combined = {} -- Special slots used to handle non-reflexive parts of reflexive verbs in {{es-verb form of}}. -- For example, for a reflexive-only verb like [[jambarse]], we want to be able to use {{es-verb form of}} on -- [[jambe]] (which should mention that it is a part of 'me jambe', first-person singular present subjunctive, and -- 'se jambe', third-person singular present subjunctive) or on [[jambamos]] (which should mention that it is a -- part of 'nos jambamos', first-person plural present indicative or preterite). Similarly, we want to use -- {{es-verb form of}} on [[jambando]] (which should mention that it is a part of 'se ... jambando', syntactic -- variant of [[jambándose]], which is the gerund of [[jambarse]]). To do this, we need to be able to map -- non-reflexive parts like [[jambe]], [[jambamos]], [[jambando]], etc. to their reflexive equivalent(s), to the -- tag(s) of the equivalent(s), and, in the case of forms like [[jambando]], [[jambar]] and imperatives, to the -- separated syntactic variant of the verb+clitic combination. We do this by creating slots for the non-reflexive -- part equivalent of each basic reflexive slot, and for the separated syntactic-variant equivalent of each basic -- reflexive slot that is formed of verb+clitic. We use slots in this way to deal with multiword lemmas. Note that -- we run into difficulties mapping between reflexive verbs, non-reflexive part equivalents, and separated syntactic -- variants if a slot contains more than one form. To handle this, if there are the same number of forms in two -- slots we're trying to match up, we assume the forms match one-to-one; otherwise we don't match up the two slots -- (which means {{es-verb form of}} won't work in this case, but such a case is extremely rare and not worth -- worrying about). Alternatives that handle this "properly" are significantly more complicated and require -- non-trivial modifications to [[Module:inflection utilities]]. local need_special_verb_form_of_slots = alternant_multiword_spec.source_template == "es-verb form of" and alternant_multiword_spec.refl if need_special_verb_form_of_slots then alternant_multiword_spec.verb_slots_reflexive_verb_form_of = { {"infinitive_non_reflexive", "-"}, {"infinitive_variant", "-"}, {"gerund_non_reflexive", "-"}, {"gerund_variant", "-"}, } else alternant_multiword_spec.verb_slots_reflexive_verb_form_of = {} end -- For generating combined forms, i.e. combinations of a basic form (specifically, infinitive, gerund or an -- imperative form) with a clitic (or in some cases, two clitics). This is a list of lists of the form -- {BASIC_SLOT, CLITICS} where BASIC_SLOT is the slot to add the clitic pronouns to (e.g. "gerund" or "imp_2s") -- and CLITICS is a list of the clitic pronouns to add. alternant_multiword_spec.verb_slot_combined_rows = {} -- For generating double combined forms (e.g. [[llevándoselo]] or [[dámela]]). This is used by {{es-verb form of}} -- when it detects that it is being requested to find the inflection tags for a double-combined form. The number of -- double-combined forms is relatively large, so to optimize this, [[Module:es-inflections]] (which implements -- {{es-verb form of}}) detects which two clitics are involved, and we only generate double-combined forms -- involving those two clitics; this is specified using `double_combined_forms_to_include`, passed into -- do_generate_forms(). The value of this field is a list of lists of the form {SINGLE_COMB_SLOT, CLITICS} where -- SINGLE_COMB_SLOT is the single-combined slot to add the object clitic pronouns to (e.g. "gerund_comb_se" or -- "imp_2s_comb_me") and CLITICS is a list of the clitic pronouns to add. CLITICS will normally be a length-one -- list whose value is one of {"lo", "la", "le", "los", "las", "les"}. alternant_multiword_spec.verb_slot_double_combined_rows = {} -- Add entries for a slot with person/number variants. -- `verb_slots` is the table to add to. -- `slot_prefix` is the prefix of the slot, typically specifying the tense/aspect. -- `tag_suffix` is a string listing the set of inflection tags to add after the person/number tags. -- `person_number_list` is a list of the person/number slot suffixes to add to `slot_prefix`. local function add_personal_slot(verb_slots, slot_prefix, tag_suffix, person_number_list) for _, persnum in ipairs(person_number_list) do local persnum_tag = all_persons_numbers[persnum] local slot = slot_prefix .. "_" .. persnum local accel = persnum_tag .. "|" .. tag_suffix table.insert(verb_slots, {slot, accel}) end end -- Add a personal slot (i.e. a slot with person/number variants) to `verb_slots_basic`. local function add_basic_personal_slot(slot_prefix, tag_suffix, person_number_list, no_special_verb_form_of_slot, need_variant_slot) add_personal_slot(alternant_multiword_spec.verb_slots_basic, slot_prefix, tag_suffix, person_number_list) -- Add special slots for handling non-reflexive parts of reflexive verbs in {{es-verb form of}}. -- See comment above in `need_special_verb_form_of_slots`. if need_special_verb_form_of_slots and not no_special_verb_form_of_slot then for _, persnum in ipairs(person_number_list) do local persnum_tag = all_persons_numbers[persnum] local basic_slot = slot_prefix .. "_" .. persnum local accel = persnum_tag .. "|" .. tag_suffix table.insert(alternant_multiword_spec.verb_slots_reflexive_verb_form_of, {basic_slot .. "_non_reflexive", "-"}) if need_variant_slot then table.insert(alternant_multiword_spec.verb_slots_reflexive_verb_form_of, {basic_slot .. "_variant", "-"}) end end end end add_basic_personal_slot("pres", "pres|ind", person_number_list_voseo) add_basic_personal_slot("impf", "impf|ind", person_number_list_basic) add_basic_personal_slot("pret", "pret|ind", person_number_list_basic) add_basic_personal_slot("fut", "fut|ind", person_number_list_basic) add_basic_personal_slot("cond", "cond", person_number_list_basic) add_basic_personal_slot("pres_sub", "pres|sub", person_number_list_voseo) add_basic_personal_slot("impf_sub_ra", "impf|sub", person_number_list_basic) add_basic_personal_slot("impf_sub_se", "impf|sub", person_number_list_basic) add_basic_personal_slot("fut_sub", "fut|sub", person_number_list_basic) -- Need variant slots because the imperative clitics are suffixed. add_basic_personal_slot("imp", "imp", imp_person_number_list, nil, "need variant slot") -- Don't need special non-reflexive-part slots because the negative imperative is multiword, of which the -- individual words are 'no' + subjunctive. add_basic_personal_slot("neg_imp", "neg|imp", neg_imp_person_number_list, "no special verb form of") -- Don't need special non-reflexive-part slots because we don't want [[jambando]] mapping to [[jambándome]] -- (only [[jambándose]]) or [[jambar]] mapping to [[jambarme]] (only [[jambarse]]). add_basic_personal_slot("infinitive", "inf", person_number_list_basic, "no special verb form of") add_basic_personal_slot("gerund", "ger", person_number_list_basic, "no special verb form of") local third_person_object_clitics = {"lo", "la", "le", "los", "las", "les"} -- Add combined-form slots. if not alternant_multiword_spec.nocomb then -- Add a row of slots representing the combination of a basic slot with a clitic. `basic_slot` is the basic slot -- descriptor, `tag_prefix` is a string describing the inflection tags of the basic slot, and `personal_clitics` -- is a list of the personal clitics ("me", "te", "se", "nos" or "os") to add to the basic slot. local function add_combined_slot_row(basic_slot, tag_prefix, personal_clitics) -- First, add each individual combined slot to `verb_slots_combined`. local clitics_with_object = m_table.append(personal_clitics, third_person_object_clitics) for _, clitic in ipairs(clitics_with_object) do local slot = basic_slot .. "_comb_" .. clitic -- You have to pass this through full_link() to get a Spanish-specific link local accel = tag_prefix .. "|combined with [[" .. clitic .. "]]" table.insert(alternant_multiword_spec.verb_slots_combined, {slot, accel}) end -- Also, add the row to `verb_slot_combined_rows`. table.insert(alternant_multiword_spec.verb_slot_combined_rows, {basic_slot, clitics_with_object}) -- Also do double-combined forms for a specific set of clitics, if requested. See the comment above -- `verb_slot_double_combined_rows` above. if alternant_multiword_spec.double_combined_forms_to_include then -- Add a special "lxx" clitic for le/les when transformed into se in double-combined forms. local personal_clitics_with_lxx = {unpack(personal_clitics)} table.insert(personal_clitics_with_lxx, "lxx") for _, personal_clitic in ipairs(personal_clitics_with_lxx) do for _, object_clitic in ipairs(third_person_object_clitics) do for _, form_to_include in ipairs(alternant_multiword_spec.double_combined_forms_to_include) do local to_include_personal_clitic, to_include_object_clitic = unpack(form_to_include) if personal_clitic == to_include_personal_clitic and object_clitic == to_include_object_clitic then local single_comb_slot = basic_slot .. "_comb_" .. personal_clitic local slot = single_comb_slot .. "_" .. object_clitic accel_clitic = personal_clitic == "lxx" and "[[le]]/[[les]]" or "[[" .. personal_clitic .. "]]" local accel = tag_prefix .. "|combined with indirect object " .. accel_clitic .. " and direct object [[" .. object_clitic .. "]]" table.insert(alternant_multiword_spec.verb_slots_combined, {slot, accel}) table.insert(alternant_multiword_spec.verb_slot_double_combined_rows, {single_comb_slot, {object_clitic}}) break end end end end end end add_combined_slot_row("infinitive", "inf", {"me", "te", "se", "nos", "os"}) add_combined_slot_row("gerund", "gerund", {"me", "te", "se", "nos", "os"}) local function add_combined_imp_slot_row(persnum, personal_clitics) add_combined_slot_row("imp_" .. persnum, all_persons_numbers[persnum] .. "|imp", personal_clitics) end add_combined_imp_slot_row("2s", {"me", "te", "nos"}) add_combined_imp_slot_row("2sv", {"me", "te", "nos"}) add_combined_imp_slot_row("3s", {"me", "se", "nos"}) add_combined_imp_slot_row("1p", {"te", "nos", "os"}) add_combined_imp_slot_row("2p", {"me", "nos", "os"}) add_combined_imp_slot_row("3p", {"me", "se", "nos"}) end -- Generate the list of all slots. alternant_multiword_spec.all_verb_slots = {} for _, slot_and_accel in ipairs(alternant_multiword_spec.verb_slots_basic) do table.insert(alternant_multiword_spec.all_verb_slots, slot_and_accel) end for _, slot_and_accel in ipairs(alternant_multiword_spec.verb_slots_combined) do table.insert(alternant_multiword_spec.all_verb_slots, slot_and_accel) end for _, slot_and_accel in ipairs(alternant_multiword_spec.verb_slots_reflexive_verb_form_of) do table.insert(alternant_multiword_spec.all_verb_slots, slot_and_accel) end alternant_multiword_spec.verb_slots_basic_map = {} for _, slotaccel in ipairs(alternant_multiword_spec.verb_slots_basic) do local slot, accel = unpack(slotaccel) alternant_multiword_spec.verb_slots_basic_map[slot] = accel end alternant_multiword_spec.verb_slots_combined_map = {} for _, slotaccel in ipairs(alternant_multiword_spec.verb_slots_combined) do local slot, accel = unpack(slotaccel) alternant_multiword_spec.verb_slots_combined_map[slot] = accel end end -- Return true if `form` is accented and is likely monosyllabic. Used in remove_monosyllabic_accents(); if true, we -- run the syllabification algorithm to determine the syllable count, and if monosyllabic, remove the accent. Also used -- in conjunction with user-specified form overrides to add an asterisk to prevent accents from being removed. local function may_need_monosyllabic_accent_removed(form) return not rfind(form, "^%-") and rfind(form, AV) and not rfind(form, V .. C .. V) end local overridable_stems = {} -- If `add_monosyllabic_asterisk` is given, add a * to accented forms that may be monosyllabic to prevent the accent -- from being removed in remove_monosyllabic_accents(). local function allow_multiple_values_for_override(separated_groups, data, add_monosyllabic_asterisk) local retvals = {} for _, separated_group in ipairs(separated_groups) do local footnotes = data.fetch_footnotes(separated_group) local form = separated_group[1] if add_monosyllabic_asterisk and may_need_monosyllabic_accent_removed(form) then form = form .. "*" end -- Add suppress_prefix to prevent the built-in verb prefix from being added to the override. local retval = {form = form, footnotes = footnotes, suppress_prefix = true} table.insert(retvals, retval) end return retvals end local function simple_choice(choices) return function(separated_groups, data) if #separated_groups > 1 then data.parse_err("For spec '" .. data.prefix .. ":', only one value currently allowed") end if #separated_groups[1] > 1 then data.parse_err("For spec '" .. data.prefix .. ":', no footnotes currently allowed") end local choice = separated_groups[1][1] if not m_table.contains(choices, choice) then data.parse_err("For spec '" .. data.prefix .. ":', saw value '" .. choice .. "' but expected one of '" .. table.concat(choices, ",") .. "'") end return choice end end for _, overridable_stem in ipairs { "pres_unstressed", "pres_stressed", "pres1_and_sub", -- Don't include pres1; use pres_1s if you need to override just that form "impf", "full_impf", "pret", {"pret_conj", simple_choice({"irreg", "ar", "er", "ir"}) }, "fut", "cond", "pres_sub_stressed", "pres_sub_unstressed", "impf_sub_ra", "impf_sub_se", "fut_sub", "pp", } do if type(overridable_stem) == "string" then overridable_stems[overridable_stem] = allow_multiple_values_for_override else local stem, validator = unpack(overridable_stem) overridable_stems[stem] = validator end end local function match_against_verbs(ref_verb, prefixes) return function(verb) for _, prefix in ipairs(prefixes) do if verb == prefix .. ref_verb then return prefix, ref_verb end end return nil end end --[=[ Special cases for verbs: diluviar, atardecer: impersonal; all finite non-3s forms are nonexistent or hypothetical. Handle using '.only3s'. empecer, atañer, concernir: all finite non-third-person forms are nonexistent or hypothetical. Handle using '.only3sp'. desnacer: Former module claimed an irregular past participle 'desnato'. Verb is not in RAE at all and barely exists; unlikely to have irregular past participle. desposeer: Former module claimed an irregular past participle 'desposeso'. Not per RAE. valer: Former module claimed an irregular imperative 'val'. Not per RAE. manumitir: Former module claimed an irregular past participle 'manumiso'. Not per RAE. raer: Former module claimed a pres 1sg rao. Not per RAE. rehuir: Handle using +ú. sustituir: Former module claimed an irregular past participle 'sustituto'. Not per RAE. venir: Former module claimed an irregular clitic combination ven + nos -> venos. Not per native speakers. --------- Verbs to check: rehuir (+ú), prohibir (+í), reunir (+ú) --------- -ir verbs: There are several types of vowel alternations: 1. No alternation. Includes some verbs in -e- and -o-, e.g. aterir(se) (no_pres_stressed), tra(n)sgredir (pres_stressed forms rare), abolir (pres_stressed forms rare), colorir (no_pres_stressed), sumergir, divergir, convergir, arrecir (no_pres_stressed), rostir, polir (obsolete), condir (obsolete), possibly ascondir (obsolete), atordir (obsolete), sacodir (obsolete), sobrevendir (possible misspelling), empedernir (no_pres_stressed), decebir (obsolete; possibly actually like concebir, i.e. decibo not decebo), premir (obsolete), expremir (obsolete), exir (obsolete), escreuir (obsolete; fix conjugation), escrebir (obsolete), agredir; sometimes the stressed forms are rare or disused. (Also embaír, desvaír are no_pres_stressed.) 2a. ie: Infinitive has -e-, changing to -ie- when stressed. No raising before i+V. Only hendir, cernir, discernir, concernir (only3sp); discernir -> discierno, discerniendo, discernió, discernamos. 2b. ye: Infinitive has -e-, changing to -ye- when stressed. No raising before i+V. Does not occur (cf. errar). 3a. ie-i: Infinitive has -e- or -i-, changing to -ie- when stressed. Raising before i+V and 1p/2p pres subjunctive: sentir -> siento, sintiendo, sintió, sintamos. adquirir -> adquiero, adquiriendo, adquirió, adquiramos. 3b. ye-i: Infinitive has -e-, changing to -ye- when stressed. Raising before i+V and 1p/2p pres subjunctive. Only erguir: erguir -> yergo, irguiendo, irguió, irgamos. 4. i: Infinitive has -e-, changing to -i- when stressed. Raising before i+V and 1p/2p pres subjunctive: vestir -> visto, vistiendo, vistió, vistamos. Variant: ceñir -> ciño, ciñendo, ciñó, ciñamos. NOTE: preterir (no_pres_stressed). 5. ue-u: Infinitive has -o-, changing to -ue- when stressed. Raising before i+V and 1p/2p pres subjunctive: Only dormir, morir and compounds. dormir -> duermo, durmiendo, durmió, durmamos. 6. ue: This type would be parallel to 'ie' but doesn't appear to exist. --------- Verbs to fix (extra forms need to be excised or deleted): The above verbs under type (1) -ir vowel alternations; [[neviscar]] (impersonal), [[acaecer]] (third-person only), [[acontecer]] (third-person only), [[cellisquear]] (impersonal), [[pintear]] (impersonal? other meaning "to play hookey" given, not in RAE), [[diluviar]] (impersonal). [[desabrir]] (not like abrir, rare in pres_stressed/sub forms), [[jabrir]] (not like abrir). Verbs with existing errors: * [[abeldar]]: (missing <ie>) [FORMS TO DELETE] * [[acaecer]]: (used only in 3rd person) [DELETE ALL FIRST AND SECOND PERSON FORMS] * [[acontecer]]: (used only in 3rd person) [DELETE ALL FIRST AND SECOND PERSON FORMS] * [[anticuar]] (conjugated without ú) [FORMS TO DELETE] * [[antojar]]: (used only in 3rd person) [DELETE ALL FIRST AND SECOND PERSON FORMS] * [[aerografiar]]: (conjugated without í) [FORMS TO DELETE] * [[afiliar]]: (conjugated with í/+ should be only +) [FORMS TO DELETE] * [[agraviar]]: (wrongly has í) [FORMS TO DELETE] * [[arrecir]]: (not given as no_pres_stressed) [FORMS TO DELETE] * [[aserrar]]: (missing <ie>) [FORMS TO DELETE] * [[aspaventar]]: (missing <ie>) [OK] * [[atesar]]: (wrongly has ie) [FORMS TO DELETE] * [[avalentar]]: (wrongly has ie) [FORMS TO DELETE] * [[autorregularse]] (conjugated as non-reflexive) [OK] * [[auxiliar]]: (conjugated with í/+ should be only +) [FORMS TO DELETE] * [[balbucir]] (pres1_and_sub nonexistent) [DELETE PRES1_AND_SUB FORMS] * [[caçar]] (conjugated as -zar) [OK] * [[calefacer]] (extra form caleface) [FIX] * [[cellisquear]]: (used only in 3rd person singular) [DELETE ALL FIRST AND SECOND PERSON FORMS, ALL 3RD PLURAL FORMS AND ALL PP NON-MS FORMS] * [[chilenizar]] (conjugated with no cons alternation) [OK] * [[colorir]]: (not given as no_pres_stressed) [FORMS TO DELETE] * [[comisariar]] (conjugated without í) [FORMS TO DELETE] * [[complacer]] (missing complega in imperative 3s) [OK] * [[comprehender]] (conjugated as comprender) [OK] * [[decebir]] (was conjugated without <i>) [FORMS TO DELETE] * [[desafiliar]]: (conjugated with í/+ should be only +) [FORMS TO DELETE] * [[desagregar]] (conjugated with no cons alternation) [FORMS TO DELETE] * [[desairar]]: (conjugated with í should be only +) [FORMS TO DELETE] * [[descordar]]: (missing <ue>) [FORMS TO DELETE] * [[deseleccionar]] (conjugated as desseleccionar) [OK] * [[desestacionalizar]] (conjugated with no cons alternation) [FORMS TO DELETE] * [[desgonzar]] (conjugated with no cons alternation) [FORMS TO DELETE] * [[desguinzar]] (conjugated with no cons alternation) [FORMS TO DELETE] * [[deshacer]] (extra form deshace, desháceme etc.) [OK] * [[desnacer]] (extra form desnato etc.) [DELETE PP FORMS] * [[desposeer]] (extra form desposeso etc.) [DELETE PP FORMS] * [[dezmar]]: (wrongly has ie) [FORMS TO DELETE] * [[diluviar]]: (used only in 3rd person singular) [DELETE ALL FIRST AND SECOND PERSON FORMS, ALL 3RD PLURAL FORMS AND ALL PP NON-MS FORMS] * [[draftear]] (extraneous param lang=es) [REMOVE PARAM] * [[ejemplarizar]] (extraneous param compound=1) [compound -> combined] * [[ejercitar]] (extraneous param compound=1) [compound -> combined] * [[empecer]] (empezca has imperatives, 1s in its verb form entry) [FIX] * [[encentar]]: (wrongly has ie) [OK] * [[encubertar]]: (wrongly has ie) [FORMS TO DELETE] * [[entrechocar]] (conjugated with no cons alternation) [FORMS TO DELETE] * [[entredecir]] (imp_2s has entredice should be entredí; extra future/cond forms entredeciré etc.) [FORMS TO DELETE/FIX] * [[escenografiar]]: (conjugated without í) [FORMS TO DELETE] * [[estar]] (incorrect combined forms, e.g. éstela instead of estela) [OK] * [[extasiar]]: (conjugated with í/+ should be only í) [FORMS TO DELETE] * [[facer]] (extra form face, fáceme, etc.) [FIX] * [[ferrar]]: (missing <ie>) [FORMS TO DELETE] * [[gloriar]]: (conjugated with í/+ should be only í) [FORMS TO DELETE] * [[hacer]] (extra form hace, háceme, etc.) [DELETE hácete] * [[homogeneizar]] (extra form homogeneízo) [DELETE homogeneízo, homogeneízas, homogeneíza, homogeneízan, homogeneícen, homogeneícemos] * [[incensar]]: (missing <ie>) [FORMS TO DELETE] * [[ir]] (extraneous param aux=ser) [REMOVE PARAM] * [[jacer]] (extra form jace, jáceme, etc.) [FIX] * [[jarrear]] (extraneous param impersonal=yes) * [[litografiar]]: (conjugated without í) [FORMS TO DELETE] * [[manumitir]] (extra form manumiso, etc.) [FIX; is an adjective] * [[mecanografiar]]: (conjugated without í) [FORMS TO DELETE] * [[mordiscar]] (conjugated with no cons alternation) [FORMS TO DELETE] * [[neviscar]]: (used only in 3rd person singular) [DELETE ALL FIRST AND SECOND PERSON FORMS, ALL 3RD PLURAL FORMS AND ALL PP NON-MS FORMS] * [[obsipar]] (conjugated as obispar) [DELETE VERB] * [[obstar]]: (used only in 3rd person) [DELETE ALL FIRST AND SECOND PERSON FORMS] * [[orificar]] (conjugated as orificiar) [OK] * [[complacer]], [[placer]] (missing plega in imperative 3s, etc.) [OK] * [[prevaler]] (extra form preval, incorrect form prévalme etc.) [DELETE preval] * [[preterir]]: (not given as no_pres_stressed) [FORMS TO DELETE] * [[raer]] (extra form rao) [DELETE rao] * [[rebordar]] (extraneous param lang=es) [REMOVE PARAM] * [[redecir]] (incorrect imp_2s redice instead of redí) [FIX] * [[reinstitucionalizar]] (conjugated with no cons alternation) [FORMS TO DELETE] * [[reirse]] [DELETE] * [[serigrafiar]]: (conjugated without í) [FORMS TO DELETE] * [[sobresalir]] (incorrect combined forms e.g. sobrésalme) [OK] * [[superpoblar]] (missing <ue>) [FORMS TO DELETE] * [[sustituir]] (extra form sustituto, etc.) [OK] * [[usucapir]]: (used only in inf and pp) [DELETE ALL FORMS BUT PP] * [[valefacer]] (extra form valeface) [FIX] * [[valer]] (extra form val, etc.) [OK] * LOTS OF COMBINED FORMS OF VERBS IN -iar, -uar, -ai-, -au-, -ei-, -eu-, etc. Example: afeitar (afeítate, afeítese, afeítense). Need a script to find them. * [[acostar]]: normally <ue> but in meaning "arrive at the coast", <> * [[adecuar]]: <+,ú> * [[aerografiar]]: <í> * [[aferrar]]: <+,ie[obsolete]> * [[afiliar]]: <> * [[aforar]]: in meaning "to gauge, to measure": <>; in meaning "otorgar fuero": <ue> * [[arrecir]]: <no_pres_stressed> * [[agraviar]]: <> * [[agriar]]: <í,+> * [[aserrar]]: <ie> * [[asolar]]: in meaning "destroy": <ue,+>; in meaning "to dry up": <> * [[aspaventar]]: <ie> * [[atentar]]: in meaning "to commit a crime" does not have vowel alt * [[atesar]]: <>; possibly <ie> in obsolete meaning "atiesar" * [[atestar]]: in meaning "to pack": <ie,+>; in meaning "testify": <> * [[autoevacuarse]]: <+,ú> * [[auxiliar]]: <> * [[avalentar]]: <> * [[cimentar]]: <ie,+> * [[colar]]: most meanings <ue> but "canonically confer (an ecclesiastical benefit)" <> * [[colorir]]: <no_pres_stressed> * [[comisariar]]: <í> * [[desafiliar]]: <> * [[desaforar]]: "to deprive of fuero": <ue,+[less common]> * [[desairar]]: <> * [[desolar]]: <ue,+> * [[desmembrar]]: <ie,+> * [[dezmar]]: <> * [[ejecutoriar]]: <í,+> * [[emparentar]]: <ie,+> * [[encentar]]: <> * [[encubertar]]: <> * [[engrosar]]: <ue,+> * [[escenografiar]]: <í> * [[evacuar]]: <+,ú> * [[expatriar]]: <í,+> * [[extasiar]]: <í> * [[ferrar]]: <ie> * [[follar]]: <ue> * [[gloriar]]: <í> * [[hibernar]]: no vowel alt or e-ie; e-ie not in RAE, ask about it * [[historiar]]: <í,+> * [[incensar]]: <ie> * [[invernar]]: <ie,+> * [[licuar]]: <+,ú> * [[litografiar]]: <í> * [[mecanografiar]]: <í> * [[paliar]]: <í,+> * [[preterir]]: <i.no_pres_stressed> * [[promiscuar]]: <+,ú> * [[readecuar]]: <+,ú> * [[repatriar]]: <í,+> * [[retrocar]]: <ue,+> * [[serigrafiar]]: <í> * [[soterrar]]: <ie,+> * [[superpoblar]]: <ue> * [[templar]]: <+,ie[in some parts of Latin America]> * [[vidriar]]: <í,+> Second round of verbs to fix: * [[apropriar]]: forms point to reflexive [FORMS TO FIX] * [[autogobernarse]]: (missing <ie>) [FORMS TO DELETE] * [[autorreproducirse]]: (wrongly had regular preterite and impf/fut sub) [FORMS TO DELETE] * [[aventarse]]: (missing <ie>) [FORMS TO DELETE] * [[aventar]]: forms point to reflexive [FORMS TO FIX] * [[culiar]]: yo culio or culío? [VERIFY] * [[desnacer]]: delete [[dasnatos]] * [[poseer]]: delete [[posesa]], [[posesos]], [[posesas]] * [[reproducir]]: (wrongly had regular preterite and impf/fut sub) [FORMS TO DELETE] * [[trasgredir]]: (wrongly had <i>) [FORMS TO DELETE] --------- Irregular conjugations. Each entry is processed in turn and consists of an object with two fields: 1. match=: Specifies the irregular verbs that match this object. 2. forms=: Specifies the irregular stems and forms for these verbs. The value of match= is either a string beginning with "^" (match only the specified verb), a string not beginning with "^" (match any verb ending in that string), or a function that is passed in the verb and should return the prefix of the verb if it matches, otherwise nil. The function match_against_verbs() is provided to facilitate matching a set of verbs with a common ending and specific prefixes (e.g. [[andar]] and [[desandar]] but not [[mandar]], etc.). The value of forms= is a table specifying stems and individual override forms. Each key of the table names either a stem (e.g. `pres_stressed`), a stem property (e.g. `vowel_alt`) or an individual override form (e.g. `pres_1s`). Each value of a stem can either be a string (a single stem), a list of strings, or a list of objects of the form {form = STEM, footnotes = {FOONOTES}}. Each value of an individual override should be of exactly the same form except that the strings specify full forms rather than stems. The values of a stem property depend on the specific property but are generally strings or booleans. In order to understand how the stem specifications work, it's important to understand the phonetic modifications done by combine_stem_ending(). In general, the complexities of predictable stem and ending modifications are all handled in this function. In particular: 1. Spelling-based modifications (c/z, g/gu, gu/gü, g/j) occur automatically as appropriate for the ending. 2. Raising of e -> i, o -> u in -ir verbs before an ending beginning with i + vowel, as well as in the 1p/2p forms of the present subjunctive (dormir -> durmiendo, durmió, durmamos), are handled here. Raising happens only for -ir verbs and only when the stem setting `raising_conj` is true (which is normally set to true when vowel alternations `ie-i`, `ye-i`, `ue-u`, `i`, `í` or `ú` are specified). 3. Numerous modifications are automatically made before an ending beginning with i + vowel. These include: a. final -i of stem absorbed: sonreír -> sonrió, sonriera, sonriendo; b. in the preterite of irregular verbs (likewise for other tenses derived from the preterite stem, i.e. imperfect and and future subjunctive), initial i absorbed after j and u (dijeron not #dijieron, likewise for condujeron, trajeron; also fueron not #fuyeron). This happens only when stem setting `pret_conj` == "irreg"; this must be set explicitly by irregular verbs. Does not apply everywhere because of cases like regular [[tejer]] (tejieron not #tejeron), regular [[concluir]] (concluyeron not #conclueron). c. initial i of ending -> y after vowel and word-initially: poseer -> poseyó, poseyera, poseyendo; ir -> yendo; d. initial i of ending -> y after gü, which becomes gu: argüir -> arguyó, arguyera, arguyendo; e. initial i of ending absorbed after ñ, ll, y: tañer -> tañó, tañera, tañendo; bullir -> bulló, bullera, bullendo 4. If the ending begins with (h)i, it gets an accent after a/e/i/o to prevent the two merging into a diphthong: caer -> caíste, caímos; reír -> reíste, reímos (pres and pret). This does not apply after u, e.g. concluir -> concluiste, concluimos. 5. In -uir verbs (i.e. -ir verbs with stem ending in -u), a y is added before endings beginning with a/e/o: concluir -> concluyo, concluyen, concluya, concluyamos. Note that preterite concluyó, gerund concluyendo, etc. are handled by a different rule above (3b). The following stems are recognized: -- pres_unstressed: The present indicative unstressed stem (2s voseo, 1p, 2p). Also controls the imperative 2p and gerund. Defaults to the infinitive stem. -- pres_stressed: The present indicative stressed stem (1s, 2s, 3s, 3p). Also controls the imperative 2s. Default is empty if indicator `no_pres_stressed`, else a vowel alternation if such an indicator is given (e.g. `ue`, `ì`), else the infinitive stem. -- pres1_and_sub: Overriding stem for 1s present indicative and the entire subjunctive. Only set by irregular verbs and by the indicator `no_pres_stressed` (since verbs of this sort, e.g. [[aterir]], are missing the entire subjunctive as well as the forms with stressed root). Used by many irregular verbs, e.g. [[caer]], [[roer]], [[salir]], [[tener]], [[valer]], [[venir]], etc. Some verbs set this and then supply an override for the pres_1sg if it's irregular, e.g. [[saber]], with irregular subjunctive stem "sep-" and special 1s present indicative "sé*" (the * indicates that the monosyllabic accent should not be removed). -- pres1: Special stem for 1s present indicative. Normally, do not set this explicitly. If you need to specify an irregular 1s present indicative, use the form override pres_1s= to specify the entire form. Defaults to pres1_and_sub if given, else pres_stressed. -- pres_sub_unstressed: The present subjunctive unstressed stem (1p, 2p, also 2s voseo for -ar verbs). Defaults to pres1_and_sub if given, else the infinitive stem. -- pres_sub_stressed: The present subjunctive stressed stem (1s, 2s, 3s, 1p, also 2s voseo for -er/-ir verbs). Defaults to pres1. -- impf: The imperfect stem. Defaults to the infinitive stem. -- pret: The preterite stem. Defaults to the infinitive stem. -- pret_conj: Determines the set of endings used in the preterite. Should be one of "ar", "er", "ir" or "irreg". Defaults to the conjugation as determined from the infinitive. -- fut: The future stem. Defaults to the infinitive stem. -- cond: The conditional stem. Defaults to fut. -- impf_sub_ra: The imperfect subjunctive -ra stem. Defaults to the preterite stem. -- impf_sub_se: The imperfect subjunctive -se stem. Defaults to the preterite stem. -- fut_sub: The future subjunctive stem. Defaults to the preterite stem. -- pp: The past participle stem. Default is based on the verb conjugation: infinitive stem + "ad" for -ar verbs, otherwise infinitive stem + "id". ]=] local built_in_conjugations = { { -- andar, desandar -- we don't want to match e.g. mandar. match = match_against_verbs("andar", {"", "des"}), forms = {pret = "anduv", pret_conj = "irreg"} }, { -- asir, desasir match = "asir", -- use 'asgu' because we're in a front environment; if we use 'asg', we'll get '#asjo' forms = {pres1_and_sub = "asgu"} }, { -- abrir, cubrir and compounds match = function(verb) local prefix, base_verb = rmatch(verb, "^(.*)(brir)$") -- Only match abrir, cubrir and compounds, and don't match desabrir/jabrir if not prefix then return nil elseif not prefix:find("a$") and not prefix:find("cu$") then return nil elseif prefix == "desa" or prefix == "ja" then return nil else return prefix, base_verb end end, forms = {pp = "biert"} }, { match = "caber", forms = {pres1_and_sub = "quep", pret = "cup", pret_conj = "irreg", fut = "cabr"} }, { -- caer, decaer, descaer, recaer match = "caer", -- use 'caigu' because we're in a front environment; if we use 'caig', we'll get '#caijo' forms = {pres1_and_sub = "caigu"} }, { -- cocer, escocer, precocer, etc. match = "cocer", -- override cons_alt, otherwise the verb would be categorized as a c-zc alternating verb forms = {vowel_alt = "ue", pres1 = "cuez", pres_sub_unstressed = "coz", cons_alt = "c-z"}, -- not cozco, as would normally be generated }, { -- dar, desdar match = match_against_verbs("dar", {"", "des"}), forms = { -- we need to override various present indicative forms and add an accent for the compounds; -- not needed for the simplex and in fact the accents will be removed in that case pres_1s = "doy", pres_2s = "dás", pres_3s = "dá", pres_2p = "dáis", pres_3p = "dán", pret = "d", pret_conj = "er", pres_sub_1s = "dé*", -- * signals that the monosyllabic accent must remain pres_sub_2s = "dés", pres_sub_3s = "dé*", pres_sub_2p = "déis", pres_sub_3p = "dén", imp_2s = "dá", } }, { -- decir, redecir, entredecir match = match_against_verbs("decir", {"", "re", "entre"}), forms = { -- for this and variant verbs in -decir, we set cons_alt to false because we don't want the -- verb categorized as a c-zc alternating verb, which would happen by default -- use 'digu' because we're in a front environment; if we use 'dig', we'll get '#dijo' pres1_and_sub = "digu", vowel_alt = "i", cons_alt = false, pret = "dij", pret_conj = "irreg", pp = "dich", fut = "dir", imp_2s = "dí" -- need the accent for the compounds; it will be removed in the simplex } }, { -- antedecir, interdecir match = match_against_verbs("decir", {"ante", "inter"}), forms = { pres1_and_sub = "digu", vowel_alt = "i", cons_alt = false, pret = "dij", pret_conj = "irreg", pp = "dich", fut = "dir" -- imp_2s regular } }, { -- bendecir, maldecir match = match_against_verbs("decir", {"ben", "mal"}), forms = { pres1_and_sub = "digu", vowel_alt = "i", cons_alt = false, pret = "dij", pret_conj = "irreg", pp = {"decid", "dit"} -- imp_2s regular, fut regular } }, { -- condecir, contradecir, desdecir, predecir, others? match = "decir", forms = { pres1_and_sub = "digu", vowel_alt = "i", cons_alt = false, pret = "dij", pret_conj = "irreg", pp = "dich", fut = {"decir", "dir"} -- imp_2s regular } }, { match = "^desvaír", forms = { pres_2s = "desvaes", pres_3s = "desvae", pres_3p = "desvaen", imp_2s = "desvae" } }, { -- conducir, producir, reducir, traducir, etc. match = "ducir", forms = {pret = "duj", pret_conj = "irreg"} }, { -- elegir, reelegir; not preelegir, per RAE match = match_against_verbs("elegir", {"", "re"}), forms = {vowel_alt = "i", pp = {"elegid", "elect"}} }, { -- erguir match = "^erguir", forms = { vowel_alt = {"i", "ye-i"}, -- We would not need to make this verb irregular except for the alternative unstressed present subjunctive forms -- in yerg-. We can't use pres_sub_unstressed = {"irgu", "yergu"} because the "i" and "ye-i" vowel alternations -- are e->i raising in the unstressed present subjunctive and we wrongly get irgamos/yirgamos etc. pres_sub_2sv = {"irgás", "yergás"}, pres_sub_1p = {"irgamos", "yergamos"}, pres_sub_2p = {"irgáis", "yergáis"}, }, }, { match = "^estar", forms = { pres_1s = "estoy", pres_2s = "estás", pres_2sv = "estás", pres_3s = "está", pres_3p = "están", pret = "estuv", pret_conj = "irreg", pres_sub_1s = "esté", pres_sub_2s = "estés", pres_sub_2sv = "estés", pres_sub_3s = "esté", pres_sub_3p = "estén", imp_2s = "está", imp_2sv = "está", } }, { -- freír, refreír match = "freír", forms = {vowel_alt = "í", pp = {"freíd", "frit"}} }, { match = "garantir", forms = { pres_stressed = {{form = "garant", footnotes = {"[only used in Argentina and Uruguay]"}}}, pres1_and_sub = {{form = "garant", footnotes = {"[only used in Argentina and Uruguay]"}}}, } }, { match = "^haber", forms = { pres_1s = "he", pres_2s = "has", pres_2sv = "has", pres_3s = {"ha", {form = "hay", footnotes = {"[used impersonally]"}}}, pres_1p = "hemos", pres_3p = "han", pres1_and_sub = "hay", -- only for subjunctive as we override pres_1s pret = "hub", pret_conj = "irreg", imp_2s = {"habe", "he"}, imp_2sv = {"habe", "he"}, fut = "habr", } }, { match = "satisfacer", forms = { -- see below for cons_alt setting and pres1_and_sub setting pres1_and_sub = "satisfagu", cons_alt = false, pret = "satisfic", pret_conj = "irreg", pp = "satisfech", fut = "satisfar", imp_2s = {"satisface", "satisfaz"} } }, { match = match_against_verbs("hacer", {"contra", "re"}), -- contrahacer/rehacer require an extra accent in the preterite (rehíce, rehízo). forms = { -- see below for cons_alt setting and pres1_and_sub setting pres1_and_sub = "hagu", cons_alt = false, pret = "hic", pret_1s = "híce", pret_3s = "hízo", pret_conj = "irreg", pp = "hech", fut = "har", imp_2s = "haz" } }, { -- hacer, deshacer, contrahacer, rehacer, facer, desfacer, jacer match = function(verb) return rmatch(verb, "^(.*[hjf])(acer)$") end, forms = { -- for these verbs, we set cons_alt to false because we don't want the verb categorized as a -- c-zc alternating verb, which would happen by default -- use 'agu' because we're in a front environment; if we use 'ag', we'll get '#hajo' pres1_and_sub = "agu", cons_alt = false, pret = "ic", pret_conj = "irreg", pp = "ech", fut = "ar", imp_2s = "az" } }, { -- imprimir, reimprimir match = "imprimir", forms = {pp = {"imprimid", "impres"}} }, { -- infecir match = "infecir", -- override cons_alt, otherwise the verb would be categorized as a c-zc alternating verb forms = {vowel_alt = "i", pres1 = "infiz", pres_sub_unstressed = "infez", cons_alt = "c-z"}, -- not infizco, as would normally be generated }, { match = "infecir", -- override cons_alt, otherwise the verb would be categorized as a c-zc alternating verb forms = {pres1_and_sub = "infez", cons_alt = "c-z"}, -- not mezco, as would normally be generated }, { match = "^ir", forms = { pres_1s = "voy", pres_2s = "vas", pres_2sv = "vas", pres_3s = "va", pres_1p = "vamos", pres_2p = "vais", pres_3p = "van", pres1_and_sub = "vay", -- only for subjunctive as we override pres_1s full_impf = "ib", impf_1p = "íbamos", pret = "fu", pret_conj = "irreg", -- this signals that fu + -ieron -> fueron not fuyeron pret_1s = "fui", pret_3s = "fue", imp_2s = "ve", imp_2sv = "andá", imp_1p = {"vamos", "vayamos"}, refl_imp_2p = {"idos", "iros"}, imp_2p_comb_os = {"idos", "iros"}, } }, { -- mecer, remecer -- we don't want to match e.g. adormecer, estremecer match = match_against_verbs("mecer", {"re", ""}), -- override cons_alt, otherwise the verb would be categorized as a c-zc alternating verb forms = {pres1_and_sub = "mez", cons_alt = "c-z"}, -- not mezco, as would normally be generated }, { -- morir, desmorir, premorir match = "morir", forms = {vowel_alt = "ue-u", pp = "muert"}, }, { -- oír, desoír, entreoír, trasoír match = "oír", -- use 'oigu' because we're in a front environment; if we use 'oig', we'll get '#oijo' forms = {pres1_and_sub = "oigu"} }, { match = "olver", -- solver, volver, bolver and derivatives forms = {vowel_alt = "ue", pp = "uelt"} }, { match = "pagar", forms = {pp = {"pagad", {form = "pag", footnotes = {"[Uruguay]"}}}} }, { -- placer, desplacer match = "placer", forms = { pret_3s = {"plació", {form = "plugo", footnotes = {"[archaic]"}}}, pret_3p = {"placieron", {form = "pluguieron", footnotes = {"[archaic]"}}}, pres_sub_3s = {"plazca", {form = "plega", footnotes = {"[archaic]"}}, {form = "plegue", footnotes = {"[archaic]"}}}, impf_sub_ra_3s = {"placiera", {form = "pluguiera", footnotes = {"[archaic]"}}}, impf_sub_ra_3p = {"placieran", {form = "pluguieran", footnotes = {"[archaic]"}}}, impf_sub_se_3s = {"placiese", {form = "pluguiese", footnotes = {"[archaic]"}}}, impf_sub_se_3p = {"placiesen", {form = "pluguiesen", footnotes = {"[archaic]"}}}, fut_sub_3s = {"placiere", {form = "pluguiere", footnotes = {"[archaic]"}}}, fut_sub_3p = {"placieren", {form = "pluguieren", footnotes = {"[archaic]"}}}, } }, { match = "poder", forms = {vowel_alt = "ue", pret = "pud", pret_conj = "irreg", fut = "podr", gerund = "pudiendo"} }, { -- poner, componer, deponer, imponer, oponer, suponer, many others match = "poner", forms = { -- use 'pongu' because we're in a front environment; if we use 'pong', we'll get '#ponjo' pres1_and_sub = "pongu", pret = "pus", pret_conj = "irreg", fut = "pondr", pp = "puest", imp_2s = "pón" -- need the accent for the compounds; it will be removed in the simplex } }, { -- proveer, desproveer match = "proveer", forms = {pp = {"provist", "proveíd"}}, }, { match = "pudrir", forms = {pp = "podrid"} }, { -- querer, desquerer, malquerer match = "querer", forms = {vowel_alt = "ie", pret = "quis", pret_conj = "irreg", fut = "querr"} }, { match = "^raer", -- use 'raigu' because we're in a front environment; if we use 'raig', we'll get '#raijo' forms = {pres1_and_sub = {"raigu", "ray"}} }, { -- roer, corroer match = "roer", -- use 'roigu' because we're in a front environment; if we use 'roig', we'll get '#roijo' forms = {pres1_and_sub = {"ro", "roigu", "roy"}} }, { -- romper, entrerromper, arromper, derromper; not corromper; FIXME: not sure about interromper (obsolete) match = function(verb) local prefix, base_verb = rmatch(verb, "^(.*)(romper)$") -- Don't match corromper if prefix == "cor" then return nil else return prefix, base_verb end end, forms = {pp = "rot"} }, { -- saber, resaber match = "saber", forms = { pres_1s = "sé*", -- * signals that the monosyllabic accent must remain pres1_and_sub = "sep", -- only for subjunctive as we override pres_1s pret = "sup", pret_conj = "irreg", fut = "sabr", } }, { match = "salir", forms = { -- use 'salgu' because we're in a front environment; if we use 'salg', we'll get '#saljo' pres1_and_sub = "salgu", fut = "saldr", imp_2s = "sal", -- These don't exist per the RAE. imp_2s_comb_lo = {}, imp_2s_comb_los = {}, imp_2s_comb_la = {}, imp_2s_comb_las = {}, imp_2s_comb_le = {}, imp_2s_comb_les = {}, }, }, { match = "scribir", -- escribir, describir, proscribir, etc. forms = {pp = {"scrit", {form = "script", footnotes = {"[Argentina and Uruguay]"}}}} }, { match = "^ser", forms = { pres_1s = "soy", pres_2s = "eres", pres_2sv = "sos", pres_3s = "es", pres_1p = "somos", pres_2p = "sois", pres_3p = "son", pres1_and_sub = "se", -- only for subjunctive as we override pres_1s full_impf = "er", impf_1p = "éramos", pret = "fu", pret_conj = "irreg", -- this signals that fu + -ieron -> fueron not fuyeron pret_1s = "fui", pret_3s = "fue", fut = "ser", imp_2s = "sé*", -- * signals that the monosyllabic accent must remain imp_2sv = "sé*", } }, { match = "^soler", forms = { vowel_alt = "ue", fut = {{form = "soler", footnotes = {"[rare but acceptable]"}}}, fut_sub = {{form = "sol", footnotes = {"[rare but acceptable]"}}}, pp = {{form = "solid", footnotes = {"[rare but acceptable]"}}}, } }, { -- tener, abstener, contener, detener, obtener, sostener, and many others match = "tener", forms = { -- use 'tengu' because we're in a front environment; if we use 'teng', we'll get '#tenjo' pres1_and_sub = "tengu", vowel_alt = "ie", pret = "tuv", pret_conj = "irreg", fut = "tendr", imp_2s = "tén" -- need the accent for the compounds; it will be removed in the simplex } }, { -- traer, atraer, detraer, distraer, extraer, sustraer, and many others match = "traer", -- use 'traigu' because we're in a front environment; if we use 'traig', we'll get '#traijo' forms = {pres1_and_sub = "traigu", pret = "traj", pret_conj = "irreg"} }, { -- valer, equivaler, prevaler match = "valer", -- use 'valgu' because we're in a front environment; if we use 'valg', we'll get '#valjo' forms = {pres1_and_sub = "valgu", fut = "valdr"} }, { match = "venir", forms = { -- use 'vengu' because we're in a front environment; if we use 'veng', we'll get '#venjo' pres1_and_sub = "vengu", vowel_alt = "ie-i", pret = "vin", pret_conj = "irreg", -- uniquely for this verb, pres sub 2sv/1p/2p do not raise the vowel even though we are an -- e-ie-i verb (contrast sentir -> sintamos/sintáis) pres_sub_2sv = "vengás", pres_sub_1p = "vengamos", pres_sub_2p = "vengáis", fut = "vendr", imp_2s = "vén" -- need the accent for the compounds; it will be removed in the simplex } }, { -- We want to match antever etc. but not atrever etc. No way to avoid listing each verb. match = match_against_verbs("ver", {"ante", "entre", "pre", "re", "tras", ""}), forms = { -- we need to override various present indicative forms and add an accent for the compounds; -- not needed for the simplex and in fact the accents will be removed in that case pres_2s = "vés", pres_2sv = "vés", pres_3s = "vé", pres_2p = "véis", pres_3p = "vén", pres1_and_sub = "ve", impf = "ve", pp = "vist", imp_2s = "vé" -- need the accent for the compounds; it will be removed in the simplex } }, { -- yacer, adyacer, subyacer match = "yacer", -- use 'yazqu/yazgu/yagu' because we're in a front environment; see 'decir' above forms = {pres1_and_sub = {"yazqu", "yazgu", "yagu"}, imp_2s = {"yace", "yaz"}} }, } local reflexive_masc_forms = { ["su"] = {"mi", "tu", "su", "nuestro", "vuestro", "su"}, ["sus"] = {"mis", "tus", "sus", "nuestros", "vuestros", "sus"}, ["sí"] = {"mí", "ti", "sí", "nosotros", "vosotros", "sí"}, ["consigo"] = {"conmigo", "contigo", "consigo", "con nosotros", "con vosotros", "consigo"}, } local reflexive_fem_forms = { ["su"] = {"mi", "tu", "su", "nuestra", "vuestra", "su"}, ["sus"] = {"mis", "tus", "sus", "nuestras", "vuestras", "sus"}, ["sí"] = {"mí", "ti", "sí", "nosotras", "vosotras", "sí"}, ["consigo"] = {"conmigo", "contigo", "consigo", "con nosotras", "con vosotras", "consigo"}, } local reflexive_forms = { ["se"] = {"me", "te", "se", "nos", "os", "se"}, ["suyo"] = {"mío", "tuyo", "suyo", "nuestro", "vuestro", "suyo"}, ["suya"] = {"mía", "tuya", "suya", "nuestra", "vuestra", "suya"}, ["suyos"] = {"míos", "tuyos", "suyos", "nuestros", "vuestros", "suyos"}, ["suyas"] = {"mías", "tuyas", "suyas", "nuestras", "vuestras", "suyas"}, } local function skip_slot(base, slot, allow_overrides) if not allow_overrides and (base.basic_overrides[slot] or base.combined_overrides[slot] or base.refl and base.basic_reflexive_only_overrides[slot]) then -- Skip any slots for which there are overrides. return true end if base.only3s and (slot:find("^pp_f") or slot:find("^pp_mp")) then -- diluviar, atardecer, neviscar; impersonal verbs have only masc sing pp return true end if not slot:find("[123]") then -- Don't skip non-personal slots. return false end if base.nofinite then return true end if (base.only3s or base.only3sp or base.only3p) and (slot:find("^imp_") or slot:find("^neg_imp_")) then return true end if base.only3s and not slot:find("3s") then -- diluviar, atardecer, neviscar return true end if base.only3sp and not slot:find("3[sp]") then -- atañer, concernir return true end if base.only3p and not slot:find("3p") then -- [[caer cuatro gotas]], [[caer chuzos de punta]], [[entrarle los siete males]] return true end return false end -- Add the `stem` to the `ending` for the given `slot` and apply any phonetic modifications. -- `is_combining_ending` is true if `ending` is actually the ending (this function is also -- called to combine prefix + stem). WARNING: This function is written very carefully; changes -- to it can easily have unintended consequences. local function combine_stem_ending(base, slot, stem, ending, is_combining_ending) if not is_combining_ending then return stem .. ending end if base.stems.raising_conj and (rfind(ending, "^i" .. V) or slot == "pres_sub_1p" or slot == "pres_sub_2p" or slot == "pres_sub_2sv") then -- need to raise e -> i, o -> u: dormir -> durmió, durmiera, durmiendo, durmamos stem = rsub(stem, "([eo])(" .. C .. "*)$", function(vowel, rest) return raise_vowel[vowel] .. rest end) -- also with stem ending in -gu or -qu (e.g. erguir -> irguió, irguiera, irguiendo, irgamos) stem = rsub(stem, "([eo])(" .. C .. "*[gq]u)$", function(vowel, rest) return raise_vowel[vowel] .. rest end) end -- Lots of sound changes involving endings beginning with i + vowel if rfind(ending, "^i" .. V) then -- (1) final -i of stem absorbed: sonreír -> sonrió, sonriera, sonriendo; note that this rule may be fed -- by the preceding one (stem sonre- raised to sonri-, then final i absorbed) stem = stem:gsub("i$", "") -- (2) In the preterite of irregular verbs (likewise for other tenses derived from the preterite stem, i.e. -- imperfect and future subjunctive), initial i absorbed after j (dijeron not #dijieron, likewise for -- condujeron, trajeron) and u (fueron not #fuyeron). Does not apply in regular verb tejer (tejieron not -- #tejeron) and concluir (concluyeron not #conclueron). if base.stems.pret_conj == "irreg" and rfind(stem, "[ju]$") then ending = ending:gsub("^i", "") end -- (3) initial i -> y after vowel and word-initially: poseer -> poseyó, poseyera, poseyendo; -- concluir -> concluyó, concluyera, concluyendo; ir -> yendo; but not conseguir/delinquir if stem == "" or (rfind(stem, V .. "$") and not rfind(stem, "[gq]u$")) then ending = ending:gsub("^i", "y") end -- (4) -gü + ie- -> -guye-: argüir -> arguyó, arguyera, arguyendo if stem:find("gü$") then -- transfer the y to the stem to avoid gü -> gu below in front/back conversions stem = stem:gsub("ü$", "uy") ending = ending:gsub("^i", "") end -- (5) initial i absorbed after ñ, ll, y: tañer -> tañó, tañera, tañendo; bullir -> bulló, bullera, bullendo if rfind(stem, "[ñy]$") or rfind(stem, "ll$") then ending = ending:gsub("^i", "") end end -- If ending begins with i, it must get an accent after a/e/i/o to prevent the two merging into a diphthong: -- caer -> caíste, caímos; reír -> reíste, reímos (pres and pret). This does not apply after u, e.g. -- concluir -> concluiste, concluimos. if ending:find("^i") and stem:find("[aeio]$") then ending = ending:gsub("^i", "í") end -- If -oír/-uir (i.e. -ir with stem ending in -o/u, e.g. oír, concluir), a y must be added before endings -- beginning with a/e/o. Check for base.stems.pret_conj == "irreg" to exclude stem fu- of [[ir]]. if base.conj == "ir" and rfind(ending, "^[aeoáéó]") and base.stems.pret_conj ~= "irreg" then if rfind(stem, "[aoú]$") then -- oír -> oye, rehuir -> rehúyo/rehúye (with indicator 'ú') stem = stem .. "y" elseif rfind(stem, "[^gq]u$") then -- concluir, but not conseguir or delinquir stem = stem .. "y" elseif stem:find("ü$") then -- argüir -> arguyendo stem = stem:gsub("ü$", "uy") end end -- Spelling changes in the stem; it depends on whether the stem given is the pre-front-vowel or -- pre-back-vowel variant, as indicated by `frontback`. We want these front-back spelling changes to happen -- between stem and ending, not between prefix and stem; the prefix may not have the same "front/backness" -- as the stem. local is_front = rfind(ending, "^[eiéí]") if base.frontback == "front" and not is_front then -- parecer -> parezco, conducir -> conduzco; use zqu to avoid triggering the following gsub(); -- the third line will replace zqu -> zc if slot ~= "pret_3s" then -- exclude hice -> hizo (not #hizco) stem = rsub(stem, "(" .. V .. ")c$", "%1zqu") end stem = stem:gsub("sc$", "squ") -- evanescer -> evanesco, fosforescer -> fosforesco stem = stem:gsub("c$", "z") -- ejercer -> ejerzo, uncir -> unzo stem = stem:gsub("qu$", "c") -- delinquir -> delinco, parecer -> parezqu- -> parezco stem = stem:gsub("g$", "j") -- coger -> cojo, afligir -> aflijo stem = stem:gsub("gu$", "g") -- distinguir -> distingo stem = stem:gsub("gü$", "gu") -- may not occur; argüir -> arguyo handled above elseif base.frontback == "back" and is_front then stem = stem:gsub("gu$", "gü") -- averiguar -> averigüé stem = stem:gsub("g$", "gu") -- cargar -> cargué stem = stem:gsub("c$", "qu") -- marcar -> marqué stem = rsub(stem, "[çz]$", "c") -- aderezar/adereçar -> aderecé end return stem .. ending end local function check_stems_for_suppress_prefix(slot, stems) -- Check whether any or all stems have `suppress_prefix`. local any_suppress_prefix = false local any_not_suppress_prefix = false if type(stems) == "table" then if stems.suppress_prefix then -- A single form object. any_suppress_prefix = true else for _, stem in ipairs(stems) do if type(stem) == "table" and stem.suppress_prefix then any_suppress_prefix = true else any_not_suppress_prefix = true end end end else any_not_suppress_prefix = true end if any_suppress_prefix and any_not_suppress_prefix then -- This should never happen because suppress_prefix is set on user-specified stem and individual form -- overrides, which should completely replace built-in overrides (which don't have suppress_prefix). error("Internal error: For slot '" .. slot .. ", saw a mixture of suppress-prefix and non-suppress-prefix stems, and can't handle") else return any_suppress_prefix end end local function add(base, slot, stems, endings, is_combining_ending, allow_overrides) if skip_slot(base, slot, allow_overrides) then return end local function do_combine_stem_ending(stem, ending) return combine_stem_ending(base, slot, stem, ending, is_combining_ending) end iut.add_forms(base.forms, slot, stems, endings, do_combine_stem_ending, nil, nil, base.all_footnotes) end local function add3(base, slot, stems, endings, allow_overrides) local suppress_prefix = base.prefix == "" or check_stems_for_suppress_prefix(slot, stems) if suppress_prefix then return add(base, slot, stems, endings, "is combining ending", allow_overrides) end if skip_slot(base, slot, allow_overrides) then return end local is_combining_ending = false local function do_combine_stem_ending(stem, ending) return combine_stem_ending(base, slot, stem, ending, is_combining_ending) end -- Have to reimplement add_multiple_forms() ourselves due to the is_combining_ending -- flag, which needs to be different when adding prefix to stems vs. stems to ending. -- Otherwise we get e.g. #reímpreso instead of reimpreso. local tempdest = {} iut.add_forms(tempdest, slot, base.prefix, stems, do_combine_stem_ending) is_combining_ending = true iut.add_forms(base.forms, slot, tempdest[slot], endings, do_combine_stem_ending) end local function insert_form(base, slot, form) if not skip_slot(base, slot) then iut.insert_form(base.forms, slot, form) end end local function insert_forms(base, slot, forms) if not skip_slot(base, slot) then iut.insert_forms(base.forms, slot, forms) end end local function add_single_stem_tense(base, slot_pref, stems, s1, s2, s3, p1, p2, p3) local function addit(slot, ending) add3(base, slot_pref .. "_" .. slot, stems, ending) end addit("1s", s1) addit("2s", s2) addit("3s", s3) addit("1p", p1) addit("2p", p2) addit("3p", p3) end local function add_present_indic(base) local function addit(slot, stems, ending) add3(base, "pres_" .. slot, stems, ending) end local s2, s2v, s3, p1, p2, p3 if base.conj == "ar" then s2, s2v, s3, p1, p2, p3 = "as", "ás", "a", "amos", "áis", "an" elseif base.conj == "er" then s2, s2v, s3, p1, p2, p3 = "es", "és", "e", "emos", "éis", "en" elseif base.conj == "ir" then s2, s2v, s3, p1, p2, p3 = "es", "ís", "e", "imos", "ís", "en" else error("Internal error: Unrecognized conjugation " .. base.conj) end addit("1s", base.stems.pres1, "o") addit("2s", base.stems.pres_stressed, s2) addit("2sv", base.stems.pres_unstressed, s2v) addit("3s", base.stems.pres_stressed, s3) addit("1p", base.stems.pres_unstressed, p1) addit("2p", base.stems.pres_unstressed, p2) addit("3p", base.stems.pres_stressed, p3) end local function add_present_subj(base) local function addit(slot, stems, ending) add3(base, "pres_sub_" .. slot, stems, ending) end local s1, s2, s2v, s3, p1, p2, p3, voseo_stem if base.conj == "ar" then s1, s2, s2v, s3, p1, p2, p3 = "e", "es", "és", "e", "emos", "éis", "en" else s1, s2, s2v, s3, p1, p2, p3 = "a", "as", "ás", "a", "amos", "áis", "an" end addit("1s", base.stems.pres_sub_stressed, s1) addit("2s", base.stems.pres_sub_stressed, s2) addit("2sv", base.stems.pres_sub_unstressed, s2v) addit("3s", base.stems.pres_sub_stressed, s3) addit("1p", base.stems.pres_sub_unstressed, p1) addit("2p", base.stems.pres_sub_unstressed, p2) addit("3p", base.stems.pres_sub_stressed, p3) end local function add_imper(base) local function addit(slot, stems, ending) add3(base, "imp_" .. slot, stems, ending) end if base.conj == "ar" then addit("2s", base.stems.pres_stressed, "a") addit("2sv", base.stems.pres_unstressed, "á") addit("2p", base.stems.pres_unstressed, "ad") elseif base.conj == "er" then addit("2s", base.stems.pres_stressed, "e") addit("2sv", base.stems.pres_unstressed, "é") addit("2p", base.stems.pres_unstressed, "ed") elseif base.conj == "ir" then addit("2s", base.stems.pres_stressed, "e") addit("2sv", base.stems.pres_unstressed, "í") addit("2p", base.stems.pres_unstressed, "id") else error("Internal error: Unrecognized conjugation " .. base.conj) end end local function add_finite_non_present(base) local function add_tense(slot, stem, s1, s2, s3, p1, p2, p3) add_single_stem_tense(base, slot, stem, s1, s2, s3, p1, p2, p3) end local stems = base.stems if stems.full_impf then -- An override needs to be supplied for the impf_1p due to the accent on the stem. add_tense("impf", stems.full_impf, "a", "as", "a", {}, "ais", "an") elseif base.conj == "ar" then add_tense("impf", stems.impf, "aba", "abas", "aba", "ábamos", "abais", "aban") else add_tense("impf", stems.impf, "ía", "ías", "ía", "íamos", "íais", "ían") end if stems.pret_conj == "irreg" then add_tense("pret", stems.pret, "e", "iste", "o", "imos", "isteis", "ieron") elseif stems.pret_conj == "ar" then add_tense("pret", stems.pret, "é", "aste", "ó", "amos", "asteis", "aron") else add_tense("pret", stems.pret, "í", "iste", "ió", "imos", "isteis", "ieron") end if stems.pret_conj == "ar" then add_tense("impf_sub_ra", stems.impf_sub_ra, "ara", "aras", "ara", "áramos", "arais", "aran") add_tense("impf_sub_se", stems.impf_sub_se, "ase", "ases", "ase", "ásemos", "aseis", "asen") add_tense("fut_sub", stems.fut_sub, "are", "ares", "are", "áremos", "areis", "aren") else add_tense("impf_sub_ra", stems.impf_sub_ra, "iera", "ieras", "iera", "iéramos", "ierais", "ieran") add_tense("impf_sub_se", stems.impf_sub_se, "iese", "ieses", "iese", "iésemos", "ieseis", "iesen") add_tense("fut_sub", stems.fut_sub, "iere", "ieres", "iere", "iéremos", "iereis", "ieren") end add_tense("fut", stems.fut, "é", "ás", "á", "emos", "éis", "án") add_tense("cond", stems.cond, "ía", "ías", "ía", "íamos", "íais", "ían") end local function add_non_finite_forms(base) local stems = base.stems local function addit(slot, stems, ending) add3(base, slot, stems, ending) end insert_form(base, "infinitive", {form = base.verb}) -- Also insert "infinitive + reflexive pronoun" combinations if we're handling a reflexive verb. See comment below for -- "gerund + reflexive pronoun" combinations. if base.refl then for _, persnum in ipairs(person_number_list_basic) do insert_form(base, "infinitive_" .. persnum, {form = base.verb}) end end local ger_ending = base.conj == "ar" and "ando" or "iendo" addit("gerund", stems.pres_unstressed, ger_ending) -- Also insert "gerund + reflexive pronoun" combinations if we're handling a reflexive verb. We insert exactly the same -- form as for the bare gerund; later on in add_reflexive_or_fixed_clitic_to_forms(), we add the appropriate clitic -- pronouns. It's important not to do this for non-reflexive verbs, because in that case, the clitic pronouns won't be -- added, and {{es-verb form of}} will wrongly consider all these combinations as possible inflections of the bare -- gerund. Thanks to [[User:JeffDoozan]] for this bug fix. if base.refl then for _, persnum in ipairs(person_number_list_basic) do addit("gerund_" .. persnum, stems.pres_unstressed, ger_ending) end end addit("pp_ms", stems.pp, "o") addit("pp_fs", stems.pp, "a") addit("pp_mp", stems.pp, "os") addit("pp_fp", stems.pp, "as") end local function copy_subjunctives_to_imperatives(base) -- Copy subjunctives to imperatives, unless there's an override for the given slot (as with the imp_1p of [[ir]]). for _, persnum in ipairs({"3s", "1p", "3p"}) do local from = "pres_sub_" .. persnum local to = "imp_" .. persnum insert_forms(base, to, iut.map_forms(base.forms[from], function(form) return form end)) end end -- Remove monosyllabic accents (e.g. the 3sg preterite of fiar is fio not #fió). Note that there are a -- few monosyllabic verb forms that intentionally have an accent, to distinguish them from other words -- with the same pronunciation. These are as follows: -- (1) [[sé]] 1sg present indicative of [[saber]]; -- (2) [[sé]] 2sg imperative of [[ser]]; -- (3) [[dé]] 1sg and 3sg present subjunctive of [[dar]]. -- For these, a * is added, which indicates that the accent needs to remain. If we see such a *, we remove -- it but otherwise leave the form alone. local function remove_monosyllabic_accents(base) for _, slotaccel in ipairs(base.alternant_multiword_spec.verb_slots_basic) do local slot, accel = unpack(slotaccel) if base.forms[slot] then for _, form in ipairs(base.forms[slot]) do if form.form:find("%*") then -- * means leave alone any accented vowel form.form = form.form:gsub("%*", "") elseif may_need_monosyllabic_accent_removed(form.form) then -- Has an accented vowel and no VCV sequence and not a suffix; may be monosyllabic, in which -- case we need to remove the accent. Check # of syllables and remove accent if only 1. Note -- that the checks for accented vowel and VCV sequence are not strictly needed, but are -- optimizations to avoid running the whole syllabification algorithm on every verb form. local syllables = com.syllabify(form.form) if #syllables == 1 then form.form = com.remove_accent_from_syllable(syllables[1]) end end end end end end -- Add the appropriate clitic pronouns in `clitics` to the forms in `base_slot`. `store_cliticized_form` is a function -- of three arguments (clitic, formobj, cliticized_form) and should store the cliticized form for the specified clitic -- and form object. local function add_forms_with_clitic(base, base_slot, clitics, store_cliticized_form) -- the special slot lxx is for le/les clitics that become "se" in double clitics if base_slot:match("_comb_lxx$") then base_slot = base_slot:gsub("_comb_lxx$", "_comb_le") end if not base.forms[base_slot] then mw.log("unhandled base_slot " .. base_slot) -- This can happen, e.g. in only3s/only3sp/only3p verbs. return end for _, formobj in ipairs(base.forms[base_slot]) do -- Figure out the correct accenting of the verb when a clitic pronoun is attached to it. We may need to -- add or remove an accent mark: -- (1) No accent mark currently, none needed: infinitive sentar -> sentarlo; imperative singular ten -> tenlo; -- (2) Accent mark currently, still needed: infinitive oír -> oírlo; -- (3) No accent mark currently, accent needed: imperative singular siente -> siéntelo; -- (4) Accent mark currently, not needed: imperative singular está -> estalo, sé -> selo. local syllables = com.syllabify(formobj.form) local sylno = com.stressed_syllable(syllables) table.insert(syllables, "lo") local needs_accent = com.accent_needed(syllables, sylno) if needs_accent then syllables[sylno] = com.add_accent_to_syllable(syllables[sylno]) else syllables[sylno] = com.remove_accent_from_syllable(syllables[sylno]) end table.remove(syllables) -- remove added clitic pronoun local reaccented_form = table.concat(syllables) for _, clitic in ipairs(clitics) do local cliticized_form -- Some further special cases. if base_slot == "imp_1p" and (clitic == "nos" or clitic == "os") then -- Final -s disappears: sintamos + nos -> sintámonos, sintamos + os -> sintámoos cliticized_form = reaccented_form:gsub("s$", "") .. clitic elseif base_slot == "imp_2p" and clitic == "os" then -- Final -d disappears, which may cause an accent to be required: -- haced + os -> haceos, sentid + os -> sentíos if reaccented_form:find("id$") then cliticized_form = reaccented_form:gsub("id$", "íos") else cliticized_form = reaccented_form:gsub("d$", "os") end else cliticized_form = reaccented_form .. clitic end store_cliticized_form(clitic, formobj, cliticized_form) end end end -- Generate the combinations of verb form (infinitive, gerund or various imperatives) + clitic pronoun. local function add_combined_forms(base) for _, base_slot_and_clitics in ipairs(base.alternant_multiword_spec.verb_slot_combined_rows) do local base_slot, clitics = unpack(base_slot_and_clitics) add_forms_with_clitic(base, base_slot, clitics, function(clitic, formobj, cliticized_form) insert_form(base, base_slot .. "_comb_" .. clitic, {form = cliticized_form, footnotes = formobj.footnotes}) end ) end for _, single_comb_slot_and_clitics in ipairs(base.alternant_multiword_spec.verb_slot_double_combined_rows) do local single_comb_slot, clitics = unpack(single_comb_slot_and_clitics) -- Handle le -> se transformation for double clitics if single_comb_slot:match("_comb_lxx$") then add_forms_with_clitic(base, single_comb_slot, clitics, function(clitic, formobj, cliticized_form) -- Only add se version for accusative clitics (lo/la/los/las) if clitic:match("^l[aeo]") then cliticized_form = cliticized_form:gsub("le" .. clitic .. "$", "se" .. clitic) insert_form(base, single_comb_slot .. "_" .. clitic, {form = cliticized_form, footnotes = formobj.footnotes}) end end ) end add_forms_with_clitic(base, single_comb_slot, clitics, function(clitic, formobj, cliticized_form) insert_form(base, single_comb_slot .. "_" .. clitic, {form = cliticized_form, footnotes = formobj.footnotes}) end ) end end local function process_slot_overrides(base, do_basic, reflexive_only) local overrides = reflexive_only and base.basic_reflexive_only_overrides or do_basic and base.basic_overrides or base.combined_overrides for slot, forms in pairs(overrides) do local suppress_prefix = base.prefix == "" or check_stems_for_suppress_prefix(slot, forms) add(base, slot, suppress_prefix and "" or base.prefix, forms, false, "allow overrides") end end -- Prefix `form` with `clitic`, adding fixed text `between` between them. Add links as appropriate unless the user -- requested no links. Check whether form already has brackets (as will be the case if the form has a fixed clitic). local function add_clitic_to_form(base, clitic, between, form) if base.alternant_multiword_spec.args.noautolinkverb then return clitic .. between .. form else local clitic_pref = "[[" .. clitic .. "]]" .. between if form:find("%[%[") then return clitic_pref .. form else return clitic_pref .. "[[" .. form .. "]]" end end end -- Add a reflexive pronoun or fixed clitic, e.g. [[lo]], as appropriate to the base form that were generated. -- `do_joined` means to do only the forms where the pronoun is joined to the end of the form; otherwise, do only the -- forms where it is not joined and precedes the form. local function add_reflexive_or_fixed_clitic_to_forms(base, do_reflexive, do_joined) for _, slotaccel in ipairs(base.alternant_multiword_spec.verb_slots_basic) do local slot, accel = unpack(slotaccel) local clitic if not do_reflexive then clitic = base.clitic elseif slot:find("[123]") then local persnum = slot:match("^.*_(.-)$") clitic = person_number_to_reflexive_pronoun[persnum] else clitic = "se" end if base.forms[slot] then if do_reflexive and slot:find("^pp_") or slot == "infinitive_linked" then -- do nothing with reflexive past participles or with infinitive linked (handled at the end) elseif slot:find("^neg_imp_") then error("Internal error: Should not have forms set for negative imperative at this stage") else local slot_has_suffixed_clitic = slot:find("infinitive") or slot:find("gerund") or slot:find("^imp_") -- Maybe generate non-reflexive parts and separated syntactic variants for use in {{es-verb form of}}. -- See comment in add_slots() above `need_special_verb_form_of_slots`. Check for do_joined so we only -- run this code once. if do_reflexive and do_joined and base.alternant_multiword_spec.source_template == "es-verb form of" and -- Skip personal variants of infinitives and gerunds so we don't think [[jambando]] is a -- non-reflexive equivalent of [[jambándome]]. not slot:find("infinitive_") and not slot:find("gerund_") then -- Clone the forms because we will be destructively modifying them just below, adding the reflexive -- pronoun. insert_forms(base, slot .. "_non_reflexive", mw.clone(base.forms[slot])) if slot_has_suffixed_clitic then insert_forms(base, slot .. "_variant", iut.map_forms(base.forms[slot], function(form) return add_clitic_to_form(base, clitic, " ... ", form) end)) end end if slot_has_suffixed_clitic then if do_joined then add_forms_with_clitic(base, slot, {clitic}, function(clitic, formobj, cliticized_form) formobj.form = cliticized_form end ) end elseif not do_joined then -- Add clitic as separate word before all other forms. for _, form in ipairs(base.forms[slot]) do form.form = add_clitic_to_form(base, clitic, " ", form.form) end end end end end end local function handle_infinitive_linked(base) -- Compute linked versions of potential lemma slots, for use in {{es-verb}}. -- We substitute the original lemma (before removing links) for forms that -- are the same as the lemma, if the original lemma has links. for _, slot in ipairs({"infinitive"}) do insert_forms(base, slot .. "_linked", iut.map_forms(base.forms[slot], function(form) if form == base.lemma and rfind(base.linked_lemma, "%[%[") then return base.linked_lemma else return form end end)) end end local function generate_negative_imperatives(base) -- Copy subjunctives to negative imperatives, preceded by "no". for _, persnum in ipairs(neg_imp_person_number_list) do local from = "pres_sub_" .. persnum local to = "neg_imp_" .. persnum insert_forms(base, to, iut.map_forms(base.forms[from], function(form) if base.alternant_multiword_spec.args.noautolinkverb then return "no " .. form elseif form:find("%[%[") then -- already linked, e.g. when reflexive return "[[no]] " .. form else return "[[no]] [[" .. form .. "]]" end end)) end end -- Process specs given by the user using 'addnote[SLOTSPEC][FOOTNOTE][FOOTNOTE][...]'. local function process_addnote_specs(base) for _, spec in ipairs(base.addnote_specs) do for _, slot_spec in ipairs(spec.slot_specs) do slot_spec = "^" .. slot_spec .. "$" for slot, forms in pairs(base.forms) do if rfind(slot, slot_spec) then -- To save on memory, side-effect the existing forms. for _, form in ipairs(forms) do form.footnotes = iut.combine_footnotes(form.footnotes, spec.footnotes) end end end end end end local function add_missing_links_to_forms(base) -- Any forms without links should get them now. Redundant ones will be stripped later. for slot, forms in pairs(base.forms) do for _, form in ipairs(forms) do if not form.form:find("%[%[") then form.form = "[[" .. form.form .. "]]" end end end end local function conjugate_verb(base) add_present_indic(base) add_present_subj(base) add_imper(base) add_finite_non_present(base) add_non_finite_forms(base) -- This should happen before add_combined_forms() so overrides of basic forms end up part of the combined forms. process_slot_overrides(base, "do basic") -- do basic slot overrides -- This should happen after process_slot_overrides() in case a derived slot is based on an override (as with the -- imp_3s of [[dar]], [[estar]]). copy_subjunctives_to_imperatives(base) -- This should happen after process_slot_overrides() because overrides may have accents in them that need to be -- removed. (This happens e.g. for most present indicative forms of [[ver]], which have accents in them for the -- prefixed derived verbs, but the accents shouldn't be present in the base verb.) remove_monosyllabic_accents(base) if not base.nocomb then add_combined_forms(base) end -- We need to add joined reflexives, then joined and non-joined clitics, then non-joined reflexives, so we get -- [[házmelo]] but [[no]] [[me]] [[lo]] [[haga]]. if base.refl then -- This should happen after remove_monosyllabic_accents() so the * marking the preservation of monosyllabic -- accents doesn't end up in the middle of a word. add_reflexive_or_fixed_clitic_to_forms(base, "do reflexive", "do joined") process_slot_overrides(base, "do basic", "do reflexive") -- do reflexive-only basic slot overrides end if base.clitic then -- This should happen after reflexives are added. add_reflexive_or_fixed_clitic_to_forms(base, false, "do joined") add_reflexive_or_fixed_clitic_to_forms(base, false, false) end if base.refl then add_reflexive_or_fixed_clitic_to_forms(base, "do reflexive", false) end -- This should happen after add_reflexive_or_fixed_clitic_to_forms() so negative imperatives get the reflexive pronoun -- and clitic in them. generate_negative_imperatives(base) if not base.nocomb then process_slot_overrides(base, false) -- do combined slot overrides end -- This should happen before add_missing_links_to_forms() so that the comparison `form == base.lemma` -- in handle_infinitive_linked() works correctly and compares unlinked forms to unlinked forms. handle_infinitive_linked(base) process_addnote_specs(base) if not base.alternant_multiword_spec.args.noautolinkverb then add_missing_links_to_forms(base) end end local function parse_indicator_spec(angle_bracket_spec) -- Store the original angle bracket spec so we can reconstruct the overall conj spec with the lemma(s) in them. local base = { angle_bracket_spec = angle_bracket_spec, user_basic_overrides = {}, user_stems = {}, addnote_specs = {}, } local function parse_err(msg) error(msg .. ": " .. angle_bracket_spec) end local function fetch_footnotes(separated_group) local footnotes for j = 2, #separated_group - 1, 2 do if separated_group[j + 1] ~= "" then parse_err("Extraneous text after bracketed footnotes: '" .. table.concat(separated_group) .. "'") end if not footnotes then footnotes = {} end table.insert(footnotes, separated_group[j]) end return footnotes end local inside = angle_bracket_spec:match("^<(.*)>$") assert(inside) if inside == "" then return base end local segments = put.parse_balanced_segment_run(inside, "[", "]") local dot_separated_groups = put.split_alternating_runs(segments, "%.") for i, dot_separated_group in ipairs(dot_separated_groups) do local first_element = dot_separated_group[1] if first_element == "addnote" then local spec_and_footnotes = fetch_footnotes(dot_separated_group) if #spec_and_footnotes < 2 then parse_err("Spec with 'addnote' should be of the form 'addnote[SLOTSPEC][FOOTNOTE][FOOTNOTE][...]'") end local slot_spec = table.remove(spec_and_footnotes, 1) local slot_spec_inside = rmatch(slot_spec, "^%[(.*)%]$") if not slot_spec_inside then parse_err("Internal error: slot_spec " .. slot_spec .. " should be surrounded with brackets") end local slot_specs = rsplit(slot_spec_inside, ",") -- FIXME: Here, [[Module:it-verb]] called strip_spaces(). Generally we don't do this. Should we? table.insert(base.addnote_specs, {slot_specs = slot_specs, footnotes = spec_and_footnotes}) elseif indicator_flags[first_element] then if #dot_separated_group > 1 then parse_err("No footnotes allowed with '" .. first_element .. "' spec") end if base[first_element] then parse_err("Spec '" .. first_element .. "' specified twice") end base[first_element] = true elseif rfind(first_element, ":") then local colon_separated_groups = put.split_alternating_runs(dot_separated_group, "%s*:%s*") local first_element = colon_separated_groups[1][1] if #colon_separated_groups[1] > 1 then parse_err("Can't attach footnotes directly to '" .. first_element .. "' spec; attach them to the " .. "colon-separated values following the initial colon") end if overridable_stems[first_element] then if base.user_stems[first_element] then parse_err("Overridable stem '" .. first_element .. "' specified twice") end table.remove(colon_separated_groups, 1) base.user_stems[first_element] = overridable_stems[first_element](colon_separated_groups, {prefix = first_element, base = base, parse_err = parse_err, fetch_footnotes = fetch_footnotes}) else -- assume a basic override; we validate further later when the possible slots are available if base.user_basic_overrides[first_element] then parse_err("Basic override '" .. first_element .. "' specified twice") end table.remove(colon_separated_groups, 1) base.user_basic_overrides[first_element] = allow_multiple_values_for_override( colon_separated_groups, {prefix = first_element, base = base, parse_err = parse_err, fetch_footnotes = fetch_footnotes}, "add monosyllabic asterisk" ) end else local comma_separated_groups = put.split_alternating_runs(dot_separated_group, "%s*,%s*") for j = 1, #comma_separated_groups do local alt = comma_separated_groups[j][1] if not vowel_alternants[alt] then if #comma_separated_groups == 1 then parse_err("Unrecognized spec or vowel alternant '" .. alt .. "'") else parse_err("Unrecognized vowel alternant '" .. alt .. "'") end end if base.vowel_alt then for _, existing_alt in ipairs(base.vowel_alt) do if existing_alt.form == alt then parse_err("Vowel alternant '" .. alt .. "' specified twice") end end else base.vowel_alt = {} end table.insert(base.vowel_alt, {form = alt, footnotes = fetch_footnotes(comma_separated_groups[j])}) end end end return base end -- Normalize all lemmas, substituting the pagename for blank lemmas and adding links to multiword lemmas. local function normalize_all_lemmas(alternant_multiword_spec, pagename) -- (1) Add links to all before and after text. Remember the original text so we can reconstruct the verb spec later. if not alternant_multiword_spec.args.noautolinktext then iut.add_links_to_before_and_after_text(alternant_multiword_spec, "remember original") end -- (2) Remove any links from the lemma, but remember the original form -- so we can use it below in the 'lemma_linked' form. iut.map_word_specs(alternant_multiword_spec, function(base) if base.lemma == "" then base.lemma = pagename end base.user_specified_lemma = base.lemma base.lemma = m_links.remove_links(base.lemma) local refl_verb, clitic = rmatch(base.lemma, "^(.-)(l[aeo]s?)$") if not refl_verb then refl_verb, clitic = base.lemma, nil end local verb, refl = rmatch(refl_verb, "^(.-)(se)$") if not verb then verb, refl = refl_verb, nil end base.user_specified_verb = verb base.refl = refl base.clitic = clitic if base.refl and base.clitic then -- We have to parse the verb suffix to see how to construct the base verb; e.g. -- abrírsela -> abrir but oírsela -> oír. We parse the verb suffix again in all cases -- in detect_indicator_spec(), after splitting off the prefix of irrregular verbs. local actual_verb local inf_stem, suffix = rmatch(base.user_specified_verb, "^(.*)([aáeéií]r)$") if not inf_stem then error("Unrecognized infinitive: " .. base.user_specified_verb) end if suffix == "ír" and inf_stem:find("[aeo]$") then -- accent on suffix should remain base.verb = base.user_specified_verb else base.verb = inf_stem .. com.remove_accent_from_syllable(suffix) end else base.verb = base.user_specified_verb end local linked_lemma if alternant_multiword_spec.args.noautolinkverb or base.user_specified_lemma:find("%[%[") then linked_lemma = base.user_specified_lemma elseif base.refl or base.clitic then -- Reconstruct the linked lemma with separate links around base verb, reflexive pronoun and clitic. linked_lemma = base.user_specified_verb == base.verb and "[[" .. base.user_specified_verb .. "]]" or "[[" .. base.verb .. "|" .. base.user_specified_verb .. "]]" linked_lemma = linked_lemma .. (refl and "[[" .. refl .. "]]" or "") .. (clitic and "[[" .. clitic .. "]]" or "") else -- Add links to the lemma so the user doesn't specifically need to, since we preserve -- links in multiword lemmas and include links in non-lemma forms rather than allowing -- the entire form to be a link. linked_lemma = iut.add_links(base.user_specified_lemma) end base.linked_lemma = linked_lemma end) end local function construct_stems(base) local stems = base.stems stems.pres_unstressed = stems.pres_unstressed or base.inf_stem stems.pres_stressed = stems.pres_stressed or -- If no_pres_stressed given, pres_stressed stem should be empty so no forms are generated. base.no_pres_stressed and {} or base.vowel_alt or base.inf_stem stems.pres1_and_sub = stems.pres1_and_sub or -- If no_pres_stressed given, the entire subjunctive is missing. base.no_pres_stressed and {} or -- If no_pres1_and_sub given, pres1 and entire subjunctive are missing. base.no_pres1_and_sub and {} or nil stems.pres1 = stems.pres1 or stems.pres1_and_sub or stems.pres_stressed stems.impf = stems.impf or base.inf_stem stems.pret = stems.pret or base.inf_stem stems.pret_conj = stems.pret_conj or base.conj stems.fut = stems.fut or base.inf_stem .. base.conj stems.cond = stems.cond or stems.fut stems.pres_sub_stressed = stems.pres_sub_stressed or stems.pres1 stems.pres_sub_unstressed = stems.pres_sub_unstressed or stems.pres1_and_sub or stems.pres_unstressed stems.impf_sub_ra = stems.impf_sub_ra or stems.pret stems.impf_sub_se = stems.impf_sub_se or stems.pret stems.fut_sub = stems.fut_sub or stems.pret stems.pp = stems.pp or base.conj == "ar" and combine_stem_ending(base, "pp_ms", base.inf_stem, "ad", "is combining ending") or -- use combine_stem_ending esp. so we get reído, caído, etc. combine_stem_ending(base, "pp_ms", base.inf_stem, "id", "is combining ending") end -- Make a list of the slots given in `list1` and optionally `list2`, with `prefix` added to the beginning of each slot -- name. The elements of each list are of the form {SLOT, ACCEL}. Used for error messages. local function construct_possible_slots(list1, list2, prefix) list2 = list2 or {} prefix = prefix or "" local slots = {} local function insert_list_slots(list) for _, slotaccel in ipairs(list) do local slot, accel = unpack(slotaccel) table.insert(slots, prefix .. slot) end end insert_list_slots(list1) insert_list_slots(list2) return m_table.serialCommaJoin(slots) end local function detect_indicator_spec(base) base.forms = {} base.non_reflexive_forms = {} base.stems = {} if (base.only3s and 1 or 0) + (base.only3sp and 1 or 0) + (base.only3p and 1 or 0) > 1 then error("Only one of 'only3s', 'only3sp' and 'only3p' can be specified") end base.basic_overrides = {} base.basic_reflexive_only_overrides = {} base.combined_overrides = {} if not base.no_built_in then for _, built_in_conj in ipairs(built_in_conjugations) do if type(built_in_conj.match) == "function" then base.prefix, base.non_prefixed_verb = built_in_conj.match(base.verb) elseif built_in_conj.match:find("^%^") and rsub(built_in_conj.match, "^%^", "") == base.verb then -- begins with ^, for exact match, and matches base.prefix, base.non_prefixed_verb = "", base.verb else base.prefix, base.non_prefixed_verb = rmatch(base.verb, "^(.*)(" .. built_in_conj.match .. ")$") end if base.prefix then -- we found a built-in verb base.irreg_verb = true for stem, forms in pairs(built_in_conj.forms) do if stem:find("^refl_") then stem = stem:gsub("^refl_", "") if not base.alternant_multiword_spec.verb_slots_basic_map[stem] then error("Internal error: setting for 'refl_" .. stem .. "' does not refer to a basic verb slot") end base.basic_reflexive_only_overrides[stem] = forms elseif base.alternant_multiword_spec.verb_slots_basic_map[stem] then -- an individual form override of a basic form base.basic_overrides[stem] = forms elseif base.alternant_multiword_spec.verb_slots_combined_map[stem] then -- an individual form override of a combined form base.combined_overrides[stem] = forms else base.stems[stem] = forms end end break end end end -- Override built-in-verb stems and overrides with user-specified ones. for stem, values in pairs(base.user_stems) do base.stems[stem] = values end for override, values in pairs(base.user_basic_overrides) do if override:find("^refl_") then if not base.refl then error("Can't set reflexive-only override '" .. override .. "' on a non-reflexive verb") end override = override:gsub("^refl_", "") if not base.alternant_multiword_spec.verb_slots_basic_map[override] then error("Unrecognized reflexive-only override 'refl_" .. override .. "': possible slots are " .. construct_possible_slots(base.alternant_multiword_spec.verb_slots_basic, nil, "refl_")) end base.basic_reflexive_only_overrides[override] = values elseif base.alternant_multiword_spec.verb_slots_basic_map[override] then base.basic_overrides[override] = values elseif base.alternant_multiword_spec.verb_slots_combined_map[override] then base.combined_overrides[override] = values else error("Unrecognized override '" .. override .. "': possible slots are " .. construct_possible_slots(base.alternant_multiword_spec.verb_slots_basic, base.alternant_multiword_spec.verb_slots_combined)) end end base.prefix = base.prefix or "" base.non_prefixed_verb = base.non_prefixed_verb or base.verb local inf_stem, suffix = rmatch(base.non_prefixed_verb, "^(.*)([aeií]r)$") if not inf_stem then error("Unrecognized infinitive: " .. base.verb) end base.inf_stem = inf_stem suffix = suffix == "ír" and "ir" or suffix base.conj = suffix base.frontback = suffix == "ar" and "back" or "front" if base.stems.vowel_alt then -- built-in verb with specified vowel alternation if base.vowel_alt then error(base.verb .. " is a recognized built-in verb, and should not have vowel alternations specified with it") end base.vowel_alt = iut.convert_to_general_list_form(base.stems.vowel_alt) end -- Propagate built-in-verb indicator flags to `base` and combine with user-specified flags. for indicator_flag, _ in pairs(indicator_flags) do base[indicator_flag] = base[indicator_flag] or base.stems[indicator_flag] end -- Convert vowel alternation indicators into stems. if base.vowel_alt then for _, altform in ipairs(base.vowel_alt) do altform.alt = altform.form -- save original indicator local alt = altform.alt if base.conj == "ir" then local raising = ( alt == "ie-i" or alt == "ye-i" or alt == "ue-u" or alt == "i" or alt == "í" or alt == "ú" ) if base.stems.raising_conj == nil then base.stems.raising_conj = raising elseif base.stems.raising_conj ~= raising then error("Can't currently support a mixture of raising (e.g. 'ie-i') and non-raising (e.g. 'ie') vowel alternations in -ir verbs") end end if alt == "+" then altform.form = base.inf_stem else local normalized_alt = alt if alt == "ie-i" or alt == "ye-i" or alt == "ue-u" then if base.conj ~= "ir" then error("Vowel alternation '" .. alt .. "' only supported with -ir verbs") end -- ie-i is like i except for the vowel raising before i+V, similarly for ye-i, ue-u, -- so convert appropriately. normalized_alt = alt == "ie-i" and "ie" or alt == "ye-i" and "ye" or "ue" end local ret = com.apply_vowel_alternation(base.inf_stem, normalized_alt) if ret.err then error("To use '" .. alt .. "', present stem '" .. base.inf_stem .. "' " .. ret.err) end altform.form = ret.ret end end end end local function detect_all_indicator_specs(alternant_multiword_spec) -- Propagate some settings up; some are used internally, others by [[Module:es-headword]]. iut.map_word_specs(alternant_multiword_spec, function(base) -- Internal indicator flags. Do these before calling detect_indicator_spec() because add_slots() uses them. for _, prop in ipairs { "refl", "clitic" } do if base[prop] then alternant_multiword_spec[prop] = true end end base.alternant_multiword_spec = alternant_multiword_spec -- If reflexive or fixed clitic, don't include combined forms. alternant_multiword_spec.nocomb = alternant_multiword_spec.nocomb or base.clitic or base.refl end) add_slots(alternant_multiword_spec) iut.map_word_specs(alternant_multiword_spec, function(base) base.nocomb = alternant_multiword_spec.args.nocomb detect_indicator_spec(base) -- User-specified indicator flags. Do these after calling detect_indicator_spec() because the latter may set these -- indicators for built-in verbs. for prop, _ in pairs(indicator_flags) do if base[prop] then alternant_multiword_spec[prop] = true end end construct_stems(base) end) end local function add_categories_and_annotation(alternant_multiword_spec, base, multiword_lemma) local function insert_ann(anntype, value) m_table.insertIfNot(alternant_multiword_spec.annotation[anntype], value) end local function insert_cat(cat, also_when_multiword) -- Don't place multiword terms in categories like 'Spanish verbs ending in -ar' to avoid spamming the -- categories with such terms. if also_when_multiword or not multiword_lemma then m_table.insertIfNot(alternant_multiword_spec.categories, cat .. "ภาษาสเปน") end end if check_for_red_links and alternant_multiword_spec.source_template == "es-conj" and multiword_lemma then for _, slot_and_accel in ipairs(alternant_multiword_spec.all_verb_slots) do local slot = slot_and_accel[1] local forms = base.forms[slot] local must_break = false if forms then for _, form in ipairs(forms) do if not form.form:find("%[%[") then local title = mw.title.new(form.form) if title and not title.exists then insert_cat("verbs with red links in their inflection tables") must_break = true break end end end end if must_break then break end end end insert_cat("คำกริยาที่ลงท้ายด้วย -" .. base.conj) if base.irreg_verb then insert_ann("irreg", "ไม่ปรกติ") insert_cat("คำกริยาไม่ปรกติ") else insert_ann("irreg", "ปรกติ") end if base.only3s then insert_ann("defective", "อบุรุษ") insert_cat("คำอบุรุษกริยา") elseif base.only3sp then insert_ann("defective", "บุรุษที่สามเท่านั้น") insert_cat("คำกริยาบุรุษที่สามเท่านั้น") elseif base.only3p then insert_ann("defective", "บุรุษที่สามพหูพจน์เท่านั้น") insert_cat("คำกริยาบุรุษที่สามพหูพจน์เท่านั้น") elseif base.no_pres_stressed or base.no_pres1_and_sub then insert_ann("defective", "บกพร่อง") insert_cat("คำกริยาบกพร่อง") else insert_ann("defective", "บุรุษปรกติ") end if base.clitic then insert_cat("verbs with lexical clitics") end if base.refl then insert_cat("คำกริยาสะท้อน") end if not base.vowel_alt then insert_ann("vowel_alt", "ไม่เปลี่ยนแปรสระ") else local inf_stem = base.inf_stem:gsub("[gq]u$", "x") for _, alt in ipairs(base.vowel_alt) do if alt.alt == "+" then insert_ann("vowel_alt", "ไม่เปลี่ยนแปรสระ") else local desc if alt.alt == "ue" and rfind(inf_stem, "u" .. C .. "*$") then desc = "เปลี่ยนแปร u-ue" -- jugar elseif alt.alt == "ie" and rfind(inf_stem, "i" .. C .. "*$") then desc = "เปลี่ยนแปร i-ie" -- adquirir elseif alt.alt == "í" and rfind(inf_stem, "e" .. C .. "*$") then desc = "เปลี่ยนแปร e-í" -- reír, freír, etc. else desc = "เปลี่ยนแปร " .. vowel_alternant_to_desc[alt.alt] end insert_ann("vowel_alt", desc) insert_cat("คำกริยาที่" .. desc) end end end local cons_alt = base.stems.cons_alt if cons_alt == nil then if base.conj == "ar" then if base.inf_stem:find("z$") then cons_alt = "c-z" elseif base.inf_stem:find("ç$") then cons_alt = "c-ç" elseif base.inf_stem:find("c$") then cons_alt = "c-qu" elseif base.inf_stem:find("g$") then cons_alt = "g-gu" elseif base.inf_stem:find("gu$") then cons_alt = "gu-gü" end else if base.no_pres_stressed or base.no_pres1_and_sub then cons_alt = nil -- no c-zc alternation in balbucir or arrecir elseif rfind(base.inf_stem, V .. "c$") then cons_alt = "c-zc" elseif base.inf_stem:find("sc$") then cons_alt = "แข็ง-อ่อน" elseif base.inf_stem:find("c$") then cons_alt = "c-z" elseif base.inf_stem:find("qu$") then cons_alt = "c-qu" elseif base.inf_stem:find("g$") then cons_alt = "g-j" elseif base.inf_stem:find("gu$") then cons_alt = "g-gu" elseif base.inf_stem:find("gü$") then cons_alt = "gu-gü" end end end if cons_alt then local desc = "เปลี่ยนแปร" .. (cons_alt ~= "แข็ง-อ่อน" and " " or "") .. cons_alt insert_ann("cons_alt", desc) insert_cat("คำกริยาที่" .. desc) else insert_ann("cons_alt", "ไม่เปลี่ยนแปรพยัญชนะ") end end -- Compute the categories to add the verb to, as well as the annotation to display in the -- conjugation title bar. We combine the code to do these functions as both categories and -- title bar contain similar information. local function compute_categories_and_annotation(alternant_multiword_spec) alternant_multiword_spec.categories = {} local ann = {} alternant_multiword_spec.annotation = ann ann.irreg = {} ann.defective = {} ann.vowel_alt = {} ann.cons_alt = {} local multiword_lemma = false for _, form in ipairs(alternant_multiword_spec.forms.infinitive) do if form.form:find(" ") then multiword_lemma = true break end end iut.map_word_specs(alternant_multiword_spec, function(base) add_categories_and_annotation(alternant_multiword_spec, base, multiword_lemma) end) local ann_parts = {} local irreg = table.concat(ann.irreg, " or ") if irreg ~= "" and irreg ~= "regular" then table.insert(ann_parts, irreg) end local defective = table.concat(ann.defective, " or ") if defective ~= "" and defective ~= "regular" then table.insert(ann_parts, defective) end local vowel_alt = table.concat(ann.vowel_alt, " or ") if vowel_alt ~= "" and vowel_alt ~= "non-alternating" then table.insert(ann_parts, vowel_alt) end local cons_alt = table.concat(ann.cons_alt, " or ") if cons_alt ~= "" and cons_alt ~= "non-alternating" then table.insert(ann_parts, cons_alt) end alternant_multiword_spec.annotation = table.concat(ann_parts, "; ") end local function show_forms(alternant_multiword_spec) local lemmas = alternant_multiword_spec.forms.infinitive alternant_multiword_spec.lemmas = lemmas -- save for later use in make_table() local reconstructed_verb_spec = iut.reconstruct_original_spec(alternant_multiword_spec) -- Initialize the footnotes with those for the future subjunctive and maybe the pres subjunctive -- voseo usage. In the latter case, we only do it if there is a distinct pres subjunctive voseo form. local function create_footnote_obj() local obj = iut.create_footnote_obj() iut.get_footnote_text({fut_sub_note}, obj) -- Compute whether the tuteo and voseo variants are different, for each voseo variant. -- We use this later in make_table(). for _, slot in ipairs({"pres_2s", "pres_sub_2s", "imp_2s"}) do alternant_multiword_spec["separate_" .. slot .. "v"] = false iut.map_word_specs(alternant_multiword_spec, function(base) if not m_table.deepEquals(base.forms[slot], base.forms[slot .. "v"]) then alternant_multiword_spec["separate_" .. slot .. "v"] = true end end) end if alternant_multiword_spec.separate_pres_sub_2sv then iut.get_footnote_text({pres_sub_voseo_note}, obj) end return obj end local function transform_accel_obj(slot, formobj, accel_obj) -- No accelerators for negative imperatives, which are always multiword and derived directly from the -- present subjunctive. if slot:find("^neg_imp") then return nil end if accel_obj then if slot:find("^pp_") then accel_obj.form = slot elseif slot == "gerund" then accel_obj.form = "gerund-" .. reconstructed_verb_spec else accel_obj.form = "verb-form-" .. reconstructed_verb_spec end end return accel_obj end local props = { lang = lang, lemmas = lemmas, create_footnote_obj = create_footnote_obj, transform_accel_obj = transform_accel_obj, } props.slot_list = alternant_multiword_spec.verb_slots_basic iut.show_forms(alternant_multiword_spec.forms, props) alternant_multiword_spec.footnote_basic = alternant_multiword_spec.forms.footnote props.create_footnote_obj = nil props.slot_list = alternant_multiword_spec.verb_slots_combined iut.show_forms(alternant_multiword_spec.forms, props) alternant_multiword_spec.footnote_combined = alternant_multiword_spec.forms.footnote end local notes_template = [=[ <div class="roa-footnote-outer-div" style="width:100%;"> <div class="roa-footnote-inner-div"> {footnote} </div></div> ]=] local pres_2sv_template = '<sup><sup>tú</sup></sup><br />{pres_2sv}<sup><sup>vos</sup></sup>' local pres_sub_2sv_template = '<sup><sup>tú</sup></sup><br />{pres_sub_2sv}<sup><sup>vos<sup class="roa-red-superscript">2</sup></sup></sup>' local imp_2sv_template = '<sup><sup>tú</sup></sup><br />{imp_2sv}<sup><sup>vos</sup></sup>' local basic_table = [=[ {description}<div class="NavFrame"> <div class="NavHead" align=center>&nbsp; &nbsp; การผันรูปของ {title} (ดูที่ [[ภาคผนวก:คำกริยาภาษาสเปน]])</div> <div class="NavContent"> {\op}| class="roa-inflection-table" |- ! colspan="3" class="roa-nonfinite-header" | <span title="infinitivo">รูปกริยากลาง</span> | colspan="5" | {infinitive} |- ! colspan="3" class="roa-nonfinite-header" | <span title="gerundio">รูปกริยาเป็นนาม</span> | colspan="5" | {gerund} |- ! rowspan="3" colspan="2" class="roa-nonfinite-header" | <span title="participio (pasado)">รูปกริยาขยายอดีตกาล</span> | colspan="2" class="roa-nonfinite-header" | ! colspan="2" class="roa-nonfinite-header" | <span title="masculino">เพศชาย</span> ! colspan="2" class="roa-nonfinite-header" | <span title="femenino">เพศหญิง</span> |- ! colspan="2" class="roa-nonfinite-header" | เอกพจน์ | colspan="2" | {pp_ms} | colspan="2" | {pp_fs} |- ! colspan="2" class="roa-nonfinite-header" | พหูพจน์ | colspan="2" | {pp_mp} | colspan="2" | {pp_fp} |- ! colspan="2" rowspan="2" class="roa-person-number-header" | ! colspan="3" class="roa-person-number-header" | เอกพจน์ ! colspan="3" class="roa-person-number-header" | พหูพจน์ |- ! class="roa-person-number-header" | บุรุษที่หนึ่ง ! class="roa-person-number-header" | บุรุษที่สอง ! class="roa-person-number-header" | บุรุษที่สาม ! class="roa-person-number-header" | บุรุษที่หนึ่ง ! class="roa-person-number-header" | บุรุษที่สอง ! class="roa-person-number-header" | บุรุษที่สาม |-{reflexive_non_finite_clause} ! rowspan="6" class="roa-indicative-left-rail" | <span title="indicativo">มาลาบอกเล่า</span> ! class="roa-native-person-number-header" | ! class="roa-native-person-number-header" | yo ! class="roa-native-person-number-header" | tú<br />vos ! class="roa-native-person-number-header" | él/ella/ello<br />usted ! class="roa-native-person-number-header" | nosotros<br />nosotras ! class="roa-native-person-number-header" | vosotros<br />vosotras ! class="roa-native-person-number-header" | ellos/ellas<br />ustedes |- ! class="roa-finite-header" | <span title="presente de indicativo">ปัจจุบันกาล</span> | {pres_1s} | {pres_2s}{pres_2sv_text} | {pres_3s} | {pres_1p} | {pres_2p} | {pres_3p} |- ! class="roa-finite-header" | <span title="pretérito imperfecto (copréterito)">ไม่สมบูรณ์</span> | {impf_1s} | {impf_2s} | {impf_3s} | {impf_1p} | {impf_2p} | {impf_3p} |- ! class="roa-finite-header" | <span title="pretérito perfecto simple (pretérito indefinido)">อดีตกาลสิ้นสุด (สมบูรณ์)</span> | {pret_1s} | {pret_2s} | {pret_3s} | {pret_1p} | {pret_2p} | {pret_3p} |- ! class="roa-finite-header" | <span title="futuro simple (futuro imperfecto)">อนาคตกาล</span> | {fut_1s} | {fut_2s} | {fut_3s} | {fut_1p} | {fut_2p} | {fut_3p} |- ! class="roa-finite-header" | <span title="condicional simple (pospretérito de modo indicativo)">มาลาเงื่อนไข</span> | {cond_1s} | {cond_2s} | {cond_3s} | {cond_1p} | {cond_2p} | {cond_3p} |- ! class="roa-person-number-header" style="height:.75em" colspan="8" | |- ! rowspan="5" class="roa-subjunctive-left-rail" | <span title="subjuntivo">มาลาสมมุติ</span> ! class="roa-native-person-number-header" | ! class="roa-native-person-number-header" | yo ! class="roa-native-person-number-header" | tú<br />vos ! class="roa-native-person-number-header" | él/ella/ello<br />usted ! class="roa-native-person-number-header" | nosotros<br />nosotras ! class="roa-native-person-number-header" | vosotros<br />vosotras ! class="roa-native-person-number-header" | ellos/ellas<br />ustedes |- ! class="roa-finite-header" | <span title="presente de subjuntivo">ปัจจุบันกาล</span> | {pres_sub_1s} | {pres_sub_2s}{pres_sub_2sv_text} | {pres_sub_3s} | {pres_sub_1p} | {pres_sub_2p} | {pres_sub_3p} |- ! class="roa-finite-header" | <span title="pretérito imperfecto de subjuntivo">ไม่สมบูรณ์</span><br />(ra) | {impf_sub_ra_1s} | {impf_sub_ra_2s} | {impf_sub_ra_3s} | {impf_sub_ra_1p} | {impf_sub_ra_2p} | {impf_sub_ra_3p} |- ! class="roa-finite-header" | <span title="pretérito imperfecto de subjuntivo">ไม่สมบูรณ์</span><br />(se) | {impf_sub_se_1s} | {impf_sub_se_2s} | {impf_sub_se_3s} | {impf_sub_se_1p} | {impf_sub_se_2p} | {impf_sub_se_3p} |- ! class="roa-finite-header" | <span title="futuro simple de subjuntivo (futuro de subjuntivo)">อนาคตกาล</span><sup class="roa-red-superscript">1</sup> | {fut_sub_1s} | {fut_sub_2s} | {fut_sub_3s} | {fut_sub_1p} | {fut_sub_2p} | {fut_sub_3p} |- ! class="roa-person-number-header" style="height:.75em" colspan="8" | |- ! rowspan="6" class="roa-imperative-left-rail" | <span title="imperativo">มาลาสั่ง</span> ! class="roa-native-person-number-header" | ! class="roa-native-person-number-header" | — ! class="roa-native-person-number-header" | tú<br />vos ! class="roa-native-person-number-header" | usted ! class="roa-native-person-number-header" | nosotros<br />nosotras ! class="roa-native-person-number-header" | vosotros<br />vosotras ! class="roa-native-person-number-header" | ustedes |- ! class="roa-finite-header" | <span title="imperativo afirmativo">เชิงยืนยัน</span> | | {imp_2s}{imp_2sv_text} | {imp_3s} | {imp_1p} | {imp_2p} | {imp_3p} |- ! class="roa-finite-header" | <span title="imperativo negativo">เชิงปฏิเสธ</span> | | {neg_imp_2s} | {neg_imp_3s} | {neg_imp_1p} | {neg_imp_2p} | {neg_imp_3p} |{\cl}{notes_clause}</div></div> ]=] local reflexive_non_finite_template = [=[ ! rowspan="3" class="roa-nonfinite-header" | personal non-finite ! class="roa-native-person-number-header" | ! class="roa-native-person-number-header" | yo ! class="roa-native-person-number-header" | tú<br />vos ! class="roa-native-person-number-header" | él/ella/ello<br />usted ! class="roa-native-person-number-header" | nosotros<br />nosotras ! class="roa-native-person-number-header" | vosotros<br />vosotras ! class="roa-native-person-number-header" | ellos/ellas<br />ustedes |- ! class="roa-finite-header" | <span title="infinitivo">รูปกริยากลาง</span> | {infinitive_1s} | {infinitive_2s} | {infinitive_3s} | {infinitive_1p} | {infinitive_2p} | {infinitive_3p} |- ! class="roa-finite-header" | <span title="gerundio">รูปกริยาเป็นนาม</span> | {gerund_1s} | {gerund_2s} | {gerund_3s} | {gerund_1p} | {gerund_2p} | {gerund_3p} |- ! class="roa-person-number-header" style="height:.75em" colspan="8" | |-]=] local combined_form_combined_tu_vos_template = [=[ ! class="roa-person-number-header" style="height:.35em" colspan="8" | |- ! rowspan="2" class="roa-combined-with-imperative" | with informal second-person singular {tuteo}<i>/</i>{voseo} imperative {imp_2s} ! class="roa-finite-header" | สัมปทานการก | {imp_2s_comb_me} | {imp_2s_comb_te} | {imp_2s_comb_le} | {imp_2s_comb_nos} | ''ไม่ใช้'' | {imp_2s_comb_les} |- ! class="roa-finite-header" | กรรมการก | {imp_2s_comb_me} | {imp_2s_comb_te} | {imp_2s_comb_lo}, {imp_2s_comb_la} | {imp_2s_comb_nos} | ''ไม่ใช้'' | {imp_2s_comb_los}, {imp_2s_comb_las} |-]=] local combined_form_separate_tu_vos_template = [=[ ! class="roa-person-number-header" style="height:.35em" colspan="8" | |- ! rowspan="2" class="roa-combined-with-imperative" | with informal second-person singular {tuteo} imperative {imp_2s} ! class="roa-finite-header" | สัมปทานการก | {imp_2s_comb_me} | {imp_2s_comb_te} | {imp_2s_comb_le} | {imp_2s_comb_nos} | ''ไม่ใช้'' | {imp_2s_comb_les} |- ! class="roa-finite-header" | กรรมการก | {imp_2s_comb_me} | {imp_2s_comb_te} | {imp_2s_comb_lo}, {imp_2s_comb_la} | {imp_2s_comb_nos} | ''ไม่ใช้'' | {imp_2s_comb_los}, {imp_2s_comb_las} |- ! class="roa-person-number-header" style="height:.35em" colspan="8" | |- ! rowspan="2" class="roa-combined-with-imperative" | with informal second-person singular {voseo} imperative {imp_2sv} ! class="roa-finite-header" | สัมปทานการก | {imp_2sv_comb_me} | {imp_2sv_comb_te} | {imp_2sv_comb_le} | {imp_2sv_comb_nos} | ''ไม่ใช้'' | {imp_2sv_comb_les} |- ! class="roa-finite-header" | กรรมการก | {imp_2sv_comb_me} | {imp_2sv_comb_te} | {imp_2sv_comb_lo}, {imp_2sv_comb_la} | {imp_2sv_comb_nos} | ''ไม่ใช้'' | {imp_2sv_comb_los}, {imp_2sv_comb_las} |-]=] local combined_form_table = [=[ {description}<div class="NavFrame"> <div class="NavHead" align=center>&nbsp; &nbsp; รูปประสมของ {title} ที่เลือกมาบางส่วน</div> <div class="NavContent"> รูปเหล่านี้สร้างขึ้นโดยอัตโนมัติและอาจไม่ได้ใช้งานจริง การใช้คำสรรพนามแตกต่างกันไปในแต่ละภูมิภาค {\op}| class="inflection-table es-inflection-table" |- ! colspan="2" rowspan="2" class="roa-person-number-header" | ! colspan="3" class="roa-person-number-header" | เอกพจน์ ! colspan="3" class="roa-person-number-header" | พหูพจน์ |- ! class="roa-person-number-header" | บุรุษที่หนึ่ง ! class="roa-person-number-header" | บุรุษที่สอง ! class="roa-person-number-header" | บุรุษที่สาม ! class="roa-person-number-header" | บุรุษที่หนึ่ง ! class="roa-person-number-header" | บุรุษที่สอง ! class="roa-person-number-header" | บุรุษที่สาม |- ! rowspan="2" class="roa-combined-with-infinitive" | with infinitive {infinitive} ! class="roa-finite-header" | สัมปทานการก | {infinitive_comb_me} | {infinitive_comb_te} | {infinitive_comb_le}, {infinitive_comb_se} | {infinitive_comb_nos} | {infinitive_comb_os} | {infinitive_comb_les}, {infinitive_comb_se} |- ! class="roa-finite-header" | กรรมการก | {infinitive_comb_me} | {infinitive_comb_te} | {infinitive_comb_lo}, {infinitive_comb_la}, {infinitive_comb_se} | {infinitive_comb_nos} | {infinitive_comb_os} | {infinitive_comb_los}, {infinitive_comb_las}, {infinitive_comb_se} |- ! class="roa-person-number-header" style="height:.35em" colspan="8" | |- ! rowspan="2" class="roa-combined-with-gerund" | with gerund {gerund} ! class="roa-finite-header" | สัมปทานการก | {gerund_comb_me} | {gerund_comb_te} | {gerund_comb_le}, {gerund_comb_se} | {gerund_comb_nos} | {gerund_comb_os} | {gerund_comb_les}, {gerund_comb_se} |- ! class="roa-finite-header" | กรรมการก | {gerund_comb_me} | {gerund_comb_te} | {gerund_comb_lo}, {gerund_comb_la}, {gerund_comb_se} | {gerund_comb_nos} | {gerund_comb_os} | {gerund_comb_los}, {gerund_comb_las}, {gerund_comb_se} |-{tu_vos_clause} ! class="roa-person-number-header" style="height:.35em" colspan="8" | |- ! rowspan="2" class="roa-combined-with-imperative" | with formal second-person singular imperative {imp_3s} ! class="roa-finite-header" | สัมปทานการก | {imp_3s_comb_me} | ''ไม่ใช้'' | {imp_3s_comb_le}, {imp_3s_comb_se} | {imp_3s_comb_nos} | ''ไม่ใช้'' | {imp_3s_comb_les} |- ! class="roa-finite-header" | กรรมการก | {imp_3s_comb_me} | ''ไม่ใช้'' | {imp_3s_comb_lo}, {imp_3s_comb_la}, {imp_3s_comb_se} | {imp_3s_comb_nos} | ''ไม่ใช้'' | {imp_3s_comb_los}, {imp_3s_comb_las} |- ! class="roa-person-number-header" style="height:.35em" colspan="8" | |- ! rowspan="2" class="roa-combined-with-imperative" | with first-person plural imperative {imp_1p} ! class="roa-finite-header" | สัมปทานการก | ''ไม่ใช้'' | {imp_1p_comb_te} | {imp_1p_comb_le} | {imp_1p_comb_nos} | {imp_1p_comb_os} | {imp_1p_comb_les} |- ! class="roa-finite-header" | กรรมการก | ''ไม่ใช้'' | {imp_1p_comb_te} | {imp_1p_comb_lo}, {imp_1p_comb_la} | {imp_1p_comb_nos} | {imp_1p_comb_os} | {imp_1p_comb_los}, {imp_1p_comb_las} |- ! class="roa-person-number-header" style="height:.35em" colspan="8" | |- ! rowspan="2" class="roa-combined-with-imperative" | with informal second-person plural imperative {imp_2p} ! class="roa-finite-header" | สัมปทานการก | {imp_2p_comb_me} | ''ไม่ใช้'' | {imp_2p_comb_le} | {imp_2p_comb_nos} | {imp_2p_comb_os} | {imp_2p_comb_les} |- ! class="roa-finite-header" | กรรมการก | {imp_2p_comb_me} | ''ไม่ใช้'' | {imp_2p_comb_lo}, {imp_2p_comb_la} | {imp_2p_comb_nos} | {imp_2p_comb_os} | {imp_2p_comb_los}, {imp_2p_comb_las} |- ! class="roa-person-number-header" style="height:.35em" colspan="8" | |- ! rowspan="2" class="roa-combined-with-imperative" | with formal second-person plural imperative {imp_3p} ! class="roa-finite-header" | สัมปทานการก | {imp_3p_comb_me} | ''ไม่ใช้'' | {imp_3p_comb_le} | {imp_3p_comb_nos} | ''ไม่ใช้'' | {imp_3p_comb_les}, {imp_3p_comb_se} |- ! class="roa-finite-header" | กรรมการก | {imp_3p_comb_me} | ''ไม่ใช้'' | {imp_3p_comb_lo}, {imp_3p_comb_la} | {imp_3p_comb_nos} | ''ไม่ใช้'' | {imp_3p_comb_los}, {imp_3p_comb_las}, {imp_3p_comb_se} |{\cl}{notes_clause}</div></div> ]=] local function make_table(alternant_multiword_spec) local forms = alternant_multiword_spec.forms forms.title = link_term(alternant_multiword_spec.lemmas[1].form) if alternant_multiword_spec.annotation ~= "" then forms.title = forms.title .. " (" .. alternant_multiword_spec.annotation .. ")" end forms.description = "" -- Format the basic table. forms.tuteo = tuteo_link forms.voseo = voseo_link forms.footnote = alternant_multiword_spec.footnote_basic forms.reflexive_non_finite_clause = alternant_multiword_spec.refl and m_string_utilities.format(reflexive_non_finite_template, forms) or "" forms.notes_clause = forms.footnote ~= "" and m_string_utilities.format(notes_template, forms) or "" -- The separate_* values are computed in show_forms(). forms.pres_2sv_text = alternant_multiword_spec.separate_pres_2sv and m_string_utilities.format(pres_2sv_template, forms) or "" forms.pres_sub_2sv_text = alternant_multiword_spec.separate_pres_sub_2sv and m_string_utilities.format(pres_sub_2sv_template, forms) or "" forms.imp_2sv_text = alternant_multiword_spec.separate_imp_2sv and m_string_utilities.format(imp_2sv_template, forms) or "" local formatted_basic_table = m_string_utilities.format(basic_table, forms) -- Format the combined table. local formatted_combined_table if alternant_multiword_spec.refl or alternant_multiword_spec.args.nocomb or alternant_multiword_spec.clitic then formatted_combined_table = "" else forms.footnote = alternant_multiword_spec.footnote_combined forms.notes_clause = forms.footnote ~= "" and m_string_utilities.format(notes_template, forms) or "" -- separate_imp_2sv is computed in show_forms(). local tu_vos_template = alternant_multiword_spec.separate_imp_2sv and combined_form_separate_tu_vos_template or combined_form_combined_tu_vos_template forms.tu_vos_clause = m_string_utilities.format(tu_vos_template, forms) formatted_combined_table = m_string_utilities.format(combined_form_table, forms) end -- Paste them together. return require("Module:TemplateStyles")("Module:roa-verb/style.css") .. formatted_basic_table .. formatted_combined_table end -- Externally callable function to parse and conjugate a verb given user-specified arguments. -- Return value is WORD_SPEC, an object where the conjugated forms are in `WORD_SPEC.forms` -- for each slot. If there are no values for a slot, the slot key will be missing. The value -- for a given slot is a list of objects {form=FORM, footnotes=FOOTNOTES}. function export.do_generate_forms(args, source_template, headword_head, double_combined_forms_to_include) local PAGENAME = mw.loadData("Module:headword/data").pagename local function in_template_space() return mw.title.getCurrentTitle().nsText == "แม่แบบ" end -- Determine the verb spec we're being asked to generate the conjugation of. This may be taken from the -- current page title or the value of |pagename=; but not when called from {{es-verb form of}}, where the -- page title is a non-lemma form. Note that the verb spec may omit the infinitive; e.g. it may be "<ue>". -- For this reason, we use the value of `pagename` computed here down below, when calling normalize_all_lemmas(). local pagename = source_template ~= "es-verb form of" and args.pagename or PAGENAME local head = headword_head or pagename local arg1 = args[1] if not arg1 then if (pagename == "es-conj" or pagename == "es-verb") and in_template_space() then arg1 = "licuar<+,ú>" elseif pagename == "es-verb form of" and in_template_space() then arg1 = "amar" else arg1 = "<>" end end -- When called from {{es-verb form of}}, determine the non-lemma form whose inflections we're being asked to -- determine. This normally comes from the page title or the value of |pagename=. local verb_form_of_form if source_template == "es-verb form of" then verb_form_of_form = args.pagename if not verb_form_of_form then if PAGENAME == "es-verb form of" and in_template_space() then verb_form_of_form = "ame" else verb_form_of_form = PAGENAME end end end local incorporated_headword_head_into_lemma = false if arg1:find("^<.*>$") then -- missing lemma if head:find(" ") then -- If multiword lemma, try to add arg spec after the first word. -- Try to preserve the brackets in the part after the verb, but don't do it -- if there aren't the same number of left and right brackets in the verb -- (which means the verb was linked as part of a larger expression). local refl_clitic_verb, post = rmatch(head, "^(.-)( .*)$") local left_brackets = rsub(refl_clitic_verb, "[^%[]", "") local right_brackets = rsub(refl_clitic_verb, "[^%]]", "") if #left_brackets == #right_brackets then arg1 = iut.remove_redundant_links(refl_clitic_verb) .. arg1 .. post incorporated_headword_head_into_lemma = true else -- Try again using the form without links. local linkless_head = m_links.remove_links(head) if linkless_head:find(" ") then refl_clitic_verb, post = rmatch(linkless_head, "^(.-)( .*)$") arg1 = refl_clitic_verb .. arg1 .. post else error("Unable to incorporate <...> spec into explicit head due to a multiword linked verb or " .. "unbalanced brackets; please include <> explicitly: " .. arg1) end end else -- Will be incorporated through `head` below in the call to normalize_all_lemmas(). incorporated_headword_head_into_lemma = true end end local parse_props = { parse_indicator_spec = parse_indicator_spec, allow_default_indicator = true, allow_blank_lemma = true, } local alternant_multiword_spec = iut.parse_inflected_text(arg1, parse_props) alternant_multiword_spec.pos = pos or "verbs" alternant_multiword_spec.args = args alternant_multiword_spec.source_template = source_template alternant_multiword_spec.verb_form_of_form = verb_form_of_form alternant_multiword_spec.incorporated_headword_head_into_lemma = incorporated_headword_head_into_lemma -- Now determine if we need to generate any double-combined forms, and if so, which clitics are involved. -- See the comment above the initialization of `verb_slot_double_combined_rows` above in add_slots(). if verb_form_of_form and rfind(verb_form_of_form, AV) then -- All double-clitic forms have an explicit accent, so we check for this. In addition, all double-clitic forms -- are of the form "(me|te|se|nos|os)(lo|la|le)s$". We have no alternations in Lua patterns, but we can exploit -- the similarity of the clitics in question. local single_comb_form, object_clitic = rmatch(verb_form_of_form, "^(.*)(l[aeo]s?)$") if single_comb_form then local personal_clitic = rmatch(single_comb_form, "^.*([mts]e)$") if not personal_clitic then personal_clitic = rmatch(single_comb_form, "^.-(n?os)$") end if personal_clitic then if personal_clitic == "nos" then -- "os" is a substring of "nos"; conceivably we could have a form ending in -n + os, and we don't -- know whether to interpret as -n + os or - + nos. alternant_multiword_spec.double_combined_forms_to_include = {{"nos", object_clitic}, {"os", object_clitic}} else if personal_clitic == "se" then -- Use the special "lxx" clitic for le/les when transformed into se in double-combined forms. personal_clitic = "lxx" end alternant_multiword_spec.double_combined_forms_to_include = {{personal_clitic, object_clitic}} end end end end normalize_all_lemmas(alternant_multiword_spec, pagename) detect_all_indicator_specs(alternant_multiword_spec) local inflect_props = { slot_list = alternant_multiword_spec.all_verb_slots, inflect_word_spec = conjugate_verb, -- We add links around the generated verbal forms rather than allow the entire multiword -- expression to be a link, so ensure that user-specified links get included as well. include_user_specified_links = true, } iut.inflect_multiword_or_alternant_multiword_spec(alternant_multiword_spec, inflect_props) -- Remove redundant brackets around entire forms. for slot, forms in pairs(alternant_multiword_spec.forms) do for _, form in ipairs(forms) do form.form = iut.remove_redundant_links(form.form) end end compute_categories_and_annotation(alternant_multiword_spec) if args.json and not from_headword and not from_verb_form_of then -- There is a circular reference in `base.alternant_multiword_spec`, which points back to top level. iut.map_word_specs(alternant_multiword_spec, function(base) base.alternant_multiword_spec = nil end) return require("Module:JSON").toJSON(alternant_multiword_spec) end return alternant_multiword_spec end -- Entry point for {{es-conj}}. Template-callable function to parse and conjugate a verb given -- user-specified arguments and generate a displayable table of the conjugated forms. function export.show(frame) local parent_args = frame:getParent().args local params = { [1] = {}, ["nocomb"] = {type = "boolean"}, ["noautolinktext"] = {type = "boolean"}, ["noautolinkverb"] = {type = "boolean"}, ["pagename"] = {}, -- for testing/documentation pages ["json"] = {type = "boolean"}, -- for bot use } local args = require("Module:parameters").process(parent_args, params) local alternant_multiword_spec = export.do_generate_forms(args, "es-conj") if type(alternant_multiword_spec) == "string" then -- JSON return value return alternant_multiword_spec end show_forms(alternant_multiword_spec) return make_table(alternant_multiword_spec) .. require("Module:utilities").format_categories(alternant_multiword_spec.categories, lang, nil, nil, force_cat) end return export mis6275cmafzurw70h9ahi5bo74tcyv มอดูล:decorations 828 315173 5775194 3139348 2026-10-14T04:29:09Z Octahedron80 267 Octahedron80 ย้ายหน้า [[มอดูล:pron qualifier]] ไปยัง [[มอดูล:decorations]]: การย้ายโดยเทคนิค 3139348 Scribunto text/plain local export = {} local labels_module = "Module:labels" local qualifier_module = "Module:qualifier" local references_module = "Module:references" local function track(page) require("Module:debug/track")("pron qualifier/" .. page) return true end --[==[ This function is used by any module that wants to add support for (some subset of) left and right regular and accent qualifiers, labels and references to a template that specifies a pronunciation or related property. It is currently used by [[Module:IPA]], [[Module:rhymes]], [[Module:hyphenation]], [[Module:homophones]] and various lang-specific modules such as [[Module:es-pronunc]] (for specifying pronunciation, rhymes, hyphenation, homophones and audio in {{tl|es-pr}}).It should potentially also be used in {{tl|audio}}. To reduce memory usage, the caller should check that any qualifiers exist before loading the module. `data` is a structure containing the following fields: * `q`: List of left regular qualifiers, each a string. * `qq`: List of right regular qualifiers, each a string. * `qualifiers`: List of qualifiers, each a string, for compatibility. If `qualifiers_right` is given, these are right qualifiers, otherwise left qualifiers. If both `qualifiers` and `q`/`qq` (depending on the value of `qualifiers_right`) are non-{nil}, `qualifiers` is ignored. * `qualifiers_right`: If specified, qualifiers in `qualifiers` are placed to the right, otherwise the left. See above. * `a`: List of left accent qualifiers, each a string. * `aa`: List of right accent qualifiers, each a string. * `l`: List of left labels, each a string. * `ll`: List of right labels, each a string. * `refs`: {nil} or a list of references or reference specs to add directly after the text; the value of a list item is either a string containing the reference text (typically a call to a citation template such as {{tl|cite-book}}, or a template wrapping such a call), or an object with fields `text` (the reference text), `name` (the name of the reference, as in {{cd|<nowiki><ref name="foo">...</ref></nowiki>}} or {{cd|<nowiki><ref name="foo" /></nowiki>}}) and/or `group` (the group of the reference, as in {{cd|<nowiki><ref name="foo" group="bar">...</ref></nowiki>}} or {{cd|<nowiki><ref name="foo" group="bar"/></nowiki>}}); this uses a parser function to format the reference appropriately and insert a footnote number that hyperlinks to the actual reference, located in the {{cd|<nowiki><references /></nowiki>}} section. * `lang`: Language object for accent qualifiers. * `text`: The text to wrap with qualifiers. *` raw`: Don't do any CSS wrapping of the formatted text. The order of qualifiers and labels, on both the left and right, is (1) labels, (2) accent qualifiers, (3) regular qualifiers. This goes in order of relative importance. ]==] function export.format_qualifiers(data) if not data.text then error("Missing `data.text`; did you try to pass `text` or `qualifiers_right` as separate params?") end if not data.lang then track("nolang") end local text = data.text -- Format the qualifiers and labels that go either before or after the main text. They are ordered as follows, on -- both the left and the right: (1) labels, (2) accent qualifiers, (3) regular qualifiers. This puts the different -- types of qualifiers/labels in order of relative importance. Return nil if no qualifiers or labels, otherwise -- a string containing all formatted qualifiers and labels surrounded by parens. local function format_qualifier_like(labels, accent_qualifiers, qualifiers) local has_qualifiers = qualifiers and qualifiers[1] local has_accent_qualifiers = accent_qualifiers and accent_qualifiers[1] local has_labels = labels and labels[1] if not has_qualifiers and not has_accent_qualifiers and not has_labels then return nil end local qualifier_like_parts = {} local function ins(part) table.insert(qualifier_like_parts, part) end local function format_label_like(labels, mode) return require(labels_module).show_labels { lang = data.lang, labels = labels, nocat = true, mode = mode, open = false, close = false, no_ib_content = true, no_track_already_seen = true, ok_to_destructively_modify = true, -- doesn't apply to `labels` raw = data.raw, } end local m_qualifier = require(qualifier_module) if has_labels then ins(format_label_like(labels)) end if has_accent_qualifiers then ins(format_label_like(accent_qualifiers, "accent")) end if has_qualifiers then ins(m_qualifier.format_qualifiers { qualifiers = qualifiers, open = false, close = false, no_ib_content = true, raw = data.raw, }) end local qualifier_inside local function wrap_qualifier_css(txt, suffix) if data.raw then return txt else return m_qualifier.wrap_qualifier_css(txt, suffix) end end if qualifier_like_parts[2] then qualifier_inside = table.concat(qualifier_like_parts, wrap_qualifier_css(",", "comma") .. " ") else qualifier_inside = qualifier_like_parts[1] end qualifier_like_parts = {} ins(wrap_qualifier_css("(", "brac")) ins(wrap_qualifier_css(qualifier_inside, "content")) ins(wrap_qualifier_css(")", "brac")) return table.concat(qualifier_like_parts) end if data.refs then text = text .. require(references_module).format_references(data.refs) end local leftq = format_qualifier_like(data.l, data.a, data.q or not data.qualifiers_right and data.qualifiers) local rightq = format_qualifier_like(data.ll, data.aa, data.qq or data.qualifiers_right and data.qualifiers) if leftq then text = leftq .. " " .. text end if rightq then text = text .. " " .. rightq end return text end return export 9z6ixjmla3cmwg4l89rkwuj7y1s06fz 5775196 5775194 2026-10-14T04:29:29Z Octahedron80 267 5775196 Scribunto text/plain local export = {} local gender_and_number_module = "Module:gender and number" local labels_module = "Module:labels" local qualifier_module = "Module:qualifier" local references_module = "Module:references" local function track(page) require("Module:debug/track")("decorations/" .. page) return true end --[==[ This function is used by any module that wants to add support for adding decorations (i.e. left and right regular and accent qualifiers, labels and references, and potentially other types of decorations in the future) to an item, where an "item" is any string of text that might want to be decorated. It is currently used for: # pronunciations and other pronunciation-related items, such as rhymes, hyphenations and homophones, as implemented in [[Module:IPA]], [[Module:rhymes]], [[Module:hyphenation]], [[Module:homophones]] and various language-specific modules such as [[Module:es-pronunc]]; # arbitrary links, as implemented in [[Module:links]]; # headwords, as implemented in [[Module:headword]]; # gender/number specs, as implemented in [[Module:gender and number]]; # *nyms of all sorts (e.g. synonyms, antonyms, hypernyms, hyponyms, coordinate terms, etc.) as implemented in [[Module:nyms]]; # affixes of all sorts, as implemented in [[Module:affix]]; # affix usage examples, as implemented in [[Module:affixusex]]; # column templates as such as {{tl|col}} and {{tl|col3}}, as implemented in [[Module:columns]]; # names (surnames, given names, patronymics, etc.) as implemented in [[Module:names]]; # object-usage qualifiers such as {{tl|+obj}}, as implemented in [[Module:object usage]]; # various inflection templates such as {{tl|ar-conj}} in [[Module:ar-verb]]; # demonyms, as implemented in [[Module:demonym]]; and several other places. Modules that implement templates that occur frequently on a page, such as [[Module:headword]] and [[Module:links]], should consider checking that any decorations exist before loading the module. This is less necessary for other, less-used templates, as this module is not very heavy and does appropriate checks itself to make sure that any decorations are present before taking action. `data` is a structure containing the following fields: * `q`: Optional list of left regular qualifiers, each a string. * `qq`: Optional list of right regular qualifiers, each a string. * `a`: Optional list of left accent qualifiers, each a string. * `aa`: Optional list of right accent qualifiers, each a string. * `l`: Optional list of left labels, each a string. * `ll`: Optional list of right labels, each a string. * `refs`: Optional list of references or reference specs to add directly after the text; the value of a list item is either a string containing the reference text (typically a call to a citation template such as {{tl|cite-book}}, or a template wrapping such a call), or an object with fields `text` (the reference text), `name` (the name of the reference, as in `<nowiki><ref name="foo">...</ref></nowiki>` or `<nowiki><ref name="foo" /></nowiki>`) and/or `group` (the group of the reference, as in `<nowiki><ref name="foo" group="bar">...</ref></nowiki>` or `<nowiki><ref name="foo" group="bar"/></nowiki>`); this uses a parser function to format the reference appropriately and insert a footnote number that hyperlinks to the actual reference, located in the `<nowiki><references /></nowiki>` section. * `genders`: Optional list of gender/number specs as accepted by [[Module:gender and number]]. * `lang`: Language object for accent qualifiers. * `text`: The text to wrap with qualifiers. *` raw`: Don't do any CSS wrapping of the formatted text. The order of qualifiers and labels, on both the left and right, is (1) labels, (2) accent qualifiers, (3) regular qualifiers. This goes in order of relative importance. References and genders go on the right, inside of labels and qualifiers, with references before genders. ]==] function export.format_decorations(data) if not data.text then error("Missing `data.text`; did you try to pass `text` as a separate param?") end if not data.lang then track("nolang") end local text = data.text -- Format the qualifiers and labels that go either before or after the main text. They are ordered as follows, on -- both the left and the right: (1) labels, (2) accent qualifiers, (3) regular qualifiers. This puts the different -- types of qualifiers/labels in order of relative importance. Return nil if no qualifiers or labels, otherwise -- a string containing all formatted qualifiers and labels surrounded by parens. local function format_qualifier_like(labels, accent_qualifiers, qualifiers) local has_qualifiers = qualifiers and qualifiers[1] local has_accent_qualifiers = accent_qualifiers and accent_qualifiers[1] local has_labels = labels and labels[1] if not has_qualifiers and not has_accent_qualifiers and not has_labels then return nil end local qualifier_like_parts = {} local function ins(part) table.insert(qualifier_like_parts, part) end local function format_label_like(labels, mode) return require(labels_module).show_labels { lang = data.lang, labels = labels, nocat = true, mode = mode, open = false, close = false, no_ib_content = true, no_track_already_seen = true, ok_to_destructively_modify = true, -- doesn't apply to `labels` raw = data.raw, } end local m_qualifier = require(qualifier_module) if has_labels then ins(format_label_like(labels)) end if has_accent_qualifiers then ins(format_label_like(accent_qualifiers, "accent")) end if has_qualifiers then ins(m_qualifier.format_qualifiers { qualifiers = qualifiers, open = false, close = false, no_ib_content = true, raw = data.raw, }) end local qualifier_inside local function wrap_qualifier_css(txt, suffix) if data.raw then return txt else return m_qualifier.wrap_qualifier_css(txt, suffix) end end if qualifier_like_parts[2] then qualifier_inside = table.concat(qualifier_like_parts, wrap_qualifier_css(",", "comma") .. " ") else qualifier_inside = qualifier_like_parts[1] end qualifier_like_parts = {} ins(wrap_qualifier_css("(", "brac")) ins(wrap_qualifier_css(qualifier_inside, "content")) ins(wrap_qualifier_css(")", "brac")) return table.concat(qualifier_like_parts) end if data.refs then text = text .. require(references_module).format_references(data.refs) end if data.genders and data.genders[1] then -- NOTE, format_genders() returns a second value (categories) but we ignore it. text = text .. "&nbsp;" .. require(gender_and_number_module).format_genders(data.genders, data.lang) end if data.qualifiers then -- FIXME: added 2026-09-18; consider removing eventually. error("`.qualifiers` is no longer supported; change the code to use `.q` or `.qq`") end local leftq = format_qualifier_like(data.l, data.a, data.q) local rightq = format_qualifier_like(data.ll, data.aa, data.qq) if leftq then text = leftq .. " " .. text end if rightq then text = text .. " " .. rightq end return text end function export.format_qualifiers(...) -- FIXME: Added 2026-09-17. Remove after a month or less. error("use format_decorations instead") end return export gvyopr8wgnkfw3arhfcjf23ibitjoha มอดูล:parameters/data 828 324678 5775231 5684268 2026-10-14T07:31:24Z Octahedron80 267 5775231 Scribunto text/plain local list_to_set = require("Module:table").listToSet local alias_of_2 = {alias_of = 2} local boolean = {type = "boolean"} local boolean_default_false = {type = "boolean", default = false} local list = {list = true} local list_allow_holes_separate_no_index = {list = true, allow_holes = true, separate_no_index = true} local no_parameters = {} local required = {required = true} local required_default_ = {required = true, default = ""} local required_lang_default_und = {required = true, type = "language", default = "und"} local type_labels = {type = "labels"} local type_qualifier = {type = "qualifier"} local type_references = {type = "references"} local m = {} -- [[Module:anchors]] m["anchor"] = { [1] = {required = true, list = true, disallow_holes = true}, } m["senseid"] = { [1] = required_lang_default_und, [2] = required_default_, id = alias_of_2, tag = {set = list_to_set{"li", "p"}, default = "li"}, } m["etymid"] = { [1] = required_lang_default_und, [2] = required_default_, id = alias_of_2, } -- [[Module:etymon]] m["etymon"] = { [1] = required_lang_default_und, [2] = {list = true, disallow_holes = true}, id = true, title = true, tree = boolean, text = true, exnihilo = boolean, etydate = true, doublet = {sublist = "comma without whitespace"}, rfe = true, etystub = true, nl = boolean, pos = true, notree = boolean_default_false, nocat = boolean_default_false, nodot = boolean_default_false, dot = true, json = boolean_default_false, } -- [[Module:transclude]] m["transclude"] = { [1] = {required = true, type = "language"}, [2] = {list = true, required = true}, id = true, sort = true, nogloss = boolean_default_false, no_truncate_gloss = boolean, include_place_extra_info = boolean, place_translation_follows = boolean, place_addl = true, lb = true, nolb = true, nocat = boolean, to = boolean, t = list, indent = true, dot = boolean, pagename = true, } -- [[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, l = type_labels, ll = type_labels, q = type_qualifier, qq = type_qualifier, ref = type_references, } m["t-needed"] = { [1] = required_lang_default_und, [2] = {set = list_to_set{"usex", "quote"}}, nocat = boolean, sort = true, } m["trans-top"] = { [1] = true, id = true, ["column-width"] = 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"] = no_parameters m["trans-see"] = { [1] = required, [2] = list, id = list_allow_holes_separate_no_index, } m["translation subpage"] = no_parameters 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 5s1ntnu4nh24xfe4y9oqjeouq18285i มอดูล:Phli-translit 828 330926 5775259 1759991 2026-10-14T09:39:19Z Octahedron80 267 5775259 Scribunto text/plain local export = {} local function track(page) return require("Module:debug/track")("Phli-translit/" .. page) end local tt = { ["𐭠"] = "อ", -- aleph ["𐭡"] = "บ", -- beth ["𐭢"] = "ค", -- gimil ["𐭣"] = "ด", -- daleth ["𐭤"] = "ฮ", -- he ["𐭥"] = "อฺ", -- waw-ayin-resh ["𐭦"] = "ซ", -- zayin ["𐭧"] = "ฮฺ", -- heth ["𐭨"] = "ฏ", -- teth ["𐭩"] = "ย", -- yodh ["𐭪"] = "ก", -- kaph ["𐭫"] = "ล", -- lamedh ["𐭬"] = "ม", -- mem-qoph ["𐭭"] = "น", -- nun ["𐭮"] = "ส", -- samekh ["𐭯"] = "พ", -- pe ["𐭰"] = "ศ", -- sadhe ["𐭱"] = "ช", -- shin ["𐭲"] = "ต", -- taw } local numbers = { ["𐭸"] = 1, ["𐭹"] = 2, ["𐭺"] = 3, ["𐭻"] = 4, ["𐭼"] = 10, ["𐭽"] = 20, ["𐭾"] = 100, ["𐭿"] = 1000, } function export.convert_numbers(numeric_str) local total = 0 for c in mw.ustring.gmatch(numeric_str, ".") do total = total + numbers[c] end return "[" .. total .. "]" end function export.tr(text, lang, sc) -- If the script is not Phli, do not transliterate if sc ~= "Phli" then return end -- Tracking page to catch automatic transcriptions of waw-ayin-resh and mem-qoph -- [[Special:WhatLinksHere/Wiktionary:Tracking/Phli-translit/ambiguous-characters]] if mw.ustring.match(text, "[𐭬𐭥]") then track("ambiguous-characters") end if mw.ustring.match(text, "[𐭠-𐭿]") then text = mw.ustring.gsub(text, "[𐭸-𐭿]+", export.convert_numbers) end -- Transliterate characters text = mw.ustring.gsub(text, ".", tt) return text end return export 0nbfr6dhei3fybfe08wt5q0dndtd4cl มอดูล:Mani-translit 828 428436 5775303 2022759 2026-10-14T10:45:05Z Octahedron80 267 5775303 Scribunto text/plain local export = {} local tt = { ["𐫀"] = "อ", -- aleph ["𐫁"] = "บ", -- beth ["𐫂"] = "บฺ", -- bheth ["𐫃"] = "ค", -- gimel ["𐫄"] = "ฆ", -- ghimel ["𐫅"] = "ด", -- daleth ["𐫆"] = "ฮ", -- he ["𐫇"] = "ว", -- waw ["𐫈"] = "วฺ", -- ud (conjunction) ["𐫉"] = "ซ", -- zayin ["𐫊"] = "ซฺ", -- zhayin ["𐫋"] = "จ", -- jayin ["𐫌"] = "จฺ", -- jhayin ["𐫍"] = "ฮฺ", -- heth ["𐫎"] = "ฏ", -- teth ["𐫏"] = "ย", -- yodh ["𐫐"] = "ก", -- kaph ["𐫑"] = "ข", -- xaph ["𐫒"] = "ขฺฺ", -- khaph (พินทุสองจุด) ["𐫓"] = "ล", -- lamedh ["𐫔"] = "ดฺ", -- dhamedh ["𐫕"] = "ตฺ", -- thamedh ["𐫖"] = "ม", -- mem ["𐫗"] = "น", -- nun ["𐫘"] = "ส", -- samekh ["𐫙"] = "อฺ", -- ayin ["𐫚"] = "อฺฺ", -- aayin (พินทุสองจุด) ["𐫛"] = "พ", -- pe ["𐫜"] = "ฟ", -- fe ["𐫝"] = "ศ", -- sadhe ["𐫞"] = "กฺ", -- qoph ["𐫟"] = "ขฺ", -- xoph ["𐫠"] = "กฺฺ", -- qhoph (พินทุสองจุด) ["𐫡"] = "ร", -- resh ["𐫢"] = "ช", -- shin ["𐫣"] = "ศฺ", -- sshin ["𐫤"] = "ต", -- taw ["𐫥"] = "~", -- abbreviation mark above ["𐫦"] = "~", -- abbreviation mark below ["𐫫"] = "[1]", -- one ["𐫬"] = "[5]", -- five ["𐫭"] = "[10]", -- ten ["𐫮"] = "[20]", -- twenty ["𐫯"] = "[100]", -- one hundred } function export.tr(text, lang, sc) -- If the script is not Mani, do not transliterate if sc ~= "Mani" then return end -- Transliterate characters text = mw.ustring.gsub(text, '.', tt) return text end return export gaq6a4czz4niz939k5n0wiyajkn9dhp คุยกับผู้ใช้:Jamnanja 3 432802 5775172 5775164 2026-10-14T01:54:11Z Octahedron80 267 /* ขอคำแนะนำเรื่องการเพิ่มอรรถาภิธานคำอุปมาอุปไมย */ ตอบกลับ 5775172 wikitext text/x-wiki {{Template:Welcome|realName=|name=Jamnanja}} -- [[ผู้ใช้:New user message|New user message]] ([[คุยกับผู้ใช้:New user message|คุย]]) 20:54, 31 สิงหาคม 2567 (+07) == ขอคำแนะนำเรื่องการเพิ่มอรรถาภิธานคำอุปมาอุปไมย == สวัสดีครับ คุณ Jamnanja คือผมมีความต้องการที่จะเพิ่มอรรถาภิธานให้กับคำจำพวกอุปมาอุปไมย ตัวอย่างเช่น เหมือน ดั่ง เฉก ประหนึ่ง ผมกำลังสองจิตสองใจว่าผมควรเพิ่มอรรถาภิธานในรูปแบบไหน 1. ทำเป็นหัวข้อย่อยในหน้าของ เหมือน โดยใช้ Template:Column หรือ Template:synonyms 2.ทำเป็นหน้าอรรถาภิธาน 1 หน้าแยกไปเลย [[ผู้ใช้:Jamnanja|Jamnanja]] ([[คุยกับผู้ใช้:Jamnanja|คุย]]) 23:33, 12 ตุลาคม 2569 (+07) :เอาแบบง่าย ๆ นะครับ ถ้ามีไม่ถึง 5 คำ แทรก <nowiki>{{syn|th|...}}</nowiki> ไว้กับความหมายเลย ถ้ามีมากกว่านั้นก็ควรสร้างหน้า ''อรรถาภิธาน:...'' พอเสร็จแล้วก็แทรก <nowiki>{{syn|th|อรรถาภิธาน:...}}</nowiki> ไว้กับความหมาย เช่นเดียวกัน [[ผู้ใช้:Octahedron80|Octahedron80]] ([[คุยกับผู้ใช้:Octahedron80|คุย]]) 08:54, 14 ตุลาคม 2569 (+07) p37po4tevq8xp5rfjfti6jj0dt6le9h 5775173 5775172 2026-10-14T01:55:43Z Octahedron80 267 /* ขอคำแนะนำเรื่องการเพิ่มอรรถาภิธานคำอุปมาอุปไมย */ 5775173 wikitext text/x-wiki {{Template:Welcome|realName=|name=Jamnanja}} -- [[ผู้ใช้:New user message|New user message]] ([[คุยกับผู้ใช้:New user message|คุย]]) 20:54, 31 สิงหาคม 2567 (+07) == ขอคำแนะนำเรื่องการเพิ่มอรรถาภิธานคำอุปมาอุปไมย == สวัสดีครับ คุณ Jamnanja คือผมมีความต้องการที่จะเพิ่มอรรถาภิธานให้กับคำจำพวกอุปมาอุปไมย ตัวอย่างเช่น เหมือน ดั่ง เฉก ประหนึ่ง ผมกำลังสองจิตสองใจว่าผมควรเพิ่มอรรถาภิธานในรูปแบบไหน 1. ทำเป็นหัวข้อย่อยในหน้าของ เหมือน โดยใช้ Template:Column หรือ Template:synonyms 2.ทำเป็นหน้าอรรถาภิธาน 1 หน้าแยกไปเลย [[ผู้ใช้:Jamnanja|Jamnanja]] ([[คุยกับผู้ใช้:Jamnanja|คุย]]) 23:33, 12 ตุลาคม 2569 (+07) :เอาแบบง่าย ๆ นะครับ ถ้ามีไม่ถึง 5 คำ แทรก <nowiki>{{syn|th|...}}</nowiki> ไว้กับความหมายเลย (คำตรงข้าม <nowiki>{{ant|th|...}}</nowiki> ก็ทำได้เหมือนกัน) ถ้ามีมากกว่านั้นก็ควรสร้างหน้า ''อรรถาภิธาน:...'' พอเสร็จแล้วก็แทรก <nowiki>{{syn|th|อรรถาภิธาน:...}}</nowiki> ไว้กับความหมาย เช่นเดียวกัน [[ผู้ใช้:Octahedron80|Octahedron80]] ([[คุยกับผู้ใช้:Octahedron80|คุย]]) 08:54, 14 ตุลาคม 2569 (+07) 8sugi6xpjkt91cqqt1swcpun6opcdta 5775174 5775173 2026-10-14T01:56:03Z Octahedron80 267 /* ขอคำแนะนำเรื่องการเพิ่มอรรถาภิธานคำอุปมาอุปไมย */ 5775174 wikitext text/x-wiki {{Template:Welcome|realName=|name=Jamnanja}} -- [[ผู้ใช้:New user message|New user message]] ([[คุยกับผู้ใช้:New user message|คุย]]) 20:54, 31 สิงหาคม 2567 (+07) == ขอคำแนะนำเรื่องการเพิ่มอรรถาภิธานคำอุปมาอุปไมย == สวัสดีครับ คุณ Jamnanja คือผมมีความต้องการที่จะเพิ่มอรรถาภิธานให้กับคำจำพวกอุปมาอุปไมย ตัวอย่างเช่น เหมือน ดั่ง เฉก ประหนึ่ง ผมกำลังสองจิตสองใจว่าผมควรเพิ่มอรรถาภิธานในรูปแบบไหน 1. ทำเป็นหัวข้อย่อยในหน้าของ เหมือน โดยใช้ Template:Column หรือ Template:synonyms 2.ทำเป็นหน้าอรรถาภิธาน 1 หน้าแยกไปเลย [[ผู้ใช้:Jamnanja|Jamnanja]] ([[คุยกับผู้ใช้:Jamnanja|คุย]]) 23:33, 12 ตุลาคม 2569 (+07) :เอาแบบง่าย ๆ นะครับ ถ้ามีไม่ถึง 5 คำ แทรก <nowiki>{{syn|th|...}}</nowiki> ไว้กับความหมายเลย (คำตรงข้าม <nowiki>{{ant|th|...}}</nowiki> ก็ทำได้เหมือนกัน) ถ้ามีมากกว่านั้นก็ควรสร้างหน้า ''อรรถาภิธาน:...'' พอเสร็จแล้วก็แทรก <nowiki>{{syn|th|อรรถาภิธาน:...}}</nowiki> ไว้กับความหมาย ในแบบเดียวกัน [[ผู้ใช้:Octahedron80|Octahedron80]] ([[คุยกับผู้ใช้:Octahedron80|คุย]]) 08:54, 14 ตุลาคม 2569 (+07) p56jv7o7hgkz2veos5vxqymlf46mtf9 AABP 0 572404 5775207 5376298 2026-10-14T05:22:06Z Octahedron80 267 /* ภาษาอังกฤษ */ เก็บกวาด 5775207 wikitext text/x-wiki == ภาษาอังกฤษ == === คำวิสามานยนาม === {{en-proper noun}} # {{initialism of|en|[[association|Association]] of [[area|Area]] [[business|Business]] [[publication|Publications]]}}. # {{initialism of|en|[[Australian]] association|Association of business|Business publication|Publications}}. # {{initialism of|en|aptitude assessment battery program}}. === คำสลับอักษร === * {{anagrams|en|a=aabp|ABAP|PABA}} {{C|en|Organizations}} ecd0eaazfjl1xt3pv1mydh3h3ri4i5l 5775208 5775207 2026-10-14T05:22:22Z Octahedron80 267 /* คำวิสามานยนาม */ 5775208 wikitext text/x-wiki == ภาษาอังกฤษ == === คำวิสามานยนาม === {{en-proper noun}} # {{initialism of|en|[[association|Association]] of [[area|Area]] [[business|Business]] [[publication|Publications]]}}. # {{initialism of|en|[[Australian]] [[association|Association]] of [[business|Business]] [[publication|Publications]]}} # {{initialism of|en|aptitude assessment battery program}}. === คำสลับอักษร === * {{anagrams|en|a=aabp|ABAP|PABA}} {{C|en|Organizations}} ry6vp3gnatrawsqc8i93znh5xpe5eai 5775209 5775208 2026-10-14T05:22:34Z Octahedron80 267 /* ภาษาอังกฤษ */ 5775209 wikitext text/x-wiki == ภาษาอังกฤษ == === คำวิสามานยนาม === {{en-proper noun}} # {{initialism of|en|[[association|Association]] of [[area|Area]] [[business|Business]] [[publication|Publications]]}} # {{initialism of|en|[[Australian]] [[association|Association]] of [[business|Business]] [[publication|Publications]]}} # {{initialism of|en|aptitude assessment battery program}} === คำสลับอักษร === * {{anagrams|en|a=aabp|ABAP|PABA}} {{C|en|Organizations}} h40p5pdxdu7zxeam293psc3z2b55sew มอดูล:pages 828 572831 5775191 5683757 2026-10-14T04:18:54Z Octahedron80 267 5775191 Scribunto text/plain local export = {} local string_utilities_module = "Module:string utilities" local concat = table.concat local find = string.find local format = string.format local getmetatable = getmetatable local get_current_section -- defined below local get_namespace_shortcut -- defined below local get_pagetype -- defined below local gsub = string.gsub local insert = table.insert local is_internal_title -- defined below local is_title -- defined below local lower = string.lower local match = string.match local new_title = mw.title.new local require = require local sub = string.sub local title_equals = mw.title.equals local tonumber = tonumber local type = type local ufind = mw.ustring.find local unstrip_nowiki = mw.text.unstripNoWiki --[==[ Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==] local function decode_entities(...) decode_entities = require(string_utilities_module).decode_entities return decode_entities(...) end local function ulower(...) ulower = require(string_utilities_module).lower return ulower(...) end local function trim(...) trim = require(string_utilities_module).trim return trim(...) 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 current_frame local function get_current_frame() current_frame, get_current_frame = mw.getCurrentFrame(), nil return current_frame end local parent_frame local function get_parent_frame() parent_frame, get_parent_frame = (current_frame or get_current_frame()):getParent(), nil return parent_frame end local namespace_shortcuts local function get_namespace_shortcuts() namespace_shortcuts, get_namespace_shortcuts = { [4] = "WT", [10] = "T", [14] = "CAT", [100] = "AP", [110] = "WS", [118] = "RC", [828] = "MOD", }, nil return namespace_shortcuts end do local transcluded --[==[ Returns {true} if the current {{tl|#invoke:}} is being transcluded, or {false} if not. If the current {{tl|#invoke:}} is part of a template, for instance, this template will therefore return {true}. Note that if a template containing an {{tl|#invoke:}} is used on its own page (e.g. to display a demonstration), this function is still able to detect that this is transclusion. This is an improvement over the other method for detecting transclusion, which is to check the parent frame title against the current page title, which fails to detect transclusion in that instance.]==] function export.is_transcluded() if transcluded == nil then transcluded = (parent_frame or get_parent_frame()) and parent_frame:preprocess("<includeonly>1</includeonly>") == "1" or false end return transcluded end end do local preview --[==[ Returns {true} if the page is currently being viewed in preview, or {false} if not.]==] function export.is_preview() if preview == nil then preview = (current_frame or get_current_frame()):preprocess("{{REVISIONID}}") == "" end return preview end end --[==[ Returns {true} if the input is a title object, or {false} if not. This therefore '''includes''' external title objects (i.e. those for pages on other wikis), such as [[w:Example]], unlike `is_internal_title` below.]==] function export.is_title(val) if not (val and type(val) == "table") then return false end local mt = getmetatable(val) -- There's no foolproof method for checking for a title object, but the -- __eq metamethod should be mw.title.equals unless the object has been -- seriously messed around with. return mt and type(mt) == "table" and getmetatable(mt) == nil and mt.__eq == title_equals and true or false end is_title = export.is_title --[==[ Returns {true} if the input is an internal title object, or {false} if not. An internal title object is a title object for a page on this wiki, such as [[example]]. This therefore '''excludes''' external title objects (i.e. those for pages on other wikis), such as [[w:Example]], unlike `is_title` above.]==] function export.is_internal_title(title) -- Note: Mainspace titles starting with "#" should be invalid, but a bug in mw.title.new and mw.title.makeTitle means a title object is returned that has the empty string for prefixedText, so they need to be filtered out. return is_title(title) and #title.prefixedText > 0 and #title.interwiki == 0 end is_internal_title = export.is_internal_title --[==[ Returns {true} if the input string is a valid link target, or {false} if not. This therefore '''includes''' link targets to other wikis, such as [[w:Example]], unlike `is_valid_page_name` below.]==] function export.is_valid_link_target(target) local target_type = type(target) if target_type == "string" then return is_title(new_title(target)) end error(format("bad argument #1 to 'is_valid_link_target' (string expected, got %s)", target_type), 2) end --[==[ Returns {true} if the input string is a valid page name on this wiki, or {false} if not. This therefore '''excludes''' page names on other wikis, such as [[w:Example]], unlike `is_valid_link_target` above.]==] function export.is_valid_page_name(name) local name_type = type(name) if name_type == "string" then return is_internal_title(new_title(name)) end error(format("bad argument #1 to 'is_valid_page_name' (string expected, got %s)", name_type), 2) end --[==[ Given a title object, returns a full link target which will always unambiguously link to it. For instance, the input {"foo"} (for the page [[foo]]) returns {":foo"}, as a leading colon always refers to mainspace, even when other namespaces might be assumed (e.g. when transcluding using `{{ }}` syntax). If `shortcut` is set, then the returned target will use the namespace shortcut, if any; for example, the title for `Template:foo` would return {"T:foo"} instead of {"Template:foo"}.]==] function export.get_link_target(title, shortcut) if not is_title(title) then error(format("bad argument #1 to 'is_valid_link_target' (title object expected, got %s)", type(title))) elseif title.interwiki ~= "" then return title.fullText elseif shortcut then local fragment = title.fragment if fragment == "" then return get_namespace_shortcut(title) .. ":" .. title.text end return get_namespace_shortcut(title) .. ":" .. title.text .. "#" .. fragment elseif title.namespace == 0 then return ":" .. title.fullText end return title.fullText end do local function find_sandbox(text) return find(text, "^ผู้ใช้:.") or find(lower(text), "sandbox", 1, true) end local function get_transclusion_subtypes(title, main_type, documentation, page_suffix) local text, subtypes = title.text, {main_type} -- Any template/module with "sandbox" in the title. These are impossible -- to screen for more accurately, as there's no consistent pattern. Also -- any user sandboxes in the form (e.g.) "Template:User:...". local sandbox = find_sandbox(text) if sandbox then insert(subtypes, "sandbox") end -- Any template/module testcases (which can be labelled and/or followed -- by further subpages). local testcase = find(text, "./[Tt]estcases?%f[%L]") if testcase then -- Order "testcase" and "sandbox" based on where the patterns occur -- in the title. local n = sandbox and sandbox < testcase and 3 or 2 insert(subtypes, n, "testcase") end -- Any template/module documentation pages. if documentation then insert(subtypes, "documentation") end local final = subtypes[#subtypes] if not (final == main_type and not page_suffix or final == "sandbox") then insert(subtypes, "page") end return concat(subtypes, " ") end local function get_snippet_subtypes(title, main_type, documentation) local ns = title.namespace return get_transclusion_subtypes(title, ( ns == 2 and "user " or ns == 8 and match(title.text, "^Gadget-.") and "gadget " or "" ) .. main_type, documentation) end --[==[ Returns the page type of the input title object in a format which can be used in running text.]==] function export.get_pagetype(title) if not is_internal_title(title) then error(mw.dumpObject(title.fullText) .. " is not a valid page name.") end -- If possibly a documentation page, get the base title and set the -- `documentation` flag. local content_model, text, documentation = title.contentModel if content_model == "wikitext" then text = title.text if title.isSubpage and title.subpageText == "documentation" then local base_title = title.basePageTitle if base_title then title, content_model, text, documentation = base_title, base_title.contentModel, base_title.text, true end end end -- Content models have overriding priority, as they can appear in -- nonstandard places due to page content model changes. if content_model == "css" or content_model == "sanitized-css" then return get_snippet_subtypes(title, "stylesheet", documentation) elseif content_model == "javascript" then return get_snippet_subtypes(title, "script", documentation) elseif content_model == "json" then return get_snippet_subtypes(title, "JSON data", documentation) elseif content_model == "MassMessageListContent" then return get_snippet_subtypes(title, "mass message delivery list", documentation) -- Modules. elseif content_model == "Scribunto" then return get_transclusion_subtypes(title, "module", documentation, false) elseif content_model == "text" then return "page" -- ??? -- Otherwise, the content model is "wikitext", so check namespaces. elseif title.isTalkPage then return "talk page" end local ns = title.namespace -- Main namespace. if ns == 0 then return "entry" -- Wiktionary: elseif ns == 4 then return find_sandbox(title.text) and "sandbox" or "project page" -- Template: elseif ns == 10 then return get_transclusion_subtypes(title, "template", documentation, false) end -- Convert the namespace to lowercase, unless it contains a capital -- letter after the initial letter (e.g. MediaWiki, TimedText). Also -- normalize any underscores. local ns_text = gsub(title.nsText, "_", " ") if ufind(ns_text, "^%U*$", 2) then ns_text = ulower(ns_text) end -- User: if ns == 2 then return ns_text .. " " .. (title.isSubpage and "subpage" or "page") -- Category: and Appendix: elseif ns == 14 or ns == 100 then return ns_text -- Thesaurus: and Reconstruction: elseif ns == 110 or ns == 118 then return ns_text .. " entry" end return ns_text .. " page" end get_pagetype = export.get_pagetype end --[==[ Returns {true} if the input title object is for a content page, or {false} if not. A content page is a page that is considered part of the dictionary itself, and excludes pages for discussion, administration, maintenance etc.]==] function export.is_content_page(title) if not is_internal_title(title) then error(mw.dumpObject(title.fullText) .. " is not a valid page name.") end local ns = title.namespace -- (main), Appendix, Thesaurus, Citations, Reconstruction. return (ns == 0 or ns == 100 or ns == 110 or ns == 114 or ns == 118) and title.contentModel == "wikitext" end --[==[ Returns {true} if the input title object is for a documentation page, or {false} if not.]==] function export.is_documentation(title) return match(get_pagetype(title), "%f[%w]documentation%f[%W]") and true or false end --[==[ Returns {true} if the input title object is for a sandbox, or {false} if not. By default, sandbox documentation pages are excluded, but this can be overridden with the `include_documentation` parameter.]==] function export.is_sandbox(title, include_documentation) local pagetype = get_pagetype(title) return match(pagetype, "%f[%w]sandbox%f[%W]") and ( include_documentation or not match(pagetype, "%f[%w]documentation%f[%W]") ) and true or false end --[==[ Returns {true} if the input title object is for a testcase page, or {false} if not. By default, testcase documentation pages are excluded, but this can be overridden with the `include_documentation` parameter.]==] function export.is_testcase_page(title, include_documentation) local pagetype = get_pagetype(title) return match(pagetype, "%f[%w]testcase%f[%W]") and ( include_documentation or not match(pagetype, "%f[%w]documentation%f[%W]") ) and true or false end --[==[ Returns the namespace shortcut for the input title object, or else the namespace text. For example, a `Template:` title returns {"T"}, a `Module:` title returns {"MOD"}, and a `User:` title returns {"User"}.]==] function export.get_namespace_shortcut(title) return (namespace_shortcuts or get_namespace_shortcuts())[title.namespace] or title.nsText end get_namespace_shortcut = export.get_namespace_shortcut do local function check_level(lvl) if type(lvl) ~= "number" then error("Heading levels must be numbers.") elseif lvl < 1 or lvl > 6 or lvl % 1 ~= 0 then error("Heading levels must be integers between 1 and 6.") end return lvl end --[==[ A helper function which iterates over the headings in `text`, which should be the content of a page or (main) section. Each iteration returns three values: `sec` (the section title), `lvl` (the section level) and `loc` (the index of the section in the given text, from the first equals sign). The section title will be automatically trimmed, and any HTML entities will be resolved. The optional parameter `a` (which should be an integer between 1 and 6) can be used to ensure that only headings of the specified level are iterated over. If `b` is also given, then they are treated as a range. The optional parameters `a` and `b` can be used to specify a range, so that only headings with levels in that range are returned.]==] local function find_headings(text, a, b) a = a and check_level(a) or nil b = b and check_level(b) or a or nil local start, loc, lvl, sec = 1 return function() repeat loc, lvl, sec, start = match(text, "()%f[^%z\n](==?=?=?=?=?)([^\n]+)%2[\t ]*%f[%z\n]()", start) lvl = lvl and #lvl until not (sec and a) or (lvl >= a and lvl <= b) return sec and trim(decode_entities(sec)) or nil, lvl, loc end end local function _get_section(content, name, level) if not (content and name) then return nil elseif find(name, "\n", 1, true) then error("Heading name cannot contain a newline.") end level = level and check_level(level) or nil name = trim(decode_entities(name)) local start for sec, lvl, loc in find_headings(content, level and 1 or nil, level) do if start and lvl <= level then return sub(content, start, loc - 1) elseif not start and (not level or lvl == level) and sec == name then start, level = loc, lvl end end return start and sub(content, start) end --[==[ A helper function to return the content of a page section. `content` is raw wikitext, `name` is the requested section, and `level` is an optional parameter that specifies the required section heading level. If `level` is not supplied, then the first section called `name` is returned. `name` can either be a string or table of section names. If a table, each name represents a section that has the next as a subsection. For example, { {"Spanish", "Noun"}} will return the first matching section called "Noun" under a section called "Spanish". These do not have to be at adjacent levels ("Noun" might be L4, while "Spanish" is L2). If `level` is given, it refers to the last name in the table (i.e. the name of the section to be returned). The returned section includes all of its subsections. If no matching section is found, return {nil}.]==] function export.get_section(content, names, level) if type(names) ~= "table" then return _get_section(content, names, level) end local i = 1 local name = names[i] if not name then error("Must specify at least 1 section.") end while true do local nxt_i = i + 1 local nxt = names[nxt_i] if nxt == nil then return _get_section(content, name, level) end content = _get_section(content, name) if content == nil then return nil elseif i == 6 then error("Not possible specify more than 6 sections: headings only go up to level 6.") end i = nxt_i name = names[i] end return content end end --[==[ Convert a physical pagename (or the current pagename, if {nil} is passed in) to its logical equivalent, but only if the physical pagename refers to one of the splits of a mammoth page such as [[a]]. Otherwise this simply returns the subpage (the part after the last slash in namespace other than the main one, otherwise the same as passed in), unless `include_base` is specified, in which case the return value will be the whole pagename minus the namespace, including the base (the part before the final slash). FIXME: This should be augmented with logic to handle unsupported titles, which is currently in process_page() in [[Module:headword/page]], so that it is a general physical-to-logical conversion function. Examples: * {physical_to_logical_pagename_if_mammoth("a")} → {"a"} * {physical_to_logical_pagename_if_mammoth("a/languages A to L")} → {"a"} (since [[a]] is marked as a mammoth page in [[Module:links/data]]) * {physical_to_logical_pagename_if_mammoth("50/50")} → {"50/50"} (since [[50/50]] is in the main namespace) * {physical_to_logical_pagename_if_mammoth("Appendix:Lojban/a")} → "a" * {physical_to_logical_pagename_if_mammoth("Appendix:Lojban/a", true)} → "Lojban/a" * {physical_to_logical_pagename_if_mammoth("Reconstruction:Proto-Slavic/a")} → "a" * {physical_to_logical_pagename_if_mammoth("Reconstruction:Proto-Slavic/a", true)} → "Proto-Slavic/a" * {physical_to_logical_pagename_if_mammoth("User:Example/sandbox/foo")} → "foo" * {physical_to_logical_pagename_if_mammoth("User:Example/sandbox/foo", true)} → "Example/sandbox/foo" ]==] function export.physical_to_logical_pagename_if_mammoth(title, include_base) if title == nil then title = mw.title.getCurrentTitle() elseif not is_title(title) then title = mw.title.new(title) end if title.nsText == "" then -- Formerly we checked for the specific known subpages of a given mammoth split page, e.g. we would convert -- [[a/languages M to Z]] to [[a]] assuming that [[a/languages M to Z]] was one of the splits, but not -- [[a/languages N to Z]]. To simplify this, we just convert anything with the right mammoth split page format -- on the assumption that it's unlikely we will ever have a legitimate non-mammoth-split pagename of this sort. local pagename = title.text local mammoth_root_page = pagename:match("^(.*)/languages [A-Z] to [A-Z]$") if mammoth_root_page then pagename = mammoth_root_page end return pagename elseif include_base then return title.text else return title.subpageText end end --[==[ Obsolete name for `physical_to_logical_pagename_if_mammoth`. FIXME: Replace all uses and remove. ]==] function export.safe_page_name(title) return export.physical_to_logical_pagename_if_mammoth(title) end do local current_section --[==[ A function which returns the number of the page section which contains the current {#invoke}, or {0} if the section can't be determined.]==] function export.get_current_section() if current_section ~= nil then return current_section end local frame = parent_frame or get_parent_frame() while frame do if find(frame:getTitle(), "^มอดูล:") then current_section = 0 return 0 end frame = frame:getParent() end local extension_tag = (current_frame or get_current_frame()).extensionTag -- We determine the section via the heading strip marker count, since they're numbered sequentially, but the only way to do this is to generate a fake heading via frame:preprocess(). The native parser assigns each heading a unique marker, but frame:preprocess() will return copies of older markers if the heading is identical to one further up the page, so the fake heading has to be unique to the page. The best way to do this is to feed it a heading containing a nowiki marker (which we will need later), since those are always unique. local nowiki_marker = extension_tag(current_frame, "nowiki") -- Note: heading strip markers have a different syntax to the ones used for tags. local h = tonumber(match( current_frame:preprocess("=" .. nowiki_marker .. "="), "\127'\"`UNIQ%-%-h%-(%d+)%-%-QINU`\"'\127" )) -- For some reason, [[Special:ExpandTemplates]] doesn't generate a heading strip marker, so if that happens we simply abort early. if not h then return 0 end -- The only way to get the section number is to increment the heading count, so we store the offset in nowiki strip markers which can be retrieved by procedurally unstripping nowiki markers, counting backwards until we find a match. local n, offset = tonumber(match( nowiki_marker, "\127'\"`UNIQ%-%-nowiki%-([%dA-F]+)%-QINU`\"'\127" ), 16) while not offset and n > 0 do n = n - 1 offset = match( unstrip_nowiki(format("\127'\"`UNIQ--nowiki-%08X-QINU`\"'\127", n)), "^HEADING\1(%d+)" -- Prefix "HEADING\1" prevents collisions. ) end offset = offset and (offset + 1) or 0 extension_tag(current_frame, "nowiki", "HEADING\1" .. offset) current_section = h - offset return current_section end get_current_section = export.get_current_section end do local L2_sections, current_L2 local function get_L2_sections() L2_sections, get_L2_sections = mw.loadData("Module:headword/data").page.L2_sections, nil return L2_sections end --[==[ A function which returns the name of the L2 language section which contains the current {#invoke}.]==] function export.get_current_L2() if current_L2 ~= nil then return current_L2 or nil -- Return nil if current_L2 is false (i.e. there's no L2). end local section = get_current_section() while section > 0 do local L2 = (L2_sections or get_L2_sections())[section] if L2 then current_L2 = L2 return L2 end section = section - 1 end current_L2 = false return nil end end return export kds3syxb29k2fww7u6j9578diywe95e सैकंड 0 573461 5775289 5685218 2026-10-14T10:19:48Z स्वर्ण 18519 5775289 wikitext text/x-wiki == ภาษาฮินดี == {{wikipedia|सैकण्ड|lang=hi}} === รูปแบบอื่น === * {{alter|hi|सैकण्ड}} === รากศัพท์ === {{bor+|hi|en|second}} === การออกเสียง === * {{hi-IPA}} === คำนาม === {{hi-noun|m|ur=سیکنڈ}} # [[วินาที]] #: {{syn|hi|सानिया}} ==== การผันรูป ==== {{hi-ndecl|<M>}} ==== ดูเพิ่ม ==== * {{รายชื่อ:หน่วยเวลา/hi}} {{topics|hi|เวลา}} k56krmx50yudatz91gotw0zg4xtvzsk मिनट 0 573462 5775294 5685219 2026-10-14T10:25:53Z स्वर्ण 18519 5775294 wikitext text/x-wiki == ภาษาฮินดี == {{wp|lang=hi}} === รากศัพท์ === {{bor+|hi|en|minute}} === การออกเสียง === * {{hi-IPA}} === คำนาม === {{hi-noun|m|ur=منٹ}} # [[นาที]] #: {{syn|hi|दक़ीक़ा}} #: {{uxa|hi|मैं दो '''मिनट''' में आ रहा हूँ।|I'm coming in two '''minutes'''.}} ==== การผันรูป ==== {{hi-ndecl|<M>}} ==== ดูเพิ่ม ==== * {{รายชื่อ:หน่วยเวลา/hi}} {{topics|hi|เวลา}} mbbvolt6ati7tl2momonbwxn4dh6ck5 มอดูล:etymon 828 576414 5775223 5757793 2026-10-14T07:09:07Z Octahedron80 267 5775223 Scribunto text/plain --[=[ This module implements the {{etymon}} template for structured etymology data on Wiktionary. It enables the creation of etymology trees and text by parsing etymon chains, scraping linked pages for their own {{etymon}} data, and recursively building a tree of derivational relationships. Authors: - Original implementation: [[User:Ioaxxere]] - Full refactor (September 2025): [[User:Fenakhay]] ([[Special:Diff/86717746]]) Modules: - [[Module:etymon]]: main module handling parsing, validation, tree building, and page scraping - [[Module:etymon/data]]: keyword definitions, configuration, and status constants - [[Module:etymon/tree]]: etymology tree rendering - [[Module:etymon/text]]: etymology text generation - [[Module:etymon/categories]]: category generation logic - [[Module:etymon/tracking]]: tracking ]=] local export = {} local __state = { cached_etymon_args = {}, cached_etymon_pages = {}, cached_descendants_checks = {}, senseid_parent_etymon = {}, available_etymon_ids = {}, single_etymons = {}, entry_title = nil, entry_lang_code = nil, current_page_has_inline_etymology = false, current_page_has_redundant_etymology = false, used_idless_etymon = false, toplevel_has_inline_etymology = false, toplevel_redundant_etymology = false, toplevel_idless_etymon = false, has_mismatched_id = false, linked_page_multiple_etymons_idless = false, linked_page_partial_etymology_sections = false, partial_etymology_targets = {}, skip_partial_etymology_category = false, max_depth_reached = 0, total_nodes = 0, language_count = {}, toplevel_keyword_stats = {}, id_stats = nil, warnings = {}, } local function reset_invocation_state() __state.current_page_has_inline_etymology = false __state.current_page_has_redundant_etymology = false __state.used_idless_etymon = false __state.toplevel_has_inline_etymology = false __state.toplevel_redundant_etymology = false __state.toplevel_idless_etymon = false __state.has_mismatched_id = false __state.linked_page_multiple_etymons_idless = false __state.linked_page_partial_etymology_sections = false __state.max_depth_reached = 0 __state.total_nodes = 0 __state.language_count = {} __state.toplevel_keyword_stats = {} __state.warnings = {} end local M = require("Module:module loader").init({ require = { data = "Module:etymon/data", tree = "Module:etymon/tree", text = "Module:etymon/text", categories = "Module:etymon/categories", tracking = "Module:etymon/tracking", descendants = "Module:etymon/descendants", affix = "Module:affix", anchors = "Module:anchors", etydate = "Module:etydate", etymology = "Module:etymology", families = "Module:families", languages = "Module:languages", languages_errorgetby = "Module:languages/errorGetBy", links = "Module:links", pages = "Module:pages", parameters = "Module:parameters", roots = "Module:roots", string_utilities = "Module:string utilities", template_parser = "Module:template parser", utilities = "Module:utilities", debug = "Module:debug", en_utilities = "Module:en-utilities", parameter_utilities = "Module:parameter utilities", parse_utilities = "Module:parse utilities", template_styles = "Module:TemplateStyles", script_utilities = "Module:script utilities", JSON = "Module:JSON", yesno = "Module:yesno", }, loadData = { headword_data = "Module:headword/data", parameters_data = "Module:parameters/data", text_allowed = "Module:etymon/data/text_allowed", }, }) local Util = {} function Util.format_error(message, preview_only) if preview_only and not M.pages.is_preview() then return nil end return '<span class="error">' .. message .. '</span>' end function Util.add_warning(message, preview_only) local formatted = Util.format_error(message, preview_only) if formatted then table.insert(__state.warnings, formatted) end end function Util.is_text_param_allowed_for_lang(lang) if not lang or type(lang) ~= "table" then return false end local types = lang.getTypes and lang:getTypes() if types and types.family then local code = lang.getCode and lang:getCode() return code and M.text_allowed.families[code] == true end local full_code = lang.getFullCode and lang:getFullCode() if full_code and M.text_allowed.langs[full_code] then return true end if lang.inFamily then for family_code in pairs(M.text_allowed.families) do if lang:inFamily(family_code) then return true end end end return false end function Util.get_lang(code, no_error) if no_error then return M.languages.getByCode(code, nil, true) end return M.languages.getByCode(code, nil, true) or M.languages_errorgetby.code(code, true, true) end -- Match a term language against a text=:lang stop target (supports etymology-only codes). function Util.lang_matches_stop_code(term_lang, stop_code) if not term_lang or not stop_code or stop_code == "" then return false end local stop_lang = Util.get_lang(stop_code, true) if not stop_lang then return false end if term_lang:getCode() == stop_lang:getCode() then return true end if stop_lang:getFullCode() == stop_lang:getCode() then return term_lang:getFullCode() == stop_lang:getCode() end return false end function Util.get_family(code) return M.families.getByCode(code) end function Util.get_lang_exception(lang) -- Families have no language-specific exceptions if lang.getTypes and lang:getTypes().family then return nil end local code = lang:getCode() local lang_exceptions = M.data.config.lang_exceptions if lang_exceptions[code] then return lang_exceptions[code] end for norm_code, exc in pairs(lang_exceptions) do if exc.normalize_to and code == exc.normalize_to then return exc end if exc.normalize_from_families then local should_normalize = false for _, family in ipairs(exc.normalize_from_families) do if lang:inFamily(family) then should_normalize = true break end end if should_normalize and exc.normalize_exclude_families then for _, family in ipairs(exc.normalize_exclude_families) do if lang:inFamily(family) then should_normalize = false break end end end if should_normalize then local ret = {} for k, v in pairs(exc) do ret[k] = v end ret.suppress_tr = nil return ret end end end return nil end function Util.get_norm_lang(lang) local exc = Util.get_lang_exception(lang) if exc and exc.normalize_to then return M.languages.getByCode(exc.normalize_to) end return lang end function Util.resolve_context_lang(lang, node_args) if type(node_args) ~= "table" then return lang end if node_args.status == M.data.STATUS.INLINE then return lang end if not (lang.hasType and lang:hasType("etymology-only")) then return lang end local full = lang.getFull and lang:getFull() if not full or full:getCode() == lang:getCode() then return lang end if full.hasAncestor and full:hasAncestor(lang) then return lang end return full end -- Add default values for boolean modifiers (e.g., <unc> becomes <unc:1>) -- This is needed because Module:parse utilities expects boolean modifiers to have explicit values function Util.add_boolean_defaults(str, param_mods) local result = str for name, spec in pairs(param_mods) do if spec.type == "boolean" then -- Replace <name> with <name:1> (but not <name:...> which already has a value) result = result:gsub("<" .. name .. ">", "<" .. name .. ":1>") end end return result end local REQUEST_TEMPLATE_PARAM_MODS = { rfe = M.parameter_utilities.construct_param_mods { { param = { "sort", "y", "m", "fragment", "section" } }, { param = { "nocat", "box", "noes" }, type = "boolean" }, }, etystub = M.parameter_utilities.construct_param_mods { { param = "sort" }, { param = { "nocat", "nocap", "nodot" }, type = "boolean" }, }, } function Util.expand_request_template(frame, template_name, param_value, lang_code) local param_mods = REQUEST_TEMPLATE_PARAM_MODS[template_name] local with_defaults = Util.add_boolean_defaults(param_value, param_mods) local parsed = M.parse_utilities.parse_inline_modifiers(with_defaults, { param_mods = param_mods, generate_obj = function(text) if M.yesno(text, false) then return { is_boolean = true } end return { text = text } end, }) local template_args = { [1] = lang_code } for name in pairs(param_mods) do template_args[name] = parsed[name] end if not parsed.is_boolean then template_args[2] = parsed.text end return " " .. frame:expandTemplate({ title = template_name, args = template_args, }) end -- Centralized term formatting: handles suppress_term (-), unknown_term (empty/+), and regular terms function Util.format_term(term, is_toplevel, opts) opts = opts or {} -- suppress_term (-) returns nil if term.suppress_term then return nil end local lang = term.lang local exc = Util.get_lang_exception(lang) if is_toplevel then local display_text = term.alt or term.title or "" local sc = term.sc or lang:findBestScript(display_text) local bold_text = tostring(mw.html.create("strong") :addClass("selflink") :wikitext(display_text)) return M.script_utilities.tag_text(bold_text, lang, sc, "term") end local link_params = { lang = lang } link_params.term = not term.unknown_term and (term.link_page or term.title) or nil link_params.alt = term.alt or term.link_page and term.title or nil link_params.id = (not term.unknown_term and term.id and term.id ~= "") and term.id or nil if not (exc and exc.suppress_tr) then link_params.tr = term.tr link_params.ts = term.ts else link_params.suppress_tr = true end link_params.lit = (opts.lit ~= "suppress") and term.lit or nil if opts.gloss ~= "suppress" then link_params.gloss = term.gloss end link_params.genders = term.genders if opts.pos ~= "suppress" then link_params.pos = term.pos link_params.ng = term.ng link_params.infl = term.infl end if exc and exc.suppress_tr then link_params.lit = nil end if opts.tree_ql ~= "suppress" then if term.q then link_params.q = term.q end if term.qq then link_params.qq = term.qq end if term.l then link_params.l = term.l end if term.ll then link_params.ll = term.ll end link_params.show_decorations = term.q or term.qq or term.l or term.ll end return M.links.full_link(link_params, "term") end local __is_content_page_cached function Util.is_content_page() if __is_content_page_cached == nil then __is_content_page_cached = M.pages.is_content_page(mw.title.getCurrentTitle()) end return __is_content_page_cached end local __page_data_cached function Util.get_page_data() if not __page_data_cached then __page_data_cached = M.headword_data.page end return __page_data_cached end -- Extract base keyword from param (without modifiers) local function get_keyword_base(param) if type(param) ~= "string" then return nil end local base = param:match("^:?([^<]+)") or param:gsub("^:", "") return base end local function is_keyword(param, allow_colon_less) if type(param) ~= "string" then return false end local keywords = M.data.keywords if param:sub(1, 1) == ":" then local base = get_keyword_base(param) return keywords[base] ~= nil end if allow_colon_less then local base = get_keyword_base(param) return keywords[base] ~= nil end return false end local function get_keyword(param, allow_colon_less) if type(param) ~= "string" then return nil end local keywords = M.data.keywords if param:sub(1, 1) == ":" then return get_keyword_base(param) end if allow_colon_less then local base = get_keyword_base(param) if keywords[base] then return base end end return nil end local function normalize_keyword(keyword) if keyword:sub(1, 1) == ":" then return keyword end return ":" .. keyword end -- Resolve keyword (possibly an alias) to its canonical form. Used only at input boundaries local function get_canonical_keyword(keyword) if not keyword then return keyword end return M.data.keyword_canonical[keyword] or keyword end local function is_affix_group_keyword(keyword) local config = keyword and M.data.keywords[keyword] return config and config.affix_categories or false end local function reject_removed_surf_keyword(param) local base = get_keyword_base(param) if base == "surf" then error("The `:surf` keyword has been removed. Use `<surf>` on a formation keyword instead (e.g. `:af<surf>`, `:bor<surf>`).") end end local function copy_keyword_info(source) local copy = {} for k, v in pairs(source) do copy[k] = v end return copy end local function lowercase_glossary_display(text) return text:gsub("(%[%[ภาคผนวก:อภิธานศัพท์#[^|]+|)([^%]])([^%]]*)%]%]", function(prefix, first, rest) return prefix .. mw.ustring.lower(first) .. rest .. "]]" end) end local function surf_should_keep_formation_phrase(base) if not base.phrase then return false end if base.glossary then return true end return not (base.phrase == "from" and (base.text == "From" or base.text == "from")) end -- Runtime overrides when <surf> is present on a keyword. local function get_effective_keyword_info(keyword, modifiers) local base = M.data.keywords[keyword] if not base or not modifiers or not modifiers.surf then return base end local effective = copy_keyword_info(base) local surf_text = "By [[ภาคผนวก:อภิธานศัพท์#surface_analysis|surface analysis]]," local surf_phrase = "by surface analysis," effective.new_sentence = true effective.invisible = "tree" if surf_should_keep_formation_phrase(base) then effective.phrase = surf_phrase .. " " .. base.phrase if base.text then effective.text = surf_text .. " " .. lowercase_glossary_display(base.text) else effective.text = surf_text .. " " .. base.phrase end else effective.text = surf_text effective.phrase = surf_phrase end return effective end -- Build text/phrase for nominalization with <g:code> (uses data module for codes only). local function get_nominalization_label_for_g(code) if not code or code == "" then return nil end local codes = M.data.nominalization_g_codes local adj = codes[code] if not adj and #code == 2 then local gender_adj = codes[code:sub(1, 1)] local number_adj = codes[code:sub(2, 2)] if gender_adj and number_adj then adj = gender_adj .. " " .. number_adj end end if not adj then return nil end local text = adj:gsub("^%l", function(c) return string.upper(c) end) .. " [[ภาคผนวก:อภิธานศัพท์#nominalization|nominalization]] of" local phrase = M.en_utilities.add_indefinite_article(adj .. " [[ภาคผนวก:อภิธานศัพท์#nominalization|nominalization]] of", false) return { text = text, phrase = phrase } end local EtymonParser = {} -- Keyword modifier definitions EtymonParser.keyword_param_mods = M.parameter_utilities.construct_param_mods { { group = "ref" }, { param = "conj" }, -- conjunction for alternatives: "and", "or", "and/or", etc. { param = { "unc", "surf" }, type = "boolean" }, { param = "text", restrict = { keywords = { "from", "derived" } } }, { param = "lit", restrict = { affix_group = true } }, { param = "g", restrict = { keywords = { "nominalization" } } }, { param = "senseid", restrict = { keywords = { "semantic loan" } } }, } -- Term modifier definitions EtymonParser.etymon_param_mods = M.parameter_utilities.construct_param_mods { {group = {"link", "q", "l", "ref", "infl"}, exclude = {"sc"}}, {param = {"ety", "postype"}}, {param = "unc", type = "boolean"}, {param = "aftype", restrict = {affix_group = true}}, {param = {"bor", "slbor", "lbor"}, type = "boolean", restrict = {affix_group = true}}, } local function get_clean_param_mods(param_mods) local clean = {} for mod_name, mod_def in pairs(param_mods) do clean[mod_name] = {} for key, value in pairs(mod_def) do if key ~= "restrict" then clean[mod_name][key] = value end end end return clean end function EtymonParser.check_modifier_restrictions(modifiers, current_keyword, param_mods) for mod_name, mod_value in pairs(modifiers) do -- Only check restrictions if the modifier has a non-false/nil value if mod_value then local mod_def = param_mods[mod_name] if mod_def and mod_def.restrict then if mod_def.restrict.affix_group then if not is_affix_group_keyword(current_keyword) then local mod_display = mod_value == true and "<" .. mod_name .. ">" or "<" .. mod_name .. ":" .. tostring(mod_value) .. ">" error("The modifier `" .. mod_display .. "` is only allowed for affix-group keywords (e.g. `:af`, `:blend`, `:univ`).") end elseif mod_def.restrict.keywords then local allowed_keywords = mod_def.restrict.keywords local is_allowed = false for _, allowed_keyword in ipairs(allowed_keywords) do if current_keyword == allowed_keyword then is_allowed = true break end end if not is_allowed then local keyword_list = {} for _, kw in ipairs(allowed_keywords) do table.insert(keyword_list, ":" .. kw) end local keyword_str = table.concat(keyword_list, #keyword_list == 2 and " or " or ", ") if #keyword_list > 2 then -- Replace last comma with "or" keyword_str = keyword_str:gsub(", ([^,]+)$", " or %1") end local mod_display = mod_value == true and "<" .. mod_name .. ">" or "<" .. mod_name .. ":" .. tostring(mod_value) .. ">" error("The modifier `" .. mod_display .. "` is only allowed for the keyword" .. (#keyword_list > 1 and "s " or " ") .. keyword_str .. ".") end end end end end end local TERM_RULE_DISALLOW = { suppress = { field = "suppress_term", label = "suppressed" }, unknown = { field = "unknown_term", label = "unknown" }, family = { field = "is_family", label = "family" }, } function EtymonParser.check_etymon_limits(count, limits, label, opts) if not limits then return end opts = opts or {} local min_etymons = limits.min_etymons if min_etymons == nil and not opts.skip_default_min then min_etymons = 1 end if min_etymons and count < min_etymons then if min_etymons > 1 then error("Detected " .. label .. " group with fewer than " .. min_etymons .. " etymons.") else error("Detected " .. label .. " with no etymons.") end end if limits.max_etymons and count > limits.max_etymons then local unit = (limits.max_etymons == 1) and "etymon" or "etymons" error("Detected " .. label .. " with more than " .. limits.max_etymons .. " " .. unit .. ".") end end function EtymonParser.check_term_rules(etymon_data, entry_lang, rules, label) label = label or "term" if rules and rules.disallow then local disallowed = {} for _, typ in ipairs(rules.disallow) do local spec = TERM_RULE_DISALLOW[typ] if spec and etymon_data[spec.field] then table.insert(disallowed, spec.label) end end if #disallowed > 0 then error(label .. " does not support " .. mw.text.listToText(disallowed, "or") .. " etymons.") end end if etymon_data.is_family then if rules and rules.family == "disallowed" then error(label .. " does not support family codes" .. (rules.family_suffix or ".")) elseif not etymon_data.suppress_term then error("Family codes require suppressed term (use family:-).") end end if rules then if rules.require_term and (not etymon_data.term or etymon_data.term == "") then error(label .. " requires a term for each listed form.") end if rules.entry_lang then if Util.get_norm_lang(etymon_data.lang):getFullCode() ~= Util.get_norm_lang(entry_lang):getFullCode() then error(label .. " terms must be in the entry language (" .. entry_lang:getFullCode() .. "), got '" .. etymon_data.lang:getFullCode() .. "'.") end end if rules.ancestor_check then M.etymology.check_ancestor(entry_lang, etymon_data.lang) end elseif etymon_data.is_family and not etymon_data.suppress_term then error("Family codes require suppressed term (use family:-).") end end function EtymonParser.check_keyword_term(etymon_data, entry_lang, keyword) local config = M.data.keywords[keyword] EtymonParser.check_term_rules(etymon_data, entry_lang, config and config.term_rules, "`:" .. keyword .. "`") end function EtymonParser.check_supplement_term(etymon_data, entry_lang, supplement_type) local config = M.data.supplements[supplement_type] EtymonParser.check_term_rules(etymon_data, entry_lang, config and config.term_rules, "|" .. supplement_type .. "=") end -- Parse keyword with modifiers (e.g., ":bor<unc>" or ":bor<ref:{{R:example}}>") function EtymonParser.parse_keyword_modifiers(param) if type(param) ~= "string" then return nil, {} end local base_keyword = get_keyword_base(param) if not base_keyword then return nil, {} end local canonical_keyword = get_canonical_keyword(base_keyword) -- Check if there are any modifiers if not param:find("<", 1, true) then return canonical_keyword, {} end -- Parse modifiers using the same mechanism as etymon parsing local rest_with_defaults = Util.add_boolean_defaults(param, EtymonParser.keyword_param_mods) local function generate_obj(ignored) return {} end local parsed = M.parse_utilities.parse_inline_modifiers(rest_with_defaults:gsub("^:?[^<]+", ""), { param_mods = get_clean_param_mods(EtymonParser.keyword_param_mods), generate_obj = generate_obj }) local modifiers = { unc = parsed.unc or false, refs = parsed.refs, text = parsed.text, lit = parsed.lit, conj = parsed.conj, g = parsed.g, surf = parsed.surf or false, senseid = parsed.senseid, } -- Validate modifiers against restrictions EtymonParser.check_modifier_restrictions(modifiers, canonical_keyword, EtymonParser.keyword_param_mods) return canonical_keyword, modifiers end local function normalize_keyword_param(keyword_with_mods) local trimmed = M.string_utilities.trim(keyword_with_mods) reject_removed_surf_keyword(trimmed:match("^:") and trimmed or (":" .. trimmed)) local base = get_keyword_base(trimmed) if not base or not M.data.keywords[base] then error("Invalid keyword '" .. trimmed .. "' in inline etymology") end local canonical_base = get_canonical_keyword(base) local without_colon = trimmed:gsub("^:", "") local mods_part = without_colon:sub(#base + 1) local kw_param = normalize_keyword(canonical_base .. mods_part) EtymonParser.parse_keyword_modifiers(kw_param) return kw_param end local function get_keyword_mod_names() local names = {} for mod_name in pairs(EtymonParser.keyword_param_mods) do names[mod_name] = true end return names end local function parse_inline_ety_run(ety_string) local body = ety_string or "" if body == "" then error("Empty inline etymology") end local keyword_mod_names = get_keyword_mod_names() local pos = 1 local len = #body local function parse_err(msg) error(msg .. " in inline etymology: '" .. body .. "'") end local function peek_double() return body:sub(pos, pos + 1) == "<<" end local function mod_name_from_unwrapped(unwrapped) return unwrapped:match("^<([^:>]+)") end local function is_keyword_mod(unwrapped) local name = mod_name_from_unwrapped(unwrapped) return name and keyword_mod_names[name] or false end local function read_double_bracket() if not peek_double() then return nil end local start = pos pos = pos + 2 while pos <= len - 1 do if body:sub(pos, pos + 1) == ">>" then local token = body:sub(start, pos + 1) pos = pos + 2 return token, token:sub(2, -2) end pos = pos + 1 end parse_err("Unmatched <<") end local function read_angle_cell() if body:sub(pos, pos) ~= "<" or peek_double() then return nil end local open = pos pos = pos + 1 local depth = 1 local i = pos while i <= len do local ch = body:sub(i, i) if ch == "<" then depth = depth + 1 elseif ch == ">" then depth = depth - 1 if depth == 0 then local inner = body:sub(open + 1, i - 1) pos = i + 1 return inner end end i = i + 1 end parse_err("Unmatched <") end local function read_bare_run() local start = pos while pos <= len and body:sub(pos, pos) ~= "<" do pos = pos + 1 end return body:sub(start, pos - 1) end local function absorb_double_keyword_mods(keyword_str) while peek_double() do local saved = pos local _, unwrapped = read_double_bracket() if is_keyword_mod(unwrapped) then keyword_str = keyword_str .. unwrapped else pos = saved break end end return keyword_str end local kw_start = pos while pos <= len and body:sub(pos, pos) ~= "<" do pos = pos + 1 end local keyword = body:sub(kw_start, pos - 1) if keyword:match("^%s*$") then parse_err("Missing keyword") end keyword = absorb_double_keyword_mods(keyword) local cells = {} while pos <= len do if peek_double() then local _, unwrapped = read_double_bracket() if is_keyword_mod(unwrapped) then parse_err("Unexpected keyword modifier " .. unwrapped .. " outside of a keyword") end table.insert(cells, "+" .. unwrapped) elseif body:sub(pos, pos) == "<" then local inner = read_angle_cell() if inner ~= "" then table.insert(cells, inner) end else local bare = read_bare_run() if bare ~= "" then if bare:sub(1, 1) ~= ":" then parse_err("Unexpected bare text '" .. bare .. "' (use :keyword for nested keywords in inline etymology)") end if not is_keyword(bare, true) then parse_err("Invalid keyword '" .. bare .. "' in inline etymology") end table.insert(cells, absorb_double_keyword_mods(bare)) end end end return { keyword = keyword, cells = cells, } end function EtymonParser.inline_ety_to_pipe(ety_string) local run = parse_inline_ety_run(ety_string) if not run.keyword or run.keyword:match("^%s*$") then return "|" end local pipe_parts = { normalize_keyword_param(M.string_utilities.trim(run.keyword)) } for _, segment in ipairs(run.cells) do if is_keyword(segment, true) then table.insert(pipe_parts, normalize_keyword_param(segment)) else table.insert(pipe_parts, segment) end end return "|" .. table.concat(pipe_parts, "|") .. "|" end function EtymonParser.pipe_to_inline_ety(pipe_string) local cells = {} for cell in pipe_string:gmatch("([^|]+)") do if cell ~= "" then table.insert(cells, cell) end end if #cells == 0 then return "" end local inline_parts = {} for index, cell in ipairs(cells) do local base = get_keyword_base(cell) if base and M.data.keywords[base] then local without_colon = cell:gsub("^:", "") local kw_base, mods = without_colon:match("^([^<]+)(.*)$") local inline_kw = (kw_base or without_colon) .. (mods or ""):gsub("<([^>]+)>", "<<%1>>") if index > 1 then inline_kw = ":" .. inline_kw end table.insert(inline_parts, inline_kw) elseif cell:sub(1, 1) == "+" then local mod = cell:sub(2) if mod:match("^<.->$") then mod = mod:sub(2, -2) end table.insert(inline_parts, "<<" .. mod .. ">>") else table.insert(inline_parts, "<" .. cell .. ">") end end return table.concat(inline_parts, "") end function EtymonParser.parse_inline_ety(ety_string, context_lang) local run = parse_inline_ety_run(ety_string) local keyword = M.string_utilities.trim(run.keyword) reject_removed_surf_keyword(":" .. keyword) if not is_keyword(keyword, true) then error("Invalid keyword '" .. keyword .. "' in inline etymology <ety:" .. keyword .. "...>") end local args = { context_lang:getCode(), normalize_keyword_param(keyword) } for _, segment in ipairs(run.cells) do if is_keyword(segment, true) then table.insert(args, normalize_keyword_param(segment)) else table.insert(args, segment) end end return args end function EtymonParser.parse_etymon(param, context_lang) if is_keyword(param) then return nil end if type(param) ~= "string" then return nil end local lang, rest local is_family = false local before_bracket = param:match("^([^<]*)") or param local lang_code, rest_match = before_bracket:match("^([a-zA-Z][a-zA-Z0-9._-]*):(.*)$") if lang_code then local potential_lang = Util.get_lang(lang_code, true) if potential_lang then lang = potential_lang rest = param:sub(#lang_code + 2) else local potential_family = Util.get_family(lang_code) if potential_family then lang = potential_family rest = param:sub(#lang_code + 2) is_family = true else lang = context_lang rest = param end end else lang = context_lang rest = param end M.tracking.track_term(rest) if rest == "" or rest == "+" then return { lang = lang, term = nil, unknown_term = true, is_family = is_family, } end if rest == "-" then return { lang = lang, term = nil, suppress_term = true, is_family = is_family, } end if not rest:find("<", 1, true) then return { lang = lang, term = M.string_utilities.trim(rest), is_family = is_family, } end local term_text = rest:match("^([^<]*)") or "" local is_unknown = (term_text == "" or term_text == "+") local is_suppress = (term_text == "-") local function generate_obj(ignored_term) return { term = (is_unknown or is_suppress) and nil or M.string_utilities.trim(term_text) } end local rest_with_defaults = Util.add_boolean_defaults(rest, EtymonParser.etymon_param_mods) local parsed_obj = M.parse_utilities.parse_inline_modifiers(rest_with_defaults, { param_mods = get_clean_param_mods(EtymonParser.etymon_param_mods), generate_obj = generate_obj }) if parsed_obj.id and parsed_obj.id:match("^!") then parsed_obj.id = parsed_obj.id:sub(2) parsed_obj.override = true end parsed_obj.lang = lang parsed_obj.is_family = is_family if is_unknown then parsed_obj.unknown_term = true elseif is_suppress then parsed_obj.suppress_term = true end return parsed_obj end function EtymonParser.validate(lang, args, id, title, pos, starts_with_lang_code) -- id is now optional, so only validate if provided if id then if mw.ustring.len(id) < 2 then error("The `id` parameter must have at least two characters.") end if id == title or id == Util.get_page_data().pagename then error("The `id` parameter must not be the same as the page title.") end end local valid_pos = { prefix = true, suffix = true, interfix = true, infix = true, root = true, word = true } if pos and not valid_pos[pos] then error("Unknown value provided for `pos`. Valid values: " .. table.concat(require("Module:table").keysToList(valid_pos), ", ") .. ".") end local current_keyword = "from" local current_keyword_explicit = false local keyword_etymons = {} local keywords = M.data.keywords local function checkKeyword() local config = keywords[current_keyword] if current_keyword == "from" and not current_keyword_explicit and #keyword_etymons == 0 then keyword_etymons = {} return end EtymonParser.check_etymon_limits(#keyword_etymons, config, "`:" .. current_keyword .. "`") keyword_etymons = {} end local start_index = starts_with_lang_code and 2 or 1 for i = start_index, #args do local param = args[i] if type(param) ~= "string" then elseif param:sub(1, 1) == ":" and not is_keyword(param) then reject_removed_surf_keyword(param) error("Invalid keyword '" .. param .. "'. Did you mean a valid keyword like ':bor', ':inh', etc.?") elseif is_keyword(param) then checkKeyword() current_keyword = get_canonical_keyword(get_keyword(param)) current_keyword_explicit = true else local etymon_data = EtymonParser.parse_etymon(param, lang) if etymon_data then table.insert(keyword_etymons, param) EtymonParser.check_keyword_term(etymon_data, lang, current_keyword) -- Check modifier restrictions EtymonParser.check_modifier_restrictions(etymon_data, current_keyword, EtymonParser.etymon_param_mods) -- postype must be "root" or "word" local VALID_POSTYPES = { root = true, word = true } if etymon_data.postype and not VALID_POSTYPES[etymon_data.postype] then error("Invalid <postype:" .. etymon_data.postype .. ">; must be \"root\" or \"word\".") end if etymon_data.ety then local inline_args = EtymonParser.parse_inline_ety(etymon_data.ety, etymon_data.lang) EtymonParser.validate(etymon_data.lang, inline_args, nil, nil, nil, true) end else table.insert(keyword_etymons, param) end end end checkKeyword() end local DataRetriever = {} local function format_etymon_id_hint(id_data, idx) local id = type(id_data) == "table" and id_data.id or id_data local pos = type(id_data) == "table" and id_data.pos if id and id ~= "" and id ~= "*" then return '"' .. id .. '"' end if pos and pos ~= "" then return "unnamed (|pos=" .. pos .. "|)" end return "etymon #" .. idx .. " (no |id= on page)" end local function etymon_target_page_link(page, norm_lang) return M.links.full_link({ term = page, lang = norm_lang, no_generate_alternants = true, }, "term") end -- Summarize {{etymon}} id slots on a linked page for preview warnings. local function summarize_available_etymon_ids(ids) local id_list = {} local all_idless = true local target_has_idless = false local any_pos = false for i, id_data in ipairs(ids) do local id = type(id_data) == "table" and id_data.id or id_data local pos = type(id_data) == "table" and id_data.pos if id and id ~= "" and id ~= "*" then all_idless = false else target_has_idless = true end if pos and pos ~= "" then any_pos = true end table.insert(id_list, format_etymon_id_hint(id_data, i)) end return { id_list = id_list, all_idless = all_idless, target_has_idless = target_has_idless, any_pos = any_pos, count = #ids, options_text = mw.text.listToText(id_list), } end local function ambiguous_etymon_suggestion(page_link, summary) if summary.all_idless then if summary.any_pos then return " None set `|id=` yet; add a unique `|id=` to each on " .. page_link .. ", then `<id:identifier>` after the term here. Section order / hints: " .. summary.options_text .. "." end return " None set `|id=` yet; add a unique `|id=` to each {{etymon}} in that section from top to bottom, then `<id:identifier>` after the term here (same value as `|id=`)." end return " Specify which one with `<id:identifier>` after the term. Options: " .. summary.options_text .. "." end local function warn_ambiguous_etymon_link(page, norm_lang, ids, is_toplevel) local page_link = etymon_target_page_link(page, norm_lang) local summary = summarize_available_etymon_ids(ids) if is_toplevel and summary.target_has_idless then __state.linked_page_multiple_etymons_idless = true end local lang_name = norm_lang:getCanonicalName() local lead = "Etymology link to " .. page_link .. " is ambiguous (" .. summary.count .. " {{etymon}} templates for " .. lang_name .. ")." Util.add_warning(lead .. ambiguous_etymon_suggestion(page_link, summary), true) end local function is_mismatched_explicit_id(base_key, cached_args, parent_etymon) return cached_args == M.data.STATUS.MISSING and not parent_etymon and #(__state.available_etymon_ids[base_key] or {}) > 0 end local function maybe_flag_partial_etymology_reference(base_key, etymon_data, cached_args, is_toplevel) if not is_toplevel or __state.skip_partial_etymology_category then return end if not __state.partial_etymology_targets[base_key] then return end if etymon_data.id and type(cached_args) == "table" then return end __state.linked_page_partial_etymology_sections = true end local function is_nonlemma_etymon_template(template_args) return template_args and M.yesno(template_args.nl, false) end local function warn_mismatched_explicit_id(page, norm_lang, base_key, etymon_id) local page_link = etymon_target_page_link(page, norm_lang) local summary = summarize_available_etymon_ids(__state.available_etymon_ids[base_key] or {}) local lang_name = norm_lang:getCanonicalName() local lead = "Etymology link to " .. page_link .. " uses `<id:" .. etymon_id .. ">`, but no {{etymon}} on that page has `|id=" .. etymon_id .. "|` for " .. lang_name .. "." Util.add_warning(lead .. " Valid IDs: " .. summary.options_text .. ".", true) end -- Given an etymon data, scrape its page and cache the result in the global state object. function DataRetriever.cache_page_etymons(etymon_page, etymon_title, key, etymon_lang, etymon_id, redirected_from, descendants_is_toplevel) local content = etymon_title:getContent() if not content then __state.cached_etymon_args[key] = M.data.STATUS.REDLINK return end -- Check if the linked page is a redirect. If it is, the template parsing -- code below will be effectively skipped, and `scrape_page` will be called -- again on the redirect target (see the bottom of this function) local lang_section_for_descendants = nil local redirect_target = etymon_title.redirect_target if not redirect_target then --content = M.pages.get_section(content, etymon_lang:getFullName(), 2) -- Fix Thai Wiktionary that prefixes language name with ภาษา local lang_name = etymon_lang:getFullName() local section_content = M.pages.get_section(content, lang_name, 2) if not section_content then section_content = M.pages.get_section(content, "ภาษา" .. lang_name, 2) end content = section_content if not content then __state.cached_etymon_args[key] = M.data.STATUS.MISSING return end lang_section_for_descendants = content end local etymon_lang_code = etymon_lang:getFullCode() local lang_page_key = etymon_lang_code .. ":" .. etymon_page local found_templates_for_lang = {} local found_ids = {} local get_node_class = M.template_parser.class_else_type -- Look for all {{etymon}} templates within the page content using the template parser -- This way the same page is never parsed more than once -- Build a map from senseids to their parent etymonids. local active_etymon_args = nil local etymology_section_count = 0 local etymology_sections_with_etymon = 0 local current_etymology_has_etymon = false local current_etymology_has_nonlemma = false local function finalize_current_etymology_section() if etymology_section_count == 0 then return end if current_etymology_has_etymon or current_etymology_has_nonlemma then etymology_sections_with_etymon = etymology_sections_with_etymon + 1 end current_etymology_has_etymon = false current_etymology_has_nonlemma = false end for node in M.template_parser.parse(content):iterate_nodes() do local node_class = get_node_class(node) if node_class == "heading" then -- A new L2 or etymology section acts as a barrier: an {{etymon}} usage -- used previously cannot be the parent of any subsequent senseids. -- Note that we don't have to check for L2s due to the usage of `M.pages.get_section` above. if node:get_name():find("^Etymology") then finalize_current_etymology_section() etymology_section_count = etymology_section_count + 1 active_etymon_args = nil end elseif node_class == "template" then local template_name = node:get_name() if template_name == "etymon" then local template_args = node:get_arguments() -- Check if this etymon is for our language if template_args[1] == etymon_lang_code then if is_nonlemma_etymon_template(template_args) then if etymology_section_count > 0 then current_etymology_has_nonlemma = true end else if etymology_section_count > 0 then current_etymology_has_etymon = true end table.insert(found_templates_for_lang, template_args) if template_args.id then local etymon_key = lang_page_key .. ":" .. template_args.id __state.cached_etymon_args[etymon_key] = template_args __state.cached_etymon_pages[etymon_key] = tostring(etymon_page) table.insert(found_ids, template_args.id) active_etymon_args = template_args else -- Store idless etymon with default key local etymon_key = lang_page_key .. ":*" __state.cached_etymon_args[etymon_key] = template_args __state.cached_etymon_pages[etymon_key] = tostring(etymon_page) table.insert(found_ids, "*") active_etymon_args = template_args end end end elseif active_etymon_args and template_name == "senseid" then local template_args = node:get_arguments() -- This should always be true for proper usages of {{senseid}}. if template_args[1] == etymon_lang_code and template_args[2] then local sense_id_key = lang_page_key .. ":" .. template_args[2] __state.senseid_parent_etymon[sense_id_key] = active_etymon_args __state.cached_etymon_pages[sense_id_key] = tostring(etymon_page) end end end end finalize_current_etymology_section() if lang_section_for_descendants and etymology_section_count > 1 and etymology_sections_with_etymon > 0 and etymology_sections_with_etymon < etymology_section_count then __state.partial_etymology_targets[lang_page_key] = true end if descendants_is_toplevel and lang_section_for_descendants and #found_templates_for_lang > 0 then M.descendants.cache_page_checks({ lang_section = lang_section_for_descendants, etymon_lang_code = etymon_lang_code, found_templates_for_lang = found_templates_for_lang, entry_title = __state.entry_title, entry_lang_code = __state.entry_lang_code, entry_lang = __state.entry_lang_code and Util.get_lang(__state.entry_lang_code, true) or nil, cached_descendants_checks = __state.cached_descendants_checks, lang_page_key = lang_page_key, redirected_from = redirected_from, }) end local id_data_list = {} for _, args in ipairs(found_templates_for_lang) do local id = args.id or "*" table.insert(id_data_list, { id = id, pos = args.pos }) end __state.available_etymon_ids[lang_page_key] = id_data_list if #found_templates_for_lang == 1 then __state.single_etymons[lang_page_key] = found_templates_for_lang[1] end if redirected_from and __state.available_etymon_ids[lang_page_key] then __state.available_etymon_ids[redirected_from] = __state.available_etymon_ids[redirected_from] or {} for _, id_data in ipairs(__state.available_etymon_ids[lang_page_key]) do table.insert(__state.available_etymon_ids[redirected_from], id_data) end end if __state.cached_etymon_args[key] ~= nil or __state.senseid_parent_etymon[key] ~= nil then -- All done! return elseif redirect_target and not redirected_from then -- Try scraping the redirect. etymon_page = redirect_target.prefixedText DataRetriever.cache_page_etymons(etymon_page, redirect_target, lang_page_key .. ":" .. etymon_id, etymon_lang, etymon_id, lang_page_key, descendants_is_toplevel) __state.cached_etymon_args[key] = __state.cached_etymon_args[etymon_lang_code .. ":" .. etymon_page .. ":" .. etymon_id] else __state.cached_etymon_args[key] = M.data.STATUS.MISSING end end local function has_linkable_term(etymon_data) if etymon_data.is_family or etymon_data.suppress_term or etymon_data.unknown_term then return false end local term = etymon_data.term if term == nil or term == "" then return false end return M.string_utilities.trim(term) ~= "" end local function map_affix_part(etymon_data) if etymon_data.alt or not has_linkable_term(etymon_data) or etymon_data.term:find("^%^") then return end local affix_type = etymon_data.aftype and (M.data.aftype_aliases[etymon_data.aftype] or etymon_data.aftype) or nil local _, link_term, display_term = M.affix.parse_term_for_affixes(etymon_data.term, etymon_data.lang, nil, affix_type, true, nil, etymon_data.id) if link_term ~= display_term then etymon_data.term = link_term etymon_data.alt = display_term end end local function get_term_page(etymon_data) return etymon_data.link_page or M.links.get_link_page(etymon_data.term, etymon_data.lang) end local function is_root_term(etymon_data, keyword) if etymon_data.postype then return etymon_data.postype == "root" end local config = keyword and M.data.keywords[keyword] return config ~= nil and config.pos_override == "root" end local function record_term_id_tracking(etymon_data) if not has_linkable_term(etymon_data) then return end local term_page = get_term_page(etymon_data) M.tracking.record_term_id_usage(__state.id_stats, etymon_data, term_page) end -- Given an etymon object, scrape its page (if necessary) and return its own etymon arguments as well as the page name. function DataRetriever.get_etymon_args(etymon_data, is_toplevel) if not has_linkable_term(etymon_data) then return M.data.STATUS.MISSING, nil, nil, nil end local page = get_term_page(etymon_data) local norm_lang = Util.get_norm_lang(etymon_data.lang) local base_key = norm_lang:getFullCode() .. ":" .. page if etymon_data.id then local key = base_key .. ":" .. etymon_data.id local cached_args = __state.cached_etymon_args[key] or __state.senseid_parent_etymon[key] if cached_args == nil then local title = mw.title.new(page) if not title then error('Invalid page title "' .. page .. '" encountered.') end DataRetriever.cache_page_etymons(page, title, key, norm_lang, etymon_data.id, nil, is_toplevel) end cached_args = __state.cached_etymon_args[key] or __state.senseid_parent_etymon[key] -- refresh -- Get etymon_id from parent if this was resolved via senseid local parent_etymon = __state.senseid_parent_etymon[key] local resolved_etymon_id = parent_etymon and parent_etymon.id local descendants_check = M.descendants.get_lookup_check({ cached_descendants_checks = __state.cached_descendants_checks, is_toplevel = is_toplevel, base_key = base_key, lookup = { explicit_id = etymon_data.id, parent_etymon = parent_etymon, }, }) if is_toplevel and descendants_check == nil then local title = mw.title.new(page) if title then DataRetriever.cache_page_etymons(page, title, key, norm_lang, etymon_data.id, nil, true) descendants_check = M.descendants.get_lookup_check({ cached_descendants_checks = __state.cached_descendants_checks, is_toplevel = true, base_key = base_key, lookup = { explicit_id = etymon_data.id, parent_etymon = parent_etymon, }, }) end end local mismatched_id = is_mismatched_explicit_id(base_key, cached_args, parent_etymon) if mismatched_id and is_toplevel then __state.has_mismatched_id = true M.tracking.record_mismatched_id_usage(__state.id_stats, norm_lang, page, etymon_data.id) warn_mismatched_explicit_id(page, norm_lang, base_key, etymon_data.id) end maybe_flag_partial_etymology_reference(base_key, etymon_data, cached_args, is_toplevel) return cached_args, __state.cached_etymon_pages[key], resolved_etymon_id, descendants_check else __state.used_idless_etymon = true if is_toplevel then __state.toplevel_idless_etymon = true end if __state.available_etymon_ids[base_key] == nil then local title = mw.title.new(page) if not title then error('Invalid page title "' .. page .. '" encountered.') end DataRetriever.cache_page_etymons(page, title, base_key .. ":*", norm_lang, "*", nil, is_toplevel) end local ids = __state.available_etymon_ids[base_key] or {} local count = #ids -- Try to filter by postype if available and we have multiple candidates if count > 1 and etymon_data.postype then local matching_ids = {} for _, id_data in ipairs(ids) do if id_data.pos == etymon_data.postype then table.insert(matching_ids, id_data) end end if #matching_ids == 1 then local matched_id = matching_ids[1].id local matched_key = base_key .. ":" .. matched_id M.tracking.record_idless_resolution(__state.id_stats, norm_lang, page, "postype") local descendants_check = M.descendants.get_lookup_check({ cached_descendants_checks = __state.cached_descendants_checks, is_toplevel = is_toplevel, base_key = base_key, lookup = { id = matched_id }, }) if is_toplevel and descendants_check == nil then local title = mw.title.new(page) if title then DataRetriever.cache_page_etymons(page, title, base_key .. ":*", norm_lang, "*", nil, true) descendants_check = M.descendants.get_lookup_check({ cached_descendants_checks = __state.cached_descendants_checks, is_toplevel = true, base_key = base_key, lookup = { id = matched_id }, }) end end local matched_args = __state.cached_etymon_args[matched_key] maybe_flag_partial_etymology_reference(base_key, etymon_data, matched_args, is_toplevel) return matched_args, __state.cached_etymon_pages[matched_key], nil, descendants_check end end if count == 1 then local only_id_data = ids[1] local only_id = (type(only_id_data) == "table" and only_id_data.id) or only_id_data or "*" M.tracking.record_idless_resolution(__state.id_stats, norm_lang, page, "single") local descendants_check = M.descendants.get_lookup_check({ cached_descendants_checks = __state.cached_descendants_checks, is_toplevel = is_toplevel, base_key = base_key, lookup = { id_data = only_id_data }, }) if is_toplevel and descendants_check == nil then local title = mw.title.new(page) if title then DataRetriever.cache_page_etymons(page, title, base_key .. ":*", norm_lang, "*", nil, true) descendants_check = M.descendants.get_lookup_check({ cached_descendants_checks = __state.cached_descendants_checks, is_toplevel = true, base_key = base_key, lookup = { id_data = only_id_data }, }) end end local single_args = __state.single_etymons[base_key] maybe_flag_partial_etymology_reference(base_key, etymon_data, single_args, is_toplevel) return single_args, __state.cached_etymon_pages[base_key .. ":" .. only_id], nil, descendants_check elseif count > 1 then M.tracking.record_idless_resolution(__state.id_stats, norm_lang, page, "ambiguous") warn_ambiguous_etymon_link(page, norm_lang, ids, is_toplevel) maybe_flag_partial_etymology_reference(base_key, etymon_data, M.data.STATUS.AMBIGUOUS, is_toplevel) return M.data.STATUS.AMBIGUOUS, nil, nil, nil else M.tracking.record_idless_resolution(__state.id_stats, norm_lang, page, "missing") maybe_flag_partial_etymology_reference(base_key, etymon_data, M.data.STATUS.MISSING, is_toplevel) return M.data.STATUS.MISSING, nil, nil, nil end end end local function keyword_invisible_in_tree(keyword_info) if not keyword_info then return false end local inv = keyword_info.invisible return inv == "all" or inv == true or inv == "tree" end -- True when the node has at least one top-level child container visible in the tree. local function node_has_visible_tree_children(node) for _, container in ipairs(node.children or {}) do if not keyword_invisible_in_tree(container.keyword_info) then return true end end return false end -- Count visible term nodes in the tree. local function get_visible_tree_depth(node, skip_child_rendering) local max_depth = 1 if skip_child_rendering or not node then return max_depth end for _, container in ipairs(node.children or {}) do local keyword_info = container.keyword_info if not keyword_invisible_in_tree(keyword_info) then local skip_grandchildren = keyword_info and keyword_info.no_child_categories for _, term in ipairs(container.terms or {}) do if term.is_duplicate then if term.original_has_children then max_depth = math.max(max_depth, 2) end else max_depth = math.max(max_depth, 1 + get_visible_tree_depth(term, skip_grandchildren)) end end end end return max_depth end local function as_param_list(val) if val == nil then return {} end if type(val) == "table" then return val end if type(val) == "string" and val ~= "" then return { val } end return {} end local TreeBuilder = {} -- Build a unique key for deduplication in the seen table function TreeBuilder.build_key(lang, title, args) local norm_lang_code = Util.get_norm_lang(lang):getFullCode() local is_table = type(args) == "table" local id = (is_table and args.id) or "" if title then return norm_lang_code .. ":" .. M.links.get_link_page(title, lang) .. ":" .. id end if is_table and args.status == M.data.STATUS.INLINE then local content_parts = {} for i = 1, #args do content_parts[i] = tostring(args[i]) end return norm_lang_code .. ":*:" .. id .. "\0" .. table.concat(content_parts, "\0") end return norm_lang_code .. ":*:" .. id end -- Copy parsed etymon modifiers onto a tree/supplement term node. function TreeBuilder.apply_etymon_fields(term, etymon_data) term.id = etymon_data.id term.gloss = etymon_data.gloss term.tr = etymon_data.tr term.ts = etymon_data.ts term.alt = etymon_data.alt term.genders = etymon_data.genders term.pos = etymon_data.pos term.ng = etymon_data.ng term.infl = etymon_data.infl term.refs = etymon_data.refs term.is_uncertain = etymon_data.unc term.lit = etymon_data.lit term.q = etymon_data.q term.qq = etymon_data.qq term.l = etymon_data.l term.ll = etymon_data.ll term.suppress_term = etymon_data.suppress_term term.unknown_term = etymon_data.unknown_term term.is_family = etymon_data.is_family term.override = etymon_data.override term.aftype = etymon_data.aftype term.postype = etymon_data.postype term.bor = etymon_data.bor term.lbor = etymon_data.lbor term.slbor = etymon_data.slbor term.link_page = etymon_data.link_page end function TreeBuilder.build_supplement_term(etymon_data, entry_lang, supplement_type) EtymonParser.check_supplement_term(etymon_data, entry_lang, supplement_type) local term = { lang = etymon_data.lang, title = etymon_data.term, children = {}, status = M.data.STATUS.OK, } TreeBuilder.apply_etymon_fields(term, etymon_data) return term end function TreeBuilder.build_supplement_terms(entry_lang, supplement_type, param_value) local terms = {} for _, term_param in ipairs(as_param_list(param_value)) do if type(term_param) == "string" and term_param ~= "" then local etymon_data = EtymonParser.parse_etymon(term_param, entry_lang) if etymon_data then table.insert(terms, TreeBuilder.build_supplement_term(etymon_data, entry_lang, supplement_type)) end end end return terms end -- Attach a |param= supplement defined in etymon_data.supplements (e.g. doublet=). function TreeBuilder.append_term_supplement(data_tree, entry_lang, supplement_type, param_value) local config = M.data.supplements[supplement_type] if not config then error("Unknown supplement '" .. tostring(supplement_type) .. "'.") end local terms = TreeBuilder.build_supplement_terms(entry_lang, supplement_type, param_value) if #terms == 0 then return end data_tree.supplements = data_tree.supplements or {} table.insert(data_tree.supplements, { type = supplement_type, config = config, terms = terms, }) M.tracking.record_keyword_usage(__state.toplevel_keyword_stats, supplement_type, entry_lang, entry_lang, true) end function TreeBuilder.build(lang, title, args, seen, depth, stop_recursion) seen = seen or {} depth = depth or 0 local is_toplevel = (depth == 0) if depth > __state.max_depth_reached then __state.max_depth_reached = depth end __state.total_nodes = __state.total_nodes + 1 local lang_code = lang:getCode() __state.language_count[lang_code] = (__state.language_count[lang_code] or 0) + 1 local current_id = (type(args) == "table" and args.id) or "" local key = TreeBuilder.build_key(lang, title, args) local node = { lang = lang, title = title, id = current_id, args = args, children = {}, status = M.data.STATUS.OK } if type(args) ~= "table" or seen[key] then node.status = args or M.data.STATUS.MISSING -- Mark as duplicate if we've seen this node before if seen[key] then node.is_duplicate = true node.duplicate_key = key local original_node = seen[key] if type(original_node) == "table" and original_node.children and #original_node.children > 0 then node.original_has_children = true end end return node end node.status = args.status or M.data.STATUS.OK seen[key] = node -- If stop_recursion is set, skip parsing children but check for visible children if stop_recursion then local keywords = M.data.keywords local has_visible_children = false for i = 2, #args do local param = args[i] if type(param) == "string" then local keyword_base = get_keyword_base(param) if keyword_base and keywords[keyword_base] then local _, kw_modifiers = EtymonParser.parse_keyword_modifiers(param:sub(1, 1) == ":" and param or (":" .. param)) if not keyword_invisible_in_tree(get_effective_keyword_info(keyword_base, kw_modifiers)) then has_visible_children = true break end elseif param:sub(1, 1) ~= ":" then -- It's a term (not a keyword), so there are visible children has_visible_children = true break end end end node.has_visible_children = has_visible_children return node end -- Parse args into keyword containers local current_keyword = "from" local current_keyword_modifiers = {} local current_container = nil local function ensure_container() if not current_container or current_container.keyword ~= current_keyword then local keyword_info = get_effective_keyword_info(current_keyword, current_keyword_modifiers) current_container = { keyword = current_keyword, keyword_info = keyword_info, keyword_modifiers = current_keyword_modifiers, terms = {}, } table.insert(node.children, current_container) -- Override keyword text/phrase for nominalization with <g:code> if current_keyword_modifiers.g and current_keyword == "nominalization" then local labels = get_nominalization_label_for_g(current_keyword_modifiers.g) if not labels then local codes = {} for c in pairs(M.data.nominalization_g_codes) do table.insert(codes, c) end table.sort(codes) error("Invalid <g:" .. tostring(current_keyword_modifiers.g) .. ">. Supported codes for nominalization: " .. table.concat(codes, ", ")) end current_container.keyword_info = copy_keyword_info(keyword_info) current_container.keyword_info.text = labels.text current_container.keyword_info.phrase = labels.phrase end end return current_container end local parse_context_lang = Util.resolve_context_lang(lang, args) for i = 2, #args do local param = args[i] if is_keyword(param) then local keyword, modifiers = EtymonParser.parse_keyword_modifiers(param) if not keyword then error("Invalid keyword '" .. param .. "'.") end current_keyword = keyword current_keyword_modifiers = modifiers current_container = nil -- Force new container for new keyword elseif type(param) == "string" and param:sub(1, 1) == ":" then reject_removed_surf_keyword(param) error("Invalid keyword '" .. param .. "'. Did you mean a valid keyword like ':bor', ':inh', etc.?") elseif type(param) == "string" then local etymon_data = EtymonParser.parse_etymon(param, parse_context_lang) if etymon_data and is_affix_group_keyword(current_keyword) then map_affix_part(etymon_data) end if etymon_data and has_linkable_term(etymon_data) and is_root_term(etymon_data, current_keyword) then etymon_data.link_page = M.roots.get_link_page(etymon_data.lang, etymon_data.term) end if etymon_data then -- Track keyword usage at top level M.tracking.record_keyword_usage(__state.toplevel_keyword_stats, current_keyword, lang, etymon_data.lang, is_toplevel) local term_node = {} local container -- Handle suppress_term (-) and unknown_term (empty or +) directly if etymon_data.suppress_term or etymon_data.unknown_term then container = ensure_container() if etymon_data.ety then local inline_args = EtymonParser.parse_inline_ety(etymon_data.ety, etymon_data.lang) inline_args.id = etymon_data.id inline_args.status = M.data.STATUS.INLINE term_node = TreeBuilder.build(etymon_data.lang, nil, inline_args, seen, depth + 1) else term_node = { lang = etymon_data.lang, children = {}, status = M.data.STATUS.OK, } end TreeBuilder.apply_etymon_fields(term_node, etymon_data) else -- Regular term: fetch arguments from page record_term_id_tracking(etymon_data) local etymon_args, page_of, resolved_etymon_id, descendants_check = DataRetriever.get_etymon_args(etymon_data, is_toplevel) -- Check for <ety> inline parameter doesn't override the scraped arguments, unless the latter are missing if etymon_data.ety then if etymon_args == M.data.STATUS.REDLINK or etymon_args == M.data.STATUS.MISSING then __state.current_page_has_inline_etymology = true if is_toplevel then __state.toplevel_has_inline_etymology = true end local inline_args = EtymonParser.parse_inline_ety(etymon_data.ety, etymon_data.lang) -- Track inline ety keywords too local inline_keyword = get_keyword(inline_args[2], true) if inline_keyword and #inline_args >= 3 then local inline_etymon = EtymonParser.parse_etymon(inline_args[3], etymon_data.lang) if inline_etymon then M.tracking.record_keyword_usage(__state.toplevel_keyword_stats, inline_keyword, etymon_data.lang, inline_etymon.lang, is_toplevel) end end inline_args.id = etymon_data.id inline_args.status = M.data.STATUS.INLINE etymon_args = inline_args term_node.page_of = __state.cached_etymon_pages[key] -- term node is on the same page as the parent else -- Scraped arguments exist, <ety> is redundant and ignored __state.current_page_has_redundant_etymology = true if is_toplevel then __state.toplevel_redundant_etymology = true end end end -- Ensure container exists before checking keyword info container = ensure_container() -- Check if current keyword has no_child_categories - if so, stop recursion local keyword_info = container.keyword_info local should_stop_recursion = (stop_recursion or (keyword_info and keyword_info.no_child_categories)) term_node = TreeBuilder.build(etymon_data.lang, etymon_data.term, etymon_args, seen, depth + 1, should_stop_recursion) term_node.target_key = Util.get_norm_lang(etymon_data.lang):getFullCode() .. ":" .. get_term_page(etymon_data) term_node.etymon_id = resolved_etymon_id -- The actual etymon id when resolved via senseid term_node.page_of = page_of TreeBuilder.apply_etymon_fields(term_node, etymon_data) term_node.missing_descendants_header, term_node.missing_descendants_entry = M.descendants.get_term_sync_flags(current_keyword, term_node.status, descendants_check) end table.insert(container.terms, term_node) end end end return node end -- Convert etymology tree to JSON-serializable table local function tree_to_json(node) local obj = { term = node.title, lang = node.lang:getCode(), lang_name = node.lang:getCanonicalName(), id = (node.id and node.id ~= "") and node.id or nil, status = node.status, is_uncertain = node.is_uncertain or nil, is_duplicate = node.is_duplicate or nil, gloss = node.gloss, transliteration = node.tr, transcription = node.ts, alt = node.alt, g = node.genders, pos = node.pos, ng = node.ng, infl = node.infl, children = {}, } for _, container in ipairs(node.children or {}) do local keyword_info = container.keyword_info if keyword_info then local container_obj = { keyword = container.keyword, keyword_label = keyword_info.text, keyword_abbrev = keyword_info.abbrev, is_group = keyword_info.is_group or nil, is_invisible = keyword_info.invisible or nil, is_uncertain = (container.keyword_modifiers and container.keyword_modifiers.unc) or nil, terms = {}, } for _, term in ipairs(container.terms or {}) do table.insert(container_obj.terms, tree_to_json(term)) end table.insert(obj.children, container_obj) end end return obj end -- Build and return the etymology data tree for a given term. function export.get_tree(lang, title, args, options) options = options or {} __state.entry_title = title __state.entry_lang_code = lang:getCode() __state.id_stats = M.tracking.new_id_stats() __state.skip_partial_etymology_category = options.skip_partial_etymology_category == true if options.validate then EtymonParser.validate(lang, args, options.id, title, options.pos, false) end local lang_code = lang:getCode() local start_index = (args[1] == lang_code) and 2 or 1 local tree_args = { [1] = lang_code, id = options.id or args.id } for i = start_index, #args do table.insert(tree_args, args[i]) end __state.cached_etymon_args[lang_code .. ":" .. title .. ":" .. (tree_args.id or "")] = tree_args local ety_data_tree = TreeBuilder.build(lang, title, tree_args) if options.json then return M.JSON.toJSON(tree_to_json(ety_data_tree)) end return ety_data_tree end -- Given a language code, page name and optionally the id= parameter, -- render the tree and only the etymology tree for the relevant page. -- Fetches and parses the corresponding {{etymon}} from the requested page, -- and any further pages needed to render the tree. -- Parameters can be passed either through the #invoke or as -- template parameters *through* an #invoke. function export.render_tree_for_etymon_on_page(frame) local frame_args = frame.args local parent_args = frame:getParent().args local langcode = frame_args[1] or parent_args[1] local pagename = frame_args[2] or parent_args[2] local id = frame_args["id"] or parent_args["id"] local display_title = frame_args["title"] or parent_args["title"] local parsed_title = mw.title.new(pagename, 0) local title if parsed_title.namespace == 0 then title = M.pages.safe_page_name(parsed_title) elseif parsed_title.namespace == 118 then title = "*" .. M.pages.safe_page_name(parsed_title) else error("Unsupported namespace for render_tree_for_etymon_on_page: " .. parsed_title.namespace) end local lang = Util.get_lang(langcode) __state.entry_title = title __state.entry_lang_code = lang:getCode() __state.id_stats = M.tracking.new_id_stats() -- Construct etymon_data for DataRetriever.get_args. local etymon_data = { lang = lang, term = title, id = id } local args, pagename = DataRetriever.get_etymon_args(etymon_data, true) if args == M.data.STATUS.MISSING then error("The etymon template was not found (language " .. langcode .. ", title '" .. title .. "'" .. (id and ", ID '" .. id .. "'" or ", no ID given") .. "). Page contents may have changed in the interim.") end local tree_title = display_title or title if lang:stripDiacritics(M.links.remove_links(tree_title)) ~= lang:stripDiacritics(M.links.remove_links(title)) then M.tracking.track_title_pagename_mismatch(lang) end reset_invocation_state() local ety_data_tree = export.get_tree(lang, tree_title, args, { validate = true, id = id, }) local output = {} table.insert(output, M.template_styles("Module:etymon/styles.css")) table.insert(output, M.tree.render({ data_tree = ety_data_tree, format_term_func = function(term, is_toplevel) return Util.format_term(term, is_toplevel, { gloss = "suppress", pos = "suppress", lit = "suppress", tree_ql = "suppress", }) end, })) return table.concat(output) end function export.main(frame) local parent_args = frame:getParent().args local args = M.parameters.process(parent_args, M.parameters_data.etymon) local lang = args[1] local etymon_args = args[2] local id = args.id local title = args.title local text = args.text local tree = args.tree local etydate = args.etydate local doublet = args.doublet local rfe = args.rfe local etystub = args.etystub local is_nonlemma = M.yesno(args.nl, false) local page_data = Util.get_page_data() if not title then title = page_data.pagename if page_data.namespace == "การสืบสร้าง" then title = "*" .. title end end local entry_pagename = page_data.pagename if page_data.namespace == "การสืบสร้าง" then entry_pagename = "*" .. entry_pagename end if lang:stripDiacritics(M.links.remove_links(title)) ~= lang:stripDiacritics(M.links.remove_links(entry_pagename)) then M.tracking.track_title_pagename_mismatch(lang) end local current_L2 = M.pages.get_current_L2() if current_L2 then local norm_lang = Util.get_norm_lang(lang) local norm_name = norm_lang:getCategoryName() --th if current_L2 ~= norm_name then local lang_desc = lang:getCode() .. " (" .. lang:getCanonicalName() .. ")" if norm_lang:getCode() ~= lang:getCode() then lang_desc = lang_desc .. ", normalized to " .. norm_lang:getCode() .. " (" .. norm_name .. ")" end error("Language '" .. lang_desc .. "' does not match the L2 header (" .. current_L2 .. ").") end end reset_invocation_state() local ety_data_tree = export.get_tree(lang, title, etymon_args, { validate = true, pos = args.pos, id = id, json = args.json, skip_partial_etymology_category = is_nonlemma, }) if args.json then return ety_data_tree end local output = {} local text_allowlist_mode = M.text_allowed.default_mode or "off" if text and text_allowlist_mode ~= "off" and not Util.is_text_param_allowed_for_lang(lang) then local msg = "Etymology texts (parameter <code>text=</code>) are not allowed for " .. lang:getFullName() .. "; see [[Template:etymon#Text allowlist|Template:etymon § Text allowlist]] for the list of languages that may use the <code>text=</code> parameter." if text_allowlist_mode == "error" then error(msg) else Util.add_warning(msg, true) end end local lang_exc = Util.get_lang_exception(lang) if lang_exc and lang_exc.disallow then local disallow = lang_exc.disallow local error_text = " for " .. lang:getFullName() if disallow.ref then error_text = error_text .. "; see " .. disallow.ref else error_text = error_text .. "." end if tree and disallow.tree then error("Etymology trees are not allowed" .. error_text) end if text and disallow.text then error("Etymology texts are not allowed" .. error_text) end end if etydate then local etydate_param_mods = M.parameter_utilities.construct_param_mods { { group = "ref" }, { param = "refn" }, { param = "nocap", type = "boolean" }, } local function generate_etydate_obj(etydate_text) local etydate_specs = {} for spec in etydate_text:gmatch("[^,]+") do table.insert(etydate_specs, mw.text.trim(spec)) end return { [1] = etydate_specs } end local parsed_etydate = M.parse_utilities.parse_inline_modifiers(etydate, { param_mods = etydate_param_mods, generate_obj = generate_etydate_obj }) local etydate_args = { [1] = parsed_etydate[1], nocap = parsed_etydate.nocap or false, } ety_data_tree.supplements = ety_data_tree.supplements or {} table.insert(ety_data_tree.supplements, { type = "etydate", etydate_text = M.etydate.format_etydate(etydate_args, { omit_refs = true }), etydate_refs = (parsed_etydate.refs and #parsed_etydate.refs > 0) and parsed_etydate.refs or nil, }) end TreeBuilder.append_term_supplement(ety_data_tree, lang, "doublet", doublet) local has_visible_children = node_has_visible_tree_children(ety_data_tree) -- Suppress trees for multiword entries and one-step chains local visible_tree_depth = get_visible_tree_depth(ety_data_tree) local is_trivial_tree = visible_tree_depth <= 2 local is_multiword = title:find("%s") ~= nil or title:find("_") ~= nil if tree and (is_multiword or is_trivial_tree) then tree = false end if tree then table.insert(output, M.template_styles("Module:etymon/styles.css")) table.insert(output, M.tree.render({ data_tree = ety_data_tree, format_term_func = function(term, is_toplevel) return Util.format_term(term, is_toplevel, { gloss = "suppress", pos = "suppress", lit = "suppress", tree_ql = "suppress", }) end, })) end local tree_disallowed = lang_exc and lang_exc.disallow and lang_exc.disallow.tree local ety_tree_json = M.JSON.toJSON(tree_to_json(ety_data_tree)) local anchor = M.anchors.etymonid(lang, id, { no_tree = args.notree, title = title, empty_tree = (not has_visible_children) or tree_disallowed, ety_tree_json = ety_tree_json, }) table.insert(output, anchor) local text_stop_lang_missing = nil if text then local max_depth, stop_at_blue_link, stop_at_lang, stop_at_lang_or_bluelink if text == "++" then max_depth, stop_at_blue_link = false, false elseif text == "+" then max_depth, stop_at_blue_link = 1, false elseif text == "*" then max_depth, stop_at_blue_link = false, true elseif text:match("^:[^*]+%*$") then -- Stop at a specific language OR first bluelink after it, e.g., ":ota*" -- If the target language is a redlink, continue to the first bluelink local lang_code = text:match("^:([^*]+)%*$") if lang_code and lang_code ~= "" then local lang_obj = Util.get_lang(lang_code, true) if lang_obj then stop_at_lang_or_bluelink = lang_code else Util.add_warning('Invalid language code "' .. lang_code .. '" in text parameter. Showing full chain instead.') max_depth, stop_at_blue_link = false, false end else Util.add_warning('Empty language code in text parameter. Showing full chain instead.') max_depth, stop_at_blue_link = false, false end elseif text:sub(1, 1) == ":" then -- Stop at a specific language, e.g., ":ar" stops at first Arabic term local lang_code = text:sub(2) if lang_code ~= "" then -- Validate the language code local lang_obj = Util.get_lang(lang_code, true) if lang_obj then stop_at_lang = lang_code else Util.add_warning('Invalid language code "' .. lang_code .. '" in text parameter. Showing full chain instead.') max_depth, stop_at_blue_link = false, false -- default to ++ end else Util.add_warning('Empty language code in text parameter. Showing full chain instead.') max_depth, stop_at_blue_link = false, false -- default to ++ end else local num = tonumber(text) if num and num >= 1 then max_depth, stop_at_blue_link = num, false else error('Invalid text value "' .. text .. '". Valid values are: "++" (full chain), "+" (first step only), "*" (until first blue link), a number (max steps), ":lang" (stop at language), or ":lang*" (stop at language or first bluelink if redlink)') end end local text_output, text_render_meta = M.text.render({ data_tree = ety_data_tree, format_term_func = Util.format_term, lang_matches_stop_code = Util.lang_matches_stop_code, max_depth = max_depth, stop_at_blue_link = stop_at_blue_link, curr_page = page_data.pagename, nodot = args.nodot, dot = args.dot, stop_at_lang = stop_at_lang, stop_at_lang_or_bluelink = stop_at_lang_or_bluelink, }) table.insert(output, text_output) if stop_at_lang and text_render_meta and not text_render_meta.stop_lang_reached then M.tracking.track_text_stop_lang_missing(lang, stop_at_lang) text_stop_lang_missing = stop_at_lang end end if rfe then table.insert(output, Util.expand_request_template(frame, "rfe", rfe, lang:getCode())) end if etystub then table.insert(output, Util.expand_request_template(frame, "etystub", etystub, lang:getCode())) end if is_nonlemma then table.insert(output, " " .. frame:expandTemplate({ title = "nonlemma", args = {}, })) end local categories = {} if Util.is_content_page() then M.tracking.track_tree_metrics({ max_depth_reached = __state.max_depth_reached, total_nodes = __state.total_nodes, language_count = __state.language_count, lang = lang, }) categories = M.categories.build({ data_tree = ety_data_tree, page_lang = lang, available_etymon_ids = __state.available_etymon_ids, senseid_parent_etymon = __state.senseid_parent_etymon, get_norm_lang_func = Util.get_norm_lang, lang_exc = lang_exc, suppress_categories = lang_exc and lang_exc.suppress_categories, nocat = args.nocat, tree = tree, text = text, exnihilo = args.exnihilo, toplevel_has_inline_etymology = __state.toplevel_has_inline_etymology, toplevel_redundant_etymology = __state.toplevel_redundant_etymology, toplevel_idless_etymon = __state.toplevel_idless_etymon, has_mismatched_id = __state.has_mismatched_id, linked_page_multiple_etymons_idless = __state.linked_page_multiple_etymons_idless, linked_page_partial_etymology_sections = __state.linked_page_partial_etymology_sections, text_stop_lang_missing = text_stop_lang_missing, }) M.tracking.track_keywords(__state.toplevel_keyword_stats, lang) M.tracking.track_page_id(lang, id) M.tracking.track_ids(__state.id_stats, lang) end if #categories > 0 then table.insert(output, M.categories.format(categories, lang)) end if __state.warnings then for i, warning in ipairs(__state.warnings) do table.insert(output, (i == 1 and "\n" or "") .. warning .. "\n") end end return table.concat(output) end return export hxkpb77r7ok0hnxlm6rl1jkzioi7jwi baraf 0 1083144 5775283 2762936 2026-10-14T10:11:56Z Octahedron80 267 5775283 wikitext text/x-wiki == ภาษาโซมาลี == === รากศัพท์ === เป็นไปได้ว่า{{bor+|so|fa|بَرْف|tr=barf}}, จาก{{der|so|pal|𐭥𐭯𐭫}}, จาก{{der|so|ira-pro|*wafrah||หิมะ}} === คำนาม === {{so-noun|head|g=m|pl=barafyo|gpl=f}} # [[หิมะ]] # [[น้ำแข็ง]] 1hgzne0gzstabd5oamgy5vdh29720kb دبیر 0 1838636 5775284 4894179 2026-10-14T10:12:32Z Octahedron80 267 5775284 wikitext text/x-wiki == ภาษาเปอร์เซีย == === รากศัพท์ === {{inh+|fa|pal|𐭣𐭯𐭩𐭥𐭥}} === การออกเสียง === {{fa-IPA|daˈbīr}} === คำนาม === {{fa-noun|tr=dabir}} # [[ครู]][[โรงเรียน]][[มัธยม]] # {{lb|fa|historical}} [[เลขานุการ]], [[อาลักษณ์]] # {{lb|fa|historical}} [[ผู้เชี่ยวชาญ]]ด้าน[[การเขียน]] ==== คำเกี่ยวข้อง ==== * {{l|fa|دبیرستان|tr=dabirestân}} * {{l|fa|دبیرستانی|tr=dabirestâni}} ==== คำสืบทอด ==== * {{desc|az|dəbir|bor=1}} * {{desc|bor=1|ota|دبیر|tr=debir}} ==== อ้างอิง ==== * {{R:fa:Steingass|دبير}} kfxe8vkbuiv4p6s9ldcvqmus96uq8vg มอดูล:etymon/data 828 2297436 5775226 5757794 2026-10-14T07:24:29Z Octahedron80 267 5775226 Scribunto text/plain local export = {} export.STATUS = { OK = "ok", INLINE = "inline", MISSING = "missing", REDLINK = "redlink", AMBIGUOUS = "ambiguous", } export.TRANSITIVE = { ALWAYS = "always", -- always recurse into children NEVER = "never", -- never recurse into children CROSS_LANG = "cross_lang", -- only recurse when source lang differs from target lang (but pos chain continues) CROSS_LANG_NO_INTERNAL_SOURCE = "cross_lang_no_internal_source", -- like CROSS_LANG, but source breaks for internal derivations in the same language context } -- Deep merge tables (nested tables are merged recursively, later values override earlier) local function deep_merge(...) local result = {} for _, t in ipairs({ ... }) do for k, v in pairs(t) do if type(v) == "table" and type(result[k]) == "table" then result[k] = deep_merge(result[k], v) else result[k] = v end end end return result end local function make_glossary_link(term, display_text) if not term then return display_text end return "[[ภาคผนวก:อภิธานศัพท์#" .. term:gsub(" ", "_") .. "|" .. display_text .. "]]" end -- Extract base word and connector from text like "Borrowed from" or "calque of" local function split_glossary_text(text) for _, pattern in ipairs({ "^(.-)(%s+[Oo][Ff])$", "^(.-)(%s+[Ff][Rr][Oo][Mm])$","^(.-)(จาก)$", "^(.-)(ของ)$" }) do local base, rest = text:match(pattern) if base then return base, rest end end return text, "" end local TRANSITIVE = export.TRANSITIVE local function create_keyword(opts) local entry = { is_group = opts.is_group or false, abbrev = opts.abbrev, glossary = opts.glossary, transitive = opts.transitive or TRANSITIVE.ALWAYS, -- default "always" inherited_chain = opts.inherited_chain or false, affix_categories = opts.affix_categories or false, borrowing_type = opts.borrowing_type, specialized_borrowing = opts.specialized_borrowing, toplevel_category = opts.toplevel_category, no_child_categories = opts.no_child_categories or false, source_category_type = opts.source_category_type, invisible = (opts.invisible == true and "all") or opts.invisible or false, pos_override = opts.pos_override, new_sentence = opts.new_sentence or false, separate_clause = opts.separate_clause or false, default_conj = opts.default_conj, min_etymons = opts.min_etymons, max_etymons = opts.max_etymons, term_rules = opts.term_rules, aliases = opts.aliases, } -- Only set text/phrase when visible in text (invisible ~= "all" and ~= "text") local inv = entry.invisible if inv ~= "all" and inv ~= "text" then entry.phrase = opts.phrase if opts.text then if opts.glossary then local base_word, rest = split_glossary_text(opts.text) entry.text = make_glossary_link(opts.glossary, base_word) .. rest else entry.text = opts.text end end end return entry end -- Shared defaults for keyword groups local DEFAULTS = { -- Keywords that pass through inheritance chain inheritance = { transitive = TRANSITIVE.ALWAYS, inherited_chain = true, }, -- Standard transitive derivation transitive = { transitive = TRANSITIVE.ALWAYS, }, -- Standard for internal derivations: transitive across languages, but not within them internal_derivation = { transitive = TRANSITIVE.CROSS_LANG, }, -- Borrowing keywords borrowing = { transitive = TRANSITIVE.ALWAYS, }, -- Affix group keywords (compound words, blends, etc.) affix_group = { is_group = true, min_etymons = 2, transitive = TRANSITIVE.CROSS_LANG, affix_categories = true, }, -- Calque-like keywords (calque, partial calque, semantic loan) calque_like = { transitive = TRANSITIVE.NEVER, no_child_categories = true, new_sentence = true, }, -- Non-transitive influence influence_like = { transitive = TRANSITIVE.NEVER, no_child_categories = true, }, } export.keywords = { -- -- Inheritance keywords -- ["from"] = create_keyword(deep_merge(DEFAULTS.inheritance, { text = "จาก", phrase = "จาก", term_rules = { entry_lang = true }, })), ["inherited"] = create_keyword(deep_merge(DEFAULTS.inheritance, { text = "สืบทอดจาก", phrase = "สืบทอดจาก", abbrev = "สืบทอด", glossary = "inherited", aliases = { "inh" }, term_rules = { family = "disallowed", family_suffix = "; use a specific language.", ancestor_check = true, }, })), -- -- Basic derivation keywords -- ["uder"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "จาก", phrase = "จาก", toplevel_category = "undefined derivations", })), ["derived"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "รับมาจาก", phrase = "รับมาจาก", abbrev = "รับ", glossary = "derived terms", aliases = { "der" }, })), -- -- Affix/compound group keywords -- ["affix"] = create_keyword(deep_merge(DEFAULTS.affix_group, { text = "จาก", phrase = "จาก", min_etymons = 1, aliases = { "af" }, })), ["blend"] = create_keyword(deep_merge(DEFAULTS.affix_group, { text = "หลอมมาจาก", phrase = "หลอมมาจาก", abbrev = "หลอม", glossary = "blend", toplevel_category = "blends", })), ["univerbation"] = create_keyword(deep_merge(DEFAULTS.affix_group, { text = "รวมเป็นหนึ่งคำจาก", phrase = "รวมเป็นหนึ่งคำจาก", abbrev = "รวมหนึ่งคำ", glossary = "univerbation", toplevel_category = "univerbations", min_etymons = 1, aliases = { "univ" }, })), ["vrd-af"] = create_keyword(deep_merge(DEFAULTS.affix_group, { text = "Vṛddhi derivative of", phrase = "a vṛddhi derivative of", abbrev = "vṛd.", glossary = "vṛddhi derivative", toplevel_category = "vrddhi derivatives", })), ["sa-af"] = create_keyword(deep_merge(DEFAULTS.affix_group, { text = "[[Sanskritic]] formation from", phrase = "a [[Sanskritic]] formation of", toplevel_category = "Sanskritic formations", })), -- -- Borrowing keywords -- ["bor"] = create_keyword(deep_merge(DEFAULTS.borrowing, { text = "ยืมมาจาก", phrase = "ยืมมาจาก", abbrev = "ยืม", glossary = "loanword", borrowing_type = "borrowed", aliases = { "borrowed" }, })), ["lbor"] = create_keyword(deep_merge(DEFAULTS.borrowing, { text = "ยืมโดยเรียนรู้จาก", phrase = "ยืมโดยเรียนรู้จาก", abbrev = "เรียนรู้", glossary = "learned borrowing", specialized_borrowing = "learned", })), ["obor"] = create_keyword(deep_merge(DEFAULTS.borrowing, { text = "ยืมอักขรวิธีจาก", phrase = "ยืมอักขรวิธีจาก", abbrev = "อักขรวิธี", glossary = "orthographic borrowing", specialized_borrowing = "orthographic", })), ["slbor"] = create_keyword(deep_merge(DEFAULTS.borrowing, { text = "ยืมโดยกึ่งเรียนรู้จาก", phrase = "ยืมโดยกึ่งเรียนรู้จาก", abbrev = "กึ่งเรียนรู้", glossary = "semi-learned borrowing", specialized_borrowing = "semi-learned", aliases = { "slb" }, })), ["ubor"] = create_keyword(deep_merge(DEFAULTS.borrowing, { text = "ยืมโดยไม่ดัดแปลงจาก", phrase = "ยืมโดยไม่ดัดแปลงจาก", abbrev = "ไม่ดัดแปลง", glossary = "unadapted borrowing", specialized_borrowing = "unadapted", })), -- -- Calque-like keywords (non-transitive, start new sentence) -- ["calque"] = create_keyword(deep_merge(DEFAULTS.calque_like, { text = "แปลตรงตัวจาก", phrase = "แปลตรงตัวจาก", abbrev = "แปลตรง", glossary = "calque", specialized_borrowing = "calque", aliases = { "cal", "clq" }, })), ["partial calque"] = create_keyword(deep_merge(DEFAULTS.calque_like, { text = "แปลตรงตัวบางส่วนจาก", phrase = "แปลตรงตัวบางส่วนจาก", abbrev = "แปลส่วน", glossary = "partial calque", specialized_borrowing = "partial-calque", aliases = { "pcal", "pclq" }, })), ["semantic loan"] = create_keyword(deep_merge(DEFAULTS.calque_like, { text = "ยืมความหมายจาก", phrase = "ยืมความหมายจาก", abbrev = "ความหมาย", glossary = "semantic loan", specialized_borrowing = "semantic-loan", aliases = { "sl" }, })), ["psm"] = create_keyword(deep_merge(DEFAULTS.calque_like, { text = "Phono-semantic matching of", phrase = "a phono-semantic matching of", abbrev = "psm.", glossary = "phono-semantic matching", specialized_borrowing = "phono-semantic-matching", aliases = { "phono-semantic matching" }, })), -- -- Influence keywords (non-transitive, separate clause) -- ["influence"] = create_keyword(deep_merge(DEFAULTS.influence_like, { text = "ได้รับอิทธิพลจาก", phrase = "ได้รับอิทธิพลจาก", abbrev = "อิทธิพล", glossary = "contamination", separate_clause = true, })), -- -- Morphological derivation keywords -- ["clipping"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "ตัดทอนจาก", phrase = "ตัดทอนจาก", abbrev = "ตัด", glossary = "clipping", toplevel_category = "clippings", aliases = { "clip" }, })), ["ellipsis"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Ellipsis of", phrase = "ellipsis of", abbrev = "ellip.", glossary = "ellipsis", toplevel_category = "ellipses", aliases = { "ellip" }, })), ["back-formation"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "ผันรูปย้อนกลับจาก", phrase = "ผันรูปย้อนกลับจาก", abbrev = "ผันย้อน", glossary = "back-formation", toplevel_category = "back-formations", aliases = { "bf" }, })), ["nominalization"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Nominalization of", phrase = "a nominalization of", abbrev = "nom.", glossary = "nominalization", toplevel_category = "nominalizations", aliases = { "nom" }, })), ["transliteration"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "ถอดอักษรจาก", phrase = "ยืมมาจาก", abbrev = "ถอดอักษร", glossary = "transliteration", aliases = { "translit" }, })), ["vrd"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Vṛddhi derivative of", phrase = "a vṛddhi derivative of", abbrev = "vṛd.", glossary = "vṛddhi derivative", toplevel_category = "vrddhi derivatives", })), ["apheretic"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Apheretic form of", phrase = "an apheretic form of", abbrev = "aph.", glossary = "apheresis", aliases = { "apheresis", "aphetic" }, })), ["denominal"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Denominal verb from", phrase = "denominal verb from", abbrev = "denom.", glossary = "denominal", toplevel_category = "denominal verbs", aliases = { "denom" }, })), ["deverbal"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Deverbal from", phrase = "deverbal from", abbrev = "deverb.", glossary = "deverbal", toplevel_category = "deverbals", })), ["reduplication"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "คำซ้ำของ", phrase = "คำซ้ำของ", abbrev = "ซ้ำ", glossary = "reduplication", toplevel_category = "reduplications", aliases = { "rdp", "redup" }, })), ["abbreviation"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "คำย่อของ", phrase = "คำย่อของ", abbrev = "ย่อ", glossary = "abbreviation", aliases = { "abbr", "abbrev" }, })), ["syllabic abbreviation"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Syllabic abbreviation of", phrase = "syllabic abbreviation of", abbrev = "syl. abbr.", glossary = "syllabic abbreviation", aliases = { "sylabbr", "sylabbrev" }, })), ["acronym"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "อักษรย่อรวมพยางค์ของ", phrase = "อักษรย่อรวมพยางค์ของ", abbrev = "ย่อพยางค์", glossary = "acronym", aliases = { "acro" }, })), ["initialism"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "อักษรย่อของ", phrase = "อักษรย่อของ", abbrev = "ย่ออักษร", glossary = "initialism", aliases = { "init" }, })), ["metathesis"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Metathesis of", phrase = "metathesis of", abbrev = "meta.", glossary = "metathesis", toplevel_category = "words derived through metathesis", aliases = { "meta" }, })), -- -- Invisible keywords (no text output) -- ["root"] = create_keyword { transitive = TRANSITIVE.ALWAYS, invisible = "all", pos_override = "root", }, ["afeq"] = create_keyword(deep_merge(DEFAULTS.affix_group, { text = "จาก", phrase = "จาก", transitive = TRANSITIVE.NEVER, min_etymons = 1, invisible = "all", })), } -- Template-parameter supplements. export.supplements = { doublet = { text = "ร่วมรากกับ", phrase = "ร่วมรากกับ", glossary = "doublet", toplevel_category = "doublets", default_conj = "and", term_rules = { entry_lang = true, require_term = true, disallow = { "suppress", "unknown", "family" }, }, }, } local aliases_to_register = {} local canonical_aliases = {} -- Map every keyword (canonical or alias) to its canonical form for consistent checks and tracking. export.keyword_canonical = {} for name, keyword_data in pairs(export.keywords) do export.keyword_canonical[name] = name if keyword_data.aliases then canonical_aliases[name] = keyword_data.aliases for _, alias in ipairs(keyword_data.aliases) do if export.keywords[alias] then error("Alias '" .. alias .. "' defined in keyword '" .. name .. "' collides with existing keyword '" .. alias .. "'.") end if aliases_to_register[alias] then error("Alias '" .. alias .. "' defined in keyword '" .. name .. "' is already claimed by another keyword.") end aliases_to_register[alias] = keyword_data export.keyword_canonical[alias] = name end keyword_data.aliases = nil end end for alias, data in pairs(aliases_to_register) do export.keywords[alias] = data end -- -- Language exception presets -- local EXCEPTION_PRESETS = { -- Fully disallowed: no tree, no text, no categories disallowed = { disallow = { tree = true, text = true }, suppress_categories = true, }, -- Suppress transliteration only no_translit = { suppress_tr = true, }, -- Suppress all categories only no_categories = { suppress_categories = true, }, } --[=[ Available exception options: disallow = { Related options for disallowing output: tree Disallow etymology trees for this language text Disallow etymology text generation for this language ref Reference link shown when tree/text is disallowed } suppress_tr Suppress transliteration in links suppress_categories Suppress all category generation normalize_to Normalize language code to a different code normalize_from_families Apply normalization to languages in these families normalize_exclude_families Exclude these families from normalization keyword_overrides Per-keyword categorisation overrides (e.g. { ["af"] = { transitive = TRANSITIVE.NEVER } }) ]=] local function create_exception(preset, overrides) local base = preset and EXCEPTION_PRESETS[preset] or {} return deep_merge(base, overrides or {}) end export.config = { lang_exceptions = { ["zh"] = create_exception("disallowed", { disallow = { ref = "[[Wiktionary:Beer parlour/2025/May#Template:etymon for Chinese]]" }, suppress_tr = true, normalize_to = "zh", normalize_from_families = { "zhx" }, normalize_exclude_families = { "qfa-cnt" }, }), }, } -- Supported codes for the nominalization <g:code> modifier (subset of common gender/number-style codes) -- Short names of the <aftype:> modifier, as [[Module:affix]] names affix types. export.aftype_aliases = { ["pre"] = "อุปสรรค", ["suf"] = "ปัจจัย", ["in"] = "อาคม", ["inter"] = "หน่วยคำเติมเชื่อม", ["circum"] = "หน่วยคำเติมคร่อม", ["naf"] = "ไม่ใช่หน่วยคำเติม", ["root"] = "ไม่ใช่หน่วยคำเติม", ["prefix"] = "อุปสรรค", ["suffix"] = "ปัจจัย", ["infix"] = "อาคม", ["interfix"] = "หน่วยคำเติมเชื่อม", ["circumfix"] = "หน่วยคำเติมคร่อม", ["non-affix"] = "ไม่ใช่หน่วยคำเติม", ["ราก"] = "ไม่ใช่หน่วยคำเติม", } export.nominalization_g_codes = { ["m"] = "เพศชาย", ["f"] = "เพศหญิง", ["n"] = "เพศกลาง", ["c"] = "เพศรวม", ["gneut"] = "ไม่ระบุเพศ", ["s"] = "เอกพจน์", ["p"] = "พหูพจน์", ["d"] = "ทวิพจน์", ["pauc"] = "ตัวบอกความน้อย", ["mf"] = "เพศชายหรือเพศหญิง", ["fm"] = "เพศชายหรือเพศหญิง", ["mfn"] = "เพศชาย เพศหญิง หรือเพศกลาง", ["mnf"] = "เพศชาย เพศหญิง หรือเพศกลาง", ["fmn"] = "เพศชาย เพศหญิง หรือเพศกลาง", ["fnm"] = "เพศชาย เพศหญิง หรือเพศกลาง", ["nmf"] = "เพศชาย เพศหญิง หรือเพศกลาง", ["nfm"] = "เพศชาย เพศหญิง หรือเพศกลาง", } -- -- Propagate keyword overrides to aliases -- if export.config.lang_exceptions then for lang_code, exception in pairs(export.config.lang_exceptions) do if exception.keyword_overrides then for canonical, aliases in pairs(canonical_aliases) do if exception.keyword_overrides[canonical] then local override_data = exception.keyword_overrides[canonical] for _, alias in ipairs(aliases) do if not exception.keyword_overrides[alias] then exception.keyword_overrides[alias] = override_data end end end end end end end return export 34gfm9wlhv0709lw3rocyabkkkcralb มอดูล:etymon/categories 828 2297439 5775227 5757836 2026-10-14T07:25:12Z Octahedron80 267 5775227 Scribunto text/plain local export = {} export.STATUS = { OK = "ok", INLINE = "inline", MISSING = "missing", REDLINK = "redlink", AMBIGUOUS = "ambiguous", } export.TRANSITIVE = { ALWAYS = "always", -- always recurse into children NEVER = "never", -- never recurse into children CROSS_LANG = "cross_lang", -- only recurse when source lang differs from target lang (but pos chain continues) CROSS_LANG_NO_INTERNAL_SOURCE = "cross_lang_no_internal_source", -- like CROSS_LANG, but source breaks for internal derivations in the same language context } -- Deep merge tables (nested tables are merged recursively, later values override earlier) local function deep_merge(...) local result = {} for _, t in ipairs({ ... }) do for k, v in pairs(t) do if type(v) == "table" and type(result[k]) == "table" then result[k] = deep_merge(result[k], v) else result[k] = v end end end return result end local function make_glossary_link(term, display_text) if not term then return display_text end return "[[ภาคผนวก:อภิธานศัพท์#" .. term:gsub(" ", "_") .. "|" .. display_text .. "]]" end -- Extract base word and connector from text like "Borrowed from" or "calque of" local function split_glossary_text(text) for _, pattern in ipairs({ "^(.-)(%s+[Oo][Ff])$", "^(.-)(%s+[Ff][Rr][Oo][Mm])$","^(.-)(จาก)$", "^(.-)(ของ)$" }) do local base, rest = text:match(pattern) if base then return base, rest end end return text, "" end local TRANSITIVE = export.TRANSITIVE local function create_keyword(opts) local entry = { is_group = opts.is_group or false, abbrev = opts.abbrev, glossary = opts.glossary, transitive = opts.transitive or TRANSITIVE.ALWAYS, -- default "always" inherited_chain = opts.inherited_chain or false, affix_categories = opts.affix_categories or false, borrowing_type = opts.borrowing_type, specialized_borrowing = opts.specialized_borrowing, toplevel_category = opts.toplevel_category, no_child_categories = opts.no_child_categories or false, source_category_type = opts.source_category_type, invisible = (opts.invisible == true and "all") or opts.invisible or false, pos_override = opts.pos_override, new_sentence = opts.new_sentence or false, separate_clause = opts.separate_clause or false, default_conj = opts.default_conj, min_etymons = opts.min_etymons, max_etymons = opts.max_etymons, term_rules = opts.term_rules, aliases = opts.aliases, } -- Only set text/phrase when visible in text (invisible ~= "all" and ~= "text") local inv = entry.invisible if inv ~= "all" and inv ~= "text" then entry.phrase = opts.phrase if opts.text then if opts.glossary then local base_word, rest = split_glossary_text(opts.text) entry.text = make_glossary_link(opts.glossary, base_word) .. rest else entry.text = opts.text end end end return entry end -- Shared defaults for keyword groups local DEFAULTS = { -- Keywords that pass through inheritance chain inheritance = { transitive = TRANSITIVE.ALWAYS, inherited_chain = true, }, -- Standard transitive derivation transitive = { transitive = TRANSITIVE.ALWAYS, }, -- Standard for internal derivations: transitive across languages, but not within them internal_derivation = { transitive = TRANSITIVE.CROSS_LANG, }, -- Borrowing keywords borrowing = { transitive = TRANSITIVE.ALWAYS, }, -- Affix group keywords (compound words, blends, etc.) affix_group = { is_group = true, min_etymons = 2, transitive = TRANSITIVE.CROSS_LANG, affix_categories = true, }, -- Calque-like keywords (calque, partial calque, semantic loan) calque_like = { transitive = TRANSITIVE.NEVER, no_child_categories = true, new_sentence = true, }, -- Non-transitive influence influence_like = { transitive = TRANSITIVE.NEVER, no_child_categories = true, }, } export.keywords = { -- -- Inheritance keywords -- ["from"] = create_keyword(deep_merge(DEFAULTS.inheritance, { text = "จาก", phrase = "จาก", term_rules = { entry_lang = true }, })), ["inherited"] = create_keyword(deep_merge(DEFAULTS.inheritance, { text = "สืบทอดจาก", phrase = "สืบทอดจาก", abbrev = "สืบทอด", glossary = "inherited", aliases = { "inh" }, term_rules = { family = "disallowed", family_suffix = "; use a specific language.", ancestor_check = true, }, })), -- -- Basic derivation keywords -- ["uder"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "จาก", phrase = "จาก", toplevel_category = "undefined derivations", })), ["derived"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "รับมาจาก", phrase = "รับมาจาก", abbrev = "รับ", glossary = "derived terms", aliases = { "der" }, })), -- -- Affix/compound group keywords -- ["affix"] = create_keyword(deep_merge(DEFAULTS.affix_group, { text = "จาก", phrase = "จาก", min_etymons = 1, aliases = { "af" }, })), ["blend"] = create_keyword(deep_merge(DEFAULTS.affix_group, { text = "หลอมมาจาก", phrase = "หลอมมาจาก", abbrev = "หลอม", glossary = "blend", toplevel_category = "blends", })), ["univerbation"] = create_keyword(deep_merge(DEFAULTS.affix_group, { text = "รวมเป็นหนึ่งคำจาก", phrase = "รวมเป็นหนึ่งคำจาก", abbrev = "รวมหนึ่งคำ", glossary = "univerbation", toplevel_category = "univerbations", min_etymons = 1, aliases = { "univ" }, })), ["vrd-af"] = create_keyword(deep_merge(DEFAULTS.affix_group, { text = "Vṛddhi derivative of", phrase = "a vṛddhi derivative of", abbrev = "vṛd.", glossary = "vṛddhi derivative", toplevel_category = "vrddhi derivatives", })), ["sa-af"] = create_keyword(deep_merge(DEFAULTS.affix_group, { text = "[[Sanskritic]] formation from", phrase = "a [[Sanskritic]] formation of", toplevel_category = "Sanskritic formations", })), -- -- Borrowing keywords -- ["bor"] = create_keyword(deep_merge(DEFAULTS.borrowing, { text = "ยืมมาจาก", phrase = "ยืมมาจาก", abbrev = "ยืม", glossary = "loanword", borrowing_type = "borrowed", aliases = { "borrowed" }, })), ["lbor"] = create_keyword(deep_merge(DEFAULTS.borrowing, { text = "ยืมโดยเรียนรู้จาก", phrase = "ยืมโดยเรียนรู้จาก", abbrev = "เรียนรู้", glossary = "learned borrowing", specialized_borrowing = "learned", })), ["obor"] = create_keyword(deep_merge(DEFAULTS.borrowing, { text = "ยืมอักขรวิธีจาก", phrase = "ยืมอักขรวิธีจาก", abbrev = "อักขรวิธี", glossary = "orthographic borrowing", specialized_borrowing = "orthographic", })), ["slbor"] = create_keyword(deep_merge(DEFAULTS.borrowing, { text = "ยืมโดยกึ่งเรียนรู้จาก", phrase = "ยืมโดยกึ่งเรียนรู้จาก", abbrev = "กึ่งเรียนรู้", glossary = "semi-learned borrowing", specialized_borrowing = "semi-learned", aliases = { "slb" }, })), ["ubor"] = create_keyword(deep_merge(DEFAULTS.borrowing, { text = "ยืมโดยไม่ดัดแปลงจาก", phrase = "ยืมโดยไม่ดัดแปลงจาก", abbrev = "ไม่ดัดแปลง", glossary = "unadapted borrowing", specialized_borrowing = "unadapted", })), -- -- Calque-like keywords (non-transitive, start new sentence) -- ["calque"] = create_keyword(deep_merge(DEFAULTS.calque_like, { text = "แปลตรงตัวจาก", phrase = "แปลตรงตัวจาก", abbrev = "แปลตรง", glossary = "calque", specialized_borrowing = "calque", aliases = { "cal", "clq" }, })), ["partial calque"] = create_keyword(deep_merge(DEFAULTS.calque_like, { text = "แปลตรงตัวบางส่วนจาก", phrase = "แปลตรงตัวบางส่วนจาก", abbrev = "แปลส่วน", glossary = "partial calque", specialized_borrowing = "partial-calque", aliases = { "pcal", "pclq" }, })), ["semantic loan"] = create_keyword(deep_merge(DEFAULTS.calque_like, { text = "ยืมความหมายจาก", phrase = "ยืมความหมายจาก", abbrev = "ความหมาย", glossary = "semantic loan", specialized_borrowing = "semantic-loan", aliases = { "sl" }, })), ["psm"] = create_keyword(deep_merge(DEFAULTS.calque_like, { text = "Phono-semantic matching of", phrase = "a phono-semantic matching of", abbrev = "psm.", glossary = "phono-semantic matching", specialized_borrowing = "phono-semantic-matching", aliases = { "phono-semantic matching" }, })), -- -- Influence keywords (non-transitive, separate clause) -- ["influence"] = create_keyword(deep_merge(DEFAULTS.influence_like, { text = "ได้รับอิทธิพลจาก", phrase = "ได้รับอิทธิพลจาก", abbrev = "อิทธิพล", glossary = "contamination", separate_clause = true, })), -- -- Morphological derivation keywords -- ["clipping"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "ตัดทอนจาก", phrase = "ตัดทอนจาก", abbrev = "ตัด", glossary = "clipping", toplevel_category = "clippings", aliases = { "clip" }, })), ["ellipsis"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Ellipsis of", phrase = "ellipsis of", abbrev = "ellip.", glossary = "ellipsis", toplevel_category = "ellipses", aliases = { "ellip" }, })), ["back-formation"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "ผันรูปย้อนกลับจาก", phrase = "ผันรูปย้อนกลับจาก", abbrev = "ผันย้อน", glossary = "back-formation", toplevel_category = "back-formations", aliases = { "bf" }, })), ["nominalization"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Nominalization of", phrase = "a nominalization of", abbrev = "nom.", glossary = "nominalization", toplevel_category = "nominalizations", aliases = { "nom" }, })), ["transliteration"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "ถอดอักษรจาก", phrase = "ยืมมาจาก", abbrev = "ถอดอักษร", glossary = "transliteration", aliases = { "translit" }, })), ["vrd"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Vṛddhi derivative of", phrase = "a vṛddhi derivative of", abbrev = "vṛd.", glossary = "vṛddhi derivative", toplevel_category = "vrddhi derivatives", })), ["apheretic"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Apheretic form of", phrase = "an apheretic form of", abbrev = "aph.", glossary = "apheresis", aliases = { "apheresis", "aphetic" }, })), ["denominal"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Denominal verb from", phrase = "denominal verb from", abbrev = "denom.", glossary = "denominal", toplevel_category = "denominal verbs", aliases = { "denom" }, })), ["deverbal"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Deverbal from", phrase = "deverbal from", abbrev = "deverb.", glossary = "deverbal", toplevel_category = "deverbals", })), ["reduplication"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "คำซ้ำของ", phrase = "คำซ้ำของ", abbrev = "ซ้ำ", glossary = "reduplication", toplevel_category = "reduplications", aliases = { "rdp", "redup" }, })), ["abbreviation"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "คำย่อของ", phrase = "คำย่อของ", abbrev = "ย่อ", glossary = "abbreviation", aliases = { "abbr", "abbrev" }, })), ["syllabic abbreviation"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Syllabic abbreviation of", phrase = "syllabic abbreviation of", abbrev = "syl. abbr.", glossary = "syllabic abbreviation", aliases = { "sylabbr", "sylabbrev" }, })), ["acronym"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "อักษรย่อรวมพยางค์ของ", phrase = "อักษรย่อรวมพยางค์ของ", abbrev = "ย่อพยางค์", glossary = "acronym", aliases = { "acro" }, })), ["initialism"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "อักษรย่อของ", phrase = "อักษรย่อของ", abbrev = "ย่ออักษร", glossary = "initialism", aliases = { "init" }, })), ["metathesis"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Metathesis of", phrase = "metathesis of", abbrev = "meta.", glossary = "metathesis", toplevel_category = "words derived through metathesis", aliases = { "meta" }, })), -- -- Invisible keywords (no text output) -- ["root"] = create_keyword { transitive = TRANSITIVE.ALWAYS, invisible = "all", pos_override = "root", }, ["afeq"] = create_keyword(deep_merge(DEFAULTS.affix_group, { text = "จาก", phrase = "จาก", transitive = TRANSITIVE.NEVER, min_etymons = 1, invisible = "all", })), } -- Template-parameter supplements. export.supplements = { doublet = { text = "ร่วมรากกับ", phrase = "ร่วมรากกับ", glossary = "doublet", toplevel_category = "doublets", default_conj = "and", term_rules = { entry_lang = true, require_term = true, disallow = { "suppress", "unknown", "family" }, }, }, } local aliases_to_register = {} local canonical_aliases = {} -- Map every keyword (canonical or alias) to its canonical form for consistent checks and tracking. export.keyword_canonical = {} for name, keyword_data in pairs(export.keywords) do export.keyword_canonical[name] = name if keyword_data.aliases then canonical_aliases[name] = keyword_data.aliases for _, alias in ipairs(keyword_data.aliases) do if export.keywords[alias] then error("Alias '" .. alias .. "' defined in keyword '" .. name .. "' collides with existing keyword '" .. alias .. "'.") end if aliases_to_register[alias] then error("Alias '" .. alias .. "' defined in keyword '" .. name .. "' is already claimed by another keyword.") end aliases_to_register[alias] = keyword_data export.keyword_canonical[alias] = name end keyword_data.aliases = nil end end for alias, data in pairs(aliases_to_register) do export.keywords[alias] = data end -- -- Language exception presets -- local EXCEPTION_PRESETS = { -- Fully disallowed: no tree, no text, no categories disallowed = { disallow = { tree = true, text = true }, suppress_categories = true, }, -- Suppress transliteration only no_translit = { suppress_tr = true, }, -- Suppress all categories only no_categories = { suppress_categories = true, }, } --[=[ Available exception options: disallow = { Related options for disallowing output: tree Disallow etymology trees for this language text Disallow etymology text generation for this language ref Reference link shown when tree/text is disallowed } suppress_tr Suppress transliteration in links suppress_categories Suppress all category generation normalize_to Normalize language code to a different code normalize_from_families Apply normalization to languages in these families normalize_exclude_families Exclude these families from normalization keyword_overrides Per-keyword categorisation overrides (e.g. { ["af"] = { transitive = TRANSITIVE.NEVER } }) ]=] local function create_exception(preset, overrides) local base = preset and EXCEPTION_PRESETS[preset] or {} return deep_merge(base, overrides or {}) end export.config = { lang_exceptions = { ["zh"] = create_exception("disallowed", { disallow = { ref = "[[Wiktionary:Beer parlour/2025/May#Template:etymon for Chinese]]" }, suppress_tr = true, normalize_to = "zh", normalize_from_families = { "zhx" }, normalize_exclude_families = { "qfa-cnt" }, }), }, } -- Supported codes for the nominalization <g:code> modifier (subset of common gender/number-style codes) -- Short names of the <aftype:> modifier, as [[Module:affix]] names affix types. export.aftype_aliases = { ["pre"] = "อุปสรรค", ["suf"] = "ปัจจัย", ["in"] = "อาคม", ["inter"] = "หน่วยคำเติมเชื่อม", ["circum"] = "หน่วยคำเติมคร่อม", ["naf"] = "ไม่ใช่หน่วยคำเติม", ["root"] = "ไม่ใช่หน่วยคำเติม", ["prefix"] = "อุปสรรค", ["suffix"] = "ปัจจัย", ["infix"] = "อาคม", ["interfix"] = "หน่วยคำเติมเชื่อม", ["circumfix"] = "หน่วยคำเติมคร่อม", ["non-affix"] = "ไม่ใช่หน่วยคำเติม", ["ราก"] = "ไม่ใช่หน่วยคำเติม", } export.nominalization_g_codes = { ["m"] = "เพศชาย", ["f"] = "เพศหญิง", ["n"] = "เพศกลาง", ["c"] = "เพศรวม", ["gneut"] = "ไม่ระบุเพศ", ["s"] = "เอกพจน์", ["p"] = "พหูพจน์", ["d"] = "ทวิพจน์", ["pauc"] = "ตัวบอกความน้อย", ["mf"] = "เพศชายหรือเพศหญิง", ["fm"] = "เพศชายหรือเพศหญิง", ["mfn"] = "เพศชาย เพศหญิง หรือเพศกลาง", ["mnf"] = "เพศชาย เพศหญิง หรือเพศกลาง", ["fmn"] = "เพศชาย เพศหญิง หรือเพศกลาง", ["fnm"] = "เพศชาย เพศหญิง หรือเพศกลาง", ["nmf"] = "เพศชาย เพศหญิง หรือเพศกลาง", ["nfm"] = "เพศชาย เพศหญิง หรือเพศกลาง", } -- -- Propagate keyword overrides to aliases -- if export.config.lang_exceptions then for lang_code, exception in pairs(export.config.lang_exceptions) do if exception.keyword_overrides then for canonical, aliases in pairs(canonical_aliases) do if exception.keyword_overrides[canonical] then local override_data = exception.keyword_overrides[canonical] for _, alias in ipairs(aliases) do if not exception.keyword_overrides[alias] then exception.keyword_overrides[alias] = override_data end end end end end end end return export 34gfm9wlhv0709lw3rocyabkkkcralb 5775228 5775227 2026-10-14T07:25:39Z Octahedron80 267 ย้อนรุ่นแก้ไข [[Special:Diff/5775227|5775227]] ของ [[Special:Contributions/Octahedron80|Octahedron80]] ([[User talk:Octahedron80|คุย]]) 5775228 Scribunto text/plain local export = {} local M = require("Module:module loader").init({ require = { etymology = "Module:etymology", affix = "Module:affix", etymology_specialized = "Module:etymology/specialized", utilities = "Module:utilities", roots = "Module:roots", }, loadData = { data = "Module:etymon/data", }, }) -- Evaluate whether a keyword is transitive for a given term local function is_transitive(transitive_mode, page_lang, term_lang) if transitive_mode == M.data.TRANSITIVE.ALWAYS then return true elseif transitive_mode == M.data.TRANSITIVE.NEVER then return false elseif transitive_mode == M.data.TRANSITIVE.CROSS_LANG then return page_lang:getCode() ~= term_lang:getCode() elseif transitive_mode == M.data.TRANSITIVE.CROSS_LANG_NO_INTERNAL_SOURCE then return page_lang:getCode() ~= term_lang:getCode() end error("Unknown transitive mode: " .. tostring(transitive_mode)) end -- Get keyword config with language-specific overrides local function get_keyword_config(keyword, lang_exc) local base_config = M.data.keywords[keyword] if not base_config then return nil -- Invalid keyword end local overrides = lang_exc and lang_exc.keyword_overrides and lang_exc.keyword_overrides[keyword] if not overrides then return base_config end -- Merge overrides into base config local merged = {} for k, v in pairs(base_config) do merged[k] = v end for k, v in pairs(overrides) do merged[k] = v end return merged end function export.get_cat_name(source) local _, cat_name = M.etymology.get_display_and_cat_name(source, true) return cat_name end -- Normalize affix type aliases local aftype_aliases = { ["pre"] = "อุปสรรค", ["suf"] = "ปัจจัย", ["in"] = "อาคม", ["inter"] = "หน่วยคำเติมเชื่อม", ["circum"] = "หน่วยคำเติมคร่อม", ["naf"] = "ไม่ใช่หน่วยคำเติม", ["root"] = "ไม่ใช่หน่วยคำเติม", ["prefix"] = "อุปสรรค", ["suffix"] = "ปัจจัย", ["infix"] = "อาคม", ["interfix"] = "หน่วยคำเติมเชื่อม", ["circumfix"] = "หน่วยคำเติมคร่อม", ["non-affix"] = "ไม่ใช่หน่วยคำเติม", ["ราก"] = "ไม่ใช่หน่วยคำเติม", } local function add_category(categories, cat_name, sort_key, sort_base) if categories[cat_name] == nil then categories[cat_name] = { sort_key = sort_key, sort_base = sort_base, } return end local existing = categories[cat_name] if existing.sort_key == nil and sort_key ~= nil then existing.sort_key = sort_key end if existing.sort_base == nil and sort_base ~= nil then existing.sort_base = sort_base end end -- Collect affix categories from top-level group containers local function collect_affix_categories(node, page_lang, available_etymon_ids, senseid_parent_etymon, lang_exc) local parts = {} local part_index = 1 for _, container in ipairs(node.children or {}) do local config = container.keyword_info if config and config.affix_categories then for _, term in ipairs(container.terms or {}) do if not term.unknown_term then local part_data = { term = term.title, tr = term.tr, ts = term.ts, alt = term.alt, itemno = part_index, orig_index = part_index } -- Determine affix type: explicit aftype > pos=root > auto-detect local aftype = term.aftype if aftype then aftype = aftype_aliases[aftype] or aftype part_data.type = aftype elseif term.args and term.args.pos and (term.args.pos == "root" or term.args.pos == "ราก") then part_data.type = "ไม่ใช่หน่วยคำเติม" end if term.lang:getCode() ~= page_lang:getCode() then part_data.lang = term.lang end local target_ids = available_etymon_ids[term.target_key] local has_multiple_ids = target_ids and #target_ids > 1 local id_exists_in_disambiguation = false local matched_id = nil -- Count available senseids for the target page local senseid_count = 0 local target_prefix = term.target_key .. ":" if senseid_parent_etymon then for key, _ in pairs(senseid_parent_etymon) do if key:sub(1, #target_prefix) == target_prefix then senseid_count = senseid_count + 1 end end end local has_multiple_senseids = senseid_count > 1 if term.id then -- Check if user provided a valid senseid local senseid_key = term.target_key .. ":" .. term.id if senseid_parent_etymon and senseid_parent_etymon[senseid_key] then if has_multiple_senseids then -- Ambiguous senseid: use senseid matched_id = term.id id_exists_in_disambiguation = true elseif has_multiple_ids then -- Unique senseid but ambiguous etymon: use etymon ID matched_id = term.etymon_id or term.id id_exists_in_disambiguation = true end else -- Check if user provided a valid etymon ID if has_multiple_ids and target_ids then for _, id_data in ipairs(target_ids) do local stored_id = type(id_data) == "table" and id_data.id or id_data if stored_id == term.id then -- Ambiguous etymon: use etymon ID id_exists_in_disambiguation = true matched_id = term.id break end end end -- Fallback: check resolved etymon_id (e.g. from previous steps) if not id_exists_in_disambiguation and has_multiple_ids and term.etymon_id and target_ids then for _, id_data in ipairs(target_ids) do local stored_id = type(id_data) == "table" and id_data.id or id_data if stored_id == term.etymon_id then id_exists_in_disambiguation = true matched_id = term.etymon_id break end end end end end -- Use the matched ID if found if term.override or id_exists_in_disambiguation then part_data.id = matched_id or term.id end table.insert(parts, part_data) part_index = part_index + 1 end end end end if #parts == 0 then return {} end local affix_data = { lang = page_lang, parts = parts, pos = "ศัพท์", sort_key = nil, } if #parts == 1 then affix_data.allow_no_affixes_or_compounds = true end local affix_categories = M.affix.get_affix_categories_only(affix_data) local result = {} for _, cat in ipairs(affix_categories) do if type(cat) == "table" then table.insert(result, { cat = cat.cat, sort_key = cat.sort_key, sort_base = cat.sort_base }) else table.insert(result, { cat = cat }) end end return result end local function lang_is_source(page_lang, source) return page_lang:getCode() == source:getCode() or page_lang:hasParent(source) end local function is_borrowing_keyword_config(config) return config and (config.borrowing_type or config.specialized_borrowing) end local function add_reborrow_category(categories, page_lang) local lang_name = page_lang:getCategoryName() --th add_category(categories, lang_name .. " terms borrowed back into " .. lang_name) end local function borrow_returns_to_page_lang(page_lang, source, in_foreign_branch) if not in_foreign_branch then return false end if source:getFullCode() == page_lang:getFullCode() then return true end return page_lang:hasParent(source) end local function node_borrows_from_lang(node, page_lang, visited, in_foreign_branch) visited = visited or {} if not node or visited[node] then return false end visited[node] = true if node.is_duplicate then if node.duplicate_of then return node_borrows_from_lang(node.duplicate_of, page_lang, visited, in_foreign_branch) end return false end local node_is_foreign = in_foreign_branch or (node.lang and node.lang:getFullCode() ~= page_lang:getFullCode()) for _, container in ipairs(node.children or {}) do if is_borrowing_keyword_config(container.keyword_info) then for _, child_term in ipairs(container.terms or {}) do if borrow_returns_to_page_lang(page_lang, child_term.lang, node_is_foreign) then return true end end end for _, child_term in ipairs(container.terms or {}) do if child_term.status == M.data.STATUS.OK or child_term.status == M.data.STATUS.INLINE then if node_borrows_from_lang(child_term, page_lang, visited, node_is_foreign) then return true end end end end return false end local function should_add_reborrow_category(page_lang, term) if page_lang:getCode() == term.lang:getCode() then return false end if term.status ~= M.data.STATUS.OK and term.status ~= M.data.STATUS.INLINE then return false end return node_borrows_from_lang(term, page_lang, {}, false) end -- Add borrowing-related categories (top-level only) local function collect_borrowing_categories(categories, page_lang, term, config, check_reborrow_path) if check_reborrow_path and should_add_reborrow_category(page_lang, term) then add_reborrow_category(categories, page_lang) end if config.borrowing_type == "ยืมมา" and not lang_is_source(page_lang, term.lang) then local temp_categories = {} M.etymology.insert_borrowed_cat(temp_categories, page_lang, term.lang) for _, cat in ipairs(temp_categories) do add_category(categories, cat) end end if config.specialized_borrowing and not lang_is_source(page_lang, term.lang) then local result = M.etymology_specialized.specialized_borrowing { bortype = config.specialized_borrowing, lang = page_lang, sources = { term.lang }, terms = { { lang = term.lang, term = "-" } }, notext = true, nocat = false, } for cat_name in result:gmatch("%[%[Category:([^%]]+)%]%]") do add_category(categories, cat_name) end end end -- Add source-based derivation categories (top-level only) local function collect_source_derivation_categories(categories, page_lang, term, config) if not config.source_category_type then return end local temp_categories = {} M.etymology.insert_source_cat_get_display { lang = page_lang, source = term.lang, categories = temp_categories, borrowing_type = config.source_category_type, nocat = false, } for _, cat in ipairs(temp_categories) do add_category(categories, cat) end end -- Add source language categories local function collect_source_categories(categories, page_lang, term, chain, get_norm_lang_func) if page_lang:getCode() == get_norm_lang_func(term.lang):getCode() then return end local temp_categories = {} M.etymology.insert_source_cat_get_display { lang = page_lang, source = term.lang, categories = temp_categories, nocat = false, } for _, cat in ipairs(temp_categories) do add_category(categories, cat) end if chain.inherited then temp_categories = {} M.etymology.insert_source_cat_get_display { lang = page_lang, source = term.lang, categories = temp_categories, borrowing_type = "สืบทอด", nocat = false, } for _, cat in ipairs(temp_categories) do add_category(categories, cat) end end end -- Add root/word categories local function collect_pos_categories(categories, page_lang, root_title, term, available_etymon_ids, chain, get_norm_lang_func, lang_exc, keyword) local pos_types = { root = "ราก", word = "คำ" } -- Determine pos: from term's postype, keyword's pos_override, or args.pos local pos local config = get_keyword_config(keyword, lang_exc) if term.postype then -- Term-level postype modifier takes highest priority pos = term.postype elseif config and config.pos_override then pos = config.pos_override elseif type(term.args) == "table" and term.args.pos then pos = term.args.pos end local pos_type = pos_types[pos] if not pos_type or term.unknown_term then return end -- Skip root/word categories for descendants of affix groups -- if pos_type then -- return -- end local same_language = get_norm_lang_func(page_lang):getFullCode() == get_norm_lang_func(term.lang):getFullCode() -- Skip self-references if same_language and root_title == term.title then return end local entry_name if pos_type == "root" or pos_type == "ราก" then entry_name = term.title M.roots.assert_root(term.lang, entry_name) else entry_name = term.lang:makeEntryName(term.title) end local lang_name = page_lang:getCategoryName() --th local cat_name if chain.passed_through then local etymon_lang_name = export.get_cat_name(term.lang) cat_name = lang_name .. " terms derived from the " .. etymon_lang_name .. " " .. pos_type .. " " .. entry_name else cat_name = lang_name .. " terms belonging to the " .. pos_type .. " " .. entry_name end -- Add ID disambiguation if needed (for roots/words: use etymon_id if resolved via senseid, otherwise use id) local target_ids = available_etymon_ids[term.target_key] local effective_id = term.etymon_id or term.id -- etymon_id if senseid, otherwise id is already an etymon id if target_ids and effective_id then local same_pos_count = 0 for _, id_data in ipairs(target_ids) do if type(id_data) == "table" and id_data.pos == pos then same_pos_count = same_pos_count + 1 end end if same_pos_count > 1 then cat_name = cat_name .. " (" .. effective_id .. ")" end end add_category(categories, cat_name) end -- Compute chain state for a term based on parent chain and keyword config -- Hyphen patterns for affix detection (regular hyphen + script-specific) local AFFIX_HYPHEN_PATTERN = "[%-%־ـ᠊]" -- regular hyphen, Hebrew maqqef, Arabic tatweel, Mongolian hyphen -- Check if a term is an actual affix (not a non-affix member of an affix group) local function is_actual_affix(term) -- Check explicit aftype modifier if term.aftype then local normalized = aftype_aliases[term.aftype] or term.aftype return normalized ~= "ไม่ใช่หน่วยคำเติม" end -- Check if pos=root (treated as non-affix) if term.args and term.args.pos and (term.args.pos == "root" or term.args.pos == "ราก") then return false end -- Auto-detect by hyphen: prefix ends with -, suffix starts with -, etc. if term.title then local title = term.title -- Strip leading * for reconstructed terms before checking hyphens title = title:gsub("^%*", "") -- Check for hyphens at start or end (handles script-specific hyphens too) if title:match("^" .. AFFIX_HYPHEN_PATTERN) or title:match(AFFIX_HYPHEN_PATTERN .. "$") then return true end end -- Default: not an affix return false end local function compute_category_chain(parent_chain, config, page_lang, term_lang, get_norm_lang_func, parent_term_lang, term) -- Track if we're inside an actual affix (for suppressing root categories on descendants) -- Only set if the term is an actual affix (prefix, suffix, etc.), not a non-affix member local inside_affix = parent_chain.inside_affix if config.affix_categories and term and is_actual_affix(term) then inside_affix = true end -- If no_child_categories is set, disable everything if config.no_child_categories then return { passed_through = parent_chain.passed_through or page_lang:getCode() ~= get_norm_lang_func(term_lang):getCode(), inherited = false, source = false, pos = false, recurse = false, inside_affix = inside_affix, } end local term_is_transitive = is_transitive(config.transitive, page_lang, term_lang) local new_source = parent_chain.source and term_is_transitive -- For CROSS_LANG_NO_INTERNAL_SOURCE: track internal derivation language context -- Check if this term is internal relative to parent term's language (if parent_term_lang provided) -- or relative to page language (if no parent_term_lang) local internal_lang = parent_chain.internal_lang local is_internal_in_context = false if config.transitive == M.data.TRANSITIVE.CROSS_LANG_NO_INTERNAL_SOURCE then local check_lang = parent_term_lang or page_lang local term_lang_code = get_norm_lang_func(term_lang):getCode() local check_lang_code = get_norm_lang_func(check_lang):getCode() if internal_lang then -- Already in an internal derivation context: check if this term is also internal is_internal_in_context = term_lang_code == internal_lang else -- Check if this term is internal relative to parent term (or page if no parent) is_internal_in_context = term_lang_code == check_lang_code end end -- Source chain behavior for CROSS_LANG_NO_INTERNAL_SOURCE if config.transitive == M.data.TRANSITIVE.CROSS_LANG_NO_INTERNAL_SOURCE then if is_internal_in_context then -- Internal derivation new_source = false internal_lang = get_norm_lang_func(term_lang):getCode() else -- Cross-language new_source = parent_chain.source and term_is_transitive internal_lang = nil end end local new_pos = parent_chain.pos return { passed_through = parent_chain.passed_through or page_lang:getCode() ~= get_norm_lang_func(term_lang):getCode(), inherited = parent_chain.inherited and config.inherited_chain, source = new_source, pos = new_pos, internal_lang = internal_lang, recurse = new_source or new_pos, inside_affix = inside_affix, } end function export.render(opts) opts = opts or {} local data_tree = opts.data_tree local page_lang = opts.page_lang local available_etymon_ids = opts.available_etymon_ids local senseid_parent_etymon = opts.senseid_parent_etymon local get_norm_lang_func = opts.get_norm_lang_func local lang_exc = opts.lang_exc local categories = {} local seen = {} local lang_name = page_lang:getCategoryName() --th local root_title = data_tree.title -- Collect the tree recursively local function collect(node, parent_chain, is_toplevel) -- Avoid processing same node twice if not node.unknown_term and node.title then local key = node.lang:getFullCode() .. ":" .. (node.title or "") .. ":" .. (node.id or "") if seen[key] then return end seen[key] = true end -- Collect affix categories at top level only if is_toplevel then local affix_cats = collect_affix_categories(node, page_lang, available_etymon_ids, senseid_parent_etymon, lang_exc) for _, cat in ipairs(affix_cats) do --add_category(categories, lang_name .. " " .. cat.cat, cat.sort_key, cat.sort_base) add_category(categories, require("Module:th-utilities").th_categorize(cat.cat, lang_name), cat.sort_key, cat.sort_base) end if node.supplements then for _, supplement in ipairs(node.supplements) do local config = supplement.config if config and config.toplevel_category then --add_category(categories, lang_name .. " " .. config.toplevel_category) add_category(categories, require("Module:th-utilities").th_categorize(config.toplevel_category, lang_name)) end end end end -- Process each container for _, container in ipairs(node.children or {}) do local keyword = container.keyword local config = get_keyword_config(keyword, lang_exc) -- Skip invalid keywords if config then -- Process each term in the container for _, term in ipairs(container.terms or {}) do local term_chain = compute_category_chain(parent_chain, config, page_lang, term.lang, get_norm_lang_func, node.lang, term) local no_child_categories = config.no_child_categories == true local term_is_transitive = is_transitive(config.transitive, page_lang, term.lang) -- Top-level only processing if is_toplevel then -- Missing/ambiguous etymon tracking if not term.unknown_term and (term.status == M.data.STATUS.MISSING or term.status == M.data.STATUS.REDLINK) then add_category(categories, lang_name .. " entries referencing missing etymons") end if not term.unknown_term and term.status == M.data.STATUS.AMBIGUOUS then add_category(categories, lang_name .. " entries referencing ambiguous etymons") end if term.missing_descendants_header then add_category(categories, lang_name .. " entries referencing etymons without Descendants sections") end if term.missing_descendants_entry then add_category(categories, lang_name .. " entries referencing etymons without this term in Descendants sections") end -- Top-level category (e.g., "undefined derivations") if config.toplevel_category then add_category(categories, lang_name .. " " .. config.toplevel_category) end -- Borrowing categories (bor, lbor, slbor, ubor, obor) if config.borrowing_type or config.specialized_borrowing then collect_borrowing_categories(categories, page_lang, term, config, true) end -- Borrowing categories from <bor>, <lbor>, or <slbor> modifiers on affix-group terms local kw_config = M.data.keywords[keyword] if kw_config and kw_config.affix_categories then if term.bor then local bor_config = { borrowing_type = "ยืมมา" } collect_borrowing_categories(categories, page_lang, term, bor_config, true) elseif term.lbor then local bor_config = { specialized_borrowing = "learned" } collect_borrowing_categories(categories, page_lang, term, bor_config, true) elseif term.slbor then local bor_config = { specialized_borrowing = "semi-learned" } collect_borrowing_categories(categories, page_lang, term, bor_config, true) end end -- Source-based derivation categories (sl, calque, pcal) if config.source_category_type then collect_source_derivation_categories(categories, page_lang, term, config) end -- Skip all child categorisation if no_child_categories is set if not no_child_categories then -- Source categories only if transitive if term_is_transitive then collect_source_categories(categories, page_lang, term, term_chain, get_norm_lang_func) end -- Pos categories always (unless no_child_categories) collect_pos_categories(categories, page_lang, root_title, term, available_etymon_ids, term_chain, get_norm_lang_func, lang_exc, keyword) end else -- Below top level, respect the parent chain if parent_chain.source then collect_source_categories(categories, page_lang, term, term_chain, get_norm_lang_func) end if parent_chain.pos then collect_pos_categories(categories, page_lang, root_title, term, available_etymon_ids, term_chain, get_norm_lang_func, lang_exc, keyword) end end -- Recurse into term's children if needed and status allows if term_chain.recurse and (term.status == M.data.STATUS.OK or term.status == M.data.STATUS.INLINE) then collect(term, term_chain, false) end end end end end -- Initial chain state local initial_chain = { passed_through = false, inherited = true, source = true, pos = true, internal_lang = nil, recurse = true, inside_affix = false, } collect(data_tree, initial_chain, true) local cat_list = {} for cat_name, sort_data in pairs(categories) do if sort_data.sort_key ~= nil or sort_data.sort_base ~= nil then table.insert(cat_list, { name = cat_name, sort_key = sort_data.sort_key, sort_base = sort_data.sort_base, }) else table.insert(cat_list, cat_name) end end return cat_list end function export.build(opts) opts = opts or {} local categories = {} if not opts.suppress_categories and not opts.nocat then categories = export.render({ data_tree = opts.data_tree, page_lang = opts.page_lang, available_etymon_ids = opts.available_etymon_ids, senseid_parent_etymon = opts.senseid_parent_etymon, get_norm_lang_func = opts.get_norm_lang_func, lang_exc = opts.lang_exc, }) end local page_lang = opts.page_lang if not page_lang then return categories end local lang_name = page_lang:getCategoryName() --th table.insert(categories, "Pages with etymon") table.insert(categories, lang_name .. " entries with etymon") if opts.tree then table.insert(categories, "Pages with etymology trees") table.insert(categories, lang_name .. " entries with etymology trees") end if opts.text then table.insert(categories, lang_name .. " entries with etymology texts") end if opts.exnihilo then table.insert(categories, lang_name .. " terms coined ex nihilo") end if opts.toplevel_has_inline_etymology then table.insert(categories, "Pages with inline etymon for redlinks") end if opts.toplevel_redundant_etymology then table.insert(categories, "Pages with redundant inline etymon") end if opts.toplevel_idless_etymon then table.insert(categories, "Pages using etymon with no ID") end if opts.has_mismatched_id then table.insert(categories, lang_name .. " entries referencing etymons with mismatched IDs") end if opts.linked_page_multiple_etymons_idless then table.insert(categories, lang_name .. " entries referencing pages with multiple etymons missing IDs") end if opts.linked_page_partial_etymology_sections then table.insert(categories, lang_name .. " entries referencing pages with etymology sections missing etymons") end if opts.text_stop_lang_missing then table.insert(categories, "Pages with etymology text stop language not in chain") table.insert(categories, lang_name .. " entries with etymology text stop language not in chain") end return categories end function export.format(entries, lang) if type(entries) ~= "table" or #entries == 0 then return "" end local parts = {} for _, category in ipairs(entries) do if type(category) == "table" and type(category.name) == "string" then table.insert(parts, M.utilities.format_categories({ category.name }, lang, category.sort_key, category.sort_base)) elseif type(category) == "string" then table.insert(parts, M.utilities.format_categories({ category }, lang)) end end return table.concat(parts) end return export qvrhubncgwhsk5ajwyfrvsyz6yhqjf0 5775229 5775228 2026-10-14T07:26:13Z Octahedron80 267 5775229 Scribunto text/plain local export = {} local M = require("Module:module loader").init({ require = { etymology = "Module:etymology", affix = "Module:affix", etymology_specialized = "Module:etymology/specialized", utilities = "Module:utilities", roots = "Module:roots", }, loadData = { data = "Module:etymon/data", }, }) -- Evaluate whether a keyword is transitive for a given term local function is_transitive(transitive_mode, page_lang, term_lang) if transitive_mode == M.data.TRANSITIVE.ALWAYS then return true elseif transitive_mode == M.data.TRANSITIVE.NEVER then return false elseif transitive_mode == M.data.TRANSITIVE.CROSS_LANG then return page_lang:getCode() ~= term_lang:getCode() elseif transitive_mode == M.data.TRANSITIVE.CROSS_LANG_NO_INTERNAL_SOURCE then return page_lang:getCode() ~= term_lang:getCode() end error("Unknown transitive mode: " .. tostring(transitive_mode)) end -- Get keyword config with language-specific overrides local function get_keyword_config(keyword, lang_exc) local base_config = M.data.keywords[keyword] if not base_config then return nil -- Invalid keyword end local overrides = lang_exc and lang_exc.keyword_overrides and lang_exc.keyword_overrides[keyword] if not overrides then return base_config end -- Merge overrides into base config local merged = {} for k, v in pairs(base_config) do merged[k] = v end for k, v in pairs(overrides) do merged[k] = v end return merged end function export.get_cat_name(source) local _, cat_name = M.etymology.get_display_and_cat_name(source, true) return cat_name end -- Normalize affix type aliases local function add_category(categories, cat_name, sort_key, sort_base) if categories[cat_name] == nil then categories[cat_name] = { sort_key = sort_key, sort_base = sort_base, } return end local existing = categories[cat_name] if existing.sort_key == nil and sort_key ~= nil then existing.sort_key = sort_key end if existing.sort_base == nil and sort_base ~= nil then existing.sort_base = sort_base end end -- Collect affix categories from top-level group containers local function collect_affix_categories(node, page_lang, available_etymon_ids, senseid_parent_etymon, lang_exc) local parts = {} local part_index = 1 for _, container in ipairs(node.children or {}) do local config = container.keyword_info if config and config.affix_categories then for _, term in ipairs(container.terms or {}) do if not term.unknown_term then local part_data = { term = term.title, tr = term.tr, ts = term.ts, alt = term.alt, itemno = part_index, orig_index = part_index } -- Determine affix type: explicit aftype > pos=root > auto-detect local aftype = term.aftype if aftype then aftype = M.data.aftype_aliases[aftype] or aftype part_data.type = aftype elseif term.args and term.args.pos and (term.args.pos == "root" or term.args.pos == "ราก") then part_data.type = "ไม่ใช่หน่วยคำเติม" end if term.lang:getCode() ~= page_lang:getCode() then part_data.lang = term.lang end local target_ids = available_etymon_ids[term.target_key] local has_multiple_ids = target_ids and #target_ids > 1 local id_exists_in_disambiguation = false local matched_id = nil -- Count available senseids for the target page local senseid_count = 0 local target_prefix = term.target_key .. ":" if senseid_parent_etymon then for key, _ in pairs(senseid_parent_etymon) do if key:sub(1, #target_prefix) == target_prefix then senseid_count = senseid_count + 1 end end end local has_multiple_senseids = senseid_count > 1 if term.id then -- Check if user provided a valid senseid local senseid_key = term.target_key .. ":" .. term.id if senseid_parent_etymon and senseid_parent_etymon[senseid_key] then if has_multiple_senseids then -- Ambiguous senseid: use senseid matched_id = term.id id_exists_in_disambiguation = true elseif has_multiple_ids then -- Unique senseid but ambiguous etymon: use etymon ID matched_id = term.etymon_id or term.id id_exists_in_disambiguation = true end else -- Check if user provided a valid etymon ID if has_multiple_ids and target_ids then for _, id_data in ipairs(target_ids) do local stored_id = type(id_data) == "table" and id_data.id or id_data if stored_id == term.id then -- Ambiguous etymon: use etymon ID id_exists_in_disambiguation = true matched_id = term.id break end end end -- Fallback: check resolved etymon_id (e.g. from previous steps) if not id_exists_in_disambiguation and has_multiple_ids and term.etymon_id and target_ids then for _, id_data in ipairs(target_ids) do local stored_id = type(id_data) == "table" and id_data.id or id_data if stored_id == term.etymon_id then id_exists_in_disambiguation = true matched_id = term.etymon_id break end end end end end -- Use the matched ID if found if term.override or id_exists_in_disambiguation then part_data.id = matched_id or term.id end table.insert(parts, part_data) part_index = part_index + 1 end end end end if #parts == 0 then return {} end local affix_data = { lang = page_lang, parts = parts, pos = "ศัพท์", sort_key = nil, } if #parts == 1 then affix_data.allow_no_affixes_or_compounds = true end local affix_categories = M.affix.get_affix_categories_only(affix_data) local result = {} for _, cat in ipairs(affix_categories) do if type(cat) == "table" then table.insert(result, { cat = cat.cat, sort_key = cat.sort_key, sort_base = cat.sort_base }) else table.insert(result, { cat = cat }) end end return result end local function lang_is_source(page_lang, source) return page_lang:getCode() == source:getCode() or page_lang:hasParent(source) end local function is_borrowing_keyword_config(config) return config and (config.borrowing_type or config.specialized_borrowing) end local function add_reborrow_category(categories, page_lang) local lang_name = page_lang:getCategoryName() --th add_category(categories, lang_name .. " terms borrowed back into " .. lang_name) end local function borrow_returns_to_page_lang(page_lang, source, in_foreign_branch) if not in_foreign_branch then return false end if source:getFullCode() == page_lang:getFullCode() then return true end return page_lang:hasParent(source) end local function node_borrows_from_lang(node, page_lang, visited, in_foreign_branch) visited = visited or {} if not node or visited[node] then return false end visited[node] = true if node.is_duplicate then if node.duplicate_of then return node_borrows_from_lang(node.duplicate_of, page_lang, visited, in_foreign_branch) end return false end local node_is_foreign = in_foreign_branch or (node.lang and node.lang:getFullCode() ~= page_lang:getFullCode()) for _, container in ipairs(node.children or {}) do if is_borrowing_keyword_config(container.keyword_info) then for _, child_term in ipairs(container.terms or {}) do if borrow_returns_to_page_lang(page_lang, child_term.lang, node_is_foreign) then return true end end end for _, child_term in ipairs(container.terms or {}) do if child_term.status == M.data.STATUS.OK or child_term.status == M.data.STATUS.INLINE then if node_borrows_from_lang(child_term, page_lang, visited, node_is_foreign) then return true end end end end return false end local function should_add_reborrow_category(page_lang, term) if page_lang:getCode() == term.lang:getCode() then return false end if term.status ~= M.data.STATUS.OK and term.status ~= M.data.STATUS.INLINE then return false end return node_borrows_from_lang(term, page_lang, {}, false) end -- Add borrowing-related categories (top-level only) local function collect_borrowing_categories(categories, page_lang, term, config, check_reborrow_path) if check_reborrow_path and should_add_reborrow_category(page_lang, term) then add_reborrow_category(categories, page_lang) end if config.borrowing_type == "ยืมมา" and not lang_is_source(page_lang, term.lang) then local temp_categories = {} M.etymology.insert_borrowed_cat(temp_categories, page_lang, term.lang) for _, cat in ipairs(temp_categories) do add_category(categories, cat) end end if config.specialized_borrowing and not lang_is_source(page_lang, term.lang) then local _, cat_names = M.etymology_specialized.specialized_borrowing { bortype = config.specialized_borrowing, lang = page_lang, sources = { term.lang }, terms = { { lang = term.lang, term = "-" } }, notext = true, } for _, cat_name in ipairs(cat_names) do add_category(categories, cat_name) end end end -- Add source-based derivation categories (top-level only) local function collect_source_derivation_categories(categories, page_lang, term, config) if not config.source_category_type then return end local temp_categories = {} M.etymology.insert_source_cat_get_display { lang = page_lang, source = term.lang, categories = temp_categories, borrowing_type = config.source_category_type, nocat = false, } for _, cat in ipairs(temp_categories) do add_category(categories, cat) end end -- Add source language categories local function collect_source_categories(categories, page_lang, term, chain, get_norm_lang_func) if page_lang:getCode() == get_norm_lang_func(term.lang):getCode() then return end local temp_categories = {} M.etymology.insert_source_cat_get_display { lang = page_lang, source = term.lang, categories = temp_categories, nocat = false, } for _, cat in ipairs(temp_categories) do add_category(categories, cat) end if chain.inherited then temp_categories = {} M.etymology.insert_source_cat_get_display { lang = page_lang, source = term.lang, categories = temp_categories, borrowing_type = "สืบทอด", nocat = false, } for _, cat in ipairs(temp_categories) do add_category(categories, cat) end end end -- Add root/word categories local function collect_pos_categories(categories, page_lang, root_title, term, available_etymon_ids, chain, get_norm_lang_func, lang_exc, keyword) local pos_types = { root = "ราก", word = "คำ" } -- Determine pos: from term's postype, keyword's pos_override, or args.pos local pos local config = get_keyword_config(keyword, lang_exc) if term.postype then -- Term-level postype modifier takes highest priority pos = term.postype elseif config and config.pos_override then pos = config.pos_override elseif type(term.args) == "table" and term.args.pos then pos = term.args.pos end local pos_type = pos_types[pos] if not pos_type or term.unknown_term then return end -- Skip root/word categories for descendants of affix groups -- if pos_type then -- return -- end local same_language = get_norm_lang_func(page_lang):getFullCode() == get_norm_lang_func(term.lang):getFullCode() -- Skip self-references if same_language and root_title == term.title then return end local entry_name if pos_type == "root" or pos_type == "ราก" then entry_name = term.title M.roots.assert_root(term.lang, entry_name) else entry_name = term.lang:makeEntryName(term.title) end local lang_name = page_lang:getCategoryName() --th local cat_name if chain.passed_through then local etymon_lang_name = export.get_cat_name(term.lang) cat_name = lang_name .. " terms derived from the " .. etymon_lang_name .. " " .. pos_type .. " " .. entry_name else cat_name = lang_name .. " terms belonging to the " .. pos_type .. " " .. entry_name end -- Add ID disambiguation if needed (for roots/words: use etymon_id if resolved via senseid, otherwise use id) local target_ids = available_etymon_ids[term.target_key] local effective_id = term.etymon_id or term.id -- etymon_id if senseid, otherwise id is already an etymon id if target_ids and effective_id then local same_pos_count = 0 for _, id_data in ipairs(target_ids) do if type(id_data) == "table" and id_data.pos == pos then same_pos_count = same_pos_count + 1 end end if same_pos_count > 1 then cat_name = cat_name .. " (" .. effective_id .. ")" end end add_category(categories, cat_name) end -- Compute chain state for a term based on parent chain and keyword config -- Hyphen patterns for affix detection (regular hyphen + script-specific) local AFFIX_HYPHEN_PATTERN = "[%-%־ـ᠊]" -- regular hyphen, Hebrew maqqef, Arabic tatweel, Mongolian hyphen -- Check if a term is an actual affix (not a non-affix member of an affix group) local function is_actual_affix(term) -- Check explicit aftype modifier if term.aftype then local normalized = M.data.aftype_aliases[term.aftype] or term.aftype return normalized ~= "ไม่ใช่หน่วยคำเติม" end -- Check if pos=root (treated as non-affix) if term.args and term.args.pos and (term.args.pos == "root" or term.args.pos == "ราก") then return false end -- Auto-detect by hyphen: prefix ends with -, suffix starts with -, etc. if term.title then local title = term.title -- Strip leading * for reconstructed terms before checking hyphens title = title:gsub("^%*", "") -- Check for hyphens at start or end (handles script-specific hyphens too) if title:match("^" .. AFFIX_HYPHEN_PATTERN) or title:match(AFFIX_HYPHEN_PATTERN .. "$") then return true end end -- Default: not an affix return false end local function compute_category_chain(parent_chain, config, page_lang, term_lang, get_norm_lang_func, parent_term_lang, term) -- Track if we're inside an actual affix (for suppressing root categories on descendants) -- Only set if the term is an actual affix (prefix, suffix, etc.), not a non-affix member local inside_affix = parent_chain.inside_affix if config.affix_categories and term and is_actual_affix(term) then inside_affix = true end -- If no_child_categories is set, disable everything if config.no_child_categories then return { passed_through = parent_chain.passed_through or page_lang:getCode() ~= get_norm_lang_func(term_lang):getCode(), inherited = false, source = false, pos = false, recurse = false, inside_affix = inside_affix, } end local term_is_transitive = is_transitive(config.transitive, page_lang, term_lang) local new_source = parent_chain.source and term_is_transitive -- For CROSS_LANG_NO_INTERNAL_SOURCE: track internal derivation language context -- Check if this term is internal relative to parent term's language (if parent_term_lang provided) -- or relative to page language (if no parent_term_lang) local internal_lang = parent_chain.internal_lang local is_internal_in_context = false if config.transitive == M.data.TRANSITIVE.CROSS_LANG_NO_INTERNAL_SOURCE then local check_lang = parent_term_lang or page_lang local term_lang_code = get_norm_lang_func(term_lang):getCode() local check_lang_code = get_norm_lang_func(check_lang):getCode() if internal_lang then -- Already in an internal derivation context: check if this term is also internal is_internal_in_context = term_lang_code == internal_lang else -- Check if this term is internal relative to parent term (or page if no parent) is_internal_in_context = term_lang_code == check_lang_code end end -- Source chain behavior for CROSS_LANG_NO_INTERNAL_SOURCE if config.transitive == M.data.TRANSITIVE.CROSS_LANG_NO_INTERNAL_SOURCE then if is_internal_in_context then -- Internal derivation new_source = false internal_lang = get_norm_lang_func(term_lang):getCode() else -- Cross-language new_source = parent_chain.source and term_is_transitive internal_lang = nil end end local new_pos = parent_chain.pos return { passed_through = parent_chain.passed_through or page_lang:getCode() ~= get_norm_lang_func(term_lang):getCode(), inherited = parent_chain.inherited and config.inherited_chain, source = new_source, pos = new_pos, internal_lang = internal_lang, recurse = new_source or new_pos, inside_affix = inside_affix, } end function export.render(opts) opts = opts or {} local data_tree = opts.data_tree local page_lang = opts.page_lang local available_etymon_ids = opts.available_etymon_ids local senseid_parent_etymon = opts.senseid_parent_etymon local get_norm_lang_func = opts.get_norm_lang_func local lang_exc = opts.lang_exc local categories = {} local seen = {} local lang_name = page_lang:getCategoryName() --th local root_title = data_tree.title -- Collect the tree recursively local function collect(node, parent_chain, is_toplevel) -- Avoid processing same node twice if not node.unknown_term and node.title then local key = node.lang:getFullCode() .. ":" .. (node.title or "") .. ":" .. (node.id or "") if seen[key] then return end seen[key] = true end -- Collect affix categories at top level only if is_toplevel then local affix_cats = collect_affix_categories(node, page_lang, available_etymon_ids, senseid_parent_etymon, lang_exc) for _, cat in ipairs(affix_cats) do --add_category(categories, lang_name .. " " .. cat.cat, cat.sort_key, cat.sort_base) add_category(categories, require("Module:th-utilities").th_categorize(cat.cat, lang_name), cat.sort_key, cat.sort_base) end if node.supplements then for _, supplement in ipairs(node.supplements) do local config = supplement.config if config and config.toplevel_category then --add_category(categories, lang_name .. " " .. config.toplevel_category) add_category(categories, require("Module:th-utilities").th_categorize(config.toplevel_category, lang_name)) end end end end -- Process each container for _, container in ipairs(node.children or {}) do local keyword = container.keyword local config = get_keyword_config(keyword, lang_exc) -- Skip invalid keywords if config then -- Process each term in the container for _, term in ipairs(container.terms or {}) do local term_chain = compute_category_chain(parent_chain, config, page_lang, term.lang, get_norm_lang_func, node.lang, term) local no_child_categories = config.no_child_categories == true local term_is_transitive = is_transitive(config.transitive, page_lang, term.lang) -- Top-level only processing if is_toplevel then -- Missing/ambiguous etymon tracking if not term.unknown_term and (term.status == M.data.STATUS.MISSING or term.status == M.data.STATUS.REDLINK) then add_category(categories, lang_name .. " entries referencing missing etymons") end if not term.unknown_term and term.status == M.data.STATUS.AMBIGUOUS then add_category(categories, lang_name .. " entries referencing ambiguous etymons") end if term.missing_descendants_header then add_category(categories, lang_name .. " entries referencing etymons without Descendants sections") end if term.missing_descendants_entry then add_category(categories, lang_name .. " entries referencing etymons without this term in Descendants sections") end -- Top-level category (e.g., "undefined derivations") if config.toplevel_category then add_category(categories, lang_name .. " " .. config.toplevel_category) end -- Borrowing categories (bor, lbor, slbor, ubor, obor) if config.borrowing_type or config.specialized_borrowing then collect_borrowing_categories(categories, page_lang, term, config, true) end -- Borrowing categories from <bor>, <lbor>, or <slbor> modifiers on affix-group terms local kw_config = M.data.keywords[keyword] if kw_config and kw_config.affix_categories then if term.bor then local bor_config = { borrowing_type = "ยืมมา" } collect_borrowing_categories(categories, page_lang, term, bor_config, true) elseif term.lbor then local bor_config = { specialized_borrowing = "learned" } collect_borrowing_categories(categories, page_lang, term, bor_config, true) elseif term.slbor then local bor_config = { specialized_borrowing = "semi-learned" } collect_borrowing_categories(categories, page_lang, term, bor_config, true) end end -- Source-based derivation categories (sl, calque, pcal) if config.source_category_type then collect_source_derivation_categories(categories, page_lang, term, config) end -- Skip all child categorisation if no_child_categories is set if not no_child_categories then -- Source categories only if transitive if term_is_transitive then collect_source_categories(categories, page_lang, term, term_chain, get_norm_lang_func) end -- Pos categories always (unless no_child_categories) collect_pos_categories(categories, page_lang, root_title, term, available_etymon_ids, term_chain, get_norm_lang_func, lang_exc, keyword) end else -- Below top level, respect the parent chain if parent_chain.source then collect_source_categories(categories, page_lang, term, term_chain, get_norm_lang_func) end if parent_chain.pos then collect_pos_categories(categories, page_lang, root_title, term, available_etymon_ids, term_chain, get_norm_lang_func, lang_exc, keyword) end end -- Recurse into term's children if needed and status allows if term_chain.recurse and (term.status == M.data.STATUS.OK or term.status == M.data.STATUS.INLINE) then collect(term, term_chain, false) end end end end end -- Initial chain state local initial_chain = { passed_through = false, inherited = true, source = true, pos = true, internal_lang = nil, recurse = true, inside_affix = false, } collect(data_tree, initial_chain, true) local cat_list = {} for cat_name, sort_data in pairs(categories) do if sort_data.sort_key ~= nil or sort_data.sort_base ~= nil then table.insert(cat_list, { name = cat_name, sort_key = sort_data.sort_key, sort_base = sort_data.sort_base, }) else table.insert(cat_list, cat_name) end end return cat_list end function export.build(opts) opts = opts or {} local categories = {} if not opts.suppress_categories and not opts.nocat then categories = export.render({ data_tree = opts.data_tree, page_lang = opts.page_lang, available_etymon_ids = opts.available_etymon_ids, senseid_parent_etymon = opts.senseid_parent_etymon, get_norm_lang_func = opts.get_norm_lang_func, lang_exc = opts.lang_exc, }) end local page_lang = opts.page_lang if not page_lang then return categories end local lang_name = page_lang:getCategoryName() --th table.insert(categories, "Pages with etymon") table.insert(categories, lang_name .. " entries with etymon") if opts.tree then table.insert(categories, "Pages with etymology trees") table.insert(categories, lang_name .. " entries with etymology trees") end if opts.text then table.insert(categories, lang_name .. " entries with etymology texts") end if opts.exnihilo then table.insert(categories, lang_name .. " terms coined ex nihilo") end if opts.toplevel_has_inline_etymology then table.insert(categories, "Pages with inline etymon for redlinks") end if opts.toplevel_redundant_etymology then table.insert(categories, "Pages with redundant inline etymon") end if opts.toplevel_idless_etymon then table.insert(categories, "Pages using etymon with no ID") end if opts.has_mismatched_id then table.insert(categories, lang_name .. " entries referencing etymons with mismatched IDs") end if opts.linked_page_multiple_etymons_idless then table.insert(categories, lang_name .. " entries referencing pages with multiple etymons missing IDs") end if opts.linked_page_partial_etymology_sections then table.insert(categories, lang_name .. " entries referencing pages with etymology sections missing etymons") end if opts.text_stop_lang_missing then table.insert(categories, "Pages with etymology text stop language not in chain") table.insert(categories, lang_name .. " entries with etymology text stop language not in chain") end return categories end function export.format(entries, lang) if type(entries) ~= "table" or #entries == 0 then return "" end local parts = {} for _, category in ipairs(entries) do if type(category) == "table" and type(category.name) == "string" then table.insert(parts, M.utilities.format_categories({ category.name }, lang, category.sort_key, category.sort_base)) elseif type(category) == "string" then table.insert(parts, M.utilities.format_categories({ category }, lang)) end end return table.concat(parts) end return export b4cjsgffaz0ga7zlnbu0t72o0iscztm แม่แบบ:RQ:S. Butler Way of All Flesh 10 2307344 5775211 5663761 2026-10-14T05:28:17Z Octahedron80 267 5775211 wikitext text/x-wiki {{#invoke:quote|call_quote_template |en |author = {{#if:{{{page|}}}{{{pageref|}}}{{{3|}}} | {{#if:{{R2A|{{{page|}}}{{{pageref|}}}{{{3|}}}|no_error=1}} | {{#if:{{{year|}}} | Daniel F[rancis] Howard | [[w:R. A. Streatfeild|R[ichard] A[lexander] Streatfeild]] }} | [[w:Samuel Butler (novelist)|Samuel Butler]] }} | [[w:Samuel Butler (novelist)|Samuel Butler]] }} |chapter = {{#if:{{{page|}}}{{{pageref|}}}{{{3|}}} | {{#if:{{{year|}}} | {{#if:{{num|{{{chapter|}}}{{{1|}}}}} | {{{chapter|{{{1|}}}}}}<!--Chapter numbers are in Arabic numerals--> | {{#if:{{R2A|{{{chapter|}}}{{{1|}}}|no_error=1}} | {{R2A|{{{chapter|{{{1|}}}}}}}}<!--If chapter number in Roman numerals, convert to Arabic numerals. Otherwise, assume it is a word such as "Introduction".--> | {{{chapter|{{{1|}}}}}} }} }} | {{#if:{{R2A|{{{chapter|}}}{{{1|}}}|no_error=1}}<!--Check if chapter in Roman numerals; if so, display--> | {{uc:{{{chapter|{{{1|}}}}}}}} | {{#if:{{num|{{{chapter|}}}{{{1|}}}}}<!--If chapter number in Arabic numerals, convert to Roman numerals. Otherwise, assume it is a word such as "Note".--> | {{uc:{{A2R|{{{chapter|{{{1|}}}}}}}}}} | {{{chapter|{{{1|}}}}}} }} }} }} }} |mainauthor = {{#if:{{{page|}}}{{{pageref|}}}{{{3|}}}|{{#if:{{R2A|{{{page|}}}{{{pageref|}}}{{{3|}}}|no_error=1}} | [[w:Samuel Butler (novelist)|Samuel Butler]] }}}} |editor = {{#if:{{{year|}}} | {{#if:{{{page|}}}{{{pageref|}}}{{{3|}}} | {{#if:{{R2A|{{{page|}}}{{{pageref|}}}{{{3|}}}|no_error=1}} | Daniel F. Howard | Daniel F[rancis] Howard }} | Daniel F[rancis] Howard }} | {{#ifeq:{{{chapter|}}}{{{1|}}}|Note | R. A. Streatfeild | [[w:R. A. Streatfeild|R[ichard] A[lexander] Streatfeild]] }} }} |title = [[w:The Way of All Flesh|{{#if:{{{year|}}}|Ernest Pontifex, or The Way of All Flesh<!--title as written in the work-->|The Way of All Flesh}}]] |month = {{#if:{{{page|}}}{{{pageref|}}}{{{3|}}} | {{#if:{{R2A|{{{page|}}}{{{pageref|}}}{{{3|}}}|no_error=1}}||(date written)}} | (date written) }} |year = {{#if:{{{page|}}}{{{pageref|}}}{{{3|}}} | {{#if:{{R2A|{{{page|}}}{{{pageref|}}}{{{3|}}}|no_error=1}} | {{{year|1903}}} | 1873–1884 }} | 1873–1884 }} |year_published = {{#if:{{{page|}}}{{{pageref|}}}{{{3|}}} | {{#if:{{R2A|{{{page|}}}{{{pageref|}}}{{{3|}}}|no_error=1}}||{{{year|1903}}}}} | {{{year|1903}}} }} |location = {{#if:{{{year|}}}|Boston, Mass.|London}} |publisher = {{#if:{{{year|}}}|[[w:Houghton Mifflin Harcourt|Houghton Mifflin Company]]; Cambridge, Mass.: [[w:Riverside Insights|The Riverside Press]]|[[w:Grant Richards (publisher)|Grant Richards]]}} |section = {{#if:{{{year|}}} | {{#if:{{{page|}}}{{{pageref|}}}{{{2|}}} | {{#if:{{num|{{{page|}}}{{{pageref|}}}{{{2|}}}}} | volume {{#ifexpr:{{{page|}}}{{{pageref|}}}{{{2|}}}<118 | I, part {{#ifexpr:{{{page|}}}{{{pageref|}}}{{{2|}}}<66|I|II}} | {{#ifexpr:{{{page|}}}{{{pageref|}}}{{{2|}}}<233 | II, part {{#ifexpr:{{{page|}}}{{{pageref|}}}{{{2|}}}<146|I|II}} | III, part {{#ifexpr:{{{page|}}}{{{pageref|}}}{{{2|}}}<282 | I | {{#ifexpr:{{{page|}}}{{{pageref|}}}{{{2|}}}<305 | II | {{#ifexpr:{{{page|}}}{{{pageref|}}}{{{2|}}}<359 | III }} }} }} }} }} | {{#if:{{{section|}}}<!--for section of introduction (1964)--> | section {{{section}}} }} }} }} }} |pageparam = 2 |pageurl = https://archive.org/details/{{#if:{{{year|}}} | ernestpontifexor00butl/page/{{#if:{{R2A|{{{page|}}}{{{pageref|}}}{{{3|}}}|no_error=1}} | n{{#expr:{{R2A|{{{page|{{{pageref|{{{2|}}}}}}}}}}}+1}} | {{{page|{{{pageref|{{{2|}}}}}}}}} }} | wayofallflesh01butl/page/{{#ifeq:{{lc:{{{page|}}}{{{pageref|}}}{{{2|}}}}}|v | n8 | {{#ifexpr:{{{page|}}}{{{pageref|}}}{{{2|}}}<13 | n{{#expr:{{{page|{{{pageref|{{{2|}}}}}}}}}+9}} | {{{page|{{{pageref|{{{2|}}}}}}}}} }} }} }}/mode/1up |oclc = {{#if:{{{year|}}}|14141329|546196}} |textparam = 3 |t={{{t|}}} |translation={{{translation|}}} |allowparams = pageref,year,chapter,1,section,t,translation }}<noinclude>{{documentation}}</noinclude> avpf49a6unzqopx9dl0x29r41ql51b8 มอดูล:module loader 828 2324028 5775197 5684434 2026-10-14T04:32:44Z Octahedron80 267 5775197 Scribunto text/plain --[==[ intro: This module lets a module declare all of its dependencies at the top, in one place, while still loading each of them only when the code reaches it. A page which never enters a given branch never pays for the modules that branch needs. Call `init` once and keep the table it returns: ``` local M = require("Module:module loader").init{ require = { utilities = "Module:utilities", languages = "Module:languages", }, loadData = { headword_data = "Module:headword/data", }, lazy = { lang = function() return require("Module:languages").getByCode("de") end, }, } M.utilities.format_categories(...) -- requires Module:utilities M.headword_data.page -- mw.loadData on first access M.lang:makeEntryName(...) -- runs the getter on first access ``` The table `init` returns is a proxy. Reading a name for the first time does the work, and the result is {rawset} onto the proxy, so every later read is a plain table lookup and no metamethod runs. The same applies one level down: for a `require` entry, reading `M.<var>name</var>.<var>member</var>` caches that member on the sub-proxy. What a name reads as depends on the field it was declared in: * `require` and `requireOptional` give a sub-proxy, not the module table. Key lookups pass through, so the module's own {__index} still fires, but only keys already read exist on the proxy itself, so {pairs} and {#} see only those. Metamethods like {__call} apply to the module, not to the proxy that stands in for it. * `loadData` and `loadDataOptional` give the {mw.loadData} table itself, which behaves like any other table, so iteration works. * `lazy` gives whatever the getter returned. A name which was never declared reads as {nil}, as does an optional module which failed to load. The failure is cached, so a missing module is looked up once and not on every access. Guard with {if M.foo then} instead of indexing straight into it. A module declared under `require` which returns a function rather than a table is cached and returned as that function, with no proxy around it. The laziness is per-access, not per-call. The proxy defers until a name is read, not until the value that name holds is called, so binding a name to a local at the top of a module loads it there and then: ``` local shallowCopy = M.table.shallowCopy -- loads Module:table immediately ``` This applies to every field, `lazy` included, since {local x = M.x} is itself the access which runs the getter. Keep the access at the call site instead, where it costs two table lookups once the first access has cached both levels. Where a bare local is wanted, use the self-overwriting loader idiom of [[Module:table]] and [[Module:parameters]], which defers to the first call and costs nothing after it, because the local is replaced by the target function: ``` local function shallowCopy(...) shallowCopy = require("Module:table").shallowCopy return shallowCopy(...) end ``` ]==] local export = {} local NIL = {} -- sentinel for optional loadData/require that failed (so we don't retry) -- Validate the declaration: a short name may appear in only one of the five fields, and a -- module path may not appear twice across require and loadData. local function validate(modules, data_modules, optional_data_modules, optional_require_modules, lazy_modules) local seen_names = {} local seen_paths = {} -- Check require modules for name, path in pairs(modules) do if seen_names[name] then error("Module loader: duplicate name '" .. name .. "'") end seen_names[name] = "require" if seen_paths[path] then error("Module loader: module '" .. path .. "' loaded twice (as '" .. seen_paths[path] .. "' and '" .. name .. "')") end seen_paths[path] = name end -- Check loadData modules for name, path in pairs(data_modules) do if seen_names[name] then error("Module loader: duplicate name '" .. name .. "' (in both require and loadData)") end seen_names[name] = "loadData" if seen_paths[path] then error("Module loader: module '" .. path .. "' loaded twice (as '" .. seen_paths[path] .. "' and '" .. name .. "')") end seen_paths[path] = name end -- Check loadDataOptional (names only; its paths are not tracked) for name, path in pairs(optional_data_modules or {}) do if seen_names[name] then error("Module loader: duplicate name '" .. name .. "'") end seen_names[name] = "loadDataOptional" end -- Check requireOptional (names only) for name, path in pairs(optional_require_modules or {}) do if seen_names[name] then error("Module loader: duplicate name '" .. name .. "'") end seen_names[name] = "requireOptional" end -- Check lazy (names only) for name in pairs(lazy_modules or {}) do if seen_names[name] then error("Module loader: duplicate name '" .. name .. "'") end seen_names[name] = "lazy" end end -- Build a proxy for a lazy object: table of name → function () → value. Each key is evaluated once and cached. local function make_lazy_object_proxy(getters) return setmetatable({}, { __index = function(proxy, key) local getter = getters[key] if type(getter) ~= "function" then return nil end local value = getter() rawset(proxy, key, value) return value end }) end --[==[ Given a table of dependency declarations ``opts``, returns the lazy proxy described above. Call it once, at the top of a module. ``opts`` may contain any of the following fields, all optional: * `require`: maps a short name to a module path, which is loaded with {require} on first access. * `loadData`: maps a short name to a data module path, which is loaded with {mw.loadData} on first access. The table is returned as-is. * `requireOptional`: as `require`, but the load runs under {pcall}, and a module which is missing or throws an error reads as {nil}. Such a module must return a table; one returning a function counts as missing. * `loadDataOptional`: as `loadData`, but the load runs under {pcall}, and a module which is missing or throws an error reads as {nil}. * `lazy`: maps a short name to a getter for a value which is not a whole module. Where `require` and `loadData` defer loading a module, `lazy` defers computing a value, such as an object built from a module, or something borrowed from elsewhere which you would rather not touch until it is needed. Each entry is either a function returning the value, or a table of names to such functions, giving a nested object whose keys are each evaluated on their own first access. For instance: ** {lang = function() return require("Module:languages").getByCode("de") end}, so that [[Module:languages]] is not loaded on pages which never reach the relevant branch. ** {ustring = function() return mw.ustring end}, a sub-namespace of {mw}. ** {concat = function() return table.concat end}, a plain alias. ** {mw = {getCurrentTitle = ..., getCurrentFrame = ..., getContentLanguage = ...}}, the nested form, with one deferred key per expensive call. Throws an error if a short name is declared in more than one field, or if the same module path is declared twice across `require` and `loadData`. Paths in the optional fields are not tracked, so a module may be declared both as required and as optional.]==] function export.init(opts) local modules = opts.require or {} local data_modules = opts.loadData or {} local optional_data_modules = opts.loadDataOptional or {} local optional_require_modules = opts.requireOptional or {} local lazy_modules = opts.lazy or {} validate(modules, data_modules, optional_data_modules, optional_require_modules, lazy_modules) local loaded_modules = {} local function get_module(module_name) local cached = loaded_modules[module_name] if cached ~= nil then if cached == NIL then return nil end return cached end if data_modules[module_name] then loaded_modules[module_name] = mw.loadData(data_modules[module_name]) elseif optional_data_modules[module_name] then local ok, data = pcall(mw.loadData, optional_data_modules[module_name]) loaded_modules[module_name] = (ok and data) and data or NIL elseif optional_require_modules[module_name] then local ok, mod = pcall(require, optional_require_modules[module_name]) loaded_modules[module_name] = (ok and mod and type(mod) == "table") and mod or NIL elseif modules[module_name] then loaded_modules[module_name] = require(modules[module_name]) end local m = loaded_modules[module_name] if m == NIL then return nil end return m end local proxy_metatable = { __index = function(proxy, module_name) -- Lazy: single getter function or table of getters (nested lazy object) if lazy_modules[module_name] then local lazy_def = lazy_modules[module_name] if type(lazy_def) == "function" then local value = lazy_def() rawset(proxy, module_name, value) return value end if type(lazy_def) == "table" then local sub = make_lazy_object_proxy(lazy_def) rawset(proxy, module_name, sub) return sub end end local module = get_module(module_name) if not module then return nil end -- If the module itself is a function, cache and return it directly if type(module) == "function" then rawset(proxy, module_name, module) return module end -- For loadData / loadDataOptional modules, return the table directly (no function proxying) if data_modules[module_name] or optional_data_modules[module_name] then rawset(proxy, module_name, module) return module end -- Everything else (require, and requireOptional that loaded) gets a proxy that -- caches individual members as they are read. local function_proxy = setmetatable({}, { __index = function(function_cache, function_name) local cached_function = module[function_name] rawset(function_cache, function_name, cached_function) return cached_function end }) rawset(proxy, module_name, function_proxy) return function_proxy end } return setmetatable({}, proxy_metatable) end return export 4yt8l6eou5zun5d9uyqgkh1c1u1w3d4 มอดูล:Latn-Tfng-translit 828 2324044 5775302 5684540 2026-10-14T10:37:23Z Octahedron80 267 5775302 Scribunto text/plain local export = {} local tt = {} tt["Latn"] = { ["common"] = { ["a"] = "ⴰ", ["ā"] = "ⴰⴰ", ["b"] = "ⴱ", ["ḇ"] = "ⴲ", ["ǧ"] = "ⴵ", ["g"] = "ⴳ", ["d"] = "ⴷ", ["ḏ"] = "ⴸ", ["ḍ"] = "ⴹ", ["ḏ̣"] = "ⴺ", ["e"] = "ⴻ", ["f"] = "ⴼ", ["k"] = "ⴽ", ["h"] = "ⵀ", ["ḥ"] = "ⵃ", ["ɛ"] = "ⵄ", ["x"] = "ⵅ", ["q"] = "ⵇ", ["i"] = "ⵉ", ["j"] = "ⵊ", ["l"] = "ⵍ", ["m"] = "ⵎ", ["n"] = "ⵏ", ["p"] = "ⵒ", ["u"] = "ⵓ", ["r"] = "ⵔ", ["ř"] = "ⵔ", ["ṛ"] = "ⵕ", ["ɣ"] = "ⵖ", ["s"] = "ⵙ", ["ṣ"] = "ⵚ", ["t"] = "ⵜ", ["ṯ"] = "ⵝ", ["š"] = "ⵛ", ["c"] = "ⵛ", ["č"] = "ⵞ", ["ṭ"] = "ⵟ", ["v"] = "ⵠ", ["w"] = "ⵡ", ["y"] = "ⵢ", ["z"] = "ⵣ", ["ẓ"] = "ⵥ", ["o"] = "ⵧ", ["ʷ"] = "ⵯ", ["."] = "⵰", }, ["tmh"] = {["b"] = "ⵀ", ["w"] = "ⵓ"}, ["thv"] = {["b"] = "ⵀ", ["w"] = "ⵓ"}, ["taq"] = {["b"] = "ⵀ", ["w"] = "ⵓ"}, ["ttq"] = {["b"] = "ⵀ", ["w"] = "ⵓ"}, ["thz"] = {["b"] = "ⵀ", ["w"] = "wⵓ", ["ɣ"] = "ⵘ"} } function export.tr(text, lang, sc) if not sc then sc = require("Module:languages").getByCode(lang or "ber"):findBestScript(text):getCode() end if sc == "Latn" then text = mw.ustring.lower(text) if tt[sc][lang] then text = mw.ustring.gsub(text, '.', tt[sc][lang]) end text = mw.ustring.gsub(text, '.', tt[sc]["common"]) else text = nil end return text end return export b1lml3cd2sdl8gt3a31oavuw82g4fx0 Иран 0 2326505 5775285 5714910 2026-10-14T10:13:32Z Octahedron80 267 5775285 wikitext text/x-wiki {{minitoc}} == ภาษาคาซัค == {{wp|kk:}} {{kk-alt}} === รากศัพท์ === {{bor+|kk|fa-cls|ایران|tr=īrān}} === คำวิสามานยนาม === {{head|kk|คำวิสามานยนาม}} # {{tcl|kk|อิหร่าน|id=Q794}} {{cln|kk|exonyms}} == ภาษาคีร์กีซ == {{wp|ky:}} === รากศัพท์ === จาก{{bor|ky|fa|ایران|tr=irân}}, จาก{{der|ky|pal|𐭠𐭩𐭥𐭠𐭭||(ดินแดน)ของชาวอิหร่าน}} === คำวิสามานยนาม === {{ky-prop}} # {{tcl|ky|อิหร่าน|id=Q794}} ==== การผันรูป ==== {{ky-decl-noun-sg}} {{cln|ky|exonyms}} == ภาษาคูมุก == === รากศัพท์ === {{bor+|kum|ru|Иран}}, แรกสุดจาก{{der|kum|fa|ایران|tr=irân}} === การออกเสียง === * {{IPA|kum|/iˈran/}} === คำวิสามานยนาม === {{head|kum|คำวิสามานยนาม}} # {{tcl|kum|อิหร่าน|id=Q794}} ==== การผันรูป ==== {{kum-decl-noun|Иран}} == ภาษาเซอร์โบ-โครเอเชีย == === การออกเสียง === * {{IPA|sh|/ǐraːn/}} * {{hyphenation|sh|И|ран}} === คำวิสามานยนาม === {{sh-propn|Ѝра̄н|m-in}} # {{tcl|sh|อิหร่าน|id=Q794}} ==== การผันรูป ==== {{sh-decl-noun-unc |Ѝра̄н |Ира́на |Ирану |Иран |Иране |Ирану |Ираном }} {{cln|sh|exonyms}} == ภาษาตาตาร์ == === รากศัพท์ === {{bor+|tt|fa|ایران}} === คำวิสามานยนาม === {{head|tt|คำวิสามานยนาม}} # {{tcl|tt|อิหร่าน|id=Q794}} == ภาษาบัลแกเรีย == {{wp|bg:}} [[ไฟล์:Iran map.png|thumb|Иран]] === การออกเสียง === * {{bg-IPA|Ира́н}} * {{audio|bg|LL-Q7918 (bul)-Kiril kovachev-Иран.wav}} * {{rhymes|bg|an|s=2}} === คำวิสามานยนาม === {{bg-proper noun|Ира́н|m|adj=ира́нски}} # {{tcl|bg|อิหร่าน|id=Q794}} ==== การผันรูป ==== {{bg-ndecl|Ира́н</n:sg>}} {{cln|bg|exonyms}} == ภาษาแบชเคียร์ == {{wp|ba:}} === รากศัพท์ === จาก{{bor|ba|fa|ایران|tr=irân}}, จาก{{der|ba|pal|𐭠𐭩𐭥𐭠𐭭|t=(ดินแดน)ของชาวอิหร่าน}} === คำวิสามานยนาม === {{ba-proper noun}} # {{tcl|ba|อิหร่าน|id=Q794}} {{cln|ba|exonyms}} == ภาษามองโกเลีย == {{wp|mn:}} === การออกเสียง === {{mn-IPA|Ира́н}} === คำวิสามานยนาม === {{mn-proper noun|ᠢᠷᠠᠨ}} # {{tcl|mn|อิหร่าน|id=Q794}} {{cln|mn|exonyms}} == ภาษามาซิโดเนีย == {{wp|mk:}} === การออกเสียง === * {{mk-IPA}} === คำวิสามานยนาม === {{mk-proper noun|m|adj=ирански}} # {{tcl|mk|อิหร่าน|id=Q794}} ==== คำเกี่ยวข้อง ==== * {{l|mk|Иранец|g=m}}, {{l|mk|Иранка|g=f}} * {{l|mk|ирански}} === ดูเพิ่ม === * {{l|mk|Персија|g=f}} * {{l|mk|Персиец|g=m}}, {{l|mk|Персијка|g=f}} * {{l|mk|персиски}} {{cln|mk|exonyms}} == ภาษารัสเซีย == {{wp|ru:}} === รูปแบบอื่น === * {{alter|ru|Ира́нъ||ru-PRO}} === รากศัพท์ === {{bor+|ru|fa|ایران|tr=irân}} === การออกเสียง === * {{ru-IPA|Ира́н}} * {{audio|ru|Ru-Иран.ogg}} === คำวิสามานยนาม === {{ru-proper noun+|Ира́н|adj=ира́нский}} # {{tcl|ru|อิหร่าน|id=Q794}} ==== การผันรูป ==== {{ru-noun-table|Ира́н|n=sg}} ==== คำเกี่ยวข้อง ==== * {{l|ru|ира́нец}}, {{l|ru|ира́нка}} * {{l|ru|ира́нский}} ==== คำสืบทอด ==== * {{desc|ky|Иран|bor=1}} === ดูเพิ่ม === * {{l|ru|перс}} * {{l|ru|персия́нин}} * {{l|ru|персия́нка}} * {{l|ru|перси́дский}} * {{l|ru|Пе́рсия}} * {{l|ru|Тегера́н}} {{list:countries in Asia/ru}} {{cln|ru|exonyms}} == ภาษารูซินแบบพันโนเนีย == === รากศัพท์ === {{bor+|rsk|sh|Иран//Iran}}, แรกสุดจาก{{der|rsk|fa|ایران|tr=irân}}, จาก{{der|rsk|pal|𐭠𐭩𐭥𐭠𐭭}} === การออกเสียง === * {{rsk-IPA}} * {{rhymes|rsk|iran|s=2}} * {{hyph|rsk|Иран}} === คำวิสามานยนาม === {{rsk-proper noun|m-in|adj=ирански|dem=Иранєц|f=Иранка}} # {{tcl|rsk|อิหร่าน|id=Q794}} ==== การผันรูป ==== {{rsk-decl-noun-table|Иран|Ирану|Ирану|Иран|Ираном|Ирану|Иранє}} === ดูเพิ่ม === {{col|rsk|title=proper nouns|Персия<g:f>}} {{list:countries in Asia/rsk}} === อ้างอิง === * {{R|rsk|SRS|v=2|p=837|Иран}} {{cln|rsk|exonyms|singularia tantum}} kgkq37jd58b9u48q4fk35s10yzh4d60 ایران 0 2326564 5775286 5714980 2026-10-14T10:14:07Z Octahedron80 267 5775286 wikitext text/x-wiki == ภาษาเปอร์เซีย == {{wp|fa:}} === รูปแบบอื่น === * {{alt|fa|ایرون|tr=irun||dialectal}} === รากศัพท์ === {{etymon|fa|id=Iran|:inh|pal:𐭠𐭩𐭥𐭠𐭭<id:Iran>|tree=1}} {{inh+|fa|pal|𐭠𐭩𐭥𐭠𐭭}}, see there for more. === การออกเสียง === {{fa-IPA|ē`rān|dari=ī`rān|cls=ē`rān,ī`rān}} * {{audio|fa|Fa-ایران.ogg|a=Iran}} * {{rhymes|fa|ɑːn|uːn<a:dialectal>|s=2}} === คำวิสามานยนาม === {{fa-proper noun|cls=ایرَان|cls2=اِیرَان}} # {{tcl|fa|อิหร่าน|id=Q794}}; {{q|historically}} [[เปอร์เซีย]] #: {{syn|fa|جُمْهوریِ اِسْلامیِ ایران|tr=jomhuri-ye eslâmi-ye irân|q=official}} #* {{quote-book|fa|title=fa:زین‌الاخبار|trans-title=Zaynu-l-axbār|author=fa:گردیزی<t:{{w|Gardizi|Gardēzī}}>|year=c. 1032|text=میان '''ایران''' و توران تعصب و فتنه افتاد تا بدین غایت هنوز اندر آنند.|tr={{xlit|fa-cls|مِیَانِ '''ایرَان''' و تُورَان تَعَصُّب و فِتْنَه اُفْتَاد تَا بَدِین غَایَت هَنُوز اَنْدَر آنْ‌اَنْد.}}|t=There has been intolerance and strife between '''Iran''' and {{w|Turan}} to the point that they are still engaged in it to this moment.|footer={{q|Classical Persian transliteration}}}} #* {{quote-book|fa|title=fa:کاروان هند|trans-title={{xlit|fa-cls|کَارْوَانِ هِنْد}}|author=fa:اشرف مازندرانی<t:{{w|Ashraf Mazandarani|Ašraf Māzandārānī}}>|year=1600s|location=w:India|text=گر به صد زحمت من از هندوستان آیم برون<br>خود بگو از عهده‌ی ایران چسان آیم برون|tr={{xlit|fa-cls|گَر بَه صَد زَحْمَت مَن اَز هِنْدُوسْتَان آیَم بِرَوَن<br>خْوَد بُگو اَز عُهْدَهٔ ایرَان چِسَان آیَم بِرَوَن}}|t=If, by a hundred efforts, I should leave Hindustan<br>Tell me, how should I ever leave my duty to '''Iran'''?|volume=1|page=71|year_published=1970|editor=fa:احمد گلچین معانی<tr:Ahmad Golčin-e Ma'âni>|footer={{q|Classical Persian Transliteration}} }} #* {{quote-book|fa|title=fa:پیام مشرق|trans-title={{w|Message from the East}}|author=fa:اقبال لاهوری<t:{{w|Muhammad Iqbal}}>|year=1923|text=سکندر رفت و شمشیر و علم رفت<br>خراج شهر و گنج کان و یم رفت<br>امم را از شهان پاینده تر دان<br>نمی بینی که ایران ماند و جم رفت|tr=sekandar raft o šamšir o 'alam raft<br>xarâj-e šahr o ganj-e kân o yam raft<br>omam râ az šahân pâyande-tar dân<br>ne-mi-bini ke '''irân''' mânad o jam raft|t=Alexander is gone, with his sword and his banner,<br>His cities' revenues and his treasures from mines and seas are gone.<br>Know that nations are more lasting than kings;<br>Do you not see that '''Iran''' remains, while {{w|Jamshid}} is gone?|footer={{q|Modern Iranian Persian Transliteration}}}} # {{given name|fa|female|xlit=Iran|from=เปอร์เซียกลาง}} ==== หมายเหตุการใช้ ==== * The female given name does not change its ending in the dialectal register. * In the Standard Colloquial of Iran (Tehrani), retains its original form {{m|fa-ira|ایران}} instead of an ''-un'' form ({{m|fa-ira|ایرون}}). ==== ลูกคำ ==== {{col|fa |{{fa-l|ایرانی|tr=ērānī}} |{{fa-l|ایرانشهر|tr=ērānšahr}} |{{fa-l|ایران‌ویج|tr=ērān-vēj}} |{{fa-l|ایران‌زمین|tr=ērān-zamīn}} }} ==== คำสืบทอด ==== {{top2}} * {{desc|as|ইৰান|bor=1}} * {{desc|bn|ইরান|bor=1}} * {{desc|ckb|ئێران|bor=1}} * {{desc|chg|ایران|bor=1}} ** {{desc|ug|ئىران}} ** {{desc|uz|Eron}} * {{desc|gu|ઈરાન|bor=1}} * {{desc|inc-hnd|-|bor=1}} {{xlit|ur|اِیرَان}} *: {{desc|hi|ईरान<tr:->}} *: {{desc|ur|اِیران<tr:->}} * {{desc|lrc|ێرۆ|tr=ırø|bor=1}} * {{desc|kmr|Îran|bor=1}} * {{desc|mr|-|bor=1}} *: {{desc|mr|इराण|sclang=1}} *: {{desc|mr|𑘃𑘨𑘰𑘜|sclang=1}} * {{desc|pa|-|bor=1}} *: {{desc|pa|ਇਰਾਨ|alts=1|sclang=1}} *: {{desc|pa|اِیران|sclang=1}} * {{desc|syl|ꠁꠞꠣꠘ|bor=1}} * {{desc|te|ఇరాన్|bor=1}} {{bottom}} == ภาษาอาเซอร์ไบจาน == {{wp|azb:}} === คำวิสามานยนาม === {{az-proper noun}} # {{spelling of|az|อักษรอาหรับ|İran|t=[[อิหร่าน]]}} == ภาษาอูรดู == {{wp|ur:}} {{Wiktionary:Picture dictionary/ur:map/Middle East}} === รากศัพท์ === {{etymon|ur|id=Iran|:bor|fa-cls:اِیرَان<id:Iran>|title=اِیرَان|tree=1}} {{bor+|ur|fa-cls|اِیرَان}} === การออกเสียง === * {{ur-IPA|اِیران}} * {{audio|ur|LL-Q1617 (urd)-نعم البدل-ایران.wav|a=Pakistan}} * {{rhymes|ur|ɑːn|s=2}} * {{hyph|ur|اِی|ران}} === คำวิสามานยนาม === {{ur-proper noun|m|head=اِیران}} # {{tcl|ur|อิหร่าน|id=Q794}} ==== คำเกี่ยวข้อง ==== * {{l|ur|اِیرانِی|t=เกี่ยวกับอิหร่าน}} === ดูเพิ่ม === {{list:countries in the Middle East/ur}} === อ้างอิง === * {{R:ur:Rekhta}} 8q54c2iojttp5dzong3gfslaoat4udl İran 0 2326565 5775287 5714981 2026-10-14T10:14:17Z Octahedron80 267 5775287 wikitext text/x-wiki == ภาษากากาอุซ == {{wp|gag:}} === คำวิสามานยนาม === {{head|gag|คำวิสามานยนาม}} # {{tcl|gag|อิหร่าน|id=Q794}} == ภาษาตาตาร์แบบไครเมีย == {{wp|crh:}} {{crh-scripts|ar=[[ایران]]|sl=Iran|cy=[[Иран]]|ml=İran}} === คำวิสามานยนาม === {{head|crh|คำวิสามานยนาม}} # {{tcl|crh|อิหร่าน|id=Q794}} == ภาษาตุรกี == {{wp|tr:}} === รากศัพท์ === {{inh+|tr|ota|ایران}}, จาก{{der|tr|fa-cls|ایران|tr=ērān}}, จาก{{der|tr|pal|𐭠𐭩𐭥𐭠𐭭}} === การออกเสียง === * {{IPA|tr|/íː.ɾan/}} * {{hyph|tr|İ|ran}} * {{audio|tr|LL-Q256 (tur)-ToprakM-İran.wav}} === คำวิสามานยนาม === {{tr-proper noun}} # {{tcl|tr|อิหร่าน|id=Q794}} #: {{syn|tr|Acem}} #: {{uxi|tr|[[İran İslam Cumhuriyeti|'''İran''' İslam Cumhuriyeti]]|สาธารณรัฐอิสลามอิหร่าน}} ==== การผันรูป ==== {{tr-prop-c-aı}} {{tr-poss-prop-c-aı}} ==== ลูกคำ ==== * {{l|tr|İranlı}} === ดูเพิ่ม === {{list:countries in Asia/tr}} == ภาษาอาเซอร์ไบจาน == {{wp|az:}} {{az-variant|Иран|ایران}} === คำวิสามานยนาม === {{az-proper noun}} # {{tcl|az|อิหร่าน|id=Q794}} ==== การผันรูป ==== {{az-decl-noun|a|c}} ==== ลูกคำ ==== * {{l|az|iranlı}} mrdkj5swwl7vszytejq1iwf6hvp67an มอดูล:Prti-translit 828 2326599 5775288 5715028 2026-10-14T10:16:52Z Octahedron80 267 5775288 Scribunto text/plain local export = {} local tt = { ['𐭀'] = 'อ', ['𐭁'] = 'บ', ['𐭂'] = 'ค', ['𐭃'] = 'ด', ['𐭄'] = 'ฮ', ['𐭅'] = 'ว', ['𐭆'] = 'ซ', ['𐭇'] = 'ฮฺ', ['𐭈'] = 'ฏ', ['𐭉'] = 'ย', ['𐭊'] = 'ก', ['𐭋'] = 'ล', ['𐭌'] = 'ม', ['𐭍'] = 'น', ['𐭎'] = 'ส', ['𐭏'] = 'อฺ', ['𐭐'] = 'พ', ['𐭑'] = 'ศ', ['𐭒'] = 'กฺ', ['𐭓'] = 'ร', ['𐭔'] = 'ช', ['𐭕'] = 'ต', } local numbers = { ['𐭘'] = 1, ['𐭙'] = 2, ['𐭚'] = 3, ['𐭛'] = 4, ['𐭜'] = 10, ['𐭝'] = 20, ['𐭞'] = 100, ['𐭟'] = 1000, } function export.convert_numbers(numeric_str) local total = 0 for c in mw.ustring.gmatch(numeric_str, ".") do total = total + numbers[c] end return "[" .. total .. "]" end function export.tr(text, lang, sc) -- If the script is not Prti, do not transliterate if sc ~= "Prti" then return end if mw.ustring.match(text, '[𐭘-𐭟]') then text = mw.ustring.gsub(text, '[𐭘-𐭟]+', export.convert_numbers) end -- Transliterate characters text = mw.ustring.gsub(text, '.', tt) return text end return export f4uhma2udtrt0rye3liavh7bp32oj2s แม่แบบ:etymon/documentation 10 2356369 5775224 5754453 2026-10-14T07:11:10Z Octahedron80 267 5775224 wikitext text/x-wiki {{documentation subpage}} {{uses lua|etymon|etymon/tree|etymon/text|etymon/categories|etymon/data|etymon/tracking|etymon/doc}} {{uses templatestyles|Module:etymon/styles.css}} {{shortcut|ety}} This template is used to indicate the entry term's immediate ancestor ({{m|en|etymon}}). Note that generally, an "etymon" can refer to any kind of etymological ancestor, but on this page, "etymon" will be used specifically for an immediate ancestor. For example, the etymon of {{m+|en|nexus}} is {{m+|la|nexus}}, and the etymon of {{m+|la|nexus}} is {{m+|la|nectō}}. Terms can have multiple etymons, so the etymons of {{m+|en|toothbrush}} are both {{m|en|tooth}} and {{m|en|brush}}. '''Current features:''' automatic categorization, tree generation, text generation, reference support. ==Usage== Suppose that the etymon of {{m+|en|sow|t=female pig}} is set to {{m+|enm|sowe}}, and the etymon of {{m|enm||sowe}} is set to {{m+|ang|sugu}}. The template is then able to intelligently connect {{m|en||sow}} and {{m|ang||sugu}}, even though they are two steps apart. The template thus reduces duplication across entries by making it unnecessary to manually specify that {{m|en||sow}} and {{m|ang||sugu}} are connected. ===Etymon IDs=== Etymon IDs are used to distinguish between multiple etymologies for the same term (''{{lg|homograph|homographs}}''), in order to ensure that the template is able to traverse these etymological chains without getting confused. This mirrors the practice of the ''{{w|Oxford English Dictionary}}''. For example, the two main senses of {{m|en||sow}} are identified as [https://doi.org/10.1093/OED/9086735656 9086735656] ({{m-g|to plant seeds}}) and [https://doi.org/10.1093/OED/9710708293 9710708293] ({{m-g|female pig}}). On Wiktionary, the etymon identifier should be a word or short phrase which summarizes an essential meaning or origin of the term rather than a meaningless string of numbers. (In contrast, [[:Template:senseid|sense ID]] values may freely use Wikidata item numbers, such as "[[d:Q84|Q84]]" for London, England.) In the case of {{m|en||sow}}, the two etymon IDs might be <code>plant seeds</code> and <code>female pig</code>. For terms with only one etymology, no ID is required either in the template's {{para|id}} parameter or in the etymon parameters. If a page has multiple etymon templates for the same language, {{em|all}} of them must have unique IDs—the template will throw an error if it finds multiple etymon templates where at least one is missing an ID. Do not add an etymon ID that is identical to any sense ID on the page, or vice versa. Use a moment's [[critical thinking]] to choose ID values that have a proper combination of (1) [[intrapage]] uniqueness and (2) adequate [[specificity]] to a meaning or an origin. ''Compare also:'' [[Template:etymid/documentation]]. ==Parameters== The template takes the following parameters: ; {{para|1|req=1}} : The language code of the current entry. ; {{para|id}} : The etymology ID. This parameter creates an anchor to the current section. For example, if the template at {{m+|en|father|id=male parent}} is {{tl|1=etymon|2=en|3=id=male parent}}, this etymology section is directly linked to by [[father#English: male parent]]. The ID must have at least two characters and must not be the same as the page title. This parameter is optional when there is only one etymology for the term, but required when there are multiple etymologies. ; {{para|title}} : This parameter manually overrides the current page title. For example, if an etymology tree is created at {{m+|la|pōnō}} (located at [[pono]]) it is necessary to specify {{para|title|pōnō}} to ensure that the macrons are displayed. ; {{para|pos}} : Indicates the part of speech of the current entry, which allows descendants to intelligently categorize themselves. Usually only necessary for Proto-Indo-European. Current allowed values are: <code>prefix</code>, <code>suffix</code>, <code>interfix</code>, <code>infix</code>, <code>root</code>, <code>word</code>. ; {{para|exnihilo}} : If set to anything, adds the entry to the category <code>[[:Category:Terms coined ex nihilo by language|<language> terms coined ex nihilo]]</code>. ; {{para|etydate}} : Specifies the date or time period when the term was first attested. Supports inline modifiers for references and formatting. For example: <code>etydate=1500s<ref:{{temp|R:OED}}></code> or <code>etydate=late 14c<nocap></code>. Ranges can also be given, e.g. <code>etydate=r,1501,1558<ref:{{temp|R:pl:SXVI|artykuł|5767}}></code> ; {{para|rfe}} : Adds a request for etymology notice. Can be set to <code>1</code> for a standard notice, or to custom text for a specific request. Supports inline modifiers: <code><nocat></code>, <code><noes></code>, <code><box></code>, <code><sort:key></code>, <code><y:year></code>, <code><m:month></code>, <code><fragment:section></code>, <code><section:name></code>. For example: {{para|rfe|1}}, <code>rfe=Need more details<nocat></code>, or <code>rfe=1<box><sort:custom></code>. ; {{para|nl}} : If set to anything, consider the entry a [[WT:Lemmas|non-lemma]]. ; {{para|nodot}} : If set to anything, removes the final dot from the generated etymology text. Useful when combining with other text or templates. ; {{para|nocat}} : If set to anything, suppresses automatic categorization. ; {{para|tree}} : If set to anything, displays an etymology tree. ; {{para|text}} : If set to anything, displays some text describing the etymology. The text modes are: :* <code>+</code> — single step (immediate parent only) :* <code>++</code> — all steps (full etymology chain) :* <code>*</code> — to the nearest blue link (stops when it reaches an existing entry) :* A number (e.g., <code>3</code>) — show up to that many steps :* <code>:langcode</code> (e.g., <code>:ar</code>) — show steps until reaching a term in the specified language :* <code>:langcode*</code> (e.g., <code>:ota*</code>) — stop at the specified language if it's a blue link, otherwise continue to the first blue link : For more information, see {{slink|#Text}}. ; {{para|2}}, {{para|3}}, ... : These are the etymon parameters. Each etymon parameter can be either an ''etymon'' or a ''derivation keyword''. There can be any number of etymon parameters. ===Etymons=== Etymons must be written using the following format: <code>languagecode:term<id:identifier><ref:reference></code>. For example: <code>en:pay<id:give money></code> represents {{m+|en|pay}} (etymology 1). As a shortcut, it is possible to omit the language code (i.e., just writing <code>pay<id:give money></code>). In this case, the template will assume that the language is the same as the one set in {{para|1}}. The ID is not mandatory - simple etymons can be written as just <code>languagecode:term</code> (e.g., <code>en:father</code>) or <code>term</code> (e.g., <code>father</code> when the language matches parameter 1). However, if there are multiple etymologies for the same term on the target page, an ID must be specified to distinguish between them. To suppress the term display (show only the language), use <code>-</code> as the term, e.g., <code>la:-</code>. Additional inline modifiers can be used: * <code><id:identifier></code> - specifies the etymology ID. Prefix with <code>!</code> (e.g., <code><id:!forced></code>) to force the ID even when disambiguation would normally not require it. * <code><t:gloss></code> - provides gloss/translation * <code><tr:transliteration></code> - provides transliteration * <code><ts:transcription></code> - provides transcription * <code><alt:alternative display></code> - alternative display text. Use this to suppress linking when referring to proto-language entries with a single descendant, which, according to policy, should not be created. * <code><pos:part of speech></code> - part of speech annotation * <code><ety:inline etymology></code> - inline etymology for redlinks (see section below) * <code><unc></code> - when set, marks the corresponding step or element as uncertain. Note that discredited, dubious, or speculative etymologies should not be added to the template at all. * <code><ref:reference text></code> - adds references (see References section) * <code><aftype:type></code> - specifies the affix type when used with <code>:af</code>. Valid values: <code>prefix</code>/<code>pre</code>, <code>suffix</code>/<code>suf</code>, <code>infix</code>/<code>in</code>, <code>interfix</code>/<code>inter</code>, <code>circumfix</code>/<code>circum</code>, <code>non-affix</code>/<code>naf</code>, <code>root</code>. * <code><postype:type></code> - specifies whether a term should be treated as a <code>root</code> or <code>word</code> for categorization purposes. Overrides the default detected from the linked page's {{para|pos}}. * <code><bor></code> - when used on a term under <code>:af</code>, adds borrowing categories for that specific component (for affixed terms where one component is borrowed). <code><lbor></code> and <code><slbor></code> are also valid term modifiers. * <code><senseid></code> - used for senseid's, see below on semantic loans. Multiple can be given by separating the id's with !!, e.g. <nowiki><senseid:sense1!!sense2></nowiki>. ===Inline etymology=== For terms that don't have their own entry (redlinks) or are missing an etymon template, you can specify the etymology inline using the <code><ety:...></code> modifier. This allows the tree to continue beyond redlinks. The syntax is: <code><ety:keyword<etymon1><etymon2>...></code> For example: * <code>enm:example<ety:inh<ang:example>></code> - specifies that the Middle English term is inherited from Old English * <code>la:verbum<ety:der<grc:ῥῆμα>></code> - specifies that the Latin term is derived from Greek The inline etymology is only used when the target page is a redlink or is missing an etymon template. If the target page has a valid etymon template, the inline etymology is ignored (and a tracking category is added). ===Derivation keywords=== Each derivation keyword applies to all the etymons that follow it. Derivation keywords must be prefixed with a colon (<code>:</code>). A derivation keyword is reset by another derivation keyword. For example, <code>|:inh|etymon1|etymon2|:bor|etymon3|etymon4</code> means that a term is inherited from both etymon1 and etymon2 and also borrowed from both etymon3 and etymon4. Using an unknown keyword (i.e. one not listed below) produces an error message. ====Keyword modifiers==== Keywords can have inline modifiers attached using angle brackets: * <code><unc></code>: marks the derivation as uncertain; for example, <code>:bor<unc></code> means "possibly borrowed from". * <code><ref:reference text></code>: adds a reference to the keyword (same syntax as etymon references). * <code><conj:conjunction></code>: specifies a custom conjunction when there are multiple etymons under the same keyword (default is "or"); Examples: ** <code>:bor<conj:and/or>|es:cruzado|pt:cruzado</code> produces "Borrowed from Spanish ''cruzado'' and/or Portuguese ''cruzado''." ** <code>:bor<conj:and>|etymon1|etymon2</code> produces "...''etymon1'' and ''etymon2''." * <code><surf></code> - used for surface analysis. ====Basic derivation==== * '''<code>:from</code>''': (default) unspecified derivation type within a language; corresponds to {{tl|from}}; only valid for same-language derivations. * '''<code>:derived</code>''' (or '''<code>:der</code>'''): used when a term is derived from another language; corresponds to {{tl|derived}}. * '''<code>:inherited</code>''' (or '''<code>:inh</code>'''): used when a term comes directly from the parent language unchanged; corresponds to {{tl|inherited}}. The template validates that the source language is a valid ancestor. * '''<code>:uder</code>''' (short for ''undefined derivation''; no long form yet): used when the exact relationship is unclear; adds to "undefined derivations" category. ====Borrowings==== * '''<code>:bor</code>''' (alias '''<code>:borrowed</code>'''): used when a term is borrowed from another language directly; corresponds to {{tl|borrowed}}. * '''<code>:lbor</code>''' (short for ''learned borrowing''): used for a {{lg|learned borrowing}}, that is a term is borrowed intentionally rather than through normal language contact; corresponds to {{tl|learned borrowing}}. * '''<code>:slbor</code>''' (short for ''semi-learned borrowing''): used for a learned borrowing which is reshaped somewhat; corresponds to {{tl|semi-learned borrowing}}. * '''<code>:obor</code>''' (short for ''orthographic borrowing''): used for {{lg|orthographic borrowing}}s; corresponds to {{tl|orthographic borrowing}}. * '''<code>:ubor</code>''' (short for ''unadapted borrowing''): used for direct borrowings that retain original orthography; corresponds to {{tl|unadapted borrowing}}. ====Semantic relationships==== * '''<code>:calque</code>''' (aliases '''<code>:cal</code>''' or '''<code>:clq</code>'''): used for {{lg|calque}}s; corresponds to {{tl|calque}}; non-transitive (does not follow the chain further). * '''<code>:partial calque</code>''' (aliases '''<code>:pcal</code>''' or '''<code>:pclq</code>'''): used for {{lg|partial calque}}s; corresponds to {{tl|partial calque}}; non-transitive. * '''<code>:semantic loan</code>''' (or '''<code>:sl</code>'''): used for {{lg|semantic loan}}s; corresponds to {{tl|semantic loan}}; non-transitive; the loaned sense may be linked using <nowiki><senseid></nowiki>, so long as {{temp|senseid}} is present near the appropriate definition. * '''<code>:influence</code>''' (no short form yet): used when a term is influenced in some way by another (e.g. the modern meaning of {{m+|en|discomfit}} is influenced by the unrelated word {{m|en|discomfort}}); non-transitive. ====Word formation==== * '''<code>:affix</code>''' (or '''<code>:af</code>'''): used for compounds, affixation, and any other template whose output contains a <code>+</code> between two terms; parameter order matters: <code>|:af|etymon1|etymon2</code> means etymon1 + etymon2; corresponds to {{tl|affix}}, {{tl|compound}}, and others; generates affix categories automatically. Keywords like ``:af`` or ``:deverbal`` can be marked with <nowiki><surf></nowiki> for surface analysis. * '''<code>:afeq</code>''' (short for "equivalent affix"): used as a "hidden {{tl|af}}", meaning that etymons with this keyword shall be invisible in tree and text output, but their respective categories shall still be generated; as an example, {{m|en|childhood}}, from {{m+|enm|childhode}} (pronounced ''cheeld-hohd''), is {{em|equivalent}} to {{af|en|child|-hood}}. * '''<code>:blend</code>''': used for {{lg|blend}}s; corresponds to {{tl|blend}}. * '''<code>:reduplication</code>''' (aliases '''<code>:rdp</code>''' or '''<code>:redup</code>'''): used for {{lg|reduplication|reduplications}}; corresponds to {{tl|reduplication}}. * '''<code>:abbreviation</code>''' (aliases '''<code>:abbr</code>''' or '''<code>:abbrev</code>'''): used for {{lg|abbreviation|abbreviations}}; corresponds to {{tl|abbreviation}}. * '''<code>:syllabic abbreviation</code>''' (aliases '''<code>:sylabbr</code>''' or '''<code>:sylabbrev</code>'''): used for {{lg|syllabic abbreviation|syllabic abbreviations}}; corresponds to {{tl|syllabic abbreviation}}. * '''<code>:acronym</code>''' (or '''<code>:acro</code>'''): used for {{lg|acronym}}s; corresponds to {{tl|acronym}}. * '''<code>:initialism</code>''' (or '''<code>:init</code>'''): used for {{lg|initialism}}s; corresponds to {{tl|initialism}}. * '''<code>:metathesis</code>''' (or '''<code>:meta</code>'''): used for {{lg|metathesis}}s; corresponds to {{tl|metathesis}}. * '''<code>:clipping</code>''' (or '''<code>:clip</code>'''): used for {{lg|clipping}}s; corresponds to {{tl|clipping}}. * '''<code>:ellipsis</code>''' (or '''<code>:ellip</code>'''): used for {{lg|ellipsis|elliptical forms}}; corresponds to {{tl|ellipsis}}. * '''<code>:univerbation</code>''' (or '''<code>:univ</code>'''): used for {{lg|univerbation}}; corresponds to {{tl|univerbation}}. * '''<code>:back-formation</code>''' (or '''<code>:bf</code>'''): used for {{lg|back-formation|back-formations}}; corresponds to {{tl|back-formation}}. * '''<code>:apheretic</code>''' (aliases '''<code>:apheresis</code>''' or '''<code>:aphetic</code>'''): used for {{lg|apheresis|apheretic forms}}. * '''<code>:denominal</code>''' (or '''<code>:denom</code>'''): used for {{lg|denominal}}s; corresponds to {{tl|denominal verb}}. * '''<code>:deverbal</code>''': used for {{lg|deverbal}}s; corresponds to {{tl|deverbal}}. * '''<code>:sa-af</code>''': used for Sanskritic formations. * '''<code>:vrd-af</code>''': used for Sanskrit vṛddhi derivatives with an affix. See '''<code>:vrd</code>''' below. ====Other==== * '''<code>:transliteration</code>''' (or '''<code>:translit</code>'''): used when a term is transliterated from another script; corresponds to {{tl|transliteration}}. * '''<code>:vrd</code>''' (short for "vṛddhi derivative"): used for Sanskrit vṛddhi derivatives. * '''<code>:root</code>''': marks the following etymons as roots for categorization purposes. The etymons will be categorized under "terms belonging to the root X" or "terms derived from the root X". This keyword is invisible in tree and text output. ===References=== References can be added to etymons using inline modifiers with the syntax <code><ref:reference text></code>. Multiple references can be separated by <code> !!! </code> (with spaces around the exclamation marks). References support the same syntax as other Wiktionary reference templates, including named references and groups (see [[Module:references]]). References are only displayed when {{para|text}} is set and only on the same entry they are defined, not on any descendants. Examples: * <code>la:verbum<ref:{{temp|R:L&S}}></code> - single reference ** Equivalent to <code><nowiki><ref>{{R:L&S}}</ref></nowiki></code> * <code>la:verbum<ref:{{temp|R:L&S}}<<name:LS>> !!! {{temp|R:Gaffiot}}></code> - multiple references with naming ** Equivalent to <code><nowiki><ref name="LS">{{R:L&S}}</ref><ref>{{R:Gaffiot}}</ref></nowiki></code> * <code>la:verbum<ref:<<name:LS>>></code> - reference to previously named reference ** Equivalent to <code><nowiki><ref name="LS"/></nowiki></code> * <code>la:verbum<ref:{{temp|R:L&S}}<<name:LS>><<group:etymology>>></code> - named reference with group ** Equivalent to <code><nowiki><ref name="LS" group="etymology">{{R:L&S}}</ref></nowiki></code> ===Trees=== {{shortcut|WT:ETYTREE}}<!-- Creating shortcuts from project namespace as this normally would be in a policy page. --> If the parameter {{para|tree|1}} is set, an etymology tree is inserted. Per an April 2024 vote, each language community decides when it is appropriate to show a tree on a particular entry.<ref>[[Wiktionary:Votes/2024-04/Allowing etymology trees on entries]]</ref> Additionally, trees should not be displayed for multi-word terms like {{m|en|United States of America}}.<ref>{{section link|Wiktionary:Beer parlour/2024/June#Use of etymology trees made with Template:etymon in the entries for multi-word terms}}</ref> Keywords with <code>no_child_categories</code> (such as <code>:calque</code>, <code>:sl</code>, <code>:pcal</code>, <code>:influence</code>) will have their subtrees hidden in the tree display, as these represent non-transitive relationships where the further etymology is not directly relevant. Similarly, duplicate nodes (when the same term appears multiple times in a tree) do not re-display their ancestry. In both cases, an L-shaped dotted connector indicates hidden ancestry, and duplicate nodes are additionally styled with dashed borders. ===Text=== {{anchor|Text allowlist}} {{shortcut|WT:ETYTEXT}}<!-- See ETYTREE above. --> The {{para|text}} parameter may be set to automatically generate a text description from the given data. The parameter should only be used in entries under the languages whose editing communities have explicitly approved it and as long as this does not imply the removal of valuable information which cannot be produced by the module.<ref>[[Wiktionary:Votes/2026-01/Reconsidering the parameters text and tree of etymon]]</ref> The following languages and families have allowed its usage. Any language in an allowed family (e.g. Indo-Aryan for family code <code>inc</code>) is also allowed. {{#invoke:etymon/doc|text_allowed_list}} ==Language-specific behavior== Some languages have special handling configured in the module: ===Chinese=== Etymology trees and text are disabled for Chinese languages (those in the <code>zhx</code> family, excluding contact languages (e.g. creoles, pidgins, mixed languages)). See [[Wiktionary:Beer parlour/2025/May#Template:etymon for Chinese]] for discussion. Categories are also suppressed, and transliterations are not displayed. ===Finnish=== The <code>:af</code> keyword is non-transitive for Finnish, meaning the etymology chain stops at affixed terms. ==Examples== <code>{{tlf|etymon|en|id=male parent|:inh|enm:fader<id:father>}}</code> (on {{m+|en|father|id=male parent}}) This means: ''father'' is inherited from Middle English ''fader''. <code>{{tlf|etymon|en|:inh|enm:fader}}</code> (on a page with only one etymology) This also works when there is only one etymology—no ID required on either side. <code>{{tlf|etymon|ine-pro|id=father|:af|*peh₂-<id:protect><unc>|*-tḗr<id:agent noun><unc>}}</code> (on {{m+|ine-pro|*ph₂tḗr|id=father}}) This means: ''*ph₂tḗr'' might come from ''*peh₂'' + ''*-tḗr''. In this case, the etymons are associated with the derivation keyword ":af". Note that since the language is not specified for either etymon, the template assumes that the two etymons are <code>ine-pro</code> (Proto-Indo-European). <code>{{tlf|etymon|pl|:deverbal|podłożyć<id:put>}}</code> (on {{m+|pl|podłoga}}) This means: ''podłoga'' comes from Polish ''podłożyć''. <code>{{tlf|etymon|en|id=bundle of flowers|:bor|fr:bouquet<id:bundle><ref:{{temp|R:TLFi}}>|text=+}}</code> This means: ''bouquet'' is borrowed from French ''bouquet'', with a reference to the TLFi dictionary. The reference will appear after the final dot in the generated text: "Borrowed from French ''bouquet''.<sup>[1]</sup>" <code>{{tlf|etymon|en|id=example|:af|un-|happy<aftype:naf>}}</code> Using <code><aftype:naf></code> to mark "happy" as a non-affix (the base word) rather than letting auto-detection determine the affix type. <code>{{tlf|etymon|en|id=example|:root|ine-pro:*bʰer-<id:carry>}}</code> Using <code>:root</code> to categorize the term under "English terms derived from the Proto-Indo-European root *bʰer-" without showing this in the tree or text. <code>{{tlf|etymon|en|id=example|:der|la:exemplum<ety:inh<la:eximō>>}}</code> Using inline etymology to specify that Latin ''exemplum'' is inherited from ''eximō'', useful when the Latin page doesn't have an etymon template. <code>{{tlf|etymon|en|:bor<conj:and/or>|es:cruzado|pt:cruzado|text=+}}</code> Using the <code><conj:and/or></code> modifier to specify a custom conjunction. This produces: "Borrowed from Spanish ''cruzado'' and/or Portuguese ''cruzado''." ===Suppressed terms=== <code>{{tlf|etymon|en|id=example|:inh|enm:-<t:some term>}}</code> Using <code>-</code> as the term to suppress the term display and show only the language. The gloss and other modifiers are still displayed. ===Text output modes=== <code>{{tlf|etymon|en|id=example|:inh|enm:word|text=+}}</code> Using {{para|text|+}} to show only the immediate etymon (one step). <code>{{tlf|etymon|en|id=example|:inh|enm:word|text=++}}</code> Using {{para|text|++}} to show the full etymology chain (all steps, following through to the ultimate origin). <code>{{tlf|etymon|en|id=example|:inh|enm:word|text=*}}</code> Using {{para|text|*}} to show the etymology chain until reaching the first existing entry (blue link). <code>{{tlf|etymon|en|id=example|:inh|enm:word|text=3}}</code> Using {{para|text|3}} to show up to 3 steps in the etymology chain. <code>{{tlf|etymon|en|id=example|:der|ar:كلمة|text=:ar}}</code> Using {{para|text|:ar}} to show the etymology chain until reaching an Arabic term. Invalid language codes will trigger a warning and default to showing the full chain. <code>{{tlf|etymon|en|id=title|:bor|ota:پاشا<tr:paşa><ety:bor<fa-cls:پَادْشَاه>>|text=:ota*}}</code> Using {{para|text|:ota*}} on {{m+|en|pasha}} to stop at Ottoman Turkish if it exists, otherwise continue to Persian. ===Multiple etymon sources=== <code>{{tlf|etymon|en|id=example|:bor<unc>|es:palabra|pt:palavra}}</code> Multiple etymons under the same keyword with uncertainty. The <code><unc></code> modifier on the keyword marks the entire derivation as uncertain ("Possibly borrowed from..."). <code>{{tlf|etymon|en|id=example|:inh|enm:word|:bor|fr:mot}}</code> Multiple keywords in sequence. This indicates the term is both inherited from Middle English ''word'' and also borrowed from French ''mot'' (e.g., for different senses or competing etymologies). <code>{{tlf|etymon|en|id=example|:af|un-<aftype:pre>|happy<aftype:naf>|-ness<aftype:suf>}}</code> Complex affixation with explicit affix types. The <code><aftype:...></code> modifier specifies whether each component is a prefix (<code>pre</code>), suffix (<code>suf</code>), infix (<code>in</code>), interfix (<code>inter</code>), circumfix (<code>circum</code>), non-affix base word (<code>naf</code>), or root (<code>root</code>). ===Semantic relationships=== <code>{{tlf|etymon|en|id=modern sense|:influence|en:comfort<id:ease>}}</code> Using <code>:influence</code> to indicate semantic influence. This is non-transitive (does not continue the etymology chain). Useful for cases like ''discomfit'' being influenced by ''discomfort''. <code>{{tlf|etymon|en|id=example|:calque|de:Weltanschauung}}</code> Using <code>:calque</code> for calques. Non-transitive; the etymology of the source term is not followed. <code>{{tlf|etymon|en|id=example|:pcal|fr:gratte-ciel}}</code> Using <code>:pcal</code> (partial calque). Also non-transitive. <code>{{tlf|etymon|en|id=example|:sl|la:musculus<id:muscle>}}</code> Using <code>:sl</code> (semantic loan). Also non-transitive. ===Specialized borrowings=== <code>{{tlf|etymon|en|id=example|:lbor|la:exemplum}}</code> Using <code>:lbor</code> for learned borrowings (terms borrowed through literary or scholarly transmission rather than natural language contact). <code>{{tlf|etymon|es|id=example|:slbor|la:capitulum}}</code> Using <code>:slbor</code> for semi-learned borrowings (learned borrowings that have been partially adapted to native phonology). <code>{{tlf|etymon|en|id=example|:ubor|fr:déjà vu}}</code> Using <code>:ubor</code> for unadapted borrowings (terms borrowed with original orthography intact). <code>{{tlf|etymon|ja|id=example|:obor|zh:電話}}</code> Using <code>:obor</code> for orthographic borrowings (borrowing of written characters/script). ===Word formation=== <code>{{tlf|etymon|en|id=example|:blend|motor|hotel}}</code> Using <code>:blend</code> for blends (portmanteau words). <code>{{tlf|etymon|en|id=example|:univ|good|bye}}</code> Using <code>:univerbation</code> (or <code>:univ</code>) for univerbation (multiple words becoming one). <code>{{tlf|etymon|en|id=example|:clip|advertisement}}</code> Using <code>:clipping</code> (or <code>:clip</code>) for clippings. <code>{{tlf|etymon|en|id=example|:bf|editor}}</code> Using <code>:back-formation</code> (or <code>:bf</code>) for back-formations. <code>{{tlf|etymon|en|id=example|:apheretic|esquire}}</code> Using <code>:apheretic</code> (or <code>:apheresis</code>) for apheretic forms (loss of initial unstressed vowel). <code>{{tlf|etymon|en|id=example|:denominal|hammer}}</code> Using <code>:denominal</code> (or <code>:denom</code>) for denominal verbs (verbs derived from nouns). <code>{{tlf|etymon|en|id=example|:deverbal|speak}}</code> Using <code>:deverbal</code> for deverbals (nouns/adjectives derived from verbs). ===Inline etymology for redlinks=== <code>{{tlf|etymon|en|id=example|:der|xno:mot<ety:inh<fro:mot<ety:inh<la:muttum>>>>}}</code> Using nested inline etymologies to specify a full chain for terms without entries. This specifies that Anglo-Norman ''mot'' is inherited from Old French ''mot'', which is inherited from Latin ''muttum''. <code>{{tlf|etymon|en|id=example|:der|enm:word<ety:af<ang:word><-s>>}}</code> Inline etymology with affixation—specifies the Middle English term is formed from Old English ''word'' + ''-s''. ===References=== <code>{{tlf|etymon|en|id=example|:bor|fr:mot<ref:{{temp|R:TLFi}} !!! {{temp|R:Larousse}}>}}</code> Multiple references on a single etymon, separated by <code> !!! </code> (with spaces). <code>{{tlf|etymon|en|id=example|:bor<ref:{{temp|R:OED}}>|fr:mot}}</code> Reference on the keyword itself (applies to the derivation relationship, not just the term). <code>{{tlf|etymon|en|id=example|:der|la:verbum<ref:<<name:LS>>{{temp|R:L&S}}>}}</code> Named reference using <code><<name:LS>></code> syntax, which allows re-use elsewhere with <code><ref:<<name:LS>>></code>. <code>{{tlf|etymon|en|id=example|:der|la:verbum<ref:<<group:etymology>>{{temp|R:L&S}}>}}</code> Reference with a group, for use with grouped reference lists. ===Tree display=== <code>{{tlf|etymon|en|id=example|:inh|enm:word|tree=1}}</code> Using {{para|tree|1}} to display an etymology tree. ===Combined features=== <code>{{tlf|etymon|en|id=give money|:inh|enm:payen<id:pay><ref:{{temp|R:MED}}>|text=++|tree=1}}</code> Comprehensive example combining: etymology ID, inheritance keyword, etymon with ID and reference, full text output, and tree display. <code>{{tlf|etymon|en|id=example|etydate=1500s<ref:{{temp|R:OED}}>|:bor<unc>|fr:exemple<t:example><tr:ɛɡzɑ̃pl>|text=+|nodot=1}}</code> Example with attestation date, uncertain borrowing, gloss and transliteration, single-step text output, and no final dot (for combining with additional text). See [[Template:etymon/testcases]] for test cases and more examples of use. ==Categorization== The template generates various categories depending on how it is used, including the ones within: * [[:Category:Entries referencing ambiguous etymons by language]] * [[:Category:Entries referencing etymons with mismatched IDs by language]] * [[:Category:Entries with etymology trees by language]] * [[:Category:Entries with etymology texts by language]] * [[:Category:Pages with etymology trees]] * [[:Category:Pages with inline etymon for redlinks]] * [[:Category:Pages with redundant inline etymon]] * [[:Category:Pages using etymon with no ID]] * [[:Category:Terms coined ex nihilo by language]] as well as various standard etymology categories depending on what entries exist in the tree: * Borrowing categories (e.g., [[:Category:English terms borrowed from French]]) * Inheritance categories (e.g., [[:Category:English terms inherited from Middle English]]) * Affix categories (e.g., [[:Category:English terms prefixed with un-]]) * Root categories (e.g., [[:Category:English terms derived from the Proto-Indo-European root *bʰer-]]) * Specialized borrowing categories (learned, semi-learned, orthographic, unadapted) * Calque, partial calque, and semantic loan categories ==Tracking== {{hatnote|See also: [[Wiktionary:Tracking#Etymon]].}} The module tracks various statistics for debugging and analysis: * Tree depth (how many generations deep the etymology goes) * Number of nodes (total terms in the tree) * Number of unique languages * Linear vs. branching trees These can be found in the tracking categories under <code>Template:tracking/etymon/</code>. ==References== {{reflist}} ==TemplateData== {{TemplateData header}} <templatedata> { "description": "This template may be used indicate a term's immediate ancestor(s).", "params": { "1": { "label": "Language", "description": "The language of the current entry.", "type": "string", "required": true, "suggested": true, "example": "en" }, "2": { "label": "Etymon parameter #1", "description": "The first etymon parameter. Typically for derivation keyword.", "type": "string", "example": ":der, alternatively - enm:fader<id:father>" }, "3": { "label": "Etymon parameter #2", "description": "The second etymon parameter.", "type": "string" }, "4": { "label": "Etymon parameter #3", "description": "The third etymon parameter.", "type": "string" }, "5": { "label": "Etymon parameter #4", "description": "The fourth etymon parameter.", "type": "string" }, "6": { "label": "Etymon parameter #5", "description": "The fifth etymon parameter.", "type": "string" }, "7": { "label": "Etymon parameter #6", "description": "The sixth etymon parameter.", "type": "string" }, "8": { "label": "Etymon parameter #7", "description": "The seventh etymon parameter.", "type": "string" }, "9": { "label": "Etymon parameter #8", "description": "The eighth etymon parameter.", "type": "string" }, "id": { "label": "Etymology ID", "description": "The ID of the current etymology section. Required when there are multiple etymologies for the same term.", "type": "string", "suggested": true, "example": "male parent" }, "title": { "label": "Title", "description": "The title of the current entry (if different from the page title)", "type": "string", "example": "father" }, "pos": { "label": "Part of speech", "description": "The part of speech (prefix, suffix, interfix, infix, root, word)", "type": "string", "example": "root" }, "etydate": { "label": "Etymology date", "description": "The date or time period when the term was first attested", "type": "string", "example": "1500s<ref:{{R:OED}}>" }, "rfe": { "label": "Request for etymology", "description": "Adds a request for etymology notice. Set to 1 for standard notice, or provide custom text. Supports inline modifiers.", "type": "string", "example": "1, or Need more details<nocat>" }, "exnihilo": { "label": "Ex nihilo", "description": "Set to categorize as a term coined ex nihilo", "type": "boolean" }, "nl": { "label": "Non-lemma", "description": "Set to consider the entry a non-lemma", "type": "boolean" }, "nocat": { "label": "No categories", "description": "Set to suppress automatic categorization", "type": "boolean" }, "nodot": { "label": "No dot", "description": "Set to remove the final dot from generated text", "type": "boolean" }, "tree": { "label": "Tree", "description": "Set this to \"1\" to display a tree.", "type": "string", "example": "1" }, "text": { "label": "Text", "description": "Automatically generates some text. Values: + (single step), ++ (all steps), * (to nearest blue link), a number (max steps), :langcode (stop at language), :langcode* (stop at language or first blue link if redlink)", "type": "string", "example": "++" } }, "paramOrder": [ "1", "id", "title", "pos", "rfe", "2", "3", "4", "5", "6", "7", "8", "9", "etydate", "exnihilo", "text", "tree", "nl", "nocat", "nodot" ], "format": "inline" } </templatedata> <includeonly> [[Category:Etymology templates]] </includeonly> kfva7n1clvasewtvhrv36uyafsos1ce Antarctig 0 2358922 5775202 5770341 2026-10-14T04:57:15Z Octahedron80 267 /* อ่านเพิ่ม */ 5775202 wikitext text/x-wiki == ภาษาเวลส์ == === รากศัพท์ === {{bor+|cy|en|Antarctic}}, จาก{{der|cy|la|antarcticus}} และ{{der|cy|grc|ἀνταρκτικός}} === คำคุณศัพท์ === {{cy-adj|-}} # [[แอนตาร์กติก]] #: {{syn|cy|cyfddeau|gwrtharthrodol|q=เลิกใช้}} #: {{ant|cy|Arctig|arthrodol|q2=เลิกใช้}} ==== ลูกคำ ==== * {{l|cy|Cylch Antarctig|y Cylch Antarctig|วงกลมแอนตาร์กติก}} === คำนาม === {{cy-noun|m|head=yr Antarctig}} # {{tcl|cy|แอนตาร์กติก|id=Q1555938}} #: {{syn|cy|[[gwrtharthrod|y gwrtharthrod]]|q=เลิกใช้}} #: {{ant|cy|[[Arctig|yr Arctig]]|[[arthrod|yr arthrod]]|q2=เลิกใช้}} === การกลายรูป === {{cy-mut}} === อ่านเพิ่ม === * {{R:cy:Gweiadur}} * {{R:cy:GPC}} {{cln|cy|ศัพท์ที่เติมปัจจัย -ig}} 662oslu0c4ooes2c9om6rw6o6qmprzc คุยกับผู้ใช้:โสภีศิริถ้ากดิ์ 3 2359197 5775188 2026-10-14T04:13:40Z New user message 2698 เพิ่ม[[Template:Welcome|สารต้อนรับ]]ในหน้าคุยของผู้ใช้ใหม่ 5775188 wikitext text/x-wiki {{Template:Welcome|name=โสภีศิริถ้ากดิ์}} -- [[ผู้ใช้:New user message|New user message]] ([[คุยกับผู้ใช้:New user message|คุย]]) 11:13, 14 ตุลาคม 2569 (+07) 488tt0vmb0kwu0i1jhhxb7k7tn39rjd มอดูล:pron qualifier 828 2359198 5775195 2026-10-14T04:29:09Z Octahedron80 267 Octahedron80 ย้ายหน้า [[มอดูล:pron qualifier]] ไปยัง [[มอดูล:decorations]]: การย้ายโดยเทคนิค 5775195 Scribunto text/plain return require [[มอดูล:decorations]] dv6b950pb9sjy74v2gj0su1l9n60bw3 balbetterà 0 2359200 5775212 2026-10-14T05:59:44Z Octahedron80 267 นำเข้าจาก enwikt เก็บกวาด 5775212 wikitext text/x-wiki == ภาษาอิตาลี == === คำกริยา === {{head|it|รูปกริยา}} # {{inflection of|it|balbettare||3|s|futr}} === คำสลับอักษร === * {{anagrams|it|a=aabbeelrtt|abbatterle|balbettare}} gbyw3qdxgsfegrv8aeg4irdb2a6wdou abbatterle 0 2359201 5775213 2026-10-14T05:59:48Z Octahedron80 267 นำเข้าจาก enwikt เก็บกวาด 5775213 wikitext text/x-wiki == ภาษาอิตาลี == === คำกริยา === {{head|it|รูปกริยา}} # {{it-compound of}} === คำสลับอักษร === * {{anagrams|it|a=aabbeelrtt|balbettare|balbetterà}} evi4xpiq090n5wi8myj3skbsrfykrtk حيتان 0 2359202 5775220 2026-10-14T07:05:44Z Octahedron80 267 นำเข้าจาก enwikt เก็บกวาด 5775220 wikitext text/x-wiki == ภาษาอาหรับ == === รากศัพท์ === {{ety|ar|nl=1}} === การออกเสียง === * {{IPA|ar|/ħiːtaːn/}} === คำนาม === {{ar-head|nounf|حِيتَان|m-p}} # {{plural of|ar|حُوت}} 629rji1itoqdcqjgtylmlcpg0vc6djn أحوات 0 2359203 5775221 2026-10-14T07:05:49Z Octahedron80 267 นำเข้าจาก enwikt เก็บกวาด 5775221 wikitext text/x-wiki == ภาษาอาหรับ == === รากศัพท์ === {{ety|ar|nl=1}} === คำนาม === {{ar-head|nounf|أَحْوَات|m-p}} # {{plural of|ar|حُوت}} 62sf1nwggatrjsnirzt4spiu3yrg5kx หมวดหมู่:รูปนามภาษาอาหรับ 14 2359204 5775222 2026-10-14T07:06:12Z Octahedron80 267 สร้างหมวดหมู่อัตโนมัติ 5775222 wikitext text/x-wiki {{auto cat}} eomzlm5v4j7ond1phrju7cnue91g5qx حوى 0 2359205 5775235 2026-10-14T08:38:36Z Octahedron80 267 นำเข้าจาก enwikt เก็บกวาด เก็บกวาด 5775235 wikitext text/x-wiki == ภาษาอาหรับ == {{ar-rootbox|ح و ي}} === รากศัพท์ 1 === {{ety|ar|id=to gather|:root|ح و ي<id:root>|tree=1}} ==== คำกริยา ==== {{ar-verb|I.pass.vn:حَيّ,حَوَايَة}} # [[รวบรวม]], [[สะสม]], [[ได้]][[รับ]] # [[รักษา]], [[ปกป้อง]], [[คุ้มกัน]] # {{lb|ar|สกรรม}} [[บรรจุ]], [[ประกอบ]][[ด้วย]] # [[ครอบครอง]], [[ยึดครอง]] ===== การผันรูป ===== {{ar-conj|I.pass.vn:حَيّ,حَوَايَة}} ===== อ้างอิง ===== * {{R:ar:Steingass}} === รากศัพท์ 2 === {{ety|ar|id=blackness|:root|ح و ي<id:root>|tree=1}} ==== คำนาม ==== {{ar-noun|حَوًى|m}} # {{ar-verbal noun of|حَوِيَ|form=I}} ===== การผันรูป ===== {{ar-decl-noun|حَوًى}} k1w5len3f3p7bhwnwqd1ud20s34e5nd มอดูล:roots 828 2359206 5775236 2026-10-14T08:39:09Z Octahedron80 267 สร้างหน้าด้วย "local export = {} local force_cat = false -- for debugging local M = require("Module:module loader").init({ require = { str_utils = "Module:string utilities", utilities = "Module:utilities", links = "Module:links", headword = "Module:headword", languages = "Module:languages", parameters = "Module:parameters", inflection_table = "Module:inflection-table", }, loadData = { radicals = "Module:roots/data/radicals", }, lazy = { co..." 5775236 Scribunto text/plain local export = {} local force_cat = false -- for debugging local M = require("Module:module loader").init({ require = { str_utils = "Module:string utilities", utilities = "Module:utilities", links = "Module:links", headword = "Module:headword", languages = "Module:languages", parameters = "Module:parameters", inflection_table = "Module:inflection-table", }, loadData = { radicals = "Module:roots/data/radicals", }, lazy = { concat = function() return table.concat end, insert = function() return table.insert end, }, }) local UI_STRINGS = { no_langcode = "Please provide a language code.", invalid_langcode = "Invalid language code: %s", not_a_table = "rootTable is not a table", min_radicals = "Root must have at least %s %s.", max_radicals = "Root must have at most %s %s.", unrecognized_pos = "Unrecognized %s radical %s in %s", unrecognized_mem = "Unrecognized radical %s in %s", not_single_letter = "'%s' in the root '%s' should be a single letter.", invalid_validation = "Unknown validation model: %s", only_one_root = "There should be only one root.", bad_root = "Invalid %s root '%s': %s", wrong_separator = "Radicals should be separated by '%s'.", root_vs_pagename = "Root is different from the pagename.", move_to_appendix = "The root page should be in the Appendix namespace. Please move it to : [[%s]]", appendix_subpage = "Appendix:%s roots/", root_category = "%s terms belonging to the root %s%s", derived_category = "%s terms derived from the %s root %s%s", derived_language = "%s terms derived from %s", root_heading = "\n! [[w:%s|%s]]", sense_wrapper = "&lrm; <span class=\"gloss-content\">(%s)</span>", article_semitic = "Semitic root", article_generic = "Root (linguistics)", } local DEFAULT_LINK_TARGET = "bare" local DEFAULT_HEADWORD = {} local function split_root(root, separator) if not separator then return { root } end return mw.text.split(root, separator, true) end local function radicals_word(count) return count == 1 and "radical" or "radicals" end function export.get_data_module_name(langcode) return "Module:roots/data/" .. langcode end local function try_load_data(langcode) local ok, data = pcall(mw.loadData, export.get_data_module_name(langcode)) if ok then return data end return nil end function export.get_data(langcode) return try_load_data(langcode) end local function get_lang_and_data(frame) local code = frame.args["lang"] if not code or code == "" then error(UI_STRINGS.no_langcode) end local lang = M.languages.getByCode(code) if not lang then error(UI_STRINGS.invalid_langcode:format(code)) end return lang, try_load_data(code) or {} end local function resolve_radicals(data) if data.validation == "positional" or data.radicals then return data.radicals end if data.shared_radicals then return M.radicals[data.shared_radicals] end return nil end local function transliterate_root(root_string, romanization) if not romanization then return nil end local out = root_string for _, pair in ipairs(romanization) do out = M.str_utils.gsub(out, pair[1], pair[2]) end return out end export.transliterate = transliterate_root local function validate_root(rootTable, joined_root, data, radical_set) if type(rootTable) ~= "table" then return nil, UI_STRINGS.not_a_table end local len = #rootTable if data.min_radicals and len < data.min_radicals then return nil, UI_STRINGS.min_radicals:format(data.min_radicals, radicals_word(data.min_radicals)) end if data.max_radicals and len > data.max_radicals then return nil, UI_STRINGS.max_radicals:format(data.max_radicals, radicals_word(data.max_radicals)) end if data.validation == "positional" then for i, radical in ipairs(rootTable) do local set, label if i == 1 then set, label = radical_set.initial, "initial" elseif i == len then set, label = radical_set.final, "final" else set, label = radical_set.medial, "medial" end if not set[radical] then return nil, UI_STRINGS.unrecognized_pos:format(label, radical, joined_root) end end elseif data.validation == "membership" then for _, radical in ipairs(rootTable) do if not radical_set[radical] then return nil, UI_STRINGS.unrecognized_mem:format(radical, joined_root) end end elseif data.validation == "single_letter" then for _, radical in ipairs(rootTable) do if mw.ustring.len(radical) ~= 1 then return nil, UI_STRINGS.not_single_letter:format(radical, joined_root) end end elseif data.validation ~= nil then return nil, UI_STRINGS.invalid_validation:format(tostring(data.validation)) end return true end local function link(term, alt, translit, id, lang) if term == "" or term == "&mdash;" then return term end return M.links.full_link({ term = term, alt = alt ~= term and alt or nil, tr = translit, lang = lang, id = id, }) end local function root_category(lang, source, root, qualifier) local suffix = (qualifier and qualifier ~= "") and (" (" .. qualifier .. ")") or "" if lang:getCode() == source:getCode() then return UI_STRINGS.root_category:format(lang:getFullName(), root, suffix) end return UI_STRINGS.derived_category:format( lang:getFullName(), source:getCanonicalName(), root, suffix) end local function check_root(source, root) local data = try_load_data(source:getCode()) if not data then return true end local bare = (root:gsub("^%*", "")) local radicals = split_root(bare, data.separator) if data.separator and #radicals == 1 and mw.ustring.len(bare) > 1 then return nil, UI_STRINGS.wrong_separator:format(data.separator) end return validate_root(radicals, bare, data, resolve_radicals(data)) end function export.get_link_page(lang, root) local data = try_load_data(lang:getCode()) if not data or data.link_target ~= "appendix_subpage" then return nil end export.assert_root(lang, root) return UI_STRINGS.appendix_subpage:format(lang:getCanonicalName()) .. (root:gsub("^%*", "")) end function export.assert_root(source, root) local ok, err = check_root(source, root) if not ok then error(UI_STRINGS.bad_root:format(source:getCanonicalName(), root, err)) end return true end function export.get_categories(lang, source, roots, ids) local categories = {} ids = ids or {} if lang:getCode() ~= source:getCode() then M.insert(categories, UI_STRINGS.derived_language:format(lang:getFullName(), source:getCanonicalName())) end for index, root in ipairs(roots) do M.insert(categories, root_category(lang, source, root, ids[index])) export.assert_root(source, root) end return categories end function export.categorize(lang, source, roots, ids, sort_key) return M.utilities.format_categories( export.get_categories(lang, source, roots, ids), lang, sort_key, nil, force_cat) end function export.show(frame) local params = { [1] = { required = true, type = "language" }, [2] = { required = true, type = "language" }, [3] = { required = true, list = true }, ["id"] = { list = true, allow_holes = true }, ["sort"] = {}, } local args = M.parameters.process(frame:getParent().args, params) return export.categorize(args[1], args[2], args[3], args.id, args.sort) end function export.root(frame) local lang, data = get_lang_and_data(frame) local radical_set = resolve_radicals(data) local link_target = data.link_target or DEFAULT_LINK_TARGET local output = {} local categories = {} local title = mw.title.getCurrentTitle() local namespace = title.nsText local params = { [1] = { list = true }, ["nocat"] = { type = "boolean" }, ["plain"] = { type = "boolean" }, ["notext"] = { type = "boolean" }, ["sense"] = {}, } if link_target == "bare" then params["head"] = true params["pagename"] = true end local args = M.parameters.process(frame:getParent().args, params) local roots = args[1] local roots_len = #roots local plain = args["plain"] if frame.args["plain"] then plain = true end local separator = data.separator local identity local subpage = "" if link_target == "appendix_subpage" then subpage = UI_STRINGS.appendix_subpage:format(lang:getCanonicalName()) local fulltitle = title.fullText fulltitle = M.str_utils.gsub(fulltitle, M.str_utils.gsub(subpage, "([^%w])", "%%%1"), "") identity = fulltitle else identity = args["pagename"] or mw.loadData("Module:headword/data").pagename end local rootLetters = {} if roots_len == 0 and not args["pagename"] and namespace == "Template" and data.preview_root then M.insert(rootLetters, split_root(data.preview_root, separator)) elseif roots_len ~= 0 then for _, root in ipairs(roots) do M.insert(rootLetters, split_root(root, separator)) end else if link_target == "bare" then local head = frame.args["head"] if args["head"] ~= nil and identity ~= lang:stripDiacritics(args["head"]) then error(UI_STRINGS.root_vs_pagename) end if head and args["head"] ~= nil then identity = args["head"] end end M.insert(rootLetters, split_root(identity, separator)) end local joined_roots = {} for i, rootLetter in ipairs(rootLetters) do M.insert(joined_roots, M.concat(rootLetter, separator)) local ok, err = validate_root(rootLetter, joined_roots[i], data, radical_set) if not ok then error(err) end end local sense = args["sense"] local sense_formatted = "" if sense ~= nil and sense ~= "" then sense_formatted = UI_STRINGS.sense_wrapper:format(sense) end if identity == joined_roots[1] then if link_target == "appendix_subpage" and namespace == "" then error(UI_STRINGS.move_to_appendix:format(subpage .. joined_roots[1])) end if roots_len > 1 then error(UI_STRINGS.only_one_root) end local headword_args = { lang = lang, pos_category = "roots", categories = {}, heads = { identity }, nomultiwordcat = true, } local headword_opts = data.headword or DEFAULT_HEADWORD if headword_opts.translits then headword_args.translits = { transliterate_root(identity, data.romanization) } end if headword_opts.noposcat then headword_args.noposcat = true elseif headword_opts.nolemmacat then headword_args.nolemmacat = true end M.insert(output, M.headword.full_headword(headword_args)) if args["nocat"] then return M.concat(output) end return M.concat(output) .. M.concat(categories) else local link_texts = {} local term_counts = {} for i, joined_root in ipairs(joined_roots) do local link_text if link_target == "appendix_subpage" then link_text = subpage .. joined_root else link_text = joined_root end local translit = data.romanization and transliterate_root(joined_root, data.romanization) or nil M.insert(link_texts, link(link_text, joined_root, translit, sense, lang) .. sense_formatted) M.insert(categories, M.utilities.format_categories( { root_category(lang, lang, joined_root, sense) }, lang, nil, nil, force_cat)) M.insert(term_counts, mw.site.stats.pagesInCategory( root_category(lang, lang, joined_root, sense), "pages")) end if args["nocat"] or plain then if args["nocat"] then return M.concat(link_texts, ", ") end return M.concat(link_texts, ", ") .. M.concat(categories) else local link_text_output = "" for i, link_text in ipairs(link_texts) do link_text_output = link_text_output .. "\n|-\n| " .. link_text .. "\n|-\n| [[:Category:" .. root_category(lang, lang, joined_roots[i], sense) .. "|" .. term_counts[i] .. " term" .. (term_counts[i] == 1 and "" or "s") .. "]]\n" end local wikicode = M.inflection_table.make_top{ title = "-", palette = data.palette, class = "floatright tr-alongside" } local article = lang:inFamily("sem") and UI_STRINGS.article_semitic or UI_STRINGS.article_generic wikicode = wikicode .. UI_STRINGS.root_heading:format(article, #term_counts == 1 and "Root" or "Roots") wikicode = wikicode .. link_text_output wikicode = wikicode .. M.inflection_table.make_bottom{} return wikicode .. M.concat(categories) end end end return export dheqjnietjb8xn481m1a1x4x9vm0r5a มอดูล:roots/data/radicals 828 2359207 5775237 2026-10-14T08:40:30Z Octahedron80 267 สร้างหน้าด้วย "local radicals = {} radicals["Arabic"] = { ["ء"] = true, ["ب"] = true, ["ت"] = true, ["ث"] = true, ["ج"] = true, ["ح"] = true, ["خ"] = true, ["د"] = true, ["ذ"] = true, ["ر"] = true, ["ز"] = true, ["س"] = true, ["ش"] = true, ["ص"] = true, ["ض"] = true, ["ط"] = true, ["ظ"] = true, ["ع"] = true, ["غ"] = true, ["ف"] = true, ["ق"] = true, ["ك"] = true, ["ل"] = true, ["م"] = true, ["ن"] = true, ["ه"] =..." 5775237 Scribunto text/plain local radicals = {} radicals["Arabic"] = { ["ء"] = true, ["ب"] = true, ["ت"] = true, ["ث"] = true, ["ج"] = true, ["ح"] = true, ["خ"] = true, ["د"] = true, ["ذ"] = true, ["ر"] = true, ["ز"] = true, ["س"] = true, ["ش"] = true, ["ص"] = true, ["ض"] = true, ["ط"] = true, ["ظ"] = true, ["ع"] = true, ["غ"] = true, ["ف"] = true, ["ق"] = true, ["ك"] = true, ["ل"] = true, ["م"] = true, ["ن"] = true, ["ه"] = true, ["و"] = true, ["ي"] = true, ["گ"] = true, ["چ"] = true, ["پ"] = true, ["ڭ"] = true, } radicals["Ethiopic"] = { ["ሀ"] = true, ["ለ"] = true, ["ሐ"] = true, ["መ"] = true, ["ሠ"] = true, ["ረ"] = true, ["ሰ"] = true, ["ሸ"] = true, ["ቀ"] = true, ["ቈ"] = true, ["በ"] = true, ["ቨ"] = true, ["ተ"] = true, ["ቸ"] = true, ["ኀ"] = true, ["ኈ"] = true, ["ነ"] = true, ["ኘ"] = true, ["አ"] = true, ["ከ"] = true, ["ኰ"] = true, ["ኸ"] = true, ["ዀ"] = true, ["ወ"] = true, ["ዐ"] = true, ["ዘ"] = true, ["ዠ"] = true, ["የ"] = true, ["ደ"] = true, ["ጀ"] = true, ["ገ"] = true, ["ጐ"] = true, ["ጘ"] = true, ["ጠ"] = true, ["ጨ"] = true, ["ጰ"] = true, ["ጸ"] = true, ["ፀ"] = true, ["ፈ"] = true, ["ፐ"] = true, } return radicals 2cg6bdi5xmbc4e4rwoqlff9owjp0ciu มอดูล:roots/data/ar 828 2359208 5775238 2026-10-14T08:41:36Z Octahedron80 267 สร้างหน้าด้วย "return { script = "Arab", separator = " ", min_radicals = 3, validation = "membership", shared_radicals = "Arabic", link_target = "appendix_subpage", headword = { translits = true, nolemmacat = true }, palette = "green", preview_root = "ك ت ب", }" 5775238 Scribunto text/plain return { script = "Arab", separator = " ", min_radicals = 3, validation = "membership", shared_radicals = "Arabic", link_target = "appendix_subpage", headword = { translits = true, nolemmacat = true }, palette = "green", preview_root = "ك ت ب", } g56r43ot95uf0cbq7q3cmpsetopsn5k มอดูล:roots/data/he 828 2359209 5775239 2026-10-14T08:42:27Z Octahedron80 267 สร้างหน้าด้วย "local medial = { ["א"] = true, ["ב"] = true, ["ג"] = true, ["ג׳"] = true, ["ד"] = true, ["ה"] = true, ["הּ"] = false, ["ו"] = true, ["ז"] = true, ["ז׳"] = true, ["ח"] = true, ["ט"] = true, ["י"] = true, ["כ"] = true, ["ל"] = true, ["מ"] = true, ["נ"] = true, ["ס"] = true, ["ע"] = true, ["פ"] = true, ["צ"] = true, ["צ׳"] = true, ["ק"] = true, ["ר"] = true, ["שׁ"] = true, ["שׂ"] = true, ["ת"]..." 5775239 Scribunto text/plain local medial = { ["א"] = true, ["ב"] = true, ["ג"] = true, ["ג׳"] = true, ["ד"] = true, ["ה"] = true, ["הּ"] = false, ["ו"] = true, ["ז"] = true, ["ז׳"] = true, ["ח"] = true, ["ט"] = true, ["י"] = true, ["כ"] = true, ["ל"] = true, ["מ"] = true, ["נ"] = true, ["ס"] = true, ["ע"] = true, ["פ"] = true, ["צ"] = true, ["צ׳"] = true, ["ק"] = true, ["ר"] = true, ["שׁ"] = true, ["שׂ"] = true, ["ת"] = true, } local final = { ["א"] = true, ["ב"] = true, ["ג"] = true, ["ג׳"] = true, ["ד"] = true, ["ה"] = true, ["הּ"] = true, ["ו"] = true, ["ז"] = true, ["ז׳"] = true, ["ח"] = true, ["ט"] = true, ["י"] = false, ["ך"] = true, ["ל"] = true, ["ם"] = true, ["ן"] = true, ["ס"] = true, ["ע"] = true, ["ף"] = true, ["ץ"] = true, ["ץ׳"] = true, ["ק"] = true, ["ר"] = true, ["שׁ"] = true, ["שׂ"] = true, ["ת"] = true, } return { script = "Hebr", separator = "־", min_radicals = 2, max_radicals = 6, validation = "positional", radicals = { initial = medial, medial = medial, final = final, }, romanization = { { "־", "-" }, { "א", "ʾ" }, { "ב", "b" }, { "ג׳", "j" }, { "ג", "g" }, { "ד", "d" }, { "ה", "h" }, { "ו", "w" }, { "ז׳", "zh" }, { "ז", "z" }, { "ח", "kh" }, { "ט", "ṭ" }, { "י", "y" }, { "[כך]", "k" }, { "ל", "l" }, { "[מם]", "m" }, { "[נן]", "n" }, { "ס", "s" }, { "ע", "ʿ" }, { "[פף]", "p" }, { "[צץ]׳", "ch" }, { "[ץצ]", "ts" }, { "ק", "q" }, { "ר", "r" }, { "שׁ", "sh" }, { "שׂ", "s" }, { "ת", "t" }, }, link_target = "bare", headword = { translits = true, noposcat = false }, palette = "grey", preview_root = "כ־ת־ב", } j8vz3jm3ranqvhgq3ybevoe0pyavw4d سمكة 0 2359210 5775248 2026-10-14T08:54:51Z Octahedron80 267 นำเข้าจาก enwikt เก็บกวาด 5775248 wikitext text/x-wiki == ภาษาอาหรับ == {{ar-rootbox|س م ك}} === รากศัพท์ === {{ety|ar|nl=1}} === การออกเสียง === {{ar-pr|سَمَكة}} === คำนาม === {{ar-sing-noun|سَمَكَة|pl=أَسْمَاك,سِمَاك,سُمُوك}} # {{inflection of|ar|سَمَك||sgl}}: [[ปลา]] (ตัวเดียว) #: {{uxa|ar|بِالْمَتْنَزِهِ، رَأَيْتُ سَمَكَةً جَمِيلَةً تَسْبَحُ فِي الْبُرْكَةِ.|t=ในสวนสาธารณะ ฉันเห็นปลาสวยงามตัวหนึ่งกำลังว่ายน้ำอยู่ในสระ}} ==== การผันรูป ==== {{ar-decl-sing-noun|سَمَكَة|coll=سَمَك|pl=أَسْمَاك|pl2=سِمَاك|pl3=سُمُوك}} == ภาษาอาหรับแบบลิแวนต์ใต้ == {{ajp-root|س م ك}} === รากศัพท์ === {{ety|ajp|:inh|ar:سَمَكَة|text=+|tree=1}} === การออกเสียง === * {{ajp-IPA|samake<p:ˈsa.ma.ke>|samaka<p:ˈsa.ma.ka>}} * {{audio|ajp|LL-Q55633582 (ajp)-Muhammad (AdrianAbdulBaha)-سمكة.wav|a=Ramallah}} === คำนาม === {{ajp-sing-noun|tr=samake|pauc=سمكات|pauctr=samakāt}} # {{inflection of|ajp|سمك|tr=samak||sgl}}: [[ปลา]] (ตัวเดียว) t0q7q7itfaxqgaaruv32c0zpb0drcdz أسماك 0 2359211 5775249 2026-10-14T08:55:31Z Octahedron80 267 นำเข้าจาก enwikt เก็บกวาด 5775249 wikitext text/x-wiki == ภาษาอาหรับ == === รากศัพท์ === {{ety|ar|nl=1}} === การออกเสียง === {{ar-pr|أَسْمَاك}} === คำนาม === {{ar-head|nounf|أَسْمَاك|f-p}} # {{plural of|ar|سَمَك}} nmpsmp3nv1wa9ec3rxgovyezoh8cjup سماك 0 2359212 5775250 2026-10-14T08:56:13Z Octahedron80 267 นำเข้าจาก enwikt เก็บกวาด 5775250 wikitext text/x-wiki == ภาษาอาหรับ == === รากศัพท์ 1 === {{ar-occupational noun}} ของ {{m|ar|سَمَك|t=ปลา}} ==== คำนาม ==== {{ar-noun|سَمَّاك|m|pl=+}} # [[คนขายปลา]] ===== การผันรูป ===== {{ar-decl-noun|سَمَّاك|pl=سَمَّاكُون}} === รากศัพท์ 2 === ==== คำนาม ==== {{ar-head|nounf|سِمَاك|p}} # {{inflection of|ar|سَمَك||plural of variety|t=ปลา}} === รากศัพท์ 3 === {{ety|ar|:bor|gkm:σύμμαχος|text=+|tree=1}} ==== คำนาม ==== {{ar-noun|سِمَّاك|m|pl=سَمَامِكَة}} # {{tlb|ar|อียิปต์|พ้นสมัย|เฉพาะคริสต์ศตวรรษที่ 8}} {{n-g|เจ้าหน้าที่ระดับล่างในระดับมณฑลที่ช่วยงานจัดเก็บภาษี}} ===== การผันรูป ===== {{ar-decl-noun|سِمَّاك|pl=سَمَامِكَة}} ==== อ้างอิง ==== * {{R:ar:Garosi:2022a|page=413, 431}} * {{R:ar:Younes:2018|page=23}} {{C|ar|อาชีพ|การเก็บภาษี}} 94battyljq9dge81by4bz0ze6nuafyk 5775252 5775250 2026-10-14T09:01:49Z Octahedron80 267 /* คำนาม */ 5775252 wikitext text/x-wiki == ภาษาอาหรับ == === รากศัพท์ 1 === {{ar-occupational noun}} ของ {{m|ar|سَمَك|t=ปลา}} ==== คำนาม ==== {{ar-noun|سَمَّاك|m|pl=+}} # [[คนขายปลา]] ===== การผันรูป ===== {{ar-decl-noun|سَمَّاك|pl=سَمَّاكُون}} === รากศัพท์ 2 === ==== คำนาม ==== {{ar-head|nounf|سِمَاك|p}} # {{inflection of|ar|سَمَك||พหูพจน์แสดงความหลากหลาย|t=ปลา}} === รากศัพท์ 3 === {{ety|ar|:bor|gkm:σύμμαχος|text=+|tree=1}} ==== คำนาม ==== {{ar-noun|سِمَّاك|m|pl=سَمَامِكَة}} # {{tlb|ar|อียิปต์|พ้นสมัย|เฉพาะคริสต์ศตวรรษที่ 8}} {{n-g|เจ้าหน้าที่ระดับล่างในระดับมณฑลที่ช่วยงานจัดเก็บภาษี}} ===== การผันรูป ===== {{ar-decl-noun|سِمَّاك|pl=سَمَامِكَة}} ==== อ้างอิง ==== * {{R:ar:Garosi:2022a|page=413, 431}} * {{R:ar:Younes:2018|page=23}} {{C|ar|อาชีพ|การเก็บภาษี}} e1fbm0v66110krjfkvqqcgmglv5vbc4 มอดูล:Armi-translit 828 2359213 5775253 2026-10-14T09:12:23Z Octahedron80 267 สร้างหน้าด้วย "local export = {} local tt = { ["𐡀"] = "อ", ["𐡁"] = "บ", ["𐡂"] = "ค", ["𐡃"] = "ด", ["𐡄"] = "ฮ", ["𐡅"] = "ว", ["𐡆"] = "ซ", ["𐡇"] = "ฮฺ", ["𐡈"] = "ฏ", ["𐡉"] = "ย", ["𐡊"] = "ก", ["𐡋"] = "ล", ["𐡌"] = "ม", ["𐡍"] = "น", ["𐡎"] = "ส", ["𐡏"] = "อฺ", ["𐡐"] = "พ", ["𐡑"] = "ศ", ["𐡒"] = "ก..." 5775253 Scribunto text/plain local export = {} local tt = { ["𐡀"] = "อ", ["𐡁"] = "บ", ["𐡂"] = "ค", ["𐡃"] = "ด", ["𐡄"] = "ฮ", ["𐡅"] = "ว", ["𐡆"] = "ซ", ["𐡇"] = "ฮฺ", ["𐡈"] = "ฏ", ["𐡉"] = "ย", ["𐡊"] = "ก", ["𐡋"] = "ล", ["𐡌"] = "ม", ["𐡍"] = "น", ["𐡎"] = "ส", ["𐡏"] = "อฺ", ["𐡐"] = "พ", ["𐡑"] = "ศ", ["𐡒"] = "กฺ", ["𐡓"] = "ร", ["𐡔"] = "ช", ["𐡕"] = "ต", ["𐡗"] = "|", ["𐡘"] = "1", ["𐡙"] = "2", ["𐡚"] = "3", ["𐡛"] = "[10]", ["𐡜"] = "[20]", ["𐡝"] = "[100]", ["𐡞"] = "[1000]", ["𐡟"] = "[10000]", } function export.tr(text, lang, sc) -- Transliterate characters text = mw.ustring.gsub(text, '.', tt) return text end return export jufam6uy13394ozt1ksn46cyhksdrqk 5775255 5775253 2026-10-14T09:19:27Z Octahedron80 267 5775255 Scribunto text/plain local export = {} local tt = { ["𐡀"] = "อ", ["𐡁"] = "บ", ["𐡂"] = "ค", ["𐡃"] = "ด", ["𐡄"] = "ฮ", ["𐡅"] = "ว", ["𐡆"] = "ซ", ["𐡇"] = "ฮฺ", ["𐡈"] = "ฏ", ["𐡉"] = "ย", ["𐡊"] = "ก", ["𐡋"] = "ล", ["𐡌"] = "ม", ["𐡍"] = "น", ["𐡎"] = "ส", ["𐡏"] = "อฺ", ["𐡐"] = "พ", ["𐡑"] = "ศ", ["𐡒"] = "กฺ", ["𐡓"] = "ร", ["𐡔"] = "ช", ["𐡕"] = "ต", ["𐡗"] = "|", ["𐡘"] = "[1]", ["𐡙"] = "[2]", ["𐡚"] = "[3]", ["𐡛"] = "[10]", ["𐡜"] = "[20]", ["𐡝"] = "[100]", ["𐡞"] = "[1000]", ["𐡟"] = "[10000]", } function export.tr(text, lang, sc) -- Transliterate characters text = mw.ustring.gsub(text, '.', tt) return text end return export mafa5pa0qew2kms8ayvd35xl67v3eer มอดูล:Hebr-Arab-translit 828 2359214 5775258 2026-10-14T09:36:14Z Octahedron80 267 สร้างหน้าด้วย "local export = {} local gsub = mw.ustring.gsub local match = mw.ustring.match local letters = "קראטוןםפףךלחיעכגדשזסבהנמצתץ" local dagesh = "ּ" local diacritics = "ְֱֲֲֳִֵֶַָָֻּ" local tt = { ['ב']='ب', ['ג']='ج',['דּ']='د',['ד']='ذ',['ה']='ه',['ו']='و',['ז']='ز', ['ח']='ح',['ט']='ط',['י']='ي', ['כּ']='ك', -- intital form ['כ']='ك', -- intial form ['ךּ']='ك', -- final form,..." 5775258 Scribunto text/plain local export = {} local gsub = mw.ustring.gsub local match = mw.ustring.match local letters = "קראטוןםפףךלחיעכגדשזסבהנמצתץ" local dagesh = "ּ" local diacritics = "ְֱֲֲֳִֵֶַָָֻּ" local tt = { ['ב']='ب', ['ג']='ج',['דּ']='د',['ד']='ذ',['ה']='ه',['ו']='و',['ז']='ز', ['ח']='ح',['ט']='ط',['י']='ي', ['כּ']='ك', -- intital form ['כ']='ك', -- intial form ['ךּ']='ك', -- final form, with dagesh = strong k sound ['ך']='ك', -- final form ['ל']='ل',['מ']='م',['ם']='م',['נ']='ن',['ן']='ن', ['ס']='س', -- no direct equivalent ['ע']='ع',['פ']='ف',['ף']='ف',['צ']='ص',['ץ']='ص',['ק']='ق',['ר']='ر',['שׁ']='ش',['שׂ']='س',['ש']='ش', ['ת']='ث', ['א']='أ', ['ַ']='َ',-- Patach/fatah ['ִ']='ِ', -- Hiriq/Damma ['ָ']='ٰ', -- Qamatz/Khanjariyah ['ְ']='ْ',-- Shva/Sukun ['ֹ']='و'-- Holam } function export.tr(text, lang, sc) -- fixes the double diacritic issues text = gsub(text, "פ" .. '([' .. diacritics .. '])' .. dagesh, "ف%1") text = mw.ustring.gsub(text, '([\214\176\214\177\214\178\214\179\214\180\214\181\214\182\214\183\214\184\214\187])([\214\185\214\188\215\129\215\130])', "%2%1") text = mw.ustring.gsub(text, '.[\214\185\214\188\215\129\215\130]', tt) -- text = gsub(text, "תּ","ت") text = gsub(text, "וֹ","و") text = gsub(text, "וּ","ُو") text = mw.ustring.gsub(text, '.', tt) return text end return export qgn9nn2qobggila3woj0kt9gnt2mqlx มอดูล:Chrs-translit 828 2359215 5775293 2026-10-14T10:23:48Z Octahedron80 267 สร้างหน้าด้วย "local export = {} local tt = { ["𐾰"] = "อ", -- aleph ["𐾱"] = "อ", -- small aleph ["𐾲"] = "บ", -- beth ["𐾳"] = "ค", -- gimel ["𐾴"] = "ด", -- daleth ["𐾵"] = "ฮ", -- he ["𐾶"] = "ว", -- waw ["𐾷"] = "ว", -- curled waw ["𐾸"] = "ซ", -- zayin ["𐾹"] = "ฮฺ", -- heth ["𐾺"] = "ย", -- yodh ["𐾻"] = "ก", -- kaph ["𐾼"] = "ล"..." 5775293 Scribunto text/plain local export = {} local tt = { ["𐾰"] = "อ", -- aleph ["𐾱"] = "อ", -- small aleph ["𐾲"] = "บ", -- beth ["𐾳"] = "ค", -- gimel ["𐾴"] = "ด", -- daleth ["𐾵"] = "ฮ", -- he ["𐾶"] = "ว", -- waw ["𐾷"] = "ว", -- curled waw ["𐾸"] = "ซ", -- zayin ["𐾹"] = "ฮฺ", -- heth ["𐾺"] = "ย", -- yodh ["𐾻"] = "ก", -- kaph ["𐾼"] = "ล", -- lamedh ["𐾽"] = "ม", -- mem ["𐾾"] = "น", -- nun ["𐾿"] = "ส", -- samekh ["𐿀"] = "อฺ", -- ayin ["𐿁"] = "พ", -- pe ["𐿂"] = "ร", -- resh ["𐿃"] = "ช", -- shin ["𐿄"] = "ต", -- taw ["𐿅"] = "[1]", -- one ["𐿆"] = "[2]", -- two ["𐿇"] = "[3]", -- three ["𐿈"] = "[4]", -- four ["𐿉"] = "[10]", -- ten ["𐿊"] = "[20]", -- twenty ["𐿋"] = "[100]", -- one hundred } function export.tr(text, lang, sc) -- If the script is not Chrs, do not transliterate if sc ~= "Chrs" then return end -- Transliterate characters text = mw.ustring.gsub(text, '.', tt) return text end return export 0xmwi3pmmpp3emkt5t5t0mcx27dmcg6