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'''. — 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 .. " " .. 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 (' → '),
-- as they contain #, which causes the numeric character reference to be
-- misparsed (wa'a → wa'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 = "]" .. link.display .. "["
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, " " .. 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 <
-- and > 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),
["<"] = "<",
[">"] = ">",
}
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%;\"> / </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>` → `displayed text`
* `<nowiki>[[page and displayed text]]</nowiki>` → `page and displayed text`
* `<nowiki>[[Category:English lemmas|WORD]]</nowiki>` → ''(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 .. " § " .. 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 (' → '),
-- as they contain #, which causes the numeric character reference to be
-- misparsed (wa'a → wa'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 = "]" .. link.display .. "["
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, " " .. 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 <
-- and > 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),
["<"] = "<",
[">"] = ">",
}
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%;\"> / </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>` → `displayed text`
* `<nowiki>[[page and displayed text]]</nowiki>` → `page and displayed text`
* `<nowiki>[[Category:English lemmas|WORD]]</nowiki>` → ''(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 .. " § " .. 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 > 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(">", ""):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(">", ""):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 `&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 {" +‎ "} 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 " +‎ " 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 {" +‎ "} 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 " +‎ " 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>: "
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;> ' .. 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%" | เอกพจน์ ''([[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 .. " — " .. 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 .. " — " .. 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
`((кожу́х<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
`<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, ":", chunks_len) -- :
)
return concat(chunks, ":"), 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, ":"), 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, ":"), 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("{{" .. concat(output, "|") .. "}}") -- {{ | }}
)
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("{{{" .. concat({name, default}, "|") .. "}}}") -- {{{ | }}}
)
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("<" .. tag .. ">")
)
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 "&#91;" to be treated equivalently, with either
one closed by either "]" or "&#93;". To do this, first replace "&#91;" and "&#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> การผันรูปของ {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> รูปประสมของ {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 .. " " .. 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 = "‎ <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 == "—" 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