Wiktionary
viwiktionary
https://vi.wiktionary.org/wiki/Wiktionary:Trang_Ch%C3%ADnh
MediaWiki 1.48.0-wmf.1
case-sensitive
Phương tiện
Đặc biệt
Thảo luận
Thành viên
Thảo luận Thành viên
Wiktionary
Thảo luận Wiktionary
Tập tin
Thảo luận Tập tin
MediaWiki
Thảo luận MediaWiki
Bản mẫu
Thảo luận Bản mẫu
Trợ giúp
Thảo luận Trợ giúp
Thể loại
Thảo luận Thể loại
Phụ lục
Thảo luận Phụ lục
TimedText
TimedText talk
Mô đun
Thảo luận Mô đun
Sự kiện
Thảo luận Sự kiện
ngôn ngữ
0
1021
2573905
2572600
2026-10-27T15:45:59Z
Hiyuune
50834
(By [[meta:Indic-TechCom/Tools|FindAndReplace]])
2573905
wikitext
text/x-wiki
=={{langname|vi}}==
{{wp}}
==={{section|etym}}===
{{vi-etym-sino|言||語}}. So sánh với {{cog|zh|言語}}, {{ncog|zh|語言|tr=ngữ ngôn}}, {{cog|ja|言語|tr=gengo}} và {{cog|ko|언어}}.
==={{section|pron}}===
{{vi-pron}}
==={{section|n}}===
{{vi-noun}}
# [[hệ thống|Hệ thống]] các [[âm thanh]], các [[từ ngữ]] và các [[quy tắc]] [[kết hợp]] chúng, làm [[phương tiện]] [[giao tiếp]] [[chung]] cho một [[cộng đồng]].
#: {{syn|vi|tiếng|ngữ ngôn}}
#: {{ux|vi|'''Ngôn ngữ''' là phương tiện giao tiếp quan trọng nhất.}}
#* {{quote-book|vi|author=Phan Khôi|year=1955|title=Việt ngữ nghiên cứu|text=Có một lần tôi muốn dịch chữ {{m|fr|langage}}, định dịch là “'''ngôn ngữ'''”, mà sợ nó trùng với '''ngôn ngữ''' là {{m|fr|langue}} theo sự quen dùng của ta, tôi bèn tra [[Pháp-Hoa]] tự điển thử xem. Thì ra họ dịch là “ngôn ngữ”. Tôi nhận thấy họ có lý lắm: Có dùng “ngữ ngôn” mà dịch ''langue'' thì mới té chữ “'''ngôn ngữ'''” ra để mà dịch ''langage''.|brackets=y}}
#* {{quote-web|vi|date=2021-07-03|title=Ngữ ngôn & Ngôn ngữ là gì? (Tâm lý học)|work=Lý tưởng|text=Ngữ ngôn là chung cho cả 1 dân tộc, một cộng đồng. Còn '''ngôn ngữ''' mang tính chất chủ thể rõ ràng.<br>Ngữ ngôn không bị mất đi bởi những thương tổn bệnh lý. Còn '''ngôn ngữ''' bị rối loạn hay bị mất do những tổn thương.}}
# Hệ thống [[kí hiệu]] làm [[phương tiện]] [[diễn đạt]], [[thông báo]].
#: {{ux|vi|'''Ngôn ngữ''' điện ảnh.}}
#: {{ux|vi|'''Ngôn ngữ''' hội hoạ.}}
# [[cách|Cách]], [[lối]] [[sử dụng]] ngôn ngữ có [[tính chất]] [[cá biệt]] [[riêng lẻ]].
#: {{ux|vi|'''Ngôn ngữ''' truyện Kiều.}}
===={{section|drv}}====
{{col3|vi|ngôn ngữ học|ngôn ngữ hình thể|ngôn ngữ lập trình|ngôn ngữ tự nhiên}}
===={{section|trans}}====
{{trans-top|hệ thống các âm thanh, các từ ngữ và các quy tắc kết hợp chúng, làm phương tiện giao tiếp chung cho một cộng đồng}}
* {{dịch|abq}}: {{t|abq|бызшва}}
* {{dịch|ab}}: {{t|ab|абызшәа}} {{qualifier|xác định}}, {{t|ab|бызшәа}} {{qualifier|bất định}}
* {{dịch|ace}}: {{t|ace|bahsa}}, {{t|ace|بَهْسَا}}
* {{dịch|aa}}: {{t|aa|af}}, {{t|aa|أَف}}
* {{dịch|af}}: {{t+|af|taal}}
* {{dịch|xag}}: {{t|xag|𐕌𐕒𐕡𐔵}}
* {{dịch|agx}}: {{t|agx|чӏал}}
* {{dịch|aho}}: {{t|aho|𑜁𑜪𑜨|tr=khäam}}
* {{dịch|ain}}: {{t|ain|イタㇰ|tr=itak}}
* {{dịch|ak}}: {{t|ak|kasa}}
* {{dịch|tw}}: {{t|tw|kasa}}
* {{dịch|akk}}: {{t|akk|𒅴|ts=lišānu}}
* {{dịch|akz}}: {{t|akz|naaɬiilka}}
* {{dịch|sq}}: {{t+|sq|gjuhë|f}}
*: Arbëresh: {{t|aae|gluha}}, {{t|aae|gjuha}}, {{t|aae|gjufa}}
*: Arvanitika: {{t|aat|gljuhë}}
* {{dịch|en}}: {{t+|en|language}}
* {{dịch|ang}}: {{t|ang|ġeþēode|n}}, {{t|ang|ġereord|n}}, {{t|ang|sprǣċ|f}}
* {{dịch|ae}}: {{t|ae|𐬵𐬌𐬰𐬎𐬎𐬁}}
* {{dịch|xbc}}: {{t|xbc|εζϐαγο}}
* {{dịch|ba}}: {{t|ba|тел}}
* {{dịch|eu}}: {{t+|eu|hizkuntza}}
* {{dịch|bn}}: {{t+|bn|ভাষা}}, {{t+|bn|জবান}}
* {{dịch|bg}}: {{t+|bg|ези́к|m}}
* {{dịch|ca}}: {{t+|ca|idioma|m}}, {{t+|ca|llengua|f}}
* {{dịch|ceb}}: {{t|ceb|pinulongan}}
* {{dịch|chg}}: {{t|chg|تیل}}, {{t|chg|زبان}}
* {{dịch|chr}}: {{t|chr|ᎦᏬᏂᎯᏍᏗ}}
* {{dịch|ny}}: {{t|ny|chinenero}}
* {{dịch|cic}}: {{t|cic|anompa}}
* {{dịch|cop}}: {{t|cop|ⲗⲁⲥ}}
* {{dịch|kw}}: {{t|kw|yeth}}
* {{dịch|he}}: {{t+|he|לָשׁוֹן|f|tr=lashón}}, {{t+|he|שָׂפָה|f|tr=safá}}
* {{dịch|ug}}: {{t+|ug|تىل}}
* {{dịch|gd}}: {{t|gd|cànan|m}}, {{t|gd|cainnt|f}}
* {{dịch|mt}}: {{t+|mt|lingwa}}, {{t|mt|lsien}}
* {{dịch|mnc}}: {{t|mnc|ᡤᡳᠰᡠᠨ}}
* {{dịch|mni}}: {{t|mni|ꯂꯣꯜ}}
* {{dịch|my}}: {{t+|my|ဘာသာစကား}}, {{t+|my|စကား}}, {{t+|my|ဘာသာ}}, {{t+|my|စာ}} {{qualifier|trong từ ghép}}
* {{dịch|ja}}: {{t+|ja|言語|tr=げんご, gengo}}, {{t+|ja|言葉|tr=ことば, kotoba}}
* {{dịch|pap}}: {{t|pap|idioma}}, {{t|pap|lenga}}
* {{dịch|qxq}}: {{t|qxq|ديل}}, {{t|qxq|dil}}
* {{dịch|qu}}:
*: Kichwa Ecuador: {{t+|qu|rimay}}, {{t|qu|shimi}}
*: Nam Quechua: {{t+|qu|rimay}}, {{t+|qu|simi}}
* {{dịch|rar}}: {{t|rar|reo}}
* {{dịch|rhg}}: {{t|rhg|zuban}}, {{t|rhg|𐴎𐴟𐴁𐴝𐴕}}
* {{dịch|rgn}}: {{t|rgn|lengva|f}}
* {{dịch|sm}}: {{t|sm|gagana}}
* {{dịch|sgs}}: {{t|sgs|kalba}}
* {{dịch|shn}}: {{t|shn|ၽႃႇသႃႇ}}
* {{dịch|es}}: {{t+|es|lengua|f}}, {{t+|es|idioma|m}}
* {{dịch|zh}}:
*: {{langname|gan}}: {{t|gan|語言}}
*: {{langname|dng}}: {{t|dng|йүян}}, {{t|dng|янйү}}, {{t|dng|йү}}, {{t|dng|хуа}}
*: {{langname|hak}}: {{t|hak|語言|tr=ngî-ngièn / ngî-ngiàn}}
*: {{langname|mnp}}: {{t|mnp|語言|tr=ngṳ̌-ngṳ̂ing}}
*: {{langname|cdo}}: {{t|cdo|語言|tr=ngṳ̄-ngiòng}}
*: {{langname|nan-hbl}}: {{t+|nan-hbl|語言|tr=gú-giân / gír-giân / gí-gân / gí-giân}}
*: {{langname|wuu}}: {{t|wuu|語言}}
*: {{langname|cmn}}: {{t+|cmn|語言|tr=yǔyán}}, {{t+|cmn|話|tr=huà}}
*: {{langname|yue}}: {{t|yue|語言|tr=jyu5 jin4}}, {{t|yue|話|tr=waa6-2}}
* {{dịch|tpw}}: {{t|tpw|nhe'enga}}
* {{dịch|tyv}}: {{t|tyv|дыл}}
* {{dịch|ude}}: {{t|ude|кэйэни}}
* {{dịch|udm}}: {{t|udm|кыл}}
*: Udmurt Beserman : {{t|udm|көл}}
* {{dịch|uga}}: {{t|uga|𐎍𐎌𐎐|ts=lašānu}}
* {{dịch|uk}}: {{t+|uk|мо́ва|f}}
* {{dịch|ulc}}: {{t|ulc|хэсэи}}
* {{dịch|ur}}: {{t+|ur|زَبان|f}}, {{t+|ur|زُبان|f}}, {{t+|ur|بھاشا|f}}, {{t|ur|بولی|f}}, {{t|ur|لِسان|f}}
* {{dịch|uru}}: {{t|uru|тил}}, {{t|uru|дил}}, {{t|uru|τιλ}}, {{t|uru|Ντίλ}}
* {{dịch|uz}}: {{t+|uz|til}}, {{t+|uz|zabon}}
* {{dịch|ve}}: {{t|ve|luambo}}
* {{dịch|vec}}: {{t|vec|łéngua|f}}
* {{dịch|vep}}: {{t|vep|kel’}}
* {{dịch|vo}}: {{t+|vo|pük}}
* {{dịch|vro}}: {{t|vro|kiil}}
*: Seto: {{t|vro|kiil}}
* {{dịch|vot}}: {{t|vot|čeeli}}
* Tiếng Yukaghir:
*: {{langname|ykg}}: {{t|ykg|аруу}}
*: {{langname|yux}}: {{t|yux|ажуу}}
* {{dịch|zza}}: {{t+|zza|zon}}, {{t|zza|zowan}}, {{t|zza|zewan}}, {{t+|zza|zıwan}}
* {{dịch|zu}}: {{t+|zu|ulimi|c11|c10}}, {{t|zu|izilimi}}
{{trans-bottom}}
==={{section|ref}}===
* {{R:FVDP}}
{{C|vi|Ngôn ngữ}}
ljn6bp1fva547m8e0scv7sqe948f29g
Bản mẫu:Dịch
10
1379
2573849
4505
2026-10-27T12:14:29Z
TheHighFighter2
42988
/* */
2573849
wikitext
text/x-wiki
#redirect [[Template:dịch]]
bo45bmijxt6rcyurej4r2w5xd6y0wmq
abjure
0
5961
2573852
2367667
2026-10-27T12:38:31Z
Hiyuune
50834
2573852
wikitext
text/x-wiki
{{also|abjuré}}
=={{langname|en}}==
==={{section|etym}}===
{{root|en|ine-pro|*h₂yew-}}
{{rfe|en}}
==={{section|pron}}===
* {{a|en|RP}}
** {{IPA|en|/əbˈd͡ʒʊə(ɹ)/|a=non-pour-poor}}
** {{IPA|en|/əbˈd͡ʒɔː(ɹ)/|a=non-pour-poor}}
* {{IPA|en|/æbˈd͡ʒʊ(ə)ɹ/|/əb-/|a=GA}}
* {{audio|en|en-us-abjure.ogg|a=GA}}
* {{audio|en|en-ca-abjure.ogg|a=CA}}
* {{rhymes|en|ʊə(ɹ)|s=2}}
* {{hyph|en|ab|jure}}
==={{section|v}}===
{{en-verb}} {{tlb|en|formal}}
# [[tuyên bố|Tuyên bố]] [[bỏ]], [[nguyện]] [[bỏ]].
#: {{ux|en|To '''abjure''' one's religion.|Bỏ đạo.}}
#: {{ux|en|To '''abjure''' one's rights.|Tuyên bố từ bỏ quyền lợi của mình.}}
# [[rút lui|Rút lui]] ý kiến, lời hứa.
#: {{ux|en|To '''abjure''' one's opinion.|Rút lui ý kiến.}}
# [[thề|Thề]] [[bỏ]] (đất nước) [[đi]] [[mãi mãi]].
===={{section|usage}}====
Không nên nhầm lẫn vói {{m|en|adjure}}.
===={{section|forms}}====
{{en-conj|old=1|stem=abjur}}
===={{section|drv}}====
{{col3|en
|abjuratory
|abjured<pos:adjective>
|abjurement
|abjurer
|abjure the realm
|abjuring<pos:adjective, noun>
}}
===={{section|rel}}====
* {{l|en|abjuration}}
* {{l|en|adjure}}
==={{section|ref}}===
* {{R:FVDP|db=ev}}
==={{section|further}}===
* {{pedia|lang=en|abjuration}}
* {{R:CDOE|page=3}}
* {{R:SOED5|page=5}}
==={{section|ana}}===
* {{anagrams|en|a=abejru|Juarbe}}
=={{langname|pt}}==
==={{section|v}}===
{{head|pt|Biến thể hình thái động từ}}
# {{pt-verb form of|abjurar}}
=={{langname|fr}}==
==={{section|pron}}===
* {{fr-IPA}}
* {{audio|fr|LL-Q150 (fra)-Lyokoï-abjure.wav|a=Lyon}}
==={{section|v}}===
{{head|fr|Biến thể hình thái động từ}}
# {{inflection of|fr|abjurer||ind//sub|pres|ở|1//3|s|;|impr|ở|2|s}}
=={{langname|ro}}==
==={{section|pron}}===
* {{IPA|ro|/abˈʒure/}}
* {{rhyme|ro|ure|s=3}}
* {{hyph|ro|ab|jú|re}}
==={{section|v}}===
{{head|ro|verb form}}
# {{verb form of|ro|abjura||sub|pres|ở|3|s//p}}
=={{langname|es}}==
==={{section|pron}}===
{{es-pr}}
==={{section|v}}===
{{head|es|Biến thể hình thái động từ}}
# {{es-verb form of|abjurar}}
a92xvycgwb0yl9rqldw1t0pnb0y9qw7
wino
0
58864
2573865
2539027
2026-10-27T13:00:17Z
Hiyuune
50834
2573865
wikitext
text/x-wiki
=={{langname|en}}==
==={{section|etym}} 1===
{{ety|en|:af|wine|-o|text=+|tree=1}}
===={{section|alt}}====
* {{alt|en|wineo|wine-o}}
===={{section|pron}}====
* {{IPA|en|/ˈwaɪnəʊ/}}
* {{audio|en|LL-Q1860 (eng)-Vealhurl-wino (wine).wav|a=Southern England}}
* {{audio|en|En-us-wino.oga|a=US}}
* {{rhymes|en|aɪnəʊ|s=2}}
===={{section|n}}====
{{en-noun|s|es}}
# {{lb|en|pejorative}} [[người|Người]] [[nghiện]] [[rượu vang]]; [[kẻ]] nghiện rượu vang ([[đặc biệt]] là rượu [[rẻ tiền]]).
#: {{syn|en|alcoholic|drunk#Danh từ|Kho từ vựng:drunkard}}
====={{section|drv}}=====
{{col4|en|wino wine}}
====={{section|rel}}=====
{{col|en
|vinous
|vinaceous
|wininess
|winy
}}
==={{section|etym}} 2===
{{ety|en|:af|W-boson|-ino|text=+|tree=1}}
===={{section|pron}}====
* {{IPA|en|/ˈwiːnəʊ/}}
* {{audio|en|LL-Q1860 (eng)-Vealhurl-wino (physics).wav|a=Southern England}}
* {{audio|en|En-us-wino2.oga|a=US}}
* {{rhymes|en|iːnəʊ|s=2}}
===={{section|n}}====
{{en-noun}}
# {{lb|en|physics}} [[hạt|Hạt]] [[cơ bản]] [[giả thuyết]] [[tương tự]] với [[W boson]] do [[siêu đối xứng]].
==={{section|ref}}===
* {{R:FVDP|db=ev}}
==={{section|ana}}===
* {{anagrams|en|a=inow|Iwon|OWIN}}
=={{langname|pl}}==
{{wp|pl:}}
==={{section|pron}}===
{{pl-pr|a=Pl-wino.ogg}}
==={{section|etym}} 1===
{{ety|pl|:inh|zlw-opl:wino<ety:inh<sla-pro:*vino>>|tree=+|text=*}}
===={{section|n}}====
{{pl-noun|n|dim=winko|aug=wińsko}}
# [[rượu|Rượu]].
====={{section|decl}}=====
{{pl-ndecl|<n>}}
====={{section|alt}}=====
* {{alt|pl|winno||Kujawy}}
====={{section|drv}}=====
{{col|pl|title=danh từ|winnica|winogrono}}
==={{section|etym}} 2===
===={{section|n}}====
{{head|pl|Biến thể hình thái danh từ|g=f}}
# {{infl of|pl|wina||voc|s}}
==={{section|further}}===
* {{R:pl:WSJP}}
* {{R:pl:PWN}}
=={{langname|dsb}}==
[[File:Red Wine Glas.jpg|100px|thumb|wino]]
==={{section|etym}}===
Từ {{inh|dsb|sla-pro|*vino}}, vay mượn từ {{der|dsb|la|vīnum}}, từ {{der|dsb|ine-pro|*wóyh₁nom}}.
==={{section|pron}}===
{{dsb-pr}}
==={{section|n}}===
{{dsb-noun|n}}
# [[rượu|Rượu]].
===={{section|decl}}====
{{dsb-decl-noun-24|win}}
=={{langname|sw}}==
[[Image:Tintenfass.jpg|thumb|wino]]
==={{section|pron}}===
* {{audio|sw|Sw-ke-wino.flac|a=Kenya}}
==={{section|n}}===
{{sw-noun|u|-}}
# [[mực|Mực]].
{{C|sw|Chất lỏng}}
aqfkpny60g1ycl428i51m5ijvb57g5x
2573934
2573865
2026-10-27T23:23:13Z
Hiyuune
50834
2573934
wikitext
text/x-wiki
=={{langname|en}}==
==={{section|etym}} 1===
{{ety|en|:af|wine|-o|text=+|tree=1}}
===={{section|alt}}====
* {{alt|en|wineo|wine-o}}
===={{section|pron}}====
* {{IPA|en|/ˈwaɪnəʊ/}}
* {{audio|en|LL-Q1860 (eng)-Vealhurl-wino (wine).wav|a=Southern England}}
* {{audio|en|En-us-wino.oga|a=US}}
* {{rhymes|en|aɪnəʊ|s=2}}
===={{section|n}}====
{{en-noun|s|es}}
# {{lb|en|pejorative}} [[người|Người]] [[nghiện]] [[rượu vang]]; [[kẻ]] nghiện rượu vang ([[đặc biệt]] là rượu [[rẻ tiền]]).
#: {{syn|en|alcoholic|drunk#Danh từ|Kho từ vựng:drunkard}}
====={{section|drv}}=====
{{col4|en|wino wine}}
====={{section|rel}}=====
{{col|en
|vinous
|vinaceous
|wininess
|winy
}}
==={{section|etym}} 2===
{{ety|en|:af|W-boson|-ino|text=+|tree=1}}
===={{section|pron}}====
* {{IPA|en|/ˈwiːnəʊ/}}
* {{audio|en|LL-Q1860 (eng)-Vealhurl-wino (physics).wav|a=Southern England}}
* {{audio|en|En-us-wino2.oga|a=US}}
* {{rhymes|en|iːnəʊ|s=2}}
===={{section|n}}====
{{en-noun}}
# {{lb|en|physics}} [[hạt|Hạt]] [[cơ bản]] [[giả thuyết]] [[tương tự]] với [[W boson]] do [[siêu đối xứng]].
==={{section|ref}}===
* {{R:FVDP|db=ev}}
==={{section|ana}}===
* {{anagrams|en|a=inow|Iwon|OWIN}}
=={{langname|pl}}==
{{wp|pl:}}
==={{section|pron}}===
{{pl-pr|a=Pl-wino.ogg}}
==={{section|etym}} 1===
{{ety|pl|:inh|zlw-opl:wino<ety:inh<sla-pro:*vino>>|tree=+|text=*}}
===={{section|n}}====
{{pl-noun|n|dim=winko|aug=wińsko}}
# [[rượu|Rượu]].
====={{section|decl}}=====
{{pl-ndecl|<n>}}
====={{section|alt}}=====
* {{alt|pl|winno||Kujawy}}
====={{section|drv}}=====
{{col|pl|title=danh từ|winnica|winogrono}}
==={{section|etym}} 2===
===={{section|n}}====
{{head|pl|Biến thể hình thái danh từ|g=f}}
# {{infl of|pl|wina||voc|s}}
==={{section|further}}===
* {{R:pl:WSJP}}
* {{R:pl:PWN}}
=={{langname|dsb}}==
[[File:Red Wine Glas.jpg|100px|thumb|wino]]
==={{section|etym}}===
Từ {{inh|dsb|sla-pro|*vino}}, vay mượn từ {{der|dsb|la|vīnum}}, từ {{der|dsb|ine-pro|*wóyh₁nom}}.
==={{section|pron}}===
{{dsb-pr}}
==={{section|n}}===
{{dsb-noun|n}}
# [[rượu vang|Rượu vang]].
===={{section|decl}}====
{{dsb-decl-noun-24|win}}
=={{langname|sw}}==
[[Image:Tintenfass.jpg|thumb|wino]]
==={{section|pron}}===
* {{audio|sw|Sw-ke-wino.flac|a=Kenya}}
==={{section|n}}===
{{sw-noun|u|-}}
# [[mực|Mực]].
{{C|sw|Chất lỏng}}
erjad1ibw3sle23037qznyiljsnpinn
signalize
0
60211
2573858
2423717
2026-10-27T12:42:41Z
Hiyuune
50834
2573858
wikitext
text/x-wiki
=={{langname|en}}==
==={{section|alt}}===
* {{alter|en|signalise||non-Oxford}}
==={{section|etym}}===
{{ety|en|:af|signal|-ize|text=+|tree=1}}
==={{section|pron}}===
* {{IPA|en|/ˈsɪɡnəlaɪz/}}
* {{audio|en|LL-Q1860 (eng)-Flame, not lame-signalize.wav|a=US}}
==={{section|v}}===
{{en-verb}} {{tlb|en|American spelling|Oxford British spelling}}
# {{lb|en|transitive|archaic}} Làm cho được [[chú ý]], làm [[nổi bật]]; đề [[cao]].
===={{section|drv}}====
{{col|en|unsignalized
|signalization}}
==={{section|ref}}===
* {{R:FVDP|db=ev}}
10wrtaou79tgsimo3pur5m5umekfk4q
màu mè
0
92799
2573949
2069539
2026-10-28T06:16:36Z
~2026-57478-84
62143
/* */
2573949
wikitext
text/x-wiki
{{-vie-}}
{{-etym-}}
{{vi-etym-redup|e2|màu}}.
{{-pron-}}
{{vie-pron}}
{{-paro-}}
:* [[màu mẽ]]
:* [[máu me]]
:* [[máu mê]]
{{-adj-}}
{{vi-adj|head=[[màu]] mè}}
# {{term|Id.}} . [[Có]] [[cái]] đẹp [[nhờ]] [[tô vẽ]] nhiều [[màu sắc]].
#: ''Trang trí rất '''màu mè'''.''
# {{term|Nói năng, cư xử}} Có [[tính chất]] [[hình thức]], [[khách sáo]], không [[chân thật]].
#: ''Ăn nói '''màu mè'''.''
#: ''Sống với nhau chân chất, không '''màu mè'''.''
{{-ref-}}
{{R:FVDP}}
[[Thể loại:Từ láy tiếng Việt]]
hb5kos1eyiz0ffdv533hdexqp4uavv3
ngụy quân
0
95264
2573851
2573846
2026-10-27T12:35:42Z
TheHighFighter2
42988
/* Dịch */
2573851
wikitext
text/x-wiki
=={{langname|vi}}==
==={{ĐM|alt}}===
* {{alt|vi|nguỵ quân}}
==={{ĐM|etym}}===
{{vi-etym-sino|偽軍}}.
==={{ĐM|pron}}===
{{vi-pron}}
==={{ĐM|noun}}===
{{vi-noun}}
# [[quân đội|Quân đội]] của [[ngụy quyền]].
#:{{syn|vi|ngụy binh}}
===={{ĐM|trans}}====
{{trans-top|Quân đội của ngụy quyền}}
* {{dịch|en}}: {{t+|en|puppet army}}
* {{dịch|nl}}: {{t|nl|marionettenleger|n}}
* {{dịch|ru}}: {{t|ru|марионе́точная а́рмия|f}}
* {{dịch|ja}}: {{t|ja|偽軍|tr=ぎぐん, gigun}}, {{t|ja|傀儡軍|tr=かいらいぐん, kairaigun}}
* {{dịch|fi}}: {{t|fi|nukkearmeija}}
* {{dịch|zh}}:
*: {{dịch|cmn}}: {{t+|cmn|偽軍|tr=wěijūn}}, {{t|cmn|傀儡軍|tr=kuǐlěijūn}}
{{trans-bottom}}
==={{ĐM|ref}}===
{{R:Tratu|nguỵ quân}}
13y9imbeyeouxgmlnfu81w7whu4wipv
abhorrence
0
95770
2573937
2271578
2026-10-28T01:14:00Z
Hiyuune
50834
/* Tiếng Anh */ (sử dụng [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]])
2573937
wikitext
text/x-wiki
=={{langname|en}}==
==={{ĐM|etym}}===
Từ {{suffix|en|abhorrent|ence}}.
==={{ĐM|pron}}===
* {{IPA|en|/əbˈhɒɹ.n̩(t)s/|a=UK}}
* {{IPA|en|/æbˈhɔɹ.n̩(t)s/|/æbˈhɑɹ.n̩(t)s/|a=US}}
* {{audio|en|en-us-abhorrence.ogg|a=US}}
==={{ĐM|n}}===
{{en-noun|~}}
# Sự [[ghê tởm]].
#: {{ux|en|To have an '''abhorrence''' of.|Sự ghét cay ghét đắng.}}
#: {{ux|en|To hold in '''abhorrence''' of.|Ghê tởm; ghét cay ghét đắng.}}
# Điều [[ghê tởm]]; [[cái]] bị [[ghét]] [[cay]] [[ghét]] đắng.
===={{ĐM|syn}}====
* {{l|en|abhorrition}}
===={{ĐM|rel}}====
{{col|en
|abhor
|abhorred
|abhorrency
|abhorrent
|abhorrently
|abhorrer
|abhorrible
|abhorring
}}
==={{ĐM|ref}}===
* {{R:FVDP|db=ev}}
70ri9o6irhrf9vi61s23x29wkguozsx
adsorber
0
105202
2573986
2321473
2026-10-28T09:21:00Z
Hiyuune
50834
/* Biến cách */ (sử dụng [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]])
2573986
wikitext
text/x-wiki
=={{langname|en}}==
==={{section|etym}}===
Từ {{suffix|en|adsorb|er|id2=danh từ tác nhân}}.
==={{section|n}}===
{{en-noun}}
# [[máy|Máy]] [[hút]] [[bám]].
===={{section|drv}}====
{{col|en|hemoadsorber}}
===={{section|desc}}====
* {{desc|pl|adsorber|bor=1}}
==={{section|see}}===
* {{l|en|absorber}}
==={{section|ana}}===
* {{anagrams|en|a=abdeorrs|boarders|readsorb|reboards}}
=={{langname|pl}}==
{{wp|lang=pl}}
==={{section|etym}}===
{{bor+|pl|en|adsorber}},<ref>{{R:pl:WSWO}}</ref> từ {{der|pl|la|[[ad-]] + [[sorbeō]]}}. {{surf|pl|adsorbować|-er}}. {{etydate|1923}}.{{ref|{{R:pl:NFJP}}}}
==={{section|pron}}===
{{pl-pr|a=LL-Q809 (pol)-Poemat-adsorber.wav}}
==={{section|n}}===
{{pl-noun|m-in}}
# [[máy|Máy]] [[hút]] [[bám]].
===={{section|decl}}====
{{pl-ndecl|<m.inan.-*>}}
===={{section|rel}}====
{{col|pl|title=tính từ|adsorbowalny|adsorpcyjny}}
{{col|pl|title=phó từ|adsorpcyjnie}}
{{col|pl|title=danh từ|adsorbat|adsorbent|adsorpcja|adsorpcyjność}}
{{pl-derived verbs|adsorbować/zaadsorbować,zadsorbować<l:obsolete>|impf_first=1}}
===={{section|see}}====
* {{l|pl|absorber}}
==={{section|ref}}===
{{reflist}}
==={{section|further}}===
* {{R:pl:PWN}}
=={{langname|fr}}==
==={{section|pron}}===
* {{fr-IPA|atsorber}}
* {{audio|fr|LL-Q150 (fra)-Lepticed7-adsorber.wav|a=Toulouse}}
* {{audio|fr|LL-Q150 (fra)-LoquaxFR-adsorber.wav|a=Vosges}}
* {{audio|fr|LL-Q150 (fra)-Lyokoï-adsorber.wav|a=Lyon}}
* {{audio|fr|LL-Q150 (fra)-Mecanautes-adsorber.wav|a=Pháp}}
* {{audio|fr|LL-Q150 (fra)-Poslovitch-adsorber.wav|a=Vosges}}
* {{audio|fr|LL-Q150 (fra)-WikiLucas00-adsorber.wav|a=Lyon}}
* {{audio|fr|LL-Q150 (fra)-Jérémy-Günther-Heinz Jähnick-adsorber.wav|a=Somain}}
==={{section|v}}===
{{fr-verb}}
# {{lb|fr|transitive|physical chemistry|physics}} [[hấp thụ|Hấp thụ]]; [[hút]] [[bám]].
===={{section|conjug}}====
{{fr-conj-auto|pron=atsorber}}
===={{section|drv}}====
* {{l|fr|adsorbeur}}
==={{section|ref}}===
* {{R:FVDP|db=fv}}
==={{section|further}}===
* {{R:fr:TLFi}}
=={{langname|es}}==
==={{section|pron}}===
{{es-pr}}
==={{section|v}}===
{{es-verb}}
# {{lb|es|transitive|physical chemistry|physics}} [[hút|Hút]] [[bám]].
===={{section|conjug}}====
{{es-conj}}
==={{section|further}}===
* {{R:es:DRAE}}
co3otptc48wy3lewkdmt39svrxmorn0
oestruation
0
122166
2573862
236210
2026-10-27T12:51:31Z
Hiyuune
50834
2573862
wikitext
text/x-wiki
=={{langname|en}}==
==={{ĐM|alt}}===
* {{alt|en|estruation}}
==={{ĐM|n}}===
{{en-noun|-|s}}
# {{lb|en|biology}} Sự động [[dục]].
#: {{syn|en|oestrus}}
==={{ĐM|ref}}===
* {{R:FVDP|db=ev}}
izqms5nzavjxm6hi20g6frn9bdlt04f
abasourdissement
0
130319
2573966
2301785
2026-10-28T07:19:00Z
Hiyuune
50834
/* Tiếng Pháp */ (sử dụng [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]])
2573966
wikitext
text/x-wiki
=={{langname|fr}}==
==={{section|etym}}===
Từ {{affix|fr|abasourdir|-ment<id:danh hóa>}}.
==={{section|pron}}===
* {{fr-IPA}}
* {{audio|fr|Fr-abasourdissement.ogg}}
* {{audio|fr|LL-Q150 (fra)-LoquaxFR-abasourdissement.wav|a=Vosges, Pháp}}
* {{audio|fr|LL-Q150 (fra)-Mecanautes-abasourdissement.wav|a=Pháp}}
==={{section|n}}===
{{fr-noun|m|-}}
# {{lb|fr|rare}} Sự làm đinh [[tai]] [[nhức]] óc.
# {{lb|fr|rare}} Sự làm [[choáng váng]], sự làm [[bàng hoàng]].
==={{section|ref}}===
* {{R:FVDP|db=fv}}
==={{section|further}}===
* {{R:fr:TLFi}}
nje2pqt5lwk96xzwncds7kdne7ktkua
chung thân
0
142200
2573930
2364626
2026-10-27T17:55:59Z
~2026-57406-24
62137
2573930
wikitext
text/x-wiki
{{-vie-}}
{{-info-}}
{{-etym-}}
{{vi-etym-sino|終身}}.
{{-pron-}}
{{vie-pron}}
{{-adj-}}
'''chung thân'''
# [[trọn|Trọn]] đời, ý [[nói]] [[Kiều]] muốn [[hỏi]] [[số phận]] của [[mình]] [[suốt]] đời [[ra sao]].
# ý [[nói]] đến chuyện đi [[tù]] hết cả [[cuộc đời]] của [[tù nhân]]
{{-ref-}}
{{R:FVDP}}
[[Thể loại:Tính từ tiếng Việt]]
sf4eeh12g54zjah6eg06c9n4ezb7f9u
máu me
0
142818
2573948
1316744
2026-10-28T06:15:49Z
~2026-57478-84
62143
2573948
wikitext
text/x-wiki
{{-vie-}}
{{-etym-}}
{{vi-etym-redup|e2|máu}}.
{{-pron-}}
{{vie-pron}}
{{-paro-}}
:* [[màu mè]]
:* [[màu mẽ]]
:* [[máu mê]]
{{-adj-}}
{{vi-adj|head=[[máu]] me|[[洫眉]], [[𧖱湄]]}}
# [[máu|Máu]] đổ [[ra]], [[giây]] nhiều [[chỗ]].
#: ''Bị thương nặng '''máu me''' đầy người.''
{{-ref-}}
{{R:FVDP}}
[[Thể loại:Từ láy tiếng Việt]]
807f4xr5846pl0pdk6tgz9sr87lnkw8
absorptance
0
175298
2573936
2449778
2026-10-28T01:12:33Z
Hiyuune
50834
2573936
wikitext
text/x-wiki
=={{langname|en}}==
==={{section|pron}}===
* {{IPA|en|/əbˈzɔːp.tn̩s/|a=RP}}
* {{IPA|en|/æbˈsɔɹp.tn̩s/<a:US>|/æbˈzɔɹp.tn̩s/|/əbˈsɔɹp.tn̩s/|/əbˈzɔɹp.tn̩s/|/æbˈsɔɹp.tn̩ts/|/æbˈzɔɹp.tn̩ts/|/əbˈsɔɹp.tn̩ts/|/əbˈzɔɹp.tn̩ts/}}
==={{section|n}}===
{{en-noun}}
# {{lb|en|optics|physics}} Độ [[hấp thu]].
==={{section|ref}}===
* {{R:FVDP|db=ev}}
nnxneca4cz347rl6ii8jlo9q5l3xs1b
Giê-hô-va
0
215311
2573870
2571564
2026-10-27T13:08:16Z
TheHighFighter2
42988
/* Dịch */ Chuyển đổi {{langname}} ---> {{dịch}}
2573870
wikitext
text/x-wiki
=={{langname|vi}}==
{{-info-}}
==={{ĐM|pron}}===
{{vi-pron}}
==={{ĐM|pr-noun}}===
{{vi-proper noun}}
# {{nhãn|vi|tôn giáo}} Tên riêng của [[Thiên Chúa]] trong [[Kinh Thánh]] [[tiếng Do Thái]].
===={{ĐM|trans}}====
{{trans-top|Tên riêng của Thiên Chúa trong Kinh Thánh tiếng Do Thái}}
* {{dịch|en}}: {{t+|en|Jehovah}}
{{trans-bottom}}
sy72cxya9qxodwmqtkgw2fs9v0vtstj
signalizing
0
228252
2573859
2167570
2026-10-27T12:48:37Z
Hiyuune
50834
2573859
wikitext
text/x-wiki
=={{langname|en}}==
==={{section|v}}===
{{head|en|Biến thể hình thái động từ}}
# {{infl of|en|signalize||ing-form}}.
ey34s2g0lbh7hybbe6p997u1sbvu2pd
signalized
0
228253
2573860
1915411
2026-10-27T12:49:15Z
Hiyuune
50834
2573860
wikitext
text/x-wiki
=={{langname|en}}==
==={{section|v}}===
{{head|en|Biến thể hình thái động từ}}
# {{infl of|en|signalize||ed-form}}
l2j79okp8frbbepb3uuvodnk9jkypa1
signalizes
0
228254
2573857
1915412
2026-10-27T12:41:09Z
Hiyuune
50834
2573857
wikitext
text/x-wiki
=={{langname|en}}==
==={{section|v}}===
{{head|en|Biến thể hình thái động từ}}
# {{infl of|en|signalize||s-verb-form}}
fffux9k3gjxemg6henrd0lj69yxu1ui
abjuring
0
228778
2573854
2278299
2026-10-27T12:39:39Z
Hiyuune
50834
/* Tiếng Anh */ (sử dụng [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]])
2573854
wikitext
text/x-wiki
=={{langname|en}}==
==={{section|n}}===
* {{IPA|en|/əbˈdʒʊɹɪŋ/|a=RP}}
* {{audio|en|En-ca-abjuring.ogg|a=Canada}}
==={{section|v}}===
{{head|en|Biến thể hình thái động từ}}
# {{infl of|en|abjure||ing-form}}.
26kqaeo1518t47hr1cd4whox5qvcghq
abjured
0
228779
2573856
2316289
2026-10-27T12:40:31Z
Hiyuune
50834
/* Tiếng Anh */ (sử dụng [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]])
2573856
wikitext
text/x-wiki
=={{langname|en}}==
==={{section|pron}}===
* {{IPA|en|/əbˈdʒʊəd/|a=RP}}
* {{audio|en|en-ca-abjured.ogg|a=Canada}}
==={{section|v}}===
{{head|en|Biến thể hình thái động từ}}
# {{infl of|en|abjure||ed-form}}
c5zwopp69sijp9e3iuo8ez8qje0myvt
abjures
0
228780
2573853
2297733
2026-10-27T12:39:16Z
Hiyuune
50834
/* Động từ */ (sử dụng [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]])
2573853
wikitext
text/x-wiki
{{also|abjurés}}
=={{langname|en}}==
==={{section|pron}}===
* {{IPA4|en|/əbˈdʒʊəz/|a=RP}}
==={{section|v}}===
{{head|en|Biến thể hình thái động từ}}
# {{infl of|en|abjure||s-verb-form}}
==={{section|ana}}===
* {{anagrams|en|a=abejrsu|Juarbes}}
=={{langname|pt}}==
==={{section|v}}===
{{head|pt|Biến thể hình thái động từ}}
# {{pt-verb form of|abjurar}}
=={{langname|fr}}==
==={{section|pron}}===
* {{fr-IPA}}
* {{audio|fr|LL-Q150 (fra)-Lyokoï-abjures.wav|a=Lyon, Pháp}}
==={{section|v}}===
{{head|fr|Biến thể hình thái động từ}}
# {{inflection of|fr|abjurer||ind//sub|pres|ở|2|s}}
=={{langname|es}}==
==={{section|v}}===
{{head|es|Biến thể hình thái động từ}}
# {{es-verb form of|abjurar}}
1u1dlgfs92bwle7j26e1bhyefqqw9ku
2573855
2573853
2026-10-27T12:39:50Z
Hiyuune
50834
/* Cách phát âm */ (sử dụng [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]])
2573855
wikitext
text/x-wiki
{{also|abjurés}}
=={{langname|en}}==
==={{section|pron}}===
* {{IPA|en|/əbˈdʒʊəz/|a=RP}}
==={{section|v}}===
{{head|en|Biến thể hình thái động từ}}
# {{infl of|en|abjure||s-verb-form}}
==={{section|ana}}===
* {{anagrams|en|a=abejrsu|Juarbes}}
=={{langname|pt}}==
==={{section|v}}===
{{head|pt|Biến thể hình thái động từ}}
# {{pt-verb form of|abjurar}}
=={{langname|fr}}==
==={{section|pron}}===
* {{fr-IPA}}
* {{audio|fr|LL-Q150 (fra)-Lyokoï-abjures.wav|a=Lyon, Pháp}}
==={{section|v}}===
{{head|fr|Biến thể hình thái động từ}}
# {{inflection of|fr|abjurer||ind//sub|pres|ở|2|s}}
=={{langname|es}}==
==={{section|v}}===
{{head|es|Biến thể hình thái động từ}}
# {{es-verb form of|abjurar}}
1qnq62urn7pw6wgubot3hn5wev6tvyu
augmenting
0
231085
2573939
2162783
2026-10-28T01:15:30Z
Hiyuune
50834
2573939
wikitext
text/x-wiki
=={{langname|en}}==
==={{section|v}}===
{{head|en|Biến thể hình thái động từ}}
# {{infl of|en|augment||ing-form}}.
ahbbb70ew2bk8pkdae6rgse3m0bfy0s
augmented
0
231086
2573938
2194489
2026-10-28T01:15:02Z
Hiyuune
50834
2573938
wikitext
text/x-wiki
=={{langname|en}}==
==={{section|pron}}===
* {{IPA|en|/ɔːɡˈmɛntɪd/|a=UK}}
* {{IPA|en|/ɔɡˈmɛntəd/|/ɑɡˈmɛntəd/<a:cot-caught>|a=US}}
* {{audio|en|LL-Q1860 (eng)-Vealhurl-augmented.wav|a=Southern England}}
==={{section|v}}===
{{head|en|Biến thể hình thái động từ}}
# {{infl of|en|augment||ed-form}}
0mrkgn28vtro1sbknrwgr3p9f915gsp
auksas
0
238760
2573942
2146773
2026-10-28T01:26:30Z
Hiyuune
50834
2573942
wikitext
text/x-wiki
=={{langname|lt}}==
{{chemical element box|lt}}
[[Image:GoldNuggetUSGOV.jpg|thumb|{{l|lt|aukso|Aukso}} {{l|lt|grynuolis}} - Quặng vàng]]
==={{section|etym}}===
Từ {{inh|lt|ine-pro|*h₂éh₂usom}}, {{m|ine-pro|*h₂ews-}}. Cùng gốc với {{cog|prg|ausis||vàng}}, {{cog|la|aurum||vàng}}, {{cog|xto|wäs||vàng}}, {{cog|txb|yasā||vàng}}.<ref>{{R:EDBIL|69-70|auksas}}</ref>
==={{section|pron}}===
{{lt-pr|áuksas}}
==={{section|n}}===
{{lt-noun|m|-|3<ref name="lkz479"></ref><ref>{{R:lt:DLKŽ1954|auksas}}</ref> ''hoặc'' 1<ref name="lkz479">“auksas” trong {{w|lang=lt|Jonas Kruopas}} [et al.]('''1968'''), ''Lietuvių Kalbos Žodynas'', tập. I. Vilnius: {{w|lang=lt|Mintis_(leidykla)|Mintis}}.</ref>|head=áuksas}}
# [[vàng|Vàng]] {{gloss|[[Au]]}}.
#: {{ux|lt|'''aukso''' [[žiedas]]|chiếc nhẫn '''vàng'''}}
===={{section|decl}}====
{{lt-noun-m-as-3|auks|áuks|n=s}}
{{lt-noun-m-as-1|auks|áuks|n=s}}
===={{section|drv}}====
{{col|lt|title=danh từ|auksaširdis<g:m>|auksaširdė<g:f>}}
{{col|lt|title=tính từ|auksinis}}
===={{section|rel}}====
{{col|lt|title=động từ|auksėti}}
==={{section|ref}}===
{{reflist}}
{{C|lt|Kim loại}}
3xjkllxwxbcl01nulsed5bwt1euwv19
biến thái
0
242803
2573873
2349212
2026-10-27T13:15:28Z
TheHighFighter2
42988
/* Dịch */ Chuyển đổi {{langname}} ---> {{dịch}}
2573873
wikitext
text/x-wiki
=={{langname|vi}}==
{{wp}}
[[File:Holometabolous_vs._Hemimetabolous.svg|thumb|right|Sơ đồ minh họa cho hai kiểu biến thái (hoàn toàn và không hoàn toàn) ở côn trùng]]
==={{ĐM|etym}}===
{{vi-etym-sino|變|biến đổi|態|hình dạng, trạng thái}}.
==={{ĐM|pron}}===
{{vi-pron}}
==={{ĐM|noun}}===
{{vi-noun}}
# [[hình thái|Hình thái]] đã [[biến đổi]] ít nhiều so với hình thái gốc.
#* {{quote-book|vi|year=2005|author=Vũ Tuyên Hoàng|title=Tản mạn đường dài|publisher=Nhà xuất bản Thanh niên|page=103|pageurl=https://www.google.com.vn/books/edition/T%E1%BA%A3n_m%E1%BA%A1n_%C4%91%C6%B0%E1%BB%9Dng_d%C3%A0i/V6hOOZ6hM5YC?hl=vi&gbpv=1&bsq=%22bi%E1%BA%BFn+th%C3%A1i%22&dq=%22bi%E1%BA%BFn+th%C3%A1i%22&printsec=frontcover|passage=Cái đĩa, cái khay, cái hộp, cái bàn dù là hình bầu dục cũng là '''biến thái''' của hình tròn.}}
# {{n-g|Trong sinh học}}:
## {{lb|vi|botany}} Hiện tượng các cơ quan [[sinh dưỡng]] ([[rễ]], [[chồi]], [[lá]]) thay đổi hình thái, [[cấu tạo]] để thực hiện những [[chức năng]] mới giúp [[sinh vật]] [[thích nghi]] với điều kiện môi trường.
##* '''2010''', Đỗ Thị Kiều An, “Phân loại và định danh cỏ dại”, trong ''[https://www.zun.vn/tai-lieu/bai-giang-co-dai-bien-phap-kiem-soat-do-thi-kieu-an-54029/ Bài giảng Cỏ dại & biện pháp kiểm soát]'', tr. 10:
##*: {{quote|vi|''Cỏ đa niên thân hành (bulb):'' nhóm này chiếm tỷ lệ rất ít. Củ của chúng là '''biến thái''' của thân, có cấu tạo giống củ hành, hình cầu, bên trong chứa đựng phôi mầm của một cây đầy đủ gồm rễ, thân, lá và hoa.}}
## {{lb|vi|zoology}} Sự biến đổi [[rõ rệt]] về [[hình dạng]] và cấu tạo của một số [[động vật]] qua từng [[giai đoạn]], từ [[ấu trùng]] tới lúc [[trưởng thành]].
##: {{ux|vi|'''Biến thái''' của sâu bọ.}}
# {{lb|vi|xúc phạm}} Người [[dâm dục]], [[đồi truỵ]], có hành vi [[tình dục]] [[lệch lạc]].
# {{lb|vi|xúc phạm}} Người [[LGBT]].
#* {{quote-newspaper|vi|url=https://anninhthudo.vn/tre-lgbt-duong-pho-bi-day-ra-duong-vi-chinh-nhung-ap-luc-cua-gia-dinh-post246571.antd|title=Trẻ LGBT đường phố: Bị đẩy ra đường vì chính những áp lực của gia đình|author=Trâm Anh|journal=An ninh thủ đô|date=02-08-2015|passage=Ông tìm mọi cách để em không thể ra ngoài “cặp kè với lũ bạn '''biến thái'''” ông đánh đập, cắt tóc, cắt hết quần áo…}}
#* {{quote-web|vi|title=Bạo lực lời nói gây nhiều hậu quả với học sinh LGBT|url=https://tiengchuong.chinhphu.vn/bao-luc-loi-noi-gay-nhieu-hau-qua-voi-hoc-sinh-lgbt-11316192.htm|site=Trang tin điện tử của Ủy ban Quốc gia phòng, chống AIDS, ma tuý, mại dâm|date=28-01-2017|passage="Thầy giáo nói em là “'''biến thái'''” trước cả lớp".}}
===={{ĐM|trans}}====
{{trans-top|Trong động vật học}}
* {{dịch|en}}: {{t+|en|metamorphosis}}
* {{dịch|ca}}: {{t+|ca|metamorfosi|f}}
* {{dịch|nl}}: {{t+|nl|metamorfose}}
* {{dịch|ru}}: {{t+|ru|метаморфо́за}}
* {{dịch|ja}}: {{t+|ja|変態|tr=へんたい, hentai}}
* {{dịch|fr}}: {{t+|fr|métamorphose|f}}
* {{dịch|eo}}: {{t|eo|metamorfozo}}
* {{dịch|cs}}: {{t+|cs|proměna|f}}
* {{dịch|es}}: {{t+|es|metamorfosis|f}}
* {{dịch|zh}}:
*: {{dịch|cmn}}: {{t+|cmn|變態|tr=biàntài}}
{{trans-bottom}}
{{trans-top|Người dâm dục, đồi truỵ}}
* {{dịch|en}}: {{t+|en|pervert}}
{{trans-bottom}}
==={{ĐM|verb}}===
{{vi-verb}}
# Biến đổi ít nhiều so với hình thái gốc.
#* {{quote-book|vi|year=2010|entry=Chim thần Mahakrút và vũ nữ Têpkâyno|author=Ủy ban nhân dân tỉnh Bạc Liêu|title=Từ điển địa chí Bạc Liêu|location=Hà Nội|publisher=Nhà xuất bản Chính trị quốc gia|page=172|pageurl=https://books.google.com.vn/books?id=GdKw75bQHZUC&pg=PA172&hl=vi#v=onepage&q&f=false|passage=Chim thần Mahakrút còn thể hiện lẫn lộn với vũ nữ Kâyno, '''biến thái''' ra những mô típ như mô típ đầu chim, thân người hai cánh tay đỡ mái chùa, mô típ đầu và thân người nhưng chân chim Krút ...}}
==={{ĐM|adj}}===
{{vi-adj}}
# Đã biến đổi ít nhiều so với hình thái gốc.
#: {{ux|vi|Bê tông '''biến thái'''.}}
# Có suy nghĩ hay hành động lệch lạc về mặt tình dục.
#: {{ux|vi|Cái thằng/con này '''biến thái''' quá đi!}}
==={{ĐM|ref}}===
* {{R:Tratu}}
* {{R:Hoàng Phê 2021|page=79}}
* {{Chú thích sách|author=Hội đồng quốc gia chỉ đạo biên soạn từ điển bách khoa Việt Nam|year=1995|title=Từ điển bách khoa Việt Nam|volume=1|page=222|pageurl=https://archive.org/details/tu-dien-bach-khoa-viet-nam-tap-1-a-d/TuDienBachKhoaVietNam-Tap1_%28A-%C4%90%29/page/222/mode/1up|location=Hà Nội|publisher=Trung tâm biên soạn từ điển bách khoa Việt Nam}}
eosw0grxm8jertjh42oa37rdlutms5g
anh hùng cứu mỹ nhân
0
243750
2573872
2212484
2026-10-27T13:12:56Z
TheHighFighter2
42988
/* Tiếng Việt */
2573872
wikitext
text/x-wiki
=={{langname|vi}}==
==={{ĐM|pron}}===
{{vi-pron}}
==={{ĐM|idiom}}===
{{vi-idiom|head=[[anh hùng]] [[cứu]] [[mỹ nhân]]}}
# Một người hùng giải cứu người đẹp gặp nạn.
===={{ĐM|trans}}====
{{trans-top|Người hùng giải cứu người đẹp gặp nạn}}
* {{dịch|zh}}:
*: {{dịch|cmn}}: {{t|cmn|英雄救美}}
{{trans-bottom}}
ttm9clabwnyj8km7cog2a1k7rafrrqd
MediaWiki:Gadget-start.js
8
245774
2573890
2317139
2026-10-27T14:07:04Z
TheHighFighter2
42988
2573890
javascript
text/javascript
$(function () {
var ns = mw.config.get("wgNamespaceNumber");
if (ns &&
!(ns === 2 && mw.config.get("wgPageName").match(/\/sandbox$|\/thử$/))) {
return;
}
var action = mw.config.get("wgAction");
if (!(action === "edit" || action === "submit") ||
!$(".mw-newarticletext, .mw-newarticletextanon").length) {
return;
}
String.prototype.wiktviCapitalize = function () {
return this[0].toUpperCase() + this.substr(1);
};
var pageName = mw.config.get("wgPageName").replace(/_/g, " ");
if (ns) pageName = pageName.replace(/.*\//, "");
var userLang = mw.config.get("wgUserLanguage");
var chosenOptions = {
width: "20em",
disable_search_threshold: 20,
placeholder_text_single: "Chọn một",
placeholder_text_multiple: "Chọn nhiều",
no_results_text: "Không tìm thấy",
enable_split_word_search: false,
search_contains: true };
var langsByCode = {};
var steps = {
lang: {
id: "lang",
required: true,
name: "ngôn ngữ",
question_fmt: "“{{PAGENAME}}” là tiếng gì?",
choices: [
// Languages with 100 or more entries are loaded immediately.
// The rest come in through ext.gadget.headers.
["vi", "tiếng Việt"],
["en", "tiếng Anh"],
["nl", "tiếng Hà Lan"],
["io", "tiếng Ido"],
["lt", "tiếng Litva"],
["no", "tiếng Na Uy"],
["ru", "tiếng Nga"],
["fr", "tiếng Pháp"],
["es", "tiếng Tây Ban Nha"],
["zh", "tiếng Trung Quốc"] ],
output: function (values) {
return "=={{langname|" + values.lang + "}}==\n\n";
} },
pron: {
id: "pron",
name: "cách phát âm",
question_fmt: "“{{PAGENAME}}” đọc như thế nào?",
visible: function (values) {
return false;
},
output: function (values) {
if (values.lang === "vi") {
return "==={{ĐM|pron}}===\n{{vi-pron}}\n\n";
}
return "";
} },
pos: {
id: "pos",
required: true,
name: "từ loại",
question_fmt: "“{{PAGENAME}}” là loại từ nào?",
visible: function (values) {
return values.lang;
},
choices: [
["noun", "danh từ"],
["verb", "động từ"],
["adj", "tính từ"],
["adv", "phó từ"],
["prep", "giới từ"],
["pronoun", "đại từ"],
["interj", "thán từ"],
["conj", "liên từ"],
["prefix", "tiền tố"],
["suffix", "hậu tố"],
["pr-noun", "danh từ riêng"],
["phrase", "cụm từ"],
["idiom", "thành ngữ"],
["proverb", "tục ngữ"] ],
output: function (values) {
// Split values.pos based on hyphen
var posParts = values.pos.split("/");
// Extract the part without gender
var posWithoutGender = posParts[0];
// Extract the gender part
var gender = posParts.length > 1 ? posParts[1] : "";
// Capitalize the first letter of the extracted part
var capitalizedPos = posWithoutGender.wiktviCapitalize();
var outputString = "==={{ĐM|" + values.pos + "}}===\n";
return outputString;
}
},
dfn: {
id: "dfn",
required: true,
name: "ngữ nghĩa",
question_fmt: "Nhập ngữ nghĩa của từ bằng văn bản thuần túy",
multiple: true,
ordered: true,
visible: function (values) {
return values.lang && values.pos;
},
example: function (values) {
var examples = {
noun: "Bản vẽ hình thể của một khu vực.",
verb: "Đùa vui một cách tinh nghịch.",
adj: "Rất hoạt bát, nhẹ nhàng và mau chóng.",
adv: "Luôn luôn đều đặn, không gián đoạn.",
prep: "Thuộc vị trí, địa điểm nào.",
pronoun: "Ngôi thứ hai khi mình nói với anh ruột hay anh họ.",
interj: "Tiếng thốt lên để than thở hoặc ngạc nhiên.",
place: "Thủ đô Vương quốc Anh.",
proverb: "Thế hệ con phải chịu hậu quả của các hành động của thế hệ cha." };
var transExamples = {
noun: "Cá thật mập.",
verb: "Lè lưỡi.",
adj: "To lớn.",
adv: "Thường xuyên.",
prep: "Ở, tại.",
pronoun: "Bà ấy, cô ấy, chị ấy.",
interj: "Ôi trời ơi!",
prefix: "Trước về thời gian.",
suffix: "Chỉ đến môn học.",
place: "Luân Đôn.",
proverb: "Đời cha ăn mặn đời con khát nước." };
var pos = values.pos;
return values.lang === "vi" ? examples[pos] : transExamples[pos];
},
getOutput: function (values, callback) {
var text = $.map(values.dfn, function (dfn, i) {
dfn = $.trim(dfn);
if (!dfn) return undefined;
// Capitalize the first word.
if (dfn[0] !== dfn[0].toUpperCase()) {
dfn = dfn.wiktviCapitalize();
}
// Punctuate the definition.
if (!dfn.match(/[.…?!)\]]$/)) dfn += ".";
return "# " + dfn;
}).join("\n") + "\n\n";
var didTryToLoad = function () {
$.wiktviLinkify(text, callback);
};
mw.loader.using("ext.gadget.linkify", didTryToLoad, didTryToLoad);
} },
syn: {
id: "syn",
name: "từ đồng nghĩa",
multiple: true,
narrow: true,
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
if (!values.syn.join("").length) return "";
return "===={{ĐM|syn}}====\n" + $.map(values.syn, function (syn, i) {
return syn ? "* {{l|" + values.lang + "|" + syn + "}}" : undefined;
}).join("\n") + "\n\n";
} },
ant: {
id: "ant",
name: "từ trái nghĩa",
multiple: true,
narrow: true,
visible: function (values) {
return values.lang && values.pos;
},
output: function (values) {
if (!values.ant.join("").length) return "";
return "===={{ĐM|ant}}====\n" + $.map(values.ant, function (ant, i) {
return ant ? "* {{l|" + values.lang + "|" + ant + "}}" : undefined;
}).join("\n") + "\n\n";
} },
trans: {
id: "trans",
name: "bản dịch",
multiple: true,
narrow: true,
keyName: "ngôn ngữ",
visible: function (values) {
return values.lang === "vi" && values.pos;
},
disabledKeys: {
"mul": true,
"vi": true },
output: function (values) {
var rows = [];
$.each(values.trans, function (lang, words) {
if (words) rows.push([lang, words]);
});
if (!rows.length) return "";
rows.sort(function (a, b) {
var aLang = langsByCode[a[0]] || [];
var bLang = langsByCode[b[0]] || [];
return (aLang[2] || "").localeCompare(bLang[2] || "", "vi");
});
return "===={{ĐM|trans}}====\n{{trans-top}}\n" + $.map(rows, function (row, i) {
var words = row[1].replace(/(\s*[,;()]+\s*)/g, "}}$1{{t|" + row[0] + "|");
return "* {{dịch|" + row[0] + "}}: {{t|" + row[0] + "|" + words + "}}";
}).join("\n") + "\n{{trans-bottom}}\n\n";
} },
infl: {
id: "infl",
name: "biến tố",
multiple: true,
visible: function (values) {
return (values.lang === "en" && ["noun", "adj", "adv"].indexOf(values.pos) >= 0) ||
(values.lang === "es" && ["noun"].indexOf(values.pos) >= 0) || (values.lang === "fr" && ["noun"].indexOf(values.pos) >= 0);
},
build: function (stepDiv, values) {
if (!values) return;
// TODO: Spanish and other languages with inflection templates.
var forms = {
en: {
noun: [
{name: "Số nhiều", "default": "Mặc định"}
],
adj: [
{name: "So sánh hơn", "default": "Mặc định"},
{name: "So sánh nhất", "default": "Mặc định"} ] },
es: {
noun: [
{
name: "Giống",
"default": pageName.match(/(?:as?|ción|ciones)$/) ? "f" : "m",
choices: [
["m", "đực"],
["f", "cái"],
["mf", "tùy"] ] } ] },
fr: {
noun: [
{
name: "Giống",
"default": "m",
choices: [
["m", "đực"],
["f", "cái"],
["mf", "tùy"] ] } ] } };
if (pageName.match(/(?:c|sh?|zh?)$/)) {
forms.en.noun[0].firstSuggestion = pageName + "es";
}
else if (pageName.match(/y$/)) {
forms.en.noun[0].firstSuggestion = pageName.substr(0, pageName.length - 1) + "ies";
}
forms.en.adv = forms.en.adj;
forms = forms[values.lang];
if (forms) forms = forms[values.pos];
if (!forms) return;
var firstInput;
var stepId = this.id;
$("#wiktvi-start-" + stepId + " .wiktvi-start-custom").detach();
var list = $("<ul class='wiktvi-start-custom'></ul>");
var fieldId = "wiktvi-start-" + stepId + "-field";
$.each(forms, function (i, form) {
var item = $("<li></li>");
item.append($(mw.html.element("label", {
"for": "wiktvi-start-" + stepId + "-field" }, form.name + ":")));
var value = form.firstSuggestion || form["default"];
if (values && this.oldLang === values.lang &&
this.oldPos === values.pos && values.infl[i]) {
value = values.infl[i];
}
var input;
if (form.choices) {
input = $(mw.html.element("select", {
"class": "wiktvi-start-step-field" }));
$.each(form.choices, function (j, choice) {
input.append($(mw.html.element("option", {
value: choice[0],
selected: choice[0] === (form.firstSuggestion || form["default"]) },
choice[1].wiktviCapitalize())));
});
}
else {
input = $(mw.html.element("input", {
type: "text",
"class": "wiktvi-start-step-field wiktvi-start-narrow",
placeholder: form["default"],
value: value }));
}
if (!i) {
input.attr("id", fieldId);
firstInput = input;
}
item.append(input);
list.append(item);
});
stepDiv.append(list);
list.find("select").chosen(chosenOptions);
this["default"] = $.map(forms, function (form, i) {
return form["default"];
});
this.oldLang = values.lang;
this.oldPos = values.pos;
return firstInput;
},
output: function (values) {
// Bản mẫu dòng tên mục từ
var posChoice = $.grep(steps.pos.choices, function (choice, i) {
return choice[0] === values.pos;
})[0];
var output = "{{head|" + values.lang + "|" + posChoice[1].wiktviCapitalize() + "}}\n";
if ((values.pos === "noun" || values.pos === "adj" || values.pos === "verb" || values.pos === "adv" || values.pos === "prep" || values.pos === "pr-noun" || values.pos === "phrase" || values.pos === "idiom" || values.pos === "proverb") && values.lang === "vi") {
return "{{vi-" + values.pos + "}}\n\n";
}
switch (values.lang) {
case "en": {
// TODO: Fetch the English Wiktionary’s inflection template parameters.
if (["noun", "adj", "adv"].indexOf(values.pos) >= 0) {
output = "{{subst:Thế bản mẫu mục từ/eng-" + values.pos;
var defaults = this["default"];
var modified = false;
$.each(values.infl, function (i, value) {
if (value && value !== defaults[i]) {
modified = true;
return false;
}
});
if (modified) {
$.each(values.infl, function (i, value) {
output += "|" + value;
});
}
return output + "}}\n\n";
}
break;
}
case "es": {
if (values.infl[0] && ["noun"].indexOf(values.pos) >= 0) {
output = "{{spa-noun|" + values.infl[0] + "}}";
}
break;
}
case "fr": {
if (values.infl[0] && ["noun"].indexOf(values.pos) >= 0) {
output = "{{fra-noun-2|" + values.infl[0] + "}}";
}
break;
}
}
return output + "\n";
} } };
steps.trans.keys = steps.lang.choices;
var ordered_steps = [
steps.lang, steps.pron, steps.pos, steps.dfn, steps.syn, steps.ant,
steps.trans, steps.infl ];
var continueBtn;
function getValuesForStep(stepId) {
var fields = $("#wiktvi-start-" + stepId + " .wiktvi-start-step-field");
var step = steps[stepId];
if (step.keys) {
var values = {};
fields.each(function (i, field) {
var key = $(field).prevAll(".wiktvi-start-step-keyfield").val();
if (key) values[key] = $(field).val();
});
return values;
}
else if (step.multiple) {
return fields.map(function (i) {
return $(this).val();
}).get();
}
return fields.val();
}
function getValues() {
var values = {};
$.each(steps, function (stepId) {
values[stepId] = getValuesForStep(stepId);
});
return values;
}
function getWikitext(callback) {
var values = getValues();
var entry = steps.lang.output(values);
entry += steps.pron.output(values);
entry += steps.pos.output(values);
entry += steps.infl.output(values);
steps.dfn.getOutput(values, function (output) {
entry += output;
entry += steps.syn.output(values);
entry += steps.ant.output(values);
entry += steps.trans.output(values);
var summary = "([[Mediawiki:Gadget-start.js|Trang mới]]) +" + values.lang + ":" + values.pos + ":" + output;
document.getElementById("wpSummary").value = summary;
// Tag the entry as having come from this tool.
entry += "{{mẫu}}\n";
callback(entry);
});
}
function populateMenus(inputs, choices, disabledValues) {
if (!disabledValues) disabledValues = {};
var vals = inputs.map(function (i, input) {
return $(input).val();
});
inputs.empty();
inputs.append($("<option></option>"));
$.each(choices, function (j, choice) {
var option = $(mw.html.element("option", {
value: choice[0] }, choice[1].wiktviCapitalize()));
if (disabledValues[choice[0]]) option.prop("disabled", true);
inputs.append(option);
});
inputs.val(function (i) {
return vals[i];
});
}
/**
* Returns a bitmap containing the keys that should be disabled for a key
* menu in the given step because they are disabled or already taken by
* another key menu.
*/
function getDisabledKeys(step) {
var occupiedKeys = {};
$("#wiktvi-start-" + step.id + " .wiktvi-start-step-keyfield").each(function (i, select) {
occupiedKeys[$(this).val()] = true;
});
if (step.disabledKeys) $.extend(occupiedKeys, step.disabledKeys);
return occupiedKeys;
}
function buildKeyInput(step) {
if (!step.keys) return undefined;
var input = $(mw.html.element("select", {
"class": "wiktvi-start-step-keyfield wiktvi-start-narrow" }));
input.attr("data-placeholder", "Chọn một " + step.keyName);
input.on("chosen:showing_dropdown", function (evt, context) {
$(this).data("wiktvi-defaultValue", $(this).val());
});
input.change(function (evt, context) {
var inputs = $("#wiktvi-start-" + step.id + " .wiktvi-start-step-keyfield").not(this);
var oldValue = $(this).data("wiktvi-defaultValue");
var newValue = context.selected;
inputs.find("option[value='" + oldValue + "']").prop('disabled', false);
inputs.find("option[value='" + newValue + "']").prop('disabled', true);
inputs.trigger("chosen:updated");
});
populateMenus(input, step.keys, getDisabledKeys(step));
return input;
}
/**
* If the current text field has text and there are no more text fields after
* the current text field in the list, add another text field to the list.
*/
function refreshForTextField(currentInput, step) {
currentInput = $(currentInput);
var value = currentInput.val();
var item = currentInput.parent();
if (value && !item.next("li").length) {
var nextInput = $(mw.html.element("input", {
type: "text",
"class": currentInput.attr("class") }));
nextInput.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var nextKeyInput = buildKeyInput(step);
var nextItem = $("<li></li>").append(nextKeyInput).append(nextInput);
item.parent().append(nextItem);
if (nextKeyInput) nextKeyInput.chosen(chosenOptions);
}
var canContinue = !continueBtn.prop("disabled");
if ((value && !canContinue) || (!value && canContinue)) {
refresh(step);
}
}
function prepareInput(input, step) {
if (!input) return;
if (step.required) input.prop('required', true);
input.change(step, refresh);
}
/**
* Shows and hides all the steps based on whether their prerequisites are met,
* and updates placeholders where applicable.
*/
function refresh(evt) {
var values = getValues();
var changedStep = evt && evt.data;
var didFocus = false;
var canContinue = true;
$.each(steps, function (stepId, step) {
if (typeof(step.visible) === "function" && !step.visible(values)) {
$("#wiktvi-start-" + stepId).slideUp();
// $("#wiktvi-start-" + stepId + " .wiktvi-start-step-field").val("");
}
else {
var stepDiv = $("#wiktvi-start-" + stepId);
if (changedStep !== step && typeof(step.build) === "function") {
prepareInput(step.build(stepDiv, values), step);
}
var input = stepDiv.find(".wiktvi-start-step-field");
if (typeof(step.example) === "function") {
input.attr("placeholder", step.example(values) || "");
}
var didSlideDown;
if (!didFocus && stepDiv.css("display") === "none" && step.required) {
didSlideDown = function (evt) {
input.focus();
};
didFocus = true;
}
stepDiv.slideDown(didSlideDown);
}
if (step.required &&
(values[stepId] === "" ||
(step.multiple && values[stepId].join("") === ""))) {
canContinue = false;
}
});
continueBtn.button(canContinue ? "enable" : "disable");
}
function destroy() {
$("#wiktvi-start-form, .noafterwizard").slideUp(function () {
$(this).remove();
});
$(".noarticletext .nowizard, #editform").slideDown();
$("#ca-edit").removeClass("selected");
$("#ca-wiktvi-nowizard").addClass("selected");
}
function build() {
$(".nowizard, .nowizard .noafterwizard").hide();
var callToAction = $("#wiktvi-calltoaction");
var origCallToActionText = callToAction.text();
$(mw.util.addPortletLink("p-views", "#", "Tạo mã nguồn",
"ca-wiktvi-nowizard", "Tạo mục từ dùng hộp sửa đổi văn bản thuần",
undefined, "#ca-watch")).click(function (evt) {
evt.preventDefault();
callToAction.text(origCallToActionText);
destroy();
});
callToAction.text(origCallToActionText +
" Điền biểu mẫu ở dưới để viết mục từ mới.");
var startForm = $("<form id='wiktvi-start-form'></form>");
var spinner;
startForm.submit(function (evt) {
evt.preventDefault();
continueBtn.button("disable");
spinner.show();
getWikitext(function (wikitext) {
$("#wpTextbox1").val(wikitext);
$("html, body").animate({scrollTop: 0});
callToAction.text("Gần xong!");
destroy();
});
});
$.each(ordered_steps, function (i, step) {
var stepDiv = $(mw.html.element("div", {
"class": "wiktvi-start-step",
id: "wiktvi-start-" + step.id }));
var name = step.name.wiktviCapitalize();
stepDiv.append($(mw.html.element("h3", {
"class": "wiktvi-start-step-name" }, new mw.html.Raw(mw.html.element("label", {
"for": "wiktvi-start-" + step.id + "-field" }, name)))));
if (step.question_fmt) {
var question = step.question_fmt.replace(/\{\{PAGENAME\}\}/g, pageName);
stepDiv.append($(mw.html.element("label", {
"class": "wiktvi-start-step-question",
"for": "wiktvi-start-" + step.id + "-field" }, question)));
}
var input;
// Menu
if (step.choices) {
input = $(mw.html.element("select", {
"class": "wiktvi-start-step-field",
id: "wiktvi-start-" + step.id + "-field" }));
input.attr("data-placeholder",
(step.multiple ? "Chọn các " : "Chọn một ") + step.name);
populateMenus(input, step.choices);
if (step.multiple) input.attr("multiple", true);
stepDiv.append(input);
}
// Custom
else if (typeof(step.build) === "function") {
input = step.build(stepDiv);
}
// Text field(s)
else {
input = $(mw.html.element("input", {
type: "text",
"class": "wiktvi-start-step-field",
id: "wiktvi-start-" + step.id + "-field" }));
if (step.narrow) input.addClass("wiktvi-start-narrow");
if (step.multiple) {
input.on("input", function (evt) {
refreshForTextField(evt.target, step);
});
var keyInput = buildKeyInput(step);
var list = $(step.ordered ? "<ol></ol>" : "<ul></ul>")
.append($("<li></li>").append(keyInput).append(input));
stepDiv.append(list);
}
else stepDiv.append(input);
}
prepareInput(input, step);
stepDiv.hide();
startForm.append(stepDiv);
});
var footer = $("<div id='wiktvi-start-footer'></div>");
spinner = $(mw.html.element("img", {
id: "wiktvi-start-spinner",
src: "//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif",
alt: "Đang tải…",
title: "Đang tải…" }));
footer.append(spinner.hide());
continueBtn = $(mw.html.element("button", {
id: "wiktvi-unhide-editform",
type: "submit" }, "Tiếp"));
footer.append(continueBtn);
startForm.append(footer);
$("#editform").hide().before(startForm);
startForm.find("select").chosen(chosenOptions);
continueBtn.button();
refresh();
$.wiktviHeaders.get(function (allHeaders) {
// Replace the initial language list with the complete list.
var headers = $.extend({}, allHeaders);
delete headers.vie;
if (headers.mul) headers.mul.name = "đa ngữ";
steps.lang.choices = steps.trans.keys = [];
$.each(headers, function (code, header) {
var match;
if (header.level === 1 && (match = header.name.match(/^tiếng (.+)| ngữ$/i))) {
var name = header.name;
if (!name.indexOf("Tiếng ")) name = "tiếng " + match[1];
var choice = [code, name, (match[1] || name)];
steps.lang.choices.push(choice);
langsByCode[code] = choice;
}
});
// Alphabetize the language list by sort key.
steps.lang.choices.sort(function (a, b) {
return a[2].localeCompare(b[2], userLang);
});
// Vietnamese goes first in language lists but is disabled in the
// translation section.
var choice = ["vi", "tiếng Việt", "Việt"];
steps.lang.choices.unshift(choice);
langsByCode.vie = choice;
var langMenu = $("#wiktvi-start-lang-field");
populateMenus(langMenu, steps.lang.choices);
langMenu.trigger("chosen:updated");
var transMenus = $("#wiktvi-start-trans .wiktvi-start-step-keyfield");
populateMenus(transMenus, steps.trans.keys, getDisabledKeys(steps.trans));
transMenus.trigger("chosen:updated");
});
}
mw.loader.using(["jquery.chosen", "jquery.ui", "mediawiki.api", "ext.gadget.headers"],
build);
});
fvly5ndwzi7t9elnt6u5975gyru0kju
Nhật Bản
0
246116
2573903
2513868
2026-10-27T15:43:36Z
Hiyuune
50834
(By [[meta:Indic-TechCom/Tools|FindAndReplace]])
2573903
wikitext
text/x-wiki
=={{langname|vi}}==
[[File:Flag of Japan.svg|thumb|240px|Quốc kỳ '''Nhật Bản''']]
[[Image:Japan satellite.jpg|thumb|240px|Ảnh chụp vệ tinh '''Nhật Bản''']]
{{wp}}
==={{section|alt}}===
*{{alt|vi|Nhật-Bản|Nhật-bản|Nhật Bổn|Nhật-Bổn|Nhật-bổn|Nhựt Bản|Nhựt-Bản|Nhựt-bản|Nhựt Bổn|Nhựt-Bổn|Nhựt-bổn}}
==={{section|etym}}===
{{vi-etym-sino|日本}}, từ {{bor|vi|ja|日本|tr=Nippon, Nihon}}.
==={{section|pron}}===
{{vi-pron}}
==={{section|pr-noun}}===
{{vi-pr-noun|head=[[Nhật]] [[bản|Bản]]}}
# {{senseid|vi|Q17}}{{place|vi|quốc gia|r/Đông Á|caplc=Tokyo}}.
#: {{ux|vi|Quốc hội '''Nhật Bản'''.}}
#: {{ux|vi|Biển '''Nhật Bản'''.}}
#: {{ux|vi|Rất nhiều thực tập sinh Việt Nam bị trục xuất khỏi '''Nhật Bản''', nguyên nhân chủ yếu là do vi phạm pháp luật như trộm cắp, cư trú và làm việc bất hợp pháp hoặc không hợp tác với cảnh sát địa phương.}}
#: {{ux|vi|Không ít người '''Nhật Bản''' có góc nhìn rất tiêu cực về Việt Nam vì những hành vi làm ngược lại tôn chỉ nhập gia tùy tục, và khả năng thay đổi hay loại bỏ định kiến này là khá khó.}}
#* {{w|Phan Chu Trinh|Phan Châu Trinh}} ''{{lang|vi|Đạo đức và luân lí Đông Tây}}'':
#*: {{uxi|vi|Người nước ta thường tự xưng là đồng loại, đồng đạo, đồng văn với '''Nhật Bản'''; thấy họ tiến thì nức nở khen chớ không khi nào chịu xét vì sao mà họ tiến tới như thế? Họ chỉ đóng tàu súng mà được giàu mạnh hay là họ còn trao dồi đạo đức, sửa đổi luân lý mới được như ngày nay? Ai có đọc đến lịch sử Nhật Bản mới biết '''Nhật Bản''' họ cũng bồi đắp nền đạo đức của họ lắm. Từ lúc Minh Trị duy tân cho đến 24 năm sau hạ chiếu lập hiến trong nước Nhật biết bao nhiêu người lo khuynh Mạc Phủ lo lập hiến pháp, biết bao nhiêu kẻ đổ máu rát cổ mới gây dựng nên một nước tân tiến rất giàu mạnh như bây giờ.}}
===={{section|trans}}====
{{trans-top|một quốc gia ở Đông Á}}
* {{dịch|ar}}: {{t+|ar|الْيَابَان|f}}
* {{dịch|ab}}: {{t+|ab|Иапониа}}
* {{dịch|ace}}: {{t+|ace|Jeupun}}
* {{dịch|ady}}: {{t+|ady|Японие}}
* {{dịch|af}}: {{t+|af|Japan}}
* {{dịch|ain}}: {{t|ain|シサㇺ|tr=Sisam}}
* {{dịch|ak}}: {{t|ak|Yapan}}, {{t|ak|Gyapan}}
* {{dịch|en}}: {{t+|en|Japan}}
* {{dịch|he}}: {{t+|he|יַפָּן|f|tr=yapán}}
* {{dịch|et}}: {{t+|et|Jaapan}}
* {{dịch|hi}}: {{t+|hi|जापान|m}}
* {{dịch|hu}}: {{t+|hu|Japán}}
* {{dịch|hrx}}: {{t|hrx|Schapong|n}}
* {{dịch|is}}: {{t+|is|Japan|n}}
* {{dịch|io}}: {{t+|io|Japonia}}
* {{dịch|ilo}}: {{t|ilo|Japon}}
* {{dịch|ln}}: {{t|ln|Zapɔ́}}
* {{dịch|lt}}: {{t+|lt|Japonija|f}}
* {{dịch|nah}}: {{t|nah|Xapōn}}
* {{dịch|na}}: {{t|na|Djapan}}
* {{dịch|nv}}: {{t|nv|Binááʼádaałtsʼózí dineʼé bikéyah}}
* {{dịch|fr}}: le {{t+|fr|Japon|m}}
* {{dịch|cs}}: {{t+|cs|Japonsko|n}}
* {{dịch|zh}}:
*: {{langname|nan-hbl}}: {{t+|nan-hbl|日本|tr=Ji̍t-pún, Li̍t-pún}}
*: {{langname|cmn}}: {{t+|cmn|日本}}
*: {{langname|cjy}}: {{t+|cjy|日本}}
* {{dịch|zu}}: {{t+|zu|IJapani}}
{{trans-bottom}}
==={{section|adj}}===
{{vi-adj|head=[[Nhật]] [[bản|Bản]]}}
# {{lb|vi|uncommon}} Thuộc về Nhật Bản.
==={{section|syn}}===
* {{vi-l|Nhật}}
* {{vi-l|Phù Tang}}
==={{section|see}}===
{{danh sách:quốc gia châu Á/vi}}
swbcuihq9wwiowaxj3rydple2sdiobo
2573912
2573903
2026-10-27T16:00:58Z
TheHighFighter2
42988
2573912
wikitext
text/x-wiki
=={{langname|vi}}==
[[File:Flag of Japan.svg|thumb|240px|Quốc kỳ '''Nhật Bản''']]
[[Image:Japan satellite.jpg|thumb|240px|Ảnh chụp vệ tinh '''Nhật Bản''']]
{{wp}}
==={{section|alt}}===
*{{alt|vi|Nhật-Bản|Nhật-bản|Nhật Bổn|Nhật-Bổn|Nhật-bổn|Nhựt Bản|Nhựt-Bản|Nhựt-bản|Nhựt Bổn|Nhựt-Bổn|Nhựt-bổn}}
==={{section|etym}}===
{{vi-etym-sino|日本}}, từ {{bor|vi|ja|日本|tr=Nippon, Nihon}}.
==={{section|pron}}===
{{vi-pron}}
==={{section|pr-noun}}===
{{vi-pr-noun|head=[[Nhật]] [[bản|Bản]]}}
# {{senseid|vi|Q17}}{{place|vi|quốc gia|r/Đông Á|caplc=Tokyo}}.
#: {{ux|vi|Quốc hội '''Nhật Bản'''.}}
#: {{ux|vi|Biển '''Nhật Bản'''.}}
#: {{ux|vi|Rất nhiều thực tập sinh Việt Nam bị trục xuất khỏi '''Nhật Bản''', nguyên nhân chủ yếu là do vi phạm pháp luật như trộm cắp, cư trú và làm việc bất hợp pháp hoặc không hợp tác với cảnh sát địa phương.}}
#: {{ux|vi|Không ít người '''Nhật Bản''' có góc nhìn rất tiêu cực về Việt Nam vì những hành vi làm ngược lại tôn chỉ nhập gia tùy tục, và khả năng thay đổi hay loại bỏ định kiến này là khá khó.}}
#* {{w|Phan Chu Trinh|Phan Châu Trinh}} ''{{lang|vi|Đạo đức và luân lí Đông Tây}}'':
#*: {{uxi|vi|Người nước ta thường tự xưng là đồng loại, đồng đạo, đồng văn với '''Nhật Bản'''; thấy họ tiến thì nức nở khen chớ không khi nào chịu xét vì sao mà họ tiến tới như thế? Họ chỉ đóng tàu súng mà được giàu mạnh hay là họ còn trao dồi đạo đức, sửa đổi luân lý mới được như ngày nay? Ai có đọc đến lịch sử Nhật Bản mới biết '''Nhật Bản''' họ cũng bồi đắp nền đạo đức của họ lắm. Từ lúc Minh Trị duy tân cho đến 24 năm sau hạ chiếu lập hiến trong nước Nhật biết bao nhiêu người lo khuynh Mạc Phủ lo lập hiến pháp, biết bao nhiêu kẻ đổ máu rát cổ mới gây dựng nên một nước tân tiến rất giàu mạnh như bây giờ.}}
===={{section|trans}}====
{{trans-top|một quốc gia ở Đông Á}}
* {{dịch|ar}}: {{t+|ar|الْيَابَان|f}}
* {{dịch|ab}}: {{t+|ab|Иапониа}}
* {{dịch|ace}}: {{t+|ace|Jeupun}}
* {{dịch|ady}}: {{t+|ady|Японие}}
* {{dịch|af}}: {{t+|af|Japan}}
* {{dịch|ain}}: {{t|ain|シサㇺ|tr=Sisam}}
* {{dịch|ak}}: {{t|ak|Yapan}}, {{t|ak|Gyapan}}
* {{dịch|en}}: {{t+|en|Japan}}
* {{dịch|he}}: {{t+|he|יַפָּן|f|tr=yapán}}
* {{dịch|et}}: {{t+|et|Jaapan}}
* {{dịch|hi}}: {{t+|hi|जापान|m}}
* {{dịch|hu}}: {{t+|hu|Japán}}
* {{dịch|hrx}}: {{t|hrx|Schapong|n}}
* {{dịch|is}}: {{t+|is|Japan|n}}
* {{dịch|io}}: {{t+|io|Japonia}}
* {{dịch|ilo}}: {{t|ilo|Japon}}
* {{dịch|ln}}: {{t|ln|Zapɔ́}}
* {{dịch|lt}}: {{t+|lt|Japonija|f}}
* {{dịch|nah}}: {{t|nah|Xapōn}}
* {{dịch|na}}: {{t|na|Djapan}}
* {{dịch|nv}}: {{t|nv|Binááʼádaałtsʼózí dineʼé bikéyah}}
* {{dịch|fr}}: le {{t+|fr|Japon|m}}
* {{dịch|cs}}: {{t+|cs|Japonsko|n}}
* {{dịch|zh}}:
*: {{dịch|nan-hbl}}: {{t+|nan-hbl|日本|tr=Ji̍t-pún, Li̍t-pún}}
*: {{dịch|cmn}}: {{t+|cmn|日本}}
*: {{dịch|cjy}}: {{t+|cjy|日本}}
* {{dịch|zu}}: {{t+|zu|IJapani}}
{{trans-bottom}}
==={{section|adj}}===
{{vi-adj|head=[[Nhật]] [[bản|Bản]]}}
# {{lb|vi|uncommon}} Thuộc về Nhật Bản.
==={{section|syn}}===
* {{vi-l|Nhật}}
* {{vi-l|Phù Tang}}
==={{section|see}}===
{{danh sách:quốc gia châu Á/vi}}
11iytddvmkr93f3e0cqk86odnpv0bo3
Úc
0
247153
2573946
2573614
2026-10-28T02:39:06Z
Hiyuune
50834
/* Đồng nghĩa */ (sử dụng [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]])
2573946
wikitext
text/x-wiki
{{also|Phụ lục:Biến thể của "uc"}}
=={{langname|vi}}==
{{swp|vi:}}
==={{section|pron}}===
{{vi-pron}}
* {{hmp|vi|út}}
* {{audio|vi|LL-Q9199 (vie)-Penn Zero MSSJ-Úc.wav|a=Hà Nội}}
==={{section|pr-noun}}===
{{vi-propn|head=[[Úc]]}} {{tlb|vi|colloquial}}
# {{sid|vi|Q408}} {{place|vi|Một <<quốc gia>> nằm ở giữa Ấn Độ Dương và Thái Bình Dương; lãnh thổ bao gồm lục địa Úc, đảo Tasmania và các đảo nhỏ lân cận|capital=Canberra|official=Thịnh vượng chung Úc}}.
===={{section|syn}}====
* {{s|quốc gia}} {{l|vi|Úc Đại Lợi}}; {{vi-l|Ô-xtrây-li-a}}
* {{s|lục địa}} {{l|vi|Úc Châu}}; {{vi-l|châu Úc}}; {{vi-l|lục địa Úc}}
===={{section|trans}}====
{{trans-top|một quốc gia nằm ở giữa Ấn Độ Dương và Thái Bình Dương}}
* {{dịch|en}}: {{t+|en|Australia}}
{{trans-bottom}}
==={{section|adj}}===
{{vi-adj|head=[[Úc]]}}
# {{lb|vi|colloquial}} {{demonym-adj|vi|<<c/Úc>>}}
==={{section|see}}===
{{list:quốc gia của châu Đại Dương/vi}}
==={{section|ana}}===
* {{anagrams|vi|cú|cu}}
4y9y0lg9i7q7lad0z3t89hpp8jfgy28
Việt Nam Cộng hòa
0
250389
2573900
2533122
2026-10-27T15:34:38Z
TheHighFighter2
42988
/* Dịch */
2573900
wikitext
text/x-wiki
=={{langname|vi}}==
{{wp}}
==={{section|alt}}===
* {{l|vi|Việt Nam Cộng hoà}}
* {{q|từ viết tắt}} {{l|vi|VNCH}}
==={{section|etym}}===
{{vi-etym-sino|越南|Việt Nam|共和|cộng hòa}}.
==={{section|pron}}===
{{vi-IPA}}
==={{section|pr-noun}}===
{{vi-propn|head=[[Việt Nam]] [[cộng hòa|Cộng hòa]]|越南共和}}
# {{senseid|vi|Q180573}} [[chính thể|Chính thể]] thành lập ngày 26 tháng 10 năm 1955 tại [[bán đảo]] [[Đông Dương]] thuộc phía nam [[vĩ tuyến]] 17, [[cáo chung]] ngày 30 tháng 4 năm 1975.
#: {{ux|vi|Phong trào khôi phục chế độ '''Việt Nam Cộng hòa'''.}}
#: {{cot|vi|Việt Nam Dân chủ Cộng hòa}}
===={{section|trans}}====
{{trans-top|Chính thể tồn tại từ năm 1955 đến năm 1975}}
* {{dịch|en}}: {{t+|en|Republic of Vietnam}}, {{t+|en|South Vietnam}}
* {{dịch|lo}}: {{t|lo|ຫວຽດນາມໃຕ້}}
* {{dịch|lv}}: {{t|lv|Dienvidvjetnama|f}}
* {{dịch|lt}}: {{t|lt|Pietų Vietnamas|m}}
* {{dịch|mk}}: {{t|mk|Јужен Виетнам|m}}
* {{dịch|ms}}: {{t|ms|Vietnam Selatan}}
* {{dịch|my}}: {{t|my|တောင်ဗီယက်နမ်}}
* {{dịch|ru}}: {{t+|ru|Ю́жный Вьетна́м|m}}
* {{dịch|ja}}: {{t+|ja|ベトナム共和国|tr=Betonamu Kyōwakoku}}, {{t+|ja|南ベトナム|tr=みなみベトナム, Minami Betonamu}}
* {{dịch|ko}}: {{t|ko|^남베트남}}
* {{dịch|zh}}:
*: {{dịch|cmn}}: {{t|cmn|越南共和國|tr=Yuènán gònghéguó}}
{{trans-bottom}}
{{C|vi|Việt Nam Cộng hòa|Lịch sử Việt Nam|Chiến tranh Việt Nam}}
lulnrfd4i6yb5txn0oweysv4kw4fudq
đuổi bắt
0
250604
2573895
2559523
2026-10-27T14:16:50Z
TheHighFighter2
42988
2573895
wikitext
text/x-wiki
=={{langname|vi}}==
{{wp}}
==={{ĐM|etym}}===
Từ {{com|vi|đuổi|bắt}}.
==={{ĐM|pron}}===
{{vi-pron}}
==={{ĐM|verb}}===
{{vi-verb}}
# Đuổi theo bắt lại.
#: {{syn|vi|rượt bắt}}
#* {{quote|vi|Muồm muỗm bay cao<br>Cào cào bay thấp<br>Chúng em '''đuổi bắt'''<br>Trên đồng cỏ xanh.}}
==={{ĐM|noun}}===
{{vi-noun}}
# {{lb|vi|game}} Trò chơi gồm một hoặc nhiều người tham gia đuổi theo và cố gắng chạm vào người khác để bắt họ.
#: {{syn|vi|rượt bắt}}
===={{ĐM|trans}}====
{{trans-top|Trò chơi}}
* {{dịch|en}}: {{t+|en|tag}}
{{trans-bottom}}
qxkf6tkjathz40t7hsc74k2gcqgpm6i
an cư lạc nghiệp
0
255302
2573897
2228373
2026-10-27T14:20:36Z
TheHighFighter2
42988
/* Dịch */
2573897
wikitext
text/x-wiki
=={{langname|vi}}==
==={{ĐM|etym}}===
{{vi-etym-sino|安居|sống yên ổn|樂業|vui vẻ làm ăn}}.
==={{ĐM|pron}}===
{{vie-pron}}
==={{ĐM|verb}}===
{{vie-verb|head=[[an cư]] [[lạc nghiệp]]}}
# {{nhãn|vi|idiomatic}} [[sống|Sống]] [[ổn định]] ở một nơi và [[yên tâm]] [[vui vẻ]] [[làm ăn]].
#* {{quote-book|vi|author={{w|Trần Trọng Kim}}|chapter=Về Huế và lập chánh phủ|chapterurl={{fullurle:s:vi:Một cơn gió bụi/4}}|title=Một cơn gió bụi|url={{fullurle:s:vi:Một cơn gió bụi}}|year=1949|year_published=1969|publisher=NXB Vĩnh Sơn|page=58|text=Theo ý tôi, bất cứ trong một chính thể nào, việc của Chính phủ là phải lấy sự dân sinh làm trọng, nghĩa là phải làm cho dân '''an cư lạc nghiệp''', rồi tìm cách giáo hóa nâng cao trình độ dân chúng về đường tinh thần và đường vật chất cho hợp thời để tiến thủ với các dân tộc khác mà vẫn giữ được đặc tính của mình.}}
#* {{quote-book|vi|chapter=Hòa hiệp|chapterurl={{fullurle:s:vi:Chị Đào, Chị Lý/Chương 18}}|title=Chị Đào, Chị Lý|author=Hồ Biểu Chánh|authorlink=Hồ Biểu Chánh|year=1957|url={{fullurle:s:vi:Chị Đào, Chị Lý}}|passage=Nhơn dịp cậu ngỏ lời khuyên bà con cứ bình tĩnh làm ăn đừng sợ chi hết, quốc gia sẵn sàng ủng hộ đồng bào, giữ an ninh trật tự cho đâu đó đều '''an cư lạc nghiệp'''.}}
===={{ĐM|trans}}====
{{trans-top|Sống ổn định ở một nơi và yên tâm vui vẻ làm ăn}}
* {{dịch|zh}}:
*: {{dịch|cmn}}: {{t+|cmn|安居樂業|tr=ānjūlèyè}}
{{trans-bottom}}
==={{ĐM|ref}}===
* {{R:Nguyễn Như Ý 1999}}
* {{R:Tratu}}
hkkk3b885zsw7hko2zod3pdfq0pb0i0
Mô đun:language-like
828
256762
2573978
2508293
2026-10-28T08:46:50Z
Hiyuune
50834
2573978
Scribunto
text/plain
local export = {}
local families_module = "Module:families"
local string_utilities_module = "Module:string utilities"
local table_module = "Module:table"
-- In a locally installed Docker container containing Wiktionary, Wikibase is typically not installed because it's
-- a real pain to get working properly. In such a case, `mw.wikibase` returns nil. We now have checks in the code
-- below so it does reasonable things in the absence of Wikibase.
local wikibase = mw.wikibase
local category_name_has_suffix -- defined as export.categoryNameHasSuffix below
local get_entity = wikibase and wikibase.getEntity or nil
local get_entity_id_for_title = wikibase and wikibase.getEntityIdForTitle or nil
local gsub = string.gsub
local ipairs = ipairs
local match = string.match
local select = select
local sitelink = wikibase and wikibase.sitelink or nil
local type = type
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 case_insensitive_pattern(...)
case_insensitive_pattern = require(string_utilities_module).case_insensitive_pattern
return case_insensitive_pattern(...)
end
local function table_flatten(...)
table_flatten = require(table_module).flatten
return table_flatten(...)
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 content_lang
local function get_content_lang()
content_lang, get_content_lang = mw.getContentLanguage(), nil
return content_lang
end
-- Implementation of getAliases() for languages, etymology languages,
-- families, scripts and writing systems.
function export.getAliases(self)
local aliases = self._aliases
if aliases == nil then
aliases = (self._data or self).aliases or {}
self._aliases = aliases
end
return aliases
end
-- Implementation of getVarieties() for languages, etymology languages,
-- families, scripts and writing systems. If `flatten` is passed in,
-- flatten down to a list of strings; otherwise, keep the structure.
function export.getVarieties(self, flatten)
local varieties = self._varieties
if varieties == nil then
varieties = (self._data or self).varieties or {}
self._varieties = varieties
end
if not flatten then
return varieties
end
local flattened_varieties = self._flattened_varieties
if flattened_varieties == nil then
flattened_varieties = table_flatten(varieties)
self._flattened_varieties = flattened_varieties
end
return flattened_varieties
end
-- Implementation of getOtherNames() for languages, etymology languages,
-- families, scripts and writing systems.
function export.getOtherNames(self)
local other_names = self._other_names
if other_names == nil then
other_names = (self._data or self).other_names or {}
self._other_names = other_names
end
return other_names
end
-- Implementation of getAllNames() for languages, etymology languages,
-- families, scripts and writing systems.
function export.getAllNames(self)
local all_names = self._allNames
if all_names == nil then
all_names = table_flatten{
self.getCanonicalNames and self:getCanonicalNames() or self:getCanonicalName(),
self:getAliases(),
self:getVarieties(),
self:getOtherNames(),
}
self._allNames = all_names
end
return all_names
end
function export.hasType(self, ...)
local n = select("#", ...)
if n == 0 then
error("Must specify at least one type.")
end
local types = self:getTypes()
if not types[...] then
return false
elseif n == 1 then
return true
end
local args = {...}
for i = 2, n do
if not types[args[i]] then
return false
end
end
return true
end
-- Implementation of template-callable getByCode() function for languages,
-- etymology languages, families and scripts. `item` is the language,
-- family or script in question; `args` is the arguments passed in by the
-- module invocation; `extra_processing`, if specified, is a function of
-- one argument (the requested property) and should return the value to
-- be returned to the caller, or nil if the property isn't recognized.
-- `extra_processing` is called after special-cased properties are handled
-- and before general-purpose processing code that works for all string
-- properties.
function export.templateGetByCode(args, extra_processing)
-- The item that the caller wanted to look up.
local item, itemname, list = args[1], args[2]
if itemname == "getAllNames" then
list = item:getAllNames()
elseif itemname == "getOtherNames" then
list = item:getOtherNames()
elseif itemname == "getAliases" then
list = item:getAliases()
elseif itemname == "getVarieties" then
list = item:getVarieties(true)
end
if list then
local index = args[3]; if index == "" then index = nil end
index = tonumber(index or error("Numeric index of the desired item in the list (parameter 3) has not been specified."))
return list[index] or ""
end
if itemname == "getFamily" and item.getFamily then
return item:getFamily():getCode()
end
if extra_processing then
local retval = extra_processing(itemname)
if retval then
return retval
end
end
if item[itemname] then
local ret = item[itemname](item)
if type(ret) == "string" then
return ret
end
error("The function \"" .. itemname .. "\" did not return a string value.")
end
error("Requested invalid item name \"" .. itemname .. "\".")
end
-- Implementation of getCommonsCategory() for languages, etymology languages,
-- families, scripts and writing systems.
function export.getWikidataItem(self)
local item = self._WikidataItem
if item == nil then
item = (self._data or self)[2]
-- If the value is nil, it's cached as false.
item = item ~= nil and (type(item) == "number" and "Q" .. item or item) or false
self._WikidataItem = item
end
return item or nil
end
do
local function get_wiki_article(self, project)
local article
-- If the project is enwiki, check the language data.
if project == "viwiki" then
article = (self._data or self).wikipedia_article
if article then
return article
end
end
-- Otherwise, check the Wikidata item for a sitelink.
local item = self:getWikidataItem()
article = item and sitelink and sitelink(item, project) or false
if article then
return article
end
-- If there's still no article, try the parent (if any).
local get_parent = self.getParent
if get_parent then
local parent = get_parent(self)
if parent then
return get_wiki_article(parent, project)
end
end
return false
end
-- Implementation of getWikipediaArticle() for languages, etymology languages,
-- families, scripts and writing systems.
function export.getWikipediaArticle(self, noCategoryFallback, project)
if project == nil then
project = "viwiki"
end
local article
if project == "viwiki" then
article = self._wikipedia_article
if article == nil then
article = get_wiki_article(self, project)
self._wikipedia_article = article
end
else
-- If the project isn't enwiki, default to no category fallback, but
-- this can be overridden by specifying the value `false`.
if noCategoryFallback == nil then
noCategoryFallback = true
end
local non_vi_wikipedia_articles = self._non_vi_wikipedia_articles
if non_vi_wikipedia_articles == nil then
non_vi_wikipedia_articles = {}
self._non_vi_wikipedia_articles = non_vi_wikipedia_articles
else
article = non_vi_wikipedia_articles[project]
end
if article == nil then
article = get_wiki_article(self, project)
non_vi_wikipedia_articles[project] = article
end
end
if article or noCategoryFallback then
return article or nil
end
return (gsub(self:getCategoryName(), "Creole language", "Creole"))
end
end
do
local function get_commons_cat_claim(item)
if item then
local entity = get_entity and get_entity(item) or nil
if entity then
-- P373 is the "Commons category" property.
local claim = entity:getBestStatements("P373")[1]
return claim and ("Thể loại:" .. claim.mainsnak.datavalue.value) or nil
end
end
end
local function get_commons_cat_sitelink(item)
if item then
local commons_sitelink = sitelink and sitelink(item, "commonswiki") or nil
-- Reject any sitelinks that aren't categories.
return commons_sitelink and match(commons_sitelink, "^Thể loại:") and commons_sitelink or nil
end
end
local function get_commons_cat(self)
-- Checks are in decreasing order of likelihood for a useful match.
-- Get the Commons Category claim from the object's item.
local lang_item = self:getWikidataItem()
local category = get_commons_cat_claim(lang_item)
if category then
return category
end
-- Otherwise, try the object's category's item.
local langcat_item = get_entity_id_for_title and get_entity_id_for_title("Thể loại:" .. self:getCategoryName()) or nil
category = get_commons_cat_claim(langcat_item)
if category then
return category
end
-- If there's no P373 claim, there might be a sitelink on the
-- object's category's item.
category = get_commons_cat_sitelink(langcat_item)
if category then
return category
end
-- Otherwise, try for a sitelink on the object's own item.
category = get_commons_cat_sitelink(lang_item)
if category then
return category
end
-- If there's still no category, try the parent (if any).
local get_parent = self.getParent
if get_parent then
local parent = get_parent(self)
if parent then
return get_commons_cat(parent)
end
end
return false
end
-- Implementation of getCommonsCategory() for languages, etymology
-- languages, families, scripts and writing systems.
function export.getCommonsCategory(self)
local category
category = self._commons_category
-- Nil values cached as false.
if category ~= nil then
return category or nil
end
category = get_commons_cat(self)
self._commons_category = category
return category or nil
end
end
function export.categoryNameHasSuffix(name, suffixes)
for _, suffix in ipairs(suffixes) do
if umatch(name, "%f[%w]" .. case_insensitive_pattern(suffix, "^.") .. "$") then
return false
end
end
return true
end
category_name_has_suffix = export.categoryNameHasSuffix
function export.categoryNameToCode(name, suffix, data, suffixes)
local truncated = match(name, "(.*)" .. suffix .. "$")
if truncated and category_name_has_suffix(truncated, suffixes) then
local code = data[truncated]
if code ~= nil then
return code, truncated
end
truncated = (content_lang or get_content_lang()):lcfirst(truncated)
code = data[truncated]
if code ~= nil then
return code, truncated
end
end
if not category_name_has_suffix(name, suffixes) then
local code = data[name]
if code ~= nil then
return code, name
end
name = (content_lang or get_content_lang()):lcfirst(name)
code = data[name]
if code ~= nil then
return code, name
end
end
return nil, nil
end
-- If a language has no specified pseudo-family but its or its family's code begins with one or these prefixes,
-- assign it to the specified pseudo-family. Note that we don't do this across-the-board because there are (currently)
-- some families whose code begins with `nai-` but are considered in the `cai` pseudo-family.
local pseudo_family_prefixes = {
cai = true,
nai = true,
paa = true,
sai = true,
sgn = true,
}
-- Implementation of getPseudoFamilies() for languages, etymology languages
-- and families.
function export.getPseudoFamilies(self)
local pseudo_families = self._pseudo_families
local function insert_pseudo_family(code)
local fam = require(families_module).getByCode(code)
if not fam then
mw.log(("WARNING: Unrecognized pseudo-family code '%s'"):format(code))
else
table.insert(pseudo_families, fam)
end
end
if pseudo_families == nil then
pseudo_families = {}
local pseudo_family_string = (self._data or self).pseudo_families
if pseudo_family_string then
local pseudo_family_codes = require(string_utilities_module).split(pseudo_family_string, "%s*,%s*")
for _, code in ipairs(pseudo_family_codes) do
insert_pseudo_family(code)
end
end
if not pseudo_families[1] then
-- Check the parent. If it's a full language, we will have to fetch the extra data to get the
-- list of pseudo-families.
local get_parent = self.getParent
if get_parent then
local parent = get_parent(self)
if parent then
local parent_code = parent:getCode()
if pseudo_family_prefixes[parent_code] then
insert_pseudo_family(parent_code)
else
pseudo_families = parent:getPseudoFamilies()
end
end
end
end
if not pseudo_families[1] then
-- Check the family to see if it's in any pseudo-families.
local get_family = self.getFamily
if get_family then
local family = get_family(self)
if family then
local family_code = family:getCode()
if pseudo_family_prefixes[family_code] then
insert_pseudo_family(family_code)
elseif family_code ~= self:getCode() then -- qfa-not has itself as a family.
pseudo_families = family:getPseudoFamilies()
end
end
end
end
if not pseudo_families[1] then
local prefix = self:getCode():match("^([a-z][a-z][a-z])%-")
if prefix and pseudo_family_prefixes[prefix] then
insert_pseudo_family(prefix)
end
end
self._pseudo_families = pseudo_families
end
return pseudo_families
end
return export
rf8s0hdzj7gfmcpg61ntzcic50060kx
Mô đun:fr-headword
828
261942
2573964
2566662
2026-10-28T07:16:10Z
Hiyuune
50834
2573964
Scribunto
text/plain
local export = {}
local pos_functions = {}
local force_cat = false -- for testing; if true, categories appear in non-mainspace pages
local check_missing = false -- whether to check for missing forms
local require_when_needed = require("Module:utilities/require when needed")
local m_table = require("Module:table")
local lang = require("Module:languages").getByCode("fr")
local langname = lang:getCanonicalName()
local en_utilities_module = "Module:en-utilities"
local headword_utilities_module = "Module:headword utilities"
local romut_module = "Module:romance utilities"
local m_en_utilities = require_when_needed(en_utilities_module)
local m_headword_utilities = require_when_needed(headword_utilities_module)
local glossary_link = require_when_needed(headword_utilities_module, "glossary_link")
local boolean_param = {type = "boolean"}
local list_param = {list = true, disallow_holes = true}
local concat = table.concat
local insert = table.insert
local remove = table.remove
local sort = table.sort
local u = mw.ustring.char
local nnbsp = u(0x202F) -- NNBSP = narrow non-breaking space, used before ? and ! in French
local prepositions = {
"à ",
"aux? ",
"d[eu] ",
"d['’]",
"des ",
"en ",
"sous ",
"sur ",
"avec ",
"pour ",
"par ",
"dans ",
"contre ",
"sans ",
"comme ",
"jusqu['’]",
-- We could list others but you get diminishing returns
}
local no_split_apostrophe_words = {
["c'est"] = true,
["quelqu'un"] = true,
["aujourd'hui"] = true,
}
local function track(page)
require("Module:debug").track("fr-headword/" .. page)
return true
end
-----------------------------------------------------------------------------------------
-- Utility functions --
-----------------------------------------------------------------------------------------
local function replace_hash_with_lemma(term, lemma)
-- If there is a % sign in the lemma, we have to replace it with %% so it doesn't get interpreted as a capture replace
-- expression.
lemma = lemma:gsub("%%", "%%%%")
-- Assign to a variable to discard second return value.
term = term:gsub("#", lemma)
return term
end
-- Parse and insert an inflection not requiring additional processing into `data.inflections`. The raw arguments come
-- from `args[field]`, which is parsed for inline modifiers. `label` is the label that the inflections are given;
-- `accel` is the accelerator form, or nil.
local function parse_and_insert_inflection(data, args, field, label, accel)
m_headword_utilities.parse_and_insert_inflection {
headdata = data,
forms = args[field],
paramname = field,
splitchar = ",",
label = label,
accel = accel and {form = accel} or nil,
}
end
local function make_plural(form, special)
local retval = require(romut_module).handle_multiword(form, special, make_plural, prepositions)
if retval then
if #retval > 1 then
error("Lỗi nội bộ: Got multiple plurals from handle_multiword(): " .. concat(retval))
end
return retval[1]
end
if form:match("[sxz]$") then
return form
elseif form:match("au$") then
return form .. "x"
elseif form:match("al$") then
return form:gsub("al$", "aux")
end
return form .. "s"
end
local function make_feminine(form, special)
local retval = require(romut_module).handle_multiword(form, special, make_feminine, prepositions)
if retval then
if #retval > 1 then
error("Lỗi nội bộ: Got multiple feminines from handle_multiword(): " .. concat(retval))
end
return retval[1]
end
if form:match("e$") then
return form
elseif form:match("en$") then
return form .. "ne"
elseif form:match("er$") then
return form:gsub("er$", "ère")
elseif form:match("el$") then
return form .. "le"
elseif form:match("et$") then
return form .. "te"
elseif form:match("on$") then
return form .. "ne"
elseif form:match("ieur$") then
return form .. "e"
elseif form:match("teur$") then
return form:gsub("teur$", "trice")
elseif form:match("eu[rx]$") then
return form:gsub("eu[rx]$", "euse")
elseif form:match("if$") then
return form:gsub("if$", "ive")
elseif form:match("c$") then
return form:gsub("c$", "que")
elseif form:match("eau$") then
return form:gsub("eau$", "elle")
else
return form .. "e"
end
end
-- For bot use
function export.make_feminine(frame)
local masc = frame.args[1] or error("giống đực trong tham số đầu tiên bắt buộc phải có.")
local special = frame.args[2]
return make_feminine(masc, special)
end
-- Handle generation of feminine or plural inflections when "+" is encountered. `list` is the existing base forms
-- (strings or form objects) that need to be inflected. `defobj` is the form object whose `term` field is "+"; any
-- qualifiers, labels and/or references from that form object will be combined with those of the base form. `inflfun`
-- is the function to do the inflection, which is passed two arguments, the base form value (a string) and the
-- `special` value specified for handling multiword expressions (or nil if no value specified). Returns the new list
-- of inflected form objects.
local function make_inflection(list, defobj, inflfun, special)
local newlist = {}
for _, form in ipairs(list) do
if type(form) == "string" then
form = {term = form}
end
local infl = inflfun(form.term, special)
local formobj = m_table.shallowCopy(form)
formobj.term = infl
m_headword_utilities.combine_termobj_decorations(formobj, defobj)
insert(newlist, formobj)
end
return newlist
end
-----------------------------------------------------------------------------------------
-- Main entry point --
-----------------------------------------------------------------------------------------
-- The main entry point.
-- This is the only function that can be invoked from a template.
function export.show(frame)
local poscat = frame.args[1] or error("Từ loại không được chỉ rõ. Vui lòng thêm tham số đầu tiên để gọi mô đun.")
local params = {
["head"] = list_param,
["id"] = true,
["json"] = boolean_param,
["splithyph"] = boolean_param,
["nolink"] = boolean_param,
["nolinkhead"] = {type = "boolean", alias_of = "nolink"},
["abbr"] = list_param,
["pagename"] = true, -- for testing
}
if pos_functions[poscat] then
for key, val in pairs(pos_functions[poscat].params) do
params[key] = val
end
end
local parargs = frame:getParent().args
local args = require("Module:parameters").process(parargs, params)
local pagename = args.pagename or mw.loadData("Module:headword/data").pagename
local user_specified_heads = args.head
local heads = user_specified_heads
if args.nolink then
if not heads[1] then
heads = {pagename}
end
else
local romut = require(romut_module)
local auto_linked_head = romut.add_links_to_multiword_term(pagename, args.splithyph, no_split_apostrophe_words)
if not heads[1] then
heads = {auto_linked_head}
else
for i, head in ipairs(heads) do
if head:find("^~") then
head = romut.apply_link_modifiers(auto_linked_head, head:sub(2))
heads[i] = head
elseif head:find("^[!?]$") then
-- If explicit head= just consists of ! or ?, add it to the end of the default head after a
-- NNBSP (thin non-breaking space).
head = auto_linked_head .. nnbsp .. head
heads[i] = head
end
if head == auto_linked_head then
track("redundant-head")
end
end
end
end
local data = {
lang = lang,
pos_category = pos_functions[poscat] and pos_functions[poscat].pos_category or poscat,
categories = {},
heads = heads,
user_specified_heads = user_specified_heads,
no_redundant_head_cat = not user_specified_heads[1],
genders = {},
inflections = {},
pagename = pagename,
id = args.id,
checkredlinks = check_missing and (pos_functions[poscat] and pos_functions[poscat].redlink_pos or true) or false,
}
if pagename:find("^%-") and poscat ~= "Biến thể hình thái hậu tố" then
data.is_suffix = true
data.pos_category = "Hậu tố"
data.checkredlinks = check_missing
local singular_poscat = require(en_utilities_module).singularize(poscat)
insert(data.categories, "Dạng hậu tố " .. singular_poscat .. " " .. langname)
insert(data.inflections, {label = "dạng hậu tố " .. singular_poscat})
end
if pos_functions[poscat] then
pos_functions[poscat].func(args, data)
end
parse_and_insert_inflection(data, args, "abbr", "từ viết tắt")
if args.json then
return require("Module:JSON").toJSON(data)
end
return require("Module:headword").full_headword(data)
end
-----------------------------------------------------------------------------------------
-- Nouns --
-----------------------------------------------------------------------------------------
local allowed_genders = m_table.listToSet {
"m", "f", "mf", "mfbysense", "mfequiv", "gneut", "m-p", "f-p", "mf-p", "mfbysense-p", "mfequiv-p", "gneut-p",
"?", "?-p",
}
local additional_allowed_pronoun_genders = m_table.listToSet {
"m-s", "f-s", "mf-s",
"p", -- mf-p doesn't make sense for e.g. [[iels]]/[[ielles]]
"n", -- e.g. [[ceci]]/[[cela]]
}
local function validate_genders(genders, is_pronoun)
for _, g in ipairs(genders) do
if type(g) == "table" then
g = g.spec
end
if not allowed_genders[g] and (not is_pronoun or not additional_allowed_pronoun_genders[g]) then
error("Không rõ giống: " .. g)
end
end
end
local function make_qual_replaced_by(replacement)
return {list = true, allow_holes = true, replaced_by = false,
instead = ("use an inline modifier on |%s= such as <q:...>, <qq:...>, <l:...> or <ll:...>"):format(replacement)
}
end
local function make_alias_replaced_by(replacement, reason)
return {list = true, disallow_holes = true, replaced_by = replacement, reason = reason or
"for consistency with the corresponding parameter in other Romance-language headword templates"
}
end
local function get_noun_params(is_proper)
return {
[1] = {list = "g", disallow_holes = true, required = not is_proper, default = "?", type = "genders",
flatten = true}, -- gender(s)
["g"] = {replaced_by = 1, reason = "for consistency"},
[2] = list_param, --plural override(s)
["pqual"] = make_qual_replaced_by("2"),
["f"] = list_param, --feminine form(s)
["fqual"] = make_qual_replaced_by("f"),
["m"] = list_param, --masculine form(s)
["fqual"] = make_qual_replaced_by("m"),
["dim"] = list_param, --diminutive(s)
["dimqual"] = make_qual_replaced_by("dim"),
["aug"] = list_param, --diminutive(s)
["pej"] = list_param, --pejorative(s)
["dem"] = list_param, --demonym(s)
["fdem"] = list_param, --female demonym(s)
}
end
local function do_noun(args, data, pos, is_proper)
local is_plurale_tantum = false
local has_singular = false
if pos == "Danh từ số đếm" then
pos = "Số từ"
insert(data.categories, 1, "Số đếm " .. langname)
end
if data.is_suffix then
pos = "Hậu tố"
end
local plpos = pos
validate_genders(args[1])
data.genders = args[1]
-- Check for specific genders and pluralia tantum.
for _, g in ipairs(args[1]) do
if type(g) == "table" then
g = g.spec
end
if g:find("-p$") then
is_plurale_tantum = true
else
has_singular = true
end
end
local lemma = data.pagename
-- Plural
local plurals = {}
local function insert_noun_inflection(terms, label, accel)
m_headword_utilities.insert_inflection {
headdata = data,
terms = terms,
label = label,
accel = accel and {form = accel} or nil,
}
end
if is_plurale_tantum and not has_singular then
if args[2][1] then
error("Can't specify plurals of plurale tantum " .. pos)
end
insert(data.inflections, {label = glossary_link("chỉ có số nhiều")})
else
plurals = m_headword_utilities.parse_term_list_with_modifiers {
paramname = {2, "pl"},
forms = args[2],
splitchar = ",",
}
-- Check for special plural signals
local mode = nil
local pl1 = plurals[1]
if pl1 and #pl1.term == 1 then
mode = pl1.term
if mode == "?" or mode == "!" or mode == "-" or mode == "~" then
pl1.term = nil
if m_headword_utilities.termobj_has_decorations(pl1) then
error(("Can't specify inline modifiers with plural code '%s'"):format(mode))
end
table.remove(plurals, 1) -- Remove the mode parameter
elseif mode ~= "+" and mode ~= "#" and not mode:find("[a-zA-Z]") then
error(("Unexpected plural code '%s'"):format(mode))
end
end
if is_plurale_tantum then
-- both singular and plural
insert(data.inflections, {label = "đôi khi " .. glossary_link("chỉ có số nhiều") .. ", trong biến thể"})
end
if mode == "?" then
-- Plural is unknown
insert(data.categories, plpos .. " số nhiều chưa được chứng thực hoặc không rõ " .. langname)
elseif mode == "!" then
-- Plural is not attested
insert(data.inflections, {label = "số nhiều chưa được chứng thực"})
insert(data.categories, plpos .. " số nhiều chưa được chứng thực " .. langname)
if plurals[1] then
error("Can't specify any plurals along with unattested plural code '!'")
end
elseif mode == "-" then
-- Uncountable noun; may occasionally have a plural
insert(data.categories, plpos .. " không đếm được " .. langname)
-- If plural forms were given explicitly, then show "usually"
if plurals[1] then
insert(data.inflections, {label = "thường " .. glossary_link("không đếm được")})
insert(data.categories, plpos .. " đếm được " .. langname)
else
insert(data.inflections, {label = glossary_link("không đếm được")})
end
else
-- Countable or mixed countable/uncountable
if not plurals[1] and not is_proper then
plurals[1] = {term = "+"}
end
if mode == "~" then
-- Mixed countable/uncountable noun, always has a plural
insert(data.inflections, {label = glossary_link("đếm được") .. " và " .. glossary_link("không đếm được")})
insert(data.categories, plpos .. " không " .. langname)
insert(data.categories, plpos .. " đếm được " .. langname)
elseif plurals[1] then
-- Countable nouns
insert(data.categories, plpos .. " đếm được " .. langname)
else
-- Uncountable nouns
insert(data.categories, plpos .. " không đếm được " .. langname)
end
end
-- Gather plurals, handling requests for default plurals.
for _, pl in ipairs(plurals) do
if pl.term == "+" then
pl.term = make_plural(lemma)
elseif pl.term:find("^%+") then
pl.term = require(romut_module).get_special_indicator(pl.term)
pl.term = make_plural(lemma, pl.term)
elseif pl.term == "s" or pl.term == "x" then
-- FIXME, convert to #s or #x
pl.term = lemma .. pl.term
else
pl.term = replace_hash_with_lemma(pl.term, lemma)
end
end
local pl1 = plurals[1]
if pl1 and not plurals[2] and pl1.term == lemma then
insert(data.inflections, {label = glossary_link("không biến cách"),
q = pl1.q, qq = pl1.qq, l = pl1.l, ll = pl1.ll, refs = pl1.refs
})
insert(data.categories, plpos .. " không biến cách " .. langname)
else
insert_noun_inflection(plurals, "số nhiều", "p")
end
if plurals[2] then
insert(data.categories, plpos .. " với nhiều dạng số nhiều " .. langname)
end
end
-- Gather masculines/feminines. For each one, generate the corresponding plural. `field` is the name of the field
-- containing the masculine or feminine forms (normally "m" or "f"); and `inflect` is a function of one or two
-- arguments to generate the default masculine or feminine from the lemma (the arguments are the lemma and
-- optionally a "special" flag to indicate how to handle multiword lemmas, and the function is normally
-- make_feminine or make_masculine [which doesn't exist, FIXME]).
local function handle_mf(field, inflect, default_plurals)
local function call_inflect(special)
if inflect then
-- Generate default feminine.
return inflect(lemma, special)
else
-- FIXME
error("Can't generate default masculine currently")
end
end
local mfs = m_headword_utilities.parse_term_list_with_modifiers {
paramname = field,
forms = args[field],
splitchar = ",",
frob = function(term)
if term == "+" then
-- Generate default masculine/feminine.
term = call_inflect()
elseif term == "e" and field == "f" then
-- FIXME: remove this special case
term = lemma .. "e"
else
term = replace_hash_with_lemma(term, lemma)
end
local special = require(romut_module).get_special_indicator(term)
if special then
term = call_inflect(special)
end
return term
end
}
return mfs
end
local feminines = handle_mf("f", make_feminine)
local masculine_plurals = {}
local masculines = handle_mf("m", nil)
local function parse_and_insert_noun_inflection(field, label, accel)
parse_and_insert_inflection(data, args, field, label, accel)
end
insert_noun_inflection(feminines, "giống cái", "f")
insert_noun_inflection(masculines, "giống đực")
parse_and_insert_noun_inflection("dim", "giảm nhẹ nghĩa")
parse_and_insert_noun_inflection("aug", "tăng kích")
parse_and_insert_noun_inflection("pej", "nghĩa xấu")
parse_and_insert_noun_inflection("dem", "tên gọi dân cư")
parse_and_insert_noun_inflection("fdem", "tên gọi dân cư dành cho nữ")
end
pos_functions["Danh từ"] = {
params = get_noun_params(),
func = function(args, data)
do_noun(args, data, "Danh từ")
end,
}
pos_functions["Danh từ riêng"] = {
params = get_noun_params("is proper"),
func = function(args, data)
do_noun(args, data, "Danh từ", "is proper")
end,
}
pos_functions["Danh từ số đếm"] = {
params = get_noun_params(),
func = function(args, data)
do_noun(args, data, "Danh từ số đếm")
end,
pos_category = "Số từ",
}
local function get_pronoun_pos(pos)
return {
params = {
[1] = {list = "g", disallow_holes = true, required = true, default = "?", type = "genders",
flatten = true}, -- gender(s)
["g"] = {replaced_by = 1, reason = "for consistency"},
["f"] = list_param,
["fqual"] = make_qual_replaced_by("f"),
["m"] = list_param,
["mqual"] = make_qual_replaced_by("m"),
["mv"] = list_param,
["mvqual"] = make_qual_replaced_by("mv"),
["fpl"] = list_param,
["fp"] = make_alias_replaced_by("fpl"),
["fpqual"] = make_qual_replaced_by("fpl"),
["mpl"] = list_param,
["mp"] = make_alias_replaced_by("mpl"),
["mpqual"] = make_qual_replaced_by("mpl"),
["pl"] = list_param,
["p"] = make_alias_replaced_by("pl"),
["pqual"] = make_qual_replaced_by("pl"),
["type"] = list_param,
},
func = function(args, data)
-- Validate and add genders.
validate_genders(args[1], "is pronoun")
data.genders = args[1]
local plpos = pos
local function parse_and_insert_pronoun_inflection(field, label, accel)
parse_and_insert_inflection(data, args, field, label, accel)
end
-- Parse and insert all inflections.
parse_and_insert_pronoun_inflection("m", "giống đực")
parse_and_insert_pronoun_inflection("mv", "nguyên âm trước ở giống đực số ít")
parse_and_insert_pronoun_inflection("f", "giống cái")
parse_and_insert_pronoun_inflection("mpl", "giống đực số nhiều")
parse_and_insert_pronoun_inflection("fpl", "giống cái số nhiều")
parse_and_insert_pronoun_inflection("pl", "số nhiều")
-- Categorize by "type"
for _, ty in ipairs(args.type) do
local category
if ty == "indefinite" then
category = "bất định"
elseif ty == "interrogative" then
category = "nghi vấn"
elseif ty == "personal" then
category = "nhân xưng"
elseif ty == "possessive" then
category = "sở hữu"
elseif ty == "reflexive" then
category = "phản thân"
elseif ty == "relative" then
category = "quan hệ"
end
if category then
if type(category) == "table" then
for _, cat in ipairs(category) do
insert(data.categories, "Đại từ " .. cat .. " " .. langname)
end
else
insert(data.categories, langname .. " " .. category .. " pronouns")
end
end
end
end
}
end
pos_functions["Đại từ"] = get_pronoun_pos("Đại từ")
pos_functions["Từ hạn định"] = get_pronoun_pos("Từ hạn định")
local function get_misc_pos()
return {
params = {
[1] = {replaced_by = "head", reason = "for consistency with other French headword templates"},
},
func = function(args, data)
end
}
end
pos_functions["Phó từ"] = get_misc_pos()
pos_functions["Giới từ"] = get_misc_pos()
pos_functions["Cụm từ"] = get_misc_pos()
pos_functions["Cụm giới từ"] = get_misc_pos()
pos_functions["Tục ngữ"] = get_misc_pos()
pos_functions["Dấu câu"] = get_misc_pos()
pos_functions["Dấu phụ"] = get_misc_pos()
pos_functions["Thán từ"] = get_misc_pos()
pos_functions["Tiền tố"] = get_misc_pos()
pos_functions["Từ viết tắt"] = get_misc_pos()
local function do_adjective(pos)
return {
params = {
[1] = true,
["inv"] = boolean_param,
["sp"] = true, -- special indicator: "first", "first-last", etc.
["onlyg"] = true,
["m"] = list_param,
["mqual"] = make_qual_replaced_by("m"),
["mv"] = list_param,
["mvqual"] = make_qual_replaced_by("mv"),
["f"] = list_param,
["fqual"] = make_qual_replaced_by("f"),
["mpl"] = list_param,
["mp"] = make_alias_replaced_by("mpl"),
["mpqual"] = make_qual_replaced_by("mpl"),
["fpl"] = list_param,
["fp"] = make_alias_replaced_by("fpl"),
["fpqual"] = make_qual_replaced_by("fpl"),
["pl"] = list_param,
["p"] = make_alias_replaced_by("pl"),
["pqual"] = make_qual_replaced_by("pl"),
["base"] = list_param,
["current"] = make_alias_replaced_by("base", "because the old name was obscure and did not clarify the purpose of the parameter"),
["comp"] = list_param,
["compqual"] = make_qual_replaced_by("comp"),
["sup"] = list_param,
["supqual"] = make_qual_replaced_by("sup"),
["intr"] = boolean_param,
},
pos_category = pos == "Tính từ số đếm" and "Số từ" or nil,
redlink_pos = pos == "Phân từ" and "Phân từ" or nil,
func = function(args, data)
local is_numeral = false
if pos == "Tính từ số đếm" then
pos = "Số từ"
is_numeral = true
insert(data.categories, 1, "Số đếm " .. langname)
end
if data.is_suffix then
pos = "Hậu tố"
end
local plpos = pos
if not is_numeral then
if args.onlyg == "p" or args.onlyg == "m-p" or args.onlyg == "f-p" then
insert(data.categories, "Danh từ chỉ có số nhiều " .. langname)
end
if args.onlyg == "s" or args.onlyg == "f-s" or args.onlyg == "f-s" then
insert(data.categories, "Danh từ chỉ có số ít " .. langname)
end
if args.onlyg then
insert(data.categories, plpos .. " khiếm khuyết " .. langname)
end
end
local lemma = data.pagename
local function insert_inflection(terms, label, accel)
m_headword_utilities.insert_inflection {
headdata = data,
terms = terms,
label = label,
accel = accel and {form = accel} or nil,
}
end
local function process_inflection(label, arg, accel, get_default, explicit_default_only)
local orig_infls = m_headword_utilities.parse_term_list_with_modifiers {
paramname = arg,
forms = args[arg],
splitchar = ",",
}
if not orig_infls[1] then
if explicit_default_only or not get_default then
orig_infls = {}
else
orig_infls = {{term = "+"}}
end
end
local infls = {}
if orig_infls[1] then
for _, infl in ipairs(orig_infls) do
if infl.term == "+" then
local defs
if get_default then
defs = get_default(infl)
else
error("Can't use '+' with " .. arg .. "=; no default available")
end
for _, def in ipairs(defs) do
insert(infls, def)
end
elseif infl.term == "e" or infl.term == "s" or infl.term == "x" then
-- FIXME: delete this in favor of #e, #s, #x
infl.term = lemma .. infl.term
insert(infls, infl)
else
infl.term = replace_hash_with_lemma(infl.term, lemma)
insert(infls, infl)
end
end
insert_inflection(infls, label, accel)
end
return infls
end
if args.sp and not require(romut_module).allowed_special_indicators[args.sp] then
local indicators = {}
for indic, _ in pairs(require(romut_module).allowed_special_indicators) do
insert(indicators, "'" .. indic .. "'")
end
sort(indicators)
error("Special inflection indicator beginning can only be " ..
mw.text.listToText(indicators) .. ": " .. args.sp)
end
local function get_base()
return args.base[1] and args.base or {lemma}
end
if args.onlyg == "p" then
insert(data.inflections, {label = "chỉ có số nhiều"})
if args[1] ~= "mf" then
-- Handle feminine plurals
process_inflection("giống cái số nhiều", "fpl", "f|p")
end
elseif args.onlyg == "s" then
insert(data.inflections, {label = "chỉ có số ít"})
if not (args[1] == "mf" or not args.f[1] and lemma:match("e$")) then
-- Handle feminines
process_inflection("giống cái số ít", "f", "f", function(defobj)
return make_inflection(get_base(), defobj, make_feminine, args.sp)
end)
end
elseif args.onlyg == "m" then
insert(data.genders, "m")
insert(data.inflections, {label = "chỉ có giống đực"})
-- Handle masculine plurals
process_inflection("giống đực số nhiều", "mpl", "m|p", function(defobj)
return make_inflection(get_base(), defobj, make_plural, args.sp)
end)
elseif args.onlyg == "f" then
insert(data.genders, "f")
insert(data.inflections, {label = "chỉ có giống cái"})
-- Handle feminine plurals
process_inflection("giống cái số nhiều", "fpl", "f|p", function(defobj)
return make_inflection(get_base(), defobj, make_plural, args.sp)
end)
elseif args.onlyg then
insert(data.genders, args.onlyg)
insert(data.inflections, {label = "khiếm khuyết"})
else
-- Gather genders
local gender = args[1]
-- Default to mf if base form ends in -e and no feminine,
-- feminine plural or gender specified
if not gender and not args.f[1] and not args.fpl[1] and lemma:match("e$")
and not lemma:find(" ", nil, true) then
gender = "mf"
end
if args.intr then
insert(data.inflections, {label = glossary_link("nội động từ")})
insert(data.inflections, {label = glossary_link("không biến cách")})
args.inv = true
elseif args.inv then
insert(data.inflections, {label = glossary_link("không biến cách")})
end
-- Handle plurals of mf adjectives
if not args.inv and gender == "mf" then
process_inflection("số nhiều", "pl", "p", function(defobj)
return make_inflection(get_base(), defobj, make_plural, args.sp)
end)
end
if not args.inv and gender ~= "mf" then
-- Handle masculine form if not same as lemma; e.g. [[sûr de soi]] with m=+, m2=sûr de lui
process_inflection("giống đực số ít", "m", "m|s",
function(defobj)
defobj = m_table.shallowCopy(defobj)
defobj.term = lemma
return {defobj}
end, "explicit default only")
-- Handle case of special masculine singular before vowel
process_inflection("nguyên âm trước ở giống đực số ít", "mv", "m|s")
-- Handle feminines
local feminines = process_inflection("giống cái", "f", "f|s", function(defobj)
return make_inflection(get_base(), defobj, make_feminine, args.sp)
end)
-- Handle masculine plurals
process_inflection("giống đực số nhiều", "mpl", "m|p", function(defobj)
return make_inflection(get_base(), defobj, make_plural, args.sp)
end)
-- Handle feminine plurals
process_inflection("giống cái số nhiều", "fpl", "f|p", function(defobj)
return make_inflection(feminines, defobj, make_plural, args.sp)
end)
end
end
-- Handle comparatives
process_inflection("so sánh hơn", "comp", "so sánh hơn")
-- Handle superlatives
process_inflection("so sánh nhất", "sup", "so sánh nhất")
end
}
end
pos_functions["Tính từ"] = do_adjective("Tính từ")
pos_functions["Phân từ quá khứ"] = do_adjective("Phân từ")
pos_functions["Tính từ số đếm"] = do_adjective("cardinal adjective")
pos_functions["Động từ"] = {
params = {
["type"] = list_param,
},
func = function(args, data)
local pos = "Động từ"
for _, ty in ipairs(args.type) do
local category, label
if ty == "auxiliary" then
category = "trợ động từ"
elseif ty == "defective" then
category = "khiếm khuyết"
label = glossary_link("defective")
elseif ty == "impersonal" then
category = "không ngôi"
label = glossary_link("impersonal")
elseif ty == "modal" then
category = "khiếm khuyết"
elseif ty == "reflexive" then
category = "phản thân"
elseif ty == "transitive" then
label = glossary_link("ngoại động từ")
category = "goại động từ"
elseif ty == "intransitive" then
label = glossary_link("nội động từ")
category = "nội động từ"
elseif ty == "ambitransitive" or ty == "ambi" then
category = {"ngoại động từ", "nội động từ"}
label = glossary_link("ngoại động từ") .. " và " .. glossary_link("nội động từ")
end
if category then
if type(category) == "table" then
for _, cat in ipairs(category) do
insert(data.categories, pos .. " " .. cat .. " " .. langname)
end
else
insert(data.categories, pos .. " " .. category .. " " .. langname)
end
end
if label then
insert(data.inflections, {label = label})
end
end
end
}
pos_functions["Số đếm không biến cách"] = {
params = {},
func = function(args, data)
data.pos_category = "Số từ"
insert(data.categories, "Số đếm " .. langname)
insert(data.categories, "Số đếm không biến cách" .. langname)
insert(data.inflections, {label = glossary_link("không biến cách")})
end
}
-----------------------------------------------------------------------------------------
-- Suffix forms --
-----------------------------------------------------------------------------------------
pos_functions["Biến thể hình thái hậu tố"] = {
params = {
[1] = {required = true, list = true, disallow_holes = true},
["g"] = {list = true, disallow_holes = true, type = "genders", flatten = true},
},
func = function(args, data)
validate_genders(args.g)
data.genders = args.g
local suffix_type = {}
for _, typ in ipairs(args[1]) do
insert(suffix_type, "dang hậu tố " .. typ)
end
insert(data.inflections, {label = "dạng biến thể hình thái của " .. m_table.serialCommaJoin(suffix_type, {conj = "hoặc"})})
end,
}
return export
hsaiiuzln3h3moqrd0e05i1oc386mr6
auld
0
262717
2573943
2188863
2026-10-28T01:33:01Z
Hiyuune
50834
2573943
wikitext
text/x-wiki
{{also|Auld}}
=={{langname|en}}==
==={{sectionalt}}===
* {{alt|en|aud|aul|oul|oul'|ould}}
==={{section|etym}}===
{{root|en|ine-pro|*h₂el- (grow)}}
Từ {{bor|en|sco|auld}} hoặc {{inh|en|enm-nor|auld}}, {{m|enm|aulde}}, {{m|enm|awld}}, {{m|enm|awlde}}, {{m|enm|ald}}, {{m|enm|alde}}, từ {{der|en|ang-nor|ald}}, biến thể của {{der|en|ang|eald}}, từ {{inh|en|gmw-pro|*ald}}, từ {{inh|en|gem-pro|*aldaz}}, từ {{inh|en|ine-pro|*h₂el-|*h₂eltós}}. So sánh cùng gốc với {{cog|la|altus}}. {{doublet|en|old}}.
==={{section|pron}}===
* {{IPA|en|/oʊld/|a=US}}
** {{audio|en|En-us-auld.oga|a=GA}}
* {{IPA|en|/əʊld/|a=UK}}
* {{IPA|en|/ɑːld/|a=cot-caught}}
* {{IPA|en|/aːʊl/|a=Liverpool}}
* {{rhymes|en|əʊld|s=1}}
* {{homophone|en|old}}
==={{section|adj}}===
{{en-adj|er}}
# {{lb|en|archaic outwith|_|Northern England|Liverpool|Scotland|Ireland}} [[cũ|Cũ]].
===={{section|syn}}====
* {{l|en|aged}}, {{l|en|eldern}}, {{l|en|hoary}}; xem thêm [[Kho từ vựng:old]]
===={{section|rel}}====
{{col4|en
|alderman
|auld lang syne
|auld birkie
|auld wives' tongues
|auld-fashioned
|auldfarrand
|auld-warld
|aulden
|eld
|elder
|old}}
==={{section|further}}===
* {{R:OneLook}}
* {{R:Century}}
* {{R:Webster 1913}}
* {{R:Dictionary.com}}
* {{R:New English Dictionary|volume=I|part=1|page=564|column=2}}
* {{R:Century|year=1914|volume=I|entry=auld|page=381|column=1}}
==={{section|ana}}===
* {{anagrams|en|a=adlu|Daul|Dula|dual|laud|udal}}
=={{langname|sco}}==
==={{section|alt}}===
* {{alt|sco|ald|aald|aal|aul'|aul|ould}}
==={{section|etym}}===
{{root|sco|ine-pro|*h₂el- (grow)}}
Từ {{der|sco|enm-nor|auld}}, {{m|enm|aulde}}, {{m|enm|awld}}, {{m|enm|awlde}}, {{m|enm|ald}}, {{m|enm|alde}}, từ {{inh|sco|ang-nor|ald}}, biến thể của {{der|sco|ang|eald}}, từ {{inh|sco|gmw-pro|*ald}}, từ {{inh|sco|gem-pro|*aldaz}}, từ {{inh|sco|ine-pro|*h₂el-|*h₂eltós}}. So sánh cùng gốc với {{cog|la|altus}}.
==={{section|pron}}===
* {{IPA|sco|/ɔl(d)/|a=Central Scots,Southern Scots}}
* {{IPA|sco|/al(d)/|a=Doric Scots,Southern Scots,''cat''-''caught'' merger}}
==={{section|adj}}===
{{sco-adj|aulder|auldest}}
# [[cũ|Cũ]].
===={{section|drv}}====
{{col4|sco
|Auld Clootie
|Auld Enemy
|Auld Reekie
|auld-warld
|aulden
|auldness
|auld manny
|auldrife
|auld-fashioned
|auld-farrant
|auld lang syne}}
==={{section|further}}===
* {{R:Lexico}}
* {{R:DSL}}
9fu52t9b74q5qxeim7zzq1mlgl4fms0
2573944
2573943
2026-10-28T01:33:25Z
Hiyuune
50834
2573944
wikitext
text/x-wiki
{{also|Auld}}
=={{langname|en}}==
==={{sectionalt}}===
* {{alt|en|aud|aul|oul|oul'|ould}}
==={{section|etym}}===
{{root|en|ine-pro|*h₂el- (grow)}}
Từ {{bor|en|sco|auld}} hoặc {{inh|en|enm-nor|auld}}, {{m|enm|aulde}}, {{m|enm|awld}}, {{m|enm|awlde}}, {{m|enm|ald}}, {{m|enm|alde}}, từ {{der|en|ang-nor|ald}}, biến thể của {{der|en|ang|eald}}, từ {{inh|en|gmw-pro|*ald}}, từ {{inh|en|gem-pro|*aldaz}}, từ {{inh|en|ine-pro|*h₂el-|*h₂eltós}}. So sánh cùng gốc với {{cog|la|altus}}. {{doublet|en|old}}.
==={{section|pron}}===
* {{IPA|en|/oʊld/|a=US}}
** {{audio|en|En-us-auld.oga|a=GA}}
* {{IPA|en|/əʊld/|a=UK}}
* {{IPA|en|/ɑːld/|a=cot-caught}}
* {{IPA|en|/aːʊl/|a=Liverpool}}
* {{rhymes|en|əʊld|s=1}}
* {{homophone|en|old}}
==={{section|adj}}===
{{en-adj|er}}
# {{lb|en|archaic|bên ngoài|_|Northern England|Liverpool|Scotland|Ireland}} [[cũ|Cũ]].
===={{section|syn}}====
* {{l|en|aged}}, {{l|en|eldern}}, {{l|en|hoary}}; xem thêm [[Kho từ vựng:old]]
===={{section|rel}}====
{{col4|en
|alderman
|auld lang syne
|auld birkie
|auld wives' tongues
|auld-fashioned
|auldfarrand
|auld-warld
|aulden
|eld
|elder
|old}}
==={{section|further}}===
* {{R:OneLook}}
* {{R:Century}}
* {{R:Webster 1913}}
* {{R:Dictionary.com}}
* {{R:New English Dictionary|volume=I|part=1|page=564|column=2}}
* {{R:Century|year=1914|volume=I|entry=auld|page=381|column=1}}
==={{section|ana}}===
* {{anagrams|en|a=adlu|Daul|Dula|dual|laud|udal}}
=={{langname|sco}}==
==={{section|alt}}===
* {{alt|sco|ald|aald|aal|aul'|aul|ould}}
==={{section|etym}}===
{{root|sco|ine-pro|*h₂el- (grow)}}
Từ {{der|sco|enm-nor|auld}}, {{m|enm|aulde}}, {{m|enm|awld}}, {{m|enm|awlde}}, {{m|enm|ald}}, {{m|enm|alde}}, từ {{inh|sco|ang-nor|ald}}, biến thể của {{der|sco|ang|eald}}, từ {{inh|sco|gmw-pro|*ald}}, từ {{inh|sco|gem-pro|*aldaz}}, từ {{inh|sco|ine-pro|*h₂el-|*h₂eltós}}. So sánh cùng gốc với {{cog|la|altus}}.
==={{section|pron}}===
* {{IPA|sco|/ɔl(d)/|a=Central Scots,Southern Scots}}
* {{IPA|sco|/al(d)/|a=Doric Scots,Southern Scots,''cat''-''caught'' merger}}
==={{section|adj}}===
{{sco-adj|aulder|auldest}}
# [[cũ|Cũ]].
===={{section|drv}}====
{{col4|sco
|Auld Clootie
|Auld Enemy
|Auld Reekie
|auld-warld
|aulden
|auldness
|auld manny
|auldrife
|auld-fashioned
|auld-farrant
|auld lang syne}}
==={{section|further}}===
* {{R:Lexico}}
* {{R:DSL}}
o53kg9pwd42mh5yz1u4jstsna82uww0
Philippines
0
266893
2573906
2573547
2026-10-27T15:46:08Z
Hiyuune
50834
(By [[meta:Indic-TechCom/Tools|FindAndReplace]])
2573906
wikitext
text/x-wiki
{{also|philippines}}
=={{langname|vi}}==
[[File:Flag of the Philippines.svg|thumb|240px|Quốc kỳ '''Philippines''']]
{{wp}}
{{commonscat}}
==={{section|alt}}===
* {{alt|vi|Phi-líp-pin||phiên âm}}
==={{section|pron}}===
{{vi-pron|Phi-líp-pin}}
==={{section|pr-noun}}===
{{vi-pr-noun}}
# {{senseid|vi|Q928}}{{place|en|quốc gia|r/Đông Nam Á|capital=Manila|official=Cộng hòa Philippines}}
===={{section|trans}}====
{{trans-top|một quốc gia ở Đông Nam Á}}
* {{dịch|ar}}: {{t+|ar|الْفِلِبِّين|f}}
* {{dịch|ab}}: {{t|ab|Филиппин}}
* {{dịch|af}}: {{t|af|Filippyne}}
* {{dịch|ak}}: {{t|akl|Pilipinas}}
* {{dịch|sq}}: {{t+|sq|Filipinet|p}}
* {{dịch|am}}: {{t|am|ፊሊፒንስ}}
* {{dịch|en}}: {{t+|en|Philippines}}
* {{dịch|hy}}: {{t|hy|Ֆիլիպիններ}}
* {{dịch|as}}: {{t|as|ফিলিপাইন}}
* {{dịch|ast}}: {{t+|ast|Filipines|f-p}}
* {{dịch|az}}:
*: Ả Rập: {{t|az|فیلیپین|tr=filippin}}
*: Latinh: {{t+|az|Filippin}}
* {{dịch|be}}: {{t|be|Філіпі́ны|f-p}}
* {{dịch|bn}}: {{t+|bn|ফিলিপাইন}}
* Tiếng Bikol:
*: {{langname|bcl}}: {{t+|bcl|Filipinas}}
* {{dịch|br}}: {{t|br|Filipinez}}
* {{dịch|bg}}: {{t+|bg|Филипи́ни|p}}
* {{dịch|ca}}: {{t+|ca|Filipines|f-p}}
* {{dịch|hi}}: {{t+|hi|फ़िलीपीन्स|m}}, {{t|hi|फिलीपिंस|m}}
* {{dịch|hu}}: {{t+|hu|Fülöp-szigetek|p}}
* {{dịch|ga}}: {{t|ga|Oileáin Fhilipíneacha|alt=Na hOileáin Fhilipíneacha|m-p}}
* {{dịch|my}}: {{t|my|ဖိလစ်ပိုင်}}
* {{dịch|ja}}: {{t+|ja|フィリピン|tr=Firipin}}, {{t+|ja|比律賓|tr=フィリピン, Firipin}} {{q|không còn dùng}}
* {{dịch|sa}}: {{t|sa|फिलिपीन्स}}
* {{dịch|shn}}: {{t|shn|မိူင်းၾီႇလိပ်ႈပိၼ်း}}
* {{dịch|tl}}: {{t+|tl|Pilipinas}}
* {{dịch|zh}}:
*: {{langname|nan-hbl}}: {{t|nan-hbl|菲律賓|tr=Hui-li̍p-pin}}
*: {{langname|cmn}}: {{t+|cmn|菲律賓}}
*: {{langname|yue}}: {{t|yue|菲律賓}}
*: {{langname|wuu}}: {{t|wuu|菲律賓}}
{{trans-bottom}}
=={{langname|en}}==
==={{section|etym}}===
{{word|en|ine|h₁éḱwos}}
{{ety|en|:bor|fr:Philippines}}
{{rfe|en}}
==={{section|pron}}===
* {{enPR|fĭlʹĭ-pēnz|a=UK}}, {{IPA|en|/ˈfɪl.ɪ.piːnz/|/ˈfɪl.ə.piːnz/|/ˈfɪl.ɪ.paɪnz/|a3=không còn dùng|ref3={{R:en:Ross:1970|page=141}}}}
** {{audio|en|LL-Q1860 (eng)-Soundguys-Philippines.wav|a=UK}}
* {{enPR|fĭlʹə-pēnz|a=US}}, {{IPA|en|/ˈfɪl.ə.piːnz/}}
** {{audio|en|En-us-Philippines.ogg|a=US}}
* {{IPA|en|/ˈfi.lip.pins/|/ˈfi.li.pins/|a=Philippines}}
** {{IPA|en|/ˈpi.lip.pins/|/ˈpi.li.pins/|a=Philippines,thông tục}}
* {{hyphenation|en|Phi|lip|pines}}
* {{hyphenation|en|Phi|li|ppines}}
* {{hyphenation|en|Phil|i|ppines}}
==={{section|pr-noun}}===
{{wp|en:}}
{{en-prop|def=1}}
# {{place|en|quần đảo|r/Đông Nam Á}}.
# {{tcl|en|Philippines|id=Q928}}
===={{section|syn}}====
* {{l|en|Filippines}}
* {{l|en|Pilipinas}}
* {{l|en|Pinas}}
===={{section|drv}}====
{{col|en|Rice Bowl of the Philippines|Christmas Capital of the Philippines|Culinary Capital of the Philippines|Salad Bowl of the Philippines|Summer Capital of the Philippines| Goodbye Philippines|Autoparts Capital of the Philippines}}
===={{section|rel}}====
{{col|en
|Filipina
|Filipinas
|Philippine
|Phils
|PI
|Tagalog
}}
==={{section|ref}}===
{{reflist}}
=={{langname|fr}}==
==={{section|etym}}===
{{ety|fr|:af|Philippe|-ine|-s|:calque|es:Filipinas}}
{{rfe|fr}}
==={{section|pron}}===
* {{fr-IPA}}
* {{audio|fr|LL-Q150 (fra)-GrandCelinien-Philippines.wav|a=Paris}}
==={{section|pr-noun}}===
{{fr-proper noun|f-p}}
# {{tcl|fr|Philippines|id=Q928}}
===={{section|drv}}====
* {{l|fr|philippin}}
* {{l|fr|Philippin}}, {{l|fr|Philippine}}, {{l|fr|Philippins}}, {{l|fr|Philippines}}
===={{section|desc}}====
* {{desc|ar|الْفِلِبِّين|bor=1}}
* {{desc|en|Philippines|bor=1}}
* {{desc|fa|فیلیپین|tr=filipin|bor=1}}
* {{desc|vi|Phi-líp-pin|bor=1}}
===={{section|see}}====
{{list:quốc gia của châu Á/fr}}
==={{section|n}}===
{{head|fr|Biến thể hình thái danh từ|g=f}}
# {{plural of|fr|Philippine}}
motypvlm32otz0qumevze65rhwx0z04
absʔalʔal
0
269491
2573861
2069775
2026-10-27T12:50:26Z
Hiyuune
50834
2573861
wikitext
text/x-wiki
=={{langname|lut}}==
==={{section|etym}}===
{{affix|lut|abs-|ʔalʔal|t1=có|t2=ngôi nhà}}
==={{section|v}}===
{{head|lut|Động từ}}
# [[có một|Có một]] [[ngôi nhà]].
dkb043fybsx7c54pntm8g6t2hvqxrzg
Mô đun:languages/data/3/a
828
270567
2573904
2573003
2026-10-27T15:45:53Z
Lcsnes
40261
2573904
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["aaa"] = {
"tiếng Ghotuo",
35463,
"alv-yek",
"Latn",
"Tiếng Ghotuo",
"Ghotuo",
}
m["aab"] = {
"tiếng Alumu-Tesu",
35034,
"nic-alu",
"Latn",
"Tiếng Alumu-Tesu",
"Alumu-Tesu",
}
m["aac"] = {
"tiếng Ari",
1811224,
"paa-pag",
"Latn",
"Tiếng Ari",
"Ari",
}
m["aad"] = {
"tiếng Amal",
56708,
"paa-iwm",
"Latn",
"Tiếng Amal",
"Amal",
}
-- "aae" IS TREATED AS "sq", SEE WT:LT
m["aaf"] = {
"tiếng Aranadan",
3507928,
"dra-mal",
"Mlym",
"Tiếng Aranadan",
"Aranadan",
translit = {Mlym = "ml-translit"},
}
m["aag"] = {
"tiếng Ambrak",
4741706,
"qfa-tor",
"Latn",
"Tiếng Ambrak",
"Ambrak",
}
m["aah"] = {
"Abu' Arapesh",
4670715,
"qfa-tor",
"Latn",
}
m["aai"] = {
"Arifama-Miniafia",
4790560,
"poz-ocw",
"Latn",
}
m["aak"] = {
"tiếng Ankave",
3446690,
"ngf",
"Latn",
"Tiếng Ankave",
"Ankave",
}
m["aal"] = {
"tiếng Afade",
56434,
"cdc-cbm",
"Latn",
"Tiếng Afade",
"Afade",
}
m["aan"] = {
"Anambé",
3507873,
"tup-gua",
"Latn",
}
m["aap"] = {
"Pará Arára",
56807,
"sai-pek",
"Latn",
}
m["aaq"] = {
"tiếng Đông Abenaki",
3515185,
"alg-abp",
"Latn",
"Tiếng Đông Abenaki",
"Đông Abenaki",
}
m["aas"] = {
"tiếng Aasax",
56620,
"cus-sou",
"Latn",
"Tiếng Aasax",
"Aasax",
}
-- "aat" IS TREATED AS "sq", SEE WT:LT
m["aau"] = {
"tiếng Abau",
3073568,
"paa-spk",
"Latn",
"Tiếng Abau",
"Abau",
}
m["aaw"] = {
"tiếng Solong",
7558834,
"poz-ocw",
"Latn",
"Tiếng Solong",
"Solong",
}
m["aax"] = {
"Mandobo Atas",
12636156,
"ngf",
"Latn",
}
m["aaz"] = {
"tiếng Amarasi",
4740192,
"poz-tim",
"Latn",
"Tiếng Amarasi",
"Amarasi",
}
m["aba"] = {
"Abé",
34833,
"alv-lag",
"Latn",
}
m["abb"] = {
"tiếng Bankon",
34860,
"bnt-bsa",
"Latn",
"Tiếng Bankon",
"Bankon",
}
m["abc"] = {
"Ambala Ayta",
3448896,
"phi",
"Latn",
}
m["abd"] = {
"Camarines Norte Agta",
3399682,
"phi",
"Latn",
}
m["abe"] = {
"tiếng Tây Abenaki",
17502788,
"alg-abp",
"Latn",
"Tiếng Tây Abenaki",
"Tây Abenaki",
}
m["abf"] = {
"Abai Sungai",
4663287,
"poz-san",
"Latn",
}
m["abg"] = {
"tiếng Abaga",
3507954,
"paa-kag",
"Latn",
"Tiếng Abaga",
"Abaga",
}
m["abh"] = {
"tiếng Ả Rập Tajikistan",
56833,
"sem-arb",
"Arab",
"Tiếng Ả Rập Tajikistan",
"Ả Rập Tajikistan",
entry_name = "ar-entryname",
}
m["abi"] = {
"tiếng Abidji",
34781,
"alv-lag",
"Latn",
"Tiếng Abidji",
"Abidji",
}
m["abj"] = {
"Aka-Bea",
2356391,
"qfa-ads",
"Latn",
}
m["abl"] = {
"Abung",
49215,
"poz-lgx",
"Latn",
}
m["abm"] = {
"tiếng Abanyom",
7502,
"nic-eko",
"Latn",
"Tiếng Abanyom",
"Abanyom",
}
m["abn"] = {
"Abua",
34835,
"nic-cde",
"Latn",
}
m["abo"] = {
"tiếng Abon",
35121,
"nic-tvn",
"Latn",
"Tiếng Abon",
"Abon",
}
m["abp"] = {
"Abenlen Ayta",
3436621,
"phi",
"Latn",
}
m["abq"] = {
"tiếng Abaza",
27567,
"cau-abz",
"Cyrl, Latn",
"Tiếng Abaza",
"Abaza",
translit = {Cyrl = "abq-translit"},
override_translit = true,
display_text = {Cyrl = s["cau-Cyrl-displaytext"]},
entry_name = {
Cyrl = s["cau-Cyrl-entryname"],
Latn = s["cau-Latn-entryname"],
},
sort_key = {
Cyrl = {
from = {
"гъв", "гъь", "гӏв", "джв", "джь", "къв", "къь", "кӏв", "кӏь", "хъв", "хӏв", "чӏв", -- 3 chars
"гв", "гъ", "гь", "гӏ", "дж", "дз", "ё", "жв", "жь", "кв", "къ", "кь", "кӏ", "ль", "лӏ", "пӏ", "тл", "тш", "тӏ", "фӏ", "хв", "хъ", "хь", "хӏ", "цӏ", "чв", "чӏ", "шв", "шӏ" -- 2 chars
},
to = {
"г" .. p[3], "г" .. p[4], "г" .. p[7], "д" .. p[2], "д" .. p[3], "к" .. p[3], "к" .. p[4], "к" .. p[7], "к" .. p[8], "х" .. p[3], "х" .. p[6], "ч" .. p[3],
"г" .. p[1], "г" .. p[2], "г" .. p[5], "г" .. p[6], "д" .. p[1], "д" .. p[4], "е" .. p[1], "ж" .. p[1], "ж" .. p[2], "к" .. p[1], "к" .. p[2], "к" .. p[5], "к" .. p[6], "л" .. p[1], "л" .. p[2], "п" .. p[1], "т" .. p[1], "т" .. p[2], "т" .. p[3], "ф" .. p[1], "х" .. p[1], "х" .. p[2], "х" .. p[4], "х" .. p[5], "ц" .. p[1], "ч" .. p[1], "ч" .. p[2], "ш" .. p[1], "ш" .. p[2]
}
},
},
}
m["abr"] = {
"tiếng Abron",
34831,
"alv-ctn",
"Latn",
"Tiếng Abron",
"Abron",
ancestors = "ak",
}
m["abs"] = {
"tiếng Mã Lai Ambon",
3124354,
"crp",
"Latn",
"Tiếng Mã Lai Ambon",
"Mã Lai Ambon",
ancestors = "ms",
}
m["abt"] = {
"tiếng Ambulas",
3508015,
"paa-spk",
"Latn",
"Tiếng Ambulas",
"Ambulas",
}
m["abu"] = {
"tiếng Abure",
34767,
"alv-ptn",
"Latn",
"Tiếng Abure",
"Abure",
}
m["abv"] = {
"Baharna Arabic",
56576,
"sem-arb",
"Arab",
entry_name = "ar-entryname",
}
m["abw"] = {
"Pal",
7126121,
"ngf-mad",
"Latn",
}
m["abx"] = {
"Inabaknon",
2820163,
"poz-sbj",
"Latn",
}
m["aby"] = {
"Aneme Wake",
3508107,
"ngf",
"Latn",
}
m["abz"] = {
"tiếng Abui",
2822110,
"qfa-tap",
"Latn",
"Tiếng Abui",
"Abui",
}
m["aca"] = {
"tiếng Achawa",
2822982,
"awd",
"Latn",
"Tiếng Achawa",
"Achawa",
}
m["acb"] = {
"Áncá",
11130787,
"nic-mom",
"Latn",
}
m["acd"] = {
"Gikyode",
35256,
"alv-gng",
"Latn",
}
m["ace"] = {
"tiếng Aceh",
27683,
"cmc",
"Latn, ms-Arab",
"Tiếng Aceh",
"Aceh",
standardChars = {
Latn = "AaBbCcDdEeÉéÈèËëFfGgHhIiJjKkLlMmNnOoÔôÖöPpQqRrSsTtUuVvWwXxYyZz", -- current orthography (not yet add Arab)
c.punc
},
}
m["ach"] = {
"tiếng Acholi",
34926,
"sdv-los",
"Latn",
"Tiếng Acholi",
"Acholi",
}
m["aci"] = {
"Aka-Cari",
2670418,
"qfa-adn",
"Latn",
}
m["ack"] = {
"Aka-Kora",
3433680,
"qfa-adn",
"Latn",
}
m["acl"] = {
"Akar-Bale",
3436825,
"qfa-ads",
"Latn",
}
m["acm"] = {
"tiếng Ả Rập Iraq",
56232,
"sem-arb",
"Arab",
"Tiếng Ả Rập Iraq",
"Ả Rập Iraq",
entry_name = "ar-entryname",
}
m["acn"] = {
"tiếng A Xương",
56582,
"tbq-brm",
"Latn",
"Tiếng A Xương",
"A Xương",
}
m["acp"] = {
"tiếng Đông Acipa",
5329945,
"nic-kmk",
"Latn",
"Tiếng Đông Acipa",
"Đông Acipa",
}
m["acr"] = {
"tiếng Achi",
34774,
"myn",
"Latn",
"Tiếng Achi",
"Achi",
}
m["acs"] = {
"Acroá",
2829146,
"sai-cje",
"Latn",
}
m["acu"] = {
"tiếng Achuar",
2823170,
"sai-jiv",
"Latn",
"Tiếng Achuar",
"Achuar",
}
m["acv"] = {
"tiếng Achumawi",
56661,
"nai-pal",
"Latn",
"Tiếng Achumawi",
"Achumawi",
}
m["acw"] = {
"tiếng Ả Rập Hijazi",
56608,
"sem-arb",
"Arab",
"Tiếng Ả Rập Hijazi",
"Ả Rập Hijazi",
entry_name = "ar-entryname",
}
m["acx"] = {
"tiếng Ả Rập Oman",
56630,
"sem-arb",
"Arab",
"Tiếng Ả Rập Oman",
"Ả Rập Oman",
entry_name = "ar-entryname",
}
m["acy"] = {
"tiếng Ả Rập Síp",
56416,
"sem-arb",
"Latn, Grek",
"Tiếng Ả Rập Síp",
"Ả Rập Síp",
ancestors = "acm",
standardChars = {
Latn = "AaBbCcDdΔδEeFfGgĠġĊċIiJjKkLlMmNnOoPpΘθRrSsTtUuVvWwXxYyZzŞş",
c.punc
},
entry_name = {remove_diacritics = c.grave .. c.acute .. c.breve},
}
m["acz"] = {
"tiếng Acheron",
34769,
"alv-tal",
"Latn",
"Tiếng Acheron",
"Acheron",
}
m["ada"] = {
"tiếng Adangme",
35141,
"alv-gda",
"Latn",
"Tiếng Adangme",
"Adangme",
}
m["adb"] = { -- rename or remove, see RFM
"Adabe",
36872,
nil,
"Latn",
}
m["add"] = {
"Dzodinka",
35266,
"nic-nka",
"Latn",
}
m["ade"] = {
"Adele",
27740,
"alv-ntg",
"Latn",
}
m["adf"] = {
"Dhofari Arabic",
56565,
"sem-arb",
"Arab",
entry_name = "ar-entryname",
}
m["adg"] = {
"Andegerebinha",
3508123,
"aus-pam",
"Latn",
}
m["adh"] = {
"Adhola",
1971400,
"sdv-los",
"Latn",
}
m["adi"] = {
"tiếng Adi",
56440,
"sit-tan",
"Latn",
"Tiếng Adi",
"Adi",
}
m["adj"] = {
"tiếng Adjukru",
34738,
"alv-lag",
"Latn",
}
m["adl"] = {
"tiếng Galo",
2857892,
"sit-tan",
"Latn",
"Tiếng Galo",
"Galo",
}
m["adn"] = {
"Adang",
3398276,
"qfa-tap",
"Latn",
}
m["ado"] = {
"Abu",
56659,
"paa-ram",
"Latn",
}
m["adp"] = {
"Adap",
3512402,
"sit-tib",
"Tibt",
ancestors = "dz",
translit = "Tibt-translit",
override_translit = true,
display_text = s["Tibt-displaytext"],
entry_name = s["Tibt-entryname"],
sort_key = "Tibt-sortkey",
wikipedia_article = "Dzongkha", -- Considered a dialect of Dzongkha
}
m["adq"] = {
"Adangbe",
34730,
"alv-gda",
"Latn",
ancestors = "ada",
}
m["adr"] = {
"Adonara",
4684505,
"poz-cet",
"Latn",
}
m["ads"] = {
"Adamorobe Sign Language",
27709,
"sgn",
"Latn", -- when documented
}
m["adt"] = {
"tiếng Adnyamathanha",
2225391,
"aus-psw",
"Latn",
"Tiếng Adnyamathanha",
"Adnyamathanha",
}
m["adu"] = {
"Aduge",
34734,
"alv-nwd",
"Latn",
ancestors = "opa",
wikipedia_article = "Okpamheri language",
}
m["adw"] = {
"Amondawa",
12626847,
"tup-gua",
"Latn",
}
m["ady"] = {
"tiếng Adygea",
27776,
"cau-cir",
"Cyrl, Latn, Arab",
"Tiếng Adygea",
"Adygea",
translit = {
Cyrl = "cau-cir-translit",
Arab = "ar-translit",
},
override_translit = true,
display_text = {Cyrl = s["cau-Cyrl-displaytext"]},
entry_name = {
Cyrl = s["cau-Cyrl-entryname"],
Latn = s["cau-Latn-entryname"],
},
sort_key = {
Cyrl = {
from = {
"кхъу", "къӏу", -- 4 chars
"гъу", "джу", "дзу", "жъу", "къу", "кхъ", "къӏ", "кӏу", "кӏь", "лъу", "лӏу", "пӏу", "сӏу", "тӏу", "фӏу", "хъу", "цӏу", "чъу", "чӏу", "шъу", "шӏу", "щӏу", -- 3 chars
"гу", "гъ", "гь", "дж", "дз", "ё", "жъ", "жь", "ку", "къ", "кь", "кӏ", "лъ", "ль", "лӏ", "пӏ", "сӏ", "тӏ", "фӏ", "ху", "хъ", "хь", "цу", "цӏ", "чу", "чъ", "чӏ", "шъ", "шӏ", "щӏ", "ӏу", "ӏь" -- 2 chars
},
to = {
"к" .. p[5], "к" .. p[7],
"г" .. p[3], "д" .. p[2], "д" .. p[4], "ж" .. p[2], "к" .. p[3], "к" .. p[4], "к" .. p[6], "к" .. p[10], "к" .. p[11], "л" .. p[2], "л" .. p[5], "п" .. p[2], "с" .. p[2], "т" .. p[2], "ф" .. p[2], "х" .. p[3], "ц" .. p[3], "ч" .. p[3], "ч" .. p[5], "ш" .. p[2], "ш" .. p[4], "щ" .. p[2],
"г" .. p[1], "г" .. p[2], "г" .. p[4], "д" .. p[1], "д" .. p[3], "е" .. p[1], "ж" .. p[1], "ж" .. p[3], "к" .. p[1], "к" .. p[2], "к" .. p[8], "к" .. p[9], "л" .. p[1], "л" .. p[3], "л" .. p[4], "п" .. p[1], "с" .. p[1], "т" .. p[1], "ф" .. p[1], "х" .. p[1], "х" .. p[2], "х" .. p[4], "ц" .. p[1], "ц" .. p[2], "ч" .. p[1], "ч" .. p[2], "ч" .. p[4], "ш" .. p[1], "ш" .. p[3], "щ" .. p[1], "ӏ" .. p[1], "ӏ" .. p[2]
}
},
},
}
m["adz"] = {
"tiếng Adzera",
nil,
"poz-ocw",
"Latn",
"Tiếng Adzera",
"Adzera",
}
m["aea"] = {
"Areba",
3509129,
"aus-pam",
"Latn",
}
m["aeb"] = {
"tiếng Ả Rập Tunisia",
56240,
"sem-arb",
"Arab",
"Tiếng Ả Rập Tunisia",
"Ả Rập Tunisia",
entry_name = "ar-entryname",
}
m["aed"] = {
"Argentine Sign Language",
3322073,
"sgn",
"Latn", -- when documented
}
m["aee"] = {
"Northeast Pashayi",
12642198,
"inc-pas",
"fa-Arab, Latn",
}
m["aek"] = {
"Haeke",
5638166,
"poz-cln",
"Latn",
}
m["ael"] = {
"Ambele",
34818,
"nic-grf",
"Latn",
}
m["aem"] = {
"tiếng Arem",
3507920,
"mkh-vie",
"Latn",
"Tiếng Arem",
"Arem",
}
m["aen"] = {
"Armenian Sign Language",
3446604,
"sgn",
}
m["aeq"] = {
"tiếng Aer",
3246741,
"inc-wes",
"Arab",
"Tiếng Aer",
"Aer",
}
m["aer"] = {
"tiếng Arrernte Đông",
10728232,
"aus-pam",
"Latn",
"Tiếng Arrernte Đông",
"Arrernte Đông",
}
m["aes"] = {
"tiếng Alsea",
2395641,
nil,
"Latn",
"Tiếng Alsea",
"Alsea",
}
m["aeu"] = {
"Akeu",
4700657,
"tbq-sil",
"Latn",
}
m["aew"] = {
"Ambakich",
56642,
"paa-ram",
"Latn",
}
m["aey"] = {
"Amele",
3508025,
"ngf-mad",
"Latn",
}
m["aez"] = {
"Aeka",
16110528,
"ngf",
"Latn",
wikipedia_article = "Orokaiva language", -- subvariety?
}
m["afb"] = {
"tiếng Ả Rập vùng Vịnh",
56385,
"sem-arb",
"Arab",
"Tiếng Ả Rập vùng Vịnh",
"Ả Rập vùng Vịnh",
entry_name = "ar-entryname",
}
m["afd"] = {
"Andai",
4753480,
"paa-arf",
"Latn",
}
m["afe"] = {
"Putukwam",
3914930,
"nic-ben",
"Latn",
}
m["afg"] = {
"Afghan Sign Language",
4689093,
"sgn",
}
m["afh"] = {
"Afrihili",
384707,
"art",
"Latn",
type = "appendix-constructed",
}
m["afi"] = {
"Akrukay",
57003,
"paa-ram",
"Latn",
}
m["afk"] = {
"Nanubae",
6964416,
"paa-arf",
"Latn",
}
m["afn"] = {
"Defaka",
35174,
"nic",
"Latn",
}
m["afo"] = {
"Eloyi",
3914066,
"nic-plt",
"Latn",
}
m["afp"] = {
"Tapei",
16887371,
"paa-arf",
"Latn",
}
m["afs"] = {
"Afro-Seminole Creole",
27867,
"crp",
"Latn",
ancestors = "en",
}
m["aft"] = {
"Afitti",
3400829,
"sdv-nyi",
"Latn",
}
m["afu"] = {
"Awutu",
34847,
"alv-gng",
"Latn",
}
m["afz"] = {
"Obokuitai",
7075258,
"paa-lkp",
"Latn",
}
m["aga"] = {
"Aguano",
3331203,
nil,
"Latn",
}
m["agb"] = {
"Legbo",
35584,
"nic-uce",
"Latn",
}
m["agc"] = {
"tiếng Agatu",
34732,
"alv-ido",
"Latn",
"Tiếng Agatu",
"Agatu",
}
m["agd"] = {
"Agarabi",
3399642,
"paa-kag",
"Latn",
}
m["age"] = {
"Angal",
10951553,
"paa-eng",
"Latn",
}
m["agf"] = {
"Arguni",
12473346,
"poz-cet",
"Latn",
}
m["agg"] = {
"Angor",
3508100,
"paa",
"Latn",
}
m["agh"] = {
"Ngelima",
7022266,
"bnt-bta",
"Latn",
}
m["agi"] = {
"tiếng Agariya",
663586,
"mun",
"Deva, Orya",
"Tiếng Agariya",
"Agariya",
}
m["agj"] = {
"tiếng Argobba",
29292,
"sem-eth",
"Ethi",
"Tiếng Argobba",
"Argobba",
}
m["agk"] = {
"Isarog Agta",
6078982,
"phi",
"Latn",
}
m["agl"] = {
"Fembe",
372927,
"ngf",
"Latn",
}
m["agm"] = {
"tiếng Angaataha",
3508001,
"ngf-ang",
"Latn",
}
m["agn"] = {
"Agutaynen",
3399717,
"phi-kal",
"Latn",
}
m["ago"] = {
"Tainae",
7676186,
"ngf",
"Latn",
}
m["agq"] = {
"tiếng Aghem",
34737,
"nic-rnw",
"Latn",
"Tiếng Aghem",
"Aghem",
}
m["agr"] = {
"Aguaruna",
1526530,
"sai-jiv",
"Latn",
"Tiếng Aguaruna",
"Aguaruna",
}
m["ags"] = {
"tiếng Esimbi",
35260,
"nic-bds",
"Latn",
"Tiếng Esimbi",
"Esimbi",
}
m["agt"] = {
"Central Cagayan Agta",
5017296,
"phi",
"Latn",
}
m["agu"] = {
"tiếng Aguacateca",
35091,
"myn",
"Latn",
"Tiếng Aguacateca",
"Aguacateca",
}
m["agv"] = {
"Remontado Agta",
3508085,
"phi",
"Latn",
}
m["agw"] = {
"Kahua",
3191906,
"poz-sls",
"Latn",
}
m["agx"] = {
"tiếng Aghul",
36498,
"cau-esm",
"Cyrl",
"Tiếng Aghul",
"Aghul",
translit = "cau-nec-translit",
override_translit = true,
display_text = {Cyrl = s["cau-Cyrl-displaytext"]},
entry_name = {Cyrl = s["cau-Cyrl-entryname"]},
sort_key = {
from = {"аь", "гъ", "гь", "гӏ", "дж", "ё", "къ", "кь", "кӏ", "оь", "пӏ", "тӏ", "уь", "хъ", "хь", "хӏ", "цӏ", "чӏ"},
to = {"а" .. p[1], "г" .. p[1], "г" .. p[2], "г" .. p[3], "д" .. p[1], "е" .. p[1], "к" .. p[1], "к" .. p[2], "к" .. p[3], "о" .. p[1], "п" .. p[1], "т" .. p[1], "у" .. p[1], "х" .. p[1], "х" .. p[2], "х" .. p[3], "ц" .. p[1], "ч" .. p[1]}
},
}
m["agy"] = {
"Southern Alta",
7569611,
"phi",
"Latn",
}
m["agz"] = {
"Mount Iriga Agta",
6921432,
"phi",
"Latn",
}
m["aha"] = {
"Ahanta",
34729,
"alv-ctn",
"Latn",
}
m["ahb"] = {
"Axamb",
2874710,
"poz-vnc",
"Latn",
}
m["ahg"] = {
"tiếng Qimant",
35663,
"cus-cen",
"Latn",
"Tiếng Qimant",
"Qimant",
}
m["ahh"] = {
"Aghu",
3436645,
"ngf",
"Latn",
}
m["ahi"] = {
"Tiagbamrin Aizi",
3400073,
"kro-aiz",
"Latn",
}
m["ahk"] = {
"tiếng Akha",
56643,
"tbq-han",
"Latn, Mymr, Thai",
"Tiếng Akha",
"Akha",
sort_key = {
Thai = {
from = {"[%pๆ]", "[็-๎]", "([เแโใไ])([ก-ฮ])"},
to = {"", "", "%2%1"}
},
},
}
m["ahl"] = {
"Igo",
35412,
"alv-ktg",
"Latn",
}
m["ahm"] = {
"Mobumrin Aizi",
35967,
"kro-aiz",
"Latn",
}
m["ahn"] = {
"Àhàn",
34723,
"alv-aah",
"Latn",
}
m["aho"] = {
"tiếng Ahom",
34778,
"tai-swe",
"Ahom",
"Tiếng Ahom",
"Ahom",
translit = "Ahom-translit",
}
m["ahp"] = {
"Aproumu Aizi",
34810,
"alv-kwa",
"Latn",
}
m["ahr"] = {
"tiếng Ahiran",
15549890,
"inc-wes",
"Deva",
"Tiếng Ahiran",
"Ahiran",
translit = "mr-translit",
}
m["ahs"] = {
"Ashe",
34823,
"nic-plc",
"Latn",
}
m["aht"] = {
"tiếng Ahtna",
21058,
"ath-nor",
"Latn",
"Tiếng Ahtna",
"Ahtna",
}
m["aia"] = {
"Arosi",
2863483,
"poz-sls",
"Latn",
}
m["aib"] = {
"tiếng Äynu",
27927,
"qfa-mix",
"Arab, Latn",
"Tiếng Äynu",
"Äynu",
ancestors = "ug, fa"
}
m["aic"] = {
"Ainbai",
3332149,
"paa-brd",
"Latn",
}
m["aid"] = {
"Alngith",
3279409,
"aus-pmn",
"Latn",
}
m["aie"] = {
"Amara",
2841180,
"poz-ocw",
"Latn",
}
m["aif"] = {
"Agi",
3331491,
"qfa-tor",
"Latn",
}
m["aig"] = {
"Antigua and Barbuda Creole English",
3244184,
"crp",
"Latn",
ancestors = "en",
}
m["aih"] = {
"Ai-Cham",
2827749,
"qfa-kms",
"Latn, Hani",
sort_key = {Hani = "Hani-sortkey"},
}
m["aii"] = {
"tiếng Tân Aram Assyria",
29440,
"sem-nna",
"Syrc, Cyrl",
"Tiếng Tân Aram Assyria",
"Tân Aram Assyria",
translit = {Syrc = "aii-translit"},
entry_name = "Syrc-entryname",
}
m["aij"] = {
"tiếng Lishanid Noshan",
3436467,
"sem-nna",
"Hebr",
"Tiếng Lishanid Noshan",
"Lishanid Noshan",
}
m["aik"] = {
"Ake",
34808,
"nic-pls",
"Latn",
}
m["ail"] = {
"Aimele",
3327418,
"ngf",
"Latn",
}
m["aim"] = {
"tiếng Aimol",
4697175,
"tbq-kuk",
"Latn, Beng",
"Tiếng Aimol",
"Aimol",
}
m["ain"] = {
"tiếng Ainu",
27969,
"qfa-ain",
"Kana, Latn, Cyrl",
"Tiếng Ainu",
"Ainu",
sort_key = {Kana = "Kana-sortkey"},
}
m["aio"] = {
"tiếng Aiton",
3399725,
"tai-swe",
"Mymr",
"Tiếng Aiton",
"Aiton",
translit = "aio-phk-translit",
entry_name = {remove_diacritics = c.VS01},
}
m["aip"] = {
"Burumakok",
5000984,
"ngf-okk",
"Latn",
}
m["air"] = {
"Airoran",
3321131,
"paa-tkw",
"Latn",
}
m["ait"] = {
"Arikem",
3446679,
"tup",
"Latn",
}
m["aiw"] = {
"tiếng Aari",
7495,
"omv-aro",
"Latn, Ethi",
"Tiếng Aari",
"Aari",
}
m["aix"] = {
"Aighon",
3504287,
"poz-ocw",
"Latn",
}
m["aiy"] = {
"Ali",
34814,
"alv-gbf",
"Latn",
}
m["aja"] = {
"Aja",
3237491,
"csu-bkr",
"Latn",
}
m["ajg"] = {
"tiếng Adja",
35035,
"alv-gbe",
"Latn",
"Tiếng Adja",
"Adja",
}
m["aji"] = {
"tiếng Ajië",
2828867,
"poz-cln",
"Latn",
"Tiếng Ajië",
"Ajië",
}
m["ajn"] = {
"Andajin",
16111302,
"aus-wor",
"Latn",
}
m["ajp"] = {
"tiếng Ả Rập Nam Levant",
nil,
"sem-arb",
"Arab",
"Tiếng Ả Rập Nam Levant",
"Ả Rập Nam Levant",
entry_name = "ar-entryname",
}
m["ajw"] = {
"Ajawa",
56645,
"cdc-wst",
"Latn",
}
m["ajz"] = {
"Amri Karbi",
3508092,
"tbq-kuk",
"Latn",
ancestors = "mjw",
}
m["akb"] = {
"Angkola Batak",
2640686,
"btk",
"Latn, Batk",
}
m["akc"] = {
"Mpur",
3327139,
"paa-wpa",
"Latn",
}
m["akd"] = {
"Ukpet-Ehom",
36618,
"nic-ucr",
"Latn",
}
m["ake"] = {
"tiếng Akawaio",
28059,
"sai-pem",
"Latn",
"Tiếng Akawaio",
"Akawaio",
}
m["akf"] = {
"Akpa",
34801,
"alv-ido",
"Latn",
}
m["akg"] = {
"Anakalangu",
4750964,
"poz-cet",
"Latn",
}
m["akh"] = {
"Angal Heneng",
10950354,
"paa-eng",
"Latn",
}
m["aki"] = {
"Aiome",
56735,
"paa",
"Latn",
}
m["akj"] = {
"tiếng Aka-Jeru",
2919121,
"qfa-adn",
"Latn, Deva",
"Tiếng Aka-Jeru",
"Aka-Jeru",
}
m["akk"] = {
"tiếng Akkad",
35518,
"sem-eas",
"Xsux, Latn",
"Tiếng Akkad",
"Akkad",
}
m["akl"] = {
"tiếng Aklanon",
8773,
"phi",
"Latn",
"Tiếng Aklanon",
"Aklanon",
ancestors = "poz-pro,phi-pro",
}
m["akm"] = {
"tiếng Aka-Bo",
35361,
"qfa-adn",
"Latn",
"Tiếng Aka-Bo",
"Aka-Bo",
}
m["ako"] = {
"Akurio",
56650,
"sai-tar",
"Latn",
}
m["akp"] = {
"tiếng Siwu",
36470,
"alv-ntg",
"Latn",
"Tiếng Siwu",
"Siwu",
}
m["akq"] = {
"Ak",
56654,
"paa",
"Latn",
}
m["akr"] = {
"tiếng Araki",
2699882,
"poz-vnc",
"Latn",
"Tiếng Araki",
"Araki",
}
m["aks"] = {
"tiếng Akaselem",
34817,
"nic-grm",
"Latn",
"Tiếng Akaselem",
"Akaselem",
}
m["akt"] = {
"Akolet",
3330162,
"poz-ocw",
"Latn",
}
m["aku"] = {
"Akum",
34799,
"nic-ykb",
"Latn",
}
m["akv"] = {
"tiếng Akhvakh",
56423,
"cau-and",
"Cyrl",
"Tiếng Akhvakh",
"Akhvakh",
translit = "cau-nec-translit",
override_translit = true,
display_text = {Cyrl = s["cau-Cyrl-displaytext"]},
entry_name = {Cyrl = s["cau-Cyrl-entryname"]},
}
m["akw"] = {
"Akwa",
34802,
"bnt-mbo",
"Latn",
}
m["akx"] = {
"tiếng Aka-Kede",
3436816,
"qfa-adc",
"Latn",
"Tiếng Aka-Kede",
"Aka-Kede",
}
m["aky"] = {
"Aka-Kol",
3436784,
"qfa-adc",
"Latn",
}
m["akz"] = {
"tiếng Alabama",
1815020,
"nai-mus",
"Latn",
"Tiếng Alabama",
"Alabama",
}
m["ala"] = {
"Alago",
34813,
"alv-ido",
"Latn",
}
m["alc"] = {
"tiếng Kawésqar",
56544,
"aqa",
"Latn",
"Tiếng Kawésqar",
"Kawésqar",
}
m["ald"] = {
"Alladian",
34837,
"alv-lag",
"Latn",
}
m["ale"] = {
"tiếng Aleut",
27210,
"esx",
"Latn, Cyrl",
"Tiếng Aleut",
"Aleut",
}
m["alf"] = {
"Alege",
34815,
"nic-ben",
"Latn",
}
m["alh"] = {
"tiếng Alawa",
2147917,
"aus-gun",
"Latn",
"Tiếng Alawa",
"Alawa",
}
m["ali"] = {
"tiếng Amaimon",
3327427,
"ngf-mad",
"Latn",
"Tiếng Amaimon",
"Amaimon",
}
m["alj"] = {
"tiếng Alangan",
3327423,
"phi",
"Latn",
"Tiếng Alangan",
"Alangan",
}
m["alk"] = {
"tiếng Alak",
2714690,
"mkh",
"Latn, Laoo",
"Tiếng Alak",
"Alak"
}
m["all"] = {
"tiếng Allar",
3393634,
"dra-mal",
"Mlym",
"Tiếng Allar",
"Allar",
translit = "ml-translit"
}
-- "aln" IS TREATED AS "sq", SEE WT:LT
m["alm"] = {
"Amblong",
11022615,
"poz-vnc",
"Latn",
}
m["alo"] = {
"Larike-Wakasihu",
3217929,
"poz-cma",
"Latn",
}
m["alp"] = {
"tiếng Alune",
3327367,
"poz-cet",
"Latn",
}
m["alq"] = {
"tiếng Algonquin",
28092,
"alg",
"Latn",
"Tiếng Algonquin",
"Algonquin",
ancestors = "oj",
}
m["alr"] = {
"tiếng Alutor",
28213,
"qfa-ckn",
"Cyrl",
"Tiếng Alutor",
"Alutor",
entry_name = {
from = {"['’]"},
to = {"ʼ"}
},
sort_key = {
from = {"вʼ", "гʼ", "ғ", "ә", "ё", "ӄ", "ӈ"},
to = {"в" .. p[1], "г" .. p[1], "г" .. p[2], "е" .. p[1], "е" .. p[2], "к" .. p[1], "н" .. p[1]}
},
}
m["alt"] = {
"tiếng Nam Altai",
1991779,
"trk-kkp",
"Cyrl",
"Tiếng Nam Altai",
"Nam Altai",
translit = "Altai-translit",
sort_key = {
from = {"ј", "ё", "ҥ", "ӧ", "ӱ"},
to = {"д" .. p[1], "е" .. p[1], "н" .. p[1], "о" .. p[1], "у" .. p[1]}
},
}
m["alu"] = {
"tiếng 'Are'are",
5160,
"poz-sls",
"Latn",
"Tiếng 'Are'are",
"'Are'are",
}
m["alw"] = {
"tiếng Alaba-K’abeena",
56652,
"cus-hec",
"Latn, Ethi",
"Tiếng Alaba-K’abeena",
"Alaba-K’abeena",
}
m["alx"] = {
"Amol",
3504260,
"qfa-tor",
"Latn",
}
m["aly"] = {
"Alyawarr",
3327389,
"aus-pam",
"Latn",
}
m["alz"] = {
"Alur",
56507,
"sdv-los",
"Latn",
}
m["ama"] = {
"Amanayé",
3508053,
"tup-gua",
"Latn",
}
m["amb"] = {
"Ambo",
3450142,
"nic-tvn",
"Latn",
}
m["amc"] = {
"tiếng Amahuaca",
2669150,
"sai-pan",
"Latn",
"Tiếng Amahuaca",
"Amahuaca",
}
m["ame"] = {
"tiếng Yanesha'",
3088540,
"awd",
"Latn",
"Tiếng Yanesha'",
"Yanesha'",
}
m["amf"] = {
"tiếng Hamer-Banna",
35764,
"omv-aro",
"Latn, Ethi",
"Tiếng Hamer-Banna",
"Hamer-Banna",
}
m["amg"] = {
"tiếng Amurdag",
3360016,
"aus-wdj",
"Latn",
"Tiếng Amurdag",
"Amurdag",
}
m["ami"] = {
"tiếng Amis",
35132,
"map",
"Latn",
"Tiếng Amis",
"Amis",
}
m["amj"] = {
"tiếng Amdang",
28335,
"ssa-fur",
"Latn",
"Tiếng Amdang",
"Amdang",
}
m["amk"] = {
"tiếng Ambai",
1875885,
"poz-hce",
"Latn",
"Tiếng Ambai",
"Ambai",
}
m["aml"] = {
"tiếng War-Jaintia",
56321,
"aav-khs",
"Latn",
"Tiếng War-Jaintia",
"War-Jaintia",
}
m["amm"] = {
"tiếng Ama",
3446626,
"qfa-mal",
"Latn",
"Tiếng Ama",
"Ama",
}
m["amn"] = {
"tiếng Amanab",
3327399,
"paa-brd",
"Latn",
"Tiếng Amanab",
"Amanab",
}
m["amo"] = {
"Amo",
34826,
"nic-kne",
"Latn",
}
m["amp"] = {
"tiếng Alamblak",
56688,
"paa",
"Latn",
"Tiếng Alamblak",
"Alamblak",
}
m["amq"] = {
"Amahai",
3327384,
"poz-cma",
"Latn",
}
m["amr"] = {
"Amarakaeri",
35128,
"sai-har",
"Latn",
}
m["ams"] = {
"tiếng Nam Amami Ōshima",
2840986,
"jpx-nry",
"Jpan",
"Tiếng Nam Amami Ōshima",
"Nam Amami Ōshima",
translit = s["jpx-translit"],
display_text = s["jpx-displaytext"],
entry_name = s["jpx-entryname"],
sort_key = s["jpx-sortkey"],
}
m["amt"] = {
"Amto",
56517,
"paa-asa",
"Latn",
}
m["amu"] = {
"tiếng Amuzgo",
3501942,
"omq",
"Latn",
"Tiếng Amuzgo",
"Amuzgo",
}
m["amv"] = {
"Ambelau",
2669214,
"poz-cma",
"Latn",
}
m["amw"] = {
"tiếng Tân Aram Tây",
34226,
"sem-arw",
"Armi, Syrc, Latn",
"Tiếng Tân Aram Tây",
"Tân Aram Tây",
entry_name = {Syrc = "Syrc-entryname"},
}
m["amx"] = {
"Anmatyerre",
10412317,
"aus-pam",
"Latn",
}
m["amy"] = {
"Ami",
12626835,
"aus-dal",
"Latn",
}
m["amz"] = {
"Atampaya",
3446651,
"aus-pam",
"Latn",
}
m["ana"] = {
"Andaqui",
2846078,
nil,
"Latn",
}
m["anb"] = {
"Andoa",
2846171,
"sai-zap",
"Latn",
}
m["anc"] = {
"tiếng Ngas",
35999,
"cdc-wst",
"Latn",
"Tiếng Ngas",
"Ngas",
}
m["and"] = {
"Ansus",
3513300,
"poz-hce",
"Latn",
}
m["ane"] = {
"tiếng Xârâcùù",
3571097,
"poz-cln",
"Latn",
"Tiếng Xârâcùù",
"Xârâcùù",
}
m["anf"] = {
"Animere",
34783,
"alv-ktg",
"Latn",
}
m["ang"] = {
"tiếng Anh cổ",
42365,
"gmw-ang",
"Latn, Runr",
"Tiếng Anh cổ",
"Anh cổ",
translit = {Runr = "Runr-translit"},
entry_name = {
Latn = {
remove_diacritics = c.acute .. c.macron .. c.dotabove,
from = {"Ƿ", "ƿ"},
to = {"W", "w"}
},
},
sort_key = {
Latn = {
from = {"æ", "ð", "þ", "ƿ"},
to = {"ae", "t" .. p[1], "t" .. p[2], "w" .. p[1]}
},
},
}
m["anh"] = {
"Nend",
6991554,
"ngf-mad",
"Latn",
}
m["ani"] = {
"tiếng Andi",
34849,
"cau-and",
"Cyrl",
"Tiếng Andi",
"Andi",
translit = "cau-nec-translit",
override_translit = true,
display_text = {Cyrl = s["cau-Cyrl-displaytext"]},
entry_name = {Cyrl = s["cau-Cyrl-entryname"]},
}
m["anj"] = {
"Anor",
56458,
"paa",
"Latn",
}
m["ank"] = {
"Goemai",
35272,
"cdc-wst",
"Latn",
}
m["anl"] = {
"Anu",
4777679,
"sit-mru",
"Latn",
}
m["anm"] = {
"tiếng Anal",
56235,
"tbq-kuk",
"Latn",
"Tiếng Anal",
"Anal",
}
m["ann"] = {
"Obolo",
36614,
"nic-lcr",
"Latn",
}
m["ano"] = {
"Andoque",
2669225,
"qfa-iso",
"Latn",
}
m["anp"] = {
"tiếng Angika",
28378,
"inc-bih",
"Deva, Kthi",
"Tiếng Angika",
"Angika",
translit = {
Deva = "hi-translit",
Kthi = "bho-Kthi-translit",
},
ancestors = "bh",
}
m["anq"] = {
"tiếng Jarawa",
2475526,
"qfa-ong",
"Latn, Deva",
"Tiếng Jarawa",
"Jarawa",
}
m["anr"] = {
"Andh",
4754314,
"inc-sou",
"Deva",
}
m["ans"] = {
"Anserma",
3446613,
"sai-chc",
"Latn",
}
m["ant"] = {
"Antakarinya",
921304,
"aus-psw",
"Latn",
}
m["anu"] = {
"tiếng Anuak",
56677,
"sdv-lon",
"Latn, Ethi",
"Tiếng Anuak",
"Anuak",
}
m["anv"] = {
"Denya",
35187,
"nic-mam",
"Latn",
}
m["anw"] = {
"tiếng Anaang",
2845320,
"nic-ief",
"Latn",
"Tiếng Anaang",
"Anaang",
}
m["anx"] = {
"Andra-Hus",
2846195,
"poz-aay",
"Latn",
}
m["any"] = {
"tiếng Anyi",
28395,
"alv-ctn",
"Latn",
"Tiếng Anyi",
"Anyi",
}
m["anz"] = {
"Anem",
56512,
"paa",
"Latn",
}
m["aoa"] = {
"tiếng Angola",
34994,
"crp",
"Latn",
"Tiếng Angola",
"Angola",
ancestors = "pt",
}
m["aob"] = {
"Abom",
3446647,
"ngf",
"Latn",
}
m["aoc"] = {
"tiếng Pemon",
10729616,
"sai-pem",
"Latn",
"Tiếng Pemon",
"Pemon",
}
m["aod"] = {
"Andarum",
3507888,
"paa",
"Latn",
}
m["aoe"] = {
"Angal Enen",
10951638,
"paa-eng",
"Latn",
}
m["aof"] = {
"Bragat",
3507977,
"qfa-tor",
"Latn",
}
m["aog"] = {
"Angoram",
56366, -- cf 6754745 for merged dialect
"paa-lsp",
"Latn",
}
m["aoi"] = {
"Anindilyakwa",
2714654,
"aus-arn",
"Latn",
}
m["aoj"] = {
"Mufian",
3507881,
"qfa-tor",
"Latn",
}
m["aok"] = {
"Arhö",
4790086,
"poz-cln",
"Latn",
}
m["aol"] = {
"Alor",
3332062,
"poz",
"Latn",
}
m["aom"] = {
"Ömie",
8078975,
"ngf",
"Latn",
}
m["aon"] = {
"Bumbita Arapesh",
3508044,
"qfa-tor",
"Latn",
}
m["aor"] = {
"Aore",
12627129,
"poz-vnc",
"Latn",
}
m["aos"] = {
"Taikat",
7676018,
"paa-brd",
"Latn",
}
m["aot"] = {
"tiếng Atong (Ấn Độ)",
5646,
"tbq-bdg",
"Latn, Beng",
"Tiếng Atong (Ấn Độ)",
"Atong (Ấn Độ)",
}
m["aou"] = {
"tiếng A'ou",
16109994,
"qfa-gel",
"Latn", -- also Hani?
"Tiếng A'ou",
"A'ou",
}
m["aox"] = {
"Atorada",
3507932,
"awd",
"Latn",
}
m["aoz"] = {
"Uab Meto",
3441962,
"poz-tim",
"Latn",
}
m["apb"] = {
"Sa'a",
36294,
"poz-sls",
"Latn",
}
m["apc"] = {
"tiếng Ả Rập Bắc Levant",
22809485,
"sem-arb",
"Arab",
"Tiếng Ả Rập Bắc Levant",
"Ả Rập Bắc Levant",
entry_name = "ar-entryname",
}
m["apd"] = {
"tiếng Ả Rập Sudan",
56573,
"sem-arb",
"Arab",
"Tiếng Ả Rập Sudan",
"Ả Rập Sudan",
entry_name = "ar-entryname",
}
m["ape"] = {
"tiếng Bukiyip",
3507895,
"paa-ara",
"Latn",
}
m["apf"] = {
"Pahanan Agta",
7135432,
"phi",
"Latn",
}
m["apg"] = {
"Ampanang",
4748035,
"poz",
"Latn",
}
m["aph"] = {
"tiếng Athpariya",
3449126,
"sit-kie",
"Deva, Latn",
"Tiếng Athpariya",
"Athpariya",
}
m["api"] = {
"Apiaká",
3507941,
"tup-gua",
"Latn",
}
m["apj"] = {
"tiếng Jicarilla",
28277,
"apa",
"Latn",
"Tiếng Jicarilla",
"Jicarilla",
}
m["apk"] = {
"tiếng Apache đồng bằng",
27861,
"apa",
"Latn",
"Tiếng Apache đồng bằng",
"Apache đồng bằng"
}
m["apl"] = {
"tiếng Lipan",
28269,
"apa",
"Latn",
"Tiếng Lipan",
"Lipan",
}
m["apm"] = {
"tiếng Chiricahua",
13368,
"apa",
"Latn",
"Tiếng Chiricahua",
"Chiricahua",
}
m["apn"] = {
"tiếng Apinayé",
2858311,
"sai-nje",
"Latn",
"Tiếng Apinayé",
"Apinayé",
}
m["apo"] = {
"Ambul",
12627135,
"poz-ocw",
"Latn",
}
m["app"] = {
"Apma",
2669188,
"poz-vnc",
"Latn",
}
m["apq"] = {
"tiếng Pucikwar",
28466,
"qfa-adc",
"Latn",
"Tiếng Pucikwar",
"Pucikwar",
}
m["apr"] = {
"Arop-Lokep",
2863482,
"poz-ocw",
"Latn",
}
m["aps"] = {
"Arop-Sissano",
12627242,
"poz-ocw",
"Latn",
}
m["apt"] = {
"tiếng Apatani",
56306,
"sit-tan",
"Latn",
"Tiếng Apatani",
"Apatani",
}
m["apu"] = {
"Apurinã",
2859081,
"awd",
"Latn",
}
m["apv"] = {
"Alapmunte",
16110782,
"sai-nmk",
"Latn",
}
m["apw"] = {
"tiếng Apache Tây",
28060,
"apa",
"Latn",
"Tiếng Apache Tây",
"Apache Tây",
}
m["apx"] = {
"tiếng Aputai",
12473343,
"poz-tim",
"Latn",
"Tiếng Aputai",
"Aputai",
}
m["apy"] = {
"tiếng Aparaí",
2736980,
"sai-gui",
"Latn",
"Tiếng Aparaí",
"Aparaí",
}
m["apz"] = {
"Safeyoka",
7398693,
"ngf",
"Latn",
}
m["aqc"] = {
"tiếng Archi",
34915,
"cau-lzg",
"Cyrl",
"Tiếng Archi",
"Archi",
translit = "cau-nec-translit",
override_translit = true,
display_text = {Cyrl = s["cau-Cyrl-displaytext"]},
entry_name = {Cyrl = s["cau-Cyrl-entryname"]},
sort_key = {
from = {
"ккъӏв", "ххьӏв", -- 5 chars
"гъӏв", "ёоӏ", "ккъӏ", "ккъв", "къӏв", "ллъв", "ххьӏ", "хъӏв", "хьӏв", "ццӏв", "ччӏв", -- 4 chars
"ааӏ", "гӏв", "гъӏ", "гъв", "гьв", "ееӏ", "ёӏ", "ёо", "ииӏ", "кӏв", "ккв", "ккъ", "къӏ", "къв", "кьв", "лӏв", "ллъ", "лъв", "льв", "ооӏ", "пӏв", "ппв", "ссв", "тӏв", "ттв", "ууӏ", "хӏв", "ххв", "хъӏ", "хъв", "хьӏ", "цӏв", "ццӏ", "ццв", "чӏв", "ччӏ", "ээӏ", "юуӏ", "яаӏ", -- 3 chars
"аӏ", "аа", "гӏ", "гв", "гъ", "гь", "дв", "еӏ", "ее", "ё", "жв", "зв", "иӏ", "ии", "кӏ", "кв", "кк", "къ", "кь", "лӏ", "лв", "лъ", "ль", "оӏ", "оо", "пӏ", "пв", "пп", "св", "сс", "тӏ", "тв", "тт", "уӏ", "уу", "фв", "хӏ", "хв", "хх", "хъ", "цӏ", "цв", "цц", "чӏ", "чв", "шв", "щв", "эӏ", "ээ", "юӏ", "юу", "яӏ", "яа" -- 2 chars
},
to = {
"к" .. p[8], "х" .. p[7],
"г" .. p[6], "е" .. p[7], "к" .. p[7], "к" .. p[9], "к" .. p[12], "л" .. p[5], "х" .. p[6], "х" .. p[10], "х" .. p[13], "ц" .. p[6], "ч" .. p[5],
"а" .. p[3], "г" .. p[2], "г" .. p[5], "г" .. p[7], "г" .. p[9], "е" .. p[3], "е" .. p[5], "е" .. p[6], "и" .. p[3], "к" .. p[2], "к" .. p[5], "к" .. p[6], "к" .. p[11], "к" .. p[13], "к" .. p[15], "л" .. p[2], "л" .. p[4], "л" .. p[7], "л" .. p[9], "о" .. p[3], "п" .. p[2], "п" .. p[5], "с" .. p[3], "т" .. p[2], "т" .. p[5], "у" .. p[3], "х" .. p[2], "х" .. p[5], "х" .. p[9], "х" .. p[11], "х" .. p[12], "ц" .. p[2], "ц" .. p[5], "ц" .. p[7], "ч" .. p[2], "ч" .. p[4], "э" .. p[3], "ю" .. p[3], "я" .. p[3],
"а" .. p[1], "а" .. p[2], "г" .. p[1], "г" .. p[3], "г" .. p[4], "г" .. p[8], "д" .. p[1], "е" .. p[1], "е" .. p[2], "е" .. p[4], "ж" .. p[1], "з" .. p[1], "и" .. p[1], "и" .. p[2], "к" .. p[1], "к" .. p[3], "к" .. p[4], "к" .. p[10], "к" .. p[14], "л" .. p[1], "л" .. p[3], "л" .. p[6], "л" .. p[8], "о" .. p[1], "о" .. p[2], "п" .. p[1], "п" .. p[3], "п" .. p[4], "с" .. p[1], "с" .. p[2], "т" .. p[1], "т" .. p[3], "т" .. p[4], "у" .. p[1], "у" .. p[2], "ф" .. p[1], "х" .. p[1], "х" .. p[3], "х" .. p[4], "х" .. p[8], "ц" .. p[1], "ц" .. p[3], "ц" .. p[4], "ч" .. p[1], "ч" .. p[3], "ш" .. p[1], "щ" .. p[1], "э" .. p[1], "э" .. p[2], "ю" .. p[1], "ю" .. p[2], "я" .. p[1], "я" .. p[2]
}
},
}
m["aqd"] = {
"Ampari Dogon",
4748057,
"nic-dgw",
"Latn",
}
m["aqg"] = {
"Arigidi",
34829,
"alv-von",
"Latn",
}
m["aqm"] = {
"Atohwaim",
11732297,
"ngf",
"Latn",
}
m["aqn"] = {
"Northern Alta",
7058116,
"phi",
"Latn",
}
m["aqp"] = {
"Atakapa",
10975683,
"qfa-iso",
"Latn",
}
m["aqr"] = {
"Arhâ",
4790085,
"poz-cln",
"Latn",
}
m["aqt"] = {
"Angaité",
15736037,
"sai-mas",
"Latn",
}
m["aqz"] = {
"tiếng Akuntsu",
4701960,
"tup",
"Latn",
"Tiếng Akuntsu",
"Akuntsu",
}
m["arc"] = {
"tiếng Aram",
28602,
"sem-ara",
"Hebr, Armi, Syrc, Palm, Nbat, Phnx, Mand, Samr, Hatr, Elym",
"Tiếng Aram",
"Aram",
translit = {
Armi = "Armi-translit",
Palm = "Palm-translit",
},
entry_name = {
Hebr = {remove_diacritics = u(0x0591) .. "-" .. u(0x05BD) .. u(0x05BF) .. "-" .. u(0x05C5) .. u(0x05C7)},
Syrc = {remove_diacritics = u(0x0730) .. "-" .. u(0x0748)},
remove_diacritics = c.macron .. c.diaer .. c.macronbelow}, -- Which script(s)?
}
m["ard"] = {
"Arabana",
3507959,
"aus-kar",
"Latn",
}
m["are"] = {
"Western Arrernte",
12645549,
"aus-pam",
"Latn",
}
m["arh"] = {
"tiếng Arhuaco",
2640621,
"cba",
"Latn",
"Tiếng Arhuaco",
"Arhuaco",
}
m["ari"] = {
"Arikara",
56539,
"cdd",
"Latn",
}
m["arj"] = {
"Arapaso",
12627166,
"sai-tuc",
"Latn",
}
m["ark"] = {
"Arikapú",
3446640,
"sai-mje",
"Latn",
}
m["arl"] = {
"Arabela",
2591221,
"sai-zap",
"Latn",
}
m["arn"] = {
"tiếng Mapuche",
33730,
"sai-ara",
"Latn",
"Tiếng Mapuche",
"Mapuche",
}
m["aro"] = {
"Araona",
958414,
"sai-tac",
"Latn",
}
m["arp"] = {
"tiếng Arapaho",
56417,
"alg-ara",
"Latn",
"Tiếng Arapaho",
"Arapaho",
}
m["arq"] = {
"tiếng Ả Rập Algérie",
56499,
"sem-arb",
"Arab",
"Tiếng Ả Rập Algérie",
"Ả Rập Algérie",
entry_name = "ar-entryname",
}
m["arr"] = {
"Arara-Karo",
35539,
"tup",
"Latn",
}
m["ars"] = {
"tiếng Ả Rập Najd",
56574,
"sem-arb",
"Arab",
"Tiếng Ả Rập Najd",
"Ả Rập Najd",
entry_name = "ar-entryname",
}
m["aru"] = {
"Arua",
2746221,
"auf",
"Latn",
}
m["arv"] = {
"Arbore",
56883,
"cus-eas",
"Latn",
}
m["arw"] = {
"tiếng Lokono",
2655664,
"awd-taa",
"Latn",
"Tiếng Lokono",
"Lokono",
}
m["arx"] = {
"Aruá",
3507907,
"tup",
"Latn",
}
m["ary"] = {
"tiếng Ả Rập Maroc",
56426,
"sem-arb",
"Arab",
"Tiếng Ả Rập Maroc",
"Ả Rập Maroc",
entry_name = "ar-entryname",
sort_key = {
from = {"^%f[" .. u(0x5D0) .. "-" .. u(0x5EA) .. "]"},
to = {u(0xFB21)},
},
}
m["arz"] = {
"tiếng Ả Rập Ai Cập",
29919,
"sem-arb",
"Arab",
"Tiếng Ả Rập Ai Cập",
"Ả Rập Ai Cập",
entry_name = "ar-entryname",
}
m["asa"] = {
"Pare",
36403,
"bnt-par",
"Latn",
}
m["asb"] = {
"tiếng Assiniboine",
2591288,
"sio-dkt",
"Latn",
"Tiếng Assiniboine",
"Assiniboine",
}
m["asc"] = {
"Casuarina Coast Asmat",
11732046,
"ngf",
"Latn",
}
m["ase"] = {
"ngôn ngữ ký hiệu Mỹ",
14759,
"sgn",
"Sgnw",
"Ngôn ngữ ký hiệu Mỹ",
"ngôn ngữ ký hiệu Mỹ",
}
m["asf"] = {
"Auslan",
29525,
"sgn",
"Latn", -- when documented
}
m["asg"] = {
"Cishingini",
35199,
"nic-kam",
"Latn",
}
m["ash"] = {
"Abishira",
2871740,
"qfa-iso",
"Latn",
}
m["asi"] = {
"Buruwai",
5001031,
"ngf",
"Latn",
}
m["asj"] = {
"Nsari",
36418,
"nic-bbe",
"Latn",
}
m["ask"] = {
"tiếng Ashkun",
29379,
"nur-sou",
"Arab, Latn",
"Tiếng Ashkun",
"Ashkun",
}
m["asl"] = {
"Asilulu",
12473347,
"poz-cma",
"Latn",
}
m["asn"] = {
"Xingú Asuriní",
8044571,
"tup-gua",
"Latn",
}
m["aso"] = {
"Dano",
5220979,
"paa-kag",
"Latn",
}
m["asp"] = {
"Algerian Sign Language",
3135421,
"sgn",
}
m["asq"] = {
"Austrian Sign Language",
36668,
"sgn",
"Latn", -- when documented
}
m["asr"] = {
"tiếng Asur",
3504321,
"mun",
"Deva, Latn",
"Tiếng Asur",
"Asur"
}
m["ass"] = {
"Ipulo",
35408,
"nic-tvc",
"Latn",
}
m["ast"] = {
"tiếng Asturias",
29507,
"roa-ibe",
"Latn",
"Tiếng Asturias",
"Asturias",
ancestors = "roa-ole",
}
m["asu"] = {
"Tocantins Asurini",
32041490,
"tup-gua",
"Latn",
}
m["asv"] = {
"Asoa",
56296,
"csu-maa",
"Latn",
}
m["asw"] = {
"Australian Aboriginal Sign Language",
955216,
"sgn",
"Latn", -- when documented
}
m["asx"] = {
"Muratayak",
11732766,
"ngf-fin",
"Latn",
}
m["asy"] = {
"Yaosakor Asmat",
16113158,
"ngf",
"Latn",
}
m["asz"] = {
"As",
2866218,
"poz-hce",
"Latn",
}
m["ata"] = {
"Pele-Ata",
56511,
"paa",
"Latn",
}
m["atb"] = {
"tiếng Zaiwa",
56594,
"tbq-brm",
"Latn, Lisu", -- also Hani?
"Tiếng Zaiwa",
"Zaiwa",
translit = {Lisu = "Lisu-translit"},
sort_key = {Lisu = s["Lisu-sortkey"]},
}
m["atc"] = {
"tiếng Atsahuaca",
4817730,
"sai-pan",
"Latn",
"Tiếng Atsahuaca",
"Atsahuaca",
}
m["atd"] = {
"Ata Manobo",
12627315,
"mno",
"Latn",
}
m["ate"] = {
"tiếng Atemble",
4813055,
"ngf-mad",
"Latn",
"Tiếng Atemble",
"Atemble",
}
m["atg"] = {
"Okpela",
7082551,
"alv-yek",
"Latn",
}
m["ati"] = {
"Attié",
34844,
"alv-lag",
"Latn",
}
m["atj"] = {
"tiếng Atikamekw",
56590,
"alg",
"Latn",
ancestors = "cr",
"Tiếng Atikamekw",
"Atikamekw",
}
m["atk"] = {
"Ati",
4815751,
"phi",
"Latn",
}
m["atl"] = {
"Mount Iraya Agta",
6921430,
"phi",
"Latn",
}
m["atm"] = {
"Ata",
4812603,
"phi",
"Latn",
}
m["ato"] = {
"Atong (Cameroon)",
34824,
"nic-grs",
"Latn",
}
m["atp"] = {
"Pudtol Atta",
12640726,
"phi",
"Latn",
}
m["atq"] = {
"Aralle-Tabulahan",
4783889,
"poz-ssw",
"Latn",
}
m["atr"] = {
"Waimiri-Atroari",
56865,
"sai-car",
"Latn",
}
m["ats"] = {
"Gros Ventre",
56628,
"alg-ara",
"Latn",
}
m["att"] = {
"Pamplona Atta",
12639245,
"phi",
"Latn",
}
m["atu"] = {
"Reel",
7306882,
"sdv-dnu",
"Latn",
}
m["atv"] = {
"tiếng Bắc Altai",
2640863,
"trk-ssb",
"Cyrl",
"Tiếng Bắc Altai",
"Bắc Altai",
translit = "Altai-translit",
}
m["atw"] = {
"Atsugewi",
56718,
"nai-pal",
"Latn",
}
m["atx"] = {
"Arutani",
56609,
nil,
"Latn",
}
m["aty"] = {
"Aneityum",
2379113,
"poz-oce",
"Latn",
}
m["atz"] = {
"Arta",
3508067,
"phi",
"Latn",
}
m["aua"] = {
"tiếng Asumboa",
4811870,
"poz-oce",
"Latn",
"Tiếng Asumboa",
"Asumboa",
}
m["aub"] = {
"Alugu",
12626798,
"tbq-urp",
"Latn", -- also Hani?
}
m["auc"] = {
"Huaorani",
758570,
"qfa-iso",
"Latn",
}
m["aud"] = {
"Anuta",
35326,
"poz-pnp",
"Latn",
}
m["aug"] = {
"Aguna",
34733,
"alv-gbe",
"Latn",
}
m["auh"] = {
"Aushi",
2872082,
"bnt-sbi",
"Latn",
}
m["aui"] = {
"tiếng Anuki",
3508132,
"poz-ocw",
"Latn",
"Tiếng Anuki",
"Anuki",
}
m["auj"] = {
"tiếng Awjila",
56398,
"ber",
"Latn, Arab, Tfng",
"Tiếng Awjila",
"Awjila",
}
m["auk"] = {
"Heyo",
3504295,
"qfa-tor",
"Latn",
}
m["aul"] = {
"Aulua",
427300,
"poz-vnc",
"Latn",
}
m["aum"] = {
"Asu",
34798,
"alv-ngb",
"Latn",
}
m["aun"] = {
"Molmo One",
12637224,
"qfa-tor",
"Latn",
}
m["auo"] = {
"Auyokawa",
56247,
"cdc-wst",
"Latn",
}
m["aup"] = {
"Makayam",
6738863,
"ngf",
"Latn",
}
m["auq"] = {
"Anus",
23855,
"poz-ocw",
"Latn",
}
m["aur"] = {
"Aruek",
3504279,
"qfa-tor",
"Latn",
}
m["aut"] = {
"Austral",
2669261,
"poz-pep",
"Latn",
}
m["auu"] = {
"Auye",
4827334,
"ngf",
"Latn",
}
m["auw"] = {
"Awyi",
3513326,
"paa-brd",
"Latn",
}
m["aux"] = {
"Aurá",
3507995,
"tup-gua",
"Latn",
}
m["auy"] = {
"Auyana",
2873211,
"paa-kag",
"Latn",
}
m["auz"] = {
"tiếng Ả Rập Uzbekistan",
3399507,
"sem-arb",
"Arab",
"Tiếng Ả Rập Uzbekistan",
"Ả Rập Uzbekistan",
entry_name = "ar-entryname",
}
m["avb"] = {
"Avau",
12627412,
"poz-ocw",
"Latn",
}
m["avd"] = {
"Alviri-Vidari",
3327357,
"xme",
"fa-Arab",
ancestors = "xme-mid",
}
m["avi"] = {
"Avikam",
34840,
"alv-lag",
"Latn",
}
m["avk"] = {
"tiếng Kotava",
1377116,
"art",
"Latn",
"Tiếng Kotava",
"Kotava",
type = "appendix-constructed",
}
m["avm"] = {
"Angkamuthi",
nil,
"aus-pmn",
"Latn",
}
m["avn"] = {
"tiếng Avatime",
34796,
"alv-ktg",
"Latn",
"Tiếng Avatime",
"Avatime",
}
m["avo"] = {
"Agavotaguerra",
3508007,
"awd",
"Latn",
}
m["avs"] = {
"Aushiri",
3409318,
"sai-zap",
"Latn",
}
m["avt"] = {
"Au",
3446608,
"qfa-tor",
"Latn",
}
m["avu"] = {
"tiếng Avokaya",
56685,
"csu-mma",
"Latn",
"Tiếng Avokaya",
"Avokaya",
}
m["avv"] = {
"Avá-Canoeiro",
4829584,
"tup-gua",
"Latn",
}
m["awa"] = {
"tiếng Awadh",
29579,
"inc-hie",
"Deva, Kthi, fa-Arab",
"Tiếng Awadh",
"Awadh",
translit = {Deva = "hi-translit"},
}
m["awb"] = {
"Awa (New Guinea)",
2874650,
"paa-kag",
"Latn",
}
m["awc"] = {
"Cicipu",
35193,
"nic-kam",
"Latn",
}
m["awe"] = {
"Awetí",
4830038,
"tup",
"Latn",
}
m["awg"] = {
"tiếng Anguthimri",
4764288,
"aus-pam",
"Latn",
"Tiếng Anguthimri",
"Anguthimri",
}
m["awh"] = {
"Awbono",
3446684,
"ngf",
"Latn",
}
m["awi"] = {
"Aekyom",
3399691,
"ngf",
"Latn",
}
m["awk"] = {
"Awabakal",
3449138,
"aus-pam",
"Latn",
}
m["awm"] = {
"Arawum",
4784537,
"ngf-mad",
"Latn",
}
m["awn"] = {
"tiếng Awngi",
34934,
"cus-cen",
"Ethi",
"Tiếng Awngi",
"Awngi",
}
m["awo"] = {
"Awak",
3446643,
"alv-wjk",
"Latn",
}
m["awr"] = {
"Awera",
56379,
"paa-lkp",
"Latn",
}
m["aws"] = {
"South Awyu",
12633986,
"ngf",
"Latn",
}
m["awt"] = {
"tiếng Araweté",
4784535,
"tup-gua",
"Latn",
"Tiếng Araweté",
"Araweté",
}
m["awu"] = {
"Central Awyu",
12628801,
"ngf",
"Latn",
}
m["awv"] = {
"Jair Awyu",
16110177,
"ngf",
"Latn",
}
m["aww"] = {
"Awun",
56369,
"paa-spk",
"Latn",
}
m["awx"] = {
"Awara",
2874670,
"ngf-fin",
"Latn",
}
m["awy"] = {
"Edera Awyu",
12630425,
"ngf",
"Latn",
}
m["axb"] = {
"Abipon",
11252539,
"sai-guc",
"Latn",
}
m["axe"] = {
"Ayerrerenge",
16112737,
"aus-pam",
"Latn",
}
m["axg"] = {
"Mato Grosso Arára",
3446660,
nil,
"Latn",
}
m["axk"] = {
"Aka (Central Africa)",
11010149,
"bnt-ngn",
"Latn",
}
m["axl"] = {
"Lower Southern Aranda",
6693295,
"aus-pam",
"Latn",
}
m["axm"] = {
"tiếng Armenia trung đại",
4438498,
"hyx",
"Armn",
"Tiếng Armenia trung đại",
"Armenia trung đại",
ancestors = "xcl",
-- Chuyển tự Armn đã có sẵn ở [[Mô đun:scripts/data]]
override_translit = true,
strip_diacritics = {
remove_diacritics = "՞՜՛՟",
from = {"եւ", "ՙ", "՚"},
to = {"և", "ʻ", "’"}
}
}
m["axx"] = {
"Xaragure",
8045635,
"poz-cln",
"Latn",
}
m["aya"] = {
"Awar",
56876,
"paa",
"Latn",
}
m["ayb"] = {
"Ayizo",
34841,
"alv-pph",
"Latn",
}
m["ayd"] = {
"Ayabadhu",
3509164,
"aus-pmn",
"Latn",
}
m["aye"] = {
"tiếng Ayere",
34788,
"alv-aah",
"Latn",
"Tiếng Ayere",
"Ayere",
}
m["ayg"] = {
"Nyanga (Togo)",
35446,
"alv-gng",
"Latn",
}
m["ayi"] = {
"Leyigha",
3914492,
"nic-uce",
"Latn",
}
m["ayk"] = {
"tiếng Akuku",
3450179,
"alv-nwd",
"Latn",
}
m["ayl"] = {
"tiếng Ả Rập Libya",
56503,
"sem-arb",
"Arab",
strip_diacritics = "ar-stripdiacritics",
}
m["ayn"] = {
"tiếng Ả Rập Sana'a",
1686766,
"sem-arb",
"Arab, Hebr",
strip_diacritics = {
Arab = "ar-stripdiacritics",
},
-- Hebr display_text, strip_diacritics, sort_key in [[Module:scripts/data]]
}
m["ayo"] = {
"Ayoreo",
56634,
"sai-zam",
"Latn",
}
m["ayp"] = {
"tiếng Ả Rập Bắc Lưỡng Hà",
56577,
"sem-arb",
"Arab",
"Tiếng Ả Rập Bắc Lưỡng Hà",
"Ả Rập Bắc Lưỡng Hà",
ancestors = "acm",
entry_name = "ar-entryname",
}
m["ayq"] = {
"Ayi",
56449,
"paa-spk",
"Latn",
}
m["ays"] = {
"Sorsogon Ayta",
7563752,
"phi",
"Latn",
}
m["ayt"] = {
"Bataan Ayta",
4921648,
"phi",
"Latn",
}
m["ayu"] = {
"tiếng Ayu",
34786,
"alv",
"Latn",
}
m["ayy"] = {
"Tayabas Ayta",
7689745,
"phi",
"Latn",
}
m["ayz"] = {
"Maybrat",
4830892,
"paa-wpa",
"Latn",
}
m["aza"] = {
"Azha",
4832486,
"tbq-axi",
"Latn",
}
m["azd"] = {
"Eastern Durango Nahuatl",
16115449,
"azc-nah",
"Latn",
}
m["azg"] = {
"San Pedro Amuzgos Amuzgo",
35092,
"omq",
"Latn",
}
m["azm"] = {
"Ipalapa Amuzgo",
12633013,
"omq",
"Latn",
}
m["azn"] = {
"Western Durango Nahuatl",
12645553,
"azc-nah",
"Latn",
}
m["azo"] = {
"tiếng Awing",
34856,
"nic-nge",
"Latn",
"Tiếng Awing",
"Awing",
}
m["azt"] = {
"Faire Atta",
12630884,
"phi",
"Latn",
}
m["azz"] = {
"Highland Puebla Nahuatl",
12953754,
"azc-nah",
"Latn",
}
return require("Module:languages").finalizeData(m, "language")
mvtcxxntvrcnm2ur6ycfj7qids48jrn
Mô đun:headword utilities
828
270803
2573877
2573213
2026-10-27T13:29:36Z
Hiyuune
50834
2573877
Scribunto
text/plain
local export = {}
--[==[
FIXME:
1. Implement ? = unknown or uncertain. There should be no label by default but it should be configurable.
The default category should be "{plpos} with unknown or uncertain plurals" for nouns/proper nouns. It should
be an error to specify values along with this. [DONE]
2. Implement ! = unattested. The default label should be "{label} not attested". The default category should be
"{plpos} with unattested plurals" for nouns/proper nouns. It should be an error to specify values along with
this. [DONE]
3. Implement +- = sometimes has a value, e.g. "countable and uncountable" for plurals. [DONE]
4. Implement ~ standing for the pagename in heads and inflections. [DONE]
5. Implement abbr= for abbreviations. [DONE]
6. Split insert_inflection into process_for_insertion() (which implements the logic for handling special mode signals)
and insert_processed_inflection() (which implements the labels and categories for mode signals) so that we can handle
mode signals correctly. [DONE]
7. Support no_label for booleans.
8. Document the various types of parts of speech and implement pos_category per part of speech definition. [DONE]
9. Implement apply_link_modifiers() for head=~~...
10. Correctly handle var=both. [DONE]
11. Implement checkredlinks and port simplify_pos to [[Module:headword]]. [CHECKREDLINKS SET; SIMPLIFY_POS NOT PORTED YET.]
12. Fix longstanding accelerator bug: if embedded links in inflection, don't make it green. [DONE]
13. Implement process_terms() callback after process_for_insertion. [DONE]
14. Implement validate_and_canonicalize() callback. [NOT DONE; INSTEAD HAVE A SEPARATE CANONICALIZE CALLBACK]
15. Document new methods.
16. Implement process_one_term() callback after process_for_insertion. [DONE]
17. Implement process() at a stage to happen universally when there is no argument to parse. [DONE]
18. Skip mode signals during validation and canonicalization. [DONE]
19. Implement inflsuf= with special handling and special inflectional categories. [DONE]
20. Add support for "boolean or string" type.
21. Support invocation-specified or user-specified languages. [DONE]
]==]
local require_when_needed = require("Module:utilities/require when needed")
local affix_module = "Module:affix"
local debug_track_module = "Module:debug/track"
local decorations_module = "Module:decorations"
local en_utilities_module = "Module:en-utilities"
local fun_is_callable_module = "Module:fun/isCallable"
local headword_module = "Module:headword"
local headword_data_module = "Module:headword/data"
local languages_module = "Module:languages"
local links_module = "Module:links"
local parameters_module = "Module:parameters"
local parse_interface_module = "Module:parse interface"
local parse_utilities_module = "Module:parse utilities"
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 lcfirst = require("Module:string utilities").lcfirst
local dump = mw.dumpObject
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
local insert = table.insert
local concat = table.concat
local remove = table.remove
local sort = table.sort
local contains = require_when_needed(table_module, "contains")
local deep_equals = require_when_needed(table_module, "deepEquals")
local extend = require_when_needed(table_module, "extend")
local insert_if_not = require_when_needed(table_module, "insertIfNot")
local is_array = require_when_needed(table_module, "isArray")
local list_to_set = require_when_needed(table_module, "listToSet")
local serial_comma_join = require_when_needed(table_module, "serialCommaJoin")
local shallow_copy = require_when_needed(table_module, "shallowCopy")
local split = require_when_needed(string_utilities_module, "split")
local ugsub = require_when_needed(string_utilities_module, "gsub")
local umatch = require_when_needed(string_utilities_module, "match")
local pattern_escape = require_when_needed(string_pattern_escape_module)
local replacement_escape = require_when_needed(string_replacement_escape_module)
local escape_wikicode = require_when_needed(parse_utilities_module, "escape_wikicode")
local parse_inline_modifiers = require_when_needed(parse_utilities_module, "parse_inline_modifiers")
local term_contains_top_level_html = require_when_needed(parse_utilities_module, "term_contains_top_level_html")
local get_lang_by_code = require_when_needed(languages_module, "getByCode")
local is_callable = require_when_needed(fun_is_callable_module)
local format_decorations = require_when_needed(decorations_module, "format_decorations")
local singularize = require_when_needed(en_utilities_module, "singularize")
local canonicalize_pos = require_when_needed(headword_module, "canonicalize_pos")
--[==[ intro:
This module contains utilities for constructing language-specific headword modules.
[fill in]
===About parts of speech===
There are several versions of the part of speech (POS) of a given term:
* The ''primary categorizing POS'' is the basic part of speech to which a term belongs. The primary POS determines
whether the term is a lemma or non-lemma form, and in its plural form, it is used to categorize the terms (unless it
is a variant term), along with caetegorizing into a ''lemmas'' or ''non-lemma forms'' category. (Variant terms,
typically marked by {{para|var|1}}, are categorized into ''variant lemmas'' or ''variant non-lemma forms'' and are not
categorized into a primary POS category.)
* The ''raw {{tl|head}} POS'' is parameter {{para|2}} to {{tl|head}}. This is used to determine the primary categorizing
POS by a process of canonicalization, because this parameter may contain abbreviations (e.g. ''a'' or ''adj'' for an
adjective) and may be (and usually is) given in its singular form.
* The ''template-indicated POS'' is the part of speech indicated by a headword template such as {{tl|nl-noun}} or
{{tl|mn-head}}. It is either inherent to the template (in a case like {{tl|nl-noun}}), in which case it is specified
by the template code and should always be in canonical, plural form; or it is specified by the user in the template
call (in a case like {{tl|mn-head}} or other generic POS template, usually ending in `-head`), in which case it may
be given in raw form, like for the raw {{tl|head}} POS, and is canonicalized using the same code that canonicalizes
raw {{tl|head}} parts of speech. The template-indicate POS may not be a valid primary categorizing POS. For example,
Dutch has a template-indicated POS ''diminutiva tantum nouns'', specified through the template {{tl|nl-dim-tant}}, but
there is no such primary categorizing POS (that POS would be `nouns`). Similarly, Arabic has a template-indicated POS
''collective nouns'', whose primary categorizing POS is also `nouns`. As a result, the template-indicated POS may need
to be converted to a primary categorizing POS through a ''normalization'' process (which uses a `pos_category` field
attached to the `pos_functions` structure for the template-indicated POS). Note that the template-indicated POS comes
in several variants:
*# It may be ''head-raw'', meaning that it uses the literal string `head` in place of the user-specified POS in a
generic POS template like {{tl|mn-head}}; it may be ''non-head'', meaning that it uses the actual user-specified
POS in such a case; or it may be ''head-augmented'', meaning that in the case of a generic POS template it is of the
form e.g. `head.nouns`, contanining both the literal `head` and the user-specified POS.
*# It may be ''suffix-raw'', meaning that it uses the primary categorizing POS `suffixes` in place of the
template-indicated POS when the term is a suffix, or it may be ''non-suffix'', meaning that it uses the
template-indicated POS in such a case. The headword for a suffix uses the template corresponding to whatever POS the
suffix forms, e.g. {{nl-noun}} for Dutch noun-forming suffixes.
*# The ''indexing POS'' is the variant of the template-indicated POS that is used to index into the `pos_functions`
table. Normally it is head-raw and non-suffix, but it will be head-augmented if the corresponding head-augmented
entry exists in `pos_functions`.
*# The ''unnormalized categorizing POS'' is the variant of the template-indicated POS that is potentially usable as the
primary categorizing POS (see above). It is non-head and suffix-raw, i.e. it uses the actual template-specified or
user-specified form of the template-indicated POS except when the term is a suffix, in which case it has the value
`suffixes`. In most circumstances this can be used directly as the primary categorizing POS, but in some cases this
POS is not a valid primary categorizing POS and needs to be normalized to get the actual primary categorizing POS, as
mentioned above under the description for the template-indicated POS.
* Finally, there is a ''simplified categorizing POS''. This collapses variations of primary categorizing parts of speech
together for use in red-link and similar categories. For example, the POS `proper nouns` is collapsed into `nouns`,
and all types of participles are collapsed into `participles`. There are two variants of the simplification process,
''lemma-status-preserving'' and ''non-lemma-status-preserving''. The latter is the default and is simpler, in that
e.g. it means any part of speech ending in `nouns` to `nouns`, even if this causes non-lemma forms to become lemmas,
as with `diminutive nouns` and `verbal nouns` (which are non-lemma forms) and likewise with `comparative adjectives`
and `superlative adjectives` (which are again non-lemma forms). The former, ''lemma-status-preserving'', keeps lemmas
as lemmas and (more important) non-lemmas as non-lemmas by converting e.g. `diminutive nouns` and `verbal nouns` to
`noun forms` rather than `nouns`. The reason two variants exist in that they have different use cases. The default,
non-lemma-status-preserving, type is used for tracking categories such as red-link and request categories, where
completeness is more important than clutter. The lemma-status-preserving variant is used for informative categories
such as ` ``lang`` masculine nouns`, which should only have lemmas in them to avoid clutter. (For the same reason,
variant terms specified using {{para|var|1}} aren't normally categorized into informative categories but are placed
in tracking categories.)
===About properties and data objects===
[fill in]
]==]
local inflection_to_cats_and_label = {
plural = {
filter_pos = function(posvars)
-- plurals also occur with determiners, adjectives etc. and we don't want to generate categories like
-- 'countable determiners', 'countable adjectives', etc. Note that the passed-in `simp_plpos` has
-- `proper nouns` converted to `nouns`.
return posvars.simp_plpos == "Danh từ"
end,
cats = {"Danh từ đếm được"},
no_cats = {"Danh từ không đếm được"},
fixed_adj_label = "đếm được",
fixed_adj_no_label = "không đếm được",
sometimes_label = "đếm được và không đếm được",
unattested_cats = "Danh từ có số nhiều chưa được chứng thực",
unknown_cats = "Danh từ có số nhiều không rõ hoặc chưa được chứng thực",
},
comparative = {
cats = {"{simp_plpos} so sánh được"},
no_cats = {"{simp_plpos} không so sánh được"},
fixed_adj_label = "so sánh được",
},
["female equivalent"] = {
cats = {"{simp_plpos} with other-gender equivalents"},
},
["male equivalent"] = {
cats = {"{simp_plpos} with other-gender equivalents"},
},
}
local param_mods = {
alt = {}, -- always allow so e.g. - can be specified as a suffix form
id = {}, -- disabled when `is_head = true`
q = {type = "qualifier"},
qq = {type = "qualifier"},
l = {type = "labels"},
ll = {type = "labels"},
-- [[Module:headword]] expects part references in `.refs`.
ref = {item_dest = "refs", type = "references", store = "insert-flattened"},
}
local optional_param_mods = {
g = {item_dest = "genders", type = "genders"},
lang = {type = "language"},
sc = {type = "script"},
t = {item_dest = "gloss"},
gloss = {},
pos = {},
ng = {},
lit = {},
tr = {},
ts = {},
face = {},
nolinkinfl = {type = "boolean"},
}
local optional_headword_param_mods = {
sc = {type = "script"},
tr = {},
ts = {},
}
local function split_on_comma(val)
if val:find(",") then
return require(parse_interface_module).split_on_comma(val)
else
return {val}
end
end
local function ine(val)
if not val then
return nil
end
val = mw.text.trim(val)
return val ~= "" and val or nil
end
--[=[
Add decorations to a term. `termobj` is the object describing 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`;
`text` is the text of the term itself, and `lang` is the language object.
]=]
local function add_decorations(text, termobj, lang)
local function field_non_empty(field)
local list = termobj[field]
if not list then
return nil
end
if type(list) ~= "table" then
error(("Internal error: Wrong type for `termobj.%s`=%s, should be \"table\""):format(
field, mw.dumpObject(list)))
end
return list[1]
end
if field_non_empty("q") or field_non_empty("qq") or field_non_empty("l") or field_non_empty("ll") or
field_non_empty("refs") then
text = format_decorations {
lang = lang,
text = text,
q = termobj.q,
qq = termobj.qq,
l = termobj.l,
ll = termobj.ll,
refs = termobj.refs,
}
end
return text
end
--[==[
Parse a single inflection or headword form or list of such forms. In either case, inline modifiers may be attached.
`data` is an object with the following fields:
* `val`: The raw value to parse. Required.
* `paramname`: The name of the parameter from which the value was taken; used in error messages. Required.
* `is_head`: We are parsing a headword parameter (a value which goes into the `heads` field of `data`). This changes
the allowed modifiers, disabling the `id` modifier and only allowing a subset of optional modifiers.
* `frob`: An optional function of one value to apply to the form after inline modifiers have been removed (i.e. to
apply to the `.term` field of the returned object).
* `include_mods`: List of extra inline modifiers to include, besides the default ones (see below). Each list item is
either a string specifying a recognized extra inline modifier (see `optional_param_mods` in the code), or a two-item
list of modifier name and modifier spec, where the spec should follow the syntax for modifier specs in
`parse_inline_modifiers` in [[Module:parse utilities]].
* `exclude_mods`: List of default inline modifiers to not include.
* `splitchar`: If specified, the value in `val` can be a list of forms to parse, separated by the value of `splitchar`
(which is a Lua pattern, as in `parse_inline_modifiers` in [[Module:parse utilities]]). Most commonly, `splitchar` is
a single comma and the values are comma-separated (in this case, splitting will not happen if a space follows the
comma).
* `parse_lang_prefix`: If specified, allow a language prefix to precede a form, and if found, store into the `.lang`
field of the returned object.
* `disable_special_modes`: If specified, don't recognize and treat specially the special mode signals (e.g. '-' for
no inflection, '?' for unknown, etc.).
* `preserve_splitchar`, `delimiter_key`, `escape_fun`, `unescape_fun`, `pre_normalize_modifiers`: As in
`parse_inline_modifiers` in [[Module:parse utilities]].
Returns an object suitable for storing as one element of one of the lists in `headdata.inflections`, where `headdata`
is the structure passed to [[Module:headword]]. If `splitchar` is specified, howeve, the return value is a list of such
objects.
The following default inline modifiers are currently recognized:
* `q`: Left qualifier.
* `qq`: Right qualifier.
* `l`: Comma-separated list of left labels. No space should follow the comma.
* `ll`: Comma-separated list of right labels. No space should follow the comma.
* `ref`: Reference or references. See {{tl|IPA}} for the syntax.
* `id`: Sense ID, in case there are multiple senses. See {{tl|l}}.
The following are the recognized additional inline modifiers:
* `g`: Comma-separated list of genders.
* `alt`: Display text.
* `lang`: Language code of language of the form, if different from the language of the headword.
* `sc`: Script code of script of the form. Almost never needed.
* `t`: Gloss for the form.
* `gloss`: Gloss for the form (alias for `t`).
* `pos`: Part of speech of the form.
* `lit`: Literal meaning of the form.
* `tr`: Manual transliteration of the form.
* `ts`: Transcription of the form, for languages where the transliteration differs markedly from the pronunciation.
* `face`: Face to display the form in, e.g. {"hypothetical"} for a hypothetical form (unlinkable and displayed in italics).
* `nolinkinfl`: Make the form unlinkable.
]==]
function export.parse_term_with_modifiers(data)
local paramname, val, frob = data.paramname, data.val, data.frob
local function generate_obj(term, parse_err)
if not data.disable_special_modes and export.special_modes[term] then
return {
term = term,
_is_mode = true
}
end
if frob then
term = frob(term, parse_err)
end
if data.parse_lang_prefix and term:find(":") then
return require(parse_utilities_module).generate_obj_maybe_parsing_lang_prefix {
term = term,
paramname = paramname,
parse_lang_prefix = true,
parse_err = parse_err,
}
else
return {term = term}
end
end
-- Check for inline modifier, e.g. מרים<tr:Miryem>. But exclude top-level HTML entry with <span ...>,
-- <sup> or similar in it.
if (val:find("<", nil, true) or data.splitchar) and not term_contains_top_level_html(val) and
-- don't parse inline modifiers if is_head and the value begins with a ~ (link modifier syntax)
(not data.is_head or not val:find("^~")) then
local param_mods = param_mods
if data.is_head then
param_mods = shallow_copy(param_mods)
param_mods.id = nil
end
if data.include_mods or data.exclude_mods then
if not data.is_head then
-- already copied when data.is_head
param_mods = shallow_copy(param_mods)
end
if data.include_mods then
local optional_mods = data.is_head and optional_headword_param_mods or optional_param_mods
for _, mod in ipairs(data.include_mods) do
if type(mod) == "table" then
if #mod ~= 2 then
error(("Internal error: Modifier spec %s in `include_mods` should be of length 2"):format(
dump(mod)))
end
local modkey, modvalue = unpack(mod)
param_mods[modkey] = modvalue
elseif not optional_mods[mod] then
error(("Internal error: Unrecognized modifier spec %s in `include_mods`"):format(
dump(mod)))
else
param_mods[mod] = optional_mods[mod]
end
end
end
if data.exclude_mods then
for _, mod in ipairs(data.exclude_mods) do
if not param_mods[mod] then
error(("Internal error: Modifier spec %s in `exclude_mods` not found among existing modifiers"
):format(dump(mod)))
else
param_mods[mod] = nil
end
end
end
end
return parse_inline_modifiers(val, {
paramname = paramname,
param_mods = param_mods,
generate_obj = generate_obj,
splitchar = data.splitchar,
preserve_splitchar = data.preserve_splitchar,
delimiter_key = data.delimiter_key,
escape_fun = data.escape_fun,
unescape_fun = data.unescape_fun,
pre_normalize_modifiers = data.pre_normalize_modifiers,
})
else
local retval = generate_obj(val)
if data.splitchar then
retval = {retval}
end
return retval
end
end
--[==[
Parse a list of inflection forms that may have inline modifiers attached. `data` is an object with the following fields:
* `forms`: The list of raw values to parse. Required.
* `paramname`: The name of the first parameter from which the value was taken; used in error messages. If this is a
two-element list, the first element is the first parameter and the second element is the prefix of the remaining
parameters. Parameter names that are numbers are handled correctly, as are those with \1 in it marking where the
parameter index goes. Required.
* `qualifiers`: If specified, a possibly gappy list of left qualifiers to add to the parsed terms (for compatibility
purposes).
* `splitchar`: As in `parse_term_with_modifiers()`. The resulting per-term lists will be flattened.
* `frob`, `include_mods`, `exclude_mods`, `is_head`, `preserve_splitchar`, `parse_lang_prefix`, `delimiter_key`,
`escape_fun`, `unescape_fun`, `pre_normalize_modifiers`: As in `parse_term_with_modifiers()`.
Returns a list of objects, suitable for storing as one of the lists in `headdata.inflections` (once a label is added),
where `headdata` is the structure passed to [[Module:headword]].
]==]
function export.parse_term_list_with_modifiers(data)
local paramname, forms = data.paramname, data.forms
local qualifiers = data.qualifiers
local first, restpref
if type(paramname) == "table" then
first = paramname[1]
restpref = paramname[2]
else
first = paramname
restpref = paramname
end
local terms = {}
data = shallow_copy(data)
for i, val in ipairs(forms) do
data.paramname = i == 1 and first or type(restpref) == "number" and restpref + i - 1 or
restpref:find("\1", nil, true) and restpref:gsub("\1", tostring(i)) or restpref .. i
data.val = val
local parsed = export.parse_term_with_modifiers(data)
if qualifiers and qualifiers[i] then
if data.splitchar then
for _, term in ipairs(parsed) do
term.q = {qualifiers[i]}
end
else
parsed.q = {qualifiers[i]}
end
end
if data.splitchar then
extend(terms, parsed)
else
terms[i] = parsed
end
end
return terms
end
--[==[
Construct a link to [[Appendix:Glossary]] for `entry`. If `text` is specified, it is the display text; otherwise,
`entry` is used.
]==]
function export.glossary_link(entry, text)
text = text or entry
return "[[Appendix:Glossary#" .. entry .. "|" .. text .. "]]"
end
function export.replace_glossary_links_in_label(label)
if label:find("<<", nil, true) then
label = label:gsub("<<(.-)|(.-)>>", export.glossary_link):gsub("<<(.-)>>", export.glossary_link)
end
return label
end
-- If a part of speech ends with one of these, either pluralized or followed by ' forms', chop off whatever precedes.
-- Also, if a part of speech is of the form `BASIC_POS FOO forms` e.g. `noun construct forms`, remove the `FOO`
-- portion. Note that "phrase" is *NOT* listed here because 'prepositional phrase' and 'postpositional phrase' are not
-- types of 'phrase' per the definition of these parts of speech in Wiktionary.
local basic_poses = {
"Tính từ",
"Phó từ",
"Từ rút gọn",
"Danh từ riêng", -- may be converted to noun
"Danh từ",
"Phân từ",
"Đại từ",
"Ký hiệu",
"Động từ",
}
local function optimized_pattern_escape(pos)
if pos:find("-", nil, true) then
return pattern_escape(pos)
else
return pos
end
end
--[==[
Canonicalize and then simplify a qualified part of speech (e.g. ''comparative adjectives'', ''past participles'',
''pronoun possessive forms'') to its unqualified equivalent (e.g. ''adjectives'', ''participles'', ''pronoun forms''').
This is used for categorization, since generally the simplified part of speech is used in categories other than the one
for the part of speech itself. (For example, comparative adjectives are normally categorized as adjectives in categories
such as `Requests for inflections in ``lang`` adjective entries`, and proper nouns are, sometimes at least, categorized
under ` ``lang`` nouns with ...` rather than ` ``lang`` proper nouns with ...`.)
There are two cases where simplification does not happen: phrases and (optionally) proper nouns. In Wiktionary,
''phrase'' really means a clause or similar constituent (or occasionally non-constituent), so `prepositional phrases`
are not types of `phrases` and are not simplified. Proper nouns are simplified to nouns for some categories but left
as-is in other category names; by default, this function simplifies `proper nouns` to `nouns`, but if the parameter
`preserve_proper_nouns` is given, `proper nouns` is left as-is.
If `preserve_lemma_status` is given, the function attempts to return a part of speech that has the same lemma status
(lemma or non-lemma form) as the original part of speech; thus, `comparative adjectives` becomes `adjective forms`
and `diminutive nouns` becomes `noun forms` because both are non-lemma forms. If `preserve_lemma_status` is not given,
the function simply chops off the qualifiers, converting e.g. `comparative adjectives` to `adjectives` even though this
changes the lemma status of the part of speech.
The part of speech can be given singular or plural on input, but is always returned in plural form.
]==]
function export.simplify_pos(pos, preserve_proper_nouns, preserve_lemma_status)
-- This allows for both singular and plural parts of speech in 'pos'.
local plpos = canonicalize_pos(pos)
for _, basic_pos in ipairs(basic_poses) do
if basic_pos ~= "Danh từ riêng" or preserve_proper_nouns then
if plpos == "Biến thể hình thái " .. lcfirst(basic_pos) then
-- So 'proper noun forms' doesn't get converted to 'noun forms' in the next round
return plpos
end
local basic_pos_pattern = optimized_pattern_escape(basic_pos)
if plpos:find(("^Biến thể hình thái %s .*.$"):format(basic_pos_pattern)) then
return "Biến thể hình thái " .. lcfirst(basic_pos)
end
if plpos:find(("^Biến thể hình thái .* %s$"):format(basic_pos_pattern)) then
return "Biến thể hình thái " .. lcfirst(basic_pos)
end
local pl_basic_pos = basic_pos
if plpos == pl_basic_pos then
-- So 'proper nouns' doesn't get converted to 'nouns' in the next round
return plpos
end
local pl_basic_pos_pattern = optimized_pattern_escape(pl_basic_pos)
if plpos:find(("^.* %s$"):format(pl_basic_pos_pattern)) then
if preserve_lemma_status then
local lemma_status = require(headword_module).pos_lemma_or_nonlemma(plpos)
if lemma_status == "non-lemma form" then
return "Biến thể hình thái " .. lcfirst(basic_pos)
end
end
return pl_basic_pos
end
end
end
return plpos
end
function export.get_pos_variants(pos)
local retval = {}
retval.origpos = pos
retval.plpos = canonicalize_pos(pos)
retval.sgpos = singularize(retval.plpos)
retval.simp_plpos = export.simplify_pos(retval.plpos)
retval.simp_sgpos = singularize(retval.simp_plpos)
retval.simp_with_propn_plpos = export.simplify_pos(retval.plpos, "preserve_proper_nouns")
retval.simp_with_propn_sgpos = singularize(retval.simp_with_propn_plpos)
return retval
end
--[==[
Canonicalize a single term or term-like object or a list of either into a list of term-like objects. `abterms` is the
term or list to canonicalize, and `field` is the name of the field holding the term (defaulting to {"term"}). This
does the minimal work necessary, meaning that the return value may partly or completely share memory with the value
passed in. As a special case, if `abterms` is {nil}, {nil} is returned. If `origin` or `originating_term` are specified,
add fields `_origin` and/or `_originating_term` containing the specified value(s) to each resulting term-like object (in
this case, the object will be copied a necessary to avoid side-effecting the passed-in objects). `origin` is intended to
be a string indicating where the term originated from (e.g. {"default"} if a default value, {"resolve_special"} if
originating from a shortcut value such as {"+"}) and `originating_term` is the shortcut term itself, if any. (There is
generally no such term for default values.) If `termobj_for_string_terms` is specified, any terms in `abterms` specified
as strings will have all their properties other than `field` taken from `termobj_for_string_terms`.
]==]
function export.canonicalize_termobj_list(abterms, field, origin, originating_term, termobj_for_string_terms)
if abterms == nil then
return nil
end
local function make_termobj_from_string_term(term)
local retval
if termobj_for_string_terms then
retval = shallow_copy(termobj_for_string_terms)
else
retval = {}
end
retval[field] = term
retval._origin = origin
retval._originating_term = originating_term
return retval
end
local function shallow_copy_and_add_origin(termobj)
termobj = shallow_copy(termobj)
termobj._origin = origin
termobj._originating_term = originating_term
return termobj
end
field = field or "term"
if type(abterms) == "string" then
return {make_termobj_from_string_term(abterms)}
elseif not abterms[1] then
if origin ~= nil or originating_term ~= nil then
abterms = shallow_copy_and_add_origin(abterms)
end
return {abterms}
else
-- Check if already in full list term and return directly if so (unless `origin_val` is given, in which case we
-- need to shallow-copy both the list and each term in it).
local must_convert = false
for _, term in ipairs(abterms) do
if type(term) == "string" then
must_convert = true
break
end
end
if not must_convert then
if origin ~= nil or originating_term ~= nil then
abterms = shallow_copy(abterms)
for i, abterm in ipairs(abterms) do
abterms[i] = shallow_copy_and_add_origin(abterm)
end
end
return abterms
end
end
local retval = {}
for _, term in ipairs(abterms) do
if type(term) == "string" then
insert(retval, make_termobj_from_string_term(term))
else
if origin ~= nil or originating_term ~= nil then
term = shallow_copy_and_add_origin(term)
end
insert(retval, term)
end
end
return retval
end
function export.canonicalize_category_list(abcats)
-- FIXME: We should allow category objects in `headdata.categories` and not just strings. This seems to require
-- changes to [[Module:headword]].
if abcats == nil then
return nil
end
if abcats == false then
return {}
end
if type(abcats) == "string" then
abcats = {abcats}
end
return abcats
end
--[==[
Insert a fixed inflection (a label not associated with any inflection values) into an `inflections` field. The
`inflections` field will be initialized if needed. `data` is an object with the following fields:
* `headdata`: The headword structure passed to [[Module:headword]]. Required.
* `inflobj`: The object whose `inflections` field the terms are inserted into. Defaults to `headdata`. Only needs
to be set for nested inflections, which are specified for an inflection object rather than the headword data
structure as a whole.
* `label`: The label that the inflections are given; any parts of the label surrounded in `<<...>>` are linked to the
glossary. (If the contents of `<<...>>` contain a `|` in them, they are a two-part link.) Required.
* `originating_term`: The term object from which this label is derived. If specified, decorations will be taken from
this object.
]==]
function export.insert_fixed_inflection(data)
local headdata, origterm, label = data.headdata, data.originating_term, data.label
local inflobj = data.inflobj or headdata
inflobj.inflections = inflobj.inflections or {}
if not origterm then
insert(inflobj.inflections, {
label = export.replace_glossary_links_in_label(label)
})
else
if origterm.id then
error(("It doesn't make sense to pass in an ID '%s' for label '%s' in conjunction with a term value '%s'"
):format(origterm.id, label, origterm.term))
end
origterm = shallow_copy(origterm)
-- Preserve decorations
origterm.term = nil
origterm.label = export.replace_glossary_links_in_label(label)
insert(inflobj.inflections, origterm)
end
end
local function yes_and_no_cats(props)
if props.cats and props.no_cats then
local yes_cats = export.canonicalize_category_list(props.cats)
local no_cats = export.canonicalize_category_list(props.no_cats)
local cats = shallow_copy(yes_cats)
extend(cats, no_cats)
return cats
end
end
local function sometimes_or_yes_and_no_cats(props)
local sometimes_cats = export.canonicalize_category_list(props.sometimes_cats)
if sometimes_cats then
return sometimes_cats
else
return yes_and_no_cats(props)
end
end
local function no_value_fallback_label(props)
if props.fixed_adj_no_label then
return props.fixed_adj_no_label
end
if props.fixed_adj_label then
return "không " .. props.fixed_adj_label
end
return "không " .. (props.label_for_cats_and_modes or props.label)
end
local function with_value_fallback_label(props)
if props.fixed_adj_label then
return props.fixed_adj_label
end
-- FIXME, should this say 'with a plural/diminutive' etc. or is 'with plural/diminutive' enough?
return "với " .. (props.label_for_cats_and_modes or props.label)
end
--[==[ var:
Table describing how to handle the "mode" signals at the beginning of an inflection, generally indicating whether the
inflection exists and to what extent. The key is the mode signal itself, and the value is table with the following keys:
* `exists`: The value to return in the `exists` field of the object returned by `process_for_insertion`. This is
currently used in calling code simply to check for different modes; it is an easier-to-read equivalent of the mode
signal itself.
* `prefix`: The prefix used to directly specify the label and category/categories associated with the mode in calling
code. For example, the mode `--+` (which also arises when `-` is used with a following term) has prefix `usually_no_`,
which means that to directly specify its label, use `usually_no_label` and to directly specify its categories, use
`usually_no_cats`. If these fields don't exist, the functions in `fallback_label` and `fallback_cats` are used to
generate the label and categories.
* `fallback_label`: This is a function of one argument, `props` (the properties passed in by the caller), which should
generate the label if the user didn't directly specify a label (see `prefix` above).
* `fallback_cats`: This is a function of one argument, `props` (the properties passed in by the caller), which should
generate the categories if the user didn't directly specify them (see `prefix` above).
* `add_default`: If true, the code will attempt to add a default value if the user specifies this mode without giving
additional terms. This depends on the caller passing in a `mode_default` property (which can be a string, a term
object, a list of either, or a function generating any of those values). In many cases the `mode_default` property
may simply have a value such as {"+"}, which is processed later by resolve_special().
* `disallow_additional`: If true, no additional terms are allowed after the mode signal; if present, an error is
thrown.
]==]
export.special_modes = {
["-"] = {exists = "không", prefix = "no_",
fallback_label = no_value_fallback_label,
}, -- converted to "--+" if a term follows
["--+"] = {exists = "usually no", prefix = "usually_no_", add_default = true,
fallback_label = function(props)
if props.no_label then
return "thường " .. props.no_label
end
return "thường " .. no_value_fallback_label(props)
end,
fallback_cats = sometimes_or_yes_and_no_cats,
},
["+-"] = {exists = "đôi khi", prefix = "sometimes_", add_default = true,
fallback_label = function(props)
return "đôi khi " .. with_value_fallback_label(props)
end,
fallback_cats = yes_and_no_cats,
},
["++-"] = {exists = "thường", prefix = "usually_", add_default = true,
fallback_label = function(props)
return "đôi khi " .. with_value_fallback_label(props)
end,
fallback_cats = sometimes_or_yes_and_no_cats,
},
["!"] = {exists = "chưa được chứng thực", prefix = "unattested_", disallow_additional = true,
fallback_label = function(props)
return (props.label_for_cats_and_modes or props.label) .. " chưa được chứng thực"
end,
-- No default cats; provided for plurals in inflection_to_cats_and_label.
},
["?"] = {exists = "unknown", prefix = "unknown_", disallow_additional = true,
-- No default label as people have preferred not to see 'plural unknown or uncertain'
-- written out. No default cats; provided for plurals in inflection_to_cats_and_label.
},
}
function export.process_for_insertion(data)
local terms, mode_default, headdata = data.terms, data.mode_default, data.headdata
local retval = {
headdata = data.headdata,
}
if terms and terms[1] then
local mode = terms[1].term
local modespec = export.special_modes[mode]
if modespec then
terms = shallow_copy(terms)
retval.mode_term = terms[1]
remove(terms, 1)
if mode == "-" and terms[1] then
mode = "--+"
end
if terms[1] and modespec.disallow_additional then
error(("It doesn't make sense to specify explicit terms along with mode signal %s (%s)"):format(
mode, modespec.exists))
elseif not terms[1] and modespec.add_default and mode_default then
if type(mode_default) == "function" then
mode_default = mode_default(headdata, retval.mode_term)
end
if mode_default then
terms = export.canonicalize_termobj_list(mode_default, "term", "mode_default", retval.mode_term)
end
end
retval.numterms = #terms
retval.terms = terms
retval.mode = mode
retval.exists = modespec.exists
retval.prefix = modespec.prefix
else
retval.numterms = #terms
retval.terms = terms
retval.exists = "yes"
retval.prefix = ""
end
else
retval.numterms = 0
retval.terms = {}
-- retval.exists = nil
retval.request = data.request
end
return retval
end
--[==[
Insert previously-parsed terms into an `inflections` field. The `inflections` field will be initialized if needed.
`data` is an object with the following fields:
* `headdata`: The headword structure passed to [[Module:headword]]. Required.
* `inflobj`: The object whose `inflections` field the terms are inserted into. Defaults to `headdata`. Only needs
to be set for nested inflections, which are specified for an inflection object rather than the headword data
structure as a whole.
* `terms`: The list of parsed terms. If {nil} or omitted, nothing happens unless `request` is set.
* `label`: The label that the inflections are given; any parts of the label surrounded in `<<...>>` are linked to the
glossary. (If the contents of `<<...>>` contain a `|` in them, they are a two-part link.) Required.
* `fixed_adj_label`: If specified, an adjective used to construct the defaults for the fixed labels specified by
`no_label`, `usually_no_label` and `sometimes_label`. Examples are "comparable" for a comparative and "countable"
for a plural.
* `no_label`: If the term is {"-"} and there are no other terms, insert a fixed label with this value. If
`fixed_adj_label` is given, defaults to {"not "} plus the adjective, else {"no "} plus the label.
* `usually_no_label`: If the term is {"-"} and there are other terms, insert a fixed label with this value. Defaults to
{"usually "} plus the value of `no_label`.
* `sometimes_label`: If the term is {"+-"}, insert a fixed label with this value. If `fixed_adj_label` is given,
defaults to {"sometimes "} plus the adjective, else {"sometimes with a(n) "} plus the label.
* `cats`: List of categories to insert when terms are given that are not {"-"}. Each category is a string naming a full
category to insert (including the appropriate language name prefixed).
* `no_cats`: List of categories to insert when a term is given as {"-"}.
* `usually_no_cats`: List of categories to insert when a term is given as {"-"} and additional terms are specified as
well (representing, e.g. for the inflection {"plural"}, a term which usually has no plural but does under some
circumstances). If omitted, both the categories in `cats` and `no_cats` are inserted.
* `sometimes_cats`: List of categories to insert when a term is given as {"+-"} (indicating that the inflection
sometimes exists and sometimes does not, normally depending on the precise sense involved). If omitted, both the
categories in `cats` and `no_cats` are inserted (same default as for `usually_no_cats`).
* `accel`: If specified, a full accelerator object to add to the inflections.
* `request`: If specified and no terms are given, insert a label with a request for inflections to be given.
* `enable_auto_translit`: If specified and terms are given, display automatic transliteration of the terms.
The return value indicates whether the inflection exists and how many terms are in it. It is an object with the
following fields:
* `exists`: {"yes"} if one or more terms were specified; {"no"} if the value was given as {"-"}; {"usually no"} if
the first value was given as {"-"} but additional terms were supplied; otherwise {nil}, indicating that the status
is unspecified.
* `numterms`: Number of terms in the inflection. Will be 0 unless `exists` has the value {"yes"} or {"usually no"}.
* `terms`: The terms that were inserted. If there are no terms, this is an empty list.
* `request`: True if no terms were specified but a term request was inserted into the inflection (because
`data.request` was specified). Otherwise {nil}.
]==]
function export.insert_processed_inflection(data)
local headdata, processed, label = data.headdata, data.processed, data.label
local inflobj = data.inflobj or headdata
local accel = data.accel
if data.accel_form then
if accel then
error("Internal error: can't specify both data.accel and data.accel_form")
end
if headdata.heads then
local lemmas = {}
local lemma_translits = {}
for i, headobj in ipairs(headdata.heads) do
lemmas[i] = headobj.term
if lemmas[i] == "+" then
error("Internal error: If you use data.accel_form, you should have resolved all occurrences of + in heads appropriately")
end
lemma_translits[i] = headobj.tr
end
accel = {
lemma = lemmas,
lemma_translit = lemma_translits,
form = data.accel_form,
}
else
accel = {
form = data.accel_form,
}
end
end
local function insert_cats(cats)
for _, cat in ipairs(cats) do
insert(headdata.categories, cat)
end
end
-- Handle the fixed label (if any) and cats (if any) associated with a mode.
if processed.mode then
local modespec = export.special_modes[processed.mode]
local mode_prefix = modespec.prefix
local fixed_label = data[mode_prefix .. "label"]
if not fixed_label and modespec.fallback_label then
fixed_label = modespec.fallback_label(data)
end
if fixed_label then
export.insert_fixed_inflection {
headdata = headdata,
inflobj = inflobj,
originating_term = processed.mode_term,
label = fixed_label,
}
end
local mode_cats = data[mode_prefix .. "cats"]
if not mode_cats and modespec.fallback_cats then
mode_cats = modespec.fallback_cats(data)
end
mode_cats = export.canonicalize_category_list(mode_cats)
if mode_cats then
insert_cats(mode_cats)
end
elseif processed.terms[1] and data.cats then
-- If no mode, no fixed label but there may be categories.
insert_cats(data.cats)
end
local terms = processed.terms
if terms[1] then
-- FIXME, if there's a mode we shallow_copy() twice.
terms = shallow_copy(terms)
terms.label = export.replace_glossary_links_in_label(label)
if accel then
terms.accel = accel
end
terms.enable_auto_translit = data.enable_auto_translit
inflobj.inflections = inflobj.inflections or {}
insert(inflobj.inflections, terms)
elseif processed.request then
inflobj.inflections = inflobj.inflections or {}
insert(inflobj.inflections, {
label = export.replace_glossary_links_in_label(label),
request = true,
})
end
end
--[==[
Insert previously-parsed terms into an `inflections` field. The `inflections` field will be initialized if needed.
`data` is an object with the following fields:
* `headdata`: The headword structure passed to [[Module:headword]]. Required.
* `inflobj`: The object whose `inflections` field the terms are inserted into. Defaults to `headdata`. Only needs
to be set for nested inflections, which are specified for an inflection object rather than the headword data
structure as a whole.
* `terms`: The list of parsed terms. If {nil} or omitted, nothing happens unless `request` is set.
* `label`: The label that the inflections are given; any parts of the label surrounded in `<<...>>` are linked to the
glossary. (If the contents of `<<...>>` contain a `|` in them, they are a two-part link.) Required.
* `fixed_adj_label`: If specified, an adjective used to construct the defaults for the fixed labels specified by
`no_label`, `usually_no_label` and `sometimes_label`. Examples are "comparable" for a comparative and "countable"
for a plural.
* `no_label`: If the term is {"-"} and there are no other terms, insert a fixed label with this value. If
`fixed_adj_label` is given, defaults to {"not "} plus the adjective, else {"no "} plus the label.
* `usually_no_label`: If the term is {"-"} and there are other terms, insert a fixed label with this value. Defaults to
{"usually "} plus the value of `no_label`.
* `sometimes_label`: If the term is {"+-"}, insert a fixed label with this value. If `fixed_adj_label` is given,
defaults to {"sometimes "} plus the adjective, else {"sometimes with a(n) "} plus the label.
* `cats`: List of categories to insert when terms are given that are not {"-"}. Each category is a string naming a full
category to insert (including the appropriate language name prefixed).
* `no_cats`: List of categories to insert when a term is given as {"-"}.
* `usually_no_cats`: List of categories to insert when a term is given as {"-"} and additional terms are specified as
well (representing, e.g. for the inflection {"plural"}, a term which usually has no plural but does under some
circumstances). If omitted, both the categories in `cats` and `no_cats` are inserted.
* `sometimes_cats`: List of categories to insert when a term is given as {"+-"} (indicating that the inflection
sometimes exists and sometimes does not, normally depending on the precise sense involved). If omitted, both the
categories in `cats` and `no_cats` are inserted (same default as for `usually_no_cats`).
* `accel`: If specified, a full accelerator object to add to the inflections.
* `request`: If specified and no terms are given, insert a label with a request for inflections to be given.
* `enable_auto_translit`: If specified and terms are given, display automatic transliteration of the terms.
The return value indicates whether the inflection exists and how many terms are in it. It is an object with the
following fields:
* `exists`: {"yes"} if one or more terms were specified; {"no"} if the value was given as {"-"}; {"usually no"} if
the first value was given as {"-"} but additional terms were supplied; otherwise {nil}, indicating that the status
is unspecified.
* `numterms`: Number of terms in the inflection. Will be 0 unless `exists` has the value {"yes"} or {"usually no"}.
* `terms`: The terms that were inserted. If there are no terms, this is an empty list.
* `request`: True if no terms were specified but a term request was inserted into the inflection (because
`data.request` was specified). Otherwise {nil}.
]==]
function export.insert_inflection(data)
local headdata, terms, mode_default = data.headdata, data.terms, data.mode_default
local processed = export.process_for_insertion {
headdata = headdata,
terms = terms,
mode_default = mode_default,
}
data = shallow_copy(data)
data.processed = processed
export.insert_processed_inflection(data)
return processed
end
--[==[
Parse raw arguments from `forms` for inline modifiers, and insert the resulting terms (which should not require
significant additional processing) into `headdata.inflections`. `data` is an object with the following fields:
* `forms`: The list of raw values to parse. If {nil} or omitted, nothing happens.
* `headdata`: The headword structure passed to [[Module:headword]]. Required.
* `paramname`: As in `parse_term_list_with_modifiers()`. Required.
* `label`: As in `insert_inflection()`. Required.
* `qualifiers`, `frob`, `include_mods`, `exclude_mods`, `is_head`, `splitchar`, `preserve_splitchar`, `delimiter_key`,
`escape_fun`, `unescape_fun`, `pre_normalize_modifiers`: As in `parse_term_list_with_modifiers()`.
* `accel`: As in `insert_inflection()`.
Return value is as in `insert_inflection()`.
]==]
function export.parse_and_insert_inflection(data)
local forms = data.forms
if forms and forms[1] then
data = shallow_copy(data)
data.forms = forms
data.terms = export.parse_term_list_with_modifiers(data)
return export.insert_inflection(data)
end
return {
numterms = 0
}
end
--[==[
Combine two sets of decorations. If either is {nil}, just return the other, and if both are {nil}, return {nil}.
]==]
function export.combine_decorations(decs1, decs2)
if not decs1 and not decs2 then
return nil
end
if not decs1 then
return decs2
end
if not decs2 then
return decs1
end
local combined = shallow_copy(decs1)
for _, dec in ipairs(decs2) do
insert_if_not(combined, dec)
end
return combined
end
function export.combine_qualifiers_or_labels(...)
-- FIXME: Đã thêm vào ngày 17 tháng 9 năm 2026.
error("Vui lòng dùng combine_decorations")
end
--[==[
Combine the decorations (qualifiers, labels, references) and ID's of two term objects. `destobj` is the "destination
term object" into which the combined properties are written, and `srcobj` is the "source object" into which the
properties are merged. `destobj` is side-effected (but the lists inside of `destobj` are not); if this is undesirable,
make sure to shallow-copy `destobj` first. If both objects have values for a given decoration, the values of `destobj`
come first. If both objects have a value for `id`, the values must match or an error is thrown; otherwise, the resulting
value of `id` comes from whichever one is defined.
'''NOTE:''' This may not be the correct behavior when deduplicating a list of term objects. See
`insert_termobj_combining_duplicates` for a different approach.
]==]
function export.combine_termobj_decorations(destobj, srcobj)
destobj.q = export.combine_decorations(destobj.q, srcobj.q)
destobj.qq = export.combine_decorations(destobj.qq, srcobj.qq)
destobj.l = export.combine_decorations(destobj.l, srcobj.l)
destobj.ll = export.combine_decorations(destobj.ll, srcobj.ll)
destobj.refs = export.combine_decorations(destobj.refs, srcobj.refs)
if destobj.id and srcobj.id and destobj.id ~= srcobj.id then
-- FIXME: We probably want to pass in an error function
error(("Can't specify two different ID's %s and %s when combining objects"):format(srcobj.id, destobj.id))
end
destobj.id = destobj.id or srcobj.id
return destobj
end
function export.combine_termobj_qualifiers_labels(...)
-- FIXME: Đã thêm vào ngày 17 tháng 9 năm 2026.
error("Vui lòng dùng combine_termobj_decorations")
end
function export.termobj_has_decorations(obj)
return obj.q and obj.q[1] or obj.qq and obj.qq[1] or obj.l and obj.l[1] or obj.ll and obj.ll[1] or
obj.refs and obj.refs[1]
end
function export.termobj_has_qualifiers_or_labels(...)
-- FIXME: Đã thêm vào ngày 17 tháng 9 năm 2026.
error("Vui lòng dùng termobj_has_decorations")
end
local function one_decoration_equal(prop1, prop2)
local prop1_is_nil = not prop1 or not prop1[1]
local prop2_is_nil = not prop2 or not prop2[1]
if prop1_is_nil and prop2_is_nil then
return true
end
if prop1_is_nil or prop2_is_nil then
return false
end
return deep_equals(prop1, prop2)
end
function export.termobj_decorations_equal(obj1, obj2)
return one_decoration_equal(obj1.q, obj2.q) and
one_decoration_equal(obj1.qq, obj2.qq) and
one_decoration_equal(obj1.l, obj2.l) and
one_decoration_equal(obj1.ll, obj2.ll) and
one_decoration_equal(obj1.refs, obj2.refs) and
obj1.id == obj2.id
end
function export.termobj_ancillary_properties_equal(...)
-- FIXME: Đã thêm vào ngày 17 tháng 9 năm 2026.
error("Vui lòng dùng termobj_decorations_equal")
end
function export.convert_termobj_to_formobj(termobj)
local formobj = {
form = termobj.term,
translit = termobj.tr,
}
local footnotes
local function mods_to_footnote(mod_prefix, mod_vals)
if mod_vals and mod_vals[1] then
footnotes = footnotes or {}
for _, val in ipairs(mod_vals) do
insert(footnotes, "[" .. mod_prefix .. ":" .. val .. "]")
end
end
end
mods_to_footnote("q", termobj.q)
mods_to_footnote("qq", termobj.qq)
mods_to_footnote("l", termobj.l)
mods_to_footnote("ll", termobj.ll)
mods_to_footnote("ref", termobj.refs)
mods_to_footnote("id", termobj.id and {termobj.id} or nil)
formobj.footnotes = footnotes
return formobj
end
local recognized_multi_mods = {
q = "q",
qq = "qq",
l = "l",
ll = "ll",
ref = "refs",
}
local recognized_single_mods = {
id = "id",
}
function export.add_footnote_to_termobj(termobj, footnote)
local stripped_footnote = footnote:match("^%[(.*)%]$")
if not stripped_footnote then
error("Internal error: Footnote should be surrounded by brackets at this stage: " .. footnote)
end
local prefix, rest = stripped_footnote:match("^([a-z]+):(.+)$")
local field, is_multi
if prefix then
if recognized_multi_mods[prefix] then
field = recognized_multi_mods[prefix]
is_multi = true
elseif recognized_single_mods[prefix] then
field = recognized_single_mods[prefix]
is_multi = false
end
end
if not field then
rest = stripped_footnote
field = "l"
is_multi = true
end
if is_multi then
if not termobj[field] then
termobj[field] = {}
end
insert(termobj[field], rest)
else
if termobj[field] and termobj[field] ~= rest then
error(("Can't set two values for '%s': '%s' and '%s'"):format(field, termobj[field], rest))
end
termobj[field] = rest
end
end
function export.convert_formobj_to_termobj(formobj)
local termobj = {
term = formobj.form,
tr = formobj.translit,
}
if formobj.footnotes then
for _, footnote in ipairs(formobj.footnotes) do
export.add_footnote_to_termobj(termobj, footnote)
end
end
return termobj
end
local function extract_termobj_field_modifiers(fieldval)
return fieldval:match("^([*+]?)(.*)$")
end
function export.remove_termobj_field_modifiers(termobj)
local function remove_field_modifiers(field)
if termobj[field] and termobj[field][1] then
local any_field_modifiers = false
for _, val in ipairs(termobj[field]) do
local field_mods, _ = extract_termobj_field_modifiers(val)
if field_mods ~= "" then
any_field_modifiers = true
break
end
end
local new_field = {}
if any_field_modifiers then
for _, val in ipairs(termobj[field]) do
local _, field_without_mods = extract_termobj_field_modifiers(val)
insert_if_not(new_field, field_without_mods)
end
termobj[field] = new_field
end
end
end
remove_field_modifiers("q")
remove_field_modifiers("qq")
remove_field_modifiers("l")
remove_field_modifiers("ll")
remove_field_modifiers("refs")
end
function export.insert_termobj_combining_duplicates(destobjs, termobj)
for _, destobj in ipairs(destobjs) do
if destobj.term == termobj.term and destobj.tr == termobj.tr then
-- Form already present; maybe combine footnotes.
local function combine_field_values(field)
if termobj[field] and termobj[field][1] then
-- Check to see if there are existing values with *; if so, remove them.
if destobj[field] and destobj[field][1] then
local any_values_with_asterisk = false
for _, val in ipairs(destobj[field]) do
local field_mods, _ = extract_termobj_field_modifiers(val)
if field_mods:find("%*") then
any_values_with_asterisk = true
break
end
end
if any_values_with_asterisk then
local filtered_values = {}
for _, val in ipairs(destobj[field]) do
local field_mods, _ = extract_termobj_field_modifiers(val)
if not field_mods:find("%*") then
insert(filtered_values, val)
end
end
if filtered_values[1] then
destobj[field] = filtered_values
else
destobj[field] = nil
end
end
end
local any_footnotes_with_plus = false
for _, val in ipairs(termobj[field]) do
local field_mods, _ = extract_termobj_field_modifiers(val)
if field_mods:find("%+") then
any_footnotes_with_plus = true
break
end
end
if any_footnotes_with_plus then
if not destobj[field] then
destobj[field] = {}
else
destobj[field] = shallow_copy(destobj[field])
end
for _, val in ipairs(termobj[field]) do
local already_seen = false
local field_mods, field_without_mods = extract_termobj_field_modifiers(val)
if field_mods:find("%+") then
for _, existing_val in ipairs(destobj[field]) do
local _, existing_field_without_mods =
extract_termobj_field_modifiers(existing_val)
if existing_field_without_mods == field_without_mods then
already_seen = true
break
end
end
if not already_seen then
insert(destobj[field], val)
end
end
end
end
end
end
combine_field_values("q")
combine_field_values("qq")
combine_field_values("l")
combine_field_values("ll")
combine_field_values("refs")
if destobj.id and termobj.id and destobj.id ~= termobj.id then
-- FIXME: We probably want to pass in an error function
error(("Can't specify two different ID's %s and %s when combining objects"):format(termobj.id, destobj.id))
end
destobj.id = destobj.id or termobj.id
return
end
end
insert(destobjs, termobj)
end
export.allowed_special_indicators = {
["first"] = true,
["first-second"] = true,
["first-last"] = true,
["second"] = true,
["last"] = true,
["each"] = true,
["+"] = true, -- requests the default behavior with preposition handling
}
--[==[
Check for special indicators (values such as {"+first"} or {"+first-last"} that are used in a `pl`, `f`, etc. argument
and indicate how to inflect a multiword term). If `form` is such an indicator, the return value is `form` minus
the initial `+` sign; otherwise, if form begins with a `+` sign, an error is thrown; otherwise the return value is nil.
]==]
function export.get_special_indicator(form, noerror)
if form:find("^%+") then
form = form:gsub("^%+", "")
if not export.allowed_special_indicators[form] then
if noerror then
return nil
end
local indicators = {}
for indic, _ in pairs(export.allowed_special_indicators) do
insert(indicators, "+" .. indic)
end
sort(indicators)
error("Special inflection indicator beginning with '+' can only be " ..
mw.text.listToText(indicators) .. ": +" .. form)
end
return form
end
return nil
end
local function add_endings(bases, endings)
local retval = {}
if type(bases) ~= "table" then
bases = {bases}
end
if type(endings) ~= "table" then
endings = {endings}
end
for _, base in ipairs(bases) do
for _, ending in ipairs(endings) do
insert(retval, base .. ending)
end
end
return retval
end
--[==[
Inflect a possibly multiword or hyphenated term `form` using the function `inflect`, which is a function of one argument
that is called on a single word to inflect and should return either the inflected word or a list of inflected words.
`special` indicates how to inflect the multiword term and should be e.g. {"first"} to inflect only the first word,
{"first-last"} to inflect the first and last words, {"each"} to inflect each word, etc. See `allowed_special_indicators`
above for the possibilities. If `special` is `+`, or is omitted and the term is multiword (i.e. containing a space
character), and `prepositions` is supplied, the function checks for multiword or hyphenated terms containing the
prepositions in `prepositions`, e.g. Italian [[senso di marcia]] or [[medaglia d'oro]] or Portuguese
[[tartaruga-do-mar]]. If such a term is found, only the first word is inflected. Otherwise, the default is
{"first-last"}. `prepositions` is a list of Lua patterns matching prepositions. The patterns will automatically have the
separator character (space or hyphen) added to the left side but not the right side, so they should contain a space
character (which will automatically be converted to the appropriate separator) on the right side unless the preposition
is joined on the right side with an apostrophe. Examples of preposition patterns for Italian are {"di "}, {"sull'"} and
{"d?all[oae] "} (which matches {"dallo "}, {"dalle "}, {"alla "}, etc.).
The return value is always either a list of inflected multiword or hyphenated terms, or nil if `special` is omitted
and `form` is not multiword. (If `special` is specified and `form` is not multiword or hyphenated, an error results.)
]==]
function export.handle_multiword(form, special, inflect, prepositions, sep)
sep = sep or form:find(" ") and " " or "%-"
local raw_sep = sep == " " and " " or "-"
-- Used to add regex version of separator in the replacement portion of ugsub() or :gsub()
local sep_replacement = sep == " " and " " or "%%-"
-- Given a Lua pattern, replace space with the appropriate separator.
local function hack_re(re)
if sep == " " then
return re
end
return (re:gsub(" ", sep_replacement))
end
if special == "first" then
local first, rest = form:match(hack_re("^(.-)( .*)$"))
if not first then
error("Special indicator 'first' can only be used with a multiword term: " .. form)
end
return add_endings(inflect(first), rest)
elseif special == "second" then
local first, second, rest = form:match(hack_re("^([^ ]+ )([^ ]+)( .*)$"))
if not first then
error("Special indicator 'second' can only be used with a term with three or more words: " .. form)
end
return add_endings(add_endings({first}, inflect(second)), rest)
elseif special == "first-second" then
local first, space, second, rest = form:match(hack_re("^([^ ]+)( )([^ ]+)( .*)$"))
if not first then
error("Special indicator 'first-second' can only be used with a term with three or more words: " .. form)
end
return add_endings(add_endings(add_endings(inflect(first), space), inflect(second)), rest)
elseif special == "each" then
local terms = split(form, sep)
if #terms < 2 then
error("Special indicator 'each' can only be used with a multiword term: " .. form)
end
for i, term in ipairs(terms) do
terms[i] = inflect(term)
if i > 1 then
terms[i] = add_endings(raw_sep, terms[i])
end
end
local result = ""
for _, term in ipairs(terms) do
result = add_endings(result, term)
end
return result
elseif special == "first-last" then
local first, middle, last = form:match(hack_re("^(.-)( .* )(.-)$"))
if not first then
first, middle, last = form:match(hack_re("^(.-)( )(.*)$"))
end
if not first then
error("Special indicator 'first-last' can only be used with a multiword term: " .. form)
end
return add_endings(add_endings(inflect(first), middle), inflect(last))
elseif special == "last" then
local rest, last = form:match(hack_re("^(.* )(.-)$"))
if not rest then
error("Special indicator 'last' can only be used with a multiword term: " .. form)
end
return add_endings(rest, inflect(last))
elseif special and special ~= "+" then
error("Unrecognized special=" .. special)
end
-- Only do default behavior if special indicator '+' explicitly given or separator is space; otherwise we will
-- break existing behavior with hyphenated words.
if (special == "+" or sep == " ") and form:find(sep) then
if prepositions then
-- check for prepositions in the middle of the word; do it this way so we can handle
-- more than one word before the preposition (and usually inflect each word)
for _, prep in ipairs(prepositions) do
local first, space_prep_rest = umatch(form, hack_re("^(.-)( " .. prep .. ".*)$"))
if first then
return add_endings(inflect(first), space_prep_rest)
end
end
end
-- multiword or hyphenated expressions default to first-last; we need to pass in the separator to avoid
-- problems with multiword terms containing hyphens in the individual words
return export.handle_multiword(form, "first-last", inflect, prepositions, sep)
end
return nil
end
local function link_hyphen_split_component(word, data)
if data.link_hyphen_split_component then
return data.link_hyphen_split_component(word)
else
return "[[" .. word .. "]]"
end
end
-- Default function to split a word on apostrophes. Don't split apostrophes at the beginning or end of a word (e.g.
-- [['ndrangheta]] or [[po']]). Handle multiple apostrophes correctly, e.g. [[l'altr'ieri]] -> [[l']][altr']][[ieri]].
function export.default_split_apostrophe(word, data)
local apostrophe_parts = split(word, "'", true, true)
local linked_apostrophe_parts = {}
local apostrophes_at_beginning = ""
local i = 1
-- Apostrophes at beginning get attached to the first word after (which will always exist but may
-- be blank if the word consists only of apostrophes).
while i < #apostrophe_parts do -- <, not <=, in case the word consists only of apostrophes
local apostrophe_part = apostrophe_parts[i]
i = i + 1
if apostrophe_part == "" then
apostrophes_at_beginning = apostrophes_at_beginning .. "'"
else
break
end
end
apostrophe_parts[i] = apostrophes_at_beginning .. apostrophe_parts[i]
-- Now, do the remaining parts. A blank part indicates more than one apostrophe in a row; we join
-- all of them to the preceding word.
while i <= #apostrophe_parts do
local apostrophe_part = apostrophe_parts[i]
if apostrophe_part == "" then
linked_apostrophe_parts[#linked_apostrophe_parts] =
linked_apostrophe_parts[#linked_apostrophe_parts] .. "'"
elseif i == #apostrophe_parts then
insert(linked_apostrophe_parts, apostrophe_part)
else
insert(linked_apostrophe_parts, apostrophe_part .. "'")
end
i = i + 1
end
for j, tolink in ipairs(linked_apostrophe_parts) do
linked_apostrophe_parts[j] = link_hyphen_split_component(tolink, data)
end
return concat(linked_apostrophe_parts)
end
--[=[
Auto-add links to a word that should not have spaces but may have hyphens and/or apostrophes. We split off final
punctuation, then split on hyphens if `data.split_hyphen` is given, and also split on apostrophes if
`data.split_apostrophe` is given. We only split on hyphens if they are in the middle of the word, not at the beginning
or end (hyphens at the beginning or end indicate suffixes or prefixes, respectively). `include_hyphen_prefixes`, if
given, is a set of prefixes (not including the final hyphen) where we should include the final hyphen in the prefix.
Hence, e.g. if "anti" is in the set, a Portuguese word like [[anti-herói]] "anti-hero" will be split [[anti-]][[herói]]
(whereas a word like [[código-fonte]] "source code" will be split as [[código]]-[[fonte]]).
If `data.split_apostrophe` is specified, we split on apostrophes unless `data.no_split_apostrophe_words` is given and
the word is in the specified set, such as French [[c'est]] and [[quelqu'un]]. If `data.split_apostrophe` is true, the
default algorithm applies, which splits on all apostrophes except those at the beginning and end of a word (as in
Italian [['ndrangheta]] or [[po']]), and includes the apostrophe in the link to its left (so we auto-split French
[[l'eau]] as [[l']][[eau]] and [[l'altr'ieri]] as [[l']][altr']][[ieri]]). If `data.split_apostrophe` is specified
but not `true`, it should be a function of one argument that does custom apostrophe-splitting. The argument is the word
to split, and the return value should be the split and linked word.
]=]
local function add_single_word_links(space_word, data, term_has_spaces)
local space_word_no_punct, punct
local punct_pattern = data.punctuation
if punct_pattern and is_callable(punct_pattern) then
space_word_no_punct, punct = punct_pattern(space_word)
else
if punct_pattern == nil then
punct_pattern = "[,;:?!]"
end
space_word_no_punct, punct = umatch(space_word, "^(.*)(" .. punct_pattern .. ")$")
end
space_word_no_punct = space_word_no_punct or space_word
punct = punct or ""
local words
if space_word_no_punct:sub(1, 1) == "-" or space_word_no_punct:sub(-1) == "-" then
-- don't split prefixes and suffixes
words = {space_word_no_punct}
else
local splitter
if term_has_spaces then
splitter = data.split_hyphen_when_space
else
splitter = data.split_hyphen_when_no_space
end
if is_callable(splitter) then
words = splitter(space_word_no_punct)
if type(words) == "string" then
return words .. punct
end
end
end
if not words then
local split_hyphen
if term_has_spaces then
split_hyphen = data.split_hyphen_when_space
else
split_hyphen = data.split_hyphen_when_no_space
if split_hyphen == nil then -- default to true; use `false` to avoid this
split_hyphen = true
end
end
if split_hyphen then
words = split(space_word_no_punct, "-", true, true)
else
words = {space_word_no_punct}
end
end
local linked_words = {}
for j, word in ipairs(words) do
if j < #words and data.include_hyphen_prefixes and data.include_hyphen_prefixes[word] then
word = "[[" .. word .. "-]]"
elseif j > 1 and data.include_hyphen_suffixes and data.include_hyphen_suffixes[word] then
word = "[[-" .. word .. "]]"
else
-- Don't split on apostrophes if the word is in `no_split_apostrophe_words`.
if (not data.no_split_apostrophe_words or not data.no_split_apostrophe_words[word]) and
data.split_apostrophe and word:find("'", nil, true) then
if data.split_apostrophe == true then
word = export.default_split_apostrophe(word, data)
else -- custom apostrophe splitter/linker
word = data.split_apostrophe(word)
end
elseif word ~= "" then -- avoid -[[]]- (e.g. f--k)
word = link_hyphen_split_component(word, data)
end
if j < #words then
word = word .. "-"
end
end
insert(linked_words, word)
end
return concat(linked_words) .. punct
end
--[=[
Auto-add links to a multiword term. `data` contains fields customizing how to do this. By default we proceed as follows:
(1) If the term already has embedded links in it, they are left unchanged.
(2) Otherwise, if there are spaces present, we split on spaces and link each word separately.
(3) If a given space-separated component ends in punctuation (defaulting to [,;:?!]), it is separated off, the remainder
of the algorithm run, and the punctuation pasted back on.
(4) If there are hyphens in a given space-separated component, we may link each hyphenated term separately depending
on the settings in `data`. Normally the hyphens are not included in the linked terms, but this can be overridden
for specific prefixes and/or suffixes. By default, if there are spaces in the multiword term, we do not link
hyphenated components (because of cases like "boire du petit-lait" where "petit-lait" should be linked as a whole),
but do so otherwise (e.g. for "avant-avant-hier"); this can overridden for cases like "croyez-le ou non".
Cases where only some of the hyphens should be split can always be handled by explicitly specifying the head (e.g.
"Nord-Pas-de-Calais" given as head=[[Nord]]-[[Pas-de-Calais]]).
(5) If there are apostrophes in a given component, we may link each apostrophe-separated term separately depending
on the settings in `data`, including the apostrophe in the link to its left (so we split "de l'eau" as
"[[de]] [[l']][[eau]]").
The settings in `data` are as follows:
`split_hyphen_when_no_space`: Whether to split on hyphens when the term has no spaces. Defaults to true if set to `nil`.
This can be a function of one argument, to implement a custom splitting algorithm for hyphen-separated terms. If
this returns [FIXME: FINISH ME ...]
If `data.split_apostrophe` is specified, we split on apostrophes unless `data.no_split_apostrophe_words` is given and
the word is in the specified set, such as French [[c'est]] and [[quelqu'un]]. If `data.split_apostrophe` is true, the
default algorithm applies, which splits on all apostrophes except those at the beginning and end of a word (as in
Italian [['ndrangheta]] or [[po']]), and includes the apostrophe in the link to its left (so we auto-split French
[[l'eau]] as [[l']][[eau]] and [[l'altr'ieri]] as [[l']][altr']][[ieri]]). If `data.split_apostrophe` is specified
but not `true`, it should be a function of one argument that does custom apostrophe-splitting. The argument is the word
to split, and the return value should be the split and linked word.
We don't always split on hyphens because of cases like "boire du petit-lait" where "petit-lait" should be linked as a
whole, but provide the option to do it for cases like "croyez-le ou non". If there's no space, however, then it makes
sense to split on hyphens by `no_split_apostrophe_words` and `include_hyphen_prefixes` allow for special-case handling
of particular words and are as described in the comment above add_single_word_links().
]=]
function export.add_links_to_multiword_term(term, data)
if term:match("[%[%]]") then
return term
end
local words = split(term, " ", true, true)
local term_has_spaces = #words > 1
local linked_words = {}
for _, word in ipairs(words) do
insert(linked_words, add_single_word_links(word, data, term_has_spaces))
end
local retval = concat(linked_words, " ")
-- If we ended up with a single link consisting of the entire term,
-- remove the link.
return retval:match("^%[%[([^%[%]]*)%]%]$") or retval
end
local function canonicalize_begin_end_spec(spec)
local from, to = spec:match("^(.-):(.*)$")
if not from then
from = spec
to = ""
end
return from, to
end
--[==[
Given a `linked_term` that is the output of add_links_to_multiword_term(), apply modifications as given in
`modifier_spec` to change the link destination of subterms (normally single-word non-lemma forms; sometimes
collections of adjacent words). This is usually used to link non-lemma forms to their corresponding lemma, but can
also be used to replace a span of adjacent separately-linked words to a single multiword lemma. The format of
`modifier_spec` is one or more semicolon-separated subterm specs, where each such spec is of the form
SUBTERM:DEST, where SUBTERM is one or more words in the `linked_term` but without brackets in them, and DEST is the
corresponding link destination to link the subterm to. Any occurrence of ~ in DEST is replaced with SUBTERM.
Alternatively, a single modifier spec can be of the form BEGIN[FROM:TO], which is equivalent to writing
BEGINFROM:BEGINTO (see example below).
For example, given the source phrase [[il bue che dice cornuto all'asino]] "the pot calling the kettle black"
(literally "the ox that calls the donkey horned/cuckolded"), the result of calling add_links_to_multiword_term()
is [[il]] [[bue]] [[che]] [[dice]] [[cornuto]] [[all']][[asino]]. With a modifier_spec of 'dice:dire', the result
is [[il]] [[bue]] [[che]] [[dire|dice]] [[cornuto]] [[all']][[asino]]. Here, based on the modifier spec, the
non-lemma form [[dice]] is replaced with the two-part link [[dire|dice]].
Another example: given the source phrase [[chi semina vento raccoglie tempesta]] "sow the wind, reap the whirlwind"
(literally (he) who sows wind gathers [the] tempest"). The result of calling add_links_to_multiword_term() is
[[chi]] [[semina]] [[vento]] [[raccoglie]] [[tempesta]], and with a modifier_spec of 'semina:~re; raccoglie:~re',
the result is [[chi]] [[seminare|semina]] [[vento]] [[raccogliere|raccoglie]] [[tempesta]]. Here we use the ~
notation to stand for the non-lemma form in the destination link.
A more complex example is [[se non hai altri moccoli puoi andare a letto al buio]], which becomes
[[se]] [[non]] [[hai]] [[altri]] [[moccoli]] [[puoi]] [[andare]] [[a]] [[letto]] [[al]] [[buio]] after calling
add_links_to_multiword_term(). With the following modifier_spec:
'hai:avere; altr[i:o]; moccol[i:o]; puoi: potere; andare a letto:~; al buio:~', the result of applying the spec is
[[se]] [[non]] [[avere|hai]] [[altro|altri]] [[moccolo|moccoli]] [[potere|puoi]] [[andare a letto]] [[al buio]].
Here, we rely on the alternative notation mentioned above for e.g. 'altr[i:o]', which is equivalent to 'altri:altro',
and link multiword subterms using e.g. 'andare a letto:~'. (The code knows how to handle multiword subexpressions
properly, and if the link text and destination are the same, only a single-part link is formed.)
]==]
function export.apply_link_modifiers(linked_term, modifier_spec, lang)
local split_modspecs = split(modifier_spec, "%s*;%s*")
for j, modspec in ipairs(split_modspecs) do
local id
if modspec:find("<") then
local rest
rest, id = modspec:match("^(.*)<id:(.-)>$")
if rest then
modspec = rest
end
end
local subterm, dest, otherlang
local begin_spec, rest, end_spec = modspec:match("^%[(.-)%]([^:]*)%[(.-)%]$")
if begin_spec then
local begin_from, begin_to = canonicalize_begin_end_spec(begin_spec)
local end_from, end_to = canonicalize_begin_end_spec(end_spec)
subterm = begin_from .. rest .. end_from
dest = begin_to .. rest .. end_to
end
if not subterm then
rest, end_spec = modspec:match("^([^:]*)%[(.-)%]$")
if rest then
local end_from, end_to = canonicalize_begin_end_spec(end_spec)
subterm = rest .. end_from
dest = rest .. end_to
end
end
if not subterm then
begin_spec, rest = modspec:match("^%[(.-)%]([^:]*)$")
if begin_spec then
local begin_from, begin_to = canonicalize_begin_end_spec(begin_spec)
subterm = begin_from .. rest
dest = begin_to .. rest
end
end
if not subterm then
subterm, dest = modspec:match("^(.-)%s*:%s*(.*)$")
if subterm and subterm ~= "^" and subterm ~= "$" then
local langdest
-- Parse off an initial language code (e.g. 'en:Higgs', 'la:minūtia' or 'grc:σκατός'). Also handle
-- Wikipedia prefixes ('w:Abatemarco' or 'w:it:Colle Val d'Elsa').
otherlang, langdest = dest:match("^([A-Za-z0-9._-]+):([^ ].*)$")
if otherlang == "w" then
local foreign_wikipedia, foreign_term = langdest:match("^([A-Za-z0-9._-]+):([^ ].*)$")
if foreign_wikipedia then
otherlang = otherlang .. ":" .. foreign_wikipedia
langdest = foreign_term
end
dest = ("%s:%s"):format(otherlang, langdest)
otherlang = nil
elseif otherlang then
otherlang = get_lang_by_code(otherlang, true, "allow etym")
dest = langdest
end
end
end
if not subterm then
if modspec == "?" or modspec == "!" then
subterm = "$"
dest = modspec
elseif modspec == "..." or modspec == "...?" then
subterm = "$"
dest = " " .. modspec
elseif modspec:find("^[A-Z]$") then
-- X, Y, etc. by themselves are unlinked, to help with snowclones
subterm = modspec
dest = "_"
else
subterm = modspec
dest = "~"
end
end
if subterm == "^" then
linked_term = dest:gsub("_", " ") .. linked_term
elseif subterm == "$" then
linked_term = linked_term .. dest:gsub("_", " ")
else
if subterm:find("[", nil, true) then
error(("Subterm '%s' in modifier spec '%s' cannot have brackets in it"):format(
escape_wikicode(subterm), escape_wikicode(modspec)))
end
local escaped_subterm = pattern_escape(subterm)
local subterm_re = "%[%[" .. escaped_subterm:gsub("(%%?[ ',%-])", "%%]*%1%%[*") .. "%]%]"
local expanded_dest
if dest:find("~", nil, true) then
expanded_dest = dest:gsub("~", replacement_escape(subterm))
else
expanded_dest = dest
end
if otherlang then
expanded_dest = expanded_dest .. "#" .. otherlang:getCanonicalName()
end
local subterm_replacement
if expanded_dest == "_" then
subterm_replacement = subterm
if id then
error("Can't supply <id:...> with an unlinked subterm")
end
if otherlang then
error("Can't supply prefixed language with an unlinked subterm")
end
elseif id or otherlang then
if id and expanded_dest:find("[", nil, true) then
error("Can't supply <id:...> with destination with embedded brackets")
end
subterm_replacement = require(links_module).language_link {
lang = otherlang or lang,
term = expanded_dest,
alt = subterm,
id = id,
}
elseif expanded_dest:find("[", nil, true) then
-- Use the destination directly if it has brackets in it (e.g. to put brackets around parts of a word).
subterm_replacement = expanded_dest
elseif expanded_dest == subterm then
subterm_replacement = "[[" .. subterm .. "]]"
else
subterm_replacement = "[[" .. expanded_dest .. "|" .. subterm .. "]]"
end
local escaped_subterm_replacement = replacement_escape(subterm_replacement)
local replaced_linked_term = ugsub(linked_term, subterm_re, escaped_subterm_replacement)
if replaced_linked_term == linked_term then
mw.log(("Attempted to replace %s with %s in %s"):format(subterm_re, escaped_subterm_replacement, linked_term))
error(("Subterm '%s' could not be located in %slinked expression %s, or replacement same as subterm"):format(
subterm, j > 1 and "intermediate " or "", escape_wikicode(linked_term)))
else
linked_term = replaced_linked_term
end
end
end
return linked_term
end
function export.format_validation_error_list(data)
local valid_list, valid_set, canonicalize = data.valid_list, data.valid_set, data.canonicalize
if not valid_list then
valid_list = {}
for valid_item, value in pairs(valid_set) do
if canonicalize then
if valid_item == value then
insert(valid_list, valid_item)
else
insert(valid_list, ("%s (mapped to %s)"):format(valid_item, value))
end
else
insert(valid_list, valid_item)
end
end
table.sort(valid_list)
end
return mw.text.listToText(valid_list, nil, " hoặc ")
end
function export.validation_error(data)
local item_type, item, valid_list, valid_set, canonicalize =
data.item_type, data.item, data.valid_list, data.valid_set, data.canonicalize
if not valid_list then
valid_list = {}
for valid_item, value in pairs(valid_set) do
if canonicalize then
if valid_item == value then
insert(valid_list, valid_item)
else
insert(valid_list, ("%s (mapped to %s)"):format(valid_item, value))
end
else
insert(valid_list, valid_item)
end
end
table.sort(valid_list)
end
error(("Invalid %s: %s; expected one of %s"):format(item_type, item,
export.format_validation_error_list {
valid_list = valid_list,
valid_set = valid_set,
canonicalize = canonicalize,
}
))
end
--[=[
Validate the items in `items` against the list or set of valid items in `valid_items`. If `field` is given, fetch the
item to check from that-named field of each object in `items`; otherwise use the items in `items` directly. If an error
occurs, `item_type` specifies the type of item to mention in the error message, which will also list the allowed items
(either taken directly from `valid_items` if a list, or from the sorted keys if a set).
If `canonicalize`, `valid_items` must be a set mapping items to their canonical equivalents, and the items in `items`
will be canonicalized accordingly and returned.
]=]
local function validate_items(data, canonicalize)
local items, field, valid_items, item_type =
data.items, data.field, data.valid_items, data.item_type
local valid_list, valid_set
if not canonicalize and valid_items[1] and is_array(valid_items) then
valid_list = valid_items
valid_set = list_to_set(valid_items)
else
valid_set = valid_items
end
for _, item in ipairs(items) do
if field then
item = item[field]
end
if not item._is_mode and not valid_set[item] then
export.validation_error {
item_type = item_type,
item = item,
valid_list = valid_list,
valid_set = not valid_list and valid_set or nil,
canonicalize = canonicalize,
}
end
end
end
function export.validate_genders(data)
local genders, valid_genders, gender_type, no_augment, canonicalize =
data.genders, data.valid_genders, data.gender_type, data.no_augment, data.canonicalize
if not genders then
return genders
end
gender_type = gender_type or "headword"
local valid_gender_set
if not data.canonicalize and valid_genders[1] and is_array(valid_genders) then
valid_gender_set = list_to_set(valid_genders)
else
valid_gender_set = valid_genders
end
local augmented_gender_set
if no_augment then
augmented_gender_set = valid_gender_set
else
augmented_gender_set = {}
for g, gval in pairs(valid_gender_set) do
augmented_gender_set[g] = gval
if g:find("^m") and not g:find("^mf") and valid_gender_set[g:gsub("^m", "f")] then
augmented_gender_set[g:gsub("^m", "mf")] = type(gval) == "string" and (gval:gsub("^m", "mf")) or gval
augmented_gender_set[g:gsub("^m", "mfbysense")] = type(gval) == "string" and (gval:gsub("^m", "mfbysense")) or gval
augmented_gender_set[g:gsub("^m", "mfequiv")] = type(gval) == "string" and (gval:gsub("^m", "mfequiv")) or gval
end
end
end
return validate_items {
items = genders,
field = "spec",
valid_items = augmented_gender_set,
item_type = ("%s gender"):format(gender_type),
canonicalize = canonicalize,
}
end
function export.has_plural_gender(genders)
local saw_p, saw_non_p
for _, val in ipairs(genders) do
local g = val.spec
if g:find("p$") then
saw_p = true
elseif g ~= "?" then
saw_non_p = true
end
end
return saw_p, saw_non_p
end
function export.is_plurale_tantum(genders)
local saw_p, saw_non_p = export.has_plural_gender(genders)
return saw_p and not saw_non_p
end
local Headdata = {}
function Headdata:resolve_prop(prop, ...)
if type(prop) == "function" then
prop = prop(self, ...)
end
return prop
end
function Headdata:get_pos_variants()
if self.process_props.pos_variants == nil then
self.process_props.pos_variants = export.get_pos_variants(self.pos_category)
end
return self.process_props.pos_variants
end
--[==[
Canonicalize a category. The category string will have the full language name (i.e. the name of the L2 language under
which an entry is inserted, which may a parent language if the language in question is an etymology-only language)
prepended to it, and any occurrences of `{plpos}` in the string replaced with the actual plural part of speech (with
some canonicalization; specifically, `proper nouns` is converted to `nouns` when replacing `{plpos}`). To specify a
full category and not have the language name prepended to it, precede it with {"Category:"}, which will be removed.
]==]
function Headdata:canonicalize_category(category)
if category:find("{") then
category = require(string_utilities_module).format(category, self:get_pos_variants())
end
if category:find("^Thể loại:") then
return (category:gsub("^Thể loại:", ""))
else
return category.. " " .. self.langfullname
end
end
--[==[
Canonicalize a list of categories according to the process described in `Headdata:canonicalize_category`. This simply
loops over each category in `categories` and calls `Headdata:canonicalize_category` on each one.
]==]
function Headdata:canonicalize_categories(categories)
if not categories then
return categories
end
categories = export.canonicalize_category_list(categories)
local canon_cats = {}
for _, cat in ipairs(categories) do
insert(canon_cats, self:canonicalize_category(cat))
end
return canon_cats
end
--[==[
Insert a category into the `categories` list in the headword `data` structure. `category` is normally a string naming
the category, which will have the full language name prepended to it and any occurrences of `{plpos}` in the string
replaced with the actual plural part of speech (with some canonicalization; specifically, `proper nouns` is converted to
`nouns` when replacing `{plpos}`). To specify a full category and not have the language name prepended to it, precede it
with {"Category:"}.
]==]
function Headdata:insert_category(category)
insert(self.categories, self:canonicalize_category(category))
end
--[==[
Validate the genders in `genders` (a list of gender spec objects, as produced by {type = "genders"} in
[[Module:parameters]] and accepted by [[Module:gender and number]]), checking that all specified genders are in the list
or set given in `valid_genders`. Optional `props` controls how the validation happens. In particular, unless
`props.no_augment` is given, then for any gender beginning with `m`, if a corresponding gender beginning with `f`
occurs, analogous genders beginning with `mf`, `mfbysense` and `mfequiv` are also allowed. For example, if `m-d`
(masculine dual) and `f-d` (feminine dual) both occur, genders `mf-d`, `mfbysense-d` and `mfequiv-d` are also allowed.
If a disallowed gender is given, an error occurs, giving the disallowed gender along with the list of all allowed
genders.
]==]
function Headdata:validate_genders(genders, valid_genders, props)
-- FIXME: This has no dependency on self. Uses should be converted to call export.validate_genders() directly.
if not genders then
return
end
props = props or {}
local gender_type, no_augment = props.gender_type, props.no_augment
export.validate_genders {
genders = genders,
valid_genders = valid_genders,
gender_type = gender_type,
no_augment = no_augment,
}
end
--[==[
Parse an inflection specified in `field`, the name of a parameter holding an inflection. If the parameter is numeric,
the field should be given as a number (as with the `params` structure passed to [[Module:parameters]]), not a string
containing the representation of a number. The field can specify multiple comma-separated terms, and each term can have
associated inline modifiers that will be parsed (unless there is top-level HTML in the parameter, i.e. HTML not
contained inside an inline modifier, e.g. as may be generated by using {{tl|l}} or similar template inside a parameter).
This is a wrapper around the top-level `parse_term_with_modifiers()` function. `props` is an optional structure
containing additional properties, including all additional properties documented for the top-level
`parse_term_with_modifiers()` function.
If the parameter in `field` is unspecified, the return value of this function will be an empty list, not {nil}, so it
is always safe to iterate over the return value.
By default, the allowed modifiers are the same as for `parse_term_with_modifiers()`, except that (normally) the
`<tr:...>` modifier will be allowed if `include_tr` was specified in the original call to `process_headword()`; likewise
for the `<ts:...>` modifier if `include_ts` was specified and the `<sc:...>` modifier if `include_sc` was specified. If
If you pass in your own `include_mods` list of additional allowed modifiers, it will (normally) automatically be
augmented with {"tr"}, {"ts"} and/or {"sc"} if `include_tr`, `include_ts` and/or `include_sc` was specified when calling
`process_headword()`. To disable automatic augmentation of these modifiers (whether or not you specify an `include_mods`
property), specify {no_augment_include_mods = true} in `props`.
]==]
function Headdata:parse_inflection(field, props)
local val = self.process_props.args[field]
if not val then
return {}
end
props = props and shallow_copy(props) or {}
local include_mods = props.include_mods
local calldata = self.process_props.calldata
if not props.no_augment_include_mods and (calldata.include_tr or calldata.include_ts or calldata.include_sc) then
include_mods = include_mods and shallow_copy(include_mods) or {}
if calldata.include_tr then
insert_if_not(include_mods, "tr")
end
if calldata.include_ts then
insert_if_not(include_mods, "ts")
end
if calldata.include_sc then
insert_if_not(include_mods, "sc")
end
end
props.val = val
props.paramname = field
props.splitchar = props.splitchar or ","
props.include_mods = include_mods
local orig_frob = props.frob
local function convert_tilde_to_pagename(term)
if orig_frob then
term = orig_frob(term)
end
if not term:find("~", nil, true) then
return term
end
term = term:gsub("\\~", "\1"):gsub("~", replacement_escape(self.pagename)):gsub("\1", "~")
return term
end
props.frob = convert_tilde_to_pagename
return export.parse_term_with_modifiers(props) or {}
end
function Headdata:process_for_insertion(terms, props)
props = props and shallow_copy(props) or {}
props.headdata = self
props.terms = terms
return export.process_for_insertion(props)
end
--[==[
Insert previously-parsed terms into the `inflections` of the headword `data` structure. This is a wrapper around
the top-level `insert_inflection()` function. `terms` is the list of parsed terms. (If {nil}, nothing happens unless
`request` is set in `props`.) `label` is the the label that the inflections are given; any parts of the label surrounded
in `<<...>>` are linked to the glossary. (If the contents of `<<...>>` contain a `|` in them, they are a two-part link.)
`props` is an optional structure containing additional properties, including all additional properties documented for
the top-level `insert_inflection()` function.
Unless `no_auto_cats` is given in `props`, certain labels automatically trigger the insertion of additional
categories in specific circumstances. This is controlled by the `inflection_to_cats_and_label` structure in
[[Module:headword utilities]]. For example, if the part of speech is {"nouns"} or {"proper nouns"} and the label (after
removing any links and `<<...>>` glossary specs) is {"plural"}, an additional category
<code><var>lang</var> countable nouns</code> will be added if a plural value is given (i.e. the value is not {"-"}). If
the value is {"-"} (which indicates that there is no plural and triggers the insertion of the fixed inflection label
{"no plural"}), <code><var>lang</var> uncountable nouns</code> will be inserted instead, and if both {"-"} and a value
are given (which triggers the insertion of the {"usually no plural"} fixed inflection label), both categories are added.
Similar categories are inserted when a comparative is given (with a label {"comparative"}), and if the label is
{"female equivalent"} or {"male equivalent"} and the value is not {"-"}, a category such as
<code><var>lang</var> nouns with other-gender equivalents</code> is inserted.
]==]
function Headdata:insert_processed_inflection(processed, label, props)
props = props and shallow_copy(props) or {}
if not props.no_auto_cats then
local bare_label = props.label_for_cats_and_modes or label
if bare_label:find("[[", nil, true) then
bare_label = require(links_module).remove_links(bare_label)
end
if bare_label:find("<<", nil, true) then
bare_label = bare_label:gsub("<<.-|(.-)>>", "%1"):gsub("<<(.-)>>", "%1")
end
local spec = inflection_to_cats_and_label[bare_label]
if spec and (not spec.filter_pos or spec.filter_pos(self:get_pos_variants())) then
-- Copy the properties other than filter_pos into `props`, but don't overwrite any existing values.
for specprop, specval in pairs(spec) do
if specprop ~= "filter_pos" and props[specprop] == nil then
props[specprop] = specval
end
end
end
end
-- Now resolve and canonicalize the properties.
local canon_props = {}
for prop, val in pairs(props) do
if prop ~= "no_auto_cats" and (prop == "cats" or prop:find("_cats$")) then
val = self:canonicalize_categories(self:resolve_prop(val))
elseif prop:find("_label$") then
val = self:resolve_prop(val)
end
canon_props[prop] = val
end
canon_props.headdata = self
canon_props.processed = processed
canon_props.label = label
export.insert_processed_inflection(canon_props)
return processed
end
--[==[
Insert previously-parsed terms into the `inflections` of the headword `data` structure. This is a wrapper around
the top-level `insert_inflection()` function. `terms` is the list of parsed terms. (If {nil}, nothing happens unless
`request` is set in `props`.) `label` is the the label that the inflections are given; any parts of the label surrounded
in `<<...>>` are linked to the glossary. (If the contents of `<<...>>` contain a `|` in them, they are a two-part link.)
`props` is an optional structure containing additional properties, including all additional properties documented for
the top-level `insert_inflection()` function.
Unless `no_auto_cats` is given in `props`, certain labels automatically trigger the insertion of additional
categories in specific circumstances. This is controlled by the `inflection_to_cats_and_label` structure in
[[Module:headword utilities]]. For example, if the part of speech is {"nouns"} or {"proper nouns"} and the label (after
removing any links and `<<...>>` glossary specs) is {"plural"}, an additional category
<code><var>lang</var> countable nouns</code> will be added if a plural value is given (i.e. the value is not {"-"}). If
the value is {"-"} (which indicates that there is no plural and triggers the insertion of the fixed inflection label
{"no plural"}), <code><var>lang</var> uncountable nouns</code> will be inserted instead, and if both {"-"} and a value
are given (which triggers the insertion of the {"usually no plural"} fixed inflection label), both categories are added.
Similar categories are inserted when a comparative is given (with a label {"comparative"}), and if the label is
{"female equivalent"} or {"male equivalent"} and the value is not {"-"}, a category such as
<code><var>lang</var> nouns with other-gender equivalents</code> is inserted.
]==]
function Headdata:insert_inflection(terms, label, props)
local processed = self:process_for_insertion(terms, props)
self:insert_processed_inflection(processed, label, props)
return processed
end
--[==[
Insert a "fixed" inflection (a label without associated values) into the `inflections` table of the headword `data`
structure, labeled according to `label` (which can have glossary links in it specified using `<<...>>`, exactly as for
`:insert_inflection()`). An example label (from {{tl|mn-noun}} in [[Module:mn-headword]]) is {"hidden-g declension"},
specifying that the noun belongs to the hidden-''g'' declension. This is a direct wrapper around the top-level function
`insert_fixed_inflection()`; see that function for more details on optional `props`.
]==]
function Headdata:insert_fixed_inflection(label, props)
props = props and shallow_copy(props) or {}
props.headdata = self
props.label = label
export.insert_fixed_inflection(props)
end
--[==[
Parse the inflection(s) specified in `field` and insert them into the `inflections` table of the headword `data`
structure, labeled according to `label`. This is equivalent to calling {terms = data:parse_inflection(field, props)}
followed by {return data:insert_inflection(terms, label, props)} and behaves the same as the combination of those two
functions. See their documentation for more details.
]==]
function Headdata:parse_and_insert_inflection(field, label, props)
local terms = self:parse_inflection(field, props)
return self:insert_inflection(terms, label, props)
end
--[==[
Generate an inflection that may be specified explicitly or defaulted (which involves looping over the specified or
defaulted heads and determining the script of each one, since the formation of the default depends on the script).
`data` is the data object passed into the POS handler. `terms` is the list of terms to process. Those where the term
itself is not `+` will be returned unchanged, while those where the term is `+` will be handled by generating the
appropriate inflections from the headwords using `make_inflection` (which is passed three arguments, `head`, `tr` and
`sccode`, i.e. the script code of `head`) and should return two values, term and translit, either of which can be
nil. A nil head will be ignored, and otherwise the decorations specified on the `+` term will be combined with the
decorations specified on the head. The return value is a list of inflections where no requests for the default
inflection remain.
]==]
function Headdata:resolve_special(terms, handle_special, props)
props = props or {}
local infls = {}
local is_special = props.is_special or function(_data, infl) return infl.term == "+" end
for _, termobj in ipairs(terms) do
local is_special_retval
if type(is_special) == "function" then
is_special_retval = is_special(self, termobj)
elseif type(is_special) == "table" then
is_special_retval = contains(is_special, termobj.term)
else
error(("Internal error: When checking whether term is special, is_special is neither a function nor a " ..
"list, but is %s; term is %s"):format(dump(is_special), dump(termobj)))
end
if not is_special_retval then
insert(infls, termobj)
else
for _, headobj in ipairs(self.heads) do
local head = headobj.term or self.pagename
local head_no_links
if props.with_links then
head = head:find("%[") and head or require(headword_module).add_multiword_links(head, not headobj.term)
head_no_links = require(links_module).remove_links(head)
else
head = require(links_module).remove_links(head)
head_no_links = head
end
local newterms = handle_special {
headdata = self,
head = head,
head_no_links = head_no_links,
tr = headobj.tr,
infl = termobj,
sc = self.lang:findBestScript(head_no_links),
}
if newterms then
newterms = export.canonicalize_termobj_list(newterms, "term", "resolve_special", termobj)
for _, newterm in ipairs(newterms) do
if not props.no_combine_handle_special_retval_with_origin then
export.combine_termobj_decorations(newterm, termobj)
end
if not props.no_combine_handle_special_retval_with_head then
export.combine_termobj_decorations(newterm, headobj)
end
insert(infls, newterm)
end
end
end
end
end
return infls
end
--[==[
Add the current page to a tracking page named `Wiktionary:Tracking/``lang``-headword/``page```, where ``lang`` is the
language code of the current language. For example, if the current language is `mak` and `page` is {"redundant-lon"},
the current page will get added to the tracking page `Wiktionary:Tracking/mak-headword/redundant-lon`. All pages added
to that tracking page can be seen by going to [[Special:WhatLinksHere/Wiktionary:Tracking/mak-headword/redundant-lon]].
This is typically used to track issues occurring in user-specified parameters that do not rise to the level of errors
(e.g. redundant parameters, deprecated usages or other dispreferred values).
]==]
function Headdata:track(page)
return require(debug_track_module)(self.langcode .. "-headword/" .. page)
end
local boolean_param = {type = "boolean"}
--[==[
Process an arbitrary headword in an arbitrary language, handling generic and language-specific arguments and calling
`full_headword()` in [[Module:headword]]. This is intended for use in implementing headword modules (e.g.
[[Module:uz-headword]] for Uzbek, [[Module:mn-headword]] for Uzbek, [[Module:gsw-headword]] for Alemannic German, etc.)
and provides a general implementation of such modules. On input, `data` is an object with the following fields:
* `lang`: The language object of the language being handled. '''Required.''' Use the special value {false} to indicate
that the language is specified by the user in {{para|1}}.
* `frame`: The frame object passed into the `show()` function of your module, which implements headword-handling for
all parts of speech in the module, including a generic POS-handling template (e.g. {{tl|uz-head}} or {{tl|mn-head}}),
which allows arbitrary parts of speech to be handled. '''Required.'''
* `pos_functions`: A table listing, for each part of speech requiring special handling, the extra parameters (if any)
that the part of speech accepts, along with how to handle them. See the examples below. '''Required.'''
* `validate_lang`: If {lang = true} is specified, this is a function of one argument (a language object, based on the
language specified in {{para|1}}) that should throw an error if the language object is disallowed. If omitted, all
languages are allowed.
* `numbered_head`: If true, explicit headwords are specified in a numbered param instead of in {{para|head}}. The param
used is usually {{para|1}}, but is {{para|2}} for generic POS templates such as {{tl|mn-head}} or for POS-specific
templates when {lang = true} is specified (e.g. {{tl|arb-noun}}), and is {{para|3}} for generic POS templates when
{lang = true} is spcified (e.g. {{tl|arb-head}}).
* `include_tr`: If true, allow explicit transliterations to be specified. The transliteration(s) for the headword(s)
themselves is/are specified in {{para|tr}} or through the {{cd|<tr:...>}} inline modifier on headwords, and
transliterations of inflections are specified through the {{cd|<tr:...>}} inline modifier. This should generally be
given when a headword for the language may be in a script other than Latin.
* `include_ts`: If true, allow explicit transcriptions to be specified. The transcription(s) for the headword(s)
themselves is/are specified in {{para|ts}} or through the {{cd|<ts:...>}} inline modifier on headwords, and
transcriptions of inflections are specified through the {{cd|<ts:...>}} inline modifier. This should generally only
be given for certain languages where the spelling is radically different from the pronunciation (e.g. in cuneiform
languages such as Hittite and Akkadian, and potentially in Tibetan), and represents a pronunciation-based rendering
(usually not direct IPA).
* `include_sc`: If true, allow an explicit script code to be specified. The overall script code for the headword(s)
themselves can be specified using {{para|sc}}, and per-headword or per-inflection script codes are specified using the
{{cd|<sc:...>}} inline modifier. This should generally be given when a language supports multiple scripts.
* `infls`: An inflections structure specifying extra generic parameters that apply to all parts of speech and how to
handle them. The format is the same as for the `infls` structure in `pos_functions`.
* `augment_params`: A callback function to add extra generic parameters, or modify existing generic parameters in the
`params` structure; but in general, extra generic parameters should be added through the `infls` structure instead.
This callback should not be used to add part-of-speech-specific parameters; those are handled through the appropriate
setting in `pos_functions`. It is passed two single arguments, the `headdata` object and the `params` table to be
augmented. See below for extra fields stored in the `process_props` structure of the `headdata` object. This function
is called after initializing the `params` table and processing the overall `infls` structure, but just before adding
part-of-speech-specific parameters (from `pos_functions`) to `params`. Thus, it can override any generic parameters
but may itself be overridden by a part-of-speech-specific parameter.
* `augment_headdata`: A callback function to modify the `headdata` object passed to `full_headword()` in
[[Module:headword]]. This should not be used to for part-of-speech-specific parameter handling; this is handled
through the appropriate setting in `pos_functions`. It is passed a single argument, the `headdata` object, as for
`augment_params`; but the `process_props` structure and other fields will be more filled out, as this callback is
called later. This can be used, for example, to override the value of a generic setting in `headdata` (e.g.
[[Module:uz-headword]] uses this to mark non-Latin terms as variant forms by setting `headdata.var`, being careful not
to override a value already set by the user). This function is called after initializing the `headdata` table with all
information taken from generic parameters and processing generic parameters specified in the overall `infls`
structure, and just before processing the appropriate part-of-speech-specific `infls` structure in `pos_functions`
(which in turn is followed by any handler function in `pos_functions`). Thus, it can override any value set during
generic parameter processing but may itself be overridden by a part-of-speech-specific parameter or handler.
* `force_cat`: If true, add the headword to the appropriate categories even on non-mainspace pages. This can be used for
testing category handling in sample template calls on userspace test pages or template documentation pages. It should
not be set in production code.
* `enable_auto_translit`: If true, turn on automatic transliteration of inflections at a global level (i.e. applying to
all inflections). This has no effect on headwords, which are automatically transliterated by default if in a non-Latin
script and automatic transliteration is available for the language. You can also set this value for particular
inflections in the `insert_inflection()` function.
The `headdata` headword data structure has an extra field in it called `process_props` that is specific to the
`process_headword()` function, containing various extra properies. As the operation of `process_headword()` proceeds,
this object gets filled out with more fields. For example, once parameter parsing happens, the resulting values are
available in the `args` field of `process_props`. The following fields are found in `process_props` (note that
`pos_category`, the canonicalized plural part of speech of the headword being processed, is *not* present here; it's
directly on `headdata`):
* `namespace`: The name of the current namespace; an empty string for the mainspace. This references the namespace of
the actual page and isn't affected by the {{para|pagename}} parameter.
* `indexing_pos`: The canonicalized part of speech of the headword used to index into `pos_functions`. This is the
same as `poscat` for specific part-of-speech templates such as {{tl|uz-noun}}, but has the value {"head"} for generic
part-of-speech templates such as {{tl|uz-head}}. (Note that `poscat` is directly available on `headdata`.)
* `generic_pos_template`: True if a generic POS templates like {{tl|uz-head}} or {{tl|mn-head}} was used. (This is
signaled by omitting the invocation parameter {{para|1}} to `process_headword`.)
* `lang_in_1`: True if the language code is to be fetched from {{para|1}}.
* `pos_param`: The parameter holding the part of speech, if a generic POS tempalte like {{tl|mn-head}} is being
processed (i.e. `generic_pos_template` is set). In such a case, it will have the value of {1} or {2}, depending on
whether the language code is being fetched from {{para|1}} (see `lang_in_1`). Otherwise it will be {nil}.
* `head_param`: The parameter holding the explicit headword. If `numbered_head` was specified (as for Mongolian headword
templates), this has the value {1}, {2} or {3} depending on whether the language code is being fetched from {{para|1}}
(see `lang_in_1`) and whether a generic POS template like {{tl|mn-head}} is being processed (see
`generic_pos_template`). Otherwise, it has the value {"head"}. Also see the `lang` and `numbered_head` properties in
the `data` structure sent to `process_headword()`.
* `is_suffix`: True if the current term is a suffix. This is set when processing the `suffix`, `nosuffix` and `clitic`
parameters; it is always {false} beforehand (i.e. during `augment_params` and processing of the general `infls`
structure).
* `insert_specs`: This is a table mapping parameter names to the return value of `Headdata:insert_inflection()`, filled
out as parameter values are processed. This lets a given parameter processing function in `infls` gain access to the
result of calling `insert_inflection()` on previous parameters (which indicates the number of items inserted as well
as whether `-` was specified).
The `pos_functions` table contains an entry for each part of speech needing special handling, where the key is the
canonical plural part of speech (e.g. {"adverbs"} or {"proper nouns"}). The value associated with each key is a table
normally containing a field `infls`, listing the extra part-of-speech-specific inflection and other parameters along
with how to handle them. The specs in `infls` are used in three ways:
# to augment the `params` object passed to the `process()` function in [[Module:parameters]], specifying how to parse
the appropriate inflectional parameters;
# to specify how to process any inflectional parameters given and insert them into the `headdata` object passed to
`full_headword()` in [[Module:headword]];
# to generate appropriate documentation for the parameters and other changes made by the headword template (e.g.
inserting categories).
Alternatively, you can separately control the augmentation of the `params` object and the procesing of the resulting
arguments. This is done by specifying two fields in place of `infls`, named `params` and `func`. `params` is a table
containing extra parameters to add to the overall `params` object passed to the `process()` function in
[[Module:parameters]]. `func` is a function of two arguments, normally called `data` (the headword data structure
`headdata`) and `args` (the processed arguments table). However, this alternative method is not normally recommended
because it leads to duplication between the `params and `func` fields and the documentation, which must be manually
specified.
A simple example, as used to handle pronouns for Turoyo, is
{
local valid_genders = {"m", "f", "m-p", "f-p", "p", "?"}
pos_functions["pronouns"] = {
infls = {
{2, type = "genders", validate = valid_genders},
{"f", label = "feminine"},
{"pl", label = "plural"},
},
}
}
The equivalent using `params` and `func` is
{
local valid_genders = {"m", "f", "m-p", "f-p", "p", "?"}
pos_functions["pronouns"] = {
params = {
[2] = {type = "genders"},
f = true,
pl = true,
},
func = function(data, args)
data:validate_genders(args[2], valid_genders)
data.genders = args[2]
data:parse_and_insert_inflection("f", "feminine")
data:parse_and_insert_inflection("pl", "plural")
end
}
}
Note how the version with separate `params and `func` is longer and splits information on the parameters between the
two fields. The `params` structure sets extra user-specifiable parameters {{para|2}} for genders (since the headword is
in {{para|1}}) as well a {{para|f}} and {{para|pl}}, and the `func` handler processes those parameters. Note how this is
done by calling methods on the headword `data` structure. Each such parameter can have multiple comma-separated values,
and each value can have inline modifiers attached to it to specify further properties of the value. The `infls` version
ends up making the same method calls, but does it for you instead of you having to do it yourself.
These methods are implemented through a metatable set on the headword `data` structure, which is removed before calling
`full_headword()` in [[Module:headword]]. The methods access extra information related to headword processing (such as
the `args` table) that is stored in the `process_props` field of the headword `data` strucuture. This field is also
removed prior to calling `full_headword()`.
The methods available on the headword `data` structure are as follows. Each one also has its own documentation.
* {parse_inflection(field, props)}: Parse value(s) specified in `field` (a user-specified parameter in the `args` table)
and return a list of term objects. Optional `props` specifies additional properties controlling the parsing.
* {insert_inflection(terms, label, props)}: Insert the terms in `terms` (a list of term objects as returned by
`parse_inflection()`) into the `inflections` list in the headword `data` structure, giving the inflection the label as
specified in `label`. Optional `props` specifies additional properties controlling the parsing.
* {parse_and_insert_inflection(field, label, props)}: A combination of `parse_inflection()` and `insert_inflection()`,
if no further processing of the parsed values needs to be done before insertion.
* {insert_fixed_inflection(label, props)}: Insert a "fixed" inflection (a label without associated values) into the
`inflections` table. An example (from {{tl|mn-noun}} in [[Module:mn-headword]]) is {"hidden-g declension"}, specifying
that the noun belongs to the hidden-''g'' declension.
* {resolve_special(terms, handle_special, props)}: Resolve "special" indicators as specified by the user in an inflection
parameter. A typical example is {"+"}, requesting a default value. `terms` is the list of parsed term objects and
`handle_special` is a handler function to process special indicators and convert them to their actual values.
* {validate_genders(genders, valid_genders, props)}: Validate that the user-specified genders in `genders` all belong to
the list given in `valid_genders`, throwing an error if not.
* {insert_category(category)}: Insert a category into the `categories` list in the headword `data` structure. `category`
is normally a string naming the category, which will have the language prepended to it and any occurrences of
`{plpos}` in the string replaced with the actual plural part of speech.
]==]
function export.process_headword(data)
local data_lang, frame, pos_functions, validate_lang, numbered_head, include_tr, include_ts, include_sc, force_cat,
enable_auto_translit, checkredlinks, infls, augment_params, augment_headdata =
data.lang, data.frame, data.pos_functions, data.validate_lang, data.numbered_head, data.include_tr,
data.include_ts, data.include_sc, data.force_cat, data.enable_auto_translit, data.checkredlinks, data.infls,
data.augment_params, data.augment_headdata
local iparams = {
[1] = true,
def = true,
}
if data_lang == "invocation" or data_lang == "invocation-or-user" then
iparams.lang = true
end
local iargs = require(parameters_module).process(frame.args, iparams)
local parargs = frame:getParent().args
local lang
local function generate_data_lang_error()
error("Internal error: `data.lang` must be either a language object, \"invocation\" for a " ..
"language specified in lang= in the invocation arguments, \"user\" for a language specified in 1= in " ..
"the user template call, or \"invocation-or-user\" for either; saw " .. dump(data.lang))
end
if not data_lang then
generate_data_lang_error()
end
local lang_in_1
if type(data_lang) == "string" then
if data_lang == "invocation" then
lang = iargs.lang
if not lang then
error("Internal error: lang= must be specified in the invocation arguments")
end
elseif data_lang == "invocation-or-user" or data_lang == "user" then
if data_lang == "invocation-or-user" then
lang = iargs.lang
end
if not lang then
lang = ine(parargs[1])
if not lang then
error("Language code (see [[WT:Language codes]]) must be specified in 1=")
end
lang_in_1 = true
end
else
generate_data_lang_error()
end
lang = require(languages_module).getByCode(lang, lang_in_1 and 1 or "lang", true)
elseif type(data_lang) ~= "table" or not data_lang.hasType or not data_lang:hasType("language") then
generate_data_lang_error()
else
lang = data_lang
end
local langcode = lang:getCode()
if validate_lang then
if type(validate_lang) == "function" then
validate_lang(langcode)
else
validate_items {
items = {langcode},
valid_items = validate_lang,
item_type = "language code"
}
end
end
local template_indicated_pos = iargs[1]
local generic_pos_template = not template_indicated_pos
local pos_param
if generic_pos_template then
pos_param = lang_in_1 and 2 or 1
template_indicated_pos = ine(parargs[pos_param]) or
mw.title.getCurrentTitle().fullText == ("Template:%s-head"):format(langcode) and "interjection" or
error(("Part of speech must be specified in %s="):format(pos_param))
template_indicated_pos = canonicalize_pos(template_indicated_pos)
end
local head_param = numbered_head and (generic_pos_template and lang_in_1 and 3 or
(generic_pos_template or lang_in_1) and 2 or 1) or "head"
local indexing_pos
if generic_pos_template then
indexing_pos = "head." .. template_indicated_pos
if not pos_functions[indexing_pos] then
indexing_pos = "head"
end
else
indexing_pos = template_indicated_pos
end
local namespace = mw.loadData(headword_data_module).page.namespace
-- Partly initialize headdata now for use in generic infls callbacks. Will be further initialized later after
-- processing parameters.
local headdata = {
lang = lang,
langcode = langcode,
langfullcode = lang:getFullCode(),
langname = lang:getCanonicalName(),
langfullname = lang:getFullName(),
process_props = {
namespace = namespace,
calldata = data,
-- preserve template-indicated POS in case we normalize it or change it to "suffix"
template_indicated_pos = template_indicated_pos,
indexing_pos = indexing_pos,
generic_pos_template = generic_pos_template,
lang_in_1 = lang_in_1,
pos_param = pos_param,
head_param = head_param,
is_suffix = false,
processed_specs = {},
insert_specs = {},
},
-- We may change this below to "suffix" or normalize it.
pos_category = template_indicated_pos,
categories = {},
inflections = {enable_auto_translit = enable_auto_translit},
force_cat_output = force_cat,
no_redundant_head_cat = true,
}
setmetatable(headdata, {__index = Headdata})
local function resolve_prop(...)
return headdata:resolve_prop(...)
end
local params = {
[head_param] = {template_default = iargs.def},
head2 = {replaced_by = false, instead = ("use comma-separated |%s="):format(head_param)},
id = true,
sort = true,
cat = true,
nolink = boolean_param,
nolinkhead = {type = "boolean", alias_of = "nolink"},
suffix = boolean_param,
nosuffix = boolean_param,
-- If true, this is an inflectional suffix (forming a given person/number/gender/etc. part of the inflection of
-- a given part of speech, potentially restricted to a given inflectional class). We always treat these as
-- suffixes, rather than suffix forms, even if they form non-lemma forms (e.g. modern English -s and archaic
-- -eth form the third-singular present indicative of a verb, i.e. they form a verb form, but they are not
-- themselves forms of some other suffix, which is what a suffix form is). We also display the label of
-- inflectional suffixes specially, and add an additional category 'LANG inflectional suffixes' along with
-- categories like 'LANG POS inflectional suffixes' in place of 'LANG POS suffix forms' etc.
inflsuf = boolean_param,
clitic = {type = "boolean or string"},
addlpos = true,
var = {type = "boolean or string", set = {"both"}},
abbr = true,
json = boolean_param,
pagename = true, -- for testing
}
if include_sc then
params.sc = {type = "script"}
end
if include_tr then
params.tr = true
params.tr2 = {replaced_by = false, instead = "use comma-separated |tr= or <tr:...> inline modifier on head"}
end
if include_ts then
params.ts = true
params.ts2 = {replaced_by = false, instead = "use comma-separated |ts= or <ts:...> inline modifier on head"}
end
if lang_in_1 then
params[1] = {required = true} -- required but ignored as already processed above
end
if generic_pos_template then
params[pos_param] = {required = true} -- required but ignored as already processed above
end
local function augment_params_from_infls(infls)
infls = resolve_prop(infls)
for _, infl in ipairs(infls) do
local function interr(txt)
error(("Internal error: %s (coming from infls spec %s)"):format(txt, dump(infl)))
end
local param = infl[1]
if param then
param = resolve_prop(param)
if type(param) ~= "string" and type(param) ~= "number" then
interr(("Parameter name %s must be a string or number"):format(dump(param)))
end
-- We handle defaults as well as validation ourselves.
local typ = resolve_prop(infl.type) or "string"
if typ ~= "genders" and typ ~= "boolean" and typ ~= "string" then
-- FIXME: Handle more types.
interr(('Unrecognized type %s; can only currently handle "genders", "boolean" and "string" (the default)'):format(
dump(typ)))
end
params[param] = {type = typ, required = resolve_prop(infl.required), template_default = resolve_prop(infl.template_default)}
if typ ~= "boolean" then
local allow_and_ignore_list_params = resolve_prop(infl.allow_and_ignore_list_params)
local ignore_param
if type(allow_and_ignore_list_params) == "string" then
ignore_param = allow_and_ignore_list_params
elseif type(param) == "string" then
ignore_param = param
end
if ignore_param then
local old_list_param = allow_and_ignore_list_params and true or
{replaced_by = false, instead = ("use comma-separated |%s="):format(param)}
params[ignore_param .. "2"] = old_list_param
params[ignore_param .. "3"] = old_list_param
params[ignore_param .. "4"] = old_list_param
end
end
end
end
end
if infls then
augment_params_from_infls(infls)
end
if augment_params then
augment_params(headdata, params)
end
if pos_functions[indexing_pos] then
local pos_infls = pos_functions[indexing_pos].infls
if pos_infls then
augment_params_from_infls(pos_infls)
end
local pos_params = pos_functions[indexing_pos].params
if pos_params then
for key, val in pairs(pos_params) do
params[key] = val
end
end
end
local args = require("Module:parameters").process(parargs, params)
local pagename = args.pagename or mw.loadData(headword_data_module).pagename
local sc = args.sc or lang:findBestScript(pagename)
headdata.pagename = pagename
headdata.process_props.args = args
headdata.sc = sc
headdata.id = args.id
headdata.sort = args.sort
-- No redundant script cat unless the user explicitly gave sc=
headdata.no_script_code_cat = not args.sc
headdata.var = args.var
local extra_term_mods = {}
if include_tr then
insert(extra_term_mods, "tr")
end
if include_ts then
insert(extra_term_mods, "ts")
end
if include_sc then
insert(extra_term_mods, "sc")
end
if not extra_term_mods[1] then
extra_term_mods = nil
end
local trs = args.tr and split_on_comma(args.tr) or {}
local num_trs = #trs
local tss = args.ts and split_on_comma(args.ts) or {}
local num_tss = #tss
local heads = args[head_param] and export.parse_term_with_modifiers {
val = args[head_param],
paramname = head_param,
splitchar = ",",
is_head = true,
include_mods = extra_term_mods,
} or {}
local num_heads = #heads
if num_heads > 0 and num_trs > 0 and num_heads ~= num_trs then
error(("%s head%s specified explicitly but %s translit%s; they must match; use '+' to stand for the default head (the pagename) or default automatic translit and '-' to stand for no translit"):format(
num_heads, num_heads > 1 and "s" or "", num_trs, num_trs > 1 and "s" or ""))
end
if num_heads > 0 and num_tss > 0 and num_heads ~= num_tss then
error(("%s head%s specified explicitly but %s transcription%s; they must match; use '+' to stand for the default head (the pagename) and '-' to stand for no transcription"):format(
num_heads, num_heads > 1 and "s" or "", num_tss, num_tss > 1 and "s" or ""))
end
if num_trs > 0 and num_tss > 0 and num_trs ~= num_tss then
error(("%s translit%s specified explicitly but %s transcription%s; they must match; use '+' to stand for default automatic translit and '-' to stand for no translit or transcription"):format(
num_trs, num_trs > 1 and "s" or "", num_tss, num_tss > 1 and "s" or ""))
end
-- Be careful here not to overwrite user_specified_heads if it's empty so we can later check user_specified_heads
-- to see if the user provided any heads.
local max_tr_ts = math.max(num_trs, num_tss)
if num_heads == 0 and max_tr_ts > 0 then
heads = {}
for i = 1, max_tr_ts do
heads[i] = {term = "+"}
end
end
if not heads[1] then
heads = {{term = "+"}}
end
for i, headobj in ipairs(heads) do
if headobj.tr and trs[i] then
if headobj.tr ~= trs[i] then
error(("Saw two different translits '%s' and '%s' for head #%s"):format(
headobj.tr, trs[i], i))
end
else
headobj.tr = headobj.tr or trs[i]
end
if headobj.tr == "+" then
headobj.tr = nil
end
if headobj.ts and tss[i] then
if headobj.ts ~= tss[i] then
error(("Saw two different transcriptions '%s' and '%s' for head #%s"):format(
headobj.ts, tss[i], i))
end
else
headobj.ts = headobj.ts or tss[i]
end
if headobj.ts == "-" then
headobj.ts = nil
end
if headobj.term == "+" then
headobj.term = args.nolink and pagename or nil
if headobj.term and namespace == "Reconstruction" then
headobj.term = "*" .. headobj.term
end
end
end
headdata.heads = heads
local pos_category = headdata.pos_category
-- Do this as late as possible in case the caller wants to conditionalize the primary categorizing POS on
-- arguments or even the presence or absence of explicit heads.
if pos_functions[indexing_pos] then
pos_category = resolve_prop(pos_functions[indexing_pos].pos_category) or pos_category
end
headdata.process_props.normalized_template_indicated_pos = pos_category
headdata.pos_category = pos_category
headdata.process_props.pos_variants = nil -- erase the cache
local function pagename_is_suffix()
if sc:getCode() == "Latn" then
-- shortcut Latin terms to avoid unnecessarily loading [[Module:affix]]
return pagename:find("^%-") and not pagename:find("%-$")
else
local affix_type, _, _, _ = require(affix_module).parse_term_for_affixes(pagename, lang, sc)
return affix_type == "Hậu tố"
end
end
local clitic_label = args.clitic
if clitic_label == true then
clitic_label = "khinh âm"
end
if clitic_label then
headdata:insert_category("khinh âm")
headdata:insert_fixed_inflection(clitic_label)
elseif args.suffix or (
not args.nosuffix and pagename_is_suffix() and pos_category ~= "Hậu tố" and pos_category ~= "Biến thể hình thái hậu tố"
) then
headdata.process_props.is_suffix = true
local pos_list_for_inflectional_suffix = {}
local pos_list_for_suffix_form = {}
local pos_list_for_forming_suffix = {}
local function handle_suffix_pos(pos, is_first)
-- Use the actual POS for the label we insert, but simplify the POS for the category. We need to separately check
-- each of them to see if they end in 'forms' because e.g. 'comparative adjectives' gets simplified to
-- 'adjective forms'.
local form_type = pos:match("^Biến thể hình thái (.*)$")
if form_type then
if args.inflsuf then
insert(pos_list_for_inflectional_suffix, form_type)
else
insert(pos_list_for_suffix_form, form_type)
end
else
local singular_pos = require(en_utilities_module).singularize(pos)
if args.inflsuf then
insert(pos_list_for_inflectional_suffix, singular_pos)
else
insert(pos_list_for_forming_suffix, singular_pos .. "-")
end
end
if args.inflsuf then
headdata:insert_category("inflectional suffixes")
end
local simplified_pos = export.simplify_pos(pos, "preserve_proper_nouns", "preserve_lemma_status")
local simplified_form_type = simplified_pos:match("^Biến thể hình thái (.*)$")
if simplified_form_type then
if args.inflsuf then
headdata:insert_category(("%s inflectional suffixes"):format(simplified_form_type))
else
headdata:insert_category(("%s suffix forms"):format(simplified_form_type))
end
else
local singular_simplified_pos = require(en_utilities_module).singularize(simplified_pos)
if args.inflsuf then
headdata:insert_category(("%s inflectional suffixes"):format(singular_simplified_pos))
else
headdata:insert_category(("%s-forming suffixes"):format(singular_simplified_pos))
end
end
local postype
if args.inflsuf then
postype = "lemma"
end
if not postype then
postype = require(headword_module).pos_lemma_or_nonlemma(pos)
end
if not postype then
postype = require(headword_module).pos_lemma_or_nonlemma(simplified_pos)
end
if not postype then
error(("Unrecognized canonicalized part of speech '%s' in addlpos=, cannot determine whether lemma or non-lemma form"):format(
pos
))
end
local this_pos_category = postype == "Mục từ" and "Hậu tố" or "Biến thể hình thái hậu tố"
if is_first then
headdata.pos_category = this_pos_category
headdata.process_props.pos_variants = nil -- erase the cache
elseif headdata.pos_category ~= this_pos_category then
local singular_normalized_this_pos_category =
require(en_utilities_module).singularize(this_pos_category)
local singular_normalized_overall_pos_category =
require(en_utilities_module).singularize(headdata.pos_category)
error(("Cannot mix suffixes and suffix forms using addlpos=; '%s' is of a %s while overall POS " ..
"'%s' is a %s; use separate POS headers for the two"):format(pos,
singular_normalized_this_pos_category, pos_category, singular_normalized_overall_pos_category))
end
end
handle_suffix_pos(pos_category, true)
if args.addlpos then
for _, addlpos in ipairs(split(args.addlpos, "%s*,%s*")) do
addlpos = canonicalize_pos(addlpos)
handle_suffix_pos(addlpos, false)
end
end
if pos_list_for_inflectional_suffix[1] then
headdata:insert_fixed_inflection(mw.text.listToText(pos_list_for_inflectional_suffix) .. " inflectional suffix")
end
if pos_list_for_suffix_form[1] then
headdata:insert_fixed_inflection(mw.text.listToText(pos_list_for_suffix_form) .. " suffix form")
end
if pos_list_for_forming_suffix[1] then
headdata:insert_fixed_inflection(mw.text.listToText(pos_list_for_forming_suffix) .. "forming suffix")
end
end
if args.cat then
for _, cat in ipairs(split_on_comma(args.cat)) do
headdata:insert_category(cat)
end
end
local function augment_headdata_from_infls(infls)
infls = resolve_prop(infls)
for _, infl in ipairs(infls) do
local function interr(txt)
error(("Internal error: %s (coming from infls spec %s)"):format(txt, dump(infl)))
end
local function process_labelobjs(labelobjs, originating_term, handle_labelobj)
if labelobjs == nil then
return
end
if type(labelobjs) ~= "string" and type(labelobjs) ~= "table" then
interr(("Wrong type '%s' for label object(s) %s, expected string or table"):format(
type(labelobjs), dump(labelobjs)
))
end
if type(labelobjs) == "string" or type(labelobjs) == "table" and not labelobjs[1] then
labelobjs = {labelobjs}
end
for _, labelobj in ipairs(labelobjs) do
local label, termobj
if type(labelobj) == "string" then
label = labelobj
termobj = originating_term
elseif type(labelobj) ~= "table" then
interr(("Wrong type '%s' for label object %s, expected string or table"):format(
type(labelobj), dump(labelobj)
))
label = labelobj.term
if type(label) ~= "string" then
interr(("Wrong type '%s' for label %s from label object %s, expected string"):format(
type(label), dump(label), dump(labelobj)
))
end
termobj = labelobj
end
handle_labelobj(label, termobj)
end
end
local param = infl[1]
if param then
local vals
-- Fetch the param and make sure it's a string or number.
param = resolve_prop(param)
if type(param) ~= "string" and type(param) ~= "number" then
interr(("Parameter name %s must be a string or number"):format(dump(param)))
end
-- Fetch the type and validate.
local typ = resolve_prop(infl.type)
if typ == nil then
typ = "string"
end
if typ ~= "genders" and typ ~= "boolean" and typ ~= "string" then
-- FIXME: Handle more types.
interr(('Unrecognized type %s; can only currently handle "genders", "boolean" and "string" (the default)'):format(
dump(typ)))
end
-- Fetch the value(s).
if typ == "genders" or typ == "boolean" then
vals = args[param]
elseif typ == "string" then
local parse_inflection_props = resolve_prop(infl.parse_inflection_props)
local include_mods = resolve_prop(infl.include_mods)
local no_augment_include_mods = resolve_prop(infl.no_augment_include_mods)
if include_mods ~= nil or no_augment_include_mods ~= nil then
if parse_inflection_props == nil then
parse_inflection_props = {}
else
parse_inflection_props = shallow_copy(parse_inflection_props)
end
if include_mods ~= nil then
parse_inflection_props.include_mods = include_mods
end
if no_augment_include_mods ~= nil then
parse_inflection_props.no_augment_include_mods = no_augment_include_mods
end
end
vals = headdata:parse_inflection(param, parse_inflection_props)
-- Convert an empty list to nil for consistent checking below.
if not vals[1] then
vals = nil
end
else
interr(("Unrecognized type '%s"):format(typ))
end
-- If value(s) nil, fetch the default.
if vals == nil and infl.default ~= nil then
local default = resolve_prop(infl.default)
if typ == "genders" then
vals = export.canonicalize_termobj_list(default, "spec", "default")
elseif typ == "boolean" then
vals = default
elseif typ == "string" then
vals = export.canonicalize_termobj_list(default, "term", "default")
else
interr(("Unrecognized type '%s"):format(typ))
end
end
-- Resolve "special" values (special signals a string values, such as requesting the default with "+").
if vals ~= nil and infl.resolve_special then
if typ ~= "string" then
interr(("Cannot specify resolve_special= for type %s"):format(dump(typ)))
end
local resolve_special_props = resolve_prop(infl.resolve_special_props, vals)
if infl.is_special ~= nil then
if resolve_special_props == nil then
resolve_special_props = {}
else
resolve_special_props = shallow_copy(resolve_special_props)
end
resolve_special_props.is_special = infl.is_special
end
vals = headdata:resolve_special(vals, infl.resolve_special, resolve_special_props)
end
-- Canonicalize the value(s).
if vals ~= nil and infl.canonicalize ~= nil then
if typ == "boolean" then
interr('Cannot specify canonicalize= when type is "boolean"')
end
local newvals = {}
local field = typ == "genders" and "spec" or "term"
for _, val in ipairs(vals) do
local newval
if val._is_mode then
newval = val
else
if type(infl.canonicalize) == "function" then
newval = infl.canonicalize(val[field])
else
newval = val[field]
newval = infl.canonicalize[newval] or newval
end
end
if val[field] ~= newval then
val = shallow_copy(val)
val[field] = newval
end
insert(newvals, val)
end
vals = newvals
end
-- Validate the value(s).
if vals ~= nil and infl.validate ~= nil then
if typ == "boolean" then
interr('Cannot specify validate= when type is "boolean"')
elseif type(infl.validate) == "function" then
infl.validate(headdata, vals)
elseif typ == "genders" then
export.validate_genders {
genders = vals,
valid_genders = infl.validate,
no_augment = resolve_prop(infl.no_augment_genders),
}
elseif typ == "string" then
validate_items {
items = vals,
field = "term",
valid_items = infl.validate,
item_type = ("values in |%s="):format(param),
}
else
interr(("Unrecognized type '%s"):format(typ))
end
end
local function run_and_check_return_value(fun, funname, ...)
local retval, intentionally_nil = fun(...)
if retval == nil and not intentionally_nil then
interr(("If you return nil from %s, you must return a second non-nil return " ..
"value to indicate that the nil return value was intentional"):format(funname))
end
return retval
end
-- Run the process_after_parse handler, if it exists.
if vals ~= nil and infl.process_after_parse ~= nil then
vals = run_and_check_return_value(infl.process_after_parse, "process_after_parse", headdata, vals)
end
-- Process strings for insertion, to handle special mode signals like -, +-, ?, etc.
local processed
if typ == "string" then
local process_for_insertion_props = resolve_prop(infl.process_for_insertion_props, vals)
processed = headdata:process_for_insertion(vals, process_for_insertion_props)
headdata.process_props.processed_specs[param] = processed
if processed.numterms > 0 and infl.process_one_term ~= nil then
local processed_terms = {}
for _, termobj in ipairs(processed.terms) do
local processed_term = run_and_check_return_value(infl.process_one_term, "process_one_term",
headdata, termobj)
if processed_term ~= nil then
-- FIXME: Should we add an origin of "process_one_term" here? (But this would erase
-- any "default" or "resolve_special" origins.)
local newterms = export.canonicalize_termobj_list(processed_term, "term", nil, nil,
termobj)
for _, newterm in ipairs(newterms) do
insert(processed_terms, newterm)
end
end
end
processed = shallow_copy(processed)
processed.terms = processed_terms
processed.numterms = #processed_terms
end
if processed.numterms > 0 and infl.process_terms ~= nil then
local process_terms_result = run_and_check_return_value(infl.process_terms, "process_terms",
processed)
process_terms_result = process_terms_result or {}
if process_terms_result[1] or not next(process_terms_result) then
processed = shallow_copy(processed)
processed.terms = process_terms_result
processed.numterms = #process_terms_result
elseif not process_terms_result.terms or not process_terms_result.numterms then
error("Return value from process_terms() should be either a list of term objects or a " ..
"`processed` structure containing such a list")
else
processed = process_terms_result
end
end
vals = processed.terms
end
local inserted_fixed_label
-- "Implement" the values. Fixed labels and cats only fire when non-falsy (i.e. we don't want to fire
-- on boolean false or empty list). We call insert_processed_inflection() always because there may be
-- a mode even when there are no values.
if vals == true or type(vals) == "table" and vals[1] then
if infl.fixed_label and infl.all_fixed_label then
interr("Cannot specify both fixed_label= and all_fixed_label=; specify one or the other")
end
local function check_fixed_label_references_val(label)
if type(label) == "table" and label[1] then
for _, lab in ipairs(label) do
if check_fixed_label_references_val(lab) then
return true
end
end
return false
end
if type(label) == "table" then
if not label.term then
interr(("Fixed label structure %s does not have a value for `.term`"):format(dump(label)))
end
label = label.term
end
if type(label) ~= "string" then
interr(("Wrong type for fixed label %s, should be string"):format(type(label)))
end
return not not label:find("{val}")
end
local fixed_label = infl.fixed_label
local all_fixed_label = infl.all_fixed_label
-- If the value being processed is boolean, there's only one value so treat a fixed_label as an
-- all_fixed_label and output only once; likewise if the caller specified a fixed_label without
-- {val} in it.
if fixed_label and (typ == "boolean" or type(fixed_label) ~= "function" and not
check_fixed_label_references_val(fixed_label)) then
all_fixed_label = fixed_label
fixed_label = nil
end
if fixed_label then
if typ == "boolean" then
interr("Boolean fixed_label values should have been converted to all_fixed_label")
end
for _, valobj in ipairs(vals) do
local labelobjs = resolve_prop(fixed_label, valobj)
process_labelobjs(labelobjs, valobj, function(label, termobj)
if label:find("{val}") then
if typ ~= "string" then
interr(('Cannot specify {val} in fixed_label %s when type is "%s"'):format(dump(label), typ))
end
label = label:gsub("{val}", replacement_escape(valobj.term))
end
headdata:insert_fixed_inflection(label, {
originating_term = termobj
})
inserted_fixed_label = true
end)
end
elseif all_fixed_label then
local labelobjs = resolve_prop(all_fixed_label, vals)
process_labelobjs(labelobjs, nil, function(label, termobj)
if label:find("{vals}") then
if typ ~= "string" then
interr(('Cannot specify {vals} in all_fixed_label %s when type is "%s"'):format(dump(label), typ))
end
local formatted_labels = {}
for _, valobj in ipairs(vals) do
insert(formatted_labels, add_decorations(valobj.term, valobj, lang))
end
label = label:gsub("{vals}", replacement_escape(serial_comma_join(formatted_labels)))
end
headdata:insert_fixed_inflection(label, termobj)
inserted_fixed_label = true
end)
end
end
-- See above; if there's a label (which can only happen when typ == "string"), always call
-- insert_processed_inflection.
local inserted_vals
if infl.label ~= nil then
if typ ~= "string" then
interr(("label=%s can only be specified for type 'string', not '%s'"):format(
dump(infl.label), typ
))
end
local label = resolve_prop(infl.label, vals)
if label ~= nil then
local insert_processed_inflection_props =
resolve_prop(infl.insert_processed_inflection_props, vals)
local function copy_to_insert_processed_inflection_props(field, val)
local fieldval = val or resolve_prop(infl[field], vals)
if fieldval ~= nil then
if insert_processed_inflection_props == nil then
insert_processed_inflection_props = {}
else
insert_processed_inflection_props =
shallow_copy(insert_processed_inflection_props)
end
insert_processed_inflection_props[field] = fieldval
end
end
copy_to_insert_processed_inflection_props("label_for_cats_and_modes")
copy_to_insert_processed_inflection_props("no_auto_cats")
copy_to_insert_processed_inflection_props("accel")
copy_to_insert_processed_inflection_props("accel_form")
copy_to_insert_processed_inflection_props("request")
if not processed then
interr("`processed` structure wasn't properly created")
end
headdata:insert_processed_inflection(processed, label, insert_processed_inflection_props)
inserted_vals = true
end
end
-- See above; only fire for cats (and gender insertion) on non-falsy values.
if vals == true or type(vals) == "table" and vals[1] then
if typ == "genders" then
headdata.genders = vals
end
local inserted_cat
if infl.cat then
if typ == "boolean" then
vals = {vals}
end
local allcats = {}
for _, valobj in ipairs(vals) do
local cats = resolve_prop(infl.cat, valobj)
if type(cats) == "string" then
cats = {cats}
end
if cats ~= nil then
for _, cat in ipairs(cats) do
if cat:find("{val}") then
cat = cat:gsub("{val}", replacement_escape(valobj.term))
end
insert_if_not(allcats, cat)
end
end
end
for _, cat in ipairs(allcats) do
headdata:insert_category(cat)
inserted_cat = true
end
end
if typ == "boolean" then
if not inserted_fixed_label and not inserted_cat then
interr(("User set boolean setting for %s= but no fixed label added and no category " ..
"inserted; if you took action in process_after_parse(), make sure to return " ..
"`nil, true`"):format(param))
end
elseif typ == "string" then
if not inserted_vals and not inserted_fixed_label and not inserted_cat then
interr(("User set value(s) %s for %s= but no inflection inserted and no fixed label " ..
"or category added"):format(dump(vals), param))
end
end
if processed then
-- FIXME, convert to use `processed_specs` field and remove this
headdata.process_props.insert_specs[param] = processed
end
end
else -- no param specified
if infl.label or infl.all_fixed_label then
interr("Cannot have label= or all_fixed_label= without specifying a param")
end
if infl.process then
infl.process(headdata)
end
if infl.fixed_label then
local labelobjs = resolve_prop(infl.fixed_label)
process_labelobjs(labelobjs, nil, function(label, termobj)
if label:find("{val}") then
interr("Cannot specify {val} in a fixed_label= value without specifying a param")
end
headdata:insert_fixed_inflection(label, {
originating_term = termobj
})
end)
end
if infl.cat then
local cats = resolve_prop(infl.cat)
if type(cats) == "string" then
cats = {cats}
end
if cats ~= nil then
for _, cat in ipairs(cats) do
if cat:find("{val}") then
interr("Cannot specify {val} in a cat= value without specifying a param")
end
headdata:insert_category(cat)
end
end
end
end
end
end
if infls then
augment_headdata_from_infls(infls)
end
if augment_headdata then
augment_headdata(headdata, args)
end
if pos_functions[indexing_pos] then
local pos_infls = pos_functions[indexing_pos].infls
if pos_infls then
augment_headdata_from_infls(pos_infls)
end
local func = pos_functions[indexing_pos].func
if func then
func(headdata, args)
end
end
headdata:parse_and_insert_inflection("abbr", "abbreviation")
headdata.checkredlinks = resolve_prop(checkredlinks)
setmetatable(headdata, nil)
headdata.process_props = nil
if args.json then
return require("Module:JSON").toJSON(headdata)
end
return require(headword_module).full_headword(headdata)
end
return export
ecrvkrn870mhkt4rpx6odo5zlwche1k
2573878
2573877
2026-10-27T13:30:48Z
Hiyuune
50834
2573878
Scribunto
text/plain
local export = {}
--[==[
FIXME:
1. Implement ? = unknown or uncertain. There should be no label by default but it should be configurable.
The default category should be "{plpos} with unknown or uncertain plurals" for nouns/proper nouns. It should
be an error to specify values along with this. [DONE]
2. Implement ! = unattested. The default label should be "{label} not attested". The default category should be
"{plpos} with unattested plurals" for nouns/proper nouns. It should be an error to specify values along with
this. [DONE]
3. Implement +- = sometimes has a value, e.g. "countable and uncountable" for plurals. [DONE]
4. Implement ~ standing for the pagename in heads and inflections. [DONE]
5. Implement abbr= for abbreviations. [DONE]
6. Split insert_inflection into process_for_insertion() (which implements the logic for handling special mode signals)
and insert_processed_inflection() (which implements the labels and categories for mode signals) so that we can handle
mode signals correctly. [DONE]
7. Support no_label for booleans.
8. Document the various types of parts of speech and implement pos_category per part of speech definition. [DONE]
9. Implement apply_link_modifiers() for head=~~...
10. Correctly handle var=both. [DONE]
11. Implement checkredlinks and port simplify_pos to [[Module:headword]]. [CHECKREDLINKS SET; SIMPLIFY_POS NOT PORTED YET.]
12. Fix longstanding accelerator bug: if embedded links in inflection, don't make it green. [DONE]
13. Implement process_terms() callback after process_for_insertion. [DONE]
14. Implement validate_and_canonicalize() callback. [NOT DONE; INSTEAD HAVE A SEPARATE CANONICALIZE CALLBACK]
15. Document new methods.
16. Implement process_one_term() callback after process_for_insertion. [DONE]
17. Implement process() at a stage to happen universally when there is no argument to parse. [DONE]
18. Skip mode signals during validation and canonicalization. [DONE]
19. Implement inflsuf= with special handling and special inflectional categories. [DONE]
20. Add support for "boolean or string" type.
21. Support invocation-specified or user-specified languages. [DONE]
]==]
local require_when_needed = require("Module:utilities/require when needed")
local affix_module = "Module:affix"
local debug_track_module = "Module:debug/track"
local decorations_module = "Module:decorations"
local en_utilities_module = "Module:en-utilities"
local fun_is_callable_module = "Module:fun/isCallable"
local headword_module = "Module:headword"
local headword_data_module = "Module:headword/data"
local languages_module = "Module:languages"
local links_module = "Module:links"
local parameters_module = "Module:parameters"
local parse_interface_module = "Module:parse interface"
local parse_utilities_module = "Module:parse utilities"
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 lcfirst = require("Module:string utilities").lcfirst
local dump = mw.dumpObject
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
local insert = table.insert
local concat = table.concat
local remove = table.remove
local sort = table.sort
local contains = require_when_needed(table_module, "contains")
local deep_equals = require_when_needed(table_module, "deepEquals")
local extend = require_when_needed(table_module, "extend")
local insert_if_not = require_when_needed(table_module, "insertIfNot")
local is_array = require_when_needed(table_module, "isArray")
local list_to_set = require_when_needed(table_module, "listToSet")
local serial_comma_join = require_when_needed(table_module, "serialCommaJoin")
local shallow_copy = require_when_needed(table_module, "shallowCopy")
local split = require_when_needed(string_utilities_module, "split")
local ugsub = require_when_needed(string_utilities_module, "gsub")
local umatch = require_when_needed(string_utilities_module, "match")
local pattern_escape = require_when_needed(string_pattern_escape_module)
local replacement_escape = require_when_needed(string_replacement_escape_module)
local escape_wikicode = require_when_needed(parse_utilities_module, "escape_wikicode")
local parse_inline_modifiers = require_when_needed(parse_utilities_module, "parse_inline_modifiers")
local term_contains_top_level_html = require_when_needed(parse_utilities_module, "term_contains_top_level_html")
local get_lang_by_code = require_when_needed(languages_module, "getByCode")
local is_callable = require_when_needed(fun_is_callable_module)
local format_decorations = require_when_needed(decorations_module, "format_decorations")
local singularize = require_when_needed(en_utilities_module, "singularize")
local canonicalize_pos = require_when_needed(headword_module, "canonicalize_pos")
--[==[ intro:
This module contains utilities for constructing language-specific headword modules.
[fill in]
===About parts of speech===
There are several versions of the part of speech (POS) of a given term:
* The ''primary categorizing POS'' is the basic part of speech to which a term belongs. The primary POS determines
whether the term is a lemma or non-lemma form, and in its plural form, it is used to categorize the terms (unless it
is a variant term), along with caetegorizing into a ''lemmas'' or ''non-lemma forms'' category. (Variant terms,
typically marked by {{para|var|1}}, are categorized into ''variant lemmas'' or ''variant non-lemma forms'' and are not
categorized into a primary POS category.)
* The ''raw {{tl|head}} POS'' is parameter {{para|2}} to {{tl|head}}. This is used to determine the primary categorizing
POS by a process of canonicalization, because this parameter may contain abbreviations (e.g. ''a'' or ''adj'' for an
adjective) and may be (and usually is) given in its singular form.
* The ''template-indicated POS'' is the part of speech indicated by a headword template such as {{tl|nl-noun}} or
{{tl|mn-head}}. It is either inherent to the template (in a case like {{tl|nl-noun}}), in which case it is specified
by the template code and should always be in canonical, plural form; or it is specified by the user in the template
call (in a case like {{tl|mn-head}} or other generic POS template, usually ending in `-head`), in which case it may
be given in raw form, like for the raw {{tl|head}} POS, and is canonicalized using the same code that canonicalizes
raw {{tl|head}} parts of speech. The template-indicate POS may not be a valid primary categorizing POS. For example,
Dutch has a template-indicated POS ''diminutiva tantum nouns'', specified through the template {{tl|nl-dim-tant}}, but
there is no such primary categorizing POS (that POS would be `nouns`). Similarly, Arabic has a template-indicated POS
''collective nouns'', whose primary categorizing POS is also `nouns`. As a result, the template-indicated POS may need
to be converted to a primary categorizing POS through a ''normalization'' process (which uses a `pos_category` field
attached to the `pos_functions` structure for the template-indicated POS). Note that the template-indicated POS comes
in several variants:
*# It may be ''head-raw'', meaning that it uses the literal string `head` in place of the user-specified POS in a
generic POS template like {{tl|mn-head}}; it may be ''non-head'', meaning that it uses the actual user-specified
POS in such a case; or it may be ''head-augmented'', meaning that in the case of a generic POS template it is of the
form e.g. `head.nouns`, contanining both the literal `head` and the user-specified POS.
*# It may be ''suffix-raw'', meaning that it uses the primary categorizing POS `suffixes` in place of the
template-indicated POS when the term is a suffix, or it may be ''non-suffix'', meaning that it uses the
template-indicated POS in such a case. The headword for a suffix uses the template corresponding to whatever POS the
suffix forms, e.g. {{nl-noun}} for Dutch noun-forming suffixes.
*# The ''indexing POS'' is the variant of the template-indicated POS that is used to index into the `pos_functions`
table. Normally it is head-raw and non-suffix, but it will be head-augmented if the corresponding head-augmented
entry exists in `pos_functions`.
*# The ''unnormalized categorizing POS'' is the variant of the template-indicated POS that is potentially usable as the
primary categorizing POS (see above). It is non-head and suffix-raw, i.e. it uses the actual template-specified or
user-specified form of the template-indicated POS except when the term is a suffix, in which case it has the value
`suffixes`. In most circumstances this can be used directly as the primary categorizing POS, but in some cases this
POS is not a valid primary categorizing POS and needs to be normalized to get the actual primary categorizing POS, as
mentioned above under the description for the template-indicated POS.
* Finally, there is a ''simplified categorizing POS''. This collapses variations of primary categorizing parts of speech
together for use in red-link and similar categories. For example, the POS `proper nouns` is collapsed into `nouns`,
and all types of participles are collapsed into `participles`. There are two variants of the simplification process,
''lemma-status-preserving'' and ''non-lemma-status-preserving''. The latter is the default and is simpler, in that
e.g. it means any part of speech ending in `nouns` to `nouns`, even if this causes non-lemma forms to become lemmas,
as with `diminutive nouns` and `verbal nouns` (which are non-lemma forms) and likewise with `comparative adjectives`
and `superlative adjectives` (which are again non-lemma forms). The former, ''lemma-status-preserving'', keeps lemmas
as lemmas and (more important) non-lemmas as non-lemmas by converting e.g. `diminutive nouns` and `verbal nouns` to
`noun forms` rather than `nouns`. The reason two variants exist in that they have different use cases. The default,
non-lemma-status-preserving, type is used for tracking categories such as red-link and request categories, where
completeness is more important than clutter. The lemma-status-preserving variant is used for informative categories
such as ` ``lang`` masculine nouns`, which should only have lemmas in them to avoid clutter. (For the same reason,
variant terms specified using {{para|var|1}} aren't normally categorized into informative categories but are placed
in tracking categories.)
===About properties and data objects===
[fill in]
]==]
local inflection_to_cats_and_label = {
plural = {
filter_pos = function(posvars)
-- plurals also occur with determiners, adjectives etc. and we don't want to generate categories like
-- 'countable determiners', 'countable adjectives', etc. Note that the passed-in `simp_plpos` has
-- `proper nouns` converted to `nouns`.
return posvars.simp_plpos == "Danh từ"
end,
cats = {"Danh từ đếm được"},
no_cats = {"Danh từ không đếm được"},
fixed_adj_label = "đếm được",
fixed_adj_no_label = "không đếm được",
sometimes_label = "đếm được và không đếm được",
unattested_cats = "Danh từ có số nhiều chưa được chứng thực",
unknown_cats = "Danh từ có số nhiều không rõ hoặc chưa được chứng thực",
},
comparative = {
cats = {"{simp_plpos} so sánh được"},
no_cats = {"{simp_plpos} không so sánh được"},
fixed_adj_label = "so sánh được",
},
["female equivalent"] = {
cats = {"{simp_plpos} with other-gender equivalents"},
},
["male equivalent"] = {
cats = {"{simp_plpos} with other-gender equivalents"},
},
}
local param_mods = {
alt = {}, -- always allow so e.g. - can be specified as a suffix form
id = {}, -- disabled when `is_head = true`
q = {type = "qualifier"},
qq = {type = "qualifier"},
l = {type = "labels"},
ll = {type = "labels"},
-- [[Module:headword]] expects part references in `.refs`.
ref = {item_dest = "refs", type = "references", store = "insert-flattened"},
}
local optional_param_mods = {
g = {item_dest = "genders", type = "genders"},
lang = {type = "language"},
sc = {type = "script"},
t = {item_dest = "gloss"},
gloss = {},
pos = {},
ng = {},
lit = {},
tr = {},
ts = {},
face = {},
nolinkinfl = {type = "boolean"},
}
local optional_headword_param_mods = {
sc = {type = "script"},
tr = {},
ts = {},
}
local function split_on_comma(val)
if val:find(",") then
return require(parse_interface_module).split_on_comma(val)
else
return {val}
end
end
local function ine(val)
if not val then
return nil
end
val = mw.text.trim(val)
return val ~= "" and val or nil
end
--[=[
Add decorations to a term. `termobj` is the object describing 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`;
`text` is the text of the term itself, and `lang` is the language object.
]=]
local function add_decorations(text, termobj, lang)
local function field_non_empty(field)
local list = termobj[field]
if not list then
return nil
end
if type(list) ~= "table" then
error(("Internal error: Wrong type for `termobj.%s`=%s, should be \"table\""):format(
field, mw.dumpObject(list)))
end
return list[1]
end
if field_non_empty("q") or field_non_empty("qq") or field_non_empty("l") or field_non_empty("ll") or
field_non_empty("refs") then
text = format_decorations {
lang = lang,
text = text,
q = termobj.q,
qq = termobj.qq,
l = termobj.l,
ll = termobj.ll,
refs = termobj.refs,
}
end
return text
end
--[==[
Parse a single inflection or headword form or list of such forms. In either case, inline modifiers may be attached.
`data` is an object with the following fields:
* `val`: The raw value to parse. Required.
* `paramname`: The name of the parameter from which the value was taken; used in error messages. Required.
* `is_head`: We are parsing a headword parameter (a value which goes into the `heads` field of `data`). This changes
the allowed modifiers, disabling the `id` modifier and only allowing a subset of optional modifiers.
* `frob`: An optional function of one value to apply to the form after inline modifiers have been removed (i.e. to
apply to the `.term` field of the returned object).
* `include_mods`: List of extra inline modifiers to include, besides the default ones (see below). Each list item is
either a string specifying a recognized extra inline modifier (see `optional_param_mods` in the code), or a two-item
list of modifier name and modifier spec, where the spec should follow the syntax for modifier specs in
`parse_inline_modifiers` in [[Module:parse utilities]].
* `exclude_mods`: List of default inline modifiers to not include.
* `splitchar`: If specified, the value in `val` can be a list of forms to parse, separated by the value of `splitchar`
(which is a Lua pattern, as in `parse_inline_modifiers` in [[Module:parse utilities]]). Most commonly, `splitchar` is
a single comma and the values are comma-separated (in this case, splitting will not happen if a space follows the
comma).
* `parse_lang_prefix`: If specified, allow a language prefix to precede a form, and if found, store into the `.lang`
field of the returned object.
* `disable_special_modes`: If specified, don't recognize and treat specially the special mode signals (e.g. '-' for
no inflection, '?' for unknown, etc.).
* `preserve_splitchar`, `delimiter_key`, `escape_fun`, `unescape_fun`, `pre_normalize_modifiers`: As in
`parse_inline_modifiers` in [[Module:parse utilities]].
Returns an object suitable for storing as one element of one of the lists in `headdata.inflections`, where `headdata`
is the structure passed to [[Module:headword]]. If `splitchar` is specified, howeve, the return value is a list of such
objects.
The following default inline modifiers are currently recognized:
* `q`: Left qualifier.
* `qq`: Right qualifier.
* `l`: Comma-separated list of left labels. No space should follow the comma.
* `ll`: Comma-separated list of right labels. No space should follow the comma.
* `ref`: Reference or references. See {{tl|IPA}} for the syntax.
* `id`: Sense ID, in case there are multiple senses. See {{tl|l}}.
The following are the recognized additional inline modifiers:
* `g`: Comma-separated list of genders.
* `alt`: Display text.
* `lang`: Language code of language of the form, if different from the language of the headword.
* `sc`: Script code of script of the form. Almost never needed.
* `t`: Gloss for the form.
* `gloss`: Gloss for the form (alias for `t`).
* `pos`: Part of speech of the form.
* `lit`: Literal meaning of the form.
* `tr`: Manual transliteration of the form.
* `ts`: Transcription of the form, for languages where the transliteration differs markedly from the pronunciation.
* `face`: Face to display the form in, e.g. {"hypothetical"} for a hypothetical form (unlinkable and displayed in italics).
* `nolinkinfl`: Make the form unlinkable.
]==]
function export.parse_term_with_modifiers(data)
local paramname, val, frob = data.paramname, data.val, data.frob
local function generate_obj(term, parse_err)
if not data.disable_special_modes and export.special_modes[term] then
return {
term = term,
_is_mode = true
}
end
if frob then
term = frob(term, parse_err)
end
if data.parse_lang_prefix and term:find(":") then
return require(parse_utilities_module).generate_obj_maybe_parsing_lang_prefix {
term = term,
paramname = paramname,
parse_lang_prefix = true,
parse_err = parse_err,
}
else
return {term = term}
end
end
-- Check for inline modifier, e.g. מרים<tr:Miryem>. But exclude top-level HTML entry with <span ...>,
-- <sup> or similar in it.
if (val:find("<", nil, true) or data.splitchar) and not term_contains_top_level_html(val) and
-- don't parse inline modifiers if is_head and the value begins with a ~ (link modifier syntax)
(not data.is_head or not val:find("^~")) then
local param_mods = param_mods
if data.is_head then
param_mods = shallow_copy(param_mods)
param_mods.id = nil
end
if data.include_mods or data.exclude_mods then
if not data.is_head then
-- already copied when data.is_head
param_mods = shallow_copy(param_mods)
end
if data.include_mods then
local optional_mods = data.is_head and optional_headword_param_mods or optional_param_mods
for _, mod in ipairs(data.include_mods) do
if type(mod) == "table" then
if #mod ~= 2 then
error(("Internal error: Modifier spec %s in `include_mods` should be of length 2"):format(
dump(mod)))
end
local modkey, modvalue = unpack(mod)
param_mods[modkey] = modvalue
elseif not optional_mods[mod] then
error(("Internal error: Unrecognized modifier spec %s in `include_mods`"):format(
dump(mod)))
else
param_mods[mod] = optional_mods[mod]
end
end
end
if data.exclude_mods then
for _, mod in ipairs(data.exclude_mods) do
if not param_mods[mod] then
error(("Internal error: Modifier spec %s in `exclude_mods` not found among existing modifiers"
):format(dump(mod)))
else
param_mods[mod] = nil
end
end
end
end
return parse_inline_modifiers(val, {
paramname = paramname,
param_mods = param_mods,
generate_obj = generate_obj,
splitchar = data.splitchar,
preserve_splitchar = data.preserve_splitchar,
delimiter_key = data.delimiter_key,
escape_fun = data.escape_fun,
unescape_fun = data.unescape_fun,
pre_normalize_modifiers = data.pre_normalize_modifiers,
})
else
local retval = generate_obj(val)
if data.splitchar then
retval = {retval}
end
return retval
end
end
--[==[
Parse a list of inflection forms that may have inline modifiers attached. `data` is an object with the following fields:
* `forms`: The list of raw values to parse. Required.
* `paramname`: The name of the first parameter from which the value was taken; used in error messages. If this is a
two-element list, the first element is the first parameter and the second element is the prefix of the remaining
parameters. Parameter names that are numbers are handled correctly, as are those with \1 in it marking where the
parameter index goes. Required.
* `qualifiers`: If specified, a possibly gappy list of left qualifiers to add to the parsed terms (for compatibility
purposes).
* `splitchar`: As in `parse_term_with_modifiers()`. The resulting per-term lists will be flattened.
* `frob`, `include_mods`, `exclude_mods`, `is_head`, `preserve_splitchar`, `parse_lang_prefix`, `delimiter_key`,
`escape_fun`, `unescape_fun`, `pre_normalize_modifiers`: As in `parse_term_with_modifiers()`.
Returns a list of objects, suitable for storing as one of the lists in `headdata.inflections` (once a label is added),
where `headdata` is the structure passed to [[Module:headword]].
]==]
function export.parse_term_list_with_modifiers(data)
local paramname, forms = data.paramname, data.forms
local qualifiers = data.qualifiers
local first, restpref
if type(paramname) == "table" then
first = paramname[1]
restpref = paramname[2]
else
first = paramname
restpref = paramname
end
local terms = {}
data = shallow_copy(data)
for i, val in ipairs(forms) do
data.paramname = i == 1 and first or type(restpref) == "number" and restpref + i - 1 or
restpref:find("\1", nil, true) and restpref:gsub("\1", tostring(i)) or restpref .. i
data.val = val
local parsed = export.parse_term_with_modifiers(data)
if qualifiers and qualifiers[i] then
if data.splitchar then
for _, term in ipairs(parsed) do
term.q = {qualifiers[i]}
end
else
parsed.q = {qualifiers[i]}
end
end
if data.splitchar then
extend(terms, parsed)
else
terms[i] = parsed
end
end
return terms
end
--[==[
Construct a link to [[Appendix:Glossary]] for `entry`. If `text` is specified, it is the display text; otherwise,
`entry` is used.
]==]
function export.glossary_link(entry, text)
text = text or entry
return "[[Phụ lục:Từ điển thuật ngữ#" .. entry .. "|" .. text .. "]]"
end
function export.replace_glossary_links_in_label(label)
if label:find("<<", nil, true) then
label = label:gsub("<<(.-)|(.-)>>", export.glossary_link):gsub("<<(.-)>>", export.glossary_link)
end
return label
end
-- If a part of speech ends with one of these, either pluralized or followed by ' forms', chop off whatever precedes.
-- Also, if a part of speech is of the form `BASIC_POS FOO forms` e.g. `noun construct forms`, remove the `FOO`
-- portion. Note that "phrase" is *NOT* listed here because 'prepositional phrase' and 'postpositional phrase' are not
-- types of 'phrase' per the definition of these parts of speech in Wiktionary.
local basic_poses = {
"Tính từ",
"Phó từ",
"Từ rút gọn",
"Danh từ riêng", -- may be converted to noun
"Danh từ",
"Phân từ",
"Đại từ",
"Ký hiệu",
"Động từ",
}
local function optimized_pattern_escape(pos)
if pos:find("-", nil, true) then
return pattern_escape(pos)
else
return pos
end
end
--[==[
Canonicalize and then simplify a qualified part of speech (e.g. ''comparative adjectives'', ''past participles'',
''pronoun possessive forms'') to its unqualified equivalent (e.g. ''adjectives'', ''participles'', ''pronoun forms''').
This is used for categorization, since generally the simplified part of speech is used in categories other than the one
for the part of speech itself. (For example, comparative adjectives are normally categorized as adjectives in categories
such as `Requests for inflections in ``lang`` adjective entries`, and proper nouns are, sometimes at least, categorized
under ` ``lang`` nouns with ...` rather than ` ``lang`` proper nouns with ...`.)
There are two cases where simplification does not happen: phrases and (optionally) proper nouns. In Wiktionary,
''phrase'' really means a clause or similar constituent (or occasionally non-constituent), so `prepositional phrases`
are not types of `phrases` and are not simplified. Proper nouns are simplified to nouns for some categories but left
as-is in other category names; by default, this function simplifies `proper nouns` to `nouns`, but if the parameter
`preserve_proper_nouns` is given, `proper nouns` is left as-is.
If `preserve_lemma_status` is given, the function attempts to return a part of speech that has the same lemma status
(lemma or non-lemma form) as the original part of speech; thus, `comparative adjectives` becomes `adjective forms`
and `diminutive nouns` becomes `noun forms` because both are non-lemma forms. If `preserve_lemma_status` is not given,
the function simply chops off the qualifiers, converting e.g. `comparative adjectives` to `adjectives` even though this
changes the lemma status of the part of speech.
The part of speech can be given singular or plural on input, but is always returned in plural form.
]==]
function export.simplify_pos(pos, preserve_proper_nouns, preserve_lemma_status)
-- This allows for both singular and plural parts of speech in 'pos'.
local plpos = canonicalize_pos(pos)
for _, basic_pos in ipairs(basic_poses) do
if basic_pos ~= "Danh từ riêng" or preserve_proper_nouns then
if plpos == "Biến thể hình thái " .. lcfirst(basic_pos) then
-- So 'proper noun forms' doesn't get converted to 'noun forms' in the next round
return plpos
end
local basic_pos_pattern = optimized_pattern_escape(basic_pos)
if plpos:find(("^Biến thể hình thái %s .*.$"):format(basic_pos_pattern)) then
return "Biến thể hình thái " .. lcfirst(basic_pos)
end
if plpos:find(("^Biến thể hình thái .* %s$"):format(basic_pos_pattern)) then
return "Biến thể hình thái " .. lcfirst(basic_pos)
end
local pl_basic_pos = basic_pos
if plpos == pl_basic_pos then
-- So 'proper nouns' doesn't get converted to 'nouns' in the next round
return plpos
end
local pl_basic_pos_pattern = optimized_pattern_escape(pl_basic_pos)
if plpos:find(("^.* %s$"):format(pl_basic_pos_pattern)) then
if preserve_lemma_status then
local lemma_status = require(headword_module).pos_lemma_or_nonlemma(plpos)
if lemma_status == "non-lemma form" then
return "Biến thể hình thái " .. lcfirst(basic_pos)
end
end
return pl_basic_pos
end
end
end
return plpos
end
function export.get_pos_variants(pos)
local retval = {}
retval.origpos = pos
retval.plpos = canonicalize_pos(pos)
retval.sgpos = singularize(retval.plpos)
retval.simp_plpos = export.simplify_pos(retval.plpos)
retval.simp_sgpos = singularize(retval.simp_plpos)
retval.simp_with_propn_plpos = export.simplify_pos(retval.plpos, "preserve_proper_nouns")
retval.simp_with_propn_sgpos = singularize(retval.simp_with_propn_plpos)
return retval
end
--[==[
Canonicalize a single term or term-like object or a list of either into a list of term-like objects. `abterms` is the
term or list to canonicalize, and `field` is the name of the field holding the term (defaulting to {"term"}). This
does the minimal work necessary, meaning that the return value may partly or completely share memory with the value
passed in. As a special case, if `abterms` is {nil}, {nil} is returned. If `origin` or `originating_term` are specified,
add fields `_origin` and/or `_originating_term` containing the specified value(s) to each resulting term-like object (in
this case, the object will be copied a necessary to avoid side-effecting the passed-in objects). `origin` is intended to
be a string indicating where the term originated from (e.g. {"default"} if a default value, {"resolve_special"} if
originating from a shortcut value such as {"+"}) and `originating_term` is the shortcut term itself, if any. (There is
generally no such term for default values.) If `termobj_for_string_terms` is specified, any terms in `abterms` specified
as strings will have all their properties other than `field` taken from `termobj_for_string_terms`.
]==]
function export.canonicalize_termobj_list(abterms, field, origin, originating_term, termobj_for_string_terms)
if abterms == nil then
return nil
end
local function make_termobj_from_string_term(term)
local retval
if termobj_for_string_terms then
retval = shallow_copy(termobj_for_string_terms)
else
retval = {}
end
retval[field] = term
retval._origin = origin
retval._originating_term = originating_term
return retval
end
local function shallow_copy_and_add_origin(termobj)
termobj = shallow_copy(termobj)
termobj._origin = origin
termobj._originating_term = originating_term
return termobj
end
field = field or "term"
if type(abterms) == "string" then
return {make_termobj_from_string_term(abterms)}
elseif not abterms[1] then
if origin ~= nil or originating_term ~= nil then
abterms = shallow_copy_and_add_origin(abterms)
end
return {abterms}
else
-- Check if already in full list term and return directly if so (unless `origin_val` is given, in which case we
-- need to shallow-copy both the list and each term in it).
local must_convert = false
for _, term in ipairs(abterms) do
if type(term) == "string" then
must_convert = true
break
end
end
if not must_convert then
if origin ~= nil or originating_term ~= nil then
abterms = shallow_copy(abterms)
for i, abterm in ipairs(abterms) do
abterms[i] = shallow_copy_and_add_origin(abterm)
end
end
return abterms
end
end
local retval = {}
for _, term in ipairs(abterms) do
if type(term) == "string" then
insert(retval, make_termobj_from_string_term(term))
else
if origin ~= nil or originating_term ~= nil then
term = shallow_copy_and_add_origin(term)
end
insert(retval, term)
end
end
return retval
end
function export.canonicalize_category_list(abcats)
-- FIXME: We should allow category objects in `headdata.categories` and not just strings. This seems to require
-- changes to [[Module:headword]].
if abcats == nil then
return nil
end
if abcats == false then
return {}
end
if type(abcats) == "string" then
abcats = {abcats}
end
return abcats
end
--[==[
Insert a fixed inflection (a label not associated with any inflection values) into an `inflections` field. The
`inflections` field will be initialized if needed. `data` is an object with the following fields:
* `headdata`: The headword structure passed to [[Module:headword]]. Required.
* `inflobj`: The object whose `inflections` field the terms are inserted into. Defaults to `headdata`. Only needs
to be set for nested inflections, which are specified for an inflection object rather than the headword data
structure as a whole.
* `label`: The label that the inflections are given; any parts of the label surrounded in `<<...>>` are linked to the
glossary. (If the contents of `<<...>>` contain a `|` in them, they are a two-part link.) Required.
* `originating_term`: The term object from which this label is derived. If specified, decorations will be taken from
this object.
]==]
function export.insert_fixed_inflection(data)
local headdata, origterm, label = data.headdata, data.originating_term, data.label
local inflobj = data.inflobj or headdata
inflobj.inflections = inflobj.inflections or {}
if not origterm then
insert(inflobj.inflections, {
label = export.replace_glossary_links_in_label(label)
})
else
if origterm.id then
error(("It doesn't make sense to pass in an ID '%s' for label '%s' in conjunction with a term value '%s'"
):format(origterm.id, label, origterm.term))
end
origterm = shallow_copy(origterm)
-- Preserve decorations
origterm.term = nil
origterm.label = export.replace_glossary_links_in_label(label)
insert(inflobj.inflections, origterm)
end
end
local function yes_and_no_cats(props)
if props.cats and props.no_cats then
local yes_cats = export.canonicalize_category_list(props.cats)
local no_cats = export.canonicalize_category_list(props.no_cats)
local cats = shallow_copy(yes_cats)
extend(cats, no_cats)
return cats
end
end
local function sometimes_or_yes_and_no_cats(props)
local sometimes_cats = export.canonicalize_category_list(props.sometimes_cats)
if sometimes_cats then
return sometimes_cats
else
return yes_and_no_cats(props)
end
end
local function no_value_fallback_label(props)
if props.fixed_adj_no_label then
return props.fixed_adj_no_label
end
if props.fixed_adj_label then
return "không " .. props.fixed_adj_label
end
return "không " .. (props.label_for_cats_and_modes or props.label)
end
local function with_value_fallback_label(props)
if props.fixed_adj_label then
return props.fixed_adj_label
end
-- FIXME, should this say 'with a plural/diminutive' etc. or is 'with plural/diminutive' enough?
return "với " .. (props.label_for_cats_and_modes or props.label)
end
--[==[ var:
Table describing how to handle the "mode" signals at the beginning of an inflection, generally indicating whether the
inflection exists and to what extent. The key is the mode signal itself, and the value is table with the following keys:
* `exists`: The value to return in the `exists` field of the object returned by `process_for_insertion`. This is
currently used in calling code simply to check for different modes; it is an easier-to-read equivalent of the mode
signal itself.
* `prefix`: The prefix used to directly specify the label and category/categories associated with the mode in calling
code. For example, the mode `--+` (which also arises when `-` is used with a following term) has prefix `usually_no_`,
which means that to directly specify its label, use `usually_no_label` and to directly specify its categories, use
`usually_no_cats`. If these fields don't exist, the functions in `fallback_label` and `fallback_cats` are used to
generate the label and categories.
* `fallback_label`: This is a function of one argument, `props` (the properties passed in by the caller), which should
generate the label if the user didn't directly specify a label (see `prefix` above).
* `fallback_cats`: This is a function of one argument, `props` (the properties passed in by the caller), which should
generate the categories if the user didn't directly specify them (see `prefix` above).
* `add_default`: If true, the code will attempt to add a default value if the user specifies this mode without giving
additional terms. This depends on the caller passing in a `mode_default` property (which can be a string, a term
object, a list of either, or a function generating any of those values). In many cases the `mode_default` property
may simply have a value such as {"+"}, which is processed later by resolve_special().
* `disallow_additional`: If true, no additional terms are allowed after the mode signal; if present, an error is
thrown.
]==]
export.special_modes = {
["-"] = {exists = "không", prefix = "no_",
fallback_label = no_value_fallback_label,
}, -- converted to "--+" if a term follows
["--+"] = {exists = "usually no", prefix = "usually_no_", add_default = true,
fallback_label = function(props)
if props.no_label then
return "thường " .. props.no_label
end
return "thường " .. no_value_fallback_label(props)
end,
fallback_cats = sometimes_or_yes_and_no_cats,
},
["+-"] = {exists = "đôi khi", prefix = "sometimes_", add_default = true,
fallback_label = function(props)
return "đôi khi " .. with_value_fallback_label(props)
end,
fallback_cats = yes_and_no_cats,
},
["++-"] = {exists = "thường", prefix = "usually_", add_default = true,
fallback_label = function(props)
return "đôi khi " .. with_value_fallback_label(props)
end,
fallback_cats = sometimes_or_yes_and_no_cats,
},
["!"] = {exists = "chưa được chứng thực", prefix = "unattested_", disallow_additional = true,
fallback_label = function(props)
return (props.label_for_cats_and_modes or props.label) .. " chưa được chứng thực"
end,
-- No default cats; provided for plurals in inflection_to_cats_and_label.
},
["?"] = {exists = "unknown", prefix = "unknown_", disallow_additional = true,
-- No default label as people have preferred not to see 'plural unknown or uncertain'
-- written out. No default cats; provided for plurals in inflection_to_cats_and_label.
},
}
function export.process_for_insertion(data)
local terms, mode_default, headdata = data.terms, data.mode_default, data.headdata
local retval = {
headdata = data.headdata,
}
if terms and terms[1] then
local mode = terms[1].term
local modespec = export.special_modes[mode]
if modespec then
terms = shallow_copy(terms)
retval.mode_term = terms[1]
remove(terms, 1)
if mode == "-" and terms[1] then
mode = "--+"
end
if terms[1] and modespec.disallow_additional then
error(("It doesn't make sense to specify explicit terms along with mode signal %s (%s)"):format(
mode, modespec.exists))
elseif not terms[1] and modespec.add_default and mode_default then
if type(mode_default) == "function" then
mode_default = mode_default(headdata, retval.mode_term)
end
if mode_default then
terms = export.canonicalize_termobj_list(mode_default, "term", "mode_default", retval.mode_term)
end
end
retval.numterms = #terms
retval.terms = terms
retval.mode = mode
retval.exists = modespec.exists
retval.prefix = modespec.prefix
else
retval.numterms = #terms
retval.terms = terms
retval.exists = "yes"
retval.prefix = ""
end
else
retval.numterms = 0
retval.terms = {}
-- retval.exists = nil
retval.request = data.request
end
return retval
end
--[==[
Insert previously-parsed terms into an `inflections` field. The `inflections` field will be initialized if needed.
`data` is an object with the following fields:
* `headdata`: The headword structure passed to [[Module:headword]]. Required.
* `inflobj`: The object whose `inflections` field the terms are inserted into. Defaults to `headdata`. Only needs
to be set for nested inflections, which are specified for an inflection object rather than the headword data
structure as a whole.
* `terms`: The list of parsed terms. If {nil} or omitted, nothing happens unless `request` is set.
* `label`: The label that the inflections are given; any parts of the label surrounded in `<<...>>` are linked to the
glossary. (If the contents of `<<...>>` contain a `|` in them, they are a two-part link.) Required.
* `fixed_adj_label`: If specified, an adjective used to construct the defaults for the fixed labels specified by
`no_label`, `usually_no_label` and `sometimes_label`. Examples are "comparable" for a comparative and "countable"
for a plural.
* `no_label`: If the term is {"-"} and there are no other terms, insert a fixed label with this value. If
`fixed_adj_label` is given, defaults to {"not "} plus the adjective, else {"no "} plus the label.
* `usually_no_label`: If the term is {"-"} and there are other terms, insert a fixed label with this value. Defaults to
{"usually "} plus the value of `no_label`.
* `sometimes_label`: If the term is {"+-"}, insert a fixed label with this value. If `fixed_adj_label` is given,
defaults to {"sometimes "} plus the adjective, else {"sometimes with a(n) "} plus the label.
* `cats`: List of categories to insert when terms are given that are not {"-"}. Each category is a string naming a full
category to insert (including the appropriate language name prefixed).
* `no_cats`: List of categories to insert when a term is given as {"-"}.
* `usually_no_cats`: List of categories to insert when a term is given as {"-"} and additional terms are specified as
well (representing, e.g. for the inflection {"plural"}, a term which usually has no plural but does under some
circumstances). If omitted, both the categories in `cats` and `no_cats` are inserted.
* `sometimes_cats`: List of categories to insert when a term is given as {"+-"} (indicating that the inflection
sometimes exists and sometimes does not, normally depending on the precise sense involved). If omitted, both the
categories in `cats` and `no_cats` are inserted (same default as for `usually_no_cats`).
* `accel`: If specified, a full accelerator object to add to the inflections.
* `request`: If specified and no terms are given, insert a label with a request for inflections to be given.
* `enable_auto_translit`: If specified and terms are given, display automatic transliteration of the terms.
The return value indicates whether the inflection exists and how many terms are in it. It is an object with the
following fields:
* `exists`: {"yes"} if one or more terms were specified; {"no"} if the value was given as {"-"}; {"usually no"} if
the first value was given as {"-"} but additional terms were supplied; otherwise {nil}, indicating that the status
is unspecified.
* `numterms`: Number of terms in the inflection. Will be 0 unless `exists` has the value {"yes"} or {"usually no"}.
* `terms`: The terms that were inserted. If there are no terms, this is an empty list.
* `request`: True if no terms were specified but a term request was inserted into the inflection (because
`data.request` was specified). Otherwise {nil}.
]==]
function export.insert_processed_inflection(data)
local headdata, processed, label = data.headdata, data.processed, data.label
local inflobj = data.inflobj or headdata
local accel = data.accel
if data.accel_form then
if accel then
error("Internal error: can't specify both data.accel and data.accel_form")
end
if headdata.heads then
local lemmas = {}
local lemma_translits = {}
for i, headobj in ipairs(headdata.heads) do
lemmas[i] = headobj.term
if lemmas[i] == "+" then
error("Internal error: If you use data.accel_form, you should have resolved all occurrences of + in heads appropriately")
end
lemma_translits[i] = headobj.tr
end
accel = {
lemma = lemmas,
lemma_translit = lemma_translits,
form = data.accel_form,
}
else
accel = {
form = data.accel_form,
}
end
end
local function insert_cats(cats)
for _, cat in ipairs(cats) do
insert(headdata.categories, cat)
end
end
-- Handle the fixed label (if any) and cats (if any) associated with a mode.
if processed.mode then
local modespec = export.special_modes[processed.mode]
local mode_prefix = modespec.prefix
local fixed_label = data[mode_prefix .. "label"]
if not fixed_label and modespec.fallback_label then
fixed_label = modespec.fallback_label(data)
end
if fixed_label then
export.insert_fixed_inflection {
headdata = headdata,
inflobj = inflobj,
originating_term = processed.mode_term,
label = fixed_label,
}
end
local mode_cats = data[mode_prefix .. "cats"]
if not mode_cats and modespec.fallback_cats then
mode_cats = modespec.fallback_cats(data)
end
mode_cats = export.canonicalize_category_list(mode_cats)
if mode_cats then
insert_cats(mode_cats)
end
elseif processed.terms[1] and data.cats then
-- If no mode, no fixed label but there may be categories.
insert_cats(data.cats)
end
local terms = processed.terms
if terms[1] then
-- FIXME, if there's a mode we shallow_copy() twice.
terms = shallow_copy(terms)
terms.label = export.replace_glossary_links_in_label(label)
if accel then
terms.accel = accel
end
terms.enable_auto_translit = data.enable_auto_translit
inflobj.inflections = inflobj.inflections or {}
insert(inflobj.inflections, terms)
elseif processed.request then
inflobj.inflections = inflobj.inflections or {}
insert(inflobj.inflections, {
label = export.replace_glossary_links_in_label(label),
request = true,
})
end
end
--[==[
Insert previously-parsed terms into an `inflections` field. The `inflections` field will be initialized if needed.
`data` is an object with the following fields:
* `headdata`: The headword structure passed to [[Module:headword]]. Required.
* `inflobj`: The object whose `inflections` field the terms are inserted into. Defaults to `headdata`. Only needs
to be set for nested inflections, which are specified for an inflection object rather than the headword data
structure as a whole.
* `terms`: The list of parsed terms. If {nil} or omitted, nothing happens unless `request` is set.
* `label`: The label that the inflections are given; any parts of the label surrounded in `<<...>>` are linked to the
glossary. (If the contents of `<<...>>` contain a `|` in them, they are a two-part link.) Required.
* `fixed_adj_label`: If specified, an adjective used to construct the defaults for the fixed labels specified by
`no_label`, `usually_no_label` and `sometimes_label`. Examples are "comparable" for a comparative and "countable"
for a plural.
* `no_label`: If the term is {"-"} and there are no other terms, insert a fixed label with this value. If
`fixed_adj_label` is given, defaults to {"not "} plus the adjective, else {"no "} plus the label.
* `usually_no_label`: If the term is {"-"} and there are other terms, insert a fixed label with this value. Defaults to
{"usually "} plus the value of `no_label`.
* `sometimes_label`: If the term is {"+-"}, insert a fixed label with this value. If `fixed_adj_label` is given,
defaults to {"sometimes "} plus the adjective, else {"sometimes with a(n) "} plus the label.
* `cats`: List of categories to insert when terms are given that are not {"-"}. Each category is a string naming a full
category to insert (including the appropriate language name prefixed).
* `no_cats`: List of categories to insert when a term is given as {"-"}.
* `usually_no_cats`: List of categories to insert when a term is given as {"-"} and additional terms are specified as
well (representing, e.g. for the inflection {"plural"}, a term which usually has no plural but does under some
circumstances). If omitted, both the categories in `cats` and `no_cats` are inserted.
* `sometimes_cats`: List of categories to insert when a term is given as {"+-"} (indicating that the inflection
sometimes exists and sometimes does not, normally depending on the precise sense involved). If omitted, both the
categories in `cats` and `no_cats` are inserted (same default as for `usually_no_cats`).
* `accel`: If specified, a full accelerator object to add to the inflections.
* `request`: If specified and no terms are given, insert a label with a request for inflections to be given.
* `enable_auto_translit`: If specified and terms are given, display automatic transliteration of the terms.
The return value indicates whether the inflection exists and how many terms are in it. It is an object with the
following fields:
* `exists`: {"yes"} if one or more terms were specified; {"no"} if the value was given as {"-"}; {"usually no"} if
the first value was given as {"-"} but additional terms were supplied; otherwise {nil}, indicating that the status
is unspecified.
* `numterms`: Number of terms in the inflection. Will be 0 unless `exists` has the value {"yes"} or {"usually no"}.
* `terms`: The terms that were inserted. If there are no terms, this is an empty list.
* `request`: True if no terms were specified but a term request was inserted into the inflection (because
`data.request` was specified). Otherwise {nil}.
]==]
function export.insert_inflection(data)
local headdata, terms, mode_default = data.headdata, data.terms, data.mode_default
local processed = export.process_for_insertion {
headdata = headdata,
terms = terms,
mode_default = mode_default,
}
data = shallow_copy(data)
data.processed = processed
export.insert_processed_inflection(data)
return processed
end
--[==[
Parse raw arguments from `forms` for inline modifiers, and insert the resulting terms (which should not require
significant additional processing) into `headdata.inflections`. `data` is an object with the following fields:
* `forms`: The list of raw values to parse. If {nil} or omitted, nothing happens.
* `headdata`: The headword structure passed to [[Module:headword]]. Required.
* `paramname`: As in `parse_term_list_with_modifiers()`. Required.
* `label`: As in `insert_inflection()`. Required.
* `qualifiers`, `frob`, `include_mods`, `exclude_mods`, `is_head`, `splitchar`, `preserve_splitchar`, `delimiter_key`,
`escape_fun`, `unescape_fun`, `pre_normalize_modifiers`: As in `parse_term_list_with_modifiers()`.
* `accel`: As in `insert_inflection()`.
Return value is as in `insert_inflection()`.
]==]
function export.parse_and_insert_inflection(data)
local forms = data.forms
if forms and forms[1] then
data = shallow_copy(data)
data.forms = forms
data.terms = export.parse_term_list_with_modifiers(data)
return export.insert_inflection(data)
end
return {
numterms = 0
}
end
--[==[
Combine two sets of decorations. If either is {nil}, just return the other, and if both are {nil}, return {nil}.
]==]
function export.combine_decorations(decs1, decs2)
if not decs1 and not decs2 then
return nil
end
if not decs1 then
return decs2
end
if not decs2 then
return decs1
end
local combined = shallow_copy(decs1)
for _, dec in ipairs(decs2) do
insert_if_not(combined, dec)
end
return combined
end
function export.combine_qualifiers_or_labels(...)
-- FIXME: Đã thêm vào ngày 17 tháng 9 năm 2026.
error("Vui lòng dùng combine_decorations")
end
--[==[
Combine the decorations (qualifiers, labels, references) and ID's of two term objects. `destobj` is the "destination
term object" into which the combined properties are written, and `srcobj` is the "source object" into which the
properties are merged. `destobj` is side-effected (but the lists inside of `destobj` are not); if this is undesirable,
make sure to shallow-copy `destobj` first. If both objects have values for a given decoration, the values of `destobj`
come first. If both objects have a value for `id`, the values must match or an error is thrown; otherwise, the resulting
value of `id` comes from whichever one is defined.
'''NOTE:''' This may not be the correct behavior when deduplicating a list of term objects. See
`insert_termobj_combining_duplicates` for a different approach.
]==]
function export.combine_termobj_decorations(destobj, srcobj)
destobj.q = export.combine_decorations(destobj.q, srcobj.q)
destobj.qq = export.combine_decorations(destobj.qq, srcobj.qq)
destobj.l = export.combine_decorations(destobj.l, srcobj.l)
destobj.ll = export.combine_decorations(destobj.ll, srcobj.ll)
destobj.refs = export.combine_decorations(destobj.refs, srcobj.refs)
if destobj.id and srcobj.id and destobj.id ~= srcobj.id then
-- FIXME: We probably want to pass in an error function
error(("Can't specify two different ID's %s and %s when combining objects"):format(srcobj.id, destobj.id))
end
destobj.id = destobj.id or srcobj.id
return destobj
end
function export.combine_termobj_qualifiers_labels(...)
-- FIXME: Đã thêm vào ngày 17 tháng 9 năm 2026.
error("Vui lòng dùng combine_termobj_decorations")
end
function export.termobj_has_decorations(obj)
return obj.q and obj.q[1] or obj.qq and obj.qq[1] or obj.l and obj.l[1] or obj.ll and obj.ll[1] or
obj.refs and obj.refs[1]
end
function export.termobj_has_qualifiers_or_labels(...)
-- FIXME: Đã thêm vào ngày 17 tháng 9 năm 2026.
error("Vui lòng dùng termobj_has_decorations")
end
local function one_decoration_equal(prop1, prop2)
local prop1_is_nil = not prop1 or not prop1[1]
local prop2_is_nil = not prop2 or not prop2[1]
if prop1_is_nil and prop2_is_nil then
return true
end
if prop1_is_nil or prop2_is_nil then
return false
end
return deep_equals(prop1, prop2)
end
function export.termobj_decorations_equal(obj1, obj2)
return one_decoration_equal(obj1.q, obj2.q) and
one_decoration_equal(obj1.qq, obj2.qq) and
one_decoration_equal(obj1.l, obj2.l) and
one_decoration_equal(obj1.ll, obj2.ll) and
one_decoration_equal(obj1.refs, obj2.refs) and
obj1.id == obj2.id
end
function export.termobj_ancillary_properties_equal(...)
-- FIXME: Đã thêm vào ngày 17 tháng 9 năm 2026.
error("Vui lòng dùng termobj_decorations_equal")
end
function export.convert_termobj_to_formobj(termobj)
local formobj = {
form = termobj.term,
translit = termobj.tr,
}
local footnotes
local function mods_to_footnote(mod_prefix, mod_vals)
if mod_vals and mod_vals[1] then
footnotes = footnotes or {}
for _, val in ipairs(mod_vals) do
insert(footnotes, "[" .. mod_prefix .. ":" .. val .. "]")
end
end
end
mods_to_footnote("q", termobj.q)
mods_to_footnote("qq", termobj.qq)
mods_to_footnote("l", termobj.l)
mods_to_footnote("ll", termobj.ll)
mods_to_footnote("ref", termobj.refs)
mods_to_footnote("id", termobj.id and {termobj.id} or nil)
formobj.footnotes = footnotes
return formobj
end
local recognized_multi_mods = {
q = "q",
qq = "qq",
l = "l",
ll = "ll",
ref = "refs",
}
local recognized_single_mods = {
id = "id",
}
function export.add_footnote_to_termobj(termobj, footnote)
local stripped_footnote = footnote:match("^%[(.*)%]$")
if not stripped_footnote then
error("Internal error: Footnote should be surrounded by brackets at this stage: " .. footnote)
end
local prefix, rest = stripped_footnote:match("^([a-z]+):(.+)$")
local field, is_multi
if prefix then
if recognized_multi_mods[prefix] then
field = recognized_multi_mods[prefix]
is_multi = true
elseif recognized_single_mods[prefix] then
field = recognized_single_mods[prefix]
is_multi = false
end
end
if not field then
rest = stripped_footnote
field = "l"
is_multi = true
end
if is_multi then
if not termobj[field] then
termobj[field] = {}
end
insert(termobj[field], rest)
else
if termobj[field] and termobj[field] ~= rest then
error(("Can't set two values for '%s': '%s' and '%s'"):format(field, termobj[field], rest))
end
termobj[field] = rest
end
end
function export.convert_formobj_to_termobj(formobj)
local termobj = {
term = formobj.form,
tr = formobj.translit,
}
if formobj.footnotes then
for _, footnote in ipairs(formobj.footnotes) do
export.add_footnote_to_termobj(termobj, footnote)
end
end
return termobj
end
local function extract_termobj_field_modifiers(fieldval)
return fieldval:match("^([*+]?)(.*)$")
end
function export.remove_termobj_field_modifiers(termobj)
local function remove_field_modifiers(field)
if termobj[field] and termobj[field][1] then
local any_field_modifiers = false
for _, val in ipairs(termobj[field]) do
local field_mods, _ = extract_termobj_field_modifiers(val)
if field_mods ~= "" then
any_field_modifiers = true
break
end
end
local new_field = {}
if any_field_modifiers then
for _, val in ipairs(termobj[field]) do
local _, field_without_mods = extract_termobj_field_modifiers(val)
insert_if_not(new_field, field_without_mods)
end
termobj[field] = new_field
end
end
end
remove_field_modifiers("q")
remove_field_modifiers("qq")
remove_field_modifiers("l")
remove_field_modifiers("ll")
remove_field_modifiers("refs")
end
function export.insert_termobj_combining_duplicates(destobjs, termobj)
for _, destobj in ipairs(destobjs) do
if destobj.term == termobj.term and destobj.tr == termobj.tr then
-- Form already present; maybe combine footnotes.
local function combine_field_values(field)
if termobj[field] and termobj[field][1] then
-- Check to see if there are existing values with *; if so, remove them.
if destobj[field] and destobj[field][1] then
local any_values_with_asterisk = false
for _, val in ipairs(destobj[field]) do
local field_mods, _ = extract_termobj_field_modifiers(val)
if field_mods:find("%*") then
any_values_with_asterisk = true
break
end
end
if any_values_with_asterisk then
local filtered_values = {}
for _, val in ipairs(destobj[field]) do
local field_mods, _ = extract_termobj_field_modifiers(val)
if not field_mods:find("%*") then
insert(filtered_values, val)
end
end
if filtered_values[1] then
destobj[field] = filtered_values
else
destobj[field] = nil
end
end
end
local any_footnotes_with_plus = false
for _, val in ipairs(termobj[field]) do
local field_mods, _ = extract_termobj_field_modifiers(val)
if field_mods:find("%+") then
any_footnotes_with_plus = true
break
end
end
if any_footnotes_with_plus then
if not destobj[field] then
destobj[field] = {}
else
destobj[field] = shallow_copy(destobj[field])
end
for _, val in ipairs(termobj[field]) do
local already_seen = false
local field_mods, field_without_mods = extract_termobj_field_modifiers(val)
if field_mods:find("%+") then
for _, existing_val in ipairs(destobj[field]) do
local _, existing_field_without_mods =
extract_termobj_field_modifiers(existing_val)
if existing_field_without_mods == field_without_mods then
already_seen = true
break
end
end
if not already_seen then
insert(destobj[field], val)
end
end
end
end
end
end
combine_field_values("q")
combine_field_values("qq")
combine_field_values("l")
combine_field_values("ll")
combine_field_values("refs")
if destobj.id and termobj.id and destobj.id ~= termobj.id then
-- FIXME: We probably want to pass in an error function
error(("Can't specify two different ID's %s and %s when combining objects"):format(termobj.id, destobj.id))
end
destobj.id = destobj.id or termobj.id
return
end
end
insert(destobjs, termobj)
end
export.allowed_special_indicators = {
["first"] = true,
["first-second"] = true,
["first-last"] = true,
["second"] = true,
["last"] = true,
["each"] = true,
["+"] = true, -- requests the default behavior with preposition handling
}
--[==[
Check for special indicators (values such as {"+first"} or {"+first-last"} that are used in a `pl`, `f`, etc. argument
and indicate how to inflect a multiword term). If `form` is such an indicator, the return value is `form` minus
the initial `+` sign; otherwise, if form begins with a `+` sign, an error is thrown; otherwise the return value is nil.
]==]
function export.get_special_indicator(form, noerror)
if form:find("^%+") then
form = form:gsub("^%+", "")
if not export.allowed_special_indicators[form] then
if noerror then
return nil
end
local indicators = {}
for indic, _ in pairs(export.allowed_special_indicators) do
insert(indicators, "+" .. indic)
end
sort(indicators)
error("Special inflection indicator beginning with '+' can only be " ..
mw.text.listToText(indicators) .. ": +" .. form)
end
return form
end
return nil
end
local function add_endings(bases, endings)
local retval = {}
if type(bases) ~= "table" then
bases = {bases}
end
if type(endings) ~= "table" then
endings = {endings}
end
for _, base in ipairs(bases) do
for _, ending in ipairs(endings) do
insert(retval, base .. ending)
end
end
return retval
end
--[==[
Inflect a possibly multiword or hyphenated term `form` using the function `inflect`, which is a function of one argument
that is called on a single word to inflect and should return either the inflected word or a list of inflected words.
`special` indicates how to inflect the multiword term and should be e.g. {"first"} to inflect only the first word,
{"first-last"} to inflect the first and last words, {"each"} to inflect each word, etc. See `allowed_special_indicators`
above for the possibilities. If `special` is `+`, or is omitted and the term is multiword (i.e. containing a space
character), and `prepositions` is supplied, the function checks for multiword or hyphenated terms containing the
prepositions in `prepositions`, e.g. Italian [[senso di marcia]] or [[medaglia d'oro]] or Portuguese
[[tartaruga-do-mar]]. If such a term is found, only the first word is inflected. Otherwise, the default is
{"first-last"}. `prepositions` is a list of Lua patterns matching prepositions. The patterns will automatically have the
separator character (space or hyphen) added to the left side but not the right side, so they should contain a space
character (which will automatically be converted to the appropriate separator) on the right side unless the preposition
is joined on the right side with an apostrophe. Examples of preposition patterns for Italian are {"di "}, {"sull'"} and
{"d?all[oae] "} (which matches {"dallo "}, {"dalle "}, {"alla "}, etc.).
The return value is always either a list of inflected multiword or hyphenated terms, or nil if `special` is omitted
and `form` is not multiword. (If `special` is specified and `form` is not multiword or hyphenated, an error results.)
]==]
function export.handle_multiword(form, special, inflect, prepositions, sep)
sep = sep or form:find(" ") and " " or "%-"
local raw_sep = sep == " " and " " or "-"
-- Used to add regex version of separator in the replacement portion of ugsub() or :gsub()
local sep_replacement = sep == " " and " " or "%%-"
-- Given a Lua pattern, replace space with the appropriate separator.
local function hack_re(re)
if sep == " " then
return re
end
return (re:gsub(" ", sep_replacement))
end
if special == "first" then
local first, rest = form:match(hack_re("^(.-)( .*)$"))
if not first then
error("Special indicator 'first' can only be used with a multiword term: " .. form)
end
return add_endings(inflect(first), rest)
elseif special == "second" then
local first, second, rest = form:match(hack_re("^([^ ]+ )([^ ]+)( .*)$"))
if not first then
error("Special indicator 'second' can only be used with a term with three or more words: " .. form)
end
return add_endings(add_endings({first}, inflect(second)), rest)
elseif special == "first-second" then
local first, space, second, rest = form:match(hack_re("^([^ ]+)( )([^ ]+)( .*)$"))
if not first then
error("Special indicator 'first-second' can only be used with a term with three or more words: " .. form)
end
return add_endings(add_endings(add_endings(inflect(first), space), inflect(second)), rest)
elseif special == "each" then
local terms = split(form, sep)
if #terms < 2 then
error("Special indicator 'each' can only be used with a multiword term: " .. form)
end
for i, term in ipairs(terms) do
terms[i] = inflect(term)
if i > 1 then
terms[i] = add_endings(raw_sep, terms[i])
end
end
local result = ""
for _, term in ipairs(terms) do
result = add_endings(result, term)
end
return result
elseif special == "first-last" then
local first, middle, last = form:match(hack_re("^(.-)( .* )(.-)$"))
if not first then
first, middle, last = form:match(hack_re("^(.-)( )(.*)$"))
end
if not first then
error("Special indicator 'first-last' can only be used with a multiword term: " .. form)
end
return add_endings(add_endings(inflect(first), middle), inflect(last))
elseif special == "last" then
local rest, last = form:match(hack_re("^(.* )(.-)$"))
if not rest then
error("Special indicator 'last' can only be used with a multiword term: " .. form)
end
return add_endings(rest, inflect(last))
elseif special and special ~= "+" then
error("Unrecognized special=" .. special)
end
-- Only do default behavior if special indicator '+' explicitly given or separator is space; otherwise we will
-- break existing behavior with hyphenated words.
if (special == "+" or sep == " ") and form:find(sep) then
if prepositions then
-- check for prepositions in the middle of the word; do it this way so we can handle
-- more than one word before the preposition (and usually inflect each word)
for _, prep in ipairs(prepositions) do
local first, space_prep_rest = umatch(form, hack_re("^(.-)( " .. prep .. ".*)$"))
if first then
return add_endings(inflect(first), space_prep_rest)
end
end
end
-- multiword or hyphenated expressions default to first-last; we need to pass in the separator to avoid
-- problems with multiword terms containing hyphens in the individual words
return export.handle_multiword(form, "first-last", inflect, prepositions, sep)
end
return nil
end
local function link_hyphen_split_component(word, data)
if data.link_hyphen_split_component then
return data.link_hyphen_split_component(word)
else
return "[[" .. word .. "]]"
end
end
-- Default function to split a word on apostrophes. Don't split apostrophes at the beginning or end of a word (e.g.
-- [['ndrangheta]] or [[po']]). Handle multiple apostrophes correctly, e.g. [[l'altr'ieri]] -> [[l']][altr']][[ieri]].
function export.default_split_apostrophe(word, data)
local apostrophe_parts = split(word, "'", true, true)
local linked_apostrophe_parts = {}
local apostrophes_at_beginning = ""
local i = 1
-- Apostrophes at beginning get attached to the first word after (which will always exist but may
-- be blank if the word consists only of apostrophes).
while i < #apostrophe_parts do -- <, not <=, in case the word consists only of apostrophes
local apostrophe_part = apostrophe_parts[i]
i = i + 1
if apostrophe_part == "" then
apostrophes_at_beginning = apostrophes_at_beginning .. "'"
else
break
end
end
apostrophe_parts[i] = apostrophes_at_beginning .. apostrophe_parts[i]
-- Now, do the remaining parts. A blank part indicates more than one apostrophe in a row; we join
-- all of them to the preceding word.
while i <= #apostrophe_parts do
local apostrophe_part = apostrophe_parts[i]
if apostrophe_part == "" then
linked_apostrophe_parts[#linked_apostrophe_parts] =
linked_apostrophe_parts[#linked_apostrophe_parts] .. "'"
elseif i == #apostrophe_parts then
insert(linked_apostrophe_parts, apostrophe_part)
else
insert(linked_apostrophe_parts, apostrophe_part .. "'")
end
i = i + 1
end
for j, tolink in ipairs(linked_apostrophe_parts) do
linked_apostrophe_parts[j] = link_hyphen_split_component(tolink, data)
end
return concat(linked_apostrophe_parts)
end
--[=[
Auto-add links to a word that should not have spaces but may have hyphens and/or apostrophes. We split off final
punctuation, then split on hyphens if `data.split_hyphen` is given, and also split on apostrophes if
`data.split_apostrophe` is given. We only split on hyphens if they are in the middle of the word, not at the beginning
or end (hyphens at the beginning or end indicate suffixes or prefixes, respectively). `include_hyphen_prefixes`, if
given, is a set of prefixes (not including the final hyphen) where we should include the final hyphen in the prefix.
Hence, e.g. if "anti" is in the set, a Portuguese word like [[anti-herói]] "anti-hero" will be split [[anti-]][[herói]]
(whereas a word like [[código-fonte]] "source code" will be split as [[código]]-[[fonte]]).
If `data.split_apostrophe` is specified, we split on apostrophes unless `data.no_split_apostrophe_words` is given and
the word is in the specified set, such as French [[c'est]] and [[quelqu'un]]. If `data.split_apostrophe` is true, the
default algorithm applies, which splits on all apostrophes except those at the beginning and end of a word (as in
Italian [['ndrangheta]] or [[po']]), and includes the apostrophe in the link to its left (so we auto-split French
[[l'eau]] as [[l']][[eau]] and [[l'altr'ieri]] as [[l']][altr']][[ieri]]). If `data.split_apostrophe` is specified
but not `true`, it should be a function of one argument that does custom apostrophe-splitting. The argument is the word
to split, and the return value should be the split and linked word.
]=]
local function add_single_word_links(space_word, data, term_has_spaces)
local space_word_no_punct, punct
local punct_pattern = data.punctuation
if punct_pattern and is_callable(punct_pattern) then
space_word_no_punct, punct = punct_pattern(space_word)
else
if punct_pattern == nil then
punct_pattern = "[,;:?!]"
end
space_word_no_punct, punct = umatch(space_word, "^(.*)(" .. punct_pattern .. ")$")
end
space_word_no_punct = space_word_no_punct or space_word
punct = punct or ""
local words
if space_word_no_punct:sub(1, 1) == "-" or space_word_no_punct:sub(-1) == "-" then
-- don't split prefixes and suffixes
words = {space_word_no_punct}
else
local splitter
if term_has_spaces then
splitter = data.split_hyphen_when_space
else
splitter = data.split_hyphen_when_no_space
end
if is_callable(splitter) then
words = splitter(space_word_no_punct)
if type(words) == "string" then
return words .. punct
end
end
end
if not words then
local split_hyphen
if term_has_spaces then
split_hyphen = data.split_hyphen_when_space
else
split_hyphen = data.split_hyphen_when_no_space
if split_hyphen == nil then -- default to true; use `false` to avoid this
split_hyphen = true
end
end
if split_hyphen then
words = split(space_word_no_punct, "-", true, true)
else
words = {space_word_no_punct}
end
end
local linked_words = {}
for j, word in ipairs(words) do
if j < #words and data.include_hyphen_prefixes and data.include_hyphen_prefixes[word] then
word = "[[" .. word .. "-]]"
elseif j > 1 and data.include_hyphen_suffixes and data.include_hyphen_suffixes[word] then
word = "[[-" .. word .. "]]"
else
-- Don't split on apostrophes if the word is in `no_split_apostrophe_words`.
if (not data.no_split_apostrophe_words or not data.no_split_apostrophe_words[word]) and
data.split_apostrophe and word:find("'", nil, true) then
if data.split_apostrophe == true then
word = export.default_split_apostrophe(word, data)
else -- custom apostrophe splitter/linker
word = data.split_apostrophe(word)
end
elseif word ~= "" then -- avoid -[[]]- (e.g. f--k)
word = link_hyphen_split_component(word, data)
end
if j < #words then
word = word .. "-"
end
end
insert(linked_words, word)
end
return concat(linked_words) .. punct
end
--[=[
Auto-add links to a multiword term. `data` contains fields customizing how to do this. By default we proceed as follows:
(1) If the term already has embedded links in it, they are left unchanged.
(2) Otherwise, if there are spaces present, we split on spaces and link each word separately.
(3) If a given space-separated component ends in punctuation (defaulting to [,;:?!]), it is separated off, the remainder
of the algorithm run, and the punctuation pasted back on.
(4) If there are hyphens in a given space-separated component, we may link each hyphenated term separately depending
on the settings in `data`. Normally the hyphens are not included in the linked terms, but this can be overridden
for specific prefixes and/or suffixes. By default, if there are spaces in the multiword term, we do not link
hyphenated components (because of cases like "boire du petit-lait" where "petit-lait" should be linked as a whole),
but do so otherwise (e.g. for "avant-avant-hier"); this can overridden for cases like "croyez-le ou non".
Cases where only some of the hyphens should be split can always be handled by explicitly specifying the head (e.g.
"Nord-Pas-de-Calais" given as head=[[Nord]]-[[Pas-de-Calais]]).
(5) If there are apostrophes in a given component, we may link each apostrophe-separated term separately depending
on the settings in `data`, including the apostrophe in the link to its left (so we split "de l'eau" as
"[[de]] [[l']][[eau]]").
The settings in `data` are as follows:
`split_hyphen_when_no_space`: Whether to split on hyphens when the term has no spaces. Defaults to true if set to `nil`.
This can be a function of one argument, to implement a custom splitting algorithm for hyphen-separated terms. If
this returns [FIXME: FINISH ME ...]
If `data.split_apostrophe` is specified, we split on apostrophes unless `data.no_split_apostrophe_words` is given and
the word is in the specified set, such as French [[c'est]] and [[quelqu'un]]. If `data.split_apostrophe` is true, the
default algorithm applies, which splits on all apostrophes except those at the beginning and end of a word (as in
Italian [['ndrangheta]] or [[po']]), and includes the apostrophe in the link to its left (so we auto-split French
[[l'eau]] as [[l']][[eau]] and [[l'altr'ieri]] as [[l']][altr']][[ieri]]). If `data.split_apostrophe` is specified
but not `true`, it should be a function of one argument that does custom apostrophe-splitting. The argument is the word
to split, and the return value should be the split and linked word.
We don't always split on hyphens because of cases like "boire du petit-lait" where "petit-lait" should be linked as a
whole, but provide the option to do it for cases like "croyez-le ou non". If there's no space, however, then it makes
sense to split on hyphens by `no_split_apostrophe_words` and `include_hyphen_prefixes` allow for special-case handling
of particular words and are as described in the comment above add_single_word_links().
]=]
function export.add_links_to_multiword_term(term, data)
if term:match("[%[%]]") then
return term
end
local words = split(term, " ", true, true)
local term_has_spaces = #words > 1
local linked_words = {}
for _, word in ipairs(words) do
insert(linked_words, add_single_word_links(word, data, term_has_spaces))
end
local retval = concat(linked_words, " ")
-- If we ended up with a single link consisting of the entire term,
-- remove the link.
return retval:match("^%[%[([^%[%]]*)%]%]$") or retval
end
local function canonicalize_begin_end_spec(spec)
local from, to = spec:match("^(.-):(.*)$")
if not from then
from = spec
to = ""
end
return from, to
end
--[==[
Given a `linked_term` that is the output of add_links_to_multiword_term(), apply modifications as given in
`modifier_spec` to change the link destination of subterms (normally single-word non-lemma forms; sometimes
collections of adjacent words). This is usually used to link non-lemma forms to their corresponding lemma, but can
also be used to replace a span of adjacent separately-linked words to a single multiword lemma. The format of
`modifier_spec` is one or more semicolon-separated subterm specs, where each such spec is of the form
SUBTERM:DEST, where SUBTERM is one or more words in the `linked_term` but without brackets in them, and DEST is the
corresponding link destination to link the subterm to. Any occurrence of ~ in DEST is replaced with SUBTERM.
Alternatively, a single modifier spec can be of the form BEGIN[FROM:TO], which is equivalent to writing
BEGINFROM:BEGINTO (see example below).
For example, given the source phrase [[il bue che dice cornuto all'asino]] "the pot calling the kettle black"
(literally "the ox that calls the donkey horned/cuckolded"), the result of calling add_links_to_multiword_term()
is [[il]] [[bue]] [[che]] [[dice]] [[cornuto]] [[all']][[asino]]. With a modifier_spec of 'dice:dire', the result
is [[il]] [[bue]] [[che]] [[dire|dice]] [[cornuto]] [[all']][[asino]]. Here, based on the modifier spec, the
non-lemma form [[dice]] is replaced with the two-part link [[dire|dice]].
Another example: given the source phrase [[chi semina vento raccoglie tempesta]] "sow the wind, reap the whirlwind"
(literally (he) who sows wind gathers [the] tempest"). The result of calling add_links_to_multiword_term() is
[[chi]] [[semina]] [[vento]] [[raccoglie]] [[tempesta]], and with a modifier_spec of 'semina:~re; raccoglie:~re',
the result is [[chi]] [[seminare|semina]] [[vento]] [[raccogliere|raccoglie]] [[tempesta]]. Here we use the ~
notation to stand for the non-lemma form in the destination link.
A more complex example is [[se non hai altri moccoli puoi andare a letto al buio]], which becomes
[[se]] [[non]] [[hai]] [[altri]] [[moccoli]] [[puoi]] [[andare]] [[a]] [[letto]] [[al]] [[buio]] after calling
add_links_to_multiword_term(). With the following modifier_spec:
'hai:avere; altr[i:o]; moccol[i:o]; puoi: potere; andare a letto:~; al buio:~', the result of applying the spec is
[[se]] [[non]] [[avere|hai]] [[altro|altri]] [[moccolo|moccoli]] [[potere|puoi]] [[andare a letto]] [[al buio]].
Here, we rely on the alternative notation mentioned above for e.g. 'altr[i:o]', which is equivalent to 'altri:altro',
and link multiword subterms using e.g. 'andare a letto:~'. (The code knows how to handle multiword subexpressions
properly, and if the link text and destination are the same, only a single-part link is formed.)
]==]
function export.apply_link_modifiers(linked_term, modifier_spec, lang)
local split_modspecs = split(modifier_spec, "%s*;%s*")
for j, modspec in ipairs(split_modspecs) do
local id
if modspec:find("<") then
local rest
rest, id = modspec:match("^(.*)<id:(.-)>$")
if rest then
modspec = rest
end
end
local subterm, dest, otherlang
local begin_spec, rest, end_spec = modspec:match("^%[(.-)%]([^:]*)%[(.-)%]$")
if begin_spec then
local begin_from, begin_to = canonicalize_begin_end_spec(begin_spec)
local end_from, end_to = canonicalize_begin_end_spec(end_spec)
subterm = begin_from .. rest .. end_from
dest = begin_to .. rest .. end_to
end
if not subterm then
rest, end_spec = modspec:match("^([^:]*)%[(.-)%]$")
if rest then
local end_from, end_to = canonicalize_begin_end_spec(end_spec)
subterm = rest .. end_from
dest = rest .. end_to
end
end
if not subterm then
begin_spec, rest = modspec:match("^%[(.-)%]([^:]*)$")
if begin_spec then
local begin_from, begin_to = canonicalize_begin_end_spec(begin_spec)
subterm = begin_from .. rest
dest = begin_to .. rest
end
end
if not subterm then
subterm, dest = modspec:match("^(.-)%s*:%s*(.*)$")
if subterm and subterm ~= "^" and subterm ~= "$" then
local langdest
-- Parse off an initial language code (e.g. 'en:Higgs', 'la:minūtia' or 'grc:σκατός'). Also handle
-- Wikipedia prefixes ('w:Abatemarco' or 'w:it:Colle Val d'Elsa').
otherlang, langdest = dest:match("^([A-Za-z0-9._-]+):([^ ].*)$")
if otherlang == "w" then
local foreign_wikipedia, foreign_term = langdest:match("^([A-Za-z0-9._-]+):([^ ].*)$")
if foreign_wikipedia then
otherlang = otherlang .. ":" .. foreign_wikipedia
langdest = foreign_term
end
dest = ("%s:%s"):format(otherlang, langdest)
otherlang = nil
elseif otherlang then
otherlang = get_lang_by_code(otherlang, true, "allow etym")
dest = langdest
end
end
end
if not subterm then
if modspec == "?" or modspec == "!" then
subterm = "$"
dest = modspec
elseif modspec == "..." or modspec == "...?" then
subterm = "$"
dest = " " .. modspec
elseif modspec:find("^[A-Z]$") then
-- X, Y, etc. by themselves are unlinked, to help with snowclones
subterm = modspec
dest = "_"
else
subterm = modspec
dest = "~"
end
end
if subterm == "^" then
linked_term = dest:gsub("_", " ") .. linked_term
elseif subterm == "$" then
linked_term = linked_term .. dest:gsub("_", " ")
else
if subterm:find("[", nil, true) then
error(("Subterm '%s' in modifier spec '%s' cannot have brackets in it"):format(
escape_wikicode(subterm), escape_wikicode(modspec)))
end
local escaped_subterm = pattern_escape(subterm)
local subterm_re = "%[%[" .. escaped_subterm:gsub("(%%?[ ',%-])", "%%]*%1%%[*") .. "%]%]"
local expanded_dest
if dest:find("~", nil, true) then
expanded_dest = dest:gsub("~", replacement_escape(subterm))
else
expanded_dest = dest
end
if otherlang then
expanded_dest = expanded_dest .. "#" .. otherlang:getCanonicalName()
end
local subterm_replacement
if expanded_dest == "_" then
subterm_replacement = subterm
if id then
error("Can't supply <id:...> with an unlinked subterm")
end
if otherlang then
error("Can't supply prefixed language with an unlinked subterm")
end
elseif id or otherlang then
if id and expanded_dest:find("[", nil, true) then
error("Can't supply <id:...> with destination with embedded brackets")
end
subterm_replacement = require(links_module).language_link {
lang = otherlang or lang,
term = expanded_dest,
alt = subterm,
id = id,
}
elseif expanded_dest:find("[", nil, true) then
-- Use the destination directly if it has brackets in it (e.g. to put brackets around parts of a word).
subterm_replacement = expanded_dest
elseif expanded_dest == subterm then
subterm_replacement = "[[" .. subterm .. "]]"
else
subterm_replacement = "[[" .. expanded_dest .. "|" .. subterm .. "]]"
end
local escaped_subterm_replacement = replacement_escape(subterm_replacement)
local replaced_linked_term = ugsub(linked_term, subterm_re, escaped_subterm_replacement)
if replaced_linked_term == linked_term then
mw.log(("Attempted to replace %s with %s in %s"):format(subterm_re, escaped_subterm_replacement, linked_term))
error(("Subterm '%s' could not be located in %slinked expression %s, or replacement same as subterm"):format(
subterm, j > 1 and "intermediate " or "", escape_wikicode(linked_term)))
else
linked_term = replaced_linked_term
end
end
end
return linked_term
end
function export.format_validation_error_list(data)
local valid_list, valid_set, canonicalize = data.valid_list, data.valid_set, data.canonicalize
if not valid_list then
valid_list = {}
for valid_item, value in pairs(valid_set) do
if canonicalize then
if valid_item == value then
insert(valid_list, valid_item)
else
insert(valid_list, ("%s (mapped to %s)"):format(valid_item, value))
end
else
insert(valid_list, valid_item)
end
end
table.sort(valid_list)
end
return mw.text.listToText(valid_list, nil, " hoặc ")
end
function export.validation_error(data)
local item_type, item, valid_list, valid_set, canonicalize =
data.item_type, data.item, data.valid_list, data.valid_set, data.canonicalize
if not valid_list then
valid_list = {}
for valid_item, value in pairs(valid_set) do
if canonicalize then
if valid_item == value then
insert(valid_list, valid_item)
else
insert(valid_list, ("%s (mapped to %s)"):format(valid_item, value))
end
else
insert(valid_list, valid_item)
end
end
table.sort(valid_list)
end
error(("Invalid %s: %s; expected one of %s"):format(item_type, item,
export.format_validation_error_list {
valid_list = valid_list,
valid_set = valid_set,
canonicalize = canonicalize,
}
))
end
--[=[
Validate the items in `items` against the list or set of valid items in `valid_items`. If `field` is given, fetch the
item to check from that-named field of each object in `items`; otherwise use the items in `items` directly. If an error
occurs, `item_type` specifies the type of item to mention in the error message, which will also list the allowed items
(either taken directly from `valid_items` if a list, or from the sorted keys if a set).
If `canonicalize`, `valid_items` must be a set mapping items to their canonical equivalents, and the items in `items`
will be canonicalized accordingly and returned.
]=]
local function validate_items(data, canonicalize)
local items, field, valid_items, item_type =
data.items, data.field, data.valid_items, data.item_type
local valid_list, valid_set
if not canonicalize and valid_items[1] and is_array(valid_items) then
valid_list = valid_items
valid_set = list_to_set(valid_items)
else
valid_set = valid_items
end
for _, item in ipairs(items) do
if field then
item = item[field]
end
if not item._is_mode and not valid_set[item] then
export.validation_error {
item_type = item_type,
item = item,
valid_list = valid_list,
valid_set = not valid_list and valid_set or nil,
canonicalize = canonicalize,
}
end
end
end
function export.validate_genders(data)
local genders, valid_genders, gender_type, no_augment, canonicalize =
data.genders, data.valid_genders, data.gender_type, data.no_augment, data.canonicalize
if not genders then
return genders
end
gender_type = gender_type or "headword"
local valid_gender_set
if not data.canonicalize and valid_genders[1] and is_array(valid_genders) then
valid_gender_set = list_to_set(valid_genders)
else
valid_gender_set = valid_genders
end
local augmented_gender_set
if no_augment then
augmented_gender_set = valid_gender_set
else
augmented_gender_set = {}
for g, gval in pairs(valid_gender_set) do
augmented_gender_set[g] = gval
if g:find("^m") and not g:find("^mf") and valid_gender_set[g:gsub("^m", "f")] then
augmented_gender_set[g:gsub("^m", "mf")] = type(gval) == "string" and (gval:gsub("^m", "mf")) or gval
augmented_gender_set[g:gsub("^m", "mfbysense")] = type(gval) == "string" and (gval:gsub("^m", "mfbysense")) or gval
augmented_gender_set[g:gsub("^m", "mfequiv")] = type(gval) == "string" and (gval:gsub("^m", "mfequiv")) or gval
end
end
end
return validate_items {
items = genders,
field = "spec",
valid_items = augmented_gender_set,
item_type = ("%s gender"):format(gender_type),
canonicalize = canonicalize,
}
end
function export.has_plural_gender(genders)
local saw_p, saw_non_p
for _, val in ipairs(genders) do
local g = val.spec
if g:find("p$") then
saw_p = true
elseif g ~= "?" then
saw_non_p = true
end
end
return saw_p, saw_non_p
end
function export.is_plurale_tantum(genders)
local saw_p, saw_non_p = export.has_plural_gender(genders)
return saw_p and not saw_non_p
end
local Headdata = {}
function Headdata:resolve_prop(prop, ...)
if type(prop) == "function" then
prop = prop(self, ...)
end
return prop
end
function Headdata:get_pos_variants()
if self.process_props.pos_variants == nil then
self.process_props.pos_variants = export.get_pos_variants(self.pos_category)
end
return self.process_props.pos_variants
end
--[==[
Canonicalize a category. The category string will have the full language name (i.e. the name of the L2 language under
which an entry is inserted, which may a parent language if the language in question is an etymology-only language)
prepended to it, and any occurrences of `{plpos}` in the string replaced with the actual plural part of speech (with
some canonicalization; specifically, `proper nouns` is converted to `nouns` when replacing `{plpos}`). To specify a
full category and not have the language name prepended to it, precede it with {"Category:"}, which will be removed.
]==]
function Headdata:canonicalize_category(category)
if category:find("{") then
category = require(string_utilities_module).format(category, self:get_pos_variants())
end
if category:find("^Thể loại:") then
return (category:gsub("^Thể loại:", ""))
else
return category.. " " .. self.langfullname
end
end
--[==[
Canonicalize a list of categories according to the process described in `Headdata:canonicalize_category`. This simply
loops over each category in `categories` and calls `Headdata:canonicalize_category` on each one.
]==]
function Headdata:canonicalize_categories(categories)
if not categories then
return categories
end
categories = export.canonicalize_category_list(categories)
local canon_cats = {}
for _, cat in ipairs(categories) do
insert(canon_cats, self:canonicalize_category(cat))
end
return canon_cats
end
--[==[
Insert a category into the `categories` list in the headword `data` structure. `category` is normally a string naming
the category, which will have the full language name prepended to it and any occurrences of `{plpos}` in the string
replaced with the actual plural part of speech (with some canonicalization; specifically, `proper nouns` is converted to
`nouns` when replacing `{plpos}`). To specify a full category and not have the language name prepended to it, precede it
with {"Category:"}.
]==]
function Headdata:insert_category(category)
insert(self.categories, self:canonicalize_category(category))
end
--[==[
Validate the genders in `genders` (a list of gender spec objects, as produced by {type = "genders"} in
[[Module:parameters]] and accepted by [[Module:gender and number]]), checking that all specified genders are in the list
or set given in `valid_genders`. Optional `props` controls how the validation happens. In particular, unless
`props.no_augment` is given, then for any gender beginning with `m`, if a corresponding gender beginning with `f`
occurs, analogous genders beginning with `mf`, `mfbysense` and `mfequiv` are also allowed. For example, if `m-d`
(masculine dual) and `f-d` (feminine dual) both occur, genders `mf-d`, `mfbysense-d` and `mfequiv-d` are also allowed.
If a disallowed gender is given, an error occurs, giving the disallowed gender along with the list of all allowed
genders.
]==]
function Headdata:validate_genders(genders, valid_genders, props)
-- FIXME: This has no dependency on self. Uses should be converted to call export.validate_genders() directly.
if not genders then
return
end
props = props or {}
local gender_type, no_augment = props.gender_type, props.no_augment
export.validate_genders {
genders = genders,
valid_genders = valid_genders,
gender_type = gender_type,
no_augment = no_augment,
}
end
--[==[
Parse an inflection specified in `field`, the name of a parameter holding an inflection. If the parameter is numeric,
the field should be given as a number (as with the `params` structure passed to [[Module:parameters]]), not a string
containing the representation of a number. The field can specify multiple comma-separated terms, and each term can have
associated inline modifiers that will be parsed (unless there is top-level HTML in the parameter, i.e. HTML not
contained inside an inline modifier, e.g. as may be generated by using {{tl|l}} or similar template inside a parameter).
This is a wrapper around the top-level `parse_term_with_modifiers()` function. `props` is an optional structure
containing additional properties, including all additional properties documented for the top-level
`parse_term_with_modifiers()` function.
If the parameter in `field` is unspecified, the return value of this function will be an empty list, not {nil}, so it
is always safe to iterate over the return value.
By default, the allowed modifiers are the same as for `parse_term_with_modifiers()`, except that (normally) the
`<tr:...>` modifier will be allowed if `include_tr` was specified in the original call to `process_headword()`; likewise
for the `<ts:...>` modifier if `include_ts` was specified and the `<sc:...>` modifier if `include_sc` was specified. If
If you pass in your own `include_mods` list of additional allowed modifiers, it will (normally) automatically be
augmented with {"tr"}, {"ts"} and/or {"sc"} if `include_tr`, `include_ts` and/or `include_sc` was specified when calling
`process_headword()`. To disable automatic augmentation of these modifiers (whether or not you specify an `include_mods`
property), specify {no_augment_include_mods = true} in `props`.
]==]
function Headdata:parse_inflection(field, props)
local val = self.process_props.args[field]
if not val then
return {}
end
props = props and shallow_copy(props) or {}
local include_mods = props.include_mods
local calldata = self.process_props.calldata
if not props.no_augment_include_mods and (calldata.include_tr or calldata.include_ts or calldata.include_sc) then
include_mods = include_mods and shallow_copy(include_mods) or {}
if calldata.include_tr then
insert_if_not(include_mods, "tr")
end
if calldata.include_ts then
insert_if_not(include_mods, "ts")
end
if calldata.include_sc then
insert_if_not(include_mods, "sc")
end
end
props.val = val
props.paramname = field
props.splitchar = props.splitchar or ","
props.include_mods = include_mods
local orig_frob = props.frob
local function convert_tilde_to_pagename(term)
if orig_frob then
term = orig_frob(term)
end
if not term:find("~", nil, true) then
return term
end
term = term:gsub("\\~", "\1"):gsub("~", replacement_escape(self.pagename)):gsub("\1", "~")
return term
end
props.frob = convert_tilde_to_pagename
return export.parse_term_with_modifiers(props) or {}
end
function Headdata:process_for_insertion(terms, props)
props = props and shallow_copy(props) or {}
props.headdata = self
props.terms = terms
return export.process_for_insertion(props)
end
--[==[
Insert previously-parsed terms into the `inflections` of the headword `data` structure. This is a wrapper around
the top-level `insert_inflection()` function. `terms` is the list of parsed terms. (If {nil}, nothing happens unless
`request` is set in `props`.) `label` is the the label that the inflections are given; any parts of the label surrounded
in `<<...>>` are linked to the glossary. (If the contents of `<<...>>` contain a `|` in them, they are a two-part link.)
`props` is an optional structure containing additional properties, including all additional properties documented for
the top-level `insert_inflection()` function.
Unless `no_auto_cats` is given in `props`, certain labels automatically trigger the insertion of additional
categories in specific circumstances. This is controlled by the `inflection_to_cats_and_label` structure in
[[Module:headword utilities]]. For example, if the part of speech is {"nouns"} or {"proper nouns"} and the label (after
removing any links and `<<...>>` glossary specs) is {"plural"}, an additional category
<code><var>lang</var> countable nouns</code> will be added if a plural value is given (i.e. the value is not {"-"}). If
the value is {"-"} (which indicates that there is no plural and triggers the insertion of the fixed inflection label
{"no plural"}), <code><var>lang</var> uncountable nouns</code> will be inserted instead, and if both {"-"} and a value
are given (which triggers the insertion of the {"usually no plural"} fixed inflection label), both categories are added.
Similar categories are inserted when a comparative is given (with a label {"comparative"}), and if the label is
{"female equivalent"} or {"male equivalent"} and the value is not {"-"}, a category such as
<code><var>lang</var> nouns with other-gender equivalents</code> is inserted.
]==]
function Headdata:insert_processed_inflection(processed, label, props)
props = props and shallow_copy(props) or {}
if not props.no_auto_cats then
local bare_label = props.label_for_cats_and_modes or label
if bare_label:find("[[", nil, true) then
bare_label = require(links_module).remove_links(bare_label)
end
if bare_label:find("<<", nil, true) then
bare_label = bare_label:gsub("<<.-|(.-)>>", "%1"):gsub("<<(.-)>>", "%1")
end
local spec = inflection_to_cats_and_label[bare_label]
if spec and (not spec.filter_pos or spec.filter_pos(self:get_pos_variants())) then
-- Copy the properties other than filter_pos into `props`, but don't overwrite any existing values.
for specprop, specval in pairs(spec) do
if specprop ~= "filter_pos" and props[specprop] == nil then
props[specprop] = specval
end
end
end
end
-- Now resolve and canonicalize the properties.
local canon_props = {}
for prop, val in pairs(props) do
if prop ~= "no_auto_cats" and (prop == "cats" or prop:find("_cats$")) then
val = self:canonicalize_categories(self:resolve_prop(val))
elseif prop:find("_label$") then
val = self:resolve_prop(val)
end
canon_props[prop] = val
end
canon_props.headdata = self
canon_props.processed = processed
canon_props.label = label
export.insert_processed_inflection(canon_props)
return processed
end
--[==[
Insert previously-parsed terms into the `inflections` of the headword `data` structure. This is a wrapper around
the top-level `insert_inflection()` function. `terms` is the list of parsed terms. (If {nil}, nothing happens unless
`request` is set in `props`.) `label` is the the label that the inflections are given; any parts of the label surrounded
in `<<...>>` are linked to the glossary. (If the contents of `<<...>>` contain a `|` in them, they are a two-part link.)
`props` is an optional structure containing additional properties, including all additional properties documented for
the top-level `insert_inflection()` function.
Unless `no_auto_cats` is given in `props`, certain labels automatically trigger the insertion of additional
categories in specific circumstances. This is controlled by the `inflection_to_cats_and_label` structure in
[[Module:headword utilities]]. For example, if the part of speech is {"nouns"} or {"proper nouns"} and the label (after
removing any links and `<<...>>` glossary specs) is {"plural"}, an additional category
<code><var>lang</var> countable nouns</code> will be added if a plural value is given (i.e. the value is not {"-"}). If
the value is {"-"} (which indicates that there is no plural and triggers the insertion of the fixed inflection label
{"no plural"}), <code><var>lang</var> uncountable nouns</code> will be inserted instead, and if both {"-"} and a value
are given (which triggers the insertion of the {"usually no plural"} fixed inflection label), both categories are added.
Similar categories are inserted when a comparative is given (with a label {"comparative"}), and if the label is
{"female equivalent"} or {"male equivalent"} and the value is not {"-"}, a category such as
<code><var>lang</var> nouns with other-gender equivalents</code> is inserted.
]==]
function Headdata:insert_inflection(terms, label, props)
local processed = self:process_for_insertion(terms, props)
self:insert_processed_inflection(processed, label, props)
return processed
end
--[==[
Insert a "fixed" inflection (a label without associated values) into the `inflections` table of the headword `data`
structure, labeled according to `label` (which can have glossary links in it specified using `<<...>>`, exactly as for
`:insert_inflection()`). An example label (from {{tl|mn-noun}} in [[Module:mn-headword]]) is {"hidden-g declension"},
specifying that the noun belongs to the hidden-''g'' declension. This is a direct wrapper around the top-level function
`insert_fixed_inflection()`; see that function for more details on optional `props`.
]==]
function Headdata:insert_fixed_inflection(label, props)
props = props and shallow_copy(props) or {}
props.headdata = self
props.label = label
export.insert_fixed_inflection(props)
end
--[==[
Parse the inflection(s) specified in `field` and insert them into the `inflections` table of the headword `data`
structure, labeled according to `label`. This is equivalent to calling {terms = data:parse_inflection(field, props)}
followed by {return data:insert_inflection(terms, label, props)} and behaves the same as the combination of those two
functions. See their documentation for more details.
]==]
function Headdata:parse_and_insert_inflection(field, label, props)
local terms = self:parse_inflection(field, props)
return self:insert_inflection(terms, label, props)
end
--[==[
Generate an inflection that may be specified explicitly or defaulted (which involves looping over the specified or
defaulted heads and determining the script of each one, since the formation of the default depends on the script).
`data` is the data object passed into the POS handler. `terms` is the list of terms to process. Those where the term
itself is not `+` will be returned unchanged, while those where the term is `+` will be handled by generating the
appropriate inflections from the headwords using `make_inflection` (which is passed three arguments, `head`, `tr` and
`sccode`, i.e. the script code of `head`) and should return two values, term and translit, either of which can be
nil. A nil head will be ignored, and otherwise the decorations specified on the `+` term will be combined with the
decorations specified on the head. The return value is a list of inflections where no requests for the default
inflection remain.
]==]
function Headdata:resolve_special(terms, handle_special, props)
props = props or {}
local infls = {}
local is_special = props.is_special or function(_data, infl) return infl.term == "+" end
for _, termobj in ipairs(terms) do
local is_special_retval
if type(is_special) == "function" then
is_special_retval = is_special(self, termobj)
elseif type(is_special) == "table" then
is_special_retval = contains(is_special, termobj.term)
else
error(("Internal error: When checking whether term is special, is_special is neither a function nor a " ..
"list, but is %s; term is %s"):format(dump(is_special), dump(termobj)))
end
if not is_special_retval then
insert(infls, termobj)
else
for _, headobj in ipairs(self.heads) do
local head = headobj.term or self.pagename
local head_no_links
if props.with_links then
head = head:find("%[") and head or require(headword_module).add_multiword_links(head, not headobj.term)
head_no_links = require(links_module).remove_links(head)
else
head = require(links_module).remove_links(head)
head_no_links = head
end
local newterms = handle_special {
headdata = self,
head = head,
head_no_links = head_no_links,
tr = headobj.tr,
infl = termobj,
sc = self.lang:findBestScript(head_no_links),
}
if newterms then
newterms = export.canonicalize_termobj_list(newterms, "term", "resolve_special", termobj)
for _, newterm in ipairs(newterms) do
if not props.no_combine_handle_special_retval_with_origin then
export.combine_termobj_decorations(newterm, termobj)
end
if not props.no_combine_handle_special_retval_with_head then
export.combine_termobj_decorations(newterm, headobj)
end
insert(infls, newterm)
end
end
end
end
end
return infls
end
--[==[
Add the current page to a tracking page named `Wiktionary:Tracking/``lang``-headword/``page```, where ``lang`` is the
language code of the current language. For example, if the current language is `mak` and `page` is {"redundant-lon"},
the current page will get added to the tracking page `Wiktionary:Tracking/mak-headword/redundant-lon`. All pages added
to that tracking page can be seen by going to [[Special:WhatLinksHere/Wiktionary:Tracking/mak-headword/redundant-lon]].
This is typically used to track issues occurring in user-specified parameters that do not rise to the level of errors
(e.g. redundant parameters, deprecated usages or other dispreferred values).
]==]
function Headdata:track(page)
return require(debug_track_module)(self.langcode .. "-headword/" .. page)
end
local boolean_param = {type = "boolean"}
--[==[
Process an arbitrary headword in an arbitrary language, handling generic and language-specific arguments and calling
`full_headword()` in [[Module:headword]]. This is intended for use in implementing headword modules (e.g.
[[Module:uz-headword]] for Uzbek, [[Module:mn-headword]] for Uzbek, [[Module:gsw-headword]] for Alemannic German, etc.)
and provides a general implementation of such modules. On input, `data` is an object with the following fields:
* `lang`: The language object of the language being handled. '''Required.''' Use the special value {false} to indicate
that the language is specified by the user in {{para|1}}.
* `frame`: The frame object passed into the `show()` function of your module, which implements headword-handling for
all parts of speech in the module, including a generic POS-handling template (e.g. {{tl|uz-head}} or {{tl|mn-head}}),
which allows arbitrary parts of speech to be handled. '''Required.'''
* `pos_functions`: A table listing, for each part of speech requiring special handling, the extra parameters (if any)
that the part of speech accepts, along with how to handle them. See the examples below. '''Required.'''
* `validate_lang`: If {lang = true} is specified, this is a function of one argument (a language object, based on the
language specified in {{para|1}}) that should throw an error if the language object is disallowed. If omitted, all
languages are allowed.
* `numbered_head`: If true, explicit headwords are specified in a numbered param instead of in {{para|head}}. The param
used is usually {{para|1}}, but is {{para|2}} for generic POS templates such as {{tl|mn-head}} or for POS-specific
templates when {lang = true} is specified (e.g. {{tl|arb-noun}}), and is {{para|3}} for generic POS templates when
{lang = true} is spcified (e.g. {{tl|arb-head}}).
* `include_tr`: If true, allow explicit transliterations to be specified. The transliteration(s) for the headword(s)
themselves is/are specified in {{para|tr}} or through the {{cd|<tr:...>}} inline modifier on headwords, and
transliterations of inflections are specified through the {{cd|<tr:...>}} inline modifier. This should generally be
given when a headword for the language may be in a script other than Latin.
* `include_ts`: If true, allow explicit transcriptions to be specified. The transcription(s) for the headword(s)
themselves is/are specified in {{para|ts}} or through the {{cd|<ts:...>}} inline modifier on headwords, and
transcriptions of inflections are specified through the {{cd|<ts:...>}} inline modifier. This should generally only
be given for certain languages where the spelling is radically different from the pronunciation (e.g. in cuneiform
languages such as Hittite and Akkadian, and potentially in Tibetan), and represents a pronunciation-based rendering
(usually not direct IPA).
* `include_sc`: If true, allow an explicit script code to be specified. The overall script code for the headword(s)
themselves can be specified using {{para|sc}}, and per-headword or per-inflection script codes are specified using the
{{cd|<sc:...>}} inline modifier. This should generally be given when a language supports multiple scripts.
* `infls`: An inflections structure specifying extra generic parameters that apply to all parts of speech and how to
handle them. The format is the same as for the `infls` structure in `pos_functions`.
* `augment_params`: A callback function to add extra generic parameters, or modify existing generic parameters in the
`params` structure; but in general, extra generic parameters should be added through the `infls` structure instead.
This callback should not be used to add part-of-speech-specific parameters; those are handled through the appropriate
setting in `pos_functions`. It is passed two single arguments, the `headdata` object and the `params` table to be
augmented. See below for extra fields stored in the `process_props` structure of the `headdata` object. This function
is called after initializing the `params` table and processing the overall `infls` structure, but just before adding
part-of-speech-specific parameters (from `pos_functions`) to `params`. Thus, it can override any generic parameters
but may itself be overridden by a part-of-speech-specific parameter.
* `augment_headdata`: A callback function to modify the `headdata` object passed to `full_headword()` in
[[Module:headword]]. This should not be used to for part-of-speech-specific parameter handling; this is handled
through the appropriate setting in `pos_functions`. It is passed a single argument, the `headdata` object, as for
`augment_params`; but the `process_props` structure and other fields will be more filled out, as this callback is
called later. This can be used, for example, to override the value of a generic setting in `headdata` (e.g.
[[Module:uz-headword]] uses this to mark non-Latin terms as variant forms by setting `headdata.var`, being careful not
to override a value already set by the user). This function is called after initializing the `headdata` table with all
information taken from generic parameters and processing generic parameters specified in the overall `infls`
structure, and just before processing the appropriate part-of-speech-specific `infls` structure in `pos_functions`
(which in turn is followed by any handler function in `pos_functions`). Thus, it can override any value set during
generic parameter processing but may itself be overridden by a part-of-speech-specific parameter or handler.
* `force_cat`: If true, add the headword to the appropriate categories even on non-mainspace pages. This can be used for
testing category handling in sample template calls on userspace test pages or template documentation pages. It should
not be set in production code.
* `enable_auto_translit`: If true, turn on automatic transliteration of inflections at a global level (i.e. applying to
all inflections). This has no effect on headwords, which are automatically transliterated by default if in a non-Latin
script and automatic transliteration is available for the language. You can also set this value for particular
inflections in the `insert_inflection()` function.
The `headdata` headword data structure has an extra field in it called `process_props` that is specific to the
`process_headword()` function, containing various extra properies. As the operation of `process_headword()` proceeds,
this object gets filled out with more fields. For example, once parameter parsing happens, the resulting values are
available in the `args` field of `process_props`. The following fields are found in `process_props` (note that
`pos_category`, the canonicalized plural part of speech of the headword being processed, is *not* present here; it's
directly on `headdata`):
* `namespace`: The name of the current namespace; an empty string for the mainspace. This references the namespace of
the actual page and isn't affected by the {{para|pagename}} parameter.
* `indexing_pos`: The canonicalized part of speech of the headword used to index into `pos_functions`. This is the
same as `poscat` for specific part-of-speech templates such as {{tl|uz-noun}}, but has the value {"head"} for generic
part-of-speech templates such as {{tl|uz-head}}. (Note that `poscat` is directly available on `headdata`.)
* `generic_pos_template`: True if a generic POS templates like {{tl|uz-head}} or {{tl|mn-head}} was used. (This is
signaled by omitting the invocation parameter {{para|1}} to `process_headword`.)
* `lang_in_1`: True if the language code is to be fetched from {{para|1}}.
* `pos_param`: The parameter holding the part of speech, if a generic POS tempalte like {{tl|mn-head}} is being
processed (i.e. `generic_pos_template` is set). In such a case, it will have the value of {1} or {2}, depending on
whether the language code is being fetched from {{para|1}} (see `lang_in_1`). Otherwise it will be {nil}.
* `head_param`: The parameter holding the explicit headword. If `numbered_head` was specified (as for Mongolian headword
templates), this has the value {1}, {2} or {3} depending on whether the language code is being fetched from {{para|1}}
(see `lang_in_1`) and whether a generic POS template like {{tl|mn-head}} is being processed (see
`generic_pos_template`). Otherwise, it has the value {"head"}. Also see the `lang` and `numbered_head` properties in
the `data` structure sent to `process_headword()`.
* `is_suffix`: True if the current term is a suffix. This is set when processing the `suffix`, `nosuffix` and `clitic`
parameters; it is always {false} beforehand (i.e. during `augment_params` and processing of the general `infls`
structure).
* `insert_specs`: This is a table mapping parameter names to the return value of `Headdata:insert_inflection()`, filled
out as parameter values are processed. This lets a given parameter processing function in `infls` gain access to the
result of calling `insert_inflection()` on previous parameters (which indicates the number of items inserted as well
as whether `-` was specified).
The `pos_functions` table contains an entry for each part of speech needing special handling, where the key is the
canonical plural part of speech (e.g. {"adverbs"} or {"proper nouns"}). The value associated with each key is a table
normally containing a field `infls`, listing the extra part-of-speech-specific inflection and other parameters along
with how to handle them. The specs in `infls` are used in three ways:
# to augment the `params` object passed to the `process()` function in [[Module:parameters]], specifying how to parse
the appropriate inflectional parameters;
# to specify how to process any inflectional parameters given and insert them into the `headdata` object passed to
`full_headword()` in [[Module:headword]];
# to generate appropriate documentation for the parameters and other changes made by the headword template (e.g.
inserting categories).
Alternatively, you can separately control the augmentation of the `params` object and the procesing of the resulting
arguments. This is done by specifying two fields in place of `infls`, named `params` and `func`. `params` is a table
containing extra parameters to add to the overall `params` object passed to the `process()` function in
[[Module:parameters]]. `func` is a function of two arguments, normally called `data` (the headword data structure
`headdata`) and `args` (the processed arguments table). However, this alternative method is not normally recommended
because it leads to duplication between the `params and `func` fields and the documentation, which must be manually
specified.
A simple example, as used to handle pronouns for Turoyo, is
{
local valid_genders = {"m", "f", "m-p", "f-p", "p", "?"}
pos_functions["pronouns"] = {
infls = {
{2, type = "genders", validate = valid_genders},
{"f", label = "feminine"},
{"pl", label = "plural"},
},
}
}
The equivalent using `params` and `func` is
{
local valid_genders = {"m", "f", "m-p", "f-p", "p", "?"}
pos_functions["pronouns"] = {
params = {
[2] = {type = "genders"},
f = true,
pl = true,
},
func = function(data, args)
data:validate_genders(args[2], valid_genders)
data.genders = args[2]
data:parse_and_insert_inflection("f", "feminine")
data:parse_and_insert_inflection("pl", "plural")
end
}
}
Note how the version with separate `params and `func` is longer and splits information on the parameters between the
two fields. The `params` structure sets extra user-specifiable parameters {{para|2}} for genders (since the headword is
in {{para|1}}) as well a {{para|f}} and {{para|pl}}, and the `func` handler processes those parameters. Note how this is
done by calling methods on the headword `data` structure. Each such parameter can have multiple comma-separated values,
and each value can have inline modifiers attached to it to specify further properties of the value. The `infls` version
ends up making the same method calls, but does it for you instead of you having to do it yourself.
These methods are implemented through a metatable set on the headword `data` structure, which is removed before calling
`full_headword()` in [[Module:headword]]. The methods access extra information related to headword processing (such as
the `args` table) that is stored in the `process_props` field of the headword `data` strucuture. This field is also
removed prior to calling `full_headword()`.
The methods available on the headword `data` structure are as follows. Each one also has its own documentation.
* {parse_inflection(field, props)}: Parse value(s) specified in `field` (a user-specified parameter in the `args` table)
and return a list of term objects. Optional `props` specifies additional properties controlling the parsing.
* {insert_inflection(terms, label, props)}: Insert the terms in `terms` (a list of term objects as returned by
`parse_inflection()`) into the `inflections` list in the headword `data` structure, giving the inflection the label as
specified in `label`. Optional `props` specifies additional properties controlling the parsing.
* {parse_and_insert_inflection(field, label, props)}: A combination of `parse_inflection()` and `insert_inflection()`,
if no further processing of the parsed values needs to be done before insertion.
* {insert_fixed_inflection(label, props)}: Insert a "fixed" inflection (a label without associated values) into the
`inflections` table. An example (from {{tl|mn-noun}} in [[Module:mn-headword]]) is {"hidden-g declension"}, specifying
that the noun belongs to the hidden-''g'' declension.
* {resolve_special(terms, handle_special, props)}: Resolve "special" indicators as specified by the user in an inflection
parameter. A typical example is {"+"}, requesting a default value. `terms` is the list of parsed term objects and
`handle_special` is a handler function to process special indicators and convert them to their actual values.
* {validate_genders(genders, valid_genders, props)}: Validate that the user-specified genders in `genders` all belong to
the list given in `valid_genders`, throwing an error if not.
* {insert_category(category)}: Insert a category into the `categories` list in the headword `data` structure. `category`
is normally a string naming the category, which will have the language prepended to it and any occurrences of
`{plpos}` in the string replaced with the actual plural part of speech.
]==]
function export.process_headword(data)
local data_lang, frame, pos_functions, validate_lang, numbered_head, include_tr, include_ts, include_sc, force_cat,
enable_auto_translit, checkredlinks, infls, augment_params, augment_headdata =
data.lang, data.frame, data.pos_functions, data.validate_lang, data.numbered_head, data.include_tr,
data.include_ts, data.include_sc, data.force_cat, data.enable_auto_translit, data.checkredlinks, data.infls,
data.augment_params, data.augment_headdata
local iparams = {
[1] = true,
def = true,
}
if data_lang == "invocation" or data_lang == "invocation-or-user" then
iparams.lang = true
end
local iargs = require(parameters_module).process(frame.args, iparams)
local parargs = frame:getParent().args
local lang
local function generate_data_lang_error()
error("Internal error: `data.lang` must be either a language object, \"invocation\" for a " ..
"language specified in lang= in the invocation arguments, \"user\" for a language specified in 1= in " ..
"the user template call, or \"invocation-or-user\" for either; saw " .. dump(data.lang))
end
if not data_lang then
generate_data_lang_error()
end
local lang_in_1
if type(data_lang) == "string" then
if data_lang == "invocation" then
lang = iargs.lang
if not lang then
error("Internal error: lang= must be specified in the invocation arguments")
end
elseif data_lang == "invocation-or-user" or data_lang == "user" then
if data_lang == "invocation-or-user" then
lang = iargs.lang
end
if not lang then
lang = ine(parargs[1])
if not lang then
error("Language code (see [[WT:Language codes]]) must be specified in 1=")
end
lang_in_1 = true
end
else
generate_data_lang_error()
end
lang = require(languages_module).getByCode(lang, lang_in_1 and 1 or "lang", true)
elseif type(data_lang) ~= "table" or not data_lang.hasType or not data_lang:hasType("language") then
generate_data_lang_error()
else
lang = data_lang
end
local langcode = lang:getCode()
if validate_lang then
if type(validate_lang) == "function" then
validate_lang(langcode)
else
validate_items {
items = {langcode},
valid_items = validate_lang,
item_type = "language code"
}
end
end
local template_indicated_pos = iargs[1]
local generic_pos_template = not template_indicated_pos
local pos_param
if generic_pos_template then
pos_param = lang_in_1 and 2 or 1
template_indicated_pos = ine(parargs[pos_param]) or
mw.title.getCurrentTitle().fullText == ("Template:%s-head"):format(langcode) and "interjection" or
error(("Part of speech must be specified in %s="):format(pos_param))
template_indicated_pos = canonicalize_pos(template_indicated_pos)
end
local head_param = numbered_head and (generic_pos_template and lang_in_1 and 3 or
(generic_pos_template or lang_in_1) and 2 or 1) or "head"
local indexing_pos
if generic_pos_template then
indexing_pos = "head." .. template_indicated_pos
if not pos_functions[indexing_pos] then
indexing_pos = "head"
end
else
indexing_pos = template_indicated_pos
end
local namespace = mw.loadData(headword_data_module).page.namespace
-- Partly initialize headdata now for use in generic infls callbacks. Will be further initialized later after
-- processing parameters.
local headdata = {
lang = lang,
langcode = langcode,
langfullcode = lang:getFullCode(),
langname = lang:getCanonicalName(),
langfullname = lang:getFullName(),
process_props = {
namespace = namespace,
calldata = data,
-- preserve template-indicated POS in case we normalize it or change it to "suffix"
template_indicated_pos = template_indicated_pos,
indexing_pos = indexing_pos,
generic_pos_template = generic_pos_template,
lang_in_1 = lang_in_1,
pos_param = pos_param,
head_param = head_param,
is_suffix = false,
processed_specs = {},
insert_specs = {},
},
-- We may change this below to "suffix" or normalize it.
pos_category = template_indicated_pos,
categories = {},
inflections = {enable_auto_translit = enable_auto_translit},
force_cat_output = force_cat,
no_redundant_head_cat = true,
}
setmetatable(headdata, {__index = Headdata})
local function resolve_prop(...)
return headdata:resolve_prop(...)
end
local params = {
[head_param] = {template_default = iargs.def},
head2 = {replaced_by = false, instead = ("use comma-separated |%s="):format(head_param)},
id = true,
sort = true,
cat = true,
nolink = boolean_param,
nolinkhead = {type = "boolean", alias_of = "nolink"},
suffix = boolean_param,
nosuffix = boolean_param,
-- If true, this is an inflectional suffix (forming a given person/number/gender/etc. part of the inflection of
-- a given part of speech, potentially restricted to a given inflectional class). We always treat these as
-- suffixes, rather than suffix forms, even if they form non-lemma forms (e.g. modern English -s and archaic
-- -eth form the third-singular present indicative of a verb, i.e. they form a verb form, but they are not
-- themselves forms of some other suffix, which is what a suffix form is). We also display the label of
-- inflectional suffixes specially, and add an additional category 'LANG inflectional suffixes' along with
-- categories like 'LANG POS inflectional suffixes' in place of 'LANG POS suffix forms' etc.
inflsuf = boolean_param,
clitic = {type = "boolean or string"},
addlpos = true,
var = {type = "boolean or string", set = {"both"}},
abbr = true,
json = boolean_param,
pagename = true, -- for testing
}
if include_sc then
params.sc = {type = "script"}
end
if include_tr then
params.tr = true
params.tr2 = {replaced_by = false, instead = "use comma-separated |tr= or <tr:...> inline modifier on head"}
end
if include_ts then
params.ts = true
params.ts2 = {replaced_by = false, instead = "use comma-separated |ts= or <ts:...> inline modifier on head"}
end
if lang_in_1 then
params[1] = {required = true} -- required but ignored as already processed above
end
if generic_pos_template then
params[pos_param] = {required = true} -- required but ignored as already processed above
end
local function augment_params_from_infls(infls)
infls = resolve_prop(infls)
for _, infl in ipairs(infls) do
local function interr(txt)
error(("Internal error: %s (coming from infls spec %s)"):format(txt, dump(infl)))
end
local param = infl[1]
if param then
param = resolve_prop(param)
if type(param) ~= "string" and type(param) ~= "number" then
interr(("Parameter name %s must be a string or number"):format(dump(param)))
end
-- We handle defaults as well as validation ourselves.
local typ = resolve_prop(infl.type) or "string"
if typ ~= "genders" and typ ~= "boolean" and typ ~= "string" then
-- FIXME: Handle more types.
interr(('Unrecognized type %s; can only currently handle "genders", "boolean" and "string" (the default)'):format(
dump(typ)))
end
params[param] = {type = typ, required = resolve_prop(infl.required), template_default = resolve_prop(infl.template_default)}
if typ ~= "boolean" then
local allow_and_ignore_list_params = resolve_prop(infl.allow_and_ignore_list_params)
local ignore_param
if type(allow_and_ignore_list_params) == "string" then
ignore_param = allow_and_ignore_list_params
elseif type(param) == "string" then
ignore_param = param
end
if ignore_param then
local old_list_param = allow_and_ignore_list_params and true or
{replaced_by = false, instead = ("use comma-separated |%s="):format(param)}
params[ignore_param .. "2"] = old_list_param
params[ignore_param .. "3"] = old_list_param
params[ignore_param .. "4"] = old_list_param
end
end
end
end
end
if infls then
augment_params_from_infls(infls)
end
if augment_params then
augment_params(headdata, params)
end
if pos_functions[indexing_pos] then
local pos_infls = pos_functions[indexing_pos].infls
if pos_infls then
augment_params_from_infls(pos_infls)
end
local pos_params = pos_functions[indexing_pos].params
if pos_params then
for key, val in pairs(pos_params) do
params[key] = val
end
end
end
local args = require("Module:parameters").process(parargs, params)
local pagename = args.pagename or mw.loadData(headword_data_module).pagename
local sc = args.sc or lang:findBestScript(pagename)
headdata.pagename = pagename
headdata.process_props.args = args
headdata.sc = sc
headdata.id = args.id
headdata.sort = args.sort
-- No redundant script cat unless the user explicitly gave sc=
headdata.no_script_code_cat = not args.sc
headdata.var = args.var
local extra_term_mods = {}
if include_tr then
insert(extra_term_mods, "tr")
end
if include_ts then
insert(extra_term_mods, "ts")
end
if include_sc then
insert(extra_term_mods, "sc")
end
if not extra_term_mods[1] then
extra_term_mods = nil
end
local trs = args.tr and split_on_comma(args.tr) or {}
local num_trs = #trs
local tss = args.ts and split_on_comma(args.ts) or {}
local num_tss = #tss
local heads = args[head_param] and export.parse_term_with_modifiers {
val = args[head_param],
paramname = head_param,
splitchar = ",",
is_head = true,
include_mods = extra_term_mods,
} or {}
local num_heads = #heads
if num_heads > 0 and num_trs > 0 and num_heads ~= num_trs then
error(("%s head%s specified explicitly but %s translit%s; they must match; use '+' to stand for the default head (the pagename) or default automatic translit and '-' to stand for no translit"):format(
num_heads, num_heads > 1 and "s" or "", num_trs, num_trs > 1 and "s" or ""))
end
if num_heads > 0 and num_tss > 0 and num_heads ~= num_tss then
error(("%s head%s specified explicitly but %s transcription%s; they must match; use '+' to stand for the default head (the pagename) and '-' to stand for no transcription"):format(
num_heads, num_heads > 1 and "s" or "", num_tss, num_tss > 1 and "s" or ""))
end
if num_trs > 0 and num_tss > 0 and num_trs ~= num_tss then
error(("%s translit%s specified explicitly but %s transcription%s; they must match; use '+' to stand for default automatic translit and '-' to stand for no translit or transcription"):format(
num_trs, num_trs > 1 and "s" or "", num_tss, num_tss > 1 and "s" or ""))
end
-- Be careful here not to overwrite user_specified_heads if it's empty so we can later check user_specified_heads
-- to see if the user provided any heads.
local max_tr_ts = math.max(num_trs, num_tss)
if num_heads == 0 and max_tr_ts > 0 then
heads = {}
for i = 1, max_tr_ts do
heads[i] = {term = "+"}
end
end
if not heads[1] then
heads = {{term = "+"}}
end
for i, headobj in ipairs(heads) do
if headobj.tr and trs[i] then
if headobj.tr ~= trs[i] then
error(("Saw two different translits '%s' and '%s' for head #%s"):format(
headobj.tr, trs[i], i))
end
else
headobj.tr = headobj.tr or trs[i]
end
if headobj.tr == "+" then
headobj.tr = nil
end
if headobj.ts and tss[i] then
if headobj.ts ~= tss[i] then
error(("Saw two different transcriptions '%s' and '%s' for head #%s"):format(
headobj.ts, tss[i], i))
end
else
headobj.ts = headobj.ts or tss[i]
end
if headobj.ts == "-" then
headobj.ts = nil
end
if headobj.term == "+" then
headobj.term = args.nolink and pagename or nil
if headobj.term and namespace == "Reconstruction" then
headobj.term = "*" .. headobj.term
end
end
end
headdata.heads = heads
local pos_category = headdata.pos_category
-- Do this as late as possible in case the caller wants to conditionalize the primary categorizing POS on
-- arguments or even the presence or absence of explicit heads.
if pos_functions[indexing_pos] then
pos_category = resolve_prop(pos_functions[indexing_pos].pos_category) or pos_category
end
headdata.process_props.normalized_template_indicated_pos = pos_category
headdata.pos_category = pos_category
headdata.process_props.pos_variants = nil -- erase the cache
local function pagename_is_suffix()
if sc:getCode() == "Latn" then
-- shortcut Latin terms to avoid unnecessarily loading [[Module:affix]]
return pagename:find("^%-") and not pagename:find("%-$")
else
local affix_type, _, _, _ = require(affix_module).parse_term_for_affixes(pagename, lang, sc)
return affix_type == "Hậu tố"
end
end
local clitic_label = args.clitic
if clitic_label == true then
clitic_label = "khinh âm"
end
if clitic_label then
headdata:insert_category("khinh âm")
headdata:insert_fixed_inflection(clitic_label)
elseif args.suffix or (
not args.nosuffix and pagename_is_suffix() and pos_category ~= "Hậu tố" and pos_category ~= "Biến thể hình thái hậu tố"
) then
headdata.process_props.is_suffix = true
local pos_list_for_inflectional_suffix = {}
local pos_list_for_suffix_form = {}
local pos_list_for_forming_suffix = {}
local function handle_suffix_pos(pos, is_first)
-- Use the actual POS for the label we insert, but simplify the POS for the category. We need to separately check
-- each of them to see if they end in 'forms' because e.g. 'comparative adjectives' gets simplified to
-- 'adjective forms'.
local form_type = pos:match("^Biến thể hình thái (.*)$")
if form_type then
if args.inflsuf then
insert(pos_list_for_inflectional_suffix, form_type)
else
insert(pos_list_for_suffix_form, form_type)
end
else
local singular_pos = require(en_utilities_module).singularize(pos)
if args.inflsuf then
insert(pos_list_for_inflectional_suffix, singular_pos)
else
insert(pos_list_for_forming_suffix, singular_pos .. "-")
end
end
if args.inflsuf then
headdata:insert_category("inflectional suffixes")
end
local simplified_pos = export.simplify_pos(pos, "preserve_proper_nouns", "preserve_lemma_status")
local simplified_form_type = simplified_pos:match("^Biến thể hình thái (.*)$")
if simplified_form_type then
if args.inflsuf then
headdata:insert_category(("%s inflectional suffixes"):format(simplified_form_type))
else
headdata:insert_category(("%s suffix forms"):format(simplified_form_type))
end
else
local singular_simplified_pos = require(en_utilities_module).singularize(simplified_pos)
if args.inflsuf then
headdata:insert_category(("%s inflectional suffixes"):format(singular_simplified_pos))
else
headdata:insert_category(("%s-forming suffixes"):format(singular_simplified_pos))
end
end
local postype
if args.inflsuf then
postype = "lemma"
end
if not postype then
postype = require(headword_module).pos_lemma_or_nonlemma(pos)
end
if not postype then
postype = require(headword_module).pos_lemma_or_nonlemma(simplified_pos)
end
if not postype then
error(("Unrecognized canonicalized part of speech '%s' in addlpos=, cannot determine whether lemma or non-lemma form"):format(
pos
))
end
local this_pos_category = postype == "Mục từ" and "Hậu tố" or "Biến thể hình thái hậu tố"
if is_first then
headdata.pos_category = this_pos_category
headdata.process_props.pos_variants = nil -- erase the cache
elseif headdata.pos_category ~= this_pos_category then
local singular_normalized_this_pos_category =
require(en_utilities_module).singularize(this_pos_category)
local singular_normalized_overall_pos_category =
require(en_utilities_module).singularize(headdata.pos_category)
error(("Cannot mix suffixes and suffix forms using addlpos=; '%s' is of a %s while overall POS " ..
"'%s' is a %s; use separate POS headers for the two"):format(pos,
singular_normalized_this_pos_category, pos_category, singular_normalized_overall_pos_category))
end
end
handle_suffix_pos(pos_category, true)
if args.addlpos then
for _, addlpos in ipairs(split(args.addlpos, "%s*,%s*")) do
addlpos = canonicalize_pos(addlpos)
handle_suffix_pos(addlpos, false)
end
end
if pos_list_for_inflectional_suffix[1] then
headdata:insert_fixed_inflection(mw.text.listToText(pos_list_for_inflectional_suffix) .. " inflectional suffix")
end
if pos_list_for_suffix_form[1] then
headdata:insert_fixed_inflection(mw.text.listToText(pos_list_for_suffix_form) .. " suffix form")
end
if pos_list_for_forming_suffix[1] then
headdata:insert_fixed_inflection(mw.text.listToText(pos_list_for_forming_suffix) .. "forming suffix")
end
end
if args.cat then
for _, cat in ipairs(split_on_comma(args.cat)) do
headdata:insert_category(cat)
end
end
local function augment_headdata_from_infls(infls)
infls = resolve_prop(infls)
for _, infl in ipairs(infls) do
local function interr(txt)
error(("Internal error: %s (coming from infls spec %s)"):format(txt, dump(infl)))
end
local function process_labelobjs(labelobjs, originating_term, handle_labelobj)
if labelobjs == nil then
return
end
if type(labelobjs) ~= "string" and type(labelobjs) ~= "table" then
interr(("Wrong type '%s' for label object(s) %s, expected string or table"):format(
type(labelobjs), dump(labelobjs)
))
end
if type(labelobjs) == "string" or type(labelobjs) == "table" and not labelobjs[1] then
labelobjs = {labelobjs}
end
for _, labelobj in ipairs(labelobjs) do
local label, termobj
if type(labelobj) == "string" then
label = labelobj
termobj = originating_term
elseif type(labelobj) ~= "table" then
interr(("Wrong type '%s' for label object %s, expected string or table"):format(
type(labelobj), dump(labelobj)
))
label = labelobj.term
if type(label) ~= "string" then
interr(("Wrong type '%s' for label %s from label object %s, expected string"):format(
type(label), dump(label), dump(labelobj)
))
end
termobj = labelobj
end
handle_labelobj(label, termobj)
end
end
local param = infl[1]
if param then
local vals
-- Fetch the param and make sure it's a string or number.
param = resolve_prop(param)
if type(param) ~= "string" and type(param) ~= "number" then
interr(("Parameter name %s must be a string or number"):format(dump(param)))
end
-- Fetch the type and validate.
local typ = resolve_prop(infl.type)
if typ == nil then
typ = "string"
end
if typ ~= "genders" and typ ~= "boolean" and typ ~= "string" then
-- FIXME: Handle more types.
interr(('Unrecognized type %s; can only currently handle "genders", "boolean" and "string" (the default)'):format(
dump(typ)))
end
-- Fetch the value(s).
if typ == "genders" or typ == "boolean" then
vals = args[param]
elseif typ == "string" then
local parse_inflection_props = resolve_prop(infl.parse_inflection_props)
local include_mods = resolve_prop(infl.include_mods)
local no_augment_include_mods = resolve_prop(infl.no_augment_include_mods)
if include_mods ~= nil or no_augment_include_mods ~= nil then
if parse_inflection_props == nil then
parse_inflection_props = {}
else
parse_inflection_props = shallow_copy(parse_inflection_props)
end
if include_mods ~= nil then
parse_inflection_props.include_mods = include_mods
end
if no_augment_include_mods ~= nil then
parse_inflection_props.no_augment_include_mods = no_augment_include_mods
end
end
vals = headdata:parse_inflection(param, parse_inflection_props)
-- Convert an empty list to nil for consistent checking below.
if not vals[1] then
vals = nil
end
else
interr(("Unrecognized type '%s"):format(typ))
end
-- If value(s) nil, fetch the default.
if vals == nil and infl.default ~= nil then
local default = resolve_prop(infl.default)
if typ == "genders" then
vals = export.canonicalize_termobj_list(default, "spec", "default")
elseif typ == "boolean" then
vals = default
elseif typ == "string" then
vals = export.canonicalize_termobj_list(default, "term", "default")
else
interr(("Unrecognized type '%s"):format(typ))
end
end
-- Resolve "special" values (special signals a string values, such as requesting the default with "+").
if vals ~= nil and infl.resolve_special then
if typ ~= "string" then
interr(("Cannot specify resolve_special= for type %s"):format(dump(typ)))
end
local resolve_special_props = resolve_prop(infl.resolve_special_props, vals)
if infl.is_special ~= nil then
if resolve_special_props == nil then
resolve_special_props = {}
else
resolve_special_props = shallow_copy(resolve_special_props)
end
resolve_special_props.is_special = infl.is_special
end
vals = headdata:resolve_special(vals, infl.resolve_special, resolve_special_props)
end
-- Canonicalize the value(s).
if vals ~= nil and infl.canonicalize ~= nil then
if typ == "boolean" then
interr('Cannot specify canonicalize= when type is "boolean"')
end
local newvals = {}
local field = typ == "genders" and "spec" or "term"
for _, val in ipairs(vals) do
local newval
if val._is_mode then
newval = val
else
if type(infl.canonicalize) == "function" then
newval = infl.canonicalize(val[field])
else
newval = val[field]
newval = infl.canonicalize[newval] or newval
end
end
if val[field] ~= newval then
val = shallow_copy(val)
val[field] = newval
end
insert(newvals, val)
end
vals = newvals
end
-- Validate the value(s).
if vals ~= nil and infl.validate ~= nil then
if typ == "boolean" then
interr('Cannot specify validate= when type is "boolean"')
elseif type(infl.validate) == "function" then
infl.validate(headdata, vals)
elseif typ == "genders" then
export.validate_genders {
genders = vals,
valid_genders = infl.validate,
no_augment = resolve_prop(infl.no_augment_genders),
}
elseif typ == "string" then
validate_items {
items = vals,
field = "term",
valid_items = infl.validate,
item_type = ("values in |%s="):format(param),
}
else
interr(("Unrecognized type '%s"):format(typ))
end
end
local function run_and_check_return_value(fun, funname, ...)
local retval, intentionally_nil = fun(...)
if retval == nil and not intentionally_nil then
interr(("If you return nil from %s, you must return a second non-nil return " ..
"value to indicate that the nil return value was intentional"):format(funname))
end
return retval
end
-- Run the process_after_parse handler, if it exists.
if vals ~= nil and infl.process_after_parse ~= nil then
vals = run_and_check_return_value(infl.process_after_parse, "process_after_parse", headdata, vals)
end
-- Process strings for insertion, to handle special mode signals like -, +-, ?, etc.
local processed
if typ == "string" then
local process_for_insertion_props = resolve_prop(infl.process_for_insertion_props, vals)
processed = headdata:process_for_insertion(vals, process_for_insertion_props)
headdata.process_props.processed_specs[param] = processed
if processed.numterms > 0 and infl.process_one_term ~= nil then
local processed_terms = {}
for _, termobj in ipairs(processed.terms) do
local processed_term = run_and_check_return_value(infl.process_one_term, "process_one_term",
headdata, termobj)
if processed_term ~= nil then
-- FIXME: Should we add an origin of "process_one_term" here? (But this would erase
-- any "default" or "resolve_special" origins.)
local newterms = export.canonicalize_termobj_list(processed_term, "term", nil, nil,
termobj)
for _, newterm in ipairs(newterms) do
insert(processed_terms, newterm)
end
end
end
processed = shallow_copy(processed)
processed.terms = processed_terms
processed.numterms = #processed_terms
end
if processed.numterms > 0 and infl.process_terms ~= nil then
local process_terms_result = run_and_check_return_value(infl.process_terms, "process_terms",
processed)
process_terms_result = process_terms_result or {}
if process_terms_result[1] or not next(process_terms_result) then
processed = shallow_copy(processed)
processed.terms = process_terms_result
processed.numterms = #process_terms_result
elseif not process_terms_result.terms or not process_terms_result.numterms then
error("Return value from process_terms() should be either a list of term objects or a " ..
"`processed` structure containing such a list")
else
processed = process_terms_result
end
end
vals = processed.terms
end
local inserted_fixed_label
-- "Implement" the values. Fixed labels and cats only fire when non-falsy (i.e. we don't want to fire
-- on boolean false or empty list). We call insert_processed_inflection() always because there may be
-- a mode even when there are no values.
if vals == true or type(vals) == "table" and vals[1] then
if infl.fixed_label and infl.all_fixed_label then
interr("Cannot specify both fixed_label= and all_fixed_label=; specify one or the other")
end
local function check_fixed_label_references_val(label)
if type(label) == "table" and label[1] then
for _, lab in ipairs(label) do
if check_fixed_label_references_val(lab) then
return true
end
end
return false
end
if type(label) == "table" then
if not label.term then
interr(("Fixed label structure %s does not have a value for `.term`"):format(dump(label)))
end
label = label.term
end
if type(label) ~= "string" then
interr(("Wrong type for fixed label %s, should be string"):format(type(label)))
end
return not not label:find("{val}")
end
local fixed_label = infl.fixed_label
local all_fixed_label = infl.all_fixed_label
-- If the value being processed is boolean, there's only one value so treat a fixed_label as an
-- all_fixed_label and output only once; likewise if the caller specified a fixed_label without
-- {val} in it.
if fixed_label and (typ == "boolean" or type(fixed_label) ~= "function" and not
check_fixed_label_references_val(fixed_label)) then
all_fixed_label = fixed_label
fixed_label = nil
end
if fixed_label then
if typ == "boolean" then
interr("Boolean fixed_label values should have been converted to all_fixed_label")
end
for _, valobj in ipairs(vals) do
local labelobjs = resolve_prop(fixed_label, valobj)
process_labelobjs(labelobjs, valobj, function(label, termobj)
if label:find("{val}") then
if typ ~= "string" then
interr(('Cannot specify {val} in fixed_label %s when type is "%s"'):format(dump(label), typ))
end
label = label:gsub("{val}", replacement_escape(valobj.term))
end
headdata:insert_fixed_inflection(label, {
originating_term = termobj
})
inserted_fixed_label = true
end)
end
elseif all_fixed_label then
local labelobjs = resolve_prop(all_fixed_label, vals)
process_labelobjs(labelobjs, nil, function(label, termobj)
if label:find("{vals}") then
if typ ~= "string" then
interr(('Cannot specify {vals} in all_fixed_label %s when type is "%s"'):format(dump(label), typ))
end
local formatted_labels = {}
for _, valobj in ipairs(vals) do
insert(formatted_labels, add_decorations(valobj.term, valobj, lang))
end
label = label:gsub("{vals}", replacement_escape(serial_comma_join(formatted_labels)))
end
headdata:insert_fixed_inflection(label, termobj)
inserted_fixed_label = true
end)
end
end
-- See above; if there's a label (which can only happen when typ == "string"), always call
-- insert_processed_inflection.
local inserted_vals
if infl.label ~= nil then
if typ ~= "string" then
interr(("label=%s can only be specified for type 'string', not '%s'"):format(
dump(infl.label), typ
))
end
local label = resolve_prop(infl.label, vals)
if label ~= nil then
local insert_processed_inflection_props =
resolve_prop(infl.insert_processed_inflection_props, vals)
local function copy_to_insert_processed_inflection_props(field, val)
local fieldval = val or resolve_prop(infl[field], vals)
if fieldval ~= nil then
if insert_processed_inflection_props == nil then
insert_processed_inflection_props = {}
else
insert_processed_inflection_props =
shallow_copy(insert_processed_inflection_props)
end
insert_processed_inflection_props[field] = fieldval
end
end
copy_to_insert_processed_inflection_props("label_for_cats_and_modes")
copy_to_insert_processed_inflection_props("no_auto_cats")
copy_to_insert_processed_inflection_props("accel")
copy_to_insert_processed_inflection_props("accel_form")
copy_to_insert_processed_inflection_props("request")
if not processed then
interr("`processed` structure wasn't properly created")
end
headdata:insert_processed_inflection(processed, label, insert_processed_inflection_props)
inserted_vals = true
end
end
-- See above; only fire for cats (and gender insertion) on non-falsy values.
if vals == true or type(vals) == "table" and vals[1] then
if typ == "genders" then
headdata.genders = vals
end
local inserted_cat
if infl.cat then
if typ == "boolean" then
vals = {vals}
end
local allcats = {}
for _, valobj in ipairs(vals) do
local cats = resolve_prop(infl.cat, valobj)
if type(cats) == "string" then
cats = {cats}
end
if cats ~= nil then
for _, cat in ipairs(cats) do
if cat:find("{val}") then
cat = cat:gsub("{val}", replacement_escape(valobj.term))
end
insert_if_not(allcats, cat)
end
end
end
for _, cat in ipairs(allcats) do
headdata:insert_category(cat)
inserted_cat = true
end
end
if typ == "boolean" then
if not inserted_fixed_label and not inserted_cat then
interr(("User set boolean setting for %s= but no fixed label added and no category " ..
"inserted; if you took action in process_after_parse(), make sure to return " ..
"`nil, true`"):format(param))
end
elseif typ == "string" then
if not inserted_vals and not inserted_fixed_label and not inserted_cat then
interr(("User set value(s) %s for %s= but no inflection inserted and no fixed label " ..
"or category added"):format(dump(vals), param))
end
end
if processed then
-- FIXME, convert to use `processed_specs` field and remove this
headdata.process_props.insert_specs[param] = processed
end
end
else -- no param specified
if infl.label or infl.all_fixed_label then
interr("Cannot have label= or all_fixed_label= without specifying a param")
end
if infl.process then
infl.process(headdata)
end
if infl.fixed_label then
local labelobjs = resolve_prop(infl.fixed_label)
process_labelobjs(labelobjs, nil, function(label, termobj)
if label:find("{val}") then
interr("Cannot specify {val} in a fixed_label= value without specifying a param")
end
headdata:insert_fixed_inflection(label, {
originating_term = termobj
})
end)
end
if infl.cat then
local cats = resolve_prop(infl.cat)
if type(cats) == "string" then
cats = {cats}
end
if cats ~= nil then
for _, cat in ipairs(cats) do
if cat:find("{val}") then
interr("Cannot specify {val} in a cat= value without specifying a param")
end
headdata:insert_category(cat)
end
end
end
end
end
end
if infls then
augment_headdata_from_infls(infls)
end
if augment_headdata then
augment_headdata(headdata, args)
end
if pos_functions[indexing_pos] then
local pos_infls = pos_functions[indexing_pos].infls
if pos_infls then
augment_headdata_from_infls(pos_infls)
end
local func = pos_functions[indexing_pos].func
if func then
func(headdata, args)
end
end
headdata:parse_and_insert_inflection("abbr", "abbreviation")
headdata.checkredlinks = resolve_prop(checkredlinks)
setmetatable(headdata, nil)
headdata.process_props = nil
if args.json then
return require("Module:JSON").toJSON(headdata)
end
return require(headword_module).full_headword(headdata)
end
return export
c06r1cslom4257soa9vnvtug7n25zne
2573883
2573878
2026-10-27T14:00:09Z
Hiyuune
50834
2573883
Scribunto
text/plain
local export = {}
--[==[
FIXME:
1. Implement ? = unknown or uncertain. There should be no label by default but it should be configurable.
The default category should be "{plpos} with unknown or uncertain plurals" for nouns/proper nouns. It should
be an error to specify values along with this. [DONE]
2. Implement ! = unattested. The default label should be "{label} not attested". The default category should be
"{plpos} with unattested plurals" for nouns/proper nouns. It should be an error to specify values along with
this. [DONE]
3. Implement +- = sometimes has a value, e.g. "countable and uncountable" for plurals. [DONE]
4. Implement ~ standing for the pagename in heads and inflections. [DONE]
5. Implement abbr= for abbreviations. [DONE]
6. Split insert_inflection into process_for_insertion() (which implements the logic for handling special mode signals)
and insert_processed_inflection() (which implements the labels and categories for mode signals) so that we can handle
mode signals correctly. [DONE]
7. Support no_label for booleans.
8. Document the various types of parts of speech and implement pos_category per part of speech definition. [DONE]
9. Implement apply_link_modifiers() for head=~~...
10. Correctly handle var=both. [DONE]
11. Implement checkredlinks and port simplify_pos to [[Module:headword]]. [CHECKREDLINKS SET; SIMPLIFY_POS NOT PORTED YET.]
12. Fix longstanding accelerator bug: if embedded links in inflection, don't make it green. [DONE]
13. Implement process_terms() callback after process_for_insertion. [DONE]
14. Implement validate_and_canonicalize() callback. [NOT DONE; INSTEAD HAVE A SEPARATE CANONICALIZE CALLBACK]
15. Document new methods.
16. Implement process_one_term() callback after process_for_insertion. [DONE]
17. Implement process() at a stage to happen universally when there is no argument to parse. [DONE]
18. Skip mode signals during validation and canonicalization. [DONE]
19. Implement inflsuf= with special handling and special inflectional categories. [DONE]
20. Add support for "boolean or string" type.
21. Support invocation-specified or user-specified languages. [DONE]
]==]
local require_when_needed = require("Module:utilities/require when needed")
local affix_module = "Module:affix"
local debug_track_module = "Module:debug/track"
local decorations_module = "Module:decorations"
local en_utilities_module = "Module:en-utilities"
local fun_is_callable_module = "Module:fun/isCallable"
local headword_module = "Module:headword"
local headword_data_module = "Module:headword/data"
local languages_module = "Module:languages"
local links_module = "Module:links"
local parameters_module = "Module:parameters"
local parse_interface_module = "Module:parse interface"
local parse_utilities_module = "Module:parse utilities"
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 lcfirst = require("Module:string utilities").lcfirst
local dump = mw.dumpObject
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
local insert = table.insert
local concat = table.concat
local remove = table.remove
local sort = table.sort
local contains = require_when_needed(table_module, "contains")
local deep_equals = require_when_needed(table_module, "deepEquals")
local extend = require_when_needed(table_module, "extend")
local insert_if_not = require_when_needed(table_module, "insertIfNot")
local is_array = require_when_needed(table_module, "isArray")
local list_to_set = require_when_needed(table_module, "listToSet")
local serial_comma_join = require_when_needed(table_module, "serialCommaJoin")
local shallow_copy = require_when_needed(table_module, "shallowCopy")
local split = require_when_needed(string_utilities_module, "split")
local ugsub = require_when_needed(string_utilities_module, "gsub")
local umatch = require_when_needed(string_utilities_module, "match")
local pattern_escape = require_when_needed(string_pattern_escape_module)
local replacement_escape = require_when_needed(string_replacement_escape_module)
local escape_wikicode = require_when_needed(parse_utilities_module, "escape_wikicode")
local parse_inline_modifiers = require_when_needed(parse_utilities_module, "parse_inline_modifiers")
local term_contains_top_level_html = require_when_needed(parse_utilities_module, "term_contains_top_level_html")
local get_lang_by_code = require_when_needed(languages_module, "getByCode")
local is_callable = require_when_needed(fun_is_callable_module)
local format_decorations = require_when_needed(decorations_module, "format_decorations")
local singularize = require_when_needed(en_utilities_module, "singularize")
local canonicalize_pos = require_when_needed(headword_module, "canonicalize_pos")
--[==[ intro:
This module contains utilities for constructing language-specific headword modules.
[fill in]
===About parts of speech===
There are several versions of the part of speech (POS) of a given term:
* The ''primary categorizing POS'' is the basic part of speech to which a term belongs. The primary POS determines
whether the term is a lemma or non-lemma form, and in its plural form, it is used to categorize the terms (unless it
is a variant term), along with caetegorizing into a ''lemmas'' or ''non-lemma forms'' category. (Variant terms,
typically marked by {{para|var|1}}, are categorized into ''variant lemmas'' or ''variant non-lemma forms'' and are not
categorized into a primary POS category.)
* The ''raw {{tl|head}} POS'' is parameter {{para|2}} to {{tl|head}}. This is used to determine the primary categorizing
POS by a process of canonicalization, because this parameter may contain abbreviations (e.g. ''a'' or ''adj'' for an
adjective) and may be (and usually is) given in its singular form.
* The ''template-indicated POS'' is the part of speech indicated by a headword template such as {{tl|nl-noun}} or
{{tl|mn-head}}. It is either inherent to the template (in a case like {{tl|nl-noun}}), in which case it is specified
by the template code and should always be in canonical, plural form; or it is specified by the user in the template
call (in a case like {{tl|mn-head}} or other generic POS template, usually ending in `-head`), in which case it may
be given in raw form, like for the raw {{tl|head}} POS, and is canonicalized using the same code that canonicalizes
raw {{tl|head}} parts of speech. The template-indicate POS may not be a valid primary categorizing POS. For example,
Dutch has a template-indicated POS ''diminutiva tantum nouns'', specified through the template {{tl|nl-dim-tant}}, but
there is no such primary categorizing POS (that POS would be `nouns`). Similarly, Arabic has a template-indicated POS
''collective nouns'', whose primary categorizing POS is also `nouns`. As a result, the template-indicated POS may need
to be converted to a primary categorizing POS through a ''normalization'' process (which uses a `pos_category` field
attached to the `pos_functions` structure for the template-indicated POS). Note that the template-indicated POS comes
in several variants:
*# It may be ''head-raw'', meaning that it uses the literal string `head` in place of the user-specified POS in a
generic POS template like {{tl|mn-head}}; it may be ''non-head'', meaning that it uses the actual user-specified
POS in such a case; or it may be ''head-augmented'', meaning that in the case of a generic POS template it is of the
form e.g. `head.nouns`, contanining both the literal `head` and the user-specified POS.
*# It may be ''suffix-raw'', meaning that it uses the primary categorizing POS `suffixes` in place of the
template-indicated POS when the term is a suffix, or it may be ''non-suffix'', meaning that it uses the
template-indicated POS in such a case. The headword for a suffix uses the template corresponding to whatever POS the
suffix forms, e.g. {{nl-noun}} for Dutch noun-forming suffixes.
*# The ''indexing POS'' is the variant of the template-indicated POS that is used to index into the `pos_functions`
table. Normally it is head-raw and non-suffix, but it will be head-augmented if the corresponding head-augmented
entry exists in `pos_functions`.
*# The ''unnormalized categorizing POS'' is the variant of the template-indicated POS that is potentially usable as the
primary categorizing POS (see above). It is non-head and suffix-raw, i.e. it uses the actual template-specified or
user-specified form of the template-indicated POS except when the term is a suffix, in which case it has the value
`suffixes`. In most circumstances this can be used directly as the primary categorizing POS, but in some cases this
POS is not a valid primary categorizing POS and needs to be normalized to get the actual primary categorizing POS, as
mentioned above under the description for the template-indicated POS.
* Finally, there is a ''simplified categorizing POS''. This collapses variations of primary categorizing parts of speech
together for use in red-link and similar categories. For example, the POS `proper nouns` is collapsed into `nouns`,
and all types of participles are collapsed into `participles`. There are two variants of the simplification process,
''lemma-status-preserving'' and ''non-lemma-status-preserving''. The latter is the default and is simpler, in that
e.g. it means any part of speech ending in `nouns` to `nouns`, even if this causes non-lemma forms to become lemmas,
as with `diminutive nouns` and `verbal nouns` (which are non-lemma forms) and likewise with `comparative adjectives`
and `superlative adjectives` (which are again non-lemma forms). The former, ''lemma-status-preserving'', keeps lemmas
as lemmas and (more important) non-lemmas as non-lemmas by converting e.g. `diminutive nouns` and `verbal nouns` to
`noun forms` rather than `nouns`. The reason two variants exist in that they have different use cases. The default,
non-lemma-status-preserving, type is used for tracking categories such as red-link and request categories, where
completeness is more important than clutter. The lemma-status-preserving variant is used for informative categories
such as ` ``lang`` masculine nouns`, which should only have lemmas in them to avoid clutter. (For the same reason,
variant terms specified using {{para|var|1}} aren't normally categorized into informative categories but are placed
in tracking categories.)
===About properties and data objects===
[fill in]
]==]
local inflection_to_cats_and_label = {
plural = {
filter_pos = function(posvars)
-- plurals also occur with determiners, adjectives etc. and we don't want to generate categories like
-- 'countable determiners', 'countable adjectives', etc. Note that the passed-in `simp_plpos` has
-- `proper nouns` converted to `nouns`.
return posvars.simp_plpos == "Danh từ"
end,
cats = {"Danh từ đếm được"},
no_cats = {"Danh từ không đếm được"},
fixed_adj_label = "đếm được",
fixed_adj_no_label = "không đếm được",
sometimes_label = "đếm được và không đếm được",
unattested_cats = "Danh từ có số nhiều chưa được chứng thực",
unknown_cats = "Danh từ có số nhiều không rõ hoặc chưa được chứng thực",
},
comparative = {
cats = {"{simp_plpos} so sánh được"},
no_cats = {"{simp_plpos} không so sánh được"},
fixed_adj_label = "so sánh được",
},
["female equivalent"] = {
cats = {"{simp_plpos} with other-gender equivalents"},
},
["male equivalent"] = {
cats = {"{simp_plpos} with other-gender equivalents"},
},
}
local param_mods = {
alt = {}, -- always allow so e.g. - can be specified as a suffix form
id = {}, -- disabled when `is_head = true`
q = {type = "qualifier"},
qq = {type = "qualifier"},
l = {type = "labels"},
ll = {type = "labels"},
-- [[Module:headword]] expects part references in `.refs`.
ref = {item_dest = "refs", type = "references", store = "insert-flattened"},
}
local optional_param_mods = {
g = {item_dest = "genders", type = "genders"},
lang = {type = "language"},
sc = {type = "script"},
t = {item_dest = "gloss"},
gloss = {},
pos = {},
ng = {},
lit = {},
tr = {},
ts = {},
face = {},
nolinkinfl = {type = "boolean"},
}
local optional_headword_param_mods = {
sc = {type = "script"},
tr = {},
ts = {},
}
local function split_on_comma(val)
if val:find(",") then
return require(parse_interface_module).split_on_comma(val)
else
return {val}
end
end
local function ine(val)
if not val then
return nil
end
val = mw.text.trim(val)
return val ~= "" and val or nil
end
--[=[
Add decorations to a term. `termobj` is the object describing 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`;
`text` is the text of the term itself, and `lang` is the language object.
]=]
local function add_decorations(text, termobj, lang)
local function field_non_empty(field)
local list = termobj[field]
if not list then
return nil
end
if type(list) ~= "table" then
error(("Internal error: Wrong type for `termobj.%s`=%s, should be \"table\""):format(
field, mw.dumpObject(list)))
end
return list[1]
end
if field_non_empty("q") or field_non_empty("qq") or field_non_empty("l") or field_non_empty("ll") or
field_non_empty("refs") then
text = format_decorations {
lang = lang,
text = text,
q = termobj.q,
qq = termobj.qq,
l = termobj.l,
ll = termobj.ll,
refs = termobj.refs,
}
end
return text
end
--[==[
Parse a single inflection or headword form or list of such forms. In either case, inline modifiers may be attached.
`data` is an object with the following fields:
* `val`: The raw value to parse. Required.
* `paramname`: The name of the parameter from which the value was taken; used in error messages. Required.
* `is_head`: We are parsing a headword parameter (a value which goes into the `heads` field of `data`). This changes
the allowed modifiers, disabling the `id` modifier and only allowing a subset of optional modifiers.
* `frob`: An optional function of one value to apply to the form after inline modifiers have been removed (i.e. to
apply to the `.term` field of the returned object).
* `include_mods`: List of extra inline modifiers to include, besides the default ones (see below). Each list item is
either a string specifying a recognized extra inline modifier (see `optional_param_mods` in the code), or a two-item
list of modifier name and modifier spec, where the spec should follow the syntax for modifier specs in
`parse_inline_modifiers` in [[Module:parse utilities]].
* `exclude_mods`: List of default inline modifiers to not include.
* `splitchar`: If specified, the value in `val` can be a list of forms to parse, separated by the value of `splitchar`
(which is a Lua pattern, as in `parse_inline_modifiers` in [[Module:parse utilities]]). Most commonly, `splitchar` is
a single comma and the values are comma-separated (in this case, splitting will not happen if a space follows the
comma).
* `parse_lang_prefix`: If specified, allow a language prefix to precede a form, and if found, store into the `.lang`
field of the returned object.
* `disable_special_modes`: If specified, don't recognize and treat specially the special mode signals (e.g. '-' for
no inflection, '?' for unknown, etc.).
* `preserve_splitchar`, `delimiter_key`, `escape_fun`, `unescape_fun`, `pre_normalize_modifiers`: As in
`parse_inline_modifiers` in [[Module:parse utilities]].
Returns an object suitable for storing as one element of one of the lists in `headdata.inflections`, where `headdata`
is the structure passed to [[Module:headword]]. If `splitchar` is specified, howeve, the return value is a list of such
objects.
The following default inline modifiers are currently recognized:
* `q`: Left qualifier.
* `qq`: Right qualifier.
* `l`: Comma-separated list of left labels. No space should follow the comma.
* `ll`: Comma-separated list of right labels. No space should follow the comma.
* `ref`: Reference or references. See {{tl|IPA}} for the syntax.
* `id`: Sense ID, in case there are multiple senses. See {{tl|l}}.
The following are the recognized additional inline modifiers:
* `g`: Comma-separated list of genders.
* `alt`: Display text.
* `lang`: Language code of language of the form, if different from the language of the headword.
* `sc`: Script code of script of the form. Almost never needed.
* `t`: Gloss for the form.
* `gloss`: Gloss for the form (alias for `t`).
* `pos`: Part of speech of the form.
* `lit`: Literal meaning of the form.
* `tr`: Manual transliteration of the form.
* `ts`: Transcription of the form, for languages where the transliteration differs markedly from the pronunciation.
* `face`: Face to display the form in, e.g. {"hypothetical"} for a hypothetical form (unlinkable and displayed in italics).
* `nolinkinfl`: Make the form unlinkable.
]==]
function export.parse_term_with_modifiers(data)
local paramname, val, frob = data.paramname, data.val, data.frob
local function generate_obj(term, parse_err)
if not data.disable_special_modes and export.special_modes[term] then
return {
term = term,
_is_mode = true
}
end
if frob then
term = frob(term, parse_err)
end
if data.parse_lang_prefix and term:find(":") then
return require(parse_utilities_module).generate_obj_maybe_parsing_lang_prefix {
term = term,
paramname = paramname,
parse_lang_prefix = true,
parse_err = parse_err,
}
else
return {term = term}
end
end
-- Check for inline modifier, e.g. מרים<tr:Miryem>. But exclude top-level HTML entry with <span ...>,
-- <sup> or similar in it.
if (val:find("<", nil, true) or data.splitchar) and not term_contains_top_level_html(val) and
-- don't parse inline modifiers if is_head and the value begins with a ~ (link modifier syntax)
(not data.is_head or not val:find("^~")) then
local param_mods = param_mods
if data.is_head then
param_mods = shallow_copy(param_mods)
param_mods.id = nil
end
if data.include_mods or data.exclude_mods then
if not data.is_head then
-- already copied when data.is_head
param_mods = shallow_copy(param_mods)
end
if data.include_mods then
local optional_mods = data.is_head and optional_headword_param_mods or optional_param_mods
for _, mod in ipairs(data.include_mods) do
if type(mod) == "table" then
if #mod ~= 2 then
error(("Internal error: Modifier spec %s in `include_mods` should be of length 2"):format(
dump(mod)))
end
local modkey, modvalue = unpack(mod)
param_mods[modkey] = modvalue
elseif not optional_mods[mod] then
error(("Internal error: Unrecognized modifier spec %s in `include_mods`"):format(
dump(mod)))
else
param_mods[mod] = optional_mods[mod]
end
end
end
if data.exclude_mods then
for _, mod in ipairs(data.exclude_mods) do
if not param_mods[mod] then
error(("Internal error: Modifier spec %s in `exclude_mods` not found among existing modifiers"
):format(dump(mod)))
else
param_mods[mod] = nil
end
end
end
end
return parse_inline_modifiers(val, {
paramname = paramname,
param_mods = param_mods,
generate_obj = generate_obj,
splitchar = data.splitchar,
preserve_splitchar = data.preserve_splitchar,
delimiter_key = data.delimiter_key,
escape_fun = data.escape_fun,
unescape_fun = data.unescape_fun,
pre_normalize_modifiers = data.pre_normalize_modifiers,
})
else
local retval = generate_obj(val)
if data.splitchar then
retval = {retval}
end
return retval
end
end
--[==[
Parse a list of inflection forms that may have inline modifiers attached. `data` is an object with the following fields:
* `forms`: The list of raw values to parse. Required.
* `paramname`: The name of the first parameter from which the value was taken; used in error messages. If this is a
two-element list, the first element is the first parameter and the second element is the prefix of the remaining
parameters. Parameter names that are numbers are handled correctly, as are those with \1 in it marking where the
parameter index goes. Required.
* `qualifiers`: If specified, a possibly gappy list of left qualifiers to add to the parsed terms (for compatibility
purposes).
* `splitchar`: As in `parse_term_with_modifiers()`. The resulting per-term lists will be flattened.
* `frob`, `include_mods`, `exclude_mods`, `is_head`, `preserve_splitchar`, `parse_lang_prefix`, `delimiter_key`,
`escape_fun`, `unescape_fun`, `pre_normalize_modifiers`: As in `parse_term_with_modifiers()`.
Returns a list of objects, suitable for storing as one of the lists in `headdata.inflections` (once a label is added),
where `headdata` is the structure passed to [[Module:headword]].
]==]
function export.parse_term_list_with_modifiers(data)
local paramname, forms = data.paramname, data.forms
local qualifiers = data.qualifiers
local first, restpref
if type(paramname) == "table" then
first = paramname[1]
restpref = paramname[2]
else
first = paramname
restpref = paramname
end
local terms = {}
data = shallow_copy(data)
for i, val in ipairs(forms) do
data.paramname = i == 1 and first or type(restpref) == "number" and restpref + i - 1 or
restpref:find("\1", nil, true) and restpref:gsub("\1", tostring(i)) or restpref .. i
data.val = val
local parsed = export.parse_term_with_modifiers(data)
if qualifiers and qualifiers[i] then
if data.splitchar then
for _, term in ipairs(parsed) do
term.q = {qualifiers[i]}
end
else
parsed.q = {qualifiers[i]}
end
end
if data.splitchar then
extend(terms, parsed)
else
terms[i] = parsed
end
end
return terms
end
--[==[
Construct a link to [[Appendix:Glossary]] for `entry`. If `text` is specified, it is the display text; otherwise,
`entry` is used.
]==]
function export.glossary_link(entry, text)
text = text or entry
return "[[Phụ lục:Từ điển thuật ngữ#" .. entry .. "|" .. text .. "]]"
end
function export.replace_glossary_links_in_label(label)
if label:find("<<", nil, true) then
label = label:gsub("<<(.-)|(.-)>>", export.glossary_link):gsub("<<(.-)>>", export.glossary_link)
end
return label
end
-- If a part of speech ends with one of these, either pluralized or followed by ' forms', chop off whatever precedes.
-- Also, if a part of speech is of the form `BASIC_POS FOO forms` e.g. `noun construct forms`, remove the `FOO`
-- portion. Note that "phrase" is *NOT* listed here because 'prepositional phrase' and 'postpositional phrase' are not
-- types of 'phrase' per the definition of these parts of speech in Wiktionary.
local basic_poses = {
"Tính từ",
"Phó từ",
"Từ rút gọn",
"Danh từ riêng", -- may be converted to noun
"Danh từ",
"Phân từ",
"Đại từ",
"Ký hiệu",
"Động từ",
}
local function optimized_pattern_escape(pos)
if pos:find("-", nil, true) then
return pattern_escape(pos)
else
return pos
end
end
--[==[
Canonicalize and then simplify a qualified part of speech (e.g. ''comparative adjectives'', ''past participles'',
''pronoun possessive forms'') to its unqualified equivalent (e.g. ''adjectives'', ''participles'', ''pronoun forms''').
This is used for categorization, since generally the simplified part of speech is used in categories other than the one
for the part of speech itself. (For example, comparative adjectives are normally categorized as adjectives in categories
such as `Requests for inflections in ``lang`` adjective entries`, and proper nouns are, sometimes at least, categorized
under ` ``lang`` nouns with ...` rather than ` ``lang`` proper nouns with ...`.)
There are two cases where simplification does not happen: phrases and (optionally) proper nouns. In Wiktionary,
''phrase'' really means a clause or similar constituent (or occasionally non-constituent), so `prepositional phrases`
are not types of `phrases` and are not simplified. Proper nouns are simplified to nouns for some categories but left
as-is in other category names; by default, this function simplifies `proper nouns` to `nouns`, but if the parameter
`preserve_proper_nouns` is given, `proper nouns` is left as-is.
If `preserve_lemma_status` is given, the function attempts to return a part of speech that has the same lemma status
(lemma or non-lemma form) as the original part of speech; thus, `comparative adjectives` becomes `adjective forms`
and `diminutive nouns` becomes `noun forms` because both are non-lemma forms. If `preserve_lemma_status` is not given,
the function simply chops off the qualifiers, converting e.g. `comparative adjectives` to `adjectives` even though this
changes the lemma status of the part of speech.
The part of speech can be given singular or plural on input, but is always returned in plural form.
]==]
function export.simplify_pos(pos, preserve_proper_nouns, preserve_lemma_status)
-- This allows for both singular and plural parts of speech in 'pos'.
local plpos = canonicalize_pos(pos)
for _, basic_pos in ipairs(basic_poses) do
if basic_pos ~= "Danh từ riêng" or preserve_proper_nouns then
if plpos == "Biến thể hình thái " .. lcfirst(basic_pos) then
-- So 'proper noun forms' doesn't get converted to 'noun forms' in the next round
return plpos
end
local basic_pos_pattern = optimized_pattern_escape(basic_pos)
if plpos:find(("^Biến thể hình thái %s .*.$"):format(basic_pos_pattern)) then
return "Biến thể hình thái " .. lcfirst(basic_pos)
end
if plpos:find(("^Biến thể hình thái .* %s$"):format(basic_pos_pattern)) then
return "Biến thể hình thái " .. lcfirst(basic_pos)
end
local pl_basic_pos = basic_pos
if plpos == pl_basic_pos then
-- So 'proper nouns' doesn't get converted to 'nouns' in the next round
return plpos
end
local pl_basic_pos_pattern = optimized_pattern_escape(pl_basic_pos)
if plpos:find(("^.* %s$"):format(pl_basic_pos_pattern)) then
if preserve_lemma_status then
local lemma_status = require(headword_module).pos_lemma_or_nonlemma(plpos)
if lemma_status == "non-lemma form" then
return "Biến thể hình thái " .. lcfirst(basic_pos)
end
end
return pl_basic_pos
end
end
end
return plpos
end
function export.get_pos_variants(pos)
local retval = {}
retval.origpos = pos
retval.plpos = canonicalize_pos(pos)
retval.sgpos = singularize(retval.plpos)
retval.simp_plpos = export.simplify_pos(retval.plpos)
retval.simp_sgpos = singularize(retval.simp_plpos)
retval.simp_with_propn_plpos = export.simplify_pos(retval.plpos, "preserve_proper_nouns")
retval.simp_with_propn_sgpos = singularize(retval.simp_with_propn_plpos)
return retval
end
--[==[
Canonicalize a single term or term-like object or a list of either into a list of term-like objects. `abterms` is the
term or list to canonicalize, and `field` is the name of the field holding the term (defaulting to {"term"}). This
does the minimal work necessary, meaning that the return value may partly or completely share memory with the value
passed in. As a special case, if `abterms` is {nil}, {nil} is returned. If `origin` or `originating_term` are specified,
add fields `_origin` and/or `_originating_term` containing the specified value(s) to each resulting term-like object (in
this case, the object will be copied a necessary to avoid side-effecting the passed-in objects). `origin` is intended to
be a string indicating where the term originated from (e.g. {"default"} if a default value, {"resolve_special"} if
originating from a shortcut value such as {"+"}) and `originating_term` is the shortcut term itself, if any. (There is
generally no such term for default values.) If `termobj_for_string_terms` is specified, any terms in `abterms` specified
as strings will have all their properties other than `field` taken from `termobj_for_string_terms`.
]==]
function export.canonicalize_termobj_list(abterms, field, origin, originating_term, termobj_for_string_terms)
if abterms == nil then
return nil
end
local function make_termobj_from_string_term(term)
local retval
if termobj_for_string_terms then
retval = shallow_copy(termobj_for_string_terms)
else
retval = {}
end
retval[field] = term
retval._origin = origin
retval._originating_term = originating_term
return retval
end
local function shallow_copy_and_add_origin(termobj)
termobj = shallow_copy(termobj)
termobj._origin = origin
termobj._originating_term = originating_term
return termobj
end
field = field or "term"
if type(abterms) == "string" then
return {make_termobj_from_string_term(abterms)}
elseif not abterms[1] then
if origin ~= nil or originating_term ~= nil then
abterms = shallow_copy_and_add_origin(abterms)
end
return {abterms}
else
-- Check if already in full list term and return directly if so (unless `origin_val` is given, in which case we
-- need to shallow-copy both the list and each term in it).
local must_convert = false
for _, term in ipairs(abterms) do
if type(term) == "string" then
must_convert = true
break
end
end
if not must_convert then
if origin ~= nil or originating_term ~= nil then
abterms = shallow_copy(abterms)
for i, abterm in ipairs(abterms) do
abterms[i] = shallow_copy_and_add_origin(abterm)
end
end
return abterms
end
end
local retval = {}
for _, term in ipairs(abterms) do
if type(term) == "string" then
insert(retval, make_termobj_from_string_term(term))
else
if origin ~= nil or originating_term ~= nil then
term = shallow_copy_and_add_origin(term)
end
insert(retval, term)
end
end
return retval
end
function export.canonicalize_category_list(abcats)
-- FIXME: We should allow category objects in `headdata.categories` and not just strings. This seems to require
-- changes to [[Module:headword]].
if abcats == nil then
return nil
end
if abcats == false then
return {}
end
if type(abcats) == "string" then
abcats = {abcats}
end
return abcats
end
--[==[
Insert a fixed inflection (a label not associated with any inflection values) into an `inflections` field. The
`inflections` field will be initialized if needed. `data` is an object with the following fields:
* `headdata`: The headword structure passed to [[Module:headword]]. Required.
* `inflobj`: The object whose `inflections` field the terms are inserted into. Defaults to `headdata`. Only needs
to be set for nested inflections, which are specified for an inflection object rather than the headword data
structure as a whole.
* `label`: The label that the inflections are given; any parts of the label surrounded in `<<...>>` are linked to the
glossary. (If the contents of `<<...>>` contain a `|` in them, they are a two-part link.) Required.
* `originating_term`: The term object from which this label is derived. If specified, decorations will be taken from
this object.
]==]
function export.insert_fixed_inflection(data)
local headdata, origterm, label = data.headdata, data.originating_term, data.label
local inflobj = data.inflobj or headdata
inflobj.inflections = inflobj.inflections or {}
if not origterm then
insert(inflobj.inflections, {
label = export.replace_glossary_links_in_label(label)
})
else
if origterm.id then
error(("It doesn't make sense to pass in an ID '%s' for label '%s' in conjunction with a term value '%s'"
):format(origterm.id, label, origterm.term))
end
origterm = shallow_copy(origterm)
-- Preserve decorations
origterm.term = nil
origterm.label = export.replace_glossary_links_in_label(label)
insert(inflobj.inflections, origterm)
end
end
local function yes_and_no_cats(props)
if props.cats and props.no_cats then
local yes_cats = export.canonicalize_category_list(props.cats)
local no_cats = export.canonicalize_category_list(props.no_cats)
local cats = shallow_copy(yes_cats)
extend(cats, no_cats)
return cats
end
end
local function sometimes_or_yes_and_no_cats(props)
local sometimes_cats = export.canonicalize_category_list(props.sometimes_cats)
if sometimes_cats then
return sometimes_cats
else
return yes_and_no_cats(props)
end
end
local function no_value_fallback_label(props)
if props.fixed_adj_no_label then
return props.fixed_adj_no_label
end
if props.fixed_adj_label then
return "không " .. props.fixed_adj_label
end
return "không " .. (props.label_for_cats_and_modes or props.label)
end
local function with_value_fallback_label(props)
if props.fixed_adj_label then
return props.fixed_adj_label
end
-- FIXME, should this say 'with a plural/diminutive' etc. or is 'with plural/diminutive' enough?
return "với " .. (props.label_for_cats_and_modes or props.label)
end
--[==[ var:
Table describing how to handle the "mode" signals at the beginning of an inflection, generally indicating whether the
inflection exists and to what extent. The key is the mode signal itself, and the value is table with the following keys:
* `exists`: The value to return in the `exists` field of the object returned by `process_for_insertion`. This is
currently used in calling code simply to check for different modes; it is an easier-to-read equivalent of the mode
signal itself.
* `prefix`: The prefix used to directly specify the label and category/categories associated with the mode in calling
code. For example, the mode `--+` (which also arises when `-` is used with a following term) has prefix `usually_no_`,
which means that to directly specify its label, use `usually_no_label` and to directly specify its categories, use
`usually_no_cats`. If these fields don't exist, the functions in `fallback_label` and `fallback_cats` are used to
generate the label and categories.
* `fallback_label`: This is a function of one argument, `props` (the properties passed in by the caller), which should
generate the label if the user didn't directly specify a label (see `prefix` above).
* `fallback_cats`: This is a function of one argument, `props` (the properties passed in by the caller), which should
generate the categories if the user didn't directly specify them (see `prefix` above).
* `add_default`: If true, the code will attempt to add a default value if the user specifies this mode without giving
additional terms. This depends on the caller passing in a `mode_default` property (which can be a string, a term
object, a list of either, or a function generating any of those values). In many cases the `mode_default` property
may simply have a value such as {"+"}, which is processed later by resolve_special().
* `disallow_additional`: If true, no additional terms are allowed after the mode signal; if present, an error is
thrown.
]==]
export.special_modes = {
["-"] = {exists = "không", prefix = "no_",
fallback_label = no_value_fallback_label,
}, -- converted to "--+" if a term follows
["--+"] = {exists = "usually no", prefix = "usually_no_", add_default = true,
fallback_label = function(props)
if props.no_label then
return "thường " .. props.no_label
end
return "thường " .. no_value_fallback_label(props)
end,
fallback_cats = sometimes_or_yes_and_no_cats,
},
["+-"] = {exists = "đôi khi", prefix = "sometimes_", add_default = true,
fallback_label = function(props)
return "đôi khi " .. with_value_fallback_label(props)
end,
fallback_cats = yes_and_no_cats,
},
["++-"] = {exists = "thường", prefix = "usually_", add_default = true,
fallback_label = function(props)
return "đôi khi " .. with_value_fallback_label(props)
end,
fallback_cats = sometimes_or_yes_and_no_cats,
},
["!"] = {exists = "chưa được chứng thực", prefix = "unattested_", disallow_additional = true,
fallback_label = function(props)
return (props.label_for_cats_and_modes or props.label) .. " chưa được chứng thực"
end,
-- No default cats; provided for plurals in inflection_to_cats_and_label.
},
["?"] = {exists = "unknown", prefix = "unknown_", disallow_additional = true,
-- No default label as people have preferred not to see 'plural unknown or uncertain'
-- written out. No default cats; provided for plurals in inflection_to_cats_and_label.
},
}
function export.process_for_insertion(data)
local terms, mode_default, headdata = data.terms, data.mode_default, data.headdata
local retval = {
headdata = data.headdata,
}
if terms and terms[1] then
local mode = terms[1].term
local modespec = export.special_modes[mode]
if modespec then
terms = shallow_copy(terms)
retval.mode_term = terms[1]
remove(terms, 1)
if mode == "-" and terms[1] then
mode = "--+"
end
if terms[1] and modespec.disallow_additional then
error(("It doesn't make sense to specify explicit terms along with mode signal %s (%s)"):format(
mode, modespec.exists))
elseif not terms[1] and modespec.add_default and mode_default then
if type(mode_default) == "function" then
mode_default = mode_default(headdata, retval.mode_term)
end
if mode_default then
terms = export.canonicalize_termobj_list(mode_default, "term", "mode_default", retval.mode_term)
end
end
retval.numterms = #terms
retval.terms = terms
retval.mode = mode
retval.exists = modespec.exists
retval.prefix = modespec.prefix
else
retval.numterms = #terms
retval.terms = terms
retval.exists = "yes"
retval.prefix = ""
end
else
retval.numterms = 0
retval.terms = {}
-- retval.exists = nil
retval.request = data.request
end
return retval
end
--[==[
Insert previously-parsed terms into an `inflections` field. The `inflections` field will be initialized if needed.
`data` is an object with the following fields:
* `headdata`: The headword structure passed to [[Module:headword]]. Required.
* `inflobj`: The object whose `inflections` field the terms are inserted into. Defaults to `headdata`. Only needs
to be set for nested inflections, which are specified for an inflection object rather than the headword data
structure as a whole.
* `terms`: The list of parsed terms. If {nil} or omitted, nothing happens unless `request` is set.
* `label`: The label that the inflections are given; any parts of the label surrounded in `<<...>>` are linked to the
glossary. (If the contents of `<<...>>` contain a `|` in them, they are a two-part link.) Required.
* `fixed_adj_label`: If specified, an adjective used to construct the defaults for the fixed labels specified by
`no_label`, `usually_no_label` and `sometimes_label`. Examples are "comparable" for a comparative and "countable"
for a plural.
* `no_label`: If the term is {"-"} and there are no other terms, insert a fixed label with this value. If
`fixed_adj_label` is given, defaults to {"not "} plus the adjective, else {"no "} plus the label.
* `usually_no_label`: If the term is {"-"} and there are other terms, insert a fixed label with this value. Defaults to
{"usually "} plus the value of `no_label`.
* `sometimes_label`: If the term is {"+-"}, insert a fixed label with this value. If `fixed_adj_label` is given,
defaults to {"sometimes "} plus the adjective, else {"sometimes with a(n) "} plus the label.
* `cats`: List of categories to insert when terms are given that are not {"-"}. Each category is a string naming a full
category to insert (including the appropriate language name prefixed).
* `no_cats`: List of categories to insert when a term is given as {"-"}.
* `usually_no_cats`: List of categories to insert when a term is given as {"-"} and additional terms are specified as
well (representing, e.g. for the inflection {"plural"}, a term which usually has no plural but does under some
circumstances). If omitted, both the categories in `cats` and `no_cats` are inserted.
* `sometimes_cats`: List of categories to insert when a term is given as {"+-"} (indicating that the inflection
sometimes exists and sometimes does not, normally depending on the precise sense involved). If omitted, both the
categories in `cats` and `no_cats` are inserted (same default as for `usually_no_cats`).
* `accel`: If specified, a full accelerator object to add to the inflections.
* `request`: If specified and no terms are given, insert a label with a request for inflections to be given.
* `enable_auto_translit`: If specified and terms are given, display automatic transliteration of the terms.
The return value indicates whether the inflection exists and how many terms are in it. It is an object with the
following fields:
* `exists`: {"yes"} if one or more terms were specified; {"no"} if the value was given as {"-"}; {"usually no"} if
the first value was given as {"-"} but additional terms were supplied; otherwise {nil}, indicating that the status
is unspecified.
* `numterms`: Number of terms in the inflection. Will be 0 unless `exists` has the value {"yes"} or {"usually no"}.
* `terms`: The terms that were inserted. If there are no terms, this is an empty list.
* `request`: True if no terms were specified but a term request was inserted into the inflection (because
`data.request` was specified). Otherwise {nil}.
]==]
function export.insert_processed_inflection(data)
local headdata, processed, label = data.headdata, data.processed, data.label
local inflobj = data.inflobj or headdata
local accel = data.accel
if data.accel_form then
if accel then
error("Internal error: can't specify both data.accel and data.accel_form")
end
if headdata.heads then
local lemmas = {}
local lemma_translits = {}
for i, headobj in ipairs(headdata.heads) do
lemmas[i] = headobj.term
if lemmas[i] == "+" then
error("Internal error: If you use data.accel_form, you should have resolved all occurrences of + in heads appropriately")
end
lemma_translits[i] = headobj.tr
end
accel = {
lemma = lemmas,
lemma_translit = lemma_translits,
form = data.accel_form,
}
else
accel = {
form = data.accel_form,
}
end
end
local function insert_cats(cats)
for _, cat in ipairs(cats) do
insert(headdata.categories, cat)
end
end
-- Handle the fixed label (if any) and cats (if any) associated with a mode.
if processed.mode then
local modespec = export.special_modes[processed.mode]
local mode_prefix = modespec.prefix
local fixed_label = data[mode_prefix .. "label"]
if not fixed_label and modespec.fallback_label then
fixed_label = modespec.fallback_label(data)
end
if fixed_label then
export.insert_fixed_inflection {
headdata = headdata,
inflobj = inflobj,
originating_term = processed.mode_term,
label = fixed_label,
}
end
local mode_cats = data[mode_prefix .. "cats"]
if not mode_cats and modespec.fallback_cats then
mode_cats = modespec.fallback_cats(data)
end
mode_cats = export.canonicalize_category_list(mode_cats)
if mode_cats then
insert_cats(mode_cats)
end
elseif processed.terms[1] and data.cats then
-- If no mode, no fixed label but there may be categories.
insert_cats(data.cats)
end
local terms = processed.terms
if terms[1] then
-- FIXME, if there's a mode we shallow_copy() twice.
terms = shallow_copy(terms)
terms.label = export.replace_glossary_links_in_label(label)
if accel then
terms.accel = accel
end
terms.enable_auto_translit = data.enable_auto_translit
inflobj.inflections = inflobj.inflections or {}
insert(inflobj.inflections, terms)
elseif processed.request then
inflobj.inflections = inflobj.inflections or {}
insert(inflobj.inflections, {
label = export.replace_glossary_links_in_label(label),
request = true,
})
end
end
--[==[
Insert previously-parsed terms into an `inflections` field. The `inflections` field will be initialized if needed.
`data` is an object with the following fields:
* `headdata`: The headword structure passed to [[Module:headword]]. Required.
* `inflobj`: The object whose `inflections` field the terms are inserted into. Defaults to `headdata`. Only needs
to be set for nested inflections, which are specified for an inflection object rather than the headword data
structure as a whole.
* `terms`: The list of parsed terms. If {nil} or omitted, nothing happens unless `request` is set.
* `label`: The label that the inflections are given; any parts of the label surrounded in `<<...>>` are linked to the
glossary. (If the contents of `<<...>>` contain a `|` in them, they are a two-part link.) Required.
* `fixed_adj_label`: If specified, an adjective used to construct the defaults for the fixed labels specified by
`no_label`, `usually_no_label` and `sometimes_label`. Examples are "comparable" for a comparative and "countable"
for a plural.
* `no_label`: If the term is {"-"} and there are no other terms, insert a fixed label with this value. If
`fixed_adj_label` is given, defaults to {"not "} plus the adjective, else {"no "} plus the label.
* `usually_no_label`: If the term is {"-"} and there are other terms, insert a fixed label with this value. Defaults to
{"usually "} plus the value of `no_label`.
* `sometimes_label`: If the term is {"+-"}, insert a fixed label with this value. If `fixed_adj_label` is given,
defaults to {"sometimes "} plus the adjective, else {"sometimes with a(n) "} plus the label.
* `cats`: List of categories to insert when terms are given that are not {"-"}. Each category is a string naming a full
category to insert (including the appropriate language name prefixed).
* `no_cats`: List of categories to insert when a term is given as {"-"}.
* `usually_no_cats`: List of categories to insert when a term is given as {"-"} and additional terms are specified as
well (representing, e.g. for the inflection {"plural"}, a term which usually has no plural but does under some
circumstances). If omitted, both the categories in `cats` and `no_cats` are inserted.
* `sometimes_cats`: List of categories to insert when a term is given as {"+-"} (indicating that the inflection
sometimes exists and sometimes does not, normally depending on the precise sense involved). If omitted, both the
categories in `cats` and `no_cats` are inserted (same default as for `usually_no_cats`).
* `accel`: If specified, a full accelerator object to add to the inflections.
* `request`: If specified and no terms are given, insert a label with a request for inflections to be given.
* `enable_auto_translit`: If specified and terms are given, display automatic transliteration of the terms.
The return value indicates whether the inflection exists and how many terms are in it. It is an object with the
following fields:
* `exists`: {"yes"} if one or more terms were specified; {"no"} if the value was given as {"-"}; {"usually no"} if
the first value was given as {"-"} but additional terms were supplied; otherwise {nil}, indicating that the status
is unspecified.
* `numterms`: Number of terms in the inflection. Will be 0 unless `exists` has the value {"yes"} or {"usually no"}.
* `terms`: The terms that were inserted. If there are no terms, this is an empty list.
* `request`: True if no terms were specified but a term request was inserted into the inflection (because
`data.request` was specified). Otherwise {nil}.
]==]
function export.insert_inflection(data)
local headdata, terms, mode_default = data.headdata, data.terms, data.mode_default
local processed = export.process_for_insertion {
headdata = headdata,
terms = terms,
mode_default = mode_default,
}
data = shallow_copy(data)
data.processed = processed
export.insert_processed_inflection(data)
return processed
end
--[==[
Parse raw arguments from `forms` for inline modifiers, and insert the resulting terms (which should not require
significant additional processing) into `headdata.inflections`. `data` is an object with the following fields:
* `forms`: The list of raw values to parse. If {nil} or omitted, nothing happens.
* `headdata`: The headword structure passed to [[Module:headword]]. Required.
* `paramname`: As in `parse_term_list_with_modifiers()`. Required.
* `label`: As in `insert_inflection()`. Required.
* `qualifiers`, `frob`, `include_mods`, `exclude_mods`, `is_head`, `splitchar`, `preserve_splitchar`, `delimiter_key`,
`escape_fun`, `unescape_fun`, `pre_normalize_modifiers`: As in `parse_term_list_with_modifiers()`.
* `accel`: As in `insert_inflection()`.
Return value is as in `insert_inflection()`.
]==]
function export.parse_and_insert_inflection(data)
local forms = data.forms
if forms and forms[1] then
data = shallow_copy(data)
data.forms = forms
data.terms = export.parse_term_list_with_modifiers(data)
return export.insert_inflection(data)
end
return {
numterms = 0
}
end
--[==[
Combine two sets of decorations. If either is {nil}, just return the other, and if both are {nil}, return {nil}.
]==]
function export.combine_decorations(decs1, decs2)
if not decs1 and not decs2 then
return nil
end
if not decs1 then
return decs2
end
if not decs2 then
return decs1
end
local combined = shallow_copy(decs1)
for _, dec in ipairs(decs2) do
insert_if_not(combined, dec)
end
return combined
end
function export.combine_qualifiers_or_labels(...)
-- FIXME: Đã thêm vào ngày 17 tháng 9 năm 2026.
error("Vui lòng dùng combine_decorations")
end
--[==[
Combine the decorations (qualifiers, labels, references) and ID's of two term objects. `destobj` is the "destination
term object" into which the combined properties are written, and `srcobj` is the "source object" into which the
properties are merged. `destobj` is side-effected (but the lists inside of `destobj` are not); if this is undesirable,
make sure to shallow-copy `destobj` first. If both objects have values for a given decoration, the values of `destobj`
come first. If both objects have a value for `id`, the values must match or an error is thrown; otherwise, the resulting
value of `id` comes from whichever one is defined.
'''NOTE:''' This may not be the correct behavior when deduplicating a list of term objects. See
`insert_termobj_combining_duplicates` for a different approach.
]==]
function export.combine_termobj_decorations(destobj, srcobj)
destobj.q = export.combine_decorations(destobj.q, srcobj.q)
destobj.qq = export.combine_decorations(destobj.qq, srcobj.qq)
destobj.l = export.combine_decorations(destobj.l, srcobj.l)
destobj.ll = export.combine_decorations(destobj.ll, srcobj.ll)
destobj.refs = export.combine_decorations(destobj.refs, srcobj.refs)
if destobj.id and srcobj.id and destobj.id ~= srcobj.id then
-- FIXME: We probably want to pass in an error function
error(("Can't specify two different ID's %s and %s when combining objects"):format(srcobj.id, destobj.id))
end
destobj.id = destobj.id or srcobj.id
return destobj
end
function export.combine_termobj_qualifiers_labels(...)
-- FIXME: Đã thêm vào ngày 17 tháng 9 năm 2026.
error("Vui lòng dùng combine_termobj_decorations")
end
function export.termobj_has_decorations(obj)
return obj.q and obj.q[1] or obj.qq and obj.qq[1] or obj.l and obj.l[1] or obj.ll and obj.ll[1] or
obj.refs and obj.refs[1]
end
function export.termobj_has_qualifiers_or_labels(...)
-- FIXME: Đã thêm vào ngày 17 tháng 9 năm 2026.
error("Vui lòng dùng termobj_has_decorations")
end
local function one_decoration_equal(prop1, prop2)
local prop1_is_nil = not prop1 or not prop1[1]
local prop2_is_nil = not prop2 or not prop2[1]
if prop1_is_nil and prop2_is_nil then
return true
end
if prop1_is_nil or prop2_is_nil then
return false
end
return deep_equals(prop1, prop2)
end
function export.termobj_decorations_equal(obj1, obj2)
return one_decoration_equal(obj1.q, obj2.q) and
one_decoration_equal(obj1.qq, obj2.qq) and
one_decoration_equal(obj1.l, obj2.l) and
one_decoration_equal(obj1.ll, obj2.ll) and
one_decoration_equal(obj1.refs, obj2.refs) and
obj1.id == obj2.id
end
function export.termobj_ancillary_properties_equal(...)
-- FIXME: Đã thêm vào ngày 17 tháng 9 năm 2026.
error("Vui lòng dùng termobj_decorations_equal")
end
function export.convert_termobj_to_formobj(termobj)
local formobj = {
form = termobj.term,
translit = termobj.tr,
}
local footnotes
local function mods_to_footnote(mod_prefix, mod_vals)
if mod_vals and mod_vals[1] then
footnotes = footnotes or {}
for _, val in ipairs(mod_vals) do
insert(footnotes, "[" .. mod_prefix .. ":" .. val .. "]")
end
end
end
mods_to_footnote("q", termobj.q)
mods_to_footnote("qq", termobj.qq)
mods_to_footnote("l", termobj.l)
mods_to_footnote("ll", termobj.ll)
mods_to_footnote("ref", termobj.refs)
mods_to_footnote("id", termobj.id and {termobj.id} or nil)
formobj.footnotes = footnotes
return formobj
end
local recognized_multi_mods = {
q = "q",
qq = "qq",
l = "l",
ll = "ll",
ref = "refs",
}
local recognized_single_mods = {
id = "id",
}
function export.add_footnote_to_termobj(termobj, footnote)
local stripped_footnote = footnote:match("^%[(.*)%]$")
if not stripped_footnote then
error("Internal error: Footnote should be surrounded by brackets at this stage: " .. footnote)
end
local prefix, rest = stripped_footnote:match("^([a-z]+):(.+)$")
local field, is_multi
if prefix then
if recognized_multi_mods[prefix] then
field = recognized_multi_mods[prefix]
is_multi = true
elseif recognized_single_mods[prefix] then
field = recognized_single_mods[prefix]
is_multi = false
end
end
if not field then
rest = stripped_footnote
field = "l"
is_multi = true
end
if is_multi then
if not termobj[field] then
termobj[field] = {}
end
insert(termobj[field], rest)
else
if termobj[field] and termobj[field] ~= rest then
error(("Can't set two values for '%s': '%s' and '%s'"):format(field, termobj[field], rest))
end
termobj[field] = rest
end
end
function export.convert_formobj_to_termobj(formobj)
local termobj = {
term = formobj.form,
tr = formobj.translit,
}
if formobj.footnotes then
for _, footnote in ipairs(formobj.footnotes) do
export.add_footnote_to_termobj(termobj, footnote)
end
end
return termobj
end
local function extract_termobj_field_modifiers(fieldval)
return fieldval:match("^([*+]?)(.*)$")
end
function export.remove_termobj_field_modifiers(termobj)
local function remove_field_modifiers(field)
if termobj[field] and termobj[field][1] then
local any_field_modifiers = false
for _, val in ipairs(termobj[field]) do
local field_mods, _ = extract_termobj_field_modifiers(val)
if field_mods ~= "" then
any_field_modifiers = true
break
end
end
local new_field = {}
if any_field_modifiers then
for _, val in ipairs(termobj[field]) do
local _, field_without_mods = extract_termobj_field_modifiers(val)
insert_if_not(new_field, field_without_mods)
end
termobj[field] = new_field
end
end
end
remove_field_modifiers("q")
remove_field_modifiers("qq")
remove_field_modifiers("l")
remove_field_modifiers("ll")
remove_field_modifiers("refs")
end
function export.insert_termobj_combining_duplicates(destobjs, termobj)
for _, destobj in ipairs(destobjs) do
if destobj.term == termobj.term and destobj.tr == termobj.tr then
-- Form already present; maybe combine footnotes.
local function combine_field_values(field)
if termobj[field] and termobj[field][1] then
-- Check to see if there are existing values with *; if so, remove them.
if destobj[field] and destobj[field][1] then
local any_values_with_asterisk = false
for _, val in ipairs(destobj[field]) do
local field_mods, _ = extract_termobj_field_modifiers(val)
if field_mods:find("%*") then
any_values_with_asterisk = true
break
end
end
if any_values_with_asterisk then
local filtered_values = {}
for _, val in ipairs(destobj[field]) do
local field_mods, _ = extract_termobj_field_modifiers(val)
if not field_mods:find("%*") then
insert(filtered_values, val)
end
end
if filtered_values[1] then
destobj[field] = filtered_values
else
destobj[field] = nil
end
end
end
local any_footnotes_with_plus = false
for _, val in ipairs(termobj[field]) do
local field_mods, _ = extract_termobj_field_modifiers(val)
if field_mods:find("%+") then
any_footnotes_with_plus = true
break
end
end
if any_footnotes_with_plus then
if not destobj[field] then
destobj[field] = {}
else
destobj[field] = shallow_copy(destobj[field])
end
for _, val in ipairs(termobj[field]) do
local already_seen = false
local field_mods, field_without_mods = extract_termobj_field_modifiers(val)
if field_mods:find("%+") then
for _, existing_val in ipairs(destobj[field]) do
local _, existing_field_without_mods =
extract_termobj_field_modifiers(existing_val)
if existing_field_without_mods == field_without_mods then
already_seen = true
break
end
end
if not already_seen then
insert(destobj[field], val)
end
end
end
end
end
end
combine_field_values("q")
combine_field_values("qq")
combine_field_values("l")
combine_field_values("ll")
combine_field_values("refs")
if destobj.id and termobj.id and destobj.id ~= termobj.id then
-- FIXME: We probably want to pass in an error function
error(("Can't specify two different ID's %s and %s when combining objects"):format(termobj.id, destobj.id))
end
destobj.id = destobj.id or termobj.id
return
end
end
insert(destobjs, termobj)
end
export.allowed_special_indicators = {
["first"] = true,
["first-second"] = true,
["first-last"] = true,
["second"] = true,
["last"] = true,
["each"] = true,
["+"] = true, -- requests the default behavior with preposition handling
}
--[==[
Check for special indicators (values such as {"+first"} or {"+first-last"} that are used in a `pl`, `f`, etc. argument
and indicate how to inflect a multiword term). If `form` is such an indicator, the return value is `form` minus
the initial `+` sign; otherwise, if form begins with a `+` sign, an error is thrown; otherwise the return value is nil.
]==]
function export.get_special_indicator(form, noerror)
if form:find("^%+") then
form = form:gsub("^%+", "")
if not export.allowed_special_indicators[form] then
if noerror then
return nil
end
local indicators = {}
for indic, _ in pairs(export.allowed_special_indicators) do
insert(indicators, "+" .. indic)
end
sort(indicators)
error("Special inflection indicator beginning with '+' can only be " ..
mw.text.listToText(indicators) .. ": +" .. form)
end
return form
end
return nil
end
local function add_endings(bases, endings)
local retval = {}
if type(bases) ~= "table" then
bases = {bases}
end
if type(endings) ~= "table" then
endings = {endings}
end
for _, base in ipairs(bases) do
for _, ending in ipairs(endings) do
insert(retval, base .. ending)
end
end
return retval
end
--[==[
Inflect a possibly multiword or hyphenated term `form` using the function `inflect`, which is a function of one argument
that is called on a single word to inflect and should return either the inflected word or a list of inflected words.
`special` indicates how to inflect the multiword term and should be e.g. {"first"} to inflect only the first word,
{"first-last"} to inflect the first and last words, {"each"} to inflect each word, etc. See `allowed_special_indicators`
above for the possibilities. If `special` is `+`, or is omitted and the term is multiword (i.e. containing a space
character), and `prepositions` is supplied, the function checks for multiword or hyphenated terms containing the
prepositions in `prepositions`, e.g. Italian [[senso di marcia]] or [[medaglia d'oro]] or Portuguese
[[tartaruga-do-mar]]. If such a term is found, only the first word is inflected. Otherwise, the default is
{"first-last"}. `prepositions` is a list of Lua patterns matching prepositions. The patterns will automatically have the
separator character (space or hyphen) added to the left side but not the right side, so they should contain a space
character (which will automatically be converted to the appropriate separator) on the right side unless the preposition
is joined on the right side with an apostrophe. Examples of preposition patterns for Italian are {"di "}, {"sull'"} and
{"d?all[oae] "} (which matches {"dallo "}, {"dalle "}, {"alla "}, etc.).
The return value is always either a list of inflected multiword or hyphenated terms, or nil if `special` is omitted
and `form` is not multiword. (If `special` is specified and `form` is not multiword or hyphenated, an error results.)
]==]
function export.handle_multiword(form, special, inflect, prepositions, sep)
sep = sep or form:find(" ") and " " or "%-"
local raw_sep = sep == " " and " " or "-"
-- Used to add regex version of separator in the replacement portion of ugsub() or :gsub()
local sep_replacement = sep == " " and " " or "%%-"
-- Given a Lua pattern, replace space with the appropriate separator.
local function hack_re(re)
if sep == " " then
return re
end
return (re:gsub(" ", sep_replacement))
end
if special == "first" then
local first, rest = form:match(hack_re("^(.-)( .*)$"))
if not first then
error("Special indicator 'first' can only be used with a multiword term: " .. form)
end
return add_endings(inflect(first), rest)
elseif special == "second" then
local first, second, rest = form:match(hack_re("^([^ ]+ )([^ ]+)( .*)$"))
if not first then
error("Special indicator 'second' can only be used with a term with three or more words: " .. form)
end
return add_endings(add_endings({first}, inflect(second)), rest)
elseif special == "first-second" then
local first, space, second, rest = form:match(hack_re("^([^ ]+)( )([^ ]+)( .*)$"))
if not first then
error("Special indicator 'first-second' can only be used with a term with three or more words: " .. form)
end
return add_endings(add_endings(add_endings(inflect(first), space), inflect(second)), rest)
elseif special == "each" then
local terms = split(form, sep)
if #terms < 2 then
error("Special indicator 'each' can only be used with a multiword term: " .. form)
end
for i, term in ipairs(terms) do
terms[i] = inflect(term)
if i > 1 then
terms[i] = add_endings(raw_sep, terms[i])
end
end
local result = ""
for _, term in ipairs(terms) do
result = add_endings(result, term)
end
return result
elseif special == "first-last" then
local first, middle, last = form:match(hack_re("^(.-)( .* )(.-)$"))
if not first then
first, middle, last = form:match(hack_re("^(.-)( )(.*)$"))
end
if not first then
error("Special indicator 'first-last' can only be used with a multiword term: " .. form)
end
return add_endings(add_endings(inflect(first), middle), inflect(last))
elseif special == "last" then
local rest, last = form:match(hack_re("^(.* )(.-)$"))
if not rest then
error("Special indicator 'last' can only be used with a multiword term: " .. form)
end
return add_endings(rest, inflect(last))
elseif special and special ~= "+" then
error("Unrecognized special=" .. special)
end
-- Only do default behavior if special indicator '+' explicitly given or separator is space; otherwise we will
-- break existing behavior with hyphenated words.
if (special == "+" or sep == " ") and form:find(sep) then
if prepositions then
-- check for prepositions in the middle of the word; do it this way so we can handle
-- more than one word before the preposition (and usually inflect each word)
for _, prep in ipairs(prepositions) do
local first, space_prep_rest = umatch(form, hack_re("^(.-)( " .. prep .. ".*)$"))
if first then
return add_endings(inflect(first), space_prep_rest)
end
end
end
-- multiword or hyphenated expressions default to first-last; we need to pass in the separator to avoid
-- problems with multiword terms containing hyphens in the individual words
return export.handle_multiword(form, "first-last", inflect, prepositions, sep)
end
return nil
end
local function link_hyphen_split_component(word, data)
if data.link_hyphen_split_component then
return data.link_hyphen_split_component(word)
else
return "[[" .. word .. "]]"
end
end
-- Default function to split a word on apostrophes. Don't split apostrophes at the beginning or end of a word (e.g.
-- [['ndrangheta]] or [[po']]). Handle multiple apostrophes correctly, e.g. [[l'altr'ieri]] -> [[l']][altr']][[ieri]].
function export.default_split_apostrophe(word, data)
local apostrophe_parts = split(word, "'", true, true)
local linked_apostrophe_parts = {}
local apostrophes_at_beginning = ""
local i = 1
-- Apostrophes at beginning get attached to the first word after (which will always exist but may
-- be blank if the word consists only of apostrophes).
while i < #apostrophe_parts do -- <, not <=, in case the word consists only of apostrophes
local apostrophe_part = apostrophe_parts[i]
i = i + 1
if apostrophe_part == "" then
apostrophes_at_beginning = apostrophes_at_beginning .. "'"
else
break
end
end
apostrophe_parts[i] = apostrophes_at_beginning .. apostrophe_parts[i]
-- Now, do the remaining parts. A blank part indicates more than one apostrophe in a row; we join
-- all of them to the preceding word.
while i <= #apostrophe_parts do
local apostrophe_part = apostrophe_parts[i]
if apostrophe_part == "" then
linked_apostrophe_parts[#linked_apostrophe_parts] =
linked_apostrophe_parts[#linked_apostrophe_parts] .. "'"
elseif i == #apostrophe_parts then
insert(linked_apostrophe_parts, apostrophe_part)
else
insert(linked_apostrophe_parts, apostrophe_part .. "'")
end
i = i + 1
end
for j, tolink in ipairs(linked_apostrophe_parts) do
linked_apostrophe_parts[j] = link_hyphen_split_component(tolink, data)
end
return concat(linked_apostrophe_parts)
end
--[=[
Auto-add links to a word that should not have spaces but may have hyphens and/or apostrophes. We split off final
punctuation, then split on hyphens if `data.split_hyphen` is given, and also split on apostrophes if
`data.split_apostrophe` is given. We only split on hyphens if they are in the middle of the word, not at the beginning
or end (hyphens at the beginning or end indicate suffixes or prefixes, respectively). `include_hyphen_prefixes`, if
given, is a set of prefixes (not including the final hyphen) where we should include the final hyphen in the prefix.
Hence, e.g. if "anti" is in the set, a Portuguese word like [[anti-herói]] "anti-hero" will be split [[anti-]][[herói]]
(whereas a word like [[código-fonte]] "source code" will be split as [[código]]-[[fonte]]).
If `data.split_apostrophe` is specified, we split on apostrophes unless `data.no_split_apostrophe_words` is given and
the word is in the specified set, such as French [[c'est]] and [[quelqu'un]]. If `data.split_apostrophe` is true, the
default algorithm applies, which splits on all apostrophes except those at the beginning and end of a word (as in
Italian [['ndrangheta]] or [[po']]), and includes the apostrophe in the link to its left (so we auto-split French
[[l'eau]] as [[l']][[eau]] and [[l'altr'ieri]] as [[l']][altr']][[ieri]]). If `data.split_apostrophe` is specified
but not `true`, it should be a function of one argument that does custom apostrophe-splitting. The argument is the word
to split, and the return value should be the split and linked word.
]=]
local function add_single_word_links(space_word, data, term_has_spaces)
local space_word_no_punct, punct
local punct_pattern = data.punctuation
if punct_pattern and is_callable(punct_pattern) then
space_word_no_punct, punct = punct_pattern(space_word)
else
if punct_pattern == nil then
punct_pattern = "[,;:?!]"
end
space_word_no_punct, punct = umatch(space_word, "^(.*)(" .. punct_pattern .. ")$")
end
space_word_no_punct = space_word_no_punct or space_word
punct = punct or ""
local words
if space_word_no_punct:sub(1, 1) == "-" or space_word_no_punct:sub(-1) == "-" then
-- don't split prefixes and suffixes
words = {space_word_no_punct}
else
local splitter
if term_has_spaces then
splitter = data.split_hyphen_when_space
else
splitter = data.split_hyphen_when_no_space
end
if is_callable(splitter) then
words = splitter(space_word_no_punct)
if type(words) == "string" then
return words .. punct
end
end
end
if not words then
local split_hyphen
if term_has_spaces then
split_hyphen = data.split_hyphen_when_space
else
split_hyphen = data.split_hyphen_when_no_space
if split_hyphen == nil then -- default to true; use `false` to avoid this
split_hyphen = true
end
end
if split_hyphen then
words = split(space_word_no_punct, "-", true, true)
else
words = {space_word_no_punct}
end
end
local linked_words = {}
for j, word in ipairs(words) do
if j < #words and data.include_hyphen_prefixes and data.include_hyphen_prefixes[word] then
word = "[[" .. word .. "-]]"
elseif j > 1 and data.include_hyphen_suffixes and data.include_hyphen_suffixes[word] then
word = "[[-" .. word .. "]]"
else
-- Don't split on apostrophes if the word is in `no_split_apostrophe_words`.
if (not data.no_split_apostrophe_words or not data.no_split_apostrophe_words[word]) and
data.split_apostrophe and word:find("'", nil, true) then
if data.split_apostrophe == true then
word = export.default_split_apostrophe(word, data)
else -- custom apostrophe splitter/linker
word = data.split_apostrophe(word)
end
elseif word ~= "" then -- avoid -[[]]- (e.g. f--k)
word = link_hyphen_split_component(word, data)
end
if j < #words then
word = word .. "-"
end
end
insert(linked_words, word)
end
return concat(linked_words) .. punct
end
--[=[
Auto-add links to a multiword term. `data` contains fields customizing how to do this. By default we proceed as follows:
(1) If the term already has embedded links in it, they are left unchanged.
(2) Otherwise, if there are spaces present, we split on spaces and link each word separately.
(3) If a given space-separated component ends in punctuation (defaulting to [,;:?!]), it is separated off, the remainder
of the algorithm run, and the punctuation pasted back on.
(4) If there are hyphens in a given space-separated component, we may link each hyphenated term separately depending
on the settings in `data`. Normally the hyphens are not included in the linked terms, but this can be overridden
for specific prefixes and/or suffixes. By default, if there are spaces in the multiword term, we do not link
hyphenated components (because of cases like "boire du petit-lait" where "petit-lait" should be linked as a whole),
but do so otherwise (e.g. for "avant-avant-hier"); this can overridden for cases like "croyez-le ou non".
Cases where only some of the hyphens should be split can always be handled by explicitly specifying the head (e.g.
"Nord-Pas-de-Calais" given as head=[[Nord]]-[[Pas-de-Calais]]).
(5) If there are apostrophes in a given component, we may link each apostrophe-separated term separately depending
on the settings in `data`, including the apostrophe in the link to its left (so we split "de l'eau" as
"[[de]] [[l']][[eau]]").
The settings in `data` are as follows:
`split_hyphen_when_no_space`: Whether to split on hyphens when the term has no spaces. Defaults to true if set to `nil`.
This can be a function of one argument, to implement a custom splitting algorithm for hyphen-separated terms. If
this returns [FIXME: FINISH ME ...]
If `data.split_apostrophe` is specified, we split on apostrophes unless `data.no_split_apostrophe_words` is given and
the word is in the specified set, such as French [[c'est]] and [[quelqu'un]]. If `data.split_apostrophe` is true, the
default algorithm applies, which splits on all apostrophes except those at the beginning and end of a word (as in
Italian [['ndrangheta]] or [[po']]), and includes the apostrophe in the link to its left (so we auto-split French
[[l'eau]] as [[l']][[eau]] and [[l'altr'ieri]] as [[l']][altr']][[ieri]]). If `data.split_apostrophe` is specified
but not `true`, it should be a function of one argument that does custom apostrophe-splitting. The argument is the word
to split, and the return value should be the split and linked word.
We don't always split on hyphens because of cases like "boire du petit-lait" where "petit-lait" should be linked as a
whole, but provide the option to do it for cases like "croyez-le ou non". If there's no space, however, then it makes
sense to split on hyphens by `no_split_apostrophe_words` and `include_hyphen_prefixes` allow for special-case handling
of particular words and are as described in the comment above add_single_word_links().
]=]
function export.add_links_to_multiword_term(term, data)
if term:match("[%[%]]") then
return term
end
local words = split(term, " ", true, true)
local term_has_spaces = #words > 1
local linked_words = {}
for _, word in ipairs(words) do
insert(linked_words, add_single_word_links(word, data, term_has_spaces))
end
local retval = concat(linked_words, " ")
-- If we ended up with a single link consisting of the entire term,
-- remove the link.
return retval:match("^%[%[([^%[%]]*)%]%]$") or retval
end
local function canonicalize_begin_end_spec(spec)
local from, to = spec:match("^(.-):(.*)$")
if not from then
from = spec
to = ""
end
return from, to
end
--[==[
Given a `linked_term` that is the output of add_links_to_multiword_term(), apply modifications as given in
`modifier_spec` to change the link destination of subterms (normally single-word non-lemma forms; sometimes
collections of adjacent words). This is usually used to link non-lemma forms to their corresponding lemma, but can
also be used to replace a span of adjacent separately-linked words to a single multiword lemma. The format of
`modifier_spec` is one or more semicolon-separated subterm specs, where each such spec is of the form
SUBTERM:DEST, where SUBTERM is one or more words in the `linked_term` but without brackets in them, and DEST is the
corresponding link destination to link the subterm to. Any occurrence of ~ in DEST is replaced with SUBTERM.
Alternatively, a single modifier spec can be of the form BEGIN[FROM:TO], which is equivalent to writing
BEGINFROM:BEGINTO (see example below).
For example, given the source phrase [[il bue che dice cornuto all'asino]] "the pot calling the kettle black"
(literally "the ox that calls the donkey horned/cuckolded"), the result of calling add_links_to_multiword_term()
is [[il]] [[bue]] [[che]] [[dice]] [[cornuto]] [[all']][[asino]]. With a modifier_spec of 'dice:dire', the result
is [[il]] [[bue]] [[che]] [[dire|dice]] [[cornuto]] [[all']][[asino]]. Here, based on the modifier spec, the
non-lemma form [[dice]] is replaced with the two-part link [[dire|dice]].
Another example: given the source phrase [[chi semina vento raccoglie tempesta]] "sow the wind, reap the whirlwind"
(literally (he) who sows wind gathers [the] tempest"). The result of calling add_links_to_multiword_term() is
[[chi]] [[semina]] [[vento]] [[raccoglie]] [[tempesta]], and with a modifier_spec of 'semina:~re; raccoglie:~re',
the result is [[chi]] [[seminare|semina]] [[vento]] [[raccogliere|raccoglie]] [[tempesta]]. Here we use the ~
notation to stand for the non-lemma form in the destination link.
A more complex example is [[se non hai altri moccoli puoi andare a letto al buio]], which becomes
[[se]] [[non]] [[hai]] [[altri]] [[moccoli]] [[puoi]] [[andare]] [[a]] [[letto]] [[al]] [[buio]] after calling
add_links_to_multiword_term(). With the following modifier_spec:
'hai:avere; altr[i:o]; moccol[i:o]; puoi: potere; andare a letto:~; al buio:~', the result of applying the spec is
[[se]] [[non]] [[avere|hai]] [[altro|altri]] [[moccolo|moccoli]] [[potere|puoi]] [[andare a letto]] [[al buio]].
Here, we rely on the alternative notation mentioned above for e.g. 'altr[i:o]', which is equivalent to 'altri:altro',
and link multiword subterms using e.g. 'andare a letto:~'. (The code knows how to handle multiword subexpressions
properly, and if the link text and destination are the same, only a single-part link is formed.)
]==]
function export.apply_link_modifiers(linked_term, modifier_spec, lang)
local split_modspecs = split(modifier_spec, "%s*;%s*")
for j, modspec in ipairs(split_modspecs) do
local id
if modspec:find("<") then
local rest
rest, id = modspec:match("^(.*)<id:(.-)>$")
if rest then
modspec = rest
end
end
local subterm, dest, otherlang
local begin_spec, rest, end_spec = modspec:match("^%[(.-)%]([^:]*)%[(.-)%]$")
if begin_spec then
local begin_from, begin_to = canonicalize_begin_end_spec(begin_spec)
local end_from, end_to = canonicalize_begin_end_spec(end_spec)
subterm = begin_from .. rest .. end_from
dest = begin_to .. rest .. end_to
end
if not subterm then
rest, end_spec = modspec:match("^([^:]*)%[(.-)%]$")
if rest then
local end_from, end_to = canonicalize_begin_end_spec(end_spec)
subterm = rest .. end_from
dest = rest .. end_to
end
end
if not subterm then
begin_spec, rest = modspec:match("^%[(.-)%]([^:]*)$")
if begin_spec then
local begin_from, begin_to = canonicalize_begin_end_spec(begin_spec)
subterm = begin_from .. rest
dest = begin_to .. rest
end
end
if not subterm then
subterm, dest = modspec:match("^(.-)%s*:%s*(.*)$")
if subterm and subterm ~= "^" and subterm ~= "$" then
local langdest
-- Parse off an initial language code (e.g. 'en:Higgs', 'la:minūtia' or 'grc:σκατός'). Also handle
-- Wikipedia prefixes ('w:Abatemarco' or 'w:it:Colle Val d'Elsa').
otherlang, langdest = dest:match("^([A-Za-z0-9._-]+):([^ ].*)$")
if otherlang == "w" then
local foreign_wikipedia, foreign_term = langdest:match("^([A-Za-z0-9._-]+):([^ ].*)$")
if foreign_wikipedia then
otherlang = otherlang .. ":" .. foreign_wikipedia
langdest = foreign_term
end
dest = ("%s:%s"):format(otherlang, langdest)
otherlang = nil
elseif otherlang then
otherlang = get_lang_by_code(otherlang, true, "allow etym")
dest = langdest
end
end
end
if not subterm then
if modspec == "?" or modspec == "!" then
subterm = "$"
dest = modspec
elseif modspec == "..." or modspec == "...?" then
subterm = "$"
dest = " " .. modspec
elseif modspec:find("^[A-Z]$") then
-- X, Y, etc. by themselves are unlinked, to help with snowclones
subterm = modspec
dest = "_"
else
subterm = modspec
dest = "~"
end
end
if subterm == "^" then
linked_term = dest:gsub("_", " ") .. linked_term
elseif subterm == "$" then
linked_term = linked_term .. dest:gsub("_", " ")
else
if subterm:find("[", nil, true) then
error(("Subterm '%s' in modifier spec '%s' cannot have brackets in it"):format(
escape_wikicode(subterm), escape_wikicode(modspec)))
end
local escaped_subterm = pattern_escape(subterm)
local subterm_re = "%[%[" .. escaped_subterm:gsub("(%%?[ ',%-])", "%%]*%1%%[*") .. "%]%]"
local expanded_dest
if dest:find("~", nil, true) then
expanded_dest = dest:gsub("~", replacement_escape(subterm))
else
expanded_dest = dest
end
if otherlang then
expanded_dest = expanded_dest .. "#" .. otherlang:getCanonicalName()
end
local subterm_replacement
if expanded_dest == "_" then
subterm_replacement = subterm
if id then
error("Can't supply <id:...> with an unlinked subterm")
end
if otherlang then
error("Can't supply prefixed language with an unlinked subterm")
end
elseif id or otherlang then
if id and expanded_dest:find("[", nil, true) then
error("Can't supply <id:...> with destination with embedded brackets")
end
subterm_replacement = require(links_module).language_link {
lang = otherlang or lang,
term = expanded_dest,
alt = subterm,
id = id,
}
elseif expanded_dest:find("[", nil, true) then
-- Use the destination directly if it has brackets in it (e.g. to put brackets around parts of a word).
subterm_replacement = expanded_dest
elseif expanded_dest == subterm then
subterm_replacement = "[[" .. subterm .. "]]"
else
subterm_replacement = "[[" .. expanded_dest .. "|" .. subterm .. "]]"
end
local escaped_subterm_replacement = replacement_escape(subterm_replacement)
local replaced_linked_term = ugsub(linked_term, subterm_re, escaped_subterm_replacement)
if replaced_linked_term == linked_term then
mw.log(("Attempted to replace %s with %s in %s"):format(subterm_re, escaped_subterm_replacement, linked_term))
error(("Subterm '%s' could not be located in %slinked expression %s, or replacement same as subterm"):format(
subterm, j > 1 and "intermediate " or "", escape_wikicode(linked_term)))
else
linked_term = replaced_linked_term
end
end
end
return linked_term
end
function export.format_validation_error_list(data)
local valid_list, valid_set, canonicalize = data.valid_list, data.valid_set, data.canonicalize
if not valid_list then
valid_list = {}
for valid_item, value in pairs(valid_set) do
if canonicalize then
if valid_item == value then
insert(valid_list, valid_item)
else
insert(valid_list, ("%s (mapped to %s)"):format(valid_item, value))
end
else
insert(valid_list, valid_item)
end
end
table.sort(valid_list)
end
return mw.text.listToText(valid_list, nil, " hoặc ")
end
function export.validation_error(data)
local item_type, item, valid_list, valid_set, canonicalize =
data.item_type, data.item, data.valid_list, data.valid_set, data.canonicalize
if not valid_list then
valid_list = {}
for valid_item, value in pairs(valid_set) do
if canonicalize then
if valid_item == value then
insert(valid_list, valid_item)
else
insert(valid_list, ("%s (mapped to %s)"):format(valid_item, value))
end
else
insert(valid_list, valid_item)
end
end
table.sort(valid_list)
end
error(("Invalid %s: %s; expected one of %s"):format(item_type, item,
export.format_validation_error_list {
valid_list = valid_list,
valid_set = valid_set,
canonicalize = canonicalize,
}
))
end
--[=[
Validate the items in `items` against the list or set of valid items in `valid_items`. If `field` is given, fetch the
item to check from that-named field of each object in `items`; otherwise use the items in `items` directly. If an error
occurs, `item_type` specifies the type of item to mention in the error message, which will also list the allowed items
(either taken directly from `valid_items` if a list, or from the sorted keys if a set).
If `canonicalize`, `valid_items` must be a set mapping items to their canonical equivalents, and the items in `items`
will be canonicalized accordingly and returned.
]=]
local function validate_items(data, canonicalize)
local items, field, valid_items, item_type =
data.items, data.field, data.valid_items, data.item_type
local valid_list, valid_set
if not canonicalize and valid_items[1] and is_array(valid_items) then
valid_list = valid_items
valid_set = list_to_set(valid_items)
else
valid_set = valid_items
end
for _, item in ipairs(items) do
if field then
item = item[field]
end
if not item._is_mode and not valid_set[item] then
export.validation_error {
item_type = item_type,
item = item,
valid_list = valid_list,
valid_set = not valid_list and valid_set or nil,
canonicalize = canonicalize,
}
end
end
end
function export.validate_genders(data)
local genders, valid_genders, gender_type, no_augment, canonicalize =
data.genders, data.valid_genders, data.gender_type, data.no_augment, data.canonicalize
if not genders then
return genders
end
gender_type = gender_type or "headword"
local valid_gender_set
if not data.canonicalize and valid_genders[1] and is_array(valid_genders) then
valid_gender_set = list_to_set(valid_genders)
else
valid_gender_set = valid_genders
end
local augmented_gender_set
if no_augment then
augmented_gender_set = valid_gender_set
else
augmented_gender_set = {}
for g, gval in pairs(valid_gender_set) do
augmented_gender_set[g] = gval
if g:find("^m") and not g:find("^mf") and valid_gender_set[g:gsub("^m", "f")] then
augmented_gender_set[g:gsub("^m", "mf")] = type(gval) == "string" and (gval:gsub("^m", "mf")) or gval
augmented_gender_set[g:gsub("^m", "mfbysense")] = type(gval) == "string" and (gval:gsub("^m", "mfbysense")) or gval
augmented_gender_set[g:gsub("^m", "mfequiv")] = type(gval) == "string" and (gval:gsub("^m", "mfequiv")) or gval
end
end
end
return validate_items {
items = genders,
field = "spec",
valid_items = augmented_gender_set,
item_type = ("%s gender"):format(gender_type),
canonicalize = canonicalize,
}
end
function export.has_plural_gender(genders)
local saw_p, saw_non_p
for _, val in ipairs(genders) do
local g = val.spec
if g:find("p$") then
saw_p = true
elseif g ~= "?" then
saw_non_p = true
end
end
return saw_p, saw_non_p
end
function export.is_plurale_tantum(genders)
local saw_p, saw_non_p = export.has_plural_gender(genders)
return saw_p and not saw_non_p
end
local Headdata = {}
function Headdata:resolve_prop(prop, ...)
if type(prop) == "function" then
prop = prop(self, ...)
end
return prop
end
function Headdata:get_pos_variants()
if self.process_props.pos_variants == nil then
self.process_props.pos_variants = export.get_pos_variants(self.pos_category)
end
return self.process_props.pos_variants
end
--[==[
Canonicalize a category. The category string will have the full language name (i.e. the name of the L2 language under
which an entry is inserted, which may a parent language if the language in question is an etymology-only language)
prepended to it, and any occurrences of `{plpos}` in the string replaced with the actual plural part of speech (with
some canonicalization; specifically, `proper nouns` is converted to `nouns` when replacing `{plpos}`). To specify a
full category and not have the language name prepended to it, precede it with {"Category:"}, which will be removed.
]==]
function Headdata:canonicalize_category(category)
if category:find("{") then
category = require(string_utilities_module).format(category, self:get_pos_variants())
end
if category:find("^Thể loại:") then
return (category:gsub("^Thể loại:", ""))
else
return category.. " " .. self.langfullname
end
end
--[==[
Canonicalize a list of categories according to the process described in `Headdata:canonicalize_category`. This simply
loops over each category in `categories` and calls `Headdata:canonicalize_category` on each one.
]==]
function Headdata:canonicalize_categories(categories)
if not categories then
return categories
end
categories = export.canonicalize_category_list(categories)
local canon_cats = {}
for _, cat in ipairs(categories) do
insert(canon_cats, self:canonicalize_category(cat))
end
return canon_cats
end
--[==[
Insert a category into the `categories` list in the headword `data` structure. `category` is normally a string naming
the category, which will have the full language name prepended to it and any occurrences of `{plpos}` in the string
replaced with the actual plural part of speech (with some canonicalization; specifically, `proper nouns` is converted to
`nouns` when replacing `{plpos}`). To specify a full category and not have the language name prepended to it, precede it
with {"Category:"}.
]==]
function Headdata:insert_category(category)
insert(self.categories, self:canonicalize_category(category))
end
--[==[
Validate the genders in `genders` (a list of gender spec objects, as produced by {type = "genders"} in
[[Module:parameters]] and accepted by [[Module:gender and number]]), checking that all specified genders are in the list
or set given in `valid_genders`. Optional `props` controls how the validation happens. In particular, unless
`props.no_augment` is given, then for any gender beginning with `m`, if a corresponding gender beginning with `f`
occurs, analogous genders beginning with `mf`, `mfbysense` and `mfequiv` are also allowed. For example, if `m-d`
(masculine dual) and `f-d` (feminine dual) both occur, genders `mf-d`, `mfbysense-d` and `mfequiv-d` are also allowed.
If a disallowed gender is given, an error occurs, giving the disallowed gender along with the list of all allowed
genders.
]==]
function Headdata:validate_genders(genders, valid_genders, props)
-- FIXME: This has no dependency on self. Uses should be converted to call export.validate_genders() directly.
if not genders then
return
end
props = props or {}
local gender_type, no_augment = props.gender_type, props.no_augment
export.validate_genders {
genders = genders,
valid_genders = valid_genders,
gender_type = gender_type,
no_augment = no_augment,
}
end
--[==[
Parse an inflection specified in `field`, the name of a parameter holding an inflection. If the parameter is numeric,
the field should be given as a number (as with the `params` structure passed to [[Module:parameters]]), not a string
containing the representation of a number. The field can specify multiple comma-separated terms, and each term can have
associated inline modifiers that will be parsed (unless there is top-level HTML in the parameter, i.e. HTML not
contained inside an inline modifier, e.g. as may be generated by using {{tl|l}} or similar template inside a parameter).
This is a wrapper around the top-level `parse_term_with_modifiers()` function. `props` is an optional structure
containing additional properties, including all additional properties documented for the top-level
`parse_term_with_modifiers()` function.
If the parameter in `field` is unspecified, the return value of this function will be an empty list, not {nil}, so it
is always safe to iterate over the return value.
By default, the allowed modifiers are the same as for `parse_term_with_modifiers()`, except that (normally) the
`<tr:...>` modifier will be allowed if `include_tr` was specified in the original call to `process_headword()`; likewise
for the `<ts:...>` modifier if `include_ts` was specified and the `<sc:...>` modifier if `include_sc` was specified. If
If you pass in your own `include_mods` list of additional allowed modifiers, it will (normally) automatically be
augmented with {"tr"}, {"ts"} and/or {"sc"} if `include_tr`, `include_ts` and/or `include_sc` was specified when calling
`process_headword()`. To disable automatic augmentation of these modifiers (whether or not you specify an `include_mods`
property), specify {no_augment_include_mods = true} in `props`.
]==]
function Headdata:parse_inflection(field, props)
local val = self.process_props.args[field]
if not val then
return {}
end
props = props and shallow_copy(props) or {}
local include_mods = props.include_mods
local calldata = self.process_props.calldata
if not props.no_augment_include_mods and (calldata.include_tr or calldata.include_ts or calldata.include_sc) then
include_mods = include_mods and shallow_copy(include_mods) or {}
if calldata.include_tr then
insert_if_not(include_mods, "tr")
end
if calldata.include_ts then
insert_if_not(include_mods, "ts")
end
if calldata.include_sc then
insert_if_not(include_mods, "sc")
end
end
props.val = val
props.paramname = field
props.splitchar = props.splitchar or ","
props.include_mods = include_mods
local orig_frob = props.frob
local function convert_tilde_to_pagename(term)
if orig_frob then
term = orig_frob(term)
end
if not term:find("~", nil, true) then
return term
end
term = term:gsub("\\~", "\1"):gsub("~", replacement_escape(self.pagename)):gsub("\1", "~")
return term
end
props.frob = convert_tilde_to_pagename
return export.parse_term_with_modifiers(props) or {}
end
function Headdata:process_for_insertion(terms, props)
props = props and shallow_copy(props) or {}
props.headdata = self
props.terms = terms
return export.process_for_insertion(props)
end
--[==[
Insert previously-parsed terms into the `inflections` of the headword `data` structure. This is a wrapper around
the top-level `insert_inflection()` function. `terms` is the list of parsed terms. (If {nil}, nothing happens unless
`request` is set in `props`.) `label` is the the label that the inflections are given; any parts of the label surrounded
in `<<...>>` are linked to the glossary. (If the contents of `<<...>>` contain a `|` in them, they are a two-part link.)
`props` is an optional structure containing additional properties, including all additional properties documented for
the top-level `insert_inflection()` function.
Unless `no_auto_cats` is given in `props`, certain labels automatically trigger the insertion of additional
categories in specific circumstances. This is controlled by the `inflection_to_cats_and_label` structure in
[[Module:headword utilities]]. For example, if the part of speech is {"nouns"} or {"proper nouns"} and the label (after
removing any links and `<<...>>` glossary specs) is {"plural"}, an additional category
<code><var>lang</var> countable nouns</code> will be added if a plural value is given (i.e. the value is not {"-"}). If
the value is {"-"} (which indicates that there is no plural and triggers the insertion of the fixed inflection label
{"no plural"}), <code><var>lang</var> uncountable nouns</code> will be inserted instead, and if both {"-"} and a value
are given (which triggers the insertion of the {"usually no plural"} fixed inflection label), both categories are added.
Similar categories are inserted when a comparative is given (with a label {"comparative"}), and if the label is
{"female equivalent"} or {"male equivalent"} and the value is not {"-"}, a category such as
<code><var>lang</var> nouns with other-gender equivalents</code> is inserted.
]==]
function Headdata:insert_processed_inflection(processed, label, props)
props = props and shallow_copy(props) or {}
if not props.no_auto_cats then
local bare_label = props.label_for_cats_and_modes or label
if bare_label:find("[[", nil, true) then
bare_label = require(links_module).remove_links(bare_label)
end
if bare_label:find("<<", nil, true) then
bare_label = bare_label:gsub("<<.-|(.-)>>", "%1"):gsub("<<(.-)>>", "%1")
end
local spec = inflection_to_cats_and_label[bare_label]
if spec and (not spec.filter_pos or spec.filter_pos(self:get_pos_variants())) then
-- Copy the properties other than filter_pos into `props`, but don't overwrite any existing values.
for specprop, specval in pairs(spec) do
if specprop ~= "filter_pos" and props[specprop] == nil then
props[specprop] = specval
end
end
end
end
-- Now resolve and canonicalize the properties.
local canon_props = {}
for prop, val in pairs(props) do
if prop ~= "no_auto_cats" and (prop == "cats" or prop:find("_cats$")) then
val = self:canonicalize_categories(self:resolve_prop(val))
elseif prop:find("_label$") then
val = self:resolve_prop(val)
end
canon_props[prop] = val
end
canon_props.headdata = self
canon_props.processed = processed
canon_props.label = label
export.insert_processed_inflection(canon_props)
return processed
end
--[==[
Insert previously-parsed terms into the `inflections` of the headword `data` structure. This is a wrapper around
the top-level `insert_inflection()` function. `terms` is the list of parsed terms. (If {nil}, nothing happens unless
`request` is set in `props`.) `label` is the the label that the inflections are given; any parts of the label surrounded
in `<<...>>` are linked to the glossary. (If the contents of `<<...>>` contain a `|` in them, they are a two-part link.)
`props` is an optional structure containing additional properties, including all additional properties documented for
the top-level `insert_inflection()` function.
Unless `no_auto_cats` is given in `props`, certain labels automatically trigger the insertion of additional
categories in specific circumstances. This is controlled by the `inflection_to_cats_and_label` structure in
[[Module:headword utilities]]. For example, if the part of speech is {"nouns"} or {"proper nouns"} and the label (after
removing any links and `<<...>>` glossary specs) is {"plural"}, an additional category
<code><var>lang</var> countable nouns</code> will be added if a plural value is given (i.e. the value is not {"-"}). If
the value is {"-"} (which indicates that there is no plural and triggers the insertion of the fixed inflection label
{"no plural"}), <code><var>lang</var> uncountable nouns</code> will be inserted instead, and if both {"-"} and a value
are given (which triggers the insertion of the {"usually no plural"} fixed inflection label), both categories are added.
Similar categories are inserted when a comparative is given (with a label {"comparative"}), and if the label is
{"female equivalent"} or {"male equivalent"} and the value is not {"-"}, a category such as
<code><var>lang</var> nouns with other-gender equivalents</code> is inserted.
]==]
function Headdata:insert_inflection(terms, label, props)
local processed = self:process_for_insertion(terms, props)
self:insert_processed_inflection(processed, label, props)
return processed
end
--[==[
Insert a "fixed" inflection (a label without associated values) into the `inflections` table of the headword `data`
structure, labeled according to `label` (which can have glossary links in it specified using `<<...>>`, exactly as for
`:insert_inflection()`). An example label (from {{tl|mn-noun}} in [[Module:mn-headword]]) is {"hidden-g declension"},
specifying that the noun belongs to the hidden-''g'' declension. This is a direct wrapper around the top-level function
`insert_fixed_inflection()`; see that function for more details on optional `props`.
]==]
function Headdata:insert_fixed_inflection(label, props)
props = props and shallow_copy(props) or {}
props.headdata = self
props.label = label
export.insert_fixed_inflection(props)
end
--[==[
Parse the inflection(s) specified in `field` and insert them into the `inflections` table of the headword `data`
structure, labeled according to `label`. This is equivalent to calling {terms = data:parse_inflection(field, props)}
followed by {return data:insert_inflection(terms, label, props)} and behaves the same as the combination of those two
functions. See their documentation for more details.
]==]
function Headdata:parse_and_insert_inflection(field, label, props)
local terms = self:parse_inflection(field, props)
return self:insert_inflection(terms, label, props)
end
--[==[
Generate an inflection that may be specified explicitly or defaulted (which involves looping over the specified or
defaulted heads and determining the script of each one, since the formation of the default depends on the script).
`data` is the data object passed into the POS handler. `terms` is the list of terms to process. Those where the term
itself is not `+` will be returned unchanged, while those where the term is `+` will be handled by generating the
appropriate inflections from the headwords using `make_inflection` (which is passed three arguments, `head`, `tr` and
`sccode`, i.e. the script code of `head`) and should return two values, term and translit, either of which can be
nil. A nil head will be ignored, and otherwise the decorations specified on the `+` term will be combined with the
decorations specified on the head. The return value is a list of inflections where no requests for the default
inflection remain.
]==]
function Headdata:resolve_special(terms, handle_special, props)
props = props or {}
local infls = {}
local is_special = props.is_special or function(_data, infl) return infl.term == "+" end
for _, termobj in ipairs(terms) do
local is_special_retval
if type(is_special) == "function" then
is_special_retval = is_special(self, termobj)
elseif type(is_special) == "table" then
is_special_retval = contains(is_special, termobj.term)
else
error(("Internal error: When checking whether term is special, is_special is neither a function nor a " ..
"list, but is %s; term is %s"):format(dump(is_special), dump(termobj)))
end
if not is_special_retval then
insert(infls, termobj)
else
for _, headobj in ipairs(self.heads) do
local head = headobj.term or self.pagename
local head_no_links
if props.with_links then
head = head:find("%[") and head or require(headword_module).add_multiword_links(head, not headobj.term)
head_no_links = require(links_module).remove_links(head)
else
head = require(links_module).remove_links(head)
head_no_links = head
end
local newterms = handle_special {
headdata = self,
head = head,
head_no_links = head_no_links,
tr = headobj.tr,
infl = termobj,
sc = self.lang:findBestScript(head_no_links),
}
if newterms then
newterms = export.canonicalize_termobj_list(newterms, "term", "resolve_special", termobj)
for _, newterm in ipairs(newterms) do
if not props.no_combine_handle_special_retval_with_origin then
export.combine_termobj_decorations(newterm, termobj)
end
if not props.no_combine_handle_special_retval_with_head then
export.combine_termobj_decorations(newterm, headobj)
end
insert(infls, newterm)
end
end
end
end
end
return infls
end
--[==[
Add the current page to a tracking page named `Wiktionary:Tracking/``lang``-headword/``page```, where ``lang`` is the
language code of the current language. For example, if the current language is `mak` and `page` is {"redundant-lon"},
the current page will get added to the tracking page `Wiktionary:Tracking/mak-headword/redundant-lon`. All pages added
to that tracking page can be seen by going to [[Special:WhatLinksHere/Wiktionary:Tracking/mak-headword/redundant-lon]].
This is typically used to track issues occurring in user-specified parameters that do not rise to the level of errors
(e.g. redundant parameters, deprecated usages or other dispreferred values).
]==]
function Headdata:track(page)
return require(debug_track_module)(self.langcode .. "-headword/" .. page)
end
local boolean_param = {type = "boolean"}
--[==[
Process an arbitrary headword in an arbitrary language, handling generic and language-specific arguments and calling
`full_headword()` in [[Module:headword]]. This is intended for use in implementing headword modules (e.g.
[[Module:uz-headword]] for Uzbek, [[Module:mn-headword]] for Uzbek, [[Module:gsw-headword]] for Alemannic German, etc.)
and provides a general implementation of such modules. On input, `data` is an object with the following fields:
* `lang`: The language object of the language being handled. '''Required.''' Use the special value {false} to indicate
that the language is specified by the user in {{para|1}}.
* `frame`: The frame object passed into the `show()` function of your module, which implements headword-handling for
all parts of speech in the module, including a generic POS-handling template (e.g. {{tl|uz-head}} or {{tl|mn-head}}),
which allows arbitrary parts of speech to be handled. '''Required.'''
* `pos_functions`: A table listing, for each part of speech requiring special handling, the extra parameters (if any)
that the part of speech accepts, along with how to handle them. See the examples below. '''Required.'''
* `validate_lang`: If {lang = true} is specified, this is a function of one argument (a language object, based on the
language specified in {{para|1}}) that should throw an error if the language object is disallowed. If omitted, all
languages are allowed.
* `numbered_head`: If true, explicit headwords are specified in a numbered param instead of in {{para|head}}. The param
used is usually {{para|1}}, but is {{para|2}} for generic POS templates such as {{tl|mn-head}} or for POS-specific
templates when {lang = true} is specified (e.g. {{tl|arb-noun}}), and is {{para|3}} for generic POS templates when
{lang = true} is spcified (e.g. {{tl|arb-head}}).
* `include_tr`: If true, allow explicit transliterations to be specified. The transliteration(s) for the headword(s)
themselves is/are specified in {{para|tr}} or through the {{cd|<tr:...>}} inline modifier on headwords, and
transliterations of inflections are specified through the {{cd|<tr:...>}} inline modifier. This should generally be
given when a headword for the language may be in a script other than Latin.
* `include_ts`: If true, allow explicit transcriptions to be specified. The transcription(s) for the headword(s)
themselves is/are specified in {{para|ts}} or through the {{cd|<ts:...>}} inline modifier on headwords, and
transcriptions of inflections are specified through the {{cd|<ts:...>}} inline modifier. This should generally only
be given for certain languages where the spelling is radically different from the pronunciation (e.g. in cuneiform
languages such as Hittite and Akkadian, and potentially in Tibetan), and represents a pronunciation-based rendering
(usually not direct IPA).
* `include_sc`: If true, allow an explicit script code to be specified. The overall script code for the headword(s)
themselves can be specified using {{para|sc}}, and per-headword or per-inflection script codes are specified using the
{{cd|<sc:...>}} inline modifier. This should generally be given when a language supports multiple scripts.
* `infls`: An inflections structure specifying extra generic parameters that apply to all parts of speech and how to
handle them. The format is the same as for the `infls` structure in `pos_functions`.
* `augment_params`: A callback function to add extra generic parameters, or modify existing generic parameters in the
`params` structure; but in general, extra generic parameters should be added through the `infls` structure instead.
This callback should not be used to add part-of-speech-specific parameters; those are handled through the appropriate
setting in `pos_functions`. It is passed two single arguments, the `headdata` object and the `params` table to be
augmented. See below for extra fields stored in the `process_props` structure of the `headdata` object. This function
is called after initializing the `params` table and processing the overall `infls` structure, but just before adding
part-of-speech-specific parameters (from `pos_functions`) to `params`. Thus, it can override any generic parameters
but may itself be overridden by a part-of-speech-specific parameter.
* `augment_headdata`: A callback function to modify the `headdata` object passed to `full_headword()` in
[[Module:headword]]. This should not be used to for part-of-speech-specific parameter handling; this is handled
through the appropriate setting in `pos_functions`. It is passed a single argument, the `headdata` object, as for
`augment_params`; but the `process_props` structure and other fields will be more filled out, as this callback is
called later. This can be used, for example, to override the value of a generic setting in `headdata` (e.g.
[[Module:uz-headword]] uses this to mark non-Latin terms as variant forms by setting `headdata.var`, being careful not
to override a value already set by the user). This function is called after initializing the `headdata` table with all
information taken from generic parameters and processing generic parameters specified in the overall `infls`
structure, and just before processing the appropriate part-of-speech-specific `infls` structure in `pos_functions`
(which in turn is followed by any handler function in `pos_functions`). Thus, it can override any value set during
generic parameter processing but may itself be overridden by a part-of-speech-specific parameter or handler.
* `force_cat`: If true, add the headword to the appropriate categories even on non-mainspace pages. This can be used for
testing category handling in sample template calls on userspace test pages or template documentation pages. It should
not be set in production code.
* `enable_auto_translit`: If true, turn on automatic transliteration of inflections at a global level (i.e. applying to
all inflections). This has no effect on headwords, which are automatically transliterated by default if in a non-Latin
script and automatic transliteration is available for the language. You can also set this value for particular
inflections in the `insert_inflection()` function.
The `headdata` headword data structure has an extra field in it called `process_props` that is specific to the
`process_headword()` function, containing various extra properies. As the operation of `process_headword()` proceeds,
this object gets filled out with more fields. For example, once parameter parsing happens, the resulting values are
available in the `args` field of `process_props`. The following fields are found in `process_props` (note that
`pos_category`, the canonicalized plural part of speech of the headword being processed, is *not* present here; it's
directly on `headdata`):
* `namespace`: The name of the current namespace; an empty string for the mainspace. This references the namespace of
the actual page and isn't affected by the {{para|pagename}} parameter.
* `indexing_pos`: The canonicalized part of speech of the headword used to index into `pos_functions`. This is the
same as `poscat` for specific part-of-speech templates such as {{tl|uz-noun}}, but has the value {"head"} for generic
part-of-speech templates such as {{tl|uz-head}}. (Note that `poscat` is directly available on `headdata`.)
* `generic_pos_template`: True if a generic POS templates like {{tl|uz-head}} or {{tl|mn-head}} was used. (This is
signaled by omitting the invocation parameter {{para|1}} to `process_headword`.)
* `lang_in_1`: True if the language code is to be fetched from {{para|1}}.
* `pos_param`: The parameter holding the part of speech, if a generic POS tempalte like {{tl|mn-head}} is being
processed (i.e. `generic_pos_template` is set). In such a case, it will have the value of {1} or {2}, depending on
whether the language code is being fetched from {{para|1}} (see `lang_in_1`). Otherwise it will be {nil}.
* `head_param`: The parameter holding the explicit headword. If `numbered_head` was specified (as for Mongolian headword
templates), this has the value {1}, {2} or {3} depending on whether the language code is being fetched from {{para|1}}
(see `lang_in_1`) and whether a generic POS template like {{tl|mn-head}} is being processed (see
`generic_pos_template`). Otherwise, it has the value {"head"}. Also see the `lang` and `numbered_head` properties in
the `data` structure sent to `process_headword()`.
* `is_suffix`: True if the current term is a suffix. This is set when processing the `suffix`, `nosuffix` and `clitic`
parameters; it is always {false} beforehand (i.e. during `augment_params` and processing of the general `infls`
structure).
* `insert_specs`: This is a table mapping parameter names to the return value of `Headdata:insert_inflection()`, filled
out as parameter values are processed. This lets a given parameter processing function in `infls` gain access to the
result of calling `insert_inflection()` on previous parameters (which indicates the number of items inserted as well
as whether `-` was specified).
The `pos_functions` table contains an entry for each part of speech needing special handling, where the key is the
canonical plural part of speech (e.g. {"adverbs"} or {"proper nouns"}). The value associated with each key is a table
normally containing a field `infls`, listing the extra part-of-speech-specific inflection and other parameters along
with how to handle them. The specs in `infls` are used in three ways:
# to augment the `params` object passed to the `process()` function in [[Module:parameters]], specifying how to parse
the appropriate inflectional parameters;
# to specify how to process any inflectional parameters given and insert them into the `headdata` object passed to
`full_headword()` in [[Module:headword]];
# to generate appropriate documentation for the parameters and other changes made by the headword template (e.g.
inserting categories).
Alternatively, you can separately control the augmentation of the `params` object and the procesing of the resulting
arguments. This is done by specifying two fields in place of `infls`, named `params` and `func`. `params` is a table
containing extra parameters to add to the overall `params` object passed to the `process()` function in
[[Module:parameters]]. `func` is a function of two arguments, normally called `data` (the headword data structure
`headdata`) and `args` (the processed arguments table). However, this alternative method is not normally recommended
because it leads to duplication between the `params and `func` fields and the documentation, which must be manually
specified.
A simple example, as used to handle pronouns for Turoyo, is
{
local valid_genders = {"m", "f", "m-p", "f-p", "p", "?"}
pos_functions["pronouns"] = {
infls = {
{2, type = "genders", validate = valid_genders},
{"f", label = "feminine"},
{"pl", label = "plural"},
},
}
}
The equivalent using `params` and `func` is
{
local valid_genders = {"m", "f", "m-p", "f-p", "p", "?"}
pos_functions["pronouns"] = {
params = {
[2] = {type = "genders"},
f = true,
pl = true,
},
func = function(data, args)
data:validate_genders(args[2], valid_genders)
data.genders = args[2]
data:parse_and_insert_inflection("f", "feminine")
data:parse_and_insert_inflection("pl", "plural")
end
}
}
Note how the version with separate `params and `func` is longer and splits information on the parameters between the
two fields. The `params` structure sets extra user-specifiable parameters {{para|2}} for genders (since the headword is
in {{para|1}}) as well a {{para|f}} and {{para|pl}}, and the `func` handler processes those parameters. Note how this is
done by calling methods on the headword `data` structure. Each such parameter can have multiple comma-separated values,
and each value can have inline modifiers attached to it to specify further properties of the value. The `infls` version
ends up making the same method calls, but does it for you instead of you having to do it yourself.
These methods are implemented through a metatable set on the headword `data` structure, which is removed before calling
`full_headword()` in [[Module:headword]]. The methods access extra information related to headword processing (such as
the `args` table) that is stored in the `process_props` field of the headword `data` strucuture. This field is also
removed prior to calling `full_headword()`.
The methods available on the headword `data` structure are as follows. Each one also has its own documentation.
* {parse_inflection(field, props)}: Parse value(s) specified in `field` (a user-specified parameter in the `args` table)
and return a list of term objects. Optional `props` specifies additional properties controlling the parsing.
* {insert_inflection(terms, label, props)}: Insert the terms in `terms` (a list of term objects as returned by
`parse_inflection()`) into the `inflections` list in the headword `data` structure, giving the inflection the label as
specified in `label`. Optional `props` specifies additional properties controlling the parsing.
* {parse_and_insert_inflection(field, label, props)}: A combination of `parse_inflection()` and `insert_inflection()`,
if no further processing of the parsed values needs to be done before insertion.
* {insert_fixed_inflection(label, props)}: Insert a "fixed" inflection (a label without associated values) into the
`inflections` table. An example (from {{tl|mn-noun}} in [[Module:mn-headword]]) is {"hidden-g declension"}, specifying
that the noun belongs to the hidden-''g'' declension.
* {resolve_special(terms, handle_special, props)}: Resolve "special" indicators as specified by the user in an inflection
parameter. A typical example is {"+"}, requesting a default value. `terms` is the list of parsed term objects and
`handle_special` is a handler function to process special indicators and convert them to their actual values.
* {validate_genders(genders, valid_genders, props)}: Validate that the user-specified genders in `genders` all belong to
the list given in `valid_genders`, throwing an error if not.
* {insert_category(category)}: Insert a category into the `categories` list in the headword `data` structure. `category`
is normally a string naming the category, which will have the language prepended to it and any occurrences of
`{plpos}` in the string replaced with the actual plural part of speech.
]==]
function export.process_headword(data)
local data_lang, frame, pos_functions, validate_lang, numbered_head, include_tr, include_ts, include_sc, force_cat,
enable_auto_translit, checkredlinks, infls, augment_params, augment_headdata =
data.lang, data.frame, data.pos_functions, data.validate_lang, data.numbered_head, data.include_tr,
data.include_ts, data.include_sc, data.force_cat, data.enable_auto_translit, data.checkredlinks, data.infls,
data.augment_params, data.augment_headdata
local iparams = {
[1] = true,
def = true,
}
if data_lang == "invocation" or data_lang == "invocation-or-user" then
iparams.lang = true
end
local iargs = require(parameters_module).process(frame.args, iparams)
local parargs = frame:getParent().args
local lang
local function generate_data_lang_error()
error("Internal error: `data.lang` must be either a language object, \"invocation\" for a " ..
"language specified in lang= in the invocation arguments, \"user\" for a language specified in 1= in " ..
"the user template call, or \"invocation-or-user\" for either; saw " .. dump(data.lang))
end
if not data_lang then
generate_data_lang_error()
end
local lang_in_1
if type(data_lang) == "string" then
if data_lang == "invocation" then
lang = iargs.lang
if not lang then
error("Internal error: lang= must be specified in the invocation arguments")
end
elseif data_lang == "invocation-or-user" or data_lang == "user" then
if data_lang == "invocation-or-user" then
lang = iargs.lang
end
if not lang then
lang = ine(parargs[1])
if not lang then
error("Language code (see [[WT:Language codes]]) must be specified in 1=")
end
lang_in_1 = true
end
else
generate_data_lang_error()
end
lang = require(languages_module).getByCode(lang, lang_in_1 and 1 or "lang", true)
elseif type(data_lang) ~= "table" or not data_lang.hasType or not data_lang:hasType("language") then
generate_data_lang_error()
else
lang = data_lang
end
local langcode = lang:getCode()
if validate_lang then
if type(validate_lang) == "function" then
validate_lang(langcode)
else
validate_items {
items = {langcode},
valid_items = validate_lang,
item_type = "language code"
}
end
end
local template_indicated_pos = iargs[1]
local generic_pos_template = not template_indicated_pos
local pos_param
if generic_pos_template then
pos_param = lang_in_1 and 2 or 1
template_indicated_pos = ine(parargs[pos_param]) or
mw.title.getCurrentTitle().fullText == ("Bản mẫu:%s-head"):format(langcode) and "interjection" or
error(("Part of speech must be specified in %s="):format(pos_param))
template_indicated_pos = canonicalize_pos(template_indicated_pos)
end
local head_param = numbered_head and (generic_pos_template and lang_in_1 and 3 or
(generic_pos_template or lang_in_1) and 2 or 1) or "head"
local indexing_pos
if generic_pos_template then
indexing_pos = "head." .. template_indicated_pos
if not pos_functions[indexing_pos] then
indexing_pos = "head"
end
else
indexing_pos = template_indicated_pos
end
local namespace = mw.loadData(headword_data_module).page.namespace
-- Partly initialize headdata now for use in generic infls callbacks. Will be further initialized later after
-- processing parameters.
local headdata = {
lang = lang,
langcode = langcode,
langfullcode = lang:getFullCode(),
langname = lang:getCanonicalName(),
langfullname = lang:getFullName(),
process_props = {
namespace = namespace,
calldata = data,
-- preserve template-indicated POS in case we normalize it or change it to "suffix"
template_indicated_pos = template_indicated_pos,
indexing_pos = indexing_pos,
generic_pos_template = generic_pos_template,
lang_in_1 = lang_in_1,
pos_param = pos_param,
head_param = head_param,
is_suffix = false,
processed_specs = {},
insert_specs = {},
},
-- We may change this below to "suffix" or normalize it.
pos_category = template_indicated_pos,
categories = {},
inflections = {enable_auto_translit = enable_auto_translit},
force_cat_output = force_cat,
no_redundant_head_cat = true,
}
setmetatable(headdata, {__index = Headdata})
local function resolve_prop(...)
return headdata:resolve_prop(...)
end
local params = {
[head_param] = {template_default = iargs.def},
head2 = {replaced_by = false, instead = ("use comma-separated |%s="):format(head_param)},
id = true,
sort = true,
cat = true,
nolink = boolean_param,
nolinkhead = {type = "boolean", alias_of = "nolink"},
suffix = boolean_param,
nosuffix = boolean_param,
-- If true, this is an inflectional suffix (forming a given person/number/gender/etc. part of the inflection of
-- a given part of speech, potentially restricted to a given inflectional class). We always treat these as
-- suffixes, rather than suffix forms, even if they form non-lemma forms (e.g. modern English -s and archaic
-- -eth form the third-singular present indicative of a verb, i.e. they form a verb form, but they are not
-- themselves forms of some other suffix, which is what a suffix form is). We also display the label of
-- inflectional suffixes specially, and add an additional category 'LANG inflectional suffixes' along with
-- categories like 'LANG POS inflectional suffixes' in place of 'LANG POS suffix forms' etc.
inflsuf = boolean_param,
clitic = {type = "boolean or string"},
addlpos = true,
var = {type = "boolean or string", set = {"both"}},
abbr = true,
json = boolean_param,
pagename = true, -- for testing
}
if include_sc then
params.sc = {type = "script"}
end
if include_tr then
params.tr = true
params.tr2 = {replaced_by = false, instead = "use comma-separated |tr= or <tr:...> inline modifier on head"}
end
if include_ts then
params.ts = true
params.ts2 = {replaced_by = false, instead = "use comma-separated |ts= or <ts:...> inline modifier on head"}
end
if lang_in_1 then
params[1] = {required = true} -- required but ignored as already processed above
end
if generic_pos_template then
params[pos_param] = {required = true} -- required but ignored as already processed above
end
local function augment_params_from_infls(infls)
infls = resolve_prop(infls)
for _, infl in ipairs(infls) do
local function interr(txt)
error(("Internal error: %s (coming from infls spec %s)"):format(txt, dump(infl)))
end
local param = infl[1]
if param then
param = resolve_prop(param)
if type(param) ~= "string" and type(param) ~= "number" then
interr(("Parameter name %s must be a string or number"):format(dump(param)))
end
-- We handle defaults as well as validation ourselves.
local typ = resolve_prop(infl.type) or "string"
if typ ~= "genders" and typ ~= "boolean" and typ ~= "string" then
-- FIXME: Handle more types.
interr(('Unrecognized type %s; can only currently handle "genders", "boolean" and "string" (the default)'):format(
dump(typ)))
end
params[param] = {type = typ, required = resolve_prop(infl.required), template_default = resolve_prop(infl.template_default)}
if typ ~= "boolean" then
local allow_and_ignore_list_params = resolve_prop(infl.allow_and_ignore_list_params)
local ignore_param
if type(allow_and_ignore_list_params) == "string" then
ignore_param = allow_and_ignore_list_params
elseif type(param) == "string" then
ignore_param = param
end
if ignore_param then
local old_list_param = allow_and_ignore_list_params and true or
{replaced_by = false, instead = ("use comma-separated |%s="):format(param)}
params[ignore_param .. "2"] = old_list_param
params[ignore_param .. "3"] = old_list_param
params[ignore_param .. "4"] = old_list_param
end
end
end
end
end
if infls then
augment_params_from_infls(infls)
end
if augment_params then
augment_params(headdata, params)
end
if pos_functions[indexing_pos] then
local pos_infls = pos_functions[indexing_pos].infls
if pos_infls then
augment_params_from_infls(pos_infls)
end
local pos_params = pos_functions[indexing_pos].params
if pos_params then
for key, val in pairs(pos_params) do
params[key] = val
end
end
end
local args = require("Module:parameters").process(parargs, params)
local pagename = args.pagename or mw.loadData(headword_data_module).pagename
local sc = args.sc or lang:findBestScript(pagename)
headdata.pagename = pagename
headdata.process_props.args = args
headdata.sc = sc
headdata.id = args.id
headdata.sort = args.sort
-- No redundant script cat unless the user explicitly gave sc=
headdata.no_script_code_cat = not args.sc
headdata.var = args.var
local extra_term_mods = {}
if include_tr then
insert(extra_term_mods, "tr")
end
if include_ts then
insert(extra_term_mods, "ts")
end
if include_sc then
insert(extra_term_mods, "sc")
end
if not extra_term_mods[1] then
extra_term_mods = nil
end
local trs = args.tr and split_on_comma(args.tr) or {}
local num_trs = #trs
local tss = args.ts and split_on_comma(args.ts) or {}
local num_tss = #tss
local heads = args[head_param] and export.parse_term_with_modifiers {
val = args[head_param],
paramname = head_param,
splitchar = ",",
is_head = true,
include_mods = extra_term_mods,
} or {}
local num_heads = #heads
if num_heads > 0 and num_trs > 0 and num_heads ~= num_trs then
error(("%s head%s specified explicitly but %s translit%s; they must match; use '+' to stand for the default head (the pagename) or default automatic translit and '-' to stand for no translit"):format(
num_heads, num_heads > 1 and "s" or "", num_trs, num_trs > 1 and "s" or ""))
end
if num_heads > 0 and num_tss > 0 and num_heads ~= num_tss then
error(("%s head%s specified explicitly but %s transcription%s; they must match; use '+' to stand for the default head (the pagename) and '-' to stand for no transcription"):format(
num_heads, num_heads > 1 and "s" or "", num_tss, num_tss > 1 and "s" or ""))
end
if num_trs > 0 and num_tss > 0 and num_trs ~= num_tss then
error(("%s translit%s specified explicitly but %s transcription%s; they must match; use '+' to stand for default automatic translit and '-' to stand for no translit or transcription"):format(
num_trs, num_trs > 1 and "s" or "", num_tss, num_tss > 1 and "s" or ""))
end
-- Be careful here not to overwrite user_specified_heads if it's empty so we can later check user_specified_heads
-- to see if the user provided any heads.
local max_tr_ts = math.max(num_trs, num_tss)
if num_heads == 0 and max_tr_ts > 0 then
heads = {}
for i = 1, max_tr_ts do
heads[i] = {term = "+"}
end
end
if not heads[1] then
heads = {{term = "+"}}
end
for i, headobj in ipairs(heads) do
if headobj.tr and trs[i] then
if headobj.tr ~= trs[i] then
error(("Saw two different translits '%s' and '%s' for head #%s"):format(
headobj.tr, trs[i], i))
end
else
headobj.tr = headobj.tr or trs[i]
end
if headobj.tr == "+" then
headobj.tr = nil
end
if headobj.ts and tss[i] then
if headobj.ts ~= tss[i] then
error(("Saw two different transcriptions '%s' and '%s' for head #%s"):format(
headobj.ts, tss[i], i))
end
else
headobj.ts = headobj.ts or tss[i]
end
if headobj.ts == "-" then
headobj.ts = nil
end
if headobj.term == "+" then
headobj.term = args.nolink and pagename or nil
if headobj.term and namespace == "Reconstruction" then
headobj.term = "*" .. headobj.term
end
end
end
headdata.heads = heads
local pos_category = headdata.pos_category
-- Do this as late as possible in case the caller wants to conditionalize the primary categorizing POS on
-- arguments or even the presence or absence of explicit heads.
if pos_functions[indexing_pos] then
pos_category = resolve_prop(pos_functions[indexing_pos].pos_category) or pos_category
end
headdata.process_props.normalized_template_indicated_pos = pos_category
headdata.pos_category = pos_category
headdata.process_props.pos_variants = nil -- erase the cache
local function pagename_is_suffix()
if sc:getCode() == "Latn" then
-- shortcut Latin terms to avoid unnecessarily loading [[Module:affix]]
return pagename:find("^%-") and not pagename:find("%-$")
else
local affix_type, _, _, _ = require(affix_module).parse_term_for_affixes(pagename, lang, sc)
return affix_type == "Hậu tố"
end
end
local clitic_label = args.clitic
if clitic_label == true then
clitic_label = "khinh âm"
end
if clitic_label then
headdata:insert_category("khinh âm")
headdata:insert_fixed_inflection(clitic_label)
elseif args.suffix or (
not args.nosuffix and pagename_is_suffix() and pos_category ~= "Hậu tố" and pos_category ~= "Biến thể hình thái hậu tố"
) then
headdata.process_props.is_suffix = true
local pos_list_for_inflectional_suffix = {}
local pos_list_for_suffix_form = {}
local pos_list_for_forming_suffix = {}
local function handle_suffix_pos(pos, is_first)
-- Use the actual POS for the label we insert, but simplify the POS for the category. We need to separately check
-- each of them to see if they end in 'forms' because e.g. 'comparative adjectives' gets simplified to
-- 'adjective forms'.
local form_type = pos:match("^Biến thể hình thái (.*)$")
if form_type then
if args.inflsuf then
insert(pos_list_for_inflectional_suffix, form_type)
else
insert(pos_list_for_suffix_form, form_type)
end
else
local singular_pos = require(en_utilities_module).singularize(pos)
if args.inflsuf then
insert(pos_list_for_inflectional_suffix, singular_pos)
else
insert(pos_list_for_forming_suffix, singular_pos .. "-")
end
end
if args.inflsuf then
headdata:insert_category("inflectional suffixes")
end
local simplified_pos = export.simplify_pos(pos, "preserve_proper_nouns", "preserve_lemma_status")
local simplified_form_type = simplified_pos:match("^Biến thể hình thái (.*)$")
if simplified_form_type then
if args.inflsuf then
headdata:insert_category(("%s inflectional suffixes"):format(simplified_form_type))
else
headdata:insert_category(("%s suffix forms"):format(simplified_form_type))
end
else
local singular_simplified_pos = require(en_utilities_module).singularize(simplified_pos)
if args.inflsuf then
headdata:insert_category(("%s inflectional suffixes"):format(singular_simplified_pos))
else
headdata:insert_category(("%s-forming suffixes"):format(singular_simplified_pos))
end
end
local postype
if args.inflsuf then
postype = "lemma"
end
if not postype then
postype = require(headword_module).pos_lemma_or_nonlemma(pos)
end
if not postype then
postype = require(headword_module).pos_lemma_or_nonlemma(simplified_pos)
end
if not postype then
error(("Unrecognized canonicalized part of speech '%s' in addlpos=, cannot determine whether lemma or non-lemma form"):format(
pos
))
end
local this_pos_category = postype == "Mục từ" and "Hậu tố" or "Biến thể hình thái hậu tố"
if is_first then
headdata.pos_category = this_pos_category
headdata.process_props.pos_variants = nil -- erase the cache
elseif headdata.pos_category ~= this_pos_category then
local singular_normalized_this_pos_category =
require(en_utilities_module).singularize(this_pos_category)
local singular_normalized_overall_pos_category =
require(en_utilities_module).singularize(headdata.pos_category)
error(("Cannot mix suffixes and suffix forms using addlpos=; '%s' is of a %s while overall POS " ..
"'%s' is a %s; use separate POS headers for the two"):format(pos,
singular_normalized_this_pos_category, pos_category, singular_normalized_overall_pos_category))
end
end
handle_suffix_pos(pos_category, true)
if args.addlpos then
for _, addlpos in ipairs(split(args.addlpos, "%s*,%s*")) do
addlpos = canonicalize_pos(addlpos)
handle_suffix_pos(addlpos, false)
end
end
if pos_list_for_inflectional_suffix[1] then
headdata:insert_fixed_inflection(mw.text.listToText(pos_list_for_inflectional_suffix) .. " inflectional suffix")
end
if pos_list_for_suffix_form[1] then
headdata:insert_fixed_inflection(mw.text.listToText(pos_list_for_suffix_form) .. " suffix form")
end
if pos_list_for_forming_suffix[1] then
headdata:insert_fixed_inflection(mw.text.listToText(pos_list_for_forming_suffix) .. "forming suffix")
end
end
if args.cat then
for _, cat in ipairs(split_on_comma(args.cat)) do
headdata:insert_category(cat)
end
end
local function augment_headdata_from_infls(infls)
infls = resolve_prop(infls)
for _, infl in ipairs(infls) do
local function interr(txt)
error(("Internal error: %s (coming from infls spec %s)"):format(txt, dump(infl)))
end
local function process_labelobjs(labelobjs, originating_term, handle_labelobj)
if labelobjs == nil then
return
end
if type(labelobjs) ~= "string" and type(labelobjs) ~= "table" then
interr(("Wrong type '%s' for label object(s) %s, expected string or table"):format(
type(labelobjs), dump(labelobjs)
))
end
if type(labelobjs) == "string" or type(labelobjs) == "table" and not labelobjs[1] then
labelobjs = {labelobjs}
end
for _, labelobj in ipairs(labelobjs) do
local label, termobj
if type(labelobj) == "string" then
label = labelobj
termobj = originating_term
elseif type(labelobj) ~= "table" then
interr(("Wrong type '%s' for label object %s, expected string or table"):format(
type(labelobj), dump(labelobj)
))
label = labelobj.term
if type(label) ~= "string" then
interr(("Wrong type '%s' for label %s from label object %s, expected string"):format(
type(label), dump(label), dump(labelobj)
))
end
termobj = labelobj
end
handle_labelobj(label, termobj)
end
end
local param = infl[1]
if param then
local vals
-- Fetch the param and make sure it's a string or number.
param = resolve_prop(param)
if type(param) ~= "string" and type(param) ~= "number" then
interr(("Parameter name %s must be a string or number"):format(dump(param)))
end
-- Fetch the type and validate.
local typ = resolve_prop(infl.type)
if typ == nil then
typ = "string"
end
if typ ~= "genders" and typ ~= "boolean" and typ ~= "string" then
-- FIXME: Handle more types.
interr(('Unrecognized type %s; can only currently handle "genders", "boolean" and "string" (the default)'):format(
dump(typ)))
end
-- Fetch the value(s).
if typ == "genders" or typ == "boolean" then
vals = args[param]
elseif typ == "string" then
local parse_inflection_props = resolve_prop(infl.parse_inflection_props)
local include_mods = resolve_prop(infl.include_mods)
local no_augment_include_mods = resolve_prop(infl.no_augment_include_mods)
if include_mods ~= nil or no_augment_include_mods ~= nil then
if parse_inflection_props == nil then
parse_inflection_props = {}
else
parse_inflection_props = shallow_copy(parse_inflection_props)
end
if include_mods ~= nil then
parse_inflection_props.include_mods = include_mods
end
if no_augment_include_mods ~= nil then
parse_inflection_props.no_augment_include_mods = no_augment_include_mods
end
end
vals = headdata:parse_inflection(param, parse_inflection_props)
-- Convert an empty list to nil for consistent checking below.
if not vals[1] then
vals = nil
end
else
interr(("Unrecognized type '%s"):format(typ))
end
-- If value(s) nil, fetch the default.
if vals == nil and infl.default ~= nil then
local default = resolve_prop(infl.default)
if typ == "genders" then
vals = export.canonicalize_termobj_list(default, "spec", "default")
elseif typ == "boolean" then
vals = default
elseif typ == "string" then
vals = export.canonicalize_termobj_list(default, "term", "default")
else
interr(("Unrecognized type '%s"):format(typ))
end
end
-- Resolve "special" values (special signals a string values, such as requesting the default with "+").
if vals ~= nil and infl.resolve_special then
if typ ~= "string" then
interr(("Cannot specify resolve_special= for type %s"):format(dump(typ)))
end
local resolve_special_props = resolve_prop(infl.resolve_special_props, vals)
if infl.is_special ~= nil then
if resolve_special_props == nil then
resolve_special_props = {}
else
resolve_special_props = shallow_copy(resolve_special_props)
end
resolve_special_props.is_special = infl.is_special
end
vals = headdata:resolve_special(vals, infl.resolve_special, resolve_special_props)
end
-- Canonicalize the value(s).
if vals ~= nil and infl.canonicalize ~= nil then
if typ == "boolean" then
interr('Cannot specify canonicalize= when type is "boolean"')
end
local newvals = {}
local field = typ == "genders" and "spec" or "term"
for _, val in ipairs(vals) do
local newval
if val._is_mode then
newval = val
else
if type(infl.canonicalize) == "function" then
newval = infl.canonicalize(val[field])
else
newval = val[field]
newval = infl.canonicalize[newval] or newval
end
end
if val[field] ~= newval then
val = shallow_copy(val)
val[field] = newval
end
insert(newvals, val)
end
vals = newvals
end
-- Validate the value(s).
if vals ~= nil and infl.validate ~= nil then
if typ == "boolean" then
interr('Cannot specify validate= when type is "boolean"')
elseif type(infl.validate) == "function" then
infl.validate(headdata, vals)
elseif typ == "genders" then
export.validate_genders {
genders = vals,
valid_genders = infl.validate,
no_augment = resolve_prop(infl.no_augment_genders),
}
elseif typ == "string" then
validate_items {
items = vals,
field = "term",
valid_items = infl.validate,
item_type = ("values in |%s="):format(param),
}
else
interr(("Unrecognized type '%s"):format(typ))
end
end
local function run_and_check_return_value(fun, funname, ...)
local retval, intentionally_nil = fun(...)
if retval == nil and not intentionally_nil then
interr(("If you return nil from %s, you must return a second non-nil return " ..
"value to indicate that the nil return value was intentional"):format(funname))
end
return retval
end
-- Run the process_after_parse handler, if it exists.
if vals ~= nil and infl.process_after_parse ~= nil then
vals = run_and_check_return_value(infl.process_after_parse, "process_after_parse", headdata, vals)
end
-- Process strings for insertion, to handle special mode signals like -, +-, ?, etc.
local processed
if typ == "string" then
local process_for_insertion_props = resolve_prop(infl.process_for_insertion_props, vals)
processed = headdata:process_for_insertion(vals, process_for_insertion_props)
headdata.process_props.processed_specs[param] = processed
if processed.numterms > 0 and infl.process_one_term ~= nil then
local processed_terms = {}
for _, termobj in ipairs(processed.terms) do
local processed_term = run_and_check_return_value(infl.process_one_term, "process_one_term",
headdata, termobj)
if processed_term ~= nil then
-- FIXME: Should we add an origin of "process_one_term" here? (But this would erase
-- any "default" or "resolve_special" origins.)
local newterms = export.canonicalize_termobj_list(processed_term, "term", nil, nil,
termobj)
for _, newterm in ipairs(newterms) do
insert(processed_terms, newterm)
end
end
end
processed = shallow_copy(processed)
processed.terms = processed_terms
processed.numterms = #processed_terms
end
if processed.numterms > 0 and infl.process_terms ~= nil then
local process_terms_result = run_and_check_return_value(infl.process_terms, "process_terms",
processed)
process_terms_result = process_terms_result or {}
if process_terms_result[1] or not next(process_terms_result) then
processed = shallow_copy(processed)
processed.terms = process_terms_result
processed.numterms = #process_terms_result
elseif not process_terms_result.terms or not process_terms_result.numterms then
error("Return value from process_terms() should be either a list of term objects or a " ..
"`processed` structure containing such a list")
else
processed = process_terms_result
end
end
vals = processed.terms
end
local inserted_fixed_label
-- "Implement" the values. Fixed labels and cats only fire when non-falsy (i.e. we don't want to fire
-- on boolean false or empty list). We call insert_processed_inflection() always because there may be
-- a mode even when there are no values.
if vals == true or type(vals) == "table" and vals[1] then
if infl.fixed_label and infl.all_fixed_label then
interr("Cannot specify both fixed_label= and all_fixed_label=; specify one or the other")
end
local function check_fixed_label_references_val(label)
if type(label) == "table" and label[1] then
for _, lab in ipairs(label) do
if check_fixed_label_references_val(lab) then
return true
end
end
return false
end
if type(label) == "table" then
if not label.term then
interr(("Fixed label structure %s does not have a value for `.term`"):format(dump(label)))
end
label = label.term
end
if type(label) ~= "string" then
interr(("Wrong type for fixed label %s, should be string"):format(type(label)))
end
return not not label:find("{val}")
end
local fixed_label = infl.fixed_label
local all_fixed_label = infl.all_fixed_label
-- If the value being processed is boolean, there's only one value so treat a fixed_label as an
-- all_fixed_label and output only once; likewise if the caller specified a fixed_label without
-- {val} in it.
if fixed_label and (typ == "boolean" or type(fixed_label) ~= "function" and not
check_fixed_label_references_val(fixed_label)) then
all_fixed_label = fixed_label
fixed_label = nil
end
if fixed_label then
if typ == "boolean" then
interr("Boolean fixed_label values should have been converted to all_fixed_label")
end
for _, valobj in ipairs(vals) do
local labelobjs = resolve_prop(fixed_label, valobj)
process_labelobjs(labelobjs, valobj, function(label, termobj)
if label:find("{val}") then
if typ ~= "string" then
interr(('Cannot specify {val} in fixed_label %s when type is "%s"'):format(dump(label), typ))
end
label = label:gsub("{val}", replacement_escape(valobj.term))
end
headdata:insert_fixed_inflection(label, {
originating_term = termobj
})
inserted_fixed_label = true
end)
end
elseif all_fixed_label then
local labelobjs = resolve_prop(all_fixed_label, vals)
process_labelobjs(labelobjs, nil, function(label, termobj)
if label:find("{vals}") then
if typ ~= "string" then
interr(('Cannot specify {vals} in all_fixed_label %s when type is "%s"'):format(dump(label), typ))
end
local formatted_labels = {}
for _, valobj in ipairs(vals) do
insert(formatted_labels, add_decorations(valobj.term, valobj, lang))
end
label = label:gsub("{vals}", replacement_escape(serial_comma_join(formatted_labels)))
end
headdata:insert_fixed_inflection(label, termobj)
inserted_fixed_label = true
end)
end
end
-- See above; if there's a label (which can only happen when typ == "string"), always call
-- insert_processed_inflection.
local inserted_vals
if infl.label ~= nil then
if typ ~= "string" then
interr(("label=%s can only be specified for type 'string', not '%s'"):format(
dump(infl.label), typ
))
end
local label = resolve_prop(infl.label, vals)
if label ~= nil then
local insert_processed_inflection_props =
resolve_prop(infl.insert_processed_inflection_props, vals)
local function copy_to_insert_processed_inflection_props(field, val)
local fieldval = val or resolve_prop(infl[field], vals)
if fieldval ~= nil then
if insert_processed_inflection_props == nil then
insert_processed_inflection_props = {}
else
insert_processed_inflection_props =
shallow_copy(insert_processed_inflection_props)
end
insert_processed_inflection_props[field] = fieldval
end
end
copy_to_insert_processed_inflection_props("label_for_cats_and_modes")
copy_to_insert_processed_inflection_props("no_auto_cats")
copy_to_insert_processed_inflection_props("accel")
copy_to_insert_processed_inflection_props("accel_form")
copy_to_insert_processed_inflection_props("request")
if not processed then
interr("`processed` structure wasn't properly created")
end
headdata:insert_processed_inflection(processed, label, insert_processed_inflection_props)
inserted_vals = true
end
end
-- See above; only fire for cats (and gender insertion) on non-falsy values.
if vals == true or type(vals) == "table" and vals[1] then
if typ == "genders" then
headdata.genders = vals
end
local inserted_cat
if infl.cat then
if typ == "boolean" then
vals = {vals}
end
local allcats = {}
for _, valobj in ipairs(vals) do
local cats = resolve_prop(infl.cat, valobj)
if type(cats) == "string" then
cats = {cats}
end
if cats ~= nil then
for _, cat in ipairs(cats) do
if cat:find("{val}") then
cat = cat:gsub("{val}", replacement_escape(valobj.term))
end
insert_if_not(allcats, cat)
end
end
end
for _, cat in ipairs(allcats) do
headdata:insert_category(cat)
inserted_cat = true
end
end
if typ == "boolean" then
if not inserted_fixed_label and not inserted_cat then
interr(("User set boolean setting for %s= but no fixed label added and no category " ..
"inserted; if you took action in process_after_parse(), make sure to return " ..
"`nil, true`"):format(param))
end
elseif typ == "string" then
if not inserted_vals and not inserted_fixed_label and not inserted_cat then
interr(("User set value(s) %s for %s= but no inflection inserted and no fixed label " ..
"or category added"):format(dump(vals), param))
end
end
if processed then
-- FIXME, convert to use `processed_specs` field and remove this
headdata.process_props.insert_specs[param] = processed
end
end
else -- no param specified
if infl.label or infl.all_fixed_label then
interr("Cannot have label= or all_fixed_label= without specifying a param")
end
if infl.process then
infl.process(headdata)
end
if infl.fixed_label then
local labelobjs = resolve_prop(infl.fixed_label)
process_labelobjs(labelobjs, nil, function(label, termobj)
if label:find("{val}") then
interr("Cannot specify {val} in a fixed_label= value without specifying a param")
end
headdata:insert_fixed_inflection(label, {
originating_term = termobj
})
end)
end
if infl.cat then
local cats = resolve_prop(infl.cat)
if type(cats) == "string" then
cats = {cats}
end
if cats ~= nil then
for _, cat in ipairs(cats) do
if cat:find("{val}") then
interr("Cannot specify {val} in a cat= value without specifying a param")
end
headdata:insert_category(cat)
end
end
end
end
end
end
if infls then
augment_headdata_from_infls(infls)
end
if augment_headdata then
augment_headdata(headdata, args)
end
if pos_functions[indexing_pos] then
local pos_infls = pos_functions[indexing_pos].infls
if pos_infls then
augment_headdata_from_infls(pos_infls)
end
local func = pos_functions[indexing_pos].func
if func then
func(headdata, args)
end
end
headdata:parse_and_insert_inflection("abbr", "abbreviation")
headdata.checkredlinks = resolve_prop(checkredlinks)
setmetatable(headdata, nil)
headdata.process_props = nil
if args.json then
return require("Module:JSON").toJSON(headdata)
end
return require(headword_module).full_headword(headdata)
end
return export
rw2qd5fmi1muswueyshlhdg7rasc5qa
Mô đun:category tree
828
279363
2573959
2363011
2026-10-28T07:04:55Z
Hiyuune
50834
2573959
Scribunto
text/plain
-- Prevent substitution.
if mw.isSubsting() then
return require("Module:unsubst")
end
local export = {}
local category_tree_submodule_prefix = "Module:category tree/"
local category_tree_styles_css = "Module:category tree/styles.css"
local m_str_utils = require("Module:string utilities")
local m_template_parser = require("Module:template parser")
local m_utilities = require("Module:utilities")
local ceil = math.ceil
local class_else_type = m_template_parser.class_else_type
local concat = table.concat
local deep_copy = require("Module:table").deepCopy
local full_url = mw.uri.fullUrl
local insert = table.insert
local is_callable = require("Module:fun").is_callable
local log10 = math.log10 or require("Module:math").log10
local new_title = mw.title.new
local pages_in_category = mw.site.stats.pagesInCategory
local parse = m_template_parser.parse
local remove_comments = require("Module:string/removeComments")
local sort = table.sort
local split = m_str_utils.split
local string_compare = require("Module:string/compare")
local trim = m_str_utils.trim
local uupper = m_str_utils.upper
local yesno = require("Module:yesno")
local current_frame = mw.getCurrentFrame()
local current_title = mw.title.getCurrentTitle()
local namespace = current_title.namespace
local poscatboiler_subsystem = "poscatboiler"
local extra_args_error = "Tham số trong bản mẫu {{((}}auto cat{{))}} không được hỗ trợ thể loại này."
-- Generates a sortkey for a numeral `n`, adding leading zeroes to avoid the "1, 10, 2, 3" sorting problem. `max_n` is the greatest expected value of `n`, and is used to determine how many leading zeroes are needed. If not supplied, it defaults to the number of languages.
function export.numeral_sortkey(n, max_n)
max_n = max_n or require("Module:list of languages").count()
return ("#%%0%dd"):format(ceil(log10(max_n + 1))):format(n)
end
function export.split_lang_label(title_text)
local getByCanonicalName = require("Module:languages").getByCanonicalName
-- Chuẩn hoá tên ngôn ngữ: viết hoa chữ đầu từ “tiếng”
local function normalize_lang_name(name)
name = mw.text.trim(name)
-- Nếu bắt đầu bằng "tiếng", viết hoa chữ "T" và chữ cái đầu của phần sau
name = mw.ustring.gsub(
name,
"^(tiếng)%s+(%l)([%l%s]*)$",
function(t, c, rest)
return "Tiếng " .. mw.ustring.upper(c) .. rest
end
)
-- Nếu không bắt đầu bằng “tiếng”, chỉ viết hoa chữ đầu
name = mw.ustring.gsub(name, "^(%l)", mw.ustring.upper)
return name
end
-- Progressively remove a word from the potential canonical name until it
-- matches an actual canonical name.
local words = split(title_text, " ", true)
for i = 2, #words do
local candidate = concat(words, " ", i, #words)
local lang = getByCanonicalName(normalize_lang_name(candidate))
if lang then
return lang, concat(words, " ", 1, i - 1)
end
end
return nil, title_text
end
local function show_error(text, title)
return require("Module:message box2").maintenance(
"red",
"[[File:Codex icon Alert red.svg|40px|alt=alert]]",
title or "Thể loại này chưa được xác định trên cây thể loại Wiktionary.",
text
)
end
local function error_not_in_category_tree()
error(
"Thể loại này chưa được xác định trên cây thể loại Wiktionary. "
.. "Hãy kiểm tra tên thể loại có lỗi chính tả hay "
.. "có được tạo ở dạng tên khác hoặc trùng lặp không hoặc có thể yêu cầu xử lý thể loại ở [[Wiktionary:Thảo luận]]. "
.. "Thể loại bị ảnh hưởng: " .. current_title.fullText,
2
)
end
-- Show the text that goes at the very top right of the page.
local function show_topright(current)
return current.getTopright and current:getTopright() or nil
end
local function link_box(content)
return ("<div class=\"noprint plainlinks\" style=\"float: right; clear: both; margin: 0 0 .5em 1em; background: var(--wikt-palette-paleblue, #f9f9f9);color:inherit; border: 1px var(--border-color-base, #aaaaaa) solid; margin-top: -1px; padding: 5px; font-weight: bold;\">%s</div>"):format(content)
end
local function show_editlink(current)
return link_box(("[%s Chỉnh sửa dữ liệu thể loại]"):format(tostring(full_url(current:getDataModule(), "action=edit"))))
end
local function show_related_changes()
local title = current_title.fullText
return link_box(("[%s <span title=\"Recent edits and other changes to pages in %s\">Thay đổi liên quan</span>]"):format(
tostring(full_url("Đặc biệt:Thay đổi liên quan", {
target = title,
showlinkedto = 0,
})),
title
))
end
local function show_pagelist(current)
local namespace = "namespace="
local info = current:getInfo()
local lang_code = info.code
if info.label == "citations" or info.label == "citations of undefined terms" then
namespace = namespace .. "Citations"
elseif lang_code then
local lang = require("Module:languages").getByCode(lang_code, true, true)
if lang then
-- Proto-Norse (gmq-pro) is probably the only language with a code ending in -pro
-- that's intended to have mostly non-reconstructed entries.
if (lang_code:find("%-pro$") and lang_code ~= "gmq-pro") or lang:hasType("reconstructed") then
namespace = namespace .. "Reconstruction"
elseif lang:hasType("appendix-constructed") then
namespace = namespace .. "Appendix"
end
end
elseif info.label:match("templates") then
namespace = namespace .. "Template"
elseif info.label:match("modules") then
namespace = namespace .. "Module"
elseif info.label:match("^Wiktionary") or info.label:match("^Pages") then
namespace = ""
end
return ([=[
{| id="newest-and-oldest-pages" class="wikitable mw-collapsible" style="float: right; clear: both; margin: 0 0 .5em 1em;"
! Trang mới nhất và cũ nhất
|-
| id="recent-additions" style="font-size:0.9em;" | '''Trang mới nhất theo [[mw:Manual:Categorylinks table#cl_timestamp|cập nhật liên kết thể loại lần cuối]]:'''
%s
|-
| id="oldest-pages" style="font-size:0.9em;" | '''Trang cũ nhất:'''
%s
|}]=]):format(
current_frame:extensionTag(
"DynamicPageList",
([=[
category=%s
%s
count=10
mode=ordered
ordermethod=categoryadd
order=descending]=]
):format(current_title.text, namespace)
),
current_frame:extensionTag(
"DynamicPageList",
([=[
category=%s
%s
count=10
mode=ordered
ordermethod=lastedit
order=ascending]=]
):format(current_title.text, namespace)
)
)
end
-- Show navigational "breadcrumbs" at the top of the page.
local function show_breadcrumbs(current)
local steps = {}
-- Start at the current label and move our way up the "chain" from child to parent, until we can't go further.
while current do
local category, display_name, nocap
if type(current) == "string" then
category = current
display_name = current:gsub("^Thể loại:", "")
else
if not current.getCategoryName then
error("Lỗi nội bộ: Bad format in breadcrumb chain structure, probably a misformatted value for `parents`: " ..
mw.dumpObject(current))
end
category = "Thể loại:" .. current:getCategoryName()
display_name, nocap = current:getBreadcrumbName()
end
if not nocap then
display_name = mw.getContentLanguage():ucfirst(display_name)
end
insert(steps, 1, ("[[:%s|%s]]"):format(category, display_name))
-- Move up the "chain" by one level.
if type(current) == "string" then
current = nil
else
current = current:getParents()
end
if current then
current = current[1].name
end
end
local templateStyles = require("Module:TemplateStyles")(category_tree_styles_css)
local ol = mw.html.create("ol")
for i, step in ipairs(steps) do
local li = mw.html.create("li")
if i ~= 1 then
local span = mw.html.create("span")
:attr("aria-hidden", "true")
:addClass("ts-categoryBreadcrumbs-separator")
:wikitext(" » ")
li:node(span)
end
li:wikitext(step)
ol:node(li)
end
return templateStyles .. tostring(mw.html.create("div")
:attr("role", "navigation")
:attr("aria-label", "Breadcrumb")
:addClass("ts-categoryBreadcrumbs")
:node(ol))
end
local function show_also(current)
local also = current._info.also
if also and #also > 0 then
return ('<div style="margin-top:-1em;margin-bottom:1.5em">%s</div>'):format(require("Module:also").main(also))
end
return nil
end
-- Show a short description text for the category.
local function show_description(current)
return current.getDescription and current:getDescription() or nil
end
local function show_appendix(current)
local appendix = current.getAppendix and current:getAppendix()
return appendix and ("Để biết thêm thông tin, xem [[%s]]."):format(appendix) or nil
end
local function sort_children(child1, child2)
return string_compare(uupper(child1.sort), uupper(child2.sort))
end
-- Show a list of child categories.
local function show_children(current)
local children = current.getChildren and current:getChildren() or nil
if not children then
return nil
end
sort(children, sort_children)
local children_list = {}
for _, child in ipairs(children) do
local child_name, child_pagetitle = child.name
if type(child_name) == "string" then
child_pagetitle = child_name
else
child_pagetitle = "Thể loại:" .. child_name:getCategoryName()
end
if new_title(child_pagetitle).exists then
insert(children_list, ("* [[:%s]]: %s"):format(
child_pagetitle,
child.description or
type(child_name) == "string" and child_name:gsub("^Thể loại:", "") .. "." or
child_name:getDescription("child")
))
end
end
return concat(children_list, "\n")
end
-- Show a table of contents with links to each letter in the language's script.
local function show_TOC(current)
local titleText = current_title.text
local inCategoryPages = pages_in_category(titleText, "pages")
local inCategorySubcats = pages_in_category(titleText, "subcats")
local TOC_type
-- Compute type of table of contents required.
if inCategoryPages > 2500 or inCategorySubcats > 2500 then
TOC_type = "full"
elseif inCategoryPages > 200 or inCategorySubcats > 200 then
TOC_type = "normal"
else
-- No (usual) need for a TOC if all pages or subcategories can fit on one page;
-- but allow this to be overridden by a custom TOC handler.
TOC_type = "none"
end
if current.getTOC then
local TOC_text = current:getTOC(TOC_type)
if TOC_text ~= true then
return TOC_text or nil
end
end
if TOC_type ~= "none" then
local templatename = current:getTOCTemplateName()
local TOC_template
if TOC_type == "full" then
-- This category is very large, see if there is a "full" version of the TOC.
local TOC_template_full = new_title(templatename .. "/full")
if TOC_template_full.exists then
TOC_template = TOC_template_full
end
end
if not TOC_template then
local TOC_template_normal = new_title(templatename)
if TOC_template_normal.exists then
TOC_template = TOC_template_normal
end
end
if TOC_template then
return current_frame:expandTemplate{title = TOC_template.text, args = {}}
end
end
return nil
end
-- Show the "catfix" that adds language attributes and script classes to the page.
local function show_catfix(current)
local lang, sc = current:getCatfixInfo()
return lang and m_utilities.catfix(lang, sc) or nil
end
-- Show the parent categories that the current category should be placed in.
local function show_categories(current, categories)
local parents = current.getParents and current:getParents() or nil
if not parents then
return nil
end
for _, parent in ipairs(parents) do
local parent_name = parent.name
local sortkey = type(parent.sort) == "table" and parent.sort:makeSortKey() or parent.sort
if type(parent_name) == "string" then
insert(categories, ("[[%s|%s]]"):format(parent_name, sortkey))
else
insert(categories, ("[[Thể loại:%s|%s]]"):format(parent_name:getCategoryName(), sortkey))
end
end
-- Also put the category in its corresponding "umbrella" or "by language" category.
local umbrella = current:getUmbrella()
if umbrella then
-- FIXME: use a language-neutral sorting function like the Unicode Collation Algorithm.
local sortkey = current._lang and current._lang:getCanonicalName() or current:getCategoryName()
sortkey = require("Module:languages").getByCode("vi", true):makeSortKey(sortkey)
if type(umbrella) == "string" then
insert(categories, ("[[%s|%s]]"):format(umbrella, sortkey))
else
insert(categories, ("[[Thể loại:%s|%s]]"):format(umbrella:getCategoryName(), sortkey))
end
end
-- Check for various unwanted parser functions, which should be integrated into the category tree data instead.
-- Note: HTML comments shouldn't be removed from `content` until after this step, as they can affect the result.
local content = current_title:getContent()
if not content then
-- This happens when using [[Special:ExpandTemplates]] to call {{auto cat}} on a nonexistent category page,
-- which is needed by Benwing's create_wanted_categories.py script.
return
end
local defaultsort, displaytitle, page_has_param
for node in parse(content):iterate_nodes() do
local node_class = class_else_type(node)
if node_class == "template" then
local name = node:get_name()
if name == "DEFAULTSORT:" and not defaultsort then
insert(categories, "[[Thể loại:Trang có xung đột với DEFAULTSORT]]")
defaultsort = true
elseif name == "DISPLAYTITLE:" and not displaytitle then
insert(categories,"[[Thể loại:Trang có xung đột với DISPLAYTITLE]]")
displaytitle = true
end
elseif node_class == "parameter" and not page_has_param then
insert(categories,"[[Thể loại:Pages with raw triple-brace template parameters]]")
page_has_param = true
end
end
-- Check for raw category markup, which should also be integrated into the category tree data.
content = remove_comments(content, "BOTH")
local head = content:find("[[", 1, true)
while head do
local close = content:find("]]", head + 2, true)
if not close then
break
end
-- Make sure there are no intervening "[[" between head and close.
local open = content:find("[[", head + 2, true)
while open and open < close do
head = open
open = content:find("[[", head + 2, true)
end
local cat = content:sub(head + 2, close - 1)
local colon = cat:match("^[ _\128-\244]*[Cc][Aa][Tt][EeGgOoRrYy _\128-\244]*():")
if colon then
local pipe = cat:find("|", colon + 1, true)
if pipe ~= #cat then
local title = new_title(pipe and cat:sub(1, pipe - 1) or cat)
if title and title.namespace == 14 then
insert(categories,"[[Thể loại:Categories with categories using raw markup]]")
break
end
end
end
head = open
end
end
local function generate_output(current)
if current then
for _, functionName in pairs{
"getBreadcrumbName",
"getDataModule",
"canBeEmpty",
"getDescription",
"getParents",
"getChildren",
"getUmbrella",
"getAppendix",
"getTOCTemplateName",
} do
if not is_callable(current[functionName]) then
require("Module:debug").track{"category tree/missing function", "category tree/missing function/" .. functionName}
end
end
end
local boxes, display, categories = {}, {}, {}
-- Categories should never show files as a gallery.
insert(categories, "__NOGALLERY__")
if current_frame:getParent():getTitle() == "Bản mẫu:auto cat" then
insert(categories, "[[Thể loại:Thể loại gọi Bản mẫu:auto cat]]")
end
-- Check if the category is empty
local totalPages = pages_in_category(current_title.text, "all")
local hugeCategory = totalPages > 1000000 -- 1 million
-- Categorize huge categories, as they cause DynamicPageList to time out and make the category inaccessible.
if hugeCategory then
insert(categories, "[[Thể loại:Thể loại quá tải]]")
end
-- Are the parameters valid?
if not current then
-- Signal failure so export.show can try the next handler.
return nil, true
end
-- Does the category have the correct name?
local currentName = current:getCategoryName()
local correctName = current_title.text == currentName
if not correctName then
insert(categories, "[[Thể loại:Trang có tên thể loại không đúng]]")
insert(display, show_error(
("Theo dữ liệu trong cây thể loại, tên thể loại phải là '''[[:Thể loại:%s]]'''."):format(currentName),
"Thể loại này có tên gọi không đúng."
))
end
-- Add cleanup category for empty categories.
local canBeEmpty = current:canBeEmpty()
if canBeEmpty and correctName then
insert(categories, " __EXPECTUNUSEDCATEGORY__")
elseif totalPages == 0 then
insert(categories, "[[Thể loại:Thể loại trống]]")
end
if current:isHidden() then
insert(categories, " __HIDDENCAT__")
end
-- Put all the float-right stuff into a <div> that does not clear, so that float-left stuff like the breadcrumbs and
-- description can go opposite the float-right stuff without vertical space.
insert(boxes, "<div style=\"float: right;\">")
insert(boxes, show_topright(current))
insert(boxes, show_editlink(current))
insert(boxes, show_related_changes())
-- Show pagelist, unless it's a huge category (since they can't use DynamicPageList - see above).
if not hugeCategory then
insert(boxes, show_pagelist(current))
end
insert(boxes, "</div>")
-- Generate the displayed information
insert(display, show_breadcrumbs(current))
insert(display, show_also(current))
insert(display, show_description(current))
insert(display, show_appendix(current))
insert(display, show_children(current))
insert(display, show_TOC(current))
insert(display, show_catfix(current))
insert(display, '<br class="clear-both-in-vector-2022-only">')
show_categories(current, categories)
return concat(boxes, "\n") .. "\n" .. concat(display, "\n\n") .. concat(categories, "")
end
--[==[
List of handler functions that try to match the page name. A handler should return the name of a submodule to
[[Module:category tree]] and an info table which is passed as an argument to the submodule. If a handler does not
recognize the page name, it should return nil. Note that the order of handlers matters!
]==]
local handlers = {}
-- Thesaurus per-language category
insert(handlers, function(title)
local code, label = title:match("^Kho từ vựng:(%l[%a-]*%a):(.+)")
if code then
return poscatboiler_subsystem, {label = title, raw = true}
end
end)
-- Topic per-language category
insert(handlers, function(title)
local code, label = title:match("^(%l[%a-]*%a):(.+)")
if code then
return poscatboiler_subsystem, {label = title, raw = true}
end
end)
-- Lect category e.g. for [[:Category:New Zealand English]] or [[:Category:Issime Walser]]
insert(handlers, function(title, args)
local lect = args.lect or args.dialect
if lect ~= "" and yesno(lect, true) then -- Same as boolean in [[Module:parameters]].
return poscatboiler_subsystem, {label = title, args = args, raw = true}
end
end)
-- poscatboiler per-language label, e.g. [[Category:English non-lemma forms]]
insert(handlers, function(title, args)
local lang, label = export.split_lang_label(title)
if not lang then
return
end
local baseLabel, script = label:match("(.+) in (.-)$")
if script and baseLabel ~= "terms" then
local scriptObj = require("Module:scripts").getByCategoryName(script)
if scriptObj then
return poscatboiler_subsystem, {label = baseLabel, code = lang:getCode(), sc = scriptObj:getCode(), args = args}
end
end
return poscatboiler_subsystem, {label = label, code = lang:getCode(), args = args}
end)
-- poscatboiler label umbrella category
insert(handlers, function(title, args)
local label = title:match("(.+) theo ngôn ngữ$")
if label then
-- The poscatboiler code will appropriately lowercase if needed.
return poscatboiler_subsystem, {label = label, args = args}
end
end)
-- poscatboiler raw handlers
insert(handlers, function(title, args)
return poscatboiler_subsystem, {label = title, args = args, raw = true}
end)
-- poscatboiler umbrella handlers without 'by language'
insert(handlers, function(title, args)
return poscatboiler_subsystem, {label = title, args = args}
end)
function export.show(frame)
local args, other_args = require("Module:parameters").process(frame:getParent().args, {
["also"] = {type = "title", sublist = "comma without whitespace", namespace = 14}
}, true)
if args.also then
for k, arg in next, args.also do
args.also[k] = arg.prefixedText
end
end
for k, arg in next, other_args do
other_args[k] = trim(arg)
end
if namespace == 10 then -- Template
return "(Bản mẫu này chỉ được sử dụng trong không gian tên [[Trợ giúp:Thể loại|thể loại]].)"
elseif namespace ~= 14 then -- Category
error("Bản mẫu/mô đun này chỉ được sử dụng trong không gian tên [[Trợ giúp:Thể loại|thể loại]].")
end
local saw_tree_lookup_failure = false
local failure_consumed_args = false
-- Go through each handler in turn. If a handler doesn't recognize the format of the category, it will return nil,
-- and we will consider the next handler. Otherwise, it returns a template name and arguments to call it with, but
-- even then, that template might return an error, and we need to consider the next handler. This happens, for
-- example, with the category "CAT:Mato Grosso, Brazil", where "Mato" is the name of a language, so the poscatboiler
-- per-language label handler fires and tries to find a label "Grosso, Brazil". This throws an error, and
-- previously, this blocked further handler consideration, but now we check for the error and continue checking
-- handlers; eventually, the topic umbrella handler will fire and correctly handle the category.
for _, handler in ipairs(handlers) do
-- Use a new title object and args table for each handler, to keep them isolated.
local submodule, info = handler(current_title.text, deep_copy(other_args))
if submodule then
info.also = deep_copy(args.also)
require("Module:debug").track("auto cat/" .. submodule)
-- `failed` is true if no match was found in the category tree.
submodule = require(category_tree_submodule_prefix .. submodule)
local cattext, failed = generate_output(submodule.main(info))
if failed then
saw_tree_lookup_failure = true
failure_consumed_args = failure_consumed_args or not not info.args
elseif not info.args and next(other_args) then
error(extra_args_error)
else
return cattext
end
end
end
-- No handler produced a valid category-tree match.
-- Preserve previous behavior: if no failing handler consumed args, surface extra-arg errors first.
if saw_tree_lookup_failure and not failure_consumed_args and next(other_args) then
error(extra_args_error)
end
error_not_in_category_tree()
end
-- TODO: new test entrypoint.
return export
jpf509srn8whoopq90aspod9b2qytqs
Mô đun:category tree/poscatboiler
828
279365
2573969
2363015
2026-10-28T08:07:41Z
Hiyuune
50834
tạm cập nhật
2573969
Scribunto
text/plain
local lang_independent_data = require("Module:category tree/data")
local lang_specific_module = "Module:category tree/lang"
local lang_specific_module_prefix = lang_specific_module .. "/"
local family_specific_module = "Module:category tree/fam"
local family_specific_module_prefix = family_specific_module .. "/"
local labels_utilities_module = "Module:labels/utilities"
local template_parser_module = "Module:template parser"
local concat = table.concat
local dump = mw.dumpObject
local expand_template = require("Module:frame").expandTemplate
local insert = table.insert
local is_callable = require("Module:fun").is_callable
local lcfirst = require("Module:string utilities").lcfirst
local list_to_set = require("Module:table").listToSet
local make_title = mw.title.makeTitle
local new_title = mw.title.new
local parse = require(template_parser_module).parse
local sparse_concat = require("Module:table").sparseConcat
local tostring = tostring
local type = type
local ucfirst = require("Module:string utilities").ucfirst
local uupper = require("Module:string utilities").upper
local function internal_error(msg)
error("Internal error: " .. msg)
end
local function get_lang(...)
local _get_lang = require("Module:languages").getByCode
function get_lang(...)
return _get_lang(...) or require("Module:languages/errorGetBy").code(...)
end
return get_lang(...)
end
local function get_script(...)
local _get_script = require("Module:scripts").getByCode
function get_script(code)
return _get_script(code) or require("Module:languages/error")(code, true, "script code")
end
return get_script(...)
end
-- Category object
local Category = {}
Category.__index = Category
function Category:get_originating_info()
local originating_info = ""
if self._info.originating_label then
originating_info = " (originating from label \"" .. self._info.originating_label .. "\" in module [[" .. self._info.originating_module .. "]])"
end
return originating_info
end
local valid_keys = list_to_set{"code", "label", "sc", "raw", "args", "also", "called_from_inside", "originating_label", "originating_module"}
function Category.new(info)
for key in pairs(info) do
if not valid_keys[key] then
internal_error("The parameter \"" .. key .. "\" was not recognized.")
end
end
local self = setmetatable({}, Category)
self._info = info
if not self._info.label then
internal_error("No label was specified.")
end
self:initCommon()
if not self._data then
internal_error("The " .. (self._info.raw and "raw " or "") .. "label \"" .. self._info.label .. "\" does not exist" .. self:get_originating_info() .. ".")
end
return self
end
function Category:initCommon()
local function patch_args(args)
-- This fixes the issue with Scribunto automatically converting keys
-- in a table as numbers to strings, which in turn causes a circular
-- error for having argument parameter names as numbers as strings.
if type(args) ~= "table" then
return args
end
local new_args = {}
for k, v in pairs(args) do
if type(k) == "string" and string.len(k) < 10 and not string.match(k, "^0") and string.match(k, "^%d+$") then
new_args[tonumber(k)] = patch_args(v)
else
new_args[k] = patch_args(v)
end
end
return new_args
end
local args_handled = false
if self._info.raw then
-- Check if the category exists
local raw_categories = lang_independent_data["RAW_CATEGORIES"]
self._data = raw_categories[self._info.label]
if self._data then
if self._data.lang then
self._lang = get_lang(self._data.lang, nil, true)
self._info.code = self._lang:getCode()
end
if self._data.sc then
self._sc = get_script(self._data.sc)
self._info.sc = self._sc:getCode()
end
else
-- Go through raw handlers
local data = {
category = self._info.label,
args = patch_args(self._info.args) or {},
called_from_inside = self._info.called_from_inside,
}
for _, handler in ipairs(lang_independent_data["RAW_HANDLERS"]) do
self._data, args_handled = handler.handler(data)
if self._data then
self._data.module = self._data.module or handler.module
break
end
end
if self._data then
-- Update the label if the handler specified a canonical name for it.
if self._data.canonical_name then
self._info.canonical_name = self._data.canonical_name
end
if self._data.lang then
if type(self._data.lang) ~= "string" then
internal_error("Received non-string value " .. dump(self._data.lang) .. " for self._data.lang, label \"" .. self._info.label .. "\"" .. self:get_originating_info() .. ".")
end
self._lang = get_lang(self._data.lang, nil, true)
self._info.code = self._lang:getCode()
end
if self._data.sc then
if type(self._data.sc) ~= "string" then
internal_error("Received non-string value " .. dump(self._data.sc) .. " for self._data.sc, label \"" .. self._info.label .. "\"" .. self:get_originating_info() .. ".")
end
self._sc = get_script(self._data.sc)
self._info.sc = self._sc:getCode()
end
end
end
else
-- Already parsed into language + label
if self._info.code then
self._lang = get_lang(self._info.code, nil, true)
else
self._lang = nil
end
if self._info.sc then
self._sc = get_script(self._info.sc)
else
self._sc = nil
end
self._info.orig_label = self._info.label
if not self._lang then
-- Umbrella categories without a preceding language always begin with a capital letter, but the actual label may be
-- lowercase (cf. [[:Category:Nouns by language]] with label 'nouns' with per-language [[:Category:English nouns]];
-- but [[:Category:Reddit slang by language]] with label 'Reddit slang' with per-language
-- [[:Category:English Reddit slang]]). Since the label is almost always lowercase, we lowercase it for umbrella
-- categories, storing the original into `orig_label`, and correct it later if needed.
self._info.label = ucfirst(self._info.label)
end
-- First, check lang-specific labels and handlers if this is not an umbrella category.
if self._lang then
local objects_with_modules = require(lang_specific_module)
local obj, seen = self._lang, {}
local object_specific_module_prefix = lang_specific_module_prefix
local is_family = false
repeat
if objects_with_modules[obj:getCode()] then
local module = object_specific_module_prefix .. obj:getCode()
local labels_and_handlers = require(module)
if labels_and_handlers.LABELS then
self._data = labels_and_handlers.LABELS[self._info.label]
if self._data then
if not is_family and self._data.umbrella == nil and self._data.umbrella_parents == nil then
self._data.umbrella = false
end
self._data.module = self._data.module or module
end
end
if not self._data and labels_and_handlers.HANDLERS then
for _, handler in ipairs(labels_and_handlers.HANDLERS) do
local data = {
label = self._info.label,
lang = self._lang,
sc = self._sc,
args = patch_args(self._info.args) or {},
called_from_inside = self._info.called_from_inside,
}
self._data, args_handled = handler(data)
if self._data then
if not is_family and self._data.umbrella == nil and
self._data.umbrella_parents == nil then
self._data.umbrella = false
end
self._data.module = self._data.module or module
break
end
end
end
if self._data then
break
end
end
seen[obj:getCode()] = true
obj = obj:getFamily()
if not is_family then
is_family = true
object_specific_module_prefix = family_specific_module_prefix
objects_with_modules = require(family_specific_module)
end
until not obj or seen[obj:getCode()]
end
local function fetch_label_data(labels)
self._data = labels[self._info.label]
-- See comment above about uppercase- vs. lowercase-initial labels, which are indistinguishable
-- in umbrella categories.
if not self._data then
self._data = labels[self._info.orig_label]
if self._data then
self._info.label = self._info.orig_label
end
end
end
-- Then check lang-independent labels.
if not self._data then
-- lang_independent_data.LABELS should always exist.
fetch_label_data(lang_independent_data.LABELS)
if not self._data and not self._lang then
-- Check family-specific labels for umbrella label.
local families_with_modules = require(family_specific_module)
for famcode, _ in pairs(families_with_modules) do
local module = family_specific_module_prefix .. famcode
local labels_and_handlers = require(module)
if labels_and_handlers.LABELS then
fetch_label_data(labels_and_handlers.LABELS)
if self._data then
self._data.module = self._data.module or module
break
end
end
end
end
end
-- Then check lang-independent handlers.
if not self._data then
local data = {
label = self._info.label,
lang = self._lang,
sc = self._sc,
args = patch_args(self._info.args) or {},
called_from_inside = self._info.called_from_inside,
}
for _, handler in ipairs(lang_independent_data["HANDLERS"]) do
self._data, args_handled = handler.handler(data)
if self._data then
self._data.module = self._data.module or handler.module
break
end
end
if not self._data and not self._lang then
-- Check family-specific labels for umbrella handler.
local families_with_modules = require(family_specific_module)
for famcode, _ in pairs(families_with_modules) do
local module = family_specific_module_prefix .. famcode
local labels_and_handlers = require(module)
if labels_and_handlers.HANDLERS then
for _, handler in ipairs(labels_and_handlers.HANDLERS) do
local data = {
label = self._info.label,
sc = self._sc,
args = patch_args(self._info.args) or {},
called_from_inside = self._info.called_from_inside,
}
self._data, args_handled = handler(data)
if self._data then
self._data.module = self._data.module or module
break
end
end
end
if self._data then
break
end
end
end
end
end
if not args_handled and self._data and self._info.args and next(self._info.args) then
local module_text = " (handled in [[" .. (self._data.module or "UNKNOWN").. "]])"
local args_text = {}
for k, v in pairs(self._info.args) do
insert(args_text, k .. "=" .. ((type(v) == "string" or type(v) == "number") and v or dump(v)))
end
error("poscatboiler label '" .. self._info.label .. "' " .. module_text .. " doesn't accept extra args " ..
concat(args_text, ", "))
end
if self._sc and not self._lang then
internal_error("Umbrella categories cannot have a script specified.")
end
end
function Category:convert_spec_to_string(desc)
if not desc then
return desc
end
local desc_type = type(desc)
if desc_type == "string" then
return desc
elseif desc_type == "number" then
return tostring(desc)
elseif not is_callable(desc) then
internal_error("`desc` must be a string, number, function, callable table or nil; received " .. dump(desc))
end
desc = desc {
lang = self._lang,
sc = self._sc,
label = self._info.label,
raw = self._info.raw,
}
if not desc then
return desc
end
desc_type = type(desc)
if desc_type == "string" then
return desc
end
internal_error("The value returned by `desc` must be a string or nil; received " .. dump(desc))
end
local function add_obj_args(args, obj, obj_type, sc_lang)
if obj then
args[obj_type .. "code"] = obj:getCode()
args[obj_type .. "name"] = obj:getCanonicalName(sc_lang)
args[obj_type .. "disp"] = obj:getDisplayForm(sc_lang)
args[obj_type .. "cat"] = obj:getCategoryName(false, sc_lang)
args[obj_type .. "link"] = obj:makeCategoryLink(sc_lang)
end
end
-- Expands `desc` like a template, passing values for specs like {{{langname}}}.
function Category:substitute_template_specs(desc)
-- This may end up happening twice but that's OK as the function is (usually) idempotent.
-- FIXME: Not idempotent if a preprocessed template returns wikicode.
desc = self:convert_spec_to_string(desc)
if not desc then
return nil
end
-- Populate the substitution arguments.
local args = {}
args.umbrella_msg = "This is an umbrella category. It contains no dictionary entries, but only other, language-specific categories, which in turn contain relevant terms in a given language."
args.umbrella_meta_msg = "This is an umbrella metacategory, covering a general area such as \"lemmas\", \"names\" or \"terms by etymology\". It contains no dictionary entries, but holds only umbrella (\"by language\") categories covering specific subtopics, which in turn contain language-specific categories holding terms in a given language for that same topic."
add_obj_args(args, self._lang, "lang")
add_obj_args(args, self._sc, "sc", self._lang)
return parse(desc, true):expand(args)
end
function Category:substitute_template_specs_in_args(args)
if not args then
return args
end
local pinfo = {}
for k, v in pairs(args) do
pinfo[self:substitute_template_specs(k)] = self:substitute_template_specs(v)
end
return pinfo
end
function Category:make_new(info)
info.originating_label = self._info.label
info.originating_module = self._data.module
info.called_from_inside = true
return Category.new(info)
end
function Category:getBreadcrumbName()
local ret
if self._sc then
-- The parent of 'LANG POS in SCRIPT script' is always 'SCRIPT script' regardless of the parents normally set,
-- so the breadcrumb should always be 'POS' regardless of the breadcrumb normally set.
return self._info.label, nil
end
if self._lang or self._info.raw then
ret = self._data.breadcrumb or
self._data.breadcrumb_base or self._data.breadcrumb_and_first_sort_base or
self._data.breadcrumb_key or self._data.breadcrumb_and_first_sort_key or
nil
else
ret = self._data.umbrella and (self._data.umbrella.breadcrumb or
self._data.umbrella.breadcrumb_base or self._data.umbrella.breadcrumb_and_first_sort_base or
self._data.umbrella.breadcrumb_key or self._data.umbrella.breadcrumb_and_first_sort_key) or
nil
end
if not ret then
ret = self._info.label
end
if type(ret) ~= "table" then
ret = {name = ret}
end
return self:substitute_template_specs(ret.name), ret.nocap
end
local function expand_toc_template_if(template)
local template_obj = new_title(template, 10)
if template_obj.exists then
return expand_template{title = template_obj.text}
end
return nil
end
-- Return the textual expansion of the first existing template among the given templates, first performing
-- substitutions on the template name such as replacing {{{langcode}}} with the current language's code (if any).
-- If no templates exist after expansion, or if nil is passed in, return nil. If a single string is passed in,
-- treat it like a one-element list consisting of that string.
function Category:get_template_text(templates)
if templates == nil then
return nil
elseif type(templates) ~= "table" then
templates = {templates}
end
for _, template in ipairs(templates) do
if template == false then
return false
end
template = self:substitute_template_specs(template)
return expand_toc_template_if(template)
end
return nil
end
function Category:getTOC(toc_type)
-- Type "none" means everything fits on a single page; in that case, display nothing.
if toc_type == "none" then
return nil
end
local templates, fallback_templates
-- If TOC type is "full" (more than 2500 entries), do the following, in order:
-- 1. look up and expand the `toc_template_full` templates (normal or umbrella, depending on whether there is
-- a current language);
-- 2. look up and expand the `toc_template` templates (normal or umbrella, as above);
-- 3. do the default behavior, which is as follows:
-- 3a. look up a language-specific "full" template according to the current language (using English if there
-- is no current language);
-- 3b. look up a script-specific "full" template according to the first script of current language (using English
-- if there is no current language);
-- 3c. look up a language-specific "normal" template according to the current language (using English if there
-- is no current language);
-- 3d. look up a script-specific "normal" template according to the first script of the current language (using
-- English if there is no current language);
-- 3e. display nothing.
--
-- If TOC type is "normal" (between 200 and 2500 entries), do the following, in order:
-- 1. look up and expand the `toc_template` templates (normal or umbrella, depending on whether there is
-- a current language);
-- 2. do the default behavior, which is as follows:
-- 2a. look up a language-specific "normal" template according to the current language (using English if there
-- is no current language);
-- 2b. look up a script-specific "normal" template according to the first script of the current language (using
-- English if there is no current language);
-- 2c. display nothing.
local data_source
if self._lang or self._info.raw then
data_source = self._data
else
data_source = self._data.umbrella
end
if data_source then
if toc_type == "full" then
templates = data_source.toc_template_full
fallback_templates = data_source.toc_template
else
templates = data_source.toc_template
end
end
local text = self:get_template_text(templates)
if text then
return text
elseif text == false then
return nil
end
text = self:get_template_text(fallback_templates)
if text then
return text
elseif text == false then
return nil
end
local default_toc_templates_to_check = {}
local lang, sc = self:getCatfixInfo()
local langcode = lang and lang:getCode() or "en"
local sccode = sc and sc:getCode() or lang and lang:getScriptCodes()[1] or "Latn"
-- FIXME: What is toctemplateprefix used for?
local tocname = (self._data.toctemplateprefix or "") .. "categoryTOC"
if toc_type == "full" then
insert(default_toc_templates_to_check, ("%s-%s/full"):format(langcode, tocname))
insert(default_toc_templates_to_check, ("%s-%s/full"):format(sccode, tocname))
end
insert(default_toc_templates_to_check, ("%s-%s"):format(langcode, tocname))
insert(default_toc_templates_to_check, ("%s-%s"):format(sccode, tocname))
for _, toc_template in ipairs(default_toc_templates_to_check) do
local toc_template_text = expand_toc_template_if(toc_template)
if toc_template_text then
return toc_template_text
end
end
return nil
end
function Category:getInfo()
return self._info
end
function Category:getDataModule()
return self._data.module
end
function Category:canBeEmpty()
if self._lang or self._info.raw then
return self._data.can_be_empty
end
return self._data.umbrella and self._data.umbrella.can_be_empty
end
function Category:isHidden()
if self._lang or self._info.raw then
return self._data.hidden
end
return self._data.umbrella and self._data.umbrella.hidden
end
function Category:getCategoryName()
if self._info.raw then
return self._info.canonical_name or self._info.label
elseif self._lang then
local ret = self._info.label .. " " .. self._lang:getCanonicalName()
if self._sc then
ret = ret .. " in " .. self._sc:getDisplayForm(self._lang)
end
return ucfirst(ret)
end
local ret = ucfirst(self._info.label)
if not (self._data.no_by_language or self._data.umbrella and self._data.umbrella.no_by_language) then
ret = ret .. " by language"
end
return ret
end
function Category:getTopright()
if self._lang or self._info.raw then
return self:substitute_template_specs(self._data.topright)
end
return self._data.umbrella and self:substitute_template_specs(self._data.umbrella.topright)
end
function Category:display_title(displaytitle, lang)
if type(displaytitle) == "string" then
displaytitle = self:substitute_template_specs(displaytitle)
else
displaytitle = displaytitle(self:getCategoryName(), lang)
end
mw.getCurrentFrame():callParserFunction("DISPLAYTITLE", "Category:" .. displaytitle)
end
function Category:get_labels_categorizing()
local m_labels_utilities = require(labels_utilities_module)
local pos_cat_labels, sense_cat_labels, use_tlb
pos_cat_labels = m_labels_utilities.find_labels_for_category(self._info.label, "pos", self._lang)
local sense_label = self._info.label:match("^(.*) terms$")
if sense_label then
use_tlb = true
else
sense_label = self._info.label:match("^terms with (.*) senses$")
end
if not sense_label then
return nil
end
sense_cat_labels = m_labels_utilities.find_labels_for_category(sense_label, "sense", self._lang)
if use_tlb then
return m_labels_utilities.format_labels_categorizing(pos_cat_labels, sense_cat_labels, self._lang)
end
local all_labels = pos_cat_labels
for k, v in pairs(sense_cat_labels) do
all_labels[k] = v
end
return m_labels_utilities.format_labels_categorizing(all_labels, nil, self._lang)
end
-- FIXME: this is clunky.
local function remove_lang_params(desc)
-- Simply remove a language name/code/category from the beginning of the string, but replace the language name
-- in the middle of the string with either "specific languages" or "specific-language" depending on whether the
-- language name appears to be an attributive qualifier of another noun or to stand by itself. This may be wrong,
-- in which case the category in question should supply its own umbrella description.
desc = desc:gsub("^{{{langname}}} ", "")
:gsub("{{{langname}}} %(", "specific languages (")
:gsub("{{{langname}}}([.,])", "specific languages%1")
:gsub("{{{langname}}} ", "specific-language ")
:gsub("{{{langdisp}}}", "specific languages")
:gsub("{{{langlink}}}", "specific languages")
return desc
end
function Category:getDescription(isChild)
-- Allows different text in the list of a category's children
local isChild = isChild == "child"
if self._lang or self._info.raw then
if not isChild and self._data.displaytitle then
self:display_title(self._data.displaytitle, self._lang)
end
if self._sc then
return self:getCategoryName() .. "."
end
local desc = self:substitute_template_specs(self._data.description)
if not desc then
return nil
elseif isChild then
return desc
end
return sparse_concat({
self:substitute_template_specs(self._data.preceding),
desc,
self:substitute_template_specs(self._data.additional),
self:substitute_template_specs(self:get_labels_categorizing()),
}, "\n\n")
end
local umbrella = self._data.umbrella
if not isChild and umbrella and umbrella.displaytitle then
self:display_title(umbrella.displaytitle)
end
local desc = self:substitute_template_specs(umbrella and umbrella.description)
local has_umbrella_desc = not not desc
if not desc then
desc = self:convert_spec_to_string(self._data.description)
if desc then
desc = remove_lang_params(desc)
desc = lcfirst(desc)
desc = desc:gsub("%.$", "")
desc = "Categories with " .. desc .. "."
else
desc = "Categories with " .. self._info.label .. " in various specific languages."
end
desc = self:substitute_template_specs(desc)
end
if isChild then
return desc
end
return sparse_concat({
self:substitute_template_specs(umbrella and umbrella.preceding or not has_umbrella_desc and self._data.preceding),
desc,
self:substitute_template_specs(umbrella and umbrella.additional or not has_umbrella_desc and self._data.additional),
self:substitute_template_specs("{{{umbrella_msg}}}"),
self:substitute_template_specs(self:get_labels_categorizing()),
}, "\n\n")
end
function Category:new_sortkey(sortkey)
local sortkey_type = type(sortkey)
if sortkey_type == "string" then
sortkey = uupper(sortkey)
elseif sortkey_type == "table" then
function sortkey:makeSortKey()
local sort_func = self.sort_func
if sort_func ~= nil then
return sort_func(self.sort_base)
end
local lang = self.lang
if lang == nil then
return self.sort_base
end
lang = get_lang(lang, nil, true)
if lang == nil then
return self.sort_base
end
local sc = self.sc
if sc ~= nil then
sc = get_script(sc)
end
return lang:makeSortKey(self.sort_base, sc)
end
end
return sortkey
end
function Category:inherit_spec(spec, parent_spec, substitute_result)
if spec == false then
return nil
end
local retval = spec or parent_spec
if substitute_result then
retval = self:substitute_template_specs(retval)
end
return retval
end
function Category:canonicalize_parents_children(cats, is_children, fallback_sort_key, fallback_sort_base)
if not cats then
return nil
elseif type(cats) == "table" then
if cats.name or cats.module then
cats = {cats}
elseif #cats == 0 then
return nil
end
else
cats = {cats}
end
local ret = {}
for _, cat in ipairs(cats) do
if type(cat) ~= "table" or not cat.name and not cat.module then
cat = {name = cat}
end
insert(ret, cat)
end
local is_umbrella = not self._lang and not self._info.raw
local table_type = is_children and "extra_children" or "parents"
for i, cat in ipairs(ret) do
local raw
if self._info.raw or is_umbrella then
raw = not cat.is_label
else
raw = cat.raw
end
local lang = self:inherit_spec(cat.lang, not raw and self._info.code or nil, "substitute")
local sc = self:inherit_spec(cat.sc, not raw and self._info.sc or nil, "substitute")
-- Get the sortkey.
local sortkey = self:inherit_spec(cat.sort, i == 1 and (fallback_sort_key or fallback_sort_base and {sort_base = fallback_sort_base}) or nil)
if type(sortkey) == "table" then
sortkey.sort_base = self:substitute_template_specs(sortkey.sort_base) or
internal_error("Missing .sort_base in '" .. table_type .. "' .sort table for '" ..
self._info.label .. "' category entry in module '" .. (self._data.module or "unknown") .. "'")
if sortkey.sort_func then
-- Not allowed to give a lang and/or script if sort_func is given.
local bad_spec = sortkey.lang and "lang" or sortkey.sc and "sc" or nil
if bad_spec then
internal_error("Cannot specify both ." .. bad_spec .. " and .sort_func in '" .. table_type ..
"' .sort table for '" .. self._info.label .. "' category entry in module '" ..
(self._data.module or "unknown") .. "'")
end
else
sortkey.lang = self:inherit_spec(sortkey.lang, lang, "substitute")
sortkey.sc = self:inherit_spec(sortkey.sc, sc, "substitute")
end
else
sortkey = self:substitute_template_specs(sortkey)
end
local name
if cat.module then
-- A reference to a category using another category tree module.
if not cat.args then
internal_error("Missing .args in '" .. table_type .. "' table with module=\"" .. cat.module .. "\" for '" ..
self._info.label .. "' category entry in module '" .. (self._data.module or "unknown") .. "'")
end
name = require("Module:category tree/" .. cat.module).new(self:substitute_template_specs_in_args(cat.args))
else
name = cat.name
if not name then
internal_error("Missing .name in " .. (is_umbrella and "umbrella " or "") .. "'" .. table_type .. "' table for '" ..
self._info.label .. "' category entry in module '" .. (self._data.module or "unknown") .. "'")
elseif type(name) == "string" then -- otherwise, assume it's a category object and use it directly
name = self:substitute_template_specs(name)
if name:find("^Thể loại:") then
-- It's a non-poscatboiler category name.
sortkey = sortkey or is_children and name:gsub("^Category:", "") or self:getCategoryName()
else
-- It's a label.
sortkey = sortkey or is_children and name or self._info.label
name = self:make_new{
label = name, code = lang, sc = sc,
raw = raw, args = self:substitute_template_specs_in_args(cat.args)
}
end
end
end
sortkey = sortkey or is_children and " " or self._info.label
ret[i] = {
name = name,
description = is_children and self:substitute_template_specs(cat.description) or nil,
sort = self:new_sortkey(sortkey)
}
end
return ret
end
function Category:getParents()
local is_umbrella, ret = not self._lang and not self._info.raw
if self._sc then
local parent1 = self:make_new{code = self._info.code, label = "terms in " .. self._sc:getDisplayForm(self._lang)}
local parent2 = self:make_new{code = self._info.code, label = self._info.label, raw = self._info.raw, args = self._info.args}
ret = {
{name = parent1, sort = self._sc:getCanonicalName(self._lang)},
{name = parent2, sort = self._sc:getCanonicalName(self._lang)},
}
else
local parents, fallback_sort_base, fallback_sort_key
if is_umbrella then
parents = self._data.umbrella and self._data.umbrella.parents or self._data.umbrella_parents
fallback_sort_base = self._data.umbrella and (self._data.umbrella.breadcrumb_base or self._data.umbrella.breadcrumb_and_first_sort_base) or nil
fallback_sort_key = self._data.umbrella and (self._data.umbrella.breadcrumb_key or self._data.umbrella.breadcrumb_and_first_sort_key) or nil
else
parents = self._data.parents
fallback_sort_base = self._data.breadcrumb_base or self._data.breadcrumb_and_first_sort_base
fallback_sort_key = self._data.breadcrumb_key or self._data.breadcrumb_and_first_sort_key
end
ret = self:canonicalize_parents_children(parents, nil, fallback_sort_key, fallback_sort_base)
if not ret then
return nil
end
end
local self_cat = self:getCategoryName()
for _, parent in ipairs(ret) do
local parent_cat = parent.name.getCategoryName and parent.name:getCategoryName()
if self_cat == parent_cat then
internal_error(("Infinite loop would occur, as parent category '%s' is the same as the child category"):format(self_cat))
end
end
return ret
end
function Category:getChildren()
local is_umbrella = not self._lang and not self._info.raw
local children = self._data.children
local ret = {}
if not is_umbrella and children then
for _, child in ipairs(children) do
child = mw.clone(child)
if type(child) ~= "table" then
child = {name = child}
end
if not child.sort then
child.sort = child.name
end
-- FIXME, is preserving the script correct?
child.name = self:make_new{code = self._info.code, label = child.name, raw = child.raw, sc = self._info.sc}
insert(ret, child)
end
end
local extra_children
if is_umbrella then
extra_children = self._data.umbrella and self._data.umbrella.extra_children
else
extra_children = self._data.extra_children
end
extra_children = self:canonicalize_parents_children(extra_children, "children")
if extra_children then
for _, child in ipairs(extra_children) do
insert(ret, child)
end
end
return #ret > 0 and ret or nil
end
function Category:getUmbrella()
local umbrella = self._data.umbrella
if umbrella == false or self._info.raw or not self._lang or self._sc then
return nil
end
-- If `umbrella` is a string, use that; otherwise, use the label.
return self:make_new({label = type(umbrella) == "string" and umbrella or self._info.label})
end
function Category:getAppendix()
-- FIXME, this should be customizable.
local lang, label = self._lang, self._info.label
if self._info.raw or not (lang and label) then
return nil
end
local appendix = make_title(100, lang:getCanonicalName() .. " " .. label)
return appendix.exists and appendix.fullText or nil
end
function Category:getCatfixInfo()
if self._lang or self._sc or self._info.raw then
local langcode, sccode = self._data.catfix, self._data.catfix_sc
local lang, sc
if langcode then
langcode = self:substitute_template_specs(langcode)
lang = get_lang(langcode, nil, true)
elseif langcode == nil then -- not false
lang = self._lang
end
if sccode then
sccode = self:substitute_template_specs(sccode)
sc = get_script(sccode)
elseif sccode == nil then -- not false
sc = self._sc
end
if lang then
lang = lang:getFull()
end
return lang, sc
elseif not self._data.umbrella then
return
end
-- umbrella
local langcode, sccode = self._data.umbrella.catfix, self._data.umbrella.catfix_sc
local lang, sc
if langcode then
langcode = self:substitute_template_specs(langcode)
lang = get_lang(langcode, nil, true)
end
if sccode then
sccode = self:substitute_template_specs(sccode)
sc = get_script(sccode)
end
if lang then
lang = lang:getFull()
end
return lang, sc
end
function Category:getTOCTemplateName()
-- This should only be invoked if getTOC() returns true, meaning to do the default algorithm, but getTOC()
-- implements its own default algorithm.
internal_error("This should never get called")
end
local export = {}
function export.main(info)
local self = setmetatable({_info = info}, Category)
self:initCommon()
return self._data and self or nil
end
export.new = Category.new
return export
pk63x9jprmhri8wowawb0873kqfv6h3
2573970
2573969
2026-10-28T08:15:09Z
Hiyuune
50834
Restored revision 2363015 by [[Special:Contributions/TheHighFighter2|TheHighFighter2]] ([[en:w:User:BrandonXLF/Restorer|Restorer]])
2573970
Scribunto
text/plain
local lang_independent_data = require("Module:category tree/data")
local lang_specific_module = "Module:category tree/lang"
local lang_specific_module_prefix = lang_specific_module .. "/"
local family_specific_module = "Module:category tree/fam"
local family_specific_module_prefix = family_specific_module .. "/"
local labels_utilities_module = "Module:labels/utilities"
local template_parser_module = "Module:template parser"
local concat = table.concat
local dump = mw.dumpObject
local expand_template = require("Module:frame").expandTemplate
local insert = table.insert
local is_callable = require("Module:fun").is_callable
local ucfirst = require("Module:string utilities").ucfirst
local lcfirst = require("Module:string utilities").lcfirst
local list_to_set = require("Module:table").listToSet
local make_title = mw.title.makeTitle
local new_title = mw.title.new
local parse = require(template_parser_module).parse
local sparse_concat = require("Module:table").sparseConcat
local tostring = tostring
local type = type
local ucfirst = require("Module:string utilities").ucfirst
local uupper = require("Module:string utilities").upper
local function get_lang(...)
local _get_lang = require("Module:languages").getByCode
function get_lang(...)
return _get_lang(...) or require("Module:languages/errorGetBy").code(...)
end
return get_lang(...)
end
local function get_script(...)
local _get_script = require("Module:scripts").getByCode
function get_script(code)
return _get_script(code) or require("Module:languages/error")(code, true, "script code")
end
return get_script(...)
end
-- Category object
local Category = {}
Category.__index = Category
function Category:get_originating_info()
local originating_info = ""
if self._info.originating_label then
originating_info = " (tên nhãn chính \"" .. self._info.originating_label .. "\" trong [[" .. self._info.originating_module .. "]])"
end
return originating_info
end
local valid_keys = list_to_set{"code", "label", "sc", "raw", "args", "also", "called_from_inside", "originating_label", "originating_module"}
function Category.new(info)
for key in pairs(info) do
if not valid_keys[key] then
error("The parameter \"" .. key .. "\" was not recognized.")
end
end
local self = setmetatable({}, Category)
self._info = info
if not self._info.label then
error("No label was specified.")
end
self:initCommon()
if not self._data then
error("tên nhãn" .. (self._info.raw and " " or "") .. " \"" .. self._info.label .. "\" không tồn tại hoặc không có sẵn " .. self:get_originating_info() .. ". Vui lòng kiểm tra xem nhãn có lỗi chính tả và trùng khớp với nhãn dữ liệu mô đun hay không")
end
return self
end
function Category:initCommon()
local function patch_args(args)
-- This fixes the issue with Scribunto automatically converting keys
-- in a table as numbers to strings, which in turn causes a circular
-- error for having argument parameter names as numbers as strings.
if type(args) ~= "table" then
return args
end
local new_args = {}
for k, v in pairs(args) do
if type(k) == "string" and string.len(k) < 10 and not string.match(k, "^0") and string.match(k, "^%d+$") then
new_args[tonumber(k)] = patch_args(v)
else
new_args[k] = patch_args(v)
end
end
return new_args
end
local args_handled = false
if self._info.raw then
-- Check if the category exists
local raw_categories = lang_independent_data["RAW_CATEGORIES"]
self._data = raw_categories[self._info.label]
if self._data then
if self._data.lang then
self._lang = get_lang(self._data.lang, nil, true)
self._info.code = self._lang:getCode()
end
if self._data.sc then
self._sc = get_script(self._data.sc)
self._info.sc = self._sc:getCode()
end
else
-- Go through raw handlers
local data = {
category = self._info.label,
args = patch_args(self._info.args) or {},
called_from_inside = self._info.called_from_inside,
}
for _, handler in ipairs(lang_independent_data["RAW_HANDLERS"]) do
self._data, args_handled = handler.handler(data)
if self._data then
self._data.module = self._data.module or handler.module
break
end
end
if self._data then
-- Update the label if the handler specified a canonical name for it.
if self._data.canonical_name then
self._info.canonical_name = self._data.canonical_name
end
if self._data.lang then
if type(self._data.lang) ~= "string" then
internal_error("Received non-string value " .. dump(self._data.lang) .. " for self._data.lang, label \"" .. self._info.label .. "\"" .. self:get_originating_info() .. ".")
end
self._lang = get_lang(self._data.lang, nil, true)
self._info.code = self._lang:getCode()
end
if self._data.sc then
if type(self._data.sc) ~= "string" then
internal_error("Received non-string value " .. dump(self._data.sc) .. " for self._data.sc, label \"" .. self._info.label .. "\"" .. self:get_originating_info() .. ".")
end
self._sc = get_script(self._data.sc)
self._info.sc = self._sc:getCode()
end
end
end
else
-- Already parsed into language + label
if self._info.code then
self._lang = get_lang(self._info.code, nil, true)
else
self._lang = nil
end
if self._info.sc then
self._sc = get_script(self._info.sc)
else
self._sc = nil
end
self._info.orig_label = self._info.label
if not self._lang then
-- Umbrella categories without a preceding language always begin with a capital letter, but the actual label may be
-- lowercase (cf. [[:Category:Nouns by language]] with label 'nouns' with per-language [[:Category:English nouns]];
-- but [[:Category:Reddit slang by language]] with label 'Reddit slang' with per-language
-- [[:Category:English Reddit slang]]). Since the label is almost always lowercase, we lowercase it for umbrella
-- categories, storing the original into `orig_label`, and correct it later if needed.
self._info.label = ucfirst(self._info.label)
end
-- First, check lang-specific labels and handlers if this is not an umbrella category.
if self._lang then
local objects_with_modules = require(lang_specific_module)
local obj, seen = self._lang, {}
local object_specific_module_prefix = lang_specific_module_prefix
local is_family = false
repeat
if objects_with_modules[obj:getCode()] then
local module = object_specific_module_prefix .. obj:getCode()
local labels_and_handlers = require(module)
if labels_and_handlers.LABELS then
self._data = labels_and_handlers.LABELS[self._info.label]
if self._data then
if not is_family and self._data.umbrella == nil and self._data.umbrella_parents == nil then
self._data.umbrella = false
end
self._data.module = self._data.module or module
end
end
if not self._data and labels_and_handlers.HANDLERS then
for _, handler in ipairs(labels_and_handlers.HANDLERS) do
local data = {
label = self._info.label,
lang = self._lang,
sc = self._sc,
args = patch_args(self._info.args) or {},
called_from_inside = self._info.called_from_inside,
}
self._data, args_handled = handler(data)
if self._data then
if not is_family and self._data.umbrella == nil and
self._data.umbrella_parents == nil then
self._data.umbrella = false
end
self._data.module = self._data.module or module
break
end
end
end
if self._data then
break
end
end
seen[obj:getCode()] = true
obj = obj:getFamily()
if not is_family then
is_family = true
object_specific_module_prefix = family_specific_module_prefix
objects_with_modules = require(family_specific_module)
end
until not obj or seen[obj:getCode()]
end
local function fetch_label_data(labels)
self._data = labels[self._info.label]
-- See comment above about uppercase- vs. lowercase-initial labels, which are indistinguishable
-- in umbrella categories.
if not self._data then
self._data = labels[self._info.orig_label]
if self._data then
self._info.label = self._info.orig_label
end
end
end
-- Then check lang-independent labels.
if not self._data then
-- lang_independent_data.LABELS should always exist.
fetch_label_data(lang_independent_data.LABELS)
if not self._data and not self._lang then
-- Check family-specific labels for umbrella label.
local families_with_modules = require(family_specific_module)
for famcode, _ in pairs(families_with_modules) do
local module = family_specific_module_prefix .. famcode
local labels_and_handlers = require(module)
if labels_and_handlers.LABELS then
fetch_label_data(labels_and_handlers.LABELS)
if self._data then
self._data.module = self._data.module or module
break
end
end
end
end
end
-- Then check lang-independent handlers.
if not self._data then
local data = {
label = self._info.label,
lang = self._lang,
sc = self._sc,
args = patch_args(self._info.args) or {},
called_from_inside = self._info.called_from_inside,
}
for _, handler in ipairs(lang_independent_data["HANDLERS"]) do
self._data, args_handled = handler.handler(data)
if self._data then
self._data.module = self._data.module or handler.module
break
end
end
if not self._data and not self._lang then
-- Check family-specific labels for umbrella handler.
local families_with_modules = require(family_specific_module)
for famcode, _ in pairs(families_with_modules) do
local module = family_specific_module_prefix .. famcode
local labels_and_handlers = require(module)
if labels_and_handlers.HANDLERS then
for _, handler in ipairs(labels_and_handlers.HANDLERS) do
local data = {
label = self._info.label,
sc = self._sc,
args = patch_args(self._info.args) or {},
called_from_inside = self._info.called_from_inside,
}
self._data, args_handled = handler(data)
if self._data then
self._data.module = self._data.module or module
break
end
end
end
if self._data then
break
end
end
end
end
end
if not args_handled and self._data and self._info.args and next(self._info.args) then
local module_text = " (handled in [[" .. (self._data.module or "UNKNOWN").. "]])"
local args_text = {}
for k, v in pairs(self._info.args) do
insert(args_text, k .. "=" .. ((type(v) == "string" or type(v) == "number") and v or dump(v)))
end
error("poscatboiler label '" .. self._info.label .. "' " .. module_text .. " doesn't accept extra args " ..
concat(args_text, ", "))
end
if self._sc and not self._lang then
error("Umbrella categories cannot have a script specified.")
end
end
function Category:convert_spec_to_string(desc)
if not desc then
return desc
end
local desc_type = type(desc)
if desc_type == "string" then
return desc
elseif desc_type == "number" then
return tostring(desc)
elseif not is_callable(desc) then
error("Internal error: `desc` must be a string, number, function, callable table or nil; received a " .. desc_type)
end
desc = desc{
lang = self._lang,
sc = self._sc,
label = self._info.label,
raw = self._info.raw,
}
if not desc then
return desc
end
desc_type = type(desc)
if desc_type == "string" then
return desc
end
error("Internal error: the value returned by `desc` must be a string or nil; received a " .. desc_type)
end
local function add_obj_args(args, obj, obj_type)
if obj then
args[obj_type .. "code"] = obj:getCode()
args[obj_type .. "name"] = obj:getCanonicalName()
args[obj_type .. "disp"] = obj:getDisplayForm()
args[obj_type .. "cat"] = obj:getCategoryName()
args[obj_type .. "link"] = obj:makeCategoryLink()
end
end
-- Expands `desc` like a template, passing values for specs like {{{langname}}}.
function Category:substitute_template_specs(desc)
-- This may end up happening twice but that's OK as the function is (usually) idempotent.
-- FIXME: Not idempotent if a preprocessed template returns wikicode.
desc = self:convert_spec_to_string(desc)
if not desc then
return nil
end
-- Populate the substitution arguments.
local args = {}
args.umbrella_msg = "Đây là một thể loại chung. Nó không trực tiếp chứa bất kỳ mục từ từ điển nào, mà chỉ bao gồm các thể loại con sắp xếp theo ngôn ngữ cụ thể, và trong các thể loại này sẽ chứa mục từ thích hợp với ngôn ngữ tương ứng."
args.umbrella_meta_msg = "Đây là một siêu thể loại chung, bao gồm các mảng nội dung tổng quát như \"mục từ\", \"tên\" hoặc \"từ nguyên\". Nó không trực tiếp chứa bất kỳ mục từ từ điển nào, mà đóng vai trò điều hướng đến các thể loại con (\"theo ngôn ngữ\") sắp xếp theo chủ đề phụ cụ thể. Và trong mỗi thể loại con của một thể loại ngôn ngữ bất kỳ, sẽ là nơi chứa mục từ thích hợp với chủ đề của ngôn ngữ đó."
add_obj_args(args, self._lang, "lang")
add_obj_args(args, self._sc, "sc")
return parse(desc, true):expand(args)
end
function Category:substitute_template_specs_in_args(args)
if not args then
return args
end
local pinfo = {}
for k, v in pairs(args) do
pinfo[self:substitute_template_specs(k)] = self:substitute_template_specs(v)
end
return pinfo
end
function Category:make_new(info)
info.originating_label = self._info.label
info.originating_module = self._data.module
info.called_from_inside = true
return Category.new(info)
end
function Category:getBreadcrumbName()
local ret
if self._lang or self._info.raw then
ret = self._data.breadcrumb
else
ret = self._data.umbrella and self._data.umbrella.breadcrumb
end
if not ret then
ret = self._info.label
end
if type(ret) ~= "table" then
ret = {name = ret}
end
local name = self:substitute_template_specs(ret.name)
local nocap = ret.nocap
if self._sc then
name = name .. " in " .. self._sc:getDisplayForm()
end
return name, nocap
end
local function expand_toc_template_if(template)
local template_obj = new_title(template, 10)
if template_obj.exists then
return expand_template{title = template_obj.text}
end
return nil
end
-- Return the textual expansion of the first existing template among the given templates, first performing
-- substitutions on the template name such as replacing {{{langcode}}} with the current language's code (if any).
-- If no templates exist after expansion, or if nil is passed in, return nil. If a single string is passed in,
-- treat it like a one-element list consisting of that string.
function Category:get_template_text(templates)
if templates == nil then
return nil
elseif type(templates) ~= "table" then
templates = {templates}
end
for _, template in ipairs(templates) do
if template == false then
return false
end
template = self:substitute_template_specs(template)
return expand_toc_template_if(template)
end
return nil
end
function Category:getTOC(toc_type)
-- Type "none" means everything fits on a single page; in that case, display nothing.
if toc_type == "none" then
return nil
end
local templates, fallback_templates
-- If TOC type is "full" (more than 2500 entries), do the following, in order:
-- 1. look up and expand the `toc_template_full` templates (normal or umbrella, depending on whether there is
-- a current language);
-- 2. look up and expand the `toc_template` templates (normal or umbrella, as above);
-- 3. do the default behavior, which is as follows:
-- 3a. look up a language-specific "full" template according to the current language (using English if there
-- is no current language);
-- 3b. look up a script-specific "full" template according to the first script of current language (using English
-- if there is no current language);
-- 3c. look up a language-specific "normal" template according to the current language (using English if there
-- is no current language);
-- 3d. look up a script-specific "normal" template according to the first script of the current language (using
-- English if there is no current language);
-- 3e. display nothing.
--
-- If TOC type is "normal" (between 200 and 2500 entries), do the following, in order:
-- 1. look up and expand the `toc_template` templates (normal or umbrella, depending on whether there is
-- a current language);
-- 2. do the default behavior, which is as follows:
-- 2a. look up a language-specific "normal" template according to the current language (using English if there
-- is no current language);
-- 2b. look up a script-specific "normal" template according to the first script of the current language (using
-- English if there is no current language);
-- 2c. display nothing.
local data_source
if self._lang or self._info.raw then
data_source = self._data
else
data_source = self._data.umbrella
end
if data_source then
if toc_type == "full" then
templates = data_source.toc_template_full
fallback_templates = data_source.toc_template
else
templates = data_source.toc_template
end
end
local text = self:get_template_text(templates)
if text then
return text
elseif text == false then
return nil
end
text = self:get_template_text(fallback_templates)
if text then
return text
elseif text == false then
return nil
end
local default_toc_templates_to_check = {}
local lang, sc = self:getCatfixInfo()
local langcode = lang and lang:getCode() or "en"
local sccode = sc and sc:getCode() or lang and lang:getScriptCodes()[1] or "Latn"
-- FIXME: What is toctemplateprefix used for?
local tocname = (self._data.toctemplateprefix or "") .. "categoryTOC"
if toc_type == "full" then
insert(default_toc_templates_to_check, ("%s-%s/full"):format(langcode, tocname))
insert(default_toc_templates_to_check, ("%s-%s/full"):format(sccode, tocname))
end
insert(default_toc_templates_to_check, ("%s-%s"):format(langcode, tocname))
insert(default_toc_templates_to_check, ("%s-%s"):format(sccode, tocname))
for _, toc_template in ipairs(default_toc_templates_to_check) do
local toc_template_text = expand_toc_template_if(toc_template)
if toc_template_text then
return toc_template_text
end
end
return nil
end
function Category:getInfo()
return self._info
end
function Category:getDataModule()
return self._data.module
end
function Category:canBeEmpty()
if self._lang or self._info.raw then
return self._data.can_be_empty
end
return self._data.umbrella and self._data.umbrella.can_be_empty
end
function Category:isHidden()
if self._lang or self._info.raw then
return self._data.hidden
end
return self._data.umbrella and self._data.umbrella.hidden
end
function Category:getCategoryName()
if self._info.raw then
return self._info.canonical_name or self._info.label
elseif self._lang then
local ret = self._info.label .. " " .. self._lang:getCanonicalName()
if self._sc then
ret = ret .. " in " .. self._sc:getDisplayForm()
end
return ucfirst(ret)
end
local ret = ucfirst(self._info.label)
if not (self._data.no_by_language or self._data.umbrella and self._data.umbrella.no_by_language) then
ret = ret .. " theo ngôn ngữ"
end
return ret
end
function Category:getTopright()
if self._lang or self._info.raw then
return self:substitute_template_specs(self._data.topright)
end
return self._data.umbrella and self:substitute_template_specs(self._data.umbrella.topright)
end
function Category:display_title(displaytitle, lang)
if type(displaytitle) == "string" then
displaytitle = self:substitute_template_specs(displaytitle)
else
displaytitle = displaytitle(self:getCategoryName(), lang)
end
mw.getCurrentFrame():callParserFunction("DISPLAYTITLE", "Category:" .. displaytitle)
end
function Category:get_labels_categorizing()
local m_labels_utilities = require(labels_utilities_module)
local pos_cat_labels, sense_cat_labels, use_tlb
pos_cat_labels = m_labels_utilities.find_labels_for_category(self._info.label, "pos", self._lang)
local sense_label = self._info.label:match("^(.*) terms$")
if sense_label then
use_tlb = true
else
sense_label = self._info.label:match("^terms with (.*) senses$")
end
if not sense_label then
return nil
end
sense_cat_labels = m_labels_utilities.find_labels_for_category(sense_label, "sense", self._lang)
if use_tlb then
return m_labels_utilities.format_labels_categorizing(pos_cat_labels, sense_cat_labels, self._lang)
end
local all_labels = pos_cat_labels
for k, v in pairs(sense_cat_labels) do
all_labels[k] = v
end
return m_labels_utilities.format_labels_categorizing(all_labels, nil, self._lang)
end
-- FIXME: this is clunky.
local function remove_lang_params(desc)
-- Simply remove a language name/code/category from the beginning of the string, but replace the language name
-- in the middle of the string with either "specific languages" or "specific-language" depending on whether the
-- language name appears to be an attributive qualifier of another noun or to stand by itself. This may be wrong,
-- in which case the category in question should supply its own umbrella description.
desc = desc:gsub("^{{{langname}}} ", "")
:gsub("{{{langname}}} %(", "ngôn ngữ cụ thể (")
:gsub("{{{langname}}}([.,])", "ngôn ngữ cụ thể%1")
:gsub("{{{langname}}} ", "ngôn ngữ cụ thể ")
:gsub("{{{langdisp}}}", "ngôn ngữ cụ thể")
:gsub("{{{langlink}}}", "ngôn ngữ cụ thể")
return desc
end
function Category:getDescription(isChild)
-- Allows different text in the list of a category's children
local isChild = isChild == "child"
if self._lang or self._info.raw then
if not isChild and self._data.displaytitle then
self:display_title(self._data.displaytitle, self._lang)
end
if self._sc then
return self:getCategoryName() .. "."
end
local desc = self:substitute_template_specs(self._data.description)
if not desc then
return nil
elseif isChild then
return desc
end
return sparse_concat({
self:substitute_template_specs(self._data.preceding),
desc,
self:substitute_template_specs(self._data.additional),
self:substitute_template_specs(self:get_labels_categorizing()),
}, "\n\n")
end
local umbrella = self._data.umbrella
if not isChild and umbrella and umbrella.displaytitle then
self:display_title(umbrella.displaytitle)
end
local desc = self:substitute_template_specs(umbrella and umbrella.description)
local has_umbrella_desc = not not desc
if not desc then
desc = self:convert_spec_to_string(self._data.description)
if desc then
desc = remove_lang_params(desc)
desc = lcfirst(desc)
desc = desc:gsub("%.$", "")
desc = "Thể loại này gồm " .. desc .. "."
else
desc = "Categories with " .. self._info.label .. " in various specific languages."
end
desc = self:substitute_template_specs(desc)
end
if isChild then
return desc
end
return sparse_concat({
self:substitute_template_specs(umbrella and umbrella.preceding or not has_umbrella_desc and self._data.preceding),
desc,
self:substitute_template_specs(umbrella and umbrella.additional or not has_umbrella_desc and self._data.additional),
self:substitute_template_specs("{{{umbrella_msg}}}"),
self:substitute_template_specs(self:get_labels_categorizing()),
}, "\n\n")
end
function Category:new_sortkey(sortkey)
local sortkey_type = type(sortkey)
if sortkey_type == "string" then
sortkey = uupper(sortkey)
elseif sortkey_type == "table" then
function sortkey:makeSortKey()
local sort_func = self.sort_func
if sort_func ~= nil then
return sort_func(self.sort_base)
end
local lang = self.lang
if lang == nil then
return self.sort_base
end
lang = get_lang(lang, nil, true)
if lang == nil then
return self.sort_base
end
local sc = self.sc
if sc ~= nil then
sc = get_script(sc)
end
return lang:makeSortKey(self.sort_base, sc)
end
end
return sortkey
end
function Category:inherit_spec(spec, parent_spec)
if spec == false then
return nil
end
return self:substitute_template_specs(spec or parent_spec)
end
function Category:canonicalize_parents_children(cats, is_children)
if not cats then
return nil
elseif type(cats) == "table" then
if cats.name or cats.module then
cats = {cats}
elseif #cats == 0 then
return nil
end
else
cats = {cats}
end
local ret = {}
for _, cat in ipairs(cats) do
if type(cat) ~= "table" or not cat.name and not cat.module then
cat = {name = cat}
end
insert(ret, cat)
end
local is_umbrella = not self._lang and not self._info.raw
local table_type = is_children and "extra_children" or "parents"
for i, cat in ipairs(ret) do
local raw
if self._info.raw or is_umbrella then
raw = not cat.is_label
else
raw = cat.raw
end
local lang = self:inherit_spec(cat.lang, not raw and self._info.code or nil)
local sc = self:inherit_spec(cat.sc, not raw and self._info.sc or nil)
-- Get the sortkey.
local sortkey = cat.sort
if type(sortkey) == "table" then
sortkey.sort_base = self:substitute_template_specs(sortkey.sort_base) or
error("Missing .sort_base in '" .. table_type .. "' .sort table for '" ..
self._info.label .. "' category entry in module '" .. (self._data.module or "unknown") .. "'")
if sortkey.sort_func then
-- Not allowed to give a lang and/or script if sort_func is given.
local bad_spec = sortkey.lang and "lang" or sortkey.sc and "sc" or nil
if bad_spec then
error("Cannot specify both ." .. bad_spec .. " and .sort_func in '" .. table_type ..
"' .sort table for '" .. self._info.label .. "' category entry in module '" ..
(self._data.module or "unknown") .. "'")
end
else
sortkey.lang = self:inherit_spec(sortkey.lang, lang)
sortkey.sc = self:inherit_spec(sortkey.sc, sc)
end
else
sortkey = self:substitute_template_specs(sortkey)
end
local name
if cat.module then
-- A reference to a category using another category tree module.
if not cat.args then
error("Missing .args in '" .. table_type .. "' table with module=\"" .. cat.module .. "\" for '" ..
self._info.label .. "' category entry in module '" .. (self._data.module or "unknown") .. "'")
end
name = require("Module:category tree/" .. cat.module).new(self:substitute_template_specs_in_args(cat.args))
else
name = cat.name
if not name then
error("Missing .name in " .. (is_umbrella and "umbrella " or "") .. "'" .. table_type .. "' table for '" ..
self._info.label .. "' category entry in module '" .. (self._data.module or "unknown") .. "'")
elseif type(name) == "string" then -- otherwise, assume it's a category object and use it directly
name = self:substitute_template_specs(name)
if name:find("^Thể loại:") then
-- It's a non-poscatboiler category name.
sortkey = sortkey or is_children and name:gsub("^Thể loại:", "") or self:getCategoryName()
else
-- It's a label.
sortkey = sortkey or is_children and name or self._info.label
name = self:make_new{
label = name, code = lang, sc = sc,
raw = raw, args = self:substitute_template_specs_in_args(cat.args)
}
end
end
end
sortkey = sortkey or is_children and " " or self._info.label
ret[i] = {
name = name,
description = is_children and self:substitute_template_specs(cat.description) or nil,
sort = self:new_sortkey(sortkey)
}
end
return ret
end
function Category:getParents()
local is_umbrella, ret = not self._lang and not self._info.raw
if self._sc then
local parent1 = self:make_new{code = self._info.code, label = "terms in " .. self._sc:getCanonicalName() .. " script"}
local parent2 = self:make_new{code = self._info.code, label = self._info.label, raw = self._info.raw, args = self._info.args}
ret = {
{name = parent1, sort = self._sc:getCanonicalName()},
{name = parent2, sort = self._sc:getCanonicalName()},
}
else
local parents
if is_umbrella then
parents = self._data.umbrella and self._data.umbrella.parents or self._data.umbrella_parents
else
parents = self._data.parents
end
ret = self:canonicalize_parents_children(parents)
if not ret then
return nil
end
end
local self_cat = self:getCategoryName()
for _, parent in ipairs(ret) do
local parent_cat = parent.name.getCategoryName and parent.name:getCategoryName()
if self_cat == parent_cat then
error(("Internal error: Infinite loop would occur, as parent category '%s' is the same as the child category"):format(self_cat))
end
end
return ret
end
function Category:getChildren()
local is_umbrella = not self._lang and not self._info.raw
local children = self._data.children
local ret = {}
if not is_umbrella and children then
for _, child in ipairs(children) do
child = mw.clone(child)
if type(child) ~= "table" then
child = {name = child}
end
if not child.sort then
child.sort = child.name
end
-- FIXME, is preserving the script correct?
child.name = self:make_new{code = self._info.code, label = child.name, raw = child.raw, sc = self._info.sc}
insert(ret, child)
end
end
local extra_children
if is_umbrella then
extra_children = self._data.umbrella and self._data.umbrella.extra_children
else
extra_children = self._data.extra_children
end
extra_children = self:canonicalize_parents_children(extra_children, "children")
if extra_children then
for _, child in ipairs(extra_children) do
insert(ret, child)
end
end
return #ret > 0 and ret or nil
end
function Category:getUmbrella()
local umbrella = self._data.umbrella
if umbrella == false or self._info.raw or not self._lang or self._sc then
return nil
end
-- If `umbrella` is a string, use that; otherwise, use the label.
return self:make_new({label = type(umbrella) == "string" and umbrella or self._info.label})
end
function Category:getAppendix()
-- FIXME, this should be customizable.
local lang, label = self._lang, self._info.label
if self._info.raw or not (lang and label) then
return nil
end
local appendix = make_title(100, lang:getCanonicalName() .. " " .. label)
return appendix.exists and appendix.fullText or nil
end
function Category:getCatfixInfo()
if self._lang or self._sc or self._info.raw then
local langcode, sccode, lang, sc = self._data.catfix, self._data.catfix_sc
if langcode then
langcode = self:substitute_template_specs(langcode)
lang = get_lang(langcode)
elseif langcode == nil then -- not false
lang = self._lang
end
if sccode then
sccode = self:substitute_template_specs(sccode)
sc = get_script(sccode)
elseif sccode == nil then -- not false
sc = self._sc
end
return lang, sc
elseif not self._data.umbrella then
return
end
-- umbrella
local langcode, sccode, lang, sc = self._data.umbrella.catfix, self._data.umbrella.catfix_sc
if langcode then
langcode = self:substitute_template_specs(langcode)
lang = get_lang(langcode)
end
if sccode then
sccode = self:substitute_template_specs(sccode)
sc = get_script(sccode)
end
return lang, sc
end
function Category:getTOCTemplateName()
-- This should only be invoked if getTOC() returns true, meaning to do the default algorithm, but getTOC()
-- implements its own default algorithm.
error("Internal error: This should never get called")
end
local export = {}
function export.main(info)
local self = setmetatable({_info = info}, Category)
self:initCommon()
return self._data and self or nil
end
export.new = Category.new
return export
c5t81tlfefafywm00uz6nfeah7yjov3
Mô đun:category tree/families
828
279377
2573977
2571723
2026-10-28T08:43:44Z
Hiyuune
50834
2573977
Scribunto
text/plain
local raw_categories = {}
local raw_handlers = {}
local concat = table.concat
local insert = table.insert
local require_when_needed = require("Module:utilities/require when needed")
local ucfirst = require_when_needed("Module:string utilities", "ucfirst")
-----------------------------------------------------------------------------
-- --
-- RAW CATEGORIES --
-- --
-----------------------------------------------------------------------------
raw_categories["All language families"] = {
topright = "{{commonscat|Languages by family}}\n{{wp|Language family,List of language families}}",
description = "This category lists all [[language family|language families]].",
parents = {"Fundamental"},
}
raw_categories["Languages by family"] = {
topright = "{{commonscat|Languages by family}}\n{{wp|Language family,List of language families}}",
description = "This category contains all languages categorized hierarchically according to the [[language family]] they belong to.",
additional = "Only top-level language families are shown here. For a full list of all language families, see [[:Category:All language families]] or [[Wiktionary:List of families]].",
parents = {
{name = "All languages", sort = " "},
{name = "All language families", sort = " "},
},
}
raw_categories["Unassigned languages"] = {
description = "Languages that have not yet been assigned to any family by Wiktionary editors, usually due to oversight.",
additional = [=[This should be distinguished from:
* [[:Category:Unclassifiable languages]] (languages that cannot be confidently assigned to any family, typically because the language is extinct or unresearched and has little available data on it);
* [[:Category:Language isolates]] (where there is general agreement that the language has no relatives); and
* [[:Category:Languages of disputed affiliation]] (languages where there is no consensus concerning which family, if any, they belong to).]=],
parents = {
{name = "Languages by family", sort = " Unassigned languages"},
"All language families",
},
}
-----------------------------------------------------------------------------
-- --
-- RAW HANDLERS --
-- --
-----------------------------------------------------------------------------
local function family_is_not_a_family(fam)
if not fam then
return false
elseif fam:getCode() == "qfa-not" then
return true
else
local family_parent = fam:getFamily()
local parent_code = family_parent and family_parent:getCode() or nil
-- Some families have no parent, some are in the pseudo-family sgn (sign languages), some are given as
-- qfa-dis (disputed) or potentially qfa-unc (unclassifiable), but are not themselves pseudo-families.
if not parent_code or parent_code == "sgn" or parent_code == "qfa-dis" or parent_code == "qfa-unc" then
return false
end
return family_is_not_a_family(family_parent)
end
end
local function infobox(fam)
local ret = {}
insert(ret, "<table class=\"wikitable\">\n")
insert(ret, "<tr>\n<th colspan=\"2\" class=\"plainlinks\">[//en.wiktionary.org/w/index.php?title=Module:families/data&action=edit Edit family data]</th>\n</tr>\n")
insert(ret, "<tr>\n<th>Canonical name</th><td>" .. fam:getCanonicalName() .. "</td>\n</tr>\n")
local otherNames = fam:getOtherNames()
if otherNames then
local names = {}
for _, name in ipairs(otherNames) do
insert(names, "<li>" .. name .. "</li>")
end
if #names > 0 then
insert(ret, "<tr>\n<th>Other names</th><td><ul>" .. concat(names, "\n") .. "</ul></td>\n</tr>\n")
end
end
local aliases = fam:getAliases()
if aliases then
local names = {}
for _, name in ipairs(aliases) do
insert(names, "<li>" .. name .. "</li>")
end
if #names > 0 then
insert(ret, "<tr>\n<th>Aliases</th><td><ul>" .. concat(names, "\n") .. "</ul></td>\n</tr>\n")
end
end
local varieties = fam:getVarieties()
if varieties then
local names = {}
for _, name in ipairs(varieties) do
if type(name) == "string" then
insert(names, "<li>" .. name .. "</li>")
else
assert(type(name) == "table")
local first_var
local subvars = {}
for i, var in ipairs(name) do
if i == 1 then
first_var = var
else
insert(subvars, "<li>" .. var .. "</li>")
end
end
if #subvars > 0 then
insert(names, "<li><dl><dt>" .. first_var .. "</dt>\n<dd><ul>" .. concat(subvars, "\n") .. "</ul></dd></dl></li>")
elseif first_var then
insert(names, "<li>" .. first_var .. "</li>")
end
end
end
if #names > 0 then
insert(ret, "<tr>\n<th>Varieties</th><td><ul>" .. concat(names, "\n") .. "</ul></td>\n</tr>\n")
end
end
insert(ret, "<tr>\n<th>[[Wiktionary:Families|Family code]]</th><td><code>" .. fam:getCode() .. "</code></td>\n</tr>\n")
insert(ret, "<tr>\n<th>[[w:Proto-language|Common ancestor]]</th><td>")
local protoLanguage = fam:getProtoLanguage()
if protoLanguage then
insert(ret, "[[:Category:" .. protoLanguage:getCategoryName() .. "|" .. protoLanguage:getCanonicalName() .. "]]")
else
insert(ret, "none")
end
insert(ret, "</td>\n")
insert(ret, "\n</tr>\n")
local parent = fam:getFamily()
if not parent then
insert(ret, "<tr>\n<th>[[Wiktionary:Families|Parent family]]</th>\n<td>")
insert(ret, "unassigned")
elseif parent:getCode() == "qfa-not" then
insert(ret, "<tr>\n<th>[[Wiktionary:Families|Parent family]]</th>\n<td>")
insert(ret, "not a family")
else
local chain = {}
while parent do
insert(chain, "[[:Category:" .. parent:getCategoryName() .. "|" .. parent:getCanonicalName() .. "]]")
parent = parent:getFamily()
if parent and parent:getCode() == "qfa-not" then
break
end
end
if #chain == 0 then
insert(ret, "<tr>\n<th>[[Wiktionary:Families|Parent family]]</th>\n<td>")
insert(ret, "no parents")
else
insert(ret, "<tr>\n<th>[[Wiktionary:Families|Parent famil"
.. (#chain == 1 and "y" or "ies") .. "]]</th>\n<td>")
for i = #chain, 1, -1 do
insert(ret, "<ul><li>" .. chain[i])
end
insert(ret, string.rep("</li></ul>", #chain))
end
end
insert(ret, "</td>\n</tr>\n")
if fam:getWikidataItem() and mw.wikibase then
local link = '[' .. mw.wikibase.getEntityUrl(fam:getWikidataItem()) .. ' ' .. fam:getWikidataItem() .. ']'
insert(ret, "<tr><th>Wikidata</th><td>" .. link .. "</td></tr>")
end
insert(ret, "</table>")
return concat(ret)
end
local function NavFrame_for_family_tree(content, title)
return '<div class="NavFrame"><div class="NavHead">'
.. (title or '{{{title}}}') .. '</div>'
.. '<div class="NavContent" style="text-align: left; font-size: calc(1em / 0.95); padding: 0.3em">'
.. content
.. '</div></div>'
end
local additional_information = {
["cai"] = "These are [[indigenous]] ([[pre-Columbian]]) languages spoken in the {{w|Mesoamerican languages|Mesoamerican}} language area, " ..
"which is considered to encompass central and southern [[Mexico]], [[Central America]] and the [[Caribbean]]. See also " ..
"{{catlink|North American Indigenous languages}} and {{catlink|South American Indigenous languages}}.",
["nai"] = "These are [[indigenous]] ([[pre-Columbian]]) languages spoken in the [[North America]]n language area, defined as [[Canada]], " ..
"the [[United States]] and northern [[Mexico]]. Languages of central and southern [[Mexico]], [[Central America]] and the [[Caribbean]] are " ..
"considered {{catlink|Mesoamerican languages}}.",
["paa"] = "These are languages of the western [[Pacific]] (mostly of [[New Guinea]]) which are neither [[Austronesian]] nor [[Australian]].",
["qfa-dis"] = "These are languages where there is no consensus concerning which family, if any, they belong to.",
["qfa-iso"] = "These are languages where there is general agreement that the language has no known relatives.",
["qfa-jew"] = "These are the various languages and dialects that developed in [[Jewish]] communities in the {{w|Jewish diaspora|diaspora}}.",
["qfa-mix"] = "A [[mixed language]] is a language which is composed of two different languages.",
["qfa-unc"] = "These are languages that cannot be confidently assigned to a family due to lack of sufficient linguistic data. " ..
"They are also commonly called {{w|unclassified language|unclassified languages}}, but this is ambiguous between " ..
"languages that cannot be classified (due to insufficient data) and those that merely have not been classified " ..
"(due to insufficient research).",
["sai"] = "These are [[indigenous]] ([[pre-Columbian]]) languages spoken in [[South America]], including immediately adjacent " ..
"[[Caribbean]] islands. The remainder of the [[Caribbean]] is considered part of the {{w|Mesoamerican languages|Mesoamerican}} " ..
"language area; see {{catlink|Mesoamerican languages}}.",
}
local preceding_information = {
["qfa-dis"] = "{{also|Category:Unclassifiable languages|Category:Unassigned languages|Category:Language isolates}}",
["qfa-iso"] = "{{also|Category:Languages of disputed affiliation|Category:Unclassifiable languages|Category:Unassigned languages}}",
["qfa-unc"] = "{{also|Category:Languages of disputed affiliation|Category:Unassigned languages|Category:Language isolates}}",
["qfa-mix"] = "{{also|Category:Creole or pidgin languages}}",
["crp"] = "{{also|Category:Mixed languages}}",
}
local specially_named_families = {
["Languages of disputed affiliation"] = "qfa-dis",
["Language isolates"] = "qfa-iso",
}
local specially_named_family_sort_keys = {
["Languages of disputed affiliation"] = "Disputed affiliation",
["Language isolates"] = "Isolate",
}
insert(raw_handlers, function(data)
local family = require("Module:families").getByCategoryName(data.category)
if not family then
local special_code = specially_named_families[data.category]
if special_code then
family = require("Module:families").getByCode(special_code)
if not family then
error(("Internal error: Family code '%s' is an invalid family code."):format(special_code))
end
end
end
if not family then
return nil
end
local parent_fam = family:getFamily()
local parent_sort_key = specially_named_family_sort_keys[data.category] or data.category
local initial_char = family_is_not_a_family(family) and " " or ""
local first_parent
if not parent_fam or parent_fam:getCode() == "qfa-not" then
first_parent = "Languages by family"
else
first_parent = parent_fam:getCategoryName()
-- Languages under 'Languages by family' should be sorted by letter; otherwise put subfamilies under '*'
-- (except for pseudo-families, which go under ' ' under 'Languages by family')
initial_char = initial_char == "" and "*" or initial_char
end
local first_parent_sort_key = initial_char .. parent_sort_key
local description, additional = "", ""
local topright
local preceding = preceding_information[family:getCode()]
local additional_preface = additional_information[family:getCode()]
if additional_preface then
additional_preface = additional_preface .. "\n\n"
else
additional_preface = ""
end
if family_is_not_a_family(family) then
additional_preface = additional_preface ..
"This is a pseudo-family, used for grouping purposes but not forming a linguistically valid [[clade]] " ..
"(i.e. a set of linguistically related languages descending from a common parent).\n\n" ..
"Information about this family:\n\n"
else
additional_preface = "Information about " .. family:getCanonicalName() .. ":\n\n"
end
if not data.called_from_inside then
topright = {}
local wikipedia_art = family:getWikipediaArticle("noCategoryFallback")
if wikipedia_art then
insert(topright, "{{wp|" .. wikipedia_art .. "}}")
end
local commons_cat = family:getCommonsCategory()
if commons_cat then
insert(topright, "{{commonscat|" .. commons_cat:gsub("^Category:", "") .. "}}")
end
topright = #topright > 0 and concat(topright, "\n") or nil
description = "This is the main category of the '''" .. family:getDisplayForm() .. "'''."
additional = additional_preface .. infobox(family)
end
local ok, tree_of_descendants = pcall(
require("Module:family tree").render,
family:getCode(), {
families = "show",
varieties = "show",
pair = "family-first",
must_have_descendants = true,
})
if ok then
if tree_of_descendants then
additional = additional .. NavFrame_for_family_tree(
tree_of_descendants,
"Family tree")
else
additional = additional .. "\n\n" .. ucfirst(family:getCanonicalName())
.. " has no descendants or varieties listed in Wiktionary's language data modules."
end
else
mw.log("error while generating tree: " .. tostring(tree_of_descendants))
end
local parents = {
{name = first_parent, sort = first_parent_sort_key},
{name = "All language families", sort = parent_sort_key},
}
for _, pseudofam in ipairs(family:getPseudoFamilies()) do
insert(parents, {name = pseudofam:getCategoryName(), sort = parent_sort_key})
end
return {
preceding = preceding,
topright = topright,
description = description,
additional = additional,
parents = parents,
breadcrumb = family:getCanonicalName(),
can_be_empty = true,
}
end)
return {RAW_CATEGORIES = raw_categories, RAW_HANDLERS = raw_handlers}
fsx49hp8aknnfi0xzrx81uv4lrpt0st
2573979
2573977
2026-10-28T08:48:07Z
Hiyuune
50834
Restored revision 2571723 by [[Special:Contributions/Hiyuune|Hiyuune]] ([[en:w:User:BrandonXLF/Restorer|Restorer]])
2573979
Scribunto
text/plain
local raw_categories = {}
local raw_handlers = {}
local concat = table.concat
local insert = table.insert
-----------------------------------------------------------------------------
-- --
-- RAW CATEGORIES --
-- --
-----------------------------------------------------------------------------
raw_categories["Tất cả ngữ hệ"] = {
topright = "{{commonscat|Languages by family}}\n{{wikipedia|Ngữ hệ}}",
description = "Thể loại này liệt kê tất cả các trang mục từ theo [[ngữ hệ]].",
parents = {"Tất cả"},
}
raw_categories["Ngôn ngữ theo ngữ hệ"] = {
topright = "{{commonscat|Languages by family}}\n{{wikipedia|Ngữ hệ}}",
description = "This category contains all languages categorized hierarchically according to the [[language family]] they belong to.",
additional = "Only top-level language families are shown here. For a full list of all language families, xem thêm [[:Thể loại:Tất cả ngữ hệ]] hoặc [[Wiktionary:Danh sách ngữ hệ]].",
parents = {
{name = "Tất cả ngôn ngữ", sort = " "},
{name = "Tất cả ngữ hệ", sort = " "},
},
}
-----------------------------------------------------------------------------
-- --
-- RAW HANDLERS --
-- --
-----------------------------------------------------------------------------
local function family_is_not_a_family(fam)
local famcode = fam:getCode()
if famcode == "paa" then
return false -- Papuan languages are not a family but have a category
elseif famcode == "qfa-iso" or famcode == "qfa-not" then
return true
else
local parfam = fam:getFamily()
if parfam and parfam:getCode() == "qfa-not" then
-- Constructed languages, sign languages, etc.; no category for them
return true
end
end
return false
end
local function infobox(fam)
local ret = {}
insert(ret, "<table class=\"wikitable\">\n")
insert(ret, "<tr>\n<th colspan=\"2\" class=\"plainlinks\">[//vi.wiktionary.org/w/index.php?title=Module:families/data&action=edit Chỉnh sửa dữ liệu ngữ hệ]</th>\n</tr>\n")
insert(ret, "<tr>\n<th>Tên chuẩn</th><td>" .. fam:getCanonicalName() .. "</td>\n</tr>\n")
local otherNames = fam:getOtherNames()
if otherNames then
local names = {}
for _, name in ipairs(otherNames) do
insert(names, "<li>" .. name .. "</li>")
end
if #names > 0 then
insert(ret, "<tr>\n<th>Tên khác</th><td><ul>" .. concat(names, "\n") .. "</ul></td>\n</tr>\n")
end
end
local aliases = fam:getAliases()
if aliases then
local names = {}
for _, name in ipairs(aliases) do
insert(names, "<li>" .. name .. "</li>")
end
if #names > 0 then
insert(ret, "<tr>\n<th>Tên hiệu</th><td><ul>" .. concat(names, "\n") .. "</ul></td>\n</tr>\n")
end
end
local varieties = fam:getVarieties()
if varieties then
local names = {}
for _, name in ipairs(varieties) do
if type(name) == "string" then
insert(names, "<li>" .. name .. "</li>")
else
assert(type(name) == "table")
local first_var
local subvars = {}
for i, var in ipairs(name) do
if i == 1 then
first_var = var
else
insert(subvars, "<li>" .. var .. "</li>")
end
end
if #subvars > 0 then
insert(names, "<li><dl><dt>" .. first_var .. "</dt>\n<dd><ul>" .. concat(subvars, "\n") .. "</ul></dd></dl></li>")
elseif first_var then
insert(names, "<li>" .. first_var .. "</li>")
end
end
end
if #names > 0 then
insert(ret, "<tr>\n<th>Biến thể</th><td><ul>" .. concat(names, "\n") .. "</ul></td>\n</tr>\n")
end
end
insert(ret, "<tr>\n<th>[[Wiktionary:Families|Mã ngữ hệ]]</th><td><code>" .. fam:getCode() .. "</code></td>\n</tr>\n")
insert(ret, "<tr>\n<th>[[w:en:Proto-language|Ngôn ngữ<br/>nguyên thủy]]</th><td>")
local protoLanguage = fam:getProtoLanguage()
if protoLanguage then
insert(ret, "[[:Thể loại:" .. protoLanguage:getCategoryName() .. "|" .. protoLanguage:getCanonicalName() .. "]]")
else
insert(ret, "không có")
end
insert(ret, "</td>\n")
insert(ret, "\n</tr>\n")
local parent = fam:getFamily()
if not parent then
insert(ret, "<tr>\n<th>[[Wiktionary:Ngữ hệ|Họ ngữ hệ]]</th>\n<td>")
insert(ret, "chưa được phân loại")
elseif parent:getCode() == "qfa-not" then
insert(ret, "<tr>\n<th>[[Wiktionary:Ngữ hệ|Họ ngữ hệ]]</th>\n<td>")
insert(ret, "không phải là ngữ hệ")
else
local chain = {}
while parent do
if family_is_not_a_family(parent) then
break
end
insert(chain, "[[:Thể loại:" .. parent:getCategoryName() .. "|" .. parent:getCanonicalName() .. "]]")
parent = parent:getFamily()
end
if #chain == 0 then
insert(ret, "<tr>\n<th>[[Wiktionary:Ngữ hệ|Họ ngữ hệ]]</th>\n<td>")
insert(ret, "no parents")
else
insert(ret, "<tr>\n<th>[[Wiktionary:Ngữ hệ|Họ ngữ hệ]]</th>\n<td>")
for i = #chain, 1, -1 do
insert(ret, "<ul><li>" .. chain[i])
end
insert(ret, string.rep("</li></ul>", #chain))
end
end
insert(ret, "</td>\n</tr>\n")
if fam:getWikidataItem() and mw.wikibase then
local link = '[' .. mw.wikibase.getEntityUrl(fam:getWikidataItem()) .. ' ' .. fam:getWikidataItem() .. ']'
insert(ret, "<tr><th>Wikidata</th><td>" .. link .. "</td></tr>")
end
insert(ret, "</table>")
return concat(ret)
end
insert(raw_handlers, function(data)
local family = require("Module:families").getByCanonicalName(data.category)
if not family then
return nil
end
local parent_fam = family:getFamily()
local first_parent
if not parent_fam or family_is_not_a_family(parent_fam) then
first_parent = "Ngôn ngữ theo ngữ hệ"
else
first_parent = parent_fam:getCategoryName()
end
local description, additional, topright = "", ""
if not data.called_from_inside then
topright = {}
local wikipedia_art = family:getWikipediaArticle()
if wikipedia_art then
insert(topright, "{{wikipedia|" .. wikipedia_art .. "}}")
end
local commons_cat = family:getCommonsCategory()
if commons_cat then
insert(topright, "{{commonscat|" .. commons_cat:gsub("^Category:", "") .. "}}")
end
topright = #topright > 0 and concat(topright, "\n") or nil
description = "Đây là thể loại chính của '''" .. family:getDisplayForm() .. "'''."
additional = "Thông tin về " .. family:getCanonicalName() .. ":\n\n" .. infobox(family)
end
local parents = {
first_parent,
"Tất cả ngữ hệ",
}
if parent_fam and parent_fam:getCode() == "sgn" then
insert(parents, "All sign languages")
end
return {
canonical_name = family:getCategoryName(),
topright = topright,
description = description,
additional = additional,
parents = parents,
breadcrumb = family:getCanonicalName(),
can_be_empty = true,
}
end)
return {RAW_CATEGORIES = raw_categories, RAW_HANDLERS = raw_handlers}
g4rzlrk3a8463vcxp2c1d93nhds2pgz
2573980
2573979
2026-10-28T08:54:27Z
Hiyuune
50834
2573980
Scribunto
text/plain
local raw_categories = {}
local raw_handlers = {}
local concat = table.concat
local insert = table.insert
local require_when_needed = require("Module:utilities/require when needed")
local ucfirst = require_when_needed("Module:string utilities", "ucfirst")
-----------------------------------------------------------------------------
-- --
-- RAW CATEGORIES --
-- --
-----------------------------------------------------------------------------
raw_categories["Tất cả ngữ hệ"] = {
topright = "{{commonscat|Languages by family}}\n{{wikipedia|Ngữ hệ}}",
description = "Thể loại này liệt kê tất cả các trang mục từ theo [[ngữ hệ]].",
parents = {"Tất cả"},
}
raw_categories["Ngôn ngữ theo ngữ hệ"] = {
topright = "{{commonscat|Languages by family}}\n{{wikipedia|Ngữ hệ}}",
description = "This category contains all languages categorized hierarchically according to the [[language family]] they belong to.",
additional = "Only top-level language families are shown here. For a full list of all language families, xem thêm [[:Thể loại:Tất cả ngữ hệ]] hoặc [[Wiktionary:Danh sách ngữ hệ]].",
parents = {
{name = "Tất cả ngôn ngữ", sort = " "},
{name = "Tất cả ngữ hệ", sort = " "},
},
}
raw_categories["Unassigned languages"] = {
description = "Languages that have not yet been assigned to any family by Wiktionary editors, usually due to oversight.",
additional = [=[This should be distinguished from:
* [[:Category:Unclassifiable languages]] (languages that cannot be confidently assigned to any family, typically because the language is extinct or unresearched and has little available data on it);
* [[:Category:Language isolates]] (where there is general agreement that the language has no relatives); and
* [[:Category:Languages of disputed affiliation]] (languages where there is no consensus concerning which family, if any, they belong to).]=],
parents = {
{name = "Languages by family", sort = " Unassigned languages"},
"All language families",
},
}
-----------------------------------------------------------------------------
-- --
-- RAW HANDLERS --
-- --
-----------------------------------------------------------------------------
local function family_is_not_a_family(fam)
if not fam then
return false
elseif fam:getCode() == "qfa-not" then
return true
else
local family_parent = fam:getFamily()
local parent_code = family_parent and family_parent:getCode() or nil
-- Some families have no parent, some are in the pseudo-family sgn (sign languages), some are given as
-- qfa-dis (disputed) or potentially qfa-unc (unclassifiable), but are not themselves pseudo-families.
if not parent_code or parent_code == "sgn" or parent_code == "qfa-dis" or parent_code == "qfa-unc" then
return false
end
return family_is_not_a_family(family_parent)
end
end
local function infobox(fam)
local ret = {}
insert(ret, "<table class=\"wikitable\">\n")
insert(ret, "<tr>\n<th colspan=\"2\" class=\"plainlinks\">[//vi.wiktionary.org/w/index.php?title=Module:families/data&action=edit Chỉnh sửa dữ liệu ngữ hệ]</th>\n</tr>\n")
insert(ret, "<tr>\n<th>Tên chuẩn</th><td>" .. fam:getCanonicalName() .. "</td>\n</tr>\n")
local otherNames = fam:getOtherNames()
if otherNames then
local names = {}
for _, name in ipairs(otherNames) do
insert(names, "<li>" .. name .. "</li>")
end
if #names > 0 then
insert(ret, "<tr>\n<th>Tên khác</th><td><ul>" .. concat(names, "\n") .. "</ul></td>\n</tr>\n")
end
end
local aliases = fam:getAliases()
if aliases then
local names = {}
for _, name in ipairs(aliases) do
insert(names, "<li>" .. name .. "</li>")
end
if #names > 0 then
insert(ret, "<tr>\n<th>Tên thay thế</th><td><ul>" .. concat(names, "\n") .. "</ul></td>\n</tr>\n")
end
end
local varieties = fam:getVarieties()
if varieties then
local names = {}
for _, name in ipairs(varieties) do
if type(name) == "string" then
insert(names, "<li>" .. name .. "</li>")
else
assert(type(name) == "table")
local first_var
local subvars = {}
for i, var in ipairs(name) do
if i == 1 then
first_var = var
else
insert(subvars, "<li>" .. var .. "</li>")
end
end
if #subvars > 0 then
insert(names, "<li><dl><dt>" .. first_var .. "</dt>\n<dd><ul>" .. concat(subvars, "\n") .. "</ul></dd></dl></li>")
elseif first_var then
insert(names, "<li>" .. first_var .. "</li>")
end
end
end
if #names > 0 then
insert(ret, "<tr>\n<th>Biến thể</th><td><ul>" .. concat(names, "\n") .. "</ul></td>\n</tr>\n")
end
end
insert(ret, "<tr>\n<th>[[Wiktionary:Ngữ hệ|Mã ngữ hệ]]</th><td><code>" .. fam:getCode() .. "</code></td>\n</tr>\n")
insert(ret, "<tr>\n<th>[[w:en:Proto-language|Ngôn ngữ<br/>nguyên thủy]]</th><td>")
local protoLanguage = fam:getProtoLanguage()
if protoLanguage then
insert(ret, "[[:Thể loại:" .. protoLanguage:getCategoryName() .. "|" .. protoLanguage:getCanonicalName() .. "]]")
else
insert(ret, "không có")
end
insert(ret, "</td>\n")
insert(ret, "\n</tr>\n")
local parent = fam:getFamily()
if not parent then
insert(ret, "<tr>\n<th>[[Wiktionary:Ngữ hệ|Họ ngữ hệ]]</th>\n<td>")
insert(ret, "chưa được phân loại")
elseif parent:getCode() == "qfa-not" then
insert(ret, "<tr>\n<th>[[Wiktionary:Ngữ hệ|Họ ngữ hệ]]</th>\n<td>")
insert(ret, "không phải là ngữ hệ")
else
local chain = {}
while parent do
insert(chain, "[[:Thể loại:" .. parent:getCategoryName() .. "|" .. parent:getCanonicalName() .. "]]")
parent = parent:getFamily()
if parent and parent:getCode() == "qfa-not" then
break
end
end
if #chain == 0 then
insert(ret, "<tr>\n<th>[[Wiktionary:Ngữ hệ|Họ ngữ hệ]]</th>\n<td>")
insert(ret, "no parents")
else
insert(ret, "<tr>\n<th>[[Wiktionary:Ngữ hệ|Họ ngữ hệ]]</th>\n<td>")
for i = #chain, 1, -1 do
insert(ret, "<ul><li>" .. chain[i])
end
insert(ret, string.rep("</li></ul>", #chain))
end
end
insert(ret, "</td>\n</tr>\n")
if fam:getWikidataItem() and mw.wikibase then
local link = '[' .. mw.wikibase.getEntityUrl(fam:getWikidataItem()) .. ' ' .. fam:getWikidataItem() .. ']'
insert(ret, "<tr><th>Khoản mục<br/>Wikidata</th><td>" .. link .. "</td></tr>")
end
insert(ret, "</table>")
return concat(ret)
end
local function NavFrame_for_family_tree(content, title)
return '<div class="NavFrame"><div class="NavHead">'
.. (title or '{{{title}}}') .. '</div>'
.. '<div class="NavContent" style="text-align: left; font-size: calc(1em / 0.95); padding: 0.3em">'
.. content
.. '</div></div>'
end
local additional_information = {
["cai"] = "These are [[indigenous]] ([[pre-Columbian]]) languages spoken in the {{w|Mesoamerican languages|Mesoamerican}} language area, " ..
"which is considered to encompass central and southern [[Mexico]], [[Central America]] and the [[Caribbean]]. See also " ..
"{{catlink|North American Indigenous languages}} and {{catlink|South American Indigenous languages}}.",
["nai"] = "These are [[indigenous]] ([[pre-Columbian]]) languages spoken in the [[North America]]n language area, defined as [[Canada]], " ..
"the [[United States]] and northern [[Mexico]]. Languages of central and southern [[Mexico]], [[Central America]] and the [[Caribbean]] are " ..
"considered {{catlink|Mesoamerican languages}}.",
["paa"] = "These are languages of the western [[Pacific]] (mostly of [[New Guinea]]) which are neither [[Austronesian]] nor [[Australian]].",
["qfa-dis"] = "These are languages where there is no consensus concerning which family, if any, they belong to.",
["qfa-iso"] = "These are languages where there is general agreement that the language has no known relatives.",
["qfa-jew"] = "These are the various languages and dialects that developed in [[Jewish]] communities in the {{w|Jewish diaspora|diaspora}}.",
["qfa-mix"] = "A [[mixed language]] is a language which is composed of two different languages.",
["qfa-unc"] = "These are languages that cannot be confidently assigned to a family due to lack of sufficient linguistic data. " ..
"They are also commonly called {{w|unclassified language|unclassified languages}}, but this is ambiguous between " ..
"languages that cannot be classified (due to insufficient data) and those that merely have not been classified " ..
"(due to insufficient research).",
["sai"] = "These are [[indigenous]] ([[pre-Columbian]]) languages spoken in [[South America]], including immediately adjacent " ..
"[[Caribbean]] islands. The remainder of the [[Caribbean]] is considered part of the {{w|Mesoamerican languages|Mesoamerican}} " ..
"language area; see {{catlink|Mesoamerican languages}}.",
}
local preceding_information = {
["qfa-dis"] = "{{also|Category:Unclassifiable languages|Category:Unassigned languages|Category:Language isolates}}",
["qfa-iso"] = "{{also|Category:Languages of disputed affiliation|Category:Unclassifiable languages|Category:Unassigned languages}}",
["qfa-unc"] = "{{also|Category:Languages of disputed affiliation|Category:Unassigned languages|Category:Language isolates}}",
["qfa-mix"] = "{{also|Category:Creole or pidgin languages}}",
["crp"] = "{{also|Category:Mixed languages}}",
}
local specially_named_families = {
["Languages of disputed affiliation"] = "qfa-dis",
["Nhóm ngôn ngữ biệt lập"] = "qfa-iso",
}
local specially_named_family_sort_keys = {
["Languages of disputed affiliation"] = "Disputed affiliation",
["Nhóm ngôn ngữ biệt lập"] = "Nhóm ngôn ngữ biệt lập",
}
insert(raw_handlers, function(data)
local family = require("Module:families").getByCategoryName(data.category)
if not family then
local special_code = specially_named_families[data.category]
if special_code then
family = require("Module:families").getByCode(special_code)
if not family then
error(("Internal error: Family code '%s' is an invalid family code."):format(special_code))
end
end
end
if not family then
return nil
end
local parent_fam = family:getFamily()
local parent_sort_key = specially_named_family_sort_keys[data.category] or data.category
local initial_char = family_is_not_a_family(family) and " " or ""
local first_parent
if not parent_fam or parent_fam:getCode() == "qfa-not" then
first_parent = "Ngôn ngữ theo ngữ hệ"
else
first_parent = parent_fam:getCategoryName()
-- Languages under 'Languages by family' should be sorted by letter; otherwise put subfamilies under '*'
-- (except for pseudo-families, which go under ' ' under 'Languages by family')
initial_char = initial_char == "" and "*" or initial_char
end
local first_parent_sort_key = initial_char .. parent_sort_key
local description, additional = "", ""
local topright
local preceding = preceding_information[family:getCode()]
local additional_preface = additional_information[family:getCode()]
if additional_preface then
additional_preface = additional_preface .. "\n\n"
else
additional_preface = ""
end
if family_is_not_a_family(family) then
additional_preface = additional_preface ..
"This is a pseudo-family, used for grouping purposes but not forming a linguistically valid [[clade]] " ..
"(i.e. a set of linguistically related languages descending from a common parent).\n\n" ..
"Information about this family:\n\n"
else
additional_preface = "Thông tin về " .. family:getCanonicalName() .. ":\n\n"
end
if not data.called_from_inside then
topright = {}
local wikipedia_art = family:getWikipediaArticle("noCategoryFallback")
if wikipedia_art then
insert(topright, "{{wp|" .. wikipedia_art .. "}}")
end
local commons_cat = family:getCommonsCategory()
if commons_cat then
insert(topright, "{{commonscat|" .. commons_cat:gsub("^Category:", "") .. "}}")
end
topright = #topright > 0 and concat(topright, "\n") or nil
description = "Đây là thể loại chính của '''" .. family:getDisplayForm() .. "'''."
additional = additional_preface .. infobox(family)
end
local ok, tree_of_descendants = pcall(
require("Module:family tree").render,
family:getCode(), {
families = "show",
varieties = "show",
pair = "family-first",
must_have_descendants = true,
})
if ok then
if tree_of_descendants then
additional = additional .. NavFrame_for_family_tree(
tree_of_descendants,
"Family tree")
else
additional = additional .. "\n\n" .. ucfirst(family:getCanonicalName())
.. " has no descendants or varieties listed in Wiktionary's language data modules."
end
else
mw.log("error while generating tree: " .. tostring(tree_of_descendants))
end
local parents = {
{name = first_parent, sort = first_parent_sort_key},
{name = "Tất cả ngữ hệ", sort = parent_sort_key},
}
for _, pseudofam in ipairs(family:getPseudoFamilies()) do
insert(parents, {name = pseudofam:getCategoryName(), sort = parent_sort_key})
end
return {
preceding = preceding,
topright = topright,
description = description,
additional = additional,
parents = parents,
breadcrumb = family:getCanonicalName(),
can_be_empty = true,
}
end)
return {RAW_CATEGORIES = raw_categories, RAW_HANDLERS = raw_handlers}
6aaiu3tqqcuccsc76b623m4th2v7dxa
Mô đun:category tree/languages
828
279380
2573971
2571064
2026-10-28T08:15:50Z
Hiyuune
50834
2573971
Scribunto
text/plain
local new_title = mw.title.new
local ucfirst = require("Module:string utilities").ucfirst
local split = require("Module:string utilities").split
local raw_categories = {}
local raw_handlers = {}
local m_languages = require("Module:languages")
local m_sc_getByCode = require("Module:scripts").getByCode
local m_table = require("Module:table")
local parse_utilities_module = "Module:parse utilities"
local location_translation = require("Module:category tree/location translation")
local concat = table.concat
local insert = table.insert
local reverse_ipairs = m_table.reverseIpairs
local serial_comma_join = m_table.serialCommaJoin
local size = m_table.size
local sorted_pairs = m_table.sortedPairs
local to_json = require("Module:JSON").toJSON
local Hang = m_sc_getByCode("Hang")
local Hani = m_sc_getByCode("Hani")
local Hira = m_sc_getByCode("Hira")
local Hrkt = m_sc_getByCode("Hrkt")
local Kana = m_sc_getByCode("Kana")
local function track(page)
-- [[Special:WhatLinksHere/Wiktionary:Tracking/category tree/languages/PAGE]]
return require("Module:debug/track")("category tree/languages/" .. page)
end
-- This handles language categories of the form e.g. [[:Category:French language]] and
-- [[:Category:British Sign Language]]; categories like [[:Category:Languages of Indonesia]]; categories like
-- [[:Category:English-based creole or pidgin languages]]; and categories like
-- [[:Category:English-based constructed languages]].
-----------------------------------------------------------------------------
-- --
-- RAW CATEGORIES --
-- --
-----------------------------------------------------------------------------
raw_categories["Tất cả ngôn ngữ"] = {
topright = "{{commonscat|Languages}}\n[[File:Languages world map-transparent background.svg|thumb|right|250px|Rough world map of language families]]",
description = "Categories for every language on Wiktionary.",
additional = "Not all languages that Wiktionary recognises may have a category here yet. There are many that have " ..
"not yet received any attention from editors, mainly because not all Wiktionary users know about every single " ..
"language. See [[Wiktionary:List of languages]] for a full list.",
parents = {
"Tất cả",
},
}
raw_categories["Đa ngữ"] = {
description = "Đây là thể loại chính của Đa ngữ.",
parents = {
"Tất cả ngôn ngữ",
},
}
raw_categories["Ngôn ngữ tuyệt chủng"] = {
description = "Categories for every [[extinct language]] on Wiktionary.",
additional = "Do not confuse this category with [[:Category:Extinct languages]], which is an umbrella category for the names of extinct languages in specific other languages (e.g. {{m+|de|Langobardisch}} for the ancient [[Lombardic]] language).",
parents = {
{name = "Tất cả ngôn ngữ", sort = " "},
},
}
raw_categories["Ngôn ngữ nhân tạo"] = {
description = "Categories for every [[Wiktionary:Reconstructed terms|reconstructed language]] on Wiktionary, and appendices for proposed proto-language reconstructions that are considered promising but have not yet gained general acceptance.",
parents = {
{name = "Ngôn ngữ tuyệt chủng", sort = " "},
{name = "Tất cả ngôn ngữ", sort = " "},
},
}
raw_categories["Mục từ chỉ có trong phụ lục"] = {
description = "Terms that do not pass the [[WT:CFI|criteria for inclusion]], but are noteworthy enough to be included in the Appendix.",
additional = "Examples are dictionary-only terms (terms found in dictionaries but not elsewhere, including but not limited to [[ghost word]]s) and certain noteworthy [[protologism]]s.",
parents = {"Phụ lục"},
}
raw_categories["Ngôn ngữ nhân tạo chỉ có trong phụ lục"] = {
description = "Theo [[Wiktionary:Quy định mục từ|quy định mục từ]], các ngôn ngữ nhân tạo không được áp dụng trong không gian chính và chỉ khả dụng trong không gian tên {{cd|Phụ lục:}}.",
additional = "Ví dụ, các mục từ tiếng Klingon chỉ có sẵn ở [[Phụ lục:Tiếng Klingon]].",
breadcrumb = "chỉ có trong phụ lục",
parents = {
{name = "Ngôn ngữ nhân tạo", sort = " "},
"Mục từ chỉ có trong phụ lục",
},
}
raw_categories["Unattested languages"] = {
description = "Categories for languages of which there are no written records and which have not been reconstructed based on their descendants.",
additional = "Most of these are [[w:Stratum (linguistics)#Unattested substrata|unattested substrate languages]] that have been proposed as sources of vocabulary in certain languages.",
parents = {
{name = "All extinct languages", sort = " "},
{name = "Tất cả ngôn ngữ", sort = " "},
},
}
raw_categories["Unwritten languages"] = {
description = "Categories for every [[unwritten]] [[language]] on Wiktionary.",
additional = "Do not confuse this category with [[:Category:Unspecified script languages]], which contains categories for languages that may be written but where the appropriate script has not yet been specified in the language data.",
parents = {
{name = "Tất cả ngôn ngữ", sort = " "},
},
}
raw_categories["Ngôn ngữ với chuyển tự tự động"] = {
preceding = "{{also|:Thể loại:Mục từ có chuyển tự thừa theo ngôn ngữ|:Thể loại:Mục từ có chuyển tự thủ công không thừa theo ngôn ngữ}}",
description = "Categories for languages with automatic [[transliteration]], i.e. which have the name of a transliteration module listed in their [[:Category:Language data modules|language data table]].",
additional = [=[For example, for {{lnl|hi}}, {{lua|1=translit_module = "hi-translit"}}, referring to [[Module:hi-translit]]). The [[:Category:Transliteration modules|transliteration module]] converts the language's native script into a Latin-script version (a transliteration), which is then displayed in templates like {{tl|l}}, {{tl|m}} and {{tl|head}}.]=],
parents = {
{name = "Tất cả ngôn ngữ", sort = " "},
{name = "Mô đun chuyển tự", sort = " "},
}
}
raw_categories["Ngôn ngữ theo quốc gia"] = {
topright = "{{commonscat|Languages by continent}}",
description = "Ngôn ngữ theo quốc gia.",
additional = "{{{umbrella_meta_msg}}}",
parents = {
{name = "Tất cả ngôn ngữ", sort = " "},
},
}
raw_categories["Ngôn ngữ không xếp với tên quốc gia"] = {
description = "Languages which do not specify (in their {{tl|auto cat}} call) the location(s) where they are spoken.",
additional = "This excludes constructed and reconstructed languages; as a result, all languages in this category explicitly specify their location as {{cd|UNKNOWN}}.",
parents = {
{name = "Yêu cầu"},
},
hidden = true,
}
-----------------------------------------------------------------------------
-- --
-- RAW HANDLERS --
-- --
-----------------------------------------------------------------------------
-- Given a category (without the "Category:" prefix), look up the page
-- defining the category, find the call to {{auto cat}} (if any),
-- and return a table of its arguments. If the category page doesn't exist
-- or doesn't have an {{auto cat}} invocation, return nil.
-- FIXME: Duplicated in [[Module:category tree/lects]].
local function scrape_category_for_auto_cat_args(cat)
local cat_page = mw.title.new("Thể loại:" .. cat)
if cat_page then
local contents = cat_page:getContent()
if contents then
for template in require("Module:template parser").find_templates(contents) do
-- The template parser automatically handles redirects and
-- canonicalizes them.
if template:get_name() == "auto cat" then
return template:get_arguments()
end
end
end
end
return nil
end
local function link_location(location)
local location_no_the = location:match("^(.*)$")
local bare_location = location_no_the or location
local bare_location_parts = split(bare_location, ", ")
for i, part in ipairs(bare_location_parts) do
bare_location_parts[i] = ("[[%s]]"):format(part)
end
local location_link = concat(bare_location_parts, ", ")
if location_no_the then location_link = location_link end
return location_link
end
local function linkbox(lang, setwiki, setwikt, setsister, entryname)
local wiktionarylinks = {}
local canonicalName = lang:getCanonicalName()
local wikimediaLanguages = lang:getWikimediaLanguages()
local wikipediaArticle = setwiki or lang:getWikipediaArticle(true)
setwiki = not wikipediaArticle and "-"
setsister = setsister and ucfirst(setsister) or nil
if setwikt then
track("setwikt")
if setwikt == "-" then track("setwikt/hyphen") end
end
if setwikt ~= "-" and wikimediaLanguages and wikimediaLanguages[1] then
for _, wikimedialang in ipairs(wikimediaLanguages) do
local check = new_title(wikimedialang:getCode() .. ":")
if check and check.isExternal then
insert(wiktionarylinks,
(
wikimedialang:getCanonicalName() ~= canonicalName
and "(''" .. wikimedialang:getCanonicalName() .. "'') "
or ""
) .. (
"'''[[:" .. wikimedialang:getCode()
.. ":|" .. wikimedialang:getCode()
.. ".wiktionary.org]]'''"
)
)
end
end
wiktionarylinks = concat(wiktionarylinks, "<br/>")
end
local wikt_plural = wikimediaLanguages[2] and "" or ""
if #wiktionarylinks == 0 then
wiktionarylinks = "''Không có.''"
end
-- Avoid showing Wiktionary links section for reconstructed languages,
-- as they are ineligible for Wiktionary editions
local wiktionarylinks_chunk = concat{
[=[|-
| style="vertical-align: top; height: 35px; width: 40px; border-bottom: 1px solid var(--wikt-palette-grey-4, lightgray);" | [[File:Wiktionary-logo-v2.svg|35px|none|link=|Wiktionary]]
|style="border-bottom: 1px solid var(--wikt-palette-grey-4, lightgray);" | '''Phiên bản Wiktionary''']=], wikt_plural, [=[ viết ở ]=], canonicalName, [=[:
<div style="padding: 5px 10px">]=], wiktionarylinks, [=[</div>
]=]}
if lang:hasType('reconstructed') then
wiktionarylinks_chunk = ''
end
if setsister then
track("setsister")
if setsister == "-" then
track("setsister/hyphen")
else
setsister = "Thể loại:" .. setsister
end
else
setsister = lang:getCommonsCategory() or "-"
end
return concat{ -- FIXME: Bare wikicode
[=[<div class="wikitable" style="float: right; clear: right; margin: 0 0 0.5em 1em; width: 300px; padding: 5px;">
<div style="text-align: center; margin-bottom: 10px; margin-top: 5px">'''Liên kết ]=], canonicalName, [=['''</div>
{| style="font-size: 90%"
|-
| style="vertical-align: top; height: 35px; border-bottom: 1px solid var(--wikt-palette-grey-4, lightgray);" | [[File:Wikipedia-logo.png|35px|none|link=|Wikipedia]]
| style="border-bottom: 1px solid var(--wikt-palette-grey-4, lightgray);" | '''Wikipedia tiếng Việt''' có bài viết về:
<div style="padding: 5px 10px">]=], (setwiki == "-" and "''Không có.''" or "'''[[w:" .. wikipediaArticle .. "|" .. wikipediaArticle .. "]]'''"), [=[</div>
|-
| style="vertical-align: top; height: 35px; border-bottom: 1px solid var(--wikt-palette-grey-4, lightgray);" | [[File:Commons-logo.svg|35px|none|link=|Wikimedia Commons]]
| style="border-bottom: 1px solid var(--wikt-palette-grey-4, lightgray);" | '''Wikimedia Commons''' có liên kết đến các nội dung liên quan đến ]=], canonicalName, [=[
<div style="padding: 5px 10px">]=], (setsister == "-" and "''Không có.''" or "'''[[commons:" .. setsister .. "|" .. setsister .. "]]'''"), [=[</div>
]=], wiktionarylinks_chunk, [=[
|-
| style="vertical-align: top; height: 35px; border-bottom: 1px solid var(--wikt-palette-grey-4, lightgray);" | [[File:Codex icon articles color-placeholder (v2.6).svg|35px|none|link=|Entry]]
| style="border-bottom: 1px solid var(--wikt-palette-grey-4, lightgray);" | '''Wiktionary tiếng Việt''' có mục từ liên quan đến tên ngôn ngữ:
<div style="padding: 5px 10px">''']=], require("Module:links").full_link({lang = m_languages.getByCode("vi"), term = entryname or canonicalName}), [=['''</div>
|-
| style="vertical-align: top; height: 35px;" | [[File:Codex icon book color-placeholder (v2.6).svg|35px|none|link=|Nguồn tham khảo]]
|| '''Tài nguyên Wiktionary''' dành cho biên tập viên đóng góp vào các mục từ ]=], canonicalName, [=[:
<div style="padding: 5px 0">
* '''[[Wiktionary:Quy định mục từ]=], canonicalName, [=[]]'''
* '''[[:Thể loại:Bản mẫu tham khảo ]=], canonicalName, [=[|Bản mẫu tham khảo]] ({{PAGESINCAT:Bản mẫu tham khảo ]=], canonicalName, [=[}})'''
* '''[[Phụ lục:Thư mục học ]=], canonicalName, [=[|Thư mục học]]'''
</div>
|}
</div>]=]
}
end
local function edit_link(title, text)
return '<span class="plainlinks">['
.. tostring(mw.uri.fullUrl(title, { action = "edit" }))
.. ' ' .. text .. ']</span>'
end
-- Should perhaps use wiki syntax.
local function infobox(lang)
local ret = {}
insert(ret, '<table class="wikitable language-category-info"')
local raw_data = lang:getData("extra")
if raw_data then
local replacements = {
[1] = "canonical-name",
[2] = "wikidata-item",
[3] = "family",
[4] = "scripts",
}
local function replacer(letter1, letter2)
return letter1:lower() .. "-" .. letter2:lower()
end
-- For each key in the language data modules, returns a descriptive
-- kebab-case version (containing ASCII lowercase words separated
-- by hyphens).
local function kebab_case(key)
key = replacements[key] or key
key = key:gsub("(%l)(%u)", replacer):gsub("(%l)_(%l)", replacer)
return key
end
local compress = {compress = true}
local function html_attribute_encode(str)
str = to_json(str, compress)
:gsub('"', """)
-- & in attributes is automatically escaped.
-- :gsub("&", "&")
:gsub("<", "<")
:gsub(">", ">")
return str
end
insert(ret, ' data-code="' .. lang:getCode() .. '"')
for k, v in sorted_pairs(raw_data) do
insert(ret, " data-" .. kebab_case(k)
.. '="'
.. html_attribute_encode(v)
.. '"')
end
end
insert(ret, '>\n')
insert(ret, '<tr class="language-category-data">\n<th colspan="2">'
.. edit_link(lang:getDataModuleName(), "Chỉnh sửa dữ liệu ngôn ngữ")
.. "</th>\n</tr>\n")
insert(ret, "<tr>\n<th>Tên chuẩn</th><td>" .. lang:getCanonicalName() .. "</td>\n</tr>\n")
local otherNames = lang:getOtherNames()
if otherNames then
local names = {}
for _, name in ipairs(otherNames) do
insert(names, "<li>" .. name .. "</li>")
end
if #names > 0 then
insert(ret, (
"<tr>\n<th>Tên khác</th><td><ul>"
.. concat(names, "\n")
.. "</ul></td>\n</tr>\n"
)
)
end
end
local aliases = lang:getAliases()
if aliases then
local names = {}
for _, name in ipairs(aliases) do
insert(names, "<li>" .. name .. "</li>")
end
if #names > 0 then
insert(ret, (
"<tr>\n<th>Tên thay thế</th><td><ul>"
.. concat(names, "\n")
.. "</ul></td>\n</tr>\n"
)
)
end
end
local varieties = lang:getVarieties()
if varieties then
local names = {}
for _, name in ipairs(varieties) do
if type(name) == "string" then
insert(names, "<li>" .. name .. "</li>")
else
assert(type(name) == "table")
local first_var
local subvars = {}
for i, var in ipairs(name) do
if i == 1 then
first_var = var
else
insert(subvars, "<li>" .. var .. "</li>")
end
end
if #subvars > 0 then
insert(names, (
"<li><dl><dt>"
.. first_var .. "</dt>\n<dd><ul>"
.. concat(subvars, "\n")
.. "</ul></dd></dl></li>"
)
)
elseif first_var then
insert(names, "<li>" .. first_var .. "</li>")
end
end
end
if #names > 0 then
insert(ret, (
"<tr>\n<th>Biến thể</th><td><ul>"
.. concat(names, "\n")
.. "</ul></td>\n</tr>\n"
)
)
end
end
insert(ret, (
"<tr>\n<th>[[Wiktionary:Ngôn ngữ|Mã ngôn ngữ]]</th><td><code>"
.. lang:getCode()
.. "</code></td>\n</tr>\n"
)
)
insert(ret, "<tr>\n<th>[[Wiktionary:Ngữ hệ|Ngữ hệ]]</th>\n")
local fam = lang:getFamily()
local famCode = fam and fam:getCode()
if not fam then
insert(ret, "<td>[[:Category:Unassigned languages|unassigned]]</td>")
elseif famCode == "qfa-dis" then
insert(ret, "<td>[[:Category:Languages of disputed affiliation|disputed affiliation]]</td>")
elseif famCode == "qfa-iso" then
insert(ret, "<td>[[:Thể loại:Nhóm ngôn ngữ biệt lập|Nhóm ngôn ngữ biệt lập]]</td>")
elseif famCode == "qfa-mix" then
insert(ret, "<td>[[:Category:Mixed languages|mixed language]]</td>")
elseif famCode == "qfa-unc" then
insert(ret, "<td>[[:Category:Unclassifiable languages|unclassifiable language]]</td>")
elseif famCode == "sgn" then
insert(ret, "<td>[[:Category:Sign languages|sign language]]</td>")
elseif famCode == "crp" then
insert(ret, "<td>[[:Category:Creole or pidgin languages|creole or pidgin]]</td>")
elseif famCode == "art" then
insert(ret, "<td>[[:Thể loại:Ngôn ngữ nhân tạo|ngôn ngữ nhân tạo]]</td>")
else
insert(ret, "<td>" .. fam:makeCategoryLink() .. "</td>")
end
insert(ret, "\n</tr>\n<tr>\n<th>Ngôn ngữ<br/>nguyên thủy</th>\n<td>")
local ancestors = lang:getAncestors()
if ancestors[2] then
local ancestorList = {}
for i, anc in ipairs(ancestors) do
ancestorList[i] = "<li>" .. anc:makeCategoryLink() .. "</li>"
end
insert(ret, "<ul>\n" .. concat(ancestorList, "\n") .. "</ul>")
else
local ancestorChain = lang:getAncestorChainOld()
if ancestorChain[1] then
local chain = {}
for _, anc in reverse_ipairs(ancestorChain) do
insert(chain, "<li>" .. anc:makeCategoryLink() .. "</li>")
end
insert(ret, "<ul>\n" .. concat(chain, "\n<ul>\n") .. ("</ul>"):rep(#chain))
else
insert(ret, "không rõ")
end
end
insert(ret, "</td>\n</tr>\n")
local scripts = lang:getScripts()
if scripts[1] then
local script_text = {}
local function makeScriptLine(sc)
local code = sc:getCode()
local url = tostring(mw.uri.fullUrl('Đặc biệt:Search', {
search = 'contentmodel:css insource:"' .. code
.. '" insource:/\\.' .. code .. '/',
ns8 = '1'
}))
local sc_catlink
if code == "Zxxx" then
sc_catlink = "[[:Category:Unwritten languages|unwritten]]"
else
sc_catlink = sc:makeCategoryLink(lang)
end
return sc_catlink
.. ' (<span class="plainlinks" title="Search for stylesheets referencing this script">[' .. url .. ' <code>' .. code .. '</code>]</span>)'
end
local function add_Hrkt(text)
insert(text, "<li>" .. makeScriptLine(Hrkt))
insert(text, "<ul>")
insert(text, "<li>" .. makeScriptLine(Hira) .. "</li>")
insert(text, "<li>" .. makeScriptLine(Kana) .. "</li>")
insert(text, "</ul>")
insert(text, "</li>")
end
for _, sc in ipairs(scripts) do
local text = {}
local code = sc:getCode()
if code == "Hrkt" then
add_Hrkt(text)
else
insert(text, "<li>" .. makeScriptLine(sc))
if code == "Jpan" then
insert(text, "<ul>")
insert(text, "<li>" .. makeScriptLine(Hani) .. "</li>")
add_Hrkt(text)
insert(text, "</ul>")
elseif code == "Kore" then
insert(text, "<ul>")
insert(text, "<li>" .. makeScriptLine(Hang) .. "</li>")
insert(text, "<li>" .. makeScriptLine(Hani) .. "</li>")
insert(text, "</ul>")
end
insert(text, "</li>")
end
insert(script_text, concat(text, "\n"))
end
insert(ret, "<tr>\n<th>[[Wiktionary:Hệ chữ viết|Hệ chữ viết]]</th>\n<td><ul>\n" .. concat(script_text, "\n") .. "</ul></td>\n</tr>\n")
else
insert(ret, "<tr>\n<th>[[Wiktionary:Hệ chữ viết|Hệ chữ viết]]</th>\n<td>chưa được phân loại</td>\n</tr>\n")
end
local function add_module_info(raw_data, heading)
if raw_data then
local scripts = lang:getScriptCodes()
local module_info, add = {}, false
if type(raw_data) == "string" then
insert(module_info,
("[[Mô đun:%s]]"):format(raw_data))
add = true
else
local raw_data_type = type(raw_data)
if raw_data_type == "table" and size(scripts) == 1 and type(raw_data[scripts[1]]) == "string" then
insert(module_info,
("[[Mô đun:%s]]"):format(raw_data[scripts[1]]))
add = true
elseif raw_data_type == "table" then
insert(module_info, "<ul>")
for script, data in sorted_pairs(raw_data) do
if type(data) == "string" and m_sc_getByCode(script) then
insert(module_info, ("<li><code>%s</code>: [[Mô đun:%s]]</li>"):format(script, data))
end
end
insert(module_info, "</ul>")
add = size(module_info) > 2
end
end
if add then
insert(ret, [=[
<tr>
<th>]=] .. heading .. [=[</th>
<td>]=] .. concat(module_info) .. [=[</td>
</tr>
]=])
end
end
end
add_module_info(raw_data.generate_alternants, "Alternant-generating<br>module")
add_module_info(raw_data.translit, "[[Wiktionary:Transliteration and romanization|Transliteration<br>module]]")
add_module_info(raw_data.display_text, "Display text<br>module")
add_module_info(raw_data.strip_diacritics, "Diacritic-stripping<br>module")
add_module_info(raw_data.sort_key, "[[sortkey|Sortkey]]<br>module")
local wikidataItem = lang:getWikidataItem()
if lang:getWikidataItem() and mw.wikibase then
local URL = mw.wikibase.getEntityUrl(wikidataItem)
local link
if URL then
link = '[[d:' .. wikidataItem .. '|' .. wikidataItem .. ']]'
else
link = '<span class="error">Khoản mục Wikidata không hợp lệ: <code>' .. wikidataItem .. '</code></span>'
end
insert(ret, "<tr><th>Wikidata</th><td>" .. link .. "</td></tr>")
end
insert(ret, "</table>")
return concat(ret)
end
local function NavFrame_for_family_tree(content, title)
return '<div class="NavFrame"><div class="NavHead">'
.. (title or '{{{title}}}') .. '</div>'
.. '<div class="NavContent" style="text-align: left; font-size: calc(1em / 0.95); padding: 0.3em">'
.. content
.. '</div></div>'
end
local function get_description_topright_additional(lang, locations, extinct, setwiki, setwikt, setsister, entryname)
local nameWithLanguage = lang:getCategoryName("nocap")
if lang:getCode() == "und" then
local description =
"Đây là thể loại chính của '''" .. nameWithLanguage .. "''', represented in Wiktionary by the [[Wiktionary:Languages|code]] '''" .. lang:getCode() .. "'''. " ..
"This language contains terms in historical writing, whose meaning has not yet been determined by scholars."
return description, nil, nil
end
local canonicalName = lang:getCanonicalName()
local topright = linkbox(lang, setwiki, setwikt, setsister, entryname)
local description = "Đây là thể loại chính của " .. "'''" .. nameWithLanguage .. "'''."
local location_links = {}
local prep
local saw_embedded_comma = false
for _, location in ipairs(locations) do
local this_prep
if location == "the world" then
this_prep = "across"
insert(location_links, location)
elseif location ~= "UNKNOWN" then
this_prep = "ở"
if location:find(",") then
saw_embedded_comma = true
end
insert(location_links, link_location(location))
end
if this_prep then
if prep and this_prep ~= prep then
error("Can't handle location 'the world' along with another location (clashing prepositions)")
end
prep = this_prep
end
end
local location_desc
if #location_links > 0 then
local location_link_text
if saw_embedded_comma and #location_links >= 3 then
location_link_text = mw.text.listToText(location_links, "; ", "; và ")
else
location_link_text = serial_comma_join(location_links)
end
location_desc = ("Ngôn ngữ này %s %s %s.\n\n"):format(
extinct and "là một ngôn ngữ đã tuyệt chủng trước đây được sử dụng" or "được sử dụng",
prep, location_link_text
)
elseif extinct then
location_desc = "Đây là một ngôn ngữ đã tuyệt chủng.\n\n"
else
location_desc = ""
end
local add = location_desc .. "Thông tin về " .. canonicalName .. ":\n\n" .. infobox(lang)
if lang:hasType("reconstructed") then
add = add .. "\n\n" ..
ucfirst(canonicalName) .. " là một ngôn ngữ tái tạo. Its words and roots are not directly attested in any written works, but have been reconstructed through the ''comparative method'', " ..
"which finds regular similarities between languages that cannot be explained by coincidence or word-borrowing, and extrapolates ancient forms from these similarities.\n\n" ..
"Theo [[Wiktionary:Quy định mục từ|quy định mục từ]], mục từ " .. canonicalName ..
" không nằm ở không gian chính, nhưng có thể áp dụng ở không gian tên '''Từ tái tạo'''."
elseif lang:hasType("appendix-constructed") then
add = add .. "\n\n" ..
ucfirst(canonicalName) .. " is a constructed language that is only in sporadic use. " ..
"According to our [[Wiktionary:Criteria for inclusion|criteria for inclusion]], terms in " .. canonicalName ..
" should '''not''' be present in entries in the main namespace, but may be added to the Appendix: namespace. " ..
"All terms in this language may be available at [[Appendix:" .. ucfirst(canonicalName) .. "]]."
end
local entry_guidelines_page = "Wiktionary:Quy định mục từ " .. canonicalName
local entry_guidelines = new_title(entry_guidelines_page)
if entry_guidelines.exists then
add = add .. "\n\n" ..
"Xin vui lòng xem '''[[" .. entry_guidelines_page .. "]]''' để biết thông tin và cân nhắc đặc biệt khi tạo mục từ " .. nameWithLanguage .. "."
end
local ok, tree_of_descendants = pcall(
require("Module:family tree").render,
lang:getCode(), {
families = "show",
varieties = "show",
pair = "family-first",
must_have_descendants = true,
})
if ok then
if tree_of_descendants then
add = add .. NavFrame_for_family_tree(
tree_of_descendants,
"Family tree")
else
add = add .. "\n\n" .. ucfirst(lang:getCanonicalName())
.. " has no descendants or varieties listed in Wiktionary's language data modules."
end
else
mw.log("error while generating tree: " .. tostring(tree_of_descendants))
end
return description, topright, add
end
local function get_parents(lang, locations, extinct)
local canonicalName = lang:getCanonicalName()
local sortkey = {sort_base = canonicalName, lang = "vi"}
local ret = {{name = "Tất cả ngôn ngữ", sort = sortkey}}
local fam = lang:getFamily()
local famCode = fam and fam:getCode()
-- FIXME: Some of the following categories should be added to this module.
if not fam then
insert(ret, {name = "Category:Unassigned languages", sort = sortkey})
elseif famCode == "qfa-dis" then
insert(ret, {name = "Category:Languages of disputed affiliation", sort = sortkey})
elseif famCode == "qfa-iso" then
insert(ret, {name = "Thể loại:Nhóm ngôn ngữ biệt lập", sort = sortkey})
elseif famCode == "qfa-mix" then
insert(ret, {name = "Category:Mixed languages", sort = sortkey})
elseif famCode == "qfa-unc" then
insert(ret, {name = "Category:Unclassifiable languages", sort = sortkey})
elseif famCode == "crp" then
insert(ret, {name = "Category:Creole or pidgin languages", sort = sortkey})
for _, anc in ipairs(lang:getAncestors()) do
-- Avoid Haitian Creole being categorised in [[:Category:Haitian Creole-based creole or pidgin languages]], as one of its ancestors is an etymology-only variety of it.
-- Use that ancestor's ancestors instead.
if anc:getFullCode() == lang:getCode() then
for _, anc_extra in ipairs(anc:getAncestors()) do
insert(ret, {name = "Thể loại:" .. ucfirst(anc_extra:getFullName()) .. "-based creole or pidgin languages", sort = sortkey})
end
else
insert(ret, {name = "Thể loại:" .. ucfirst(anc:getFullName()) .. "-based creole or pidgin languages", sort = sortkey})
end
end
elseif famCode == "art" then
if lang:hasType("appendix-constructed") then
insert(ret, {name = "Thể loại:Ngôn ngữ nhân tạo chỉ có trong phụ lục", sort = sortkey})
else
insert(ret, {name = "Thể loại:Ngôn ngữ nhân tạo", sort = sortkey})
end
for _, anc in ipairs(lang:getAncestors()) do
if anc:getFullCode() == lang:getCode() then
for _, anc_extra in ipairs(anc:getAncestors()) do
insert(ret, {name = "Thể loại:" .. ucfirst(anc_extra:getFullName()) .. "-based constructed languages", sort = sortkey})
end
else
insert(ret, {name = "Thể loại:" .. ucfirst(anc:getFullName()) .. "-based constructed languages", sort = sortkey})
end
end
else
insert(ret, {name = "Thể loại:" .. fam:getCategoryName(), sort = sortkey})
if lang:hasType("reconstructed") then
insert(ret, {
name = "Thể loại:Ngôn ngữ nhân tạo",
sort = {sort_base = canonicalName:gsub("^Proto%-", ""), lang = "en"}
})
end
end
for _, pseudofam in ipairs(lang:getPseudoFamilies()) do
insert(ret, {name = "Thể loại:" .. pseudofam:getCategoryName(), sort = sortkey})
end
local function add_sc_cat(sc)
local catname
if sc:getCode() == "Zxxx" then
catname = "Unwritten languages"
else
catname = sc:getCategoryName(false, lang)
end
insert(ret, {name = "Thể loại:" .. catname, sort = sortkey})
end
local function add_Hrkt()
add_sc_cat(Hrkt)
add_sc_cat(Hira)
add_sc_cat(Kana)
end
for _, sc in ipairs(lang:getScripts()) do
if sc:getCode() == "Hrkt" then
add_Hrkt()
else
add_sc_cat(sc)
if sc:getCode() == "Jpan" then
add_sc_cat(Hani)
add_Hrkt()
elseif sc:getCode() == "Kore" then
add_sc_cat(Hang)
add_sc_cat(Hani)
end
end
end
if lang:hasTranslit() then
insert(ret, {name = "Thể loại:Ngôn ngữ với chuyển tự tự động", sort = sortkey})
end
local function insert_location_language_cat(location)
local cat = "Ngôn ngữ tại " .. location
insert(ret, {name = "Thể loại:" .. cat, sort = sortkey})
local auto_cat_args = scrape_category_for_auto_cat_args(cat)
local location_parent = auto_cat_args and auto_cat_args.parent
if location_parent then
local split_parents = require(parse_utilities_module).split_on_comma(location_parent)
for _, parent in ipairs(split_parents) do
parent = parent:match("^(.-):.*$") or parent
insert_location_language_cat(parent)
end
end
end
local saw_location = false
for _, location in ipairs(locations) do
if location ~= "UNKNOWN" then
saw_location = true
insert_location_language_cat(location)
end
end
if extinct then
insert(ret, {name = "Thể loại:Ngôn ngữ tuyệt chủng", sort = sortkey})
end
if not saw_location and not (lang:hasType("reconstructed") or (fam and fam:getCode() == "art")) then
-- Constructed and reconstructed languages don't need a location specified and often won't have one,
-- so don't put them in this maintenance category.
insert(ret, {name = "Thể loại:Ngôn ngữ không xếp với tên quốc gia", sort = sortkey})
end
return ret
end
local function get_children()
local ret = {}
-- FIXME: We should work on the children mechanism so it isn't necessary to manually specify these.
for _, label in ipairs({"Phụ lục", "Bảo trì mục từ", "Mục từ", "Cụm từ", "Vần", "Ký hiệu", "Bản mẫu", "Từ nguyên", "Từ theo cách sử dụng"}) do
insert(ret, {name = label, is_label = true})
end
insert(ret, {name = "Từ dẫn xuất từ {{{langname}}}", is_label = true, lang = false})
insert(ret, {name = "{{{langcode}}}:Tất cả chủ đề", sort = "tất cả chủ đề"})
insert(ret, {name = "Biến thể {{{langname}}}"})
insert(ret, {name = "Yêu cầu liên quan đến {{{langname}}}"})
insert(ret, {name = "Vần:{{{langname}}}", description = "Liệt kê các mục từ {{{langname}}} theo vần."})
insert(ret, {name = "Thành viên {{{langcode}}}", description = "Wiktionary users categorized by fluency levels in {{{langdisp}}}."})
return ret
end
-- Handle language categories of the form e.g. [[:Category:French language]] and
-- [[:Category:British Sign Language]].
insert(raw_handlers, function(data)
local category = data.category
if not (category:match("^[Tt]iếng") or category:match("[Ll]ect$")) then
return nil
end
local lang = m_languages.getByCanonicalName(category)
if not lang then
local langname = category
if langname then
lang = m_languages.getByCanonicalName(langname)
end
if not lang then
return nil
end
end
local args = require("Module:parameters").process(data.args, {
[1] = {list = true},
["setwiki"] = true,
["setwikt"] = true,
["setsister"] = true,
["entryname"] = true,
["extinct"] = {type = "boolean"},
})
-- If called from inside, don't require any arguments, as they can't be known
-- in general and aren't needed just to generate the first parent (used for
-- breadcrumbs).
if #args[1] == 0 and not data.called_from_inside then
-- At least one location must be specified unless the language is constructed (e.g. Esperanto) or reconstructed (e.g. Proto-Indo-European).
local fam = lang:getFamily()
if not (lang:hasType("reconstructed") or (fam and fam:getCode() == "art")) then
error("At least one location (param 1=) must be specified for language '" .. lang:getCanonicalName() .. "' (code '" .. lang:getCode() .. "'). " ..
"Use the value UNKNOWN if the language's location is truly unknown.")
end
end
local description, topright, additional = "", "", ""
-- If called from inside the category tree system, it's called when generating
-- parents or children, and we don't need to generate the description or additional
-- text (which is very expensive in terms of memory because it calls [[Module:family tree]],
-- which calls [[Module:languages/data/all]]).
if not data.called_from_inside then
description, topright, additional = get_description_topright_additional(
lang, args[1], args.extinct, args.setwiki, args.setwikt, args.setsister, args.entryname
)
end
return {
canonical_name = lang:getCategoryName(),
description = description,
lang = lang:getCode(),
topright = topright,
additional = additional,
breadcrumb = lang:getCanonicalName(),
parents = get_parents(lang, args[1], args.extinct),
extra_children = get_children(lang),
umbrella = false,
can_be_empty = true,
}, true
end)
-- Handle categories such as [[:Category:Languages of Indonesia]].
insert(raw_handlers, function(data)
local location = data.category:match("^Ngôn ngữ tại (.*)$")
if location then
local args = require("Module:parameters").process(data.args, {
["flagfile"] = true,
["commonscat"] = true,
["wp"] = true,
["basename"] = true,
["parent"] = true,
["locationcat"] = true,
["locationlink"] = true,
})
local topright
local basename = args.basename or location:gsub(", .*", "")
local translated_basename = location_translation[basename] or basename
if args.flagfile ~= "-" then
local flagfile_arg = args.flagfile or ("Flag of %s.svg"):format(translated_basename)
local files = require(parse_utilities_module).split_on_comma(flagfile_arg)
local topright_parts = {}
for _, file in ipairs(files) do
local flagfile = "File:" .. file
local flagfile_page = new_title(flagfile)
if flagfile_page and flagfile_page.file.exists then
insert(topright_parts, ("[[%s|right|100px|border]]"):format(flagfile))
elseif args.flagfile then
error(("Explicit flagfile '%s' doesn't exist"):format(flagfile))
end
end
topright = concat(topright_parts)
end
if args.wp then
local wp = require("Module:yesno")(args.wp, "+")
if wp == "+" or wp == true then
wp = data.category
end
if wp then
local wp_topright = ("{{wikipedia|%s}}"):format(wp)
if topright then
topright = topright .. wp_topright
else
topright = wp_topright
end
end
end
if args.commonscat then
local commonscat = require("Module:yesno")(args.commonscat, "+")
if commonscat == "+" or commonscat == true then
commonscat = data.category
end
if commonscat then
local commons_topright = ("{{commonscat|%s}}"):format(commonscat)
if topright then
topright = topright .. commons_topright
else
topright = commons_topright
end
end
end
local bare_location = location:match("^(.*)$") or location
local location_link = args.locationlink or link_location(location)
local bare_basename = basename:match("^(.*)$") or basename
local parents = {}
if args.parent then
local explicit_parents = require(parse_utilities_module).split_on_comma(args.parent)
for i, parent in ipairs(explicit_parents) do
local actual_parent, sort_key = parent:match("^(.-):(.*)$")
if actual_parent then
parent = actual_parent
sort_key = sort_key:gsub("%+", bare_location)
else
sort_key = " " .. bare_location
end
insert(parents, {name = "Ngôn ngữ tại " .. parent, sort = sort_key})
end
else
insert(parents, {name = "Ngôn ngữ theo quốc gia", sort = {sort_base = bare_location, lang = "vi"}})
end
if args.locationcat then
local explicit_location_cats = require(parse_utilities_module).split_on_comma(args.locationcat)
for i, locationcat in ipairs(explicit_location_cats) do
insert(parents, {name = "Thể loại:" .. locationcat, sort = " Languages"})
end
else
local location_cat = ("Thể loại:%s"):format(bare_location)
local location_page = new_title(location_cat)
if location_page and location_page.exists then
insert(parents, {name = location_cat, sort = "Languages"})
end
end
local description = ("Thể loại dành cho ngôn ngữ tại %s."):format(location_link)
return {
topright = topright,
description = description,
parents = parents,
breadcrumb = bare_basename,
additional = "{{{umbrella_msg}}}",
}, true
end
end)
-- Handle categories such as [[:Category:English-based creole or pidgin languages]].
insert(raw_handlers, function(data)
local langname = data.category:match("(.*)%-based creole or pidgin languages$")
if langname then
local lang = m_languages.getByCanonicalName(langname)
if lang then
return {
lang = lang:getCode(),
description = "Languages which developed as a [[creole]] or [[pidgin]] from " .. lang:makeCategoryLink() .. ".",
parents = {{name = "Creole or pidgin languages", sort = {sort_base = "*" .. langname, lang = "en"}}},
breadcrumb = lang:getCanonicalName() .. "-based",
}
end
end
end)
-- Handle categories such as [[:Category:English-based constructed languages]].
insert(raw_handlers, function(data)
local langname = data.category:match("(.*)%-based constructed languages$")
if langname then
local lang = m_languages.getByCanonicalName(langname)
if lang then
return {
lang = lang:getCode(),
description = "Constructed languages which are based on " .. lang:makeCategoryLink() .. ".",
parents = {{name = "Constructed languages", sort = {sort_base = "*" .. langname, lang = "en"}}},
breadcrumb = lang:getCanonicalName() .. "-based",
}
end
end
end)
return {
RAW_CATEGORIES = raw_categories,
RAW_HANDLERS = raw_handlers
}
jwcm3z9llzdcnj4s7g19bh9csyb7eru
2573981
2573971
2026-10-28T09:03:50Z
Hiyuune
50834
2573981
Scribunto
text/plain
local new_title = mw.title.new
local ucfirst = require("Module:string utilities").ucfirst
local split = require("Module:string utilities").split
local raw_categories = {}
local raw_handlers = {}
local m_languages = require("Module:languages")
local m_sc_getByCode = require("Module:scripts").getByCode
local m_table = require("Module:table")
local parse_utilities_module = "Module:parse utilities"
local location_translation = require("Module:category tree/location translation")
local concat = table.concat
local insert = table.insert
local reverse_ipairs = m_table.reverseIpairs
local serial_comma_join = m_table.serialCommaJoin
local size = m_table.size
local sorted_pairs = m_table.sortedPairs
local to_json = require("Module:JSON").toJSON
local Hang = m_sc_getByCode("Hang")
local Hani = m_sc_getByCode("Hani")
local Hira = m_sc_getByCode("Hira")
local Hrkt = m_sc_getByCode("Hrkt")
local Kana = m_sc_getByCode("Kana")
local function track(page)
-- [[Special:WhatLinksHere/Wiktionary:Tracking/category tree/languages/PAGE]]
return require("Module:debug/track")("category tree/languages/" .. page)
end
-- This handles language categories of the form e.g. [[:Category:French language]] and
-- [[:Category:British Sign Language]]; categories like [[:Category:Languages of Indonesia]]; categories like
-- [[:Category:English-based creole or pidgin languages]]; and categories like
-- [[:Category:English-based constructed languages]].
-----------------------------------------------------------------------------
-- --
-- RAW CATEGORIES --
-- --
-----------------------------------------------------------------------------
raw_categories["Tất cả ngôn ngữ"] = {
topright = "{{commonscat|Languages}}\n[[File:Languages world map-transparent background.svg|thumb|right|250px|Rough world map of language families]]",
description = "Categories for every language on Wiktionary.",
additional = "Not all languages that Wiktionary recognises may have a category here yet. There are many that have " ..
"not yet received any attention from editors, mainly because not all Wiktionary users know about every single " ..
"language. See [[Wiktionary:List of languages]] for a full list.",
parents = {
"Tất cả",
},
}
raw_categories["Đa ngữ"] = {
description = "Đây là thể loại chính của Đa ngữ.",
parents = {
"Tất cả ngôn ngữ",
},
}
raw_categories["Ngôn ngữ tuyệt chủng"] = {
description = "Categories for every [[extinct language]] on Wiktionary.",
additional = "Do not confuse this category with [[:Category:Extinct languages]], which is an umbrella category for the names of extinct languages in specific other languages (e.g. {{m+|de|Langobardisch}} for the ancient [[Lombardic]] language).",
parents = {
{name = "Tất cả ngôn ngữ", sort = " "},
},
}
raw_categories["Ngôn ngữ nhân tạo"] = {
description = "Categories for every [[Wiktionary:Reconstructed terms|reconstructed language]] on Wiktionary, and appendices for proposed proto-language reconstructions that are considered promising but have not yet gained general acceptance.",
parents = {
{name = "Ngôn ngữ tuyệt chủng", sort = " "},
{name = "Tất cả ngôn ngữ", sort = " "},
},
}
raw_categories["Mục từ chỉ có trong phụ lục"] = {
description = "Terms that do not pass the [[WT:CFI|criteria for inclusion]], but are noteworthy enough to be included in the Appendix.",
additional = "Examples are dictionary-only terms (terms found in dictionaries but not elsewhere, including but not limited to [[ghost word]]s) and certain noteworthy [[protologism]]s.",
parents = {"Phụ lục"},
}
raw_categories["Ngôn ngữ nhân tạo chỉ có trong phụ lục"] = {
description = "Theo [[Wiktionary:Quy định mục từ|quy định mục từ]], các ngôn ngữ nhân tạo không được áp dụng trong không gian chính và chỉ khả dụng trong không gian tên {{cd|Phụ lục:}}.",
additional = "Ví dụ, các mục từ tiếng Klingon chỉ có sẵn ở [[Phụ lục:Tiếng Klingon]].",
breadcrumb = "chỉ có trong phụ lục",
parents = {
{name = "Ngôn ngữ nhân tạo", sort = " "},
"Mục từ chỉ có trong phụ lục",
},
}
raw_categories["Unattested languages"] = {
description = "Categories for languages of which there are no written records and which have not been reconstructed based on their descendants.",
additional = "Most of these are [[w:Stratum (linguistics)#Unattested substrata|unattested substrate languages]] that have been proposed as sources of vocabulary in certain languages.",
parents = {
{name = "All extinct languages", sort = " "},
{name = "Tất cả ngôn ngữ", sort = " "},
},
}
raw_categories["Unwritten languages"] = {
description = "Categories for every [[unwritten]] [[language]] on Wiktionary.",
additional = "Do not confuse this category with [[:Category:Unspecified script languages]], which contains categories for languages that may be written but where the appropriate script has not yet been specified in the language data.",
parents = {
{name = "Tất cả ngôn ngữ", sort = " "},
},
}
raw_categories["Ngôn ngữ với chuyển tự tự động"] = {
preceding = "{{also|:Thể loại:Mục từ có chuyển tự thừa theo ngôn ngữ|:Thể loại:Mục từ có chuyển tự thủ công không thừa theo ngôn ngữ}}",
description = "Categories for languages with automatic [[transliteration]], i.e. which have the name of a transliteration module listed in their [[:Category:Language data modules|language data table]].",
additional = [=[For example, for {{lnl|hi}}, {{lua|1=translit_module = "hi-translit"}}, referring to [[Module:hi-translit]]). The [[:Category:Transliteration modules|transliteration module]] converts the language's native script into a Latin-script version (a transliteration), which is then displayed in templates like {{tl|l}}, {{tl|m}} and {{tl|head}}.]=],
parents = {
{name = "Tất cả ngôn ngữ", sort = " "},
{name = "Mô đun chuyển tự", sort = " "},
}
}
raw_categories["Ngôn ngữ theo quốc gia"] = {
topright = "{{commonscat|Languages by continent}}",
description = "Ngôn ngữ theo quốc gia.",
additional = "{{{umbrella_meta_msg}}}",
parents = {
{name = "Tất cả ngôn ngữ", sort = " "},
},
}
raw_categories["Ngôn ngữ không xếp với tên quốc gia"] = {
description = "Languages which do not specify (in their {{tl|auto cat}} call) the location(s) where they are spoken.",
additional = "This excludes constructed and reconstructed languages; as a result, all languages in this category explicitly specify their location as {{cd|UNKNOWN}}.",
parents = {
{name = "Yêu cầu"},
},
hidden = true,
}
-----------------------------------------------------------------------------
-- --
-- RAW HANDLERS --
-- --
-----------------------------------------------------------------------------
-- Given a category (without the "Category:" prefix), look up the page
-- defining the category, find the call to {{auto cat}} (if any),
-- and return a table of its arguments. If the category page doesn't exist
-- or doesn't have an {{auto cat}} invocation, return nil.
-- FIXME: Duplicated in [[Module:category tree/lects]].
local function scrape_category_for_auto_cat_args(cat)
local cat_page = mw.title.new("Thể loại:" .. cat)
if cat_page then
local contents = cat_page:getContent()
if contents then
for template in require("Module:template parser").find_templates(contents) do
-- The template parser automatically handles redirects and
-- canonicalizes them.
if template:get_name() == "auto cat" then
return template:get_arguments()
end
end
end
end
return nil
end
local function link_location(location)
local location_no_the = location:match("^(.*)$")
local bare_location = location_no_the or location
local bare_location_parts = split(bare_location, ", ")
for i, part in ipairs(bare_location_parts) do
bare_location_parts[i] = ("[[%s]]"):format(part)
end
local location_link = concat(bare_location_parts, ", ")
if location_no_the then location_link = location_link end
return location_link
end
local function linkbox(lang, setwiki, setwikt, setsister, entryname)
local wiktionarylinks = {}
local canonicalName = lang:getCanonicalName()
local wikimediaLanguages = lang:getWikimediaLanguages()
local wikipediaArticle = setwiki or lang:getWikipediaArticle(true)
setwiki = not wikipediaArticle and "-"
setsister = setsister and ucfirst(setsister) or nil
if setwikt then
track("setwikt")
if setwikt == "-" then track("setwikt/hyphen") end
end
if setwikt ~= "-" and wikimediaLanguages and wikimediaLanguages[1] then
for _, wikimedialang in ipairs(wikimediaLanguages) do
local check = new_title(wikimedialang:getCode() .. ":")
if check and check.isExternal then
insert(wiktionarylinks,
(
wikimedialang:getCanonicalName() ~= canonicalName
and "(''" .. wikimedialang:getCanonicalName() .. "'') "
or ""
) .. (
"'''[[:" .. wikimedialang:getCode()
.. ":|" .. wikimedialang:getCode()
.. ".wiktionary.org]]'''"
)
)
end
end
wiktionarylinks = concat(wiktionarylinks, "<br/>")
end
local wikt_plural = wikimediaLanguages[2] and "" or ""
if #wiktionarylinks == 0 then
wiktionarylinks = "''Không có.''"
end
-- Avoid showing Wiktionary links section for reconstructed languages,
-- as they are ineligible for Wiktionary editions
local wiktionarylinks_chunk = concat{
[=[|-
| style="vertical-align: top; height: 35px; width: 40px; border-bottom: 1px solid var(--wikt-palette-grey-4, lightgray);" | [[File:Wiktionary-logo-v2.svg|35px|none|link=|Wiktionary]]
|style="border-bottom: 1px solid var(--wikt-palette-grey-4, lightgray);" | '''Phiên bản Wiktionary''']=], wikt_plural, [=[ viết ở ]=], canonicalName, [=[:
<div style="padding: 5px 10px">]=], wiktionarylinks, [=[</div>
]=]}
if lang:hasType('reconstructed') then
wiktionarylinks_chunk = ''
end
if setsister then
track("setsister")
if setsister == "-" then
track("setsister/hyphen")
else
setsister = "Thể loại:" .. setsister
end
else
setsister = lang:getCommonsCategory() or "-"
end
return concat{ -- FIXME: Bare wikicode
[=[<div class="wikitable" style="float: right; clear: right; margin: 0 0 0.5em 1em; width: 300px; padding: 5px;">
<div style="text-align: center; margin-bottom: 10px; margin-top: 5px">'''Liên kết ]=], canonicalName, [=['''</div>
{| style="font-size: 90%"
|-
| style="vertical-align: top; height: 35px; border-bottom: 1px solid var(--wikt-palette-grey-4, lightgray);" | [[File:Wikipedia-logo.png|35px|none|link=|Wikipedia]]
| style="border-bottom: 1px solid var(--wikt-palette-grey-4, lightgray);" | '''Wikipedia tiếng Việt''' có bài viết về:
<div style="padding: 5px 10px">]=], (setwiki == "-" and "''Không có.''" or "'''[[w:" .. wikipediaArticle .. "|" .. wikipediaArticle .. "]]'''"), [=[</div>
|-
| style="vertical-align: top; height: 35px; border-bottom: 1px solid var(--wikt-palette-grey-4, lightgray);" | [[File:Commons-logo.svg|35px|none|link=|Wikimedia Commons]]
| style="border-bottom: 1px solid var(--wikt-palette-grey-4, lightgray);" | '''Wikimedia Commons''' có liên kết đến các nội dung liên quan đến ]=], canonicalName, [=[
<div style="padding: 5px 10px">]=], (setsister == "-" and "''Không có.''" or "'''[[commons:" .. setsister .. "|" .. setsister .. "]]'''"), [=[</div>
]=], wiktionarylinks_chunk, [=[
|-
| style="vertical-align: top; height: 35px; border-bottom: 1px solid var(--wikt-palette-grey-4, lightgray);" | [[File:Codex icon articles color-placeholder (v2.6).svg|35px|none|link=|Entry]]
| style="border-bottom: 1px solid var(--wikt-palette-grey-4, lightgray);" | '''Wiktionary tiếng Việt''' có mục từ liên quan đến tên ngôn ngữ:
<div style="padding: 5px 10px">''']=], require("Module:links").full_link({lang = m_languages.getByCode("vi"), term = entryname or canonicalName}), [=['''</div>
|-
| style="vertical-align: top; height: 35px;" | [[File:Codex icon book color-placeholder (v2.6).svg|35px|none|link=|Nguồn tham khảo]]
|| '''Tài nguyên Wiktionary''' dành cho biên tập viên đóng góp vào các mục từ ]=], canonicalName, [=[:
<div style="padding: 5px 0">
* '''[[Wiktionary:Quy định mục từ]=], canonicalName, [=[]]'''
* '''[[:Thể loại:Bản mẫu tham khảo ]=], canonicalName, [=[|Bản mẫu tham khảo]] ({{PAGESINCAT:Bản mẫu tham khảo ]=], canonicalName, [=[}})'''
* '''[[Phụ lục:Thư mục học ]=], canonicalName, [=[|Thư mục học]]'''
</div>
|}
</div>]=]
}
end
local function edit_link(title, text)
return '<span class="plainlinks">['
.. tostring(mw.uri.fullUrl(title, { action = "edit" }))
.. ' ' .. text .. ']</span>'
end
-- Should perhaps use wiki syntax.
local function infobox(lang)
local ret = {}
insert(ret, '<table class="wikitable language-category-info"')
local raw_data = lang:getData("extra")
if raw_data then
local replacements = {
[1] = "canonical-name",
[2] = "wikidata-item",
[3] = "family",
[4] = "scripts",
}
local function replacer(letter1, letter2)
return letter1:lower() .. "-" .. letter2:lower()
end
-- For each key in the language data modules, returns a descriptive
-- kebab-case version (containing ASCII lowercase words separated
-- by hyphens).
local function kebab_case(key)
key = tostring(key)
key = key:gsub("[_ ]+", "-")
return key
end
local compress = {compress = true}
local function html_attribute_encode(str)
str = to_json(str, compress)
:gsub('"', """)
-- & in attributes is automatically escaped.
-- :gsub("&", "&")
:gsub("<", "<")
:gsub(">", ">")
return str
end
insert(ret, ' data-code="' .. lang:getCode() .. '"')
for k, v in sorted_pairs(raw_data) do
insert(ret, " data-" .. kebab_case(k)
.. '="'
.. html_attribute_encode(v)
.. '"')
end
end
insert(ret, '>\n')
insert(ret, '<tr class="language-category-data">\n<th colspan="2">'
.. edit_link(lang:getDataModuleName(), "Chỉnh sửa dữ liệu ngôn ngữ")
.. "</th>\n</tr>\n")
insert(ret, "<tr>\n<th>Tên chuẩn</th><td>" .. lang:getCanonicalName() .. "</td>\n</tr>\n")
local otherNames = lang:getOtherNames()
if otherNames then
local names = {}
for _, name in ipairs(otherNames) do
insert(names, "<li>" .. name .. "</li>")
end
if #names > 0 then
insert(ret, (
"<tr>\n<th>Tên khác</th><td><ul>"
.. concat(names, "\n")
.. "</ul></td>\n</tr>\n"
)
)
end
end
local aliases = lang:getAliases()
if aliases then
local names = {}
for _, name in ipairs(aliases) do
insert(names, "<li>" .. name .. "</li>")
end
if #names > 0 then
insert(ret, (
"<tr>\n<th>Tên thay thế</th><td><ul>"
.. concat(names, "\n")
.. "</ul></td>\n</tr>\n"
)
)
end
end
local varieties = lang:getVarieties()
if varieties then
local names = {}
for _, name in ipairs(varieties) do
if type(name) == "string" then
insert(names, "<li>" .. name .. "</li>")
else
assert(type(name) == "table")
local first_var
local subvars = {}
for i, var in ipairs(name) do
if i == 1 then
first_var = var
else
insert(subvars, "<li>" .. var .. "</li>")
end
end
if #subvars > 0 then
insert(names, (
"<li><dl><dt>"
.. first_var .. "</dt>\n<dd><ul>"
.. concat(subvars, "\n")
.. "</ul></dd></dl></li>"
)
)
elseif first_var then
insert(names, "<li>" .. first_var .. "</li>")
end
end
end
if #names > 0 then
insert(ret, (
"<tr>\n<th>Biến thể</th><td><ul>"
.. concat(names, "\n")
.. "</ul></td>\n</tr>\n"
)
)
end
end
insert(ret, (
"<tr>\n<th>[[Wiktionary:Ngôn ngữ|Mã ngôn ngữ]]</th><td><code>"
.. lang:getCode()
.. "</code></td>\n</tr>\n"
)
)
insert(ret, "<tr>\n<th>[[Wiktionary:Ngữ hệ|Ngữ hệ]]</th>\n")
local fam = lang:getFamily()
local famCode = fam and fam:getCode()
if not fam then
insert(ret, "<td>[[:Category:Unassigned languages|unassigned]]</td>")
elseif famCode == "qfa-dis" then
insert(ret, "<td>[[:Category:Languages of disputed affiliation|disputed affiliation]]</td>")
elseif famCode == "qfa-iso" then
insert(ret, "<td>[[:Thể loại:Nhóm ngôn ngữ biệt lập|Nhóm ngôn ngữ biệt lập]]</td>")
elseif famCode == "qfa-mix" then
insert(ret, "<td>[[:Category:Mixed languages|mixed language]]</td>")
elseif famCode == "qfa-unc" then
insert(ret, "<td>[[:Category:Unclassifiable languages|unclassifiable language]]</td>")
elseif famCode == "sgn" then
insert(ret, "<td>[[:Category:Sign languages|sign language]]</td>")
elseif famCode == "crp" then
insert(ret, "<td>[[:Category:Creole or pidgin languages|creole or pidgin]]</td>")
elseif famCode == "art" then
insert(ret, "<td>[[:Thể loại:Ngôn ngữ nhân tạo|ngôn ngữ nhân tạo]]</td>")
else
insert(ret, "<td>" .. fam:makeCategoryLink() .. "</td>")
end
insert(ret, "\n</tr>\n<tr>\n<th>Ngôn ngữ<br/>nguyên thủy</th>\n<td>")
local ancestors = lang:getAncestors()
if ancestors[2] then
local ancestorList = {}
for i, anc in ipairs(ancestors) do
ancestorList[i] = "<li>" .. anc:makeCategoryLink() .. "</li>"
end
insert(ret, "<ul>\n" .. concat(ancestorList, "\n") .. "</ul>")
else
local ancestorChain = lang:getAncestorChainOld()
if ancestorChain[1] then
local chain = {}
for _, anc in reverse_ipairs(ancestorChain) do
insert(chain, "<li>" .. anc:makeCategoryLink() .. "</li>")
end
insert(ret, "<ul>\n" .. concat(chain, "\n<ul>\n") .. ("</ul>"):rep(#chain))
else
insert(ret, "không rõ")
end
end
insert(ret, "</td>\n</tr>\n")
local scripts = lang:getScripts()
if scripts[1] then
local script_text = {}
local function makeScriptLine(sc)
local code = sc:getCode()
local url = tostring(mw.uri.fullUrl('Đặc biệt:Search', {
search = 'contentmodel:css insource:"' .. code
.. '" insource:/\\.' .. code .. '/',
ns8 = '1'
}))
local sc_catlink
if code == "Zxxx" then
sc_catlink = "[[:Category:Unwritten languages|unwritten]]"
else
sc_catlink = sc:makeCategoryLink(lang)
end
return sc_catlink
.. ' (<span class="plainlinks" title="Search for stylesheets referencing this script">[' .. url .. ' <code>' .. code .. '</code>]</span>)'
end
local function add_Hrkt(text)
insert(text, "<li>" .. makeScriptLine(Hrkt))
insert(text, "<ul>")
insert(text, "<li>" .. makeScriptLine(Hira) .. "</li>")
insert(text, "<li>" .. makeScriptLine(Kana) .. "</li>")
insert(text, "</ul>")
insert(text, "</li>")
end
for _, sc in ipairs(scripts) do
local text = {}
local code = sc:getCode()
if code == "Hrkt" then
add_Hrkt(text)
else
insert(text, "<li>" .. makeScriptLine(sc))
if code == "Jpan" then
insert(text, "<ul>")
insert(text, "<li>" .. makeScriptLine(Hani) .. "</li>")
add_Hrkt(text)
insert(text, "</ul>")
elseif code == "Kore" then
insert(text, "<ul>")
insert(text, "<li>" .. makeScriptLine(Hang) .. "</li>")
insert(text, "<li>" .. makeScriptLine(Hani) .. "</li>")
insert(text, "</ul>")
end
insert(text, "</li>")
end
insert(script_text, concat(text, "\n"))
end
insert(ret, "<tr>\n<th>[[Wiktionary:Hệ chữ viết|Hệ chữ viết]]</th>\n<td><ul>\n" .. concat(script_text, "\n") .. "</ul></td>\n</tr>\n")
else
insert(ret, "<tr>\n<th>[[Wiktionary:Hệ chữ viết|Hệ chữ viết]]</th>\n<td>chưa được phân loại</td>\n</tr>\n")
end
local function add_module_info(raw_data, heading)
if raw_data then
local scripts = lang:getScriptCodes()
local module_info, add = {}, false
if type(raw_data) == "string" then
insert(module_info,
("[[Mô đun:%s]]"):format(raw_data))
add = true
else
local raw_data_type = type(raw_data)
if raw_data_type == "table" and size(scripts) == 1 and type(raw_data[scripts[1]]) == "string" then
insert(module_info,
("[[Mô đun:%s]]"):format(raw_data[scripts[1]]))
add = true
elseif raw_data_type == "table" then
insert(module_info, "<ul>")
for script, data in sorted_pairs(raw_data) do
if type(data) == "string" and m_sc_getByCode(script) then
insert(module_info, ("<li><code>%s</code>: [[Mô đun:%s]]</li>"):format(script, data))
end
end
insert(module_info, "</ul>")
add = size(module_info) > 2
end
end
if add then
insert(ret, [=[
<tr>
<th>]=] .. heading .. [=[</th>
<td>]=] .. concat(module_info) .. [=[</td>
</tr>
]=])
end
end
end
add_module_info(raw_data.generate_alternants, "Alternant-generating<br>module")
add_module_info(raw_data.translit, "Mô đun<br/>chuyển tự")
add_module_info(raw_data.display_text, "Display text<br>module")
add_module_info(raw_data.strip_diacritics, "Diacritic-stripping<br>module")
add_module_info(raw_data.sort_key, "[[sortkey|Sortkey]]<br>module")
local wikidataItem = lang:getWikidataItem()
if lang:getWikidataItem() and mw.wikibase then
local URL = mw.wikibase.getEntityUrl(wikidataItem)
local link
if URL then
link = '[[d:' .. wikidataItem .. '|' .. wikidataItem .. ']]'
else
link = '<span class="error">Khoản mục Wikidata không hợp lệ: <code>' .. wikidataItem .. '</code></span>'
end
insert(ret, "<tr><th>Khoản mục<br/>Wikidata</th><td>" .. link .. "</td></tr>")
end
insert(ret, "</table>")
return concat(ret)
end
local function NavFrame_for_family_tree(content, title)
return '<div class="NavFrame"><div class="NavHead">'
.. (title or '{{{title}}}') .. '</div>'
.. '<div class="NavContent" style="text-align: left; font-size: calc(1em / 0.95); padding: 0.3em">'
.. content
.. '</div></div>'
end
local function get_description_topright_additional(lang, locations, extinct, setwiki, setwikt, setsister, entryname)
local nameWithLanguage = lang:getCategoryName("nocap")
if lang:getCode() == "und" then
local description =
"Đây là thể loại chính của '''" .. nameWithLanguage .. "''', represented in Wiktionary by the [[Wiktionary:Languages|code]] '''" .. lang:getCode() .. "'''. " ..
"This language contains terms in historical writing, whose meaning has not yet been determined by scholars."
return description, nil, nil
end
local canonicalName = lang:getCanonicalName()
local topright = linkbox(lang, setwiki, setwikt, setsister, entryname)
local description = "Đây là thể loại chính của " .. "'''" .. nameWithLanguage .. "'''."
local location_links = {}
local prep
local saw_embedded_comma = false
for _, location in ipairs(locations) do
local this_prep
if location == "the world" then
this_prep = "across"
insert(location_links, location)
elseif location ~= "UNKNOWN" then
this_prep = "ở"
if location:find(",") then
saw_embedded_comma = true
end
insert(location_links, link_location(location))
end
if this_prep then
if prep and this_prep ~= prep then
error("Can't handle location 'the world' along with another location (clashing prepositions)")
end
prep = this_prep
end
end
local location_desc
if #location_links > 0 then
local location_link_text
if saw_embedded_comma and #location_links >= 3 then
location_link_text = mw.text.listToText(location_links, "; ", "; và ")
else
location_link_text = serial_comma_join(location_links)
end
location_desc = ("Ngôn ngữ này %s %s %s.\n\n"):format(
extinct and "là một ngôn ngữ đã tuyệt chủng trước đây được sử dụng" or "được sử dụng",
prep, location_link_text
)
elseif extinct then
location_desc = "Đây là một ngôn ngữ đã tuyệt chủng.\n\n"
else
location_desc = ""
end
local add = location_desc .. "Thông tin về " .. canonicalName .. ":\n\n" .. infobox(lang)
if lang:hasType("reconstructed") then
add = add .. "\n\n" ..
ucfirst(canonicalName) .. " là một ngôn ngữ tái tạo. Its words and roots are not directly attested in any written works, but have been reconstructed through the ''comparative method'', " ..
"which finds regular similarities between languages that cannot be explained by coincidence or word-borrowing, and extrapolates ancient forms from these similarities.\n\n" ..
"Theo [[Wiktionary:Quy định mục từ|quy định mục từ]], mục từ " .. canonicalName ..
" không nằm ở không gian chính, nhưng có thể áp dụng ở không gian tên '''Từ tái tạo'''."
elseif lang:hasType("appendix-constructed") then
add = add .. "\n\n" ..
ucfirst(canonicalName) .. " is a constructed language that is only in sporadic use. " ..
"According to our [[Wiktionary:Criteria for inclusion|criteria for inclusion]], terms in " .. canonicalName ..
" should '''not''' be present in entries in the main namespace, but may be added to the Appendix: namespace. " ..
"All terms in this language may be available at [[Appendix:" .. ucfirst(canonicalName) .. "]]."
end
local entry_guidelines_page = "Wiktionary:Quy định mục từ " .. canonicalName
local entry_guidelines = new_title(entry_guidelines_page)
if entry_guidelines.exists then
add = add .. "\n\n" ..
"Xin vui lòng xem '''[[" .. entry_guidelines_page .. "]]''' để biết thông tin và cân nhắc đặc biệt khi tạo mục từ " .. nameWithLanguage .. "."
end
local ok, tree_of_descendants = pcall(
require("Module:family tree").render,
lang:getCode(), {
families = "show",
varieties = "show",
pair = "family-first",
must_have_descendants = true,
})
if ok then
if tree_of_descendants then
add = add .. NavFrame_for_family_tree(
tree_of_descendants,
"Family tree")
else
add = add .. "\n\n" .. ucfirst(lang:getCanonicalName())
.. " has no descendants or varieties listed in Wiktionary's language data modules."
end
else
mw.log("error while generating tree: " .. tostring(tree_of_descendants))
end
return description, topright, add
end
local function get_parents(lang, locations, extinct)
local canonicalName = lang:getCanonicalName()
local sortkey = {sort_base = canonicalName, lang = "vi"}
local ret = {{name = "Tất cả ngôn ngữ", sort = sortkey}}
local fam = lang:getFamily()
local famCode = fam and fam:getCode()
-- FIXME: Some of the following categories should be added to this module.
if not fam then
insert(ret, {name = "Category:Unassigned languages", sort = sortkey})
elseif famCode == "qfa-dis" then
insert(ret, {name = "Category:Languages of disputed affiliation", sort = sortkey})
elseif famCode == "qfa-iso" then
insert(ret, {name = "Thể loại:Nhóm ngôn ngữ biệt lập", sort = sortkey})
elseif famCode == "qfa-mix" then
insert(ret, {name = "Category:Mixed languages", sort = sortkey})
elseif famCode == "qfa-unc" then
insert(ret, {name = "Category:Unclassifiable languages", sort = sortkey})
elseif famCode == "crp" then
insert(ret, {name = "Category:Creole or pidgin languages", sort = sortkey})
for _, anc in ipairs(lang:getAncestors()) do
-- Avoid Haitian Creole being categorised in [[:Category:Haitian Creole-based creole or pidgin languages]], as one of its ancestors is an etymology-only variety of it.
-- Use that ancestor's ancestors instead.
if anc:getFullCode() == lang:getCode() then
for _, anc_extra in ipairs(anc:getAncestors()) do
insert(ret, {name = "Thể loại:" .. ucfirst(anc_extra:getFullName()) .. "-based creole or pidgin languages", sort = sortkey})
end
else
insert(ret, {name = "Thể loại:" .. ucfirst(anc:getFullName()) .. "-based creole or pidgin languages", sort = sortkey})
end
end
elseif famCode == "art" then
if lang:hasType("appendix-constructed") then
insert(ret, {name = "Thể loại:Ngôn ngữ nhân tạo chỉ có trong phụ lục", sort = sortkey})
else
insert(ret, {name = "Thể loại:Ngôn ngữ nhân tạo", sort = sortkey})
end
for _, anc in ipairs(lang:getAncestors()) do
if anc:getFullCode() == lang:getCode() then
for _, anc_extra in ipairs(anc:getAncestors()) do
insert(ret, {name = "Thể loại:" .. ucfirst(anc_extra:getFullName()) .. "-based constructed languages", sort = sortkey})
end
else
insert(ret, {name = "Thể loại:" .. ucfirst(anc:getFullName()) .. "-based constructed languages", sort = sortkey})
end
end
else
insert(ret, {name = "Thể loại:" .. fam:getCategoryName(), sort = sortkey})
if lang:hasType("reconstructed") then
insert(ret, {
name = "Thể loại:Ngôn ngữ nhân tạo",
sort = {sort_base = canonicalName:gsub("^Proto%-", ""), lang = "en"}
})
end
end
if lang and type(lang.getPseudoFamilies) == "function" then
for _, pseudofam in ipairs(lang:getPseudoFamilies()) do
end
end
local function add_sc_cat(sc)
local catname
if sc:getCode() == "Zxxx" then
catname = "Unwritten languages"
else
catname = sc:getCategoryName(false, lang)
end
insert(ret, {name = "Thể loại:" .. catname, sort = sortkey})
end
local function add_Hrkt()
add_sc_cat(Hrkt)
add_sc_cat(Hira)
add_sc_cat(Kana)
end
for _, sc in ipairs(lang:getScripts()) do
if sc:getCode() == "Hrkt" then
add_Hrkt()
else
add_sc_cat(sc)
if sc:getCode() == "Jpan" then
add_sc_cat(Hani)
add_Hrkt()
elseif sc:getCode() == "Kore" then
add_sc_cat(Hang)
add_sc_cat(Hani)
end
end
end
if lang:hasTranslit() then
insert(ret, {name = "Thể loại:Ngôn ngữ với chuyển tự tự động", sort = sortkey})
end
local function insert_location_language_cat(location)
local cat = "Ngôn ngữ tại " .. location
insert(ret, {name = "Thể loại:" .. cat, sort = sortkey})
local auto_cat_args = scrape_category_for_auto_cat_args(cat)
local location_parent = auto_cat_args and auto_cat_args.parent
if location_parent then
local split_parents = require(parse_utilities_module).split_on_comma(location_parent)
for _, parent in ipairs(split_parents) do
parent = parent:match("^(.-):.*$") or parent
insert_location_language_cat(parent)
end
end
end
local saw_location = false
for _, location in ipairs(locations) do
if location ~= "UNKNOWN" then
saw_location = true
insert_location_language_cat(location)
end
end
if extinct then
insert(ret, {name = "Thể loại:Ngôn ngữ tuyệt chủng", sort = sortkey})
end
if not saw_location and not (lang:hasType("reconstructed") or (fam and fam:getCode() == "art")) then
-- Constructed and reconstructed languages don't need a location specified and often won't have one,
-- so don't put them in this maintenance category.
insert(ret, {name = "Thể loại:Ngôn ngữ không xếp với tên quốc gia", sort = sortkey})
end
return ret
end
local function get_children()
local ret = {}
-- FIXME: We should work on the children mechanism so it isn't necessary to manually specify these.
for _, label in ipairs({"Phụ lục", "Bảo trì mục từ", "Mục từ", "Cụm từ", "Vần", "Ký hiệu", "Bản mẫu", "Từ nguyên", "Từ theo cách sử dụng"}) do
insert(ret, {name = label, is_label = true})
end
insert(ret, {name = "Từ dẫn xuất từ {{{langname}}}", is_label = true, lang = false})
insert(ret, {name = "{{{langcode}}}:Tất cả chủ đề", sort = "tất cả chủ đề"})
insert(ret, {name = "Biến thể {{{langname}}}"})
insert(ret, {name = "Yêu cầu liên quan đến {{{langname}}}"})
-- insert(ret, {name = "Vần:{{{langname}}}", description = "Liệt kê các mục từ {{{langname}}} theo vần."})
insert(ret, {name = "Thành viên {{{langcode}}}", description = "Wiktionary users categorized by fluency levels in {{{langdisp}}}."})
return ret
end
-- Handle language categories of the form e.g. [[:Category:French language]] and
-- [[:Category:British Sign Language]].
insert(raw_handlers, function(data)
local category = data.category
if not (category:match("^[Tt]iếng") or category:match("[Ll]ect$")) then
return nil
end
local lang = m_languages.getByCanonicalName(category)
if not lang then
local langname = category
if langname then
lang = m_languages.getByCanonicalName(langname)
end
if not lang then
return nil
end
end
local args = require("Module:parameters").process(data.args, {
[1] = {list = true},
["setwiki"] = true,
["setwikt"] = true,
["setsister"] = true,
["entryname"] = true,
["extinct"] = {type = "boolean"},
})
-- If called from inside, don't require any arguments, as they can't be known
-- in general and aren't needed just to generate the first parent (used for
-- breadcrumbs).
if #args[1] == 0 and not data.called_from_inside then
-- At least one location must be specified unless the language is constructed (e.g. Esperanto) or reconstructed (e.g. Proto-Indo-European).
local fam = lang:getFamily()
if not (lang:hasType("reconstructed") or (fam and fam:getCode() == "art")) then
error("At least one location (param 1=) must be specified for language '" .. lang:getCanonicalName() .. "' (code '" .. lang:getCode() .. "'). " ..
"Use the value UNKNOWN if the language's location is truly unknown.")
end
end
local description, topright, additional = "", "", ""
-- If called from inside the category tree system, it's called when generating
-- parents or children, and we don't need to generate the description or additional
-- text (which is very expensive in terms of memory because it calls [[Module:family tree]],
-- which calls [[Module:languages/data/all]]).
if not data.called_from_inside then
description, topright, additional = get_description_topright_additional(
lang, args[1], args.extinct, args.setwiki, args.setwikt, args.setsister, args.entryname
)
end
return {
canonical_name = lang:getCategoryName(),
description = description,
lang = lang:getCode(),
topright = topright,
additional = additional,
breadcrumb = lang:getCanonicalName(),
parents = get_parents(lang, args[1], args.extinct),
extra_children = get_children(lang),
umbrella = false,
can_be_empty = true,
}, true
end)
-- Handle categories such as [[:Category:Languages of Indonesia]].
insert(raw_handlers, function(data)
local location = data.category:match("^Ngôn ngữ tại (.*)$")
if location then
local args = require("Module:parameters").process(data.args, {
["flagfile"] = true,
["commonscat"] = true,
["wp"] = true,
["basename"] = true,
["parent"] = true,
["locationcat"] = true,
["locationlink"] = true,
})
local topright
local basename = args.basename or location:gsub(", .*", "")
local translated_basename = location_translation[basename] or basename
if args.flagfile ~= "-" then
local flagfile_arg = args.flagfile or ("Flag of %s.svg"):format(translated_basename)
local files = require(parse_utilities_module).split_on_comma(flagfile_arg)
local topright_parts = {}
for _, file in ipairs(files) do
local flagfile = "File:" .. file
local flagfile_page = new_title(flagfile)
if flagfile_page and flagfile_page.file.exists then
insert(topright_parts, ("[[%s|right|100px|border]]"):format(flagfile))
elseif args.flagfile then
error(("Explicit flagfile '%s' doesn't exist"):format(flagfile))
end
end
topright = concat(topright_parts)
end
if args.wp then
local wp = require("Module:yesno")(args.wp, "+")
if wp == "+" or wp == true then
wp = data.category
end
if wp then
local wp_topright = ("{{wikipedia|%s}}"):format(wp)
if topright then
topright = topright .. wp_topright
else
topright = wp_topright
end
end
end
if args.commonscat then
local commonscat = require("Module:yesno")(args.commonscat, "+")
if commonscat == "+" or commonscat == true then
commonscat = data.category
end
if commonscat then
local commons_topright = ("{{commonscat|%s}}"):format(commonscat)
if topright then
topright = topright .. commons_topright
else
topright = commons_topright
end
end
end
local bare_location = location:match("^(.*)$") or location
local location_link = args.locationlink or link_location(location)
local bare_basename = basename:match("^(.*)$") or basename
local parents = {}
if args.parent then
local explicit_parents = require(parse_utilities_module).split_on_comma(args.parent)
for i, parent in ipairs(explicit_parents) do
local actual_parent, sort_key = parent:match("^(.-):(.*)$")
if actual_parent then
parent = actual_parent
sort_key = sort_key:gsub("%+", bare_location)
else
sort_key = " " .. bare_location
end
insert(parents, {name = "Ngôn ngữ tại " .. parent, sort = sort_key})
end
else
insert(parents, {name = "Ngôn ngữ theo quốc gia", sort = {sort_base = bare_location, lang = "vi"}})
end
if args.locationcat then
local explicit_location_cats = require(parse_utilities_module).split_on_comma(args.locationcat)
for i, locationcat in ipairs(explicit_location_cats) do
insert(parents, {name = "Thể loại:" .. locationcat, sort = " Languages"})
end
else
local location_cat = ("Thể loại:%s"):format(bare_location)
local location_page = new_title(location_cat)
if location_page and location_page.exists then
insert(parents, {name = location_cat, sort = "Languages"})
end
end
local description = ("Thể loại dành cho ngôn ngữ tại %s."):format(location_link)
return {
topright = topright,
description = description,
parents = parents,
breadcrumb = bare_basename,
additional = "{{{umbrella_msg}}}",
}, true
end
end)
-- Handle categories such as [[:Category:English-based creole or pidgin languages]].
insert(raw_handlers, function(data)
local langname = data.category:match("(.*)%-based creole or pidgin languages$")
if langname then
local lang = m_languages.getByCanonicalName(langname)
if lang then
return {
lang = lang:getCode(),
description = "Languages which developed as a [[creole]] or [[pidgin]] from " .. lang:makeCategoryLink() .. ".",
parents = {{name = "Creole or pidgin languages", sort = {sort_base = "*" .. langname, lang = "en"}}},
breadcrumb = lang:getCanonicalName() .. "-based",
}
end
end
end)
-- Handle categories such as [[:Category:English-based constructed languages]].
insert(raw_handlers, function(data)
local langname = data.category:match("(.*)%-based constructed languages$")
if langname then
local lang = m_languages.getByCanonicalName(langname)
if lang then
return {
lang = lang:getCode(),
description = "Constructed languages which are based on " .. lang:makeCategoryLink() .. ".",
parents = {{name = "Constructed languages", sort = {sort_base = "*" .. langname, lang = "en"}}},
breadcrumb = lang:getCanonicalName() .. "-based",
}
end
end
end)
return {
RAW_CATEGORIES = raw_categories,
RAW_HANDLERS = raw_handlers
}
1sm6jitb6iontlya2nlvzol1wykrtph
2573982
2573981
2026-10-28T09:05:52Z
Hiyuune
50834
2573982
Scribunto
text/plain
local new_title = mw.title.new
local ucfirst = require("Module:string utilities").ucfirst
local split = require("Module:string utilities").split
local raw_categories = {}
local raw_handlers = {}
local m_languages = require("Module:languages")
local m_sc_getByCode = require("Module:scripts").getByCode
local m_table = require("Module:table")
local parse_utilities_module = "Module:parse utilities"
local location_translation = require("Module:category tree/location translation")
local concat = table.concat
local insert = table.insert
local reverse_ipairs = m_table.reverseIpairs
local serial_comma_join = m_table.serialCommaJoin
local size = m_table.size
local sorted_pairs = m_table.sortedPairs
local to_json = require("Module:JSON").toJSON
local Hang = m_sc_getByCode("Hang")
local Hani = m_sc_getByCode("Hani")
local Hira = m_sc_getByCode("Hira")
local Hrkt = m_sc_getByCode("Hrkt")
local Kana = m_sc_getByCode("Kana")
local function track(page)
-- [[Special:WhatLinksHere/Wiktionary:Tracking/category tree/languages/PAGE]]
return require("Module:debug/track")("category tree/languages/" .. page)
end
-- This handles language categories of the form e.g. [[:Category:French language]] and
-- [[:Category:British Sign Language]]; categories like [[:Category:Languages of Indonesia]]; categories like
-- [[:Category:English-based creole or pidgin languages]]; and categories like
-- [[:Category:English-based constructed languages]].
-----------------------------------------------------------------------------
-- --
-- RAW CATEGORIES --
-- --
-----------------------------------------------------------------------------
raw_categories["Tất cả ngôn ngữ"] = {
topright = "{{commonscat|Languages}}\n[[File:Languages world map-transparent background.svg|thumb|right|250px|Rough world map of language families]]",
description = "Categories for every language on Wiktionary.",
additional = "Not all languages that Wiktionary recognises may have a category here yet. There are many that have " ..
"not yet received any attention from editors, mainly because not all Wiktionary users know about every single " ..
"language. See [[Wiktionary:List of languages]] for a full list.",
parents = {
"Tất cả",
},
}
raw_categories["Đa ngữ"] = {
description = "Đây là thể loại chính của Đa ngữ.",
parents = {
"Tất cả ngôn ngữ",
},
}
raw_categories["Ngôn ngữ tuyệt chủng"] = {
description = "Categories for every [[extinct language]] on Wiktionary.",
additional = "Do not confuse this category with [[:Category:Extinct languages]], which is an umbrella category for the names of extinct languages in specific other languages (e.g. {{m+|de|Langobardisch}} for the ancient [[Lombardic]] language).",
parents = {
{name = "Tất cả ngôn ngữ", sort = " "},
},
}
raw_categories["Ngôn ngữ nhân tạo"] = {
description = "Categories for every [[Wiktionary:Reconstructed terms|reconstructed language]] on Wiktionary, and appendices for proposed proto-language reconstructions that are considered promising but have not yet gained general acceptance.",
parents = {
{name = "Ngôn ngữ tuyệt chủng", sort = " "},
{name = "Tất cả ngôn ngữ", sort = " "},
},
}
raw_categories["Mục từ chỉ có trong phụ lục"] = {
description = "Terms that do not pass the [[WT:CFI|criteria for inclusion]], but are noteworthy enough to be included in the Appendix.",
additional = "Examples are dictionary-only terms (terms found in dictionaries but not elsewhere, including but not limited to [[ghost word]]s) and certain noteworthy [[protologism]]s.",
parents = {"Phụ lục"},
}
raw_categories["Ngôn ngữ nhân tạo chỉ có trong phụ lục"] = {
description = "Theo [[Wiktionary:Quy định mục từ|quy định mục từ]], các ngôn ngữ nhân tạo không được áp dụng trong không gian chính và chỉ khả dụng trong không gian tên {{cd|Phụ lục:}}.",
additional = "Ví dụ, các mục từ tiếng Klingon chỉ có sẵn ở [[Phụ lục:Tiếng Klingon]].",
breadcrumb = "chỉ có trong phụ lục",
parents = {
{name = "Ngôn ngữ nhân tạo", sort = " "},
"Mục từ chỉ có trong phụ lục",
},
}
raw_categories["Unattested languages"] = {
description = "Categories for languages of which there are no written records and which have not been reconstructed based on their descendants.",
additional = "Most of these are [[w:Stratum (linguistics)#Unattested substrata|unattested substrate languages]] that have been proposed as sources of vocabulary in certain languages.",
parents = {
{name = "All extinct languages", sort = " "},
{name = "Tất cả ngôn ngữ", sort = " "},
},
}
raw_categories["Unwritten languages"] = {
description = "Categories for every [[unwritten]] [[language]] on Wiktionary.",
additional = "Do not confuse this category with [[:Category:Unspecified script languages]], which contains categories for languages that may be written but where the appropriate script has not yet been specified in the language data.",
parents = {
{name = "Tất cả ngôn ngữ", sort = " "},
},
}
raw_categories["Ngôn ngữ với chuyển tự tự động"] = {
preceding = "{{also|:Thể loại:Mục từ có chuyển tự thừa theo ngôn ngữ|:Thể loại:Mục từ có chuyển tự thủ công không thừa theo ngôn ngữ}}",
description = "Categories for languages with automatic [[transliteration]], i.e. which have the name of a transliteration module listed in their [[:Category:Language data modules|language data table]].",
additional = [=[For example, for {{lnl|hi}}, {{lua|1=translit_module = "hi-translit"}}, referring to [[Module:hi-translit]]). The [[:Category:Transliteration modules|transliteration module]] converts the language's native script into a Latin-script version (a transliteration), which is then displayed in templates like {{tl|l}}, {{tl|m}} and {{tl|head}}.]=],
parents = {
{name = "Tất cả ngôn ngữ", sort = " "},
{name = "Mô đun chuyển tự", sort = " "},
}
}
raw_categories["Ngôn ngữ theo quốc gia"] = {
topright = "{{commonscat|Languages by continent}}",
description = "Ngôn ngữ theo quốc gia.",
additional = "{{{umbrella_meta_msg}}}",
parents = {
{name = "Tất cả ngôn ngữ", sort = " "},
},
}
raw_categories["Ngôn ngữ không xếp với tên quốc gia"] = {
description = "Languages which do not specify (in their {{tl|auto cat}} call) the location(s) where they are spoken.",
additional = "This excludes constructed and reconstructed languages; as a result, all languages in this category explicitly specify their location as {{cd|UNKNOWN}}.",
parents = {
{name = "Yêu cầu"},
},
hidden = true,
}
-----------------------------------------------------------------------------
-- --
-- RAW HANDLERS --
-- --
-----------------------------------------------------------------------------
-- Given a category (without the "Category:" prefix), look up the page
-- defining the category, find the call to {{auto cat}} (if any),
-- and return a table of its arguments. If the category page doesn't exist
-- or doesn't have an {{auto cat}} invocation, return nil.
-- FIXME: Duplicated in [[Module:category tree/lects]].
local function scrape_category_for_auto_cat_args(cat)
local cat_page = mw.title.new("Thể loại:" .. cat)
if cat_page then
local contents = cat_page:getContent()
if contents then
for template in require("Module:template parser").find_templates(contents) do
-- The template parser automatically handles redirects and
-- canonicalizes them.
if template:get_name() == "auto cat" then
return template:get_arguments()
end
end
end
end
return nil
end
local function link_location(location)
local location_no_the = location:match("^(.*)$")
local bare_location = location_no_the or location
local bare_location_parts = split(bare_location, ", ")
for i, part in ipairs(bare_location_parts) do
bare_location_parts[i] = ("[[%s]]"):format(part)
end
local location_link = concat(bare_location_parts, ", ")
if location_no_the then location_link = location_link end
return location_link
end
local function linkbox(lang, setwiki, setwikt, setsister, entryname)
local wiktionarylinks = {}
local canonicalName = lang:getCanonicalName()
local wikimediaLanguages = lang:getWikimediaLanguages()
local wikipediaArticle = setwiki or lang:getWikipediaArticle(true)
setwiki = not wikipediaArticle and "-"
setsister = setsister and ucfirst(setsister) or nil
if setwikt then
track("setwikt")
if setwikt == "-" then track("setwikt/hyphen") end
end
if setwikt ~= "-" and wikimediaLanguages and wikimediaLanguages[1] then
for _, wikimedialang in ipairs(wikimediaLanguages) do
local check = new_title(wikimedialang:getCode() .. ":")
if check and check.isExternal then
insert(wiktionarylinks,
(
wikimedialang:getCanonicalName() ~= canonicalName
and "(''" .. wikimedialang:getCanonicalName() .. "'') "
or ""
) .. (
"'''[[:" .. wikimedialang:getCode()
.. ":|" .. wikimedialang:getCode()
.. ".wiktionary.org]]'''"
)
)
end
end
wiktionarylinks = concat(wiktionarylinks, "<br/>")
end
local wikt_plural = wikimediaLanguages[2] and "" or ""
if #wiktionarylinks == 0 then
wiktionarylinks = "''Không có.''"
end
-- Avoid showing Wiktionary links section for reconstructed languages,
-- as they are ineligible for Wiktionary editions
local wiktionarylinks_chunk = concat{
[=[|-
| style="vertical-align: top; height: 35px; width: 40px; border-bottom: 1px solid var(--wikt-palette-grey-4, lightgray);" | [[File:Wiktionary-logo-v2.svg|35px|none|link=|Wiktionary]]
|style="border-bottom: 1px solid var(--wikt-palette-grey-4, lightgray);" | '''Phiên bản Wiktionary''']=], wikt_plural, [=[ viết ở ]=], canonicalName, [=[:
<div style="padding: 5px 10px">]=], wiktionarylinks, [=[</div>
]=]}
if lang:hasType('reconstructed') then
wiktionarylinks_chunk = ''
end
if setsister then
track("setsister")
if setsister == "-" then
track("setsister/hyphen")
else
setsister = "Thể loại:" .. setsister
end
else
setsister = lang:getCommonsCategory() or "-"
end
return concat{ -- FIXME: Bare wikicode
[=[<div class="wikitable" style="float: right; clear: right; margin: 0 0 0.5em 1em; width: 300px; padding: 5px;">
<div style="text-align: center; margin-bottom: 10px; margin-top: 5px">'''Liên kết ]=], canonicalName, [=['''</div>
{| style="font-size: 90%"
|-
| style="vertical-align: top; height: 35px; border-bottom: 1px solid var(--wikt-palette-grey-4, lightgray);" | [[File:Wikipedia-logo.png|35px|none|link=|Wikipedia]]
| style="border-bottom: 1px solid var(--wikt-palette-grey-4, lightgray);" | '''Wikipedia tiếng Việt''' có bài viết về:
<div style="padding: 5px 10px">]=], (setwiki == "-" and "''Không có.''" or "'''[[w:" .. wikipediaArticle .. "|" .. wikipediaArticle .. "]]'''"), [=[</div>
|-
| style="vertical-align: top; height: 35px; border-bottom: 1px solid var(--wikt-palette-grey-4, lightgray);" | [[File:Commons-logo.svg|35px|none|link=|Wikimedia Commons]]
| style="border-bottom: 1px solid var(--wikt-palette-grey-4, lightgray);" | '''Wikimedia Commons''' có liên kết đến các nội dung liên quan đến ]=], canonicalName, [=[
<div style="padding: 5px 10px">]=], (setsister == "-" and "''Không có.''" or "'''[[commons:" .. setsister .. "|" .. setsister .. "]]'''"), [=[</div>
]=], wiktionarylinks_chunk, [=[
|-
| style="vertical-align: top; height: 35px; border-bottom: 1px solid var(--wikt-palette-grey-4, lightgray);" | [[File:Codex icon articles color-placeholder (v2.6).svg|35px|none|link=|Entry]]
| style="border-bottom: 1px solid var(--wikt-palette-grey-4, lightgray);" | '''Wiktionary tiếng Việt''' có mục từ liên quan đến tên ngôn ngữ:
<div style="padding: 5px 10px">''']=], require("Module:links").full_link({lang = m_languages.getByCode("vi"), term = entryname or canonicalName}), [=['''</div>
|-
| style="vertical-align: top; height: 35px;" | [[File:Codex icon book color-placeholder (v2.6).svg|35px|none|link=|Nguồn tham khảo]]
|| '''Tài nguyên Wiktionary''' dành cho biên tập viên đóng góp vào các mục từ ]=], canonicalName, [=[:
<div style="padding: 5px 0">
* '''[[Wiktionary:Quy định mục từ ]=], canonicalName, [=[]]'''
* '''[[:Thể loại:Bản mẫu tham khảo ]=], canonicalName, [=[|Bản mẫu tham khảo]] ({{PAGESINCAT:Bản mẫu tham khảo ]=], canonicalName, [=[}})'''
* '''[[Phụ lục:Thư mục học ]=], canonicalName, [=[|Thư mục học]]'''
</div>
|}
</div>]=]
}
end
local function edit_link(title, text)
return '<span class="plainlinks">['
.. tostring(mw.uri.fullUrl(title, { action = "edit" }))
.. ' ' .. text .. ']</span>'
end
-- Should perhaps use wiki syntax.
local function infobox(lang)
local ret = {}
insert(ret, '<table class="wikitable language-category-info"')
local raw_data = lang:getData("extra")
if raw_data then
local replacements = {
[1] = "canonical-name",
[2] = "wikidata-item",
[3] = "family",
[4] = "scripts",
}
local function replacer(letter1, letter2)
return letter1:lower() .. "-" .. letter2:lower()
end
-- For each key in the language data modules, returns a descriptive
-- kebab-case version (containing ASCII lowercase words separated
-- by hyphens).
local function kebab_case(key)
key = tostring(key)
key = key:gsub("[_ ]+", "-")
return key
end
local compress = {compress = true}
local function html_attribute_encode(str)
str = to_json(str, compress)
:gsub('"', """)
-- & in attributes is automatically escaped.
-- :gsub("&", "&")
:gsub("<", "<")
:gsub(">", ">")
return str
end
insert(ret, ' data-code="' .. lang:getCode() .. '"')
for k, v in sorted_pairs(raw_data) do
insert(ret, " data-" .. kebab_case(k)
.. '="'
.. html_attribute_encode(v)
.. '"')
end
end
insert(ret, '>\n')
insert(ret, '<tr class="language-category-data">\n<th colspan="2">'
.. edit_link(lang:getDataModuleName(), "Chỉnh sửa dữ liệu ngôn ngữ")
.. "</th>\n</tr>\n")
insert(ret, "<tr>\n<th>Tên chuẩn</th><td>" .. lang:getCanonicalName() .. "</td>\n</tr>\n")
local otherNames = lang:getOtherNames()
if otherNames then
local names = {}
for _, name in ipairs(otherNames) do
insert(names, "<li>" .. name .. "</li>")
end
if #names > 0 then
insert(ret, (
"<tr>\n<th>Tên khác</th><td><ul>"
.. concat(names, "\n")
.. "</ul></td>\n</tr>\n"
)
)
end
end
local aliases = lang:getAliases()
if aliases then
local names = {}
for _, name in ipairs(aliases) do
insert(names, "<li>" .. name .. "</li>")
end
if #names > 0 then
insert(ret, (
"<tr>\n<th>Tên thay thế</th><td><ul>"
.. concat(names, "\n")
.. "</ul></td>\n</tr>\n"
)
)
end
end
local varieties = lang:getVarieties()
if varieties then
local names = {}
for _, name in ipairs(varieties) do
if type(name) == "string" then
insert(names, "<li>" .. name .. "</li>")
else
assert(type(name) == "table")
local first_var
local subvars = {}
for i, var in ipairs(name) do
if i == 1 then
first_var = var
else
insert(subvars, "<li>" .. var .. "</li>")
end
end
if #subvars > 0 then
insert(names, (
"<li><dl><dt>"
.. first_var .. "</dt>\n<dd><ul>"
.. concat(subvars, "\n")
.. "</ul></dd></dl></li>"
)
)
elseif first_var then
insert(names, "<li>" .. first_var .. "</li>")
end
end
end
if #names > 0 then
insert(ret, (
"<tr>\n<th>Biến thể</th><td><ul>"
.. concat(names, "\n")
.. "</ul></td>\n</tr>\n"
)
)
end
end
insert(ret, (
"<tr>\n<th>[[Wiktionary:Ngôn ngữ|Mã ngôn ngữ]]</th><td><code>"
.. lang:getCode()
.. "</code></td>\n</tr>\n"
)
)
insert(ret, "<tr>\n<th>[[Wiktionary:Ngữ hệ|Ngữ hệ]]</th>\n")
local fam = lang:getFamily()
local famCode = fam and fam:getCode()
if not fam then
insert(ret, "<td>[[:Category:Unassigned languages|unassigned]]</td>")
elseif famCode == "qfa-dis" then
insert(ret, "<td>[[:Category:Languages of disputed affiliation|disputed affiliation]]</td>")
elseif famCode == "qfa-iso" then
insert(ret, "<td>[[:Thể loại:Nhóm ngôn ngữ biệt lập|Nhóm ngôn ngữ biệt lập]]</td>")
elseif famCode == "qfa-mix" then
insert(ret, "<td>[[:Category:Mixed languages|mixed language]]</td>")
elseif famCode == "qfa-unc" then
insert(ret, "<td>[[:Category:Unclassifiable languages|unclassifiable language]]</td>")
elseif famCode == "sgn" then
insert(ret, "<td>[[:Category:Sign languages|sign language]]</td>")
elseif famCode == "crp" then
insert(ret, "<td>[[:Category:Creole or pidgin languages|creole or pidgin]]</td>")
elseif famCode == "art" then
insert(ret, "<td>[[:Thể loại:Ngôn ngữ nhân tạo|ngôn ngữ nhân tạo]]</td>")
else
insert(ret, "<td>" .. fam:makeCategoryLink() .. "</td>")
end
insert(ret, "\n</tr>\n<tr>\n<th>Ngôn ngữ<br/>nguyên thủy</th>\n<td>")
local ancestors = lang:getAncestors()
if ancestors[2] then
local ancestorList = {}
for i, anc in ipairs(ancestors) do
ancestorList[i] = "<li>" .. anc:makeCategoryLink() .. "</li>"
end
insert(ret, "<ul>\n" .. concat(ancestorList, "\n") .. "</ul>")
else
local ancestorChain = lang:getAncestorChainOld()
if ancestorChain[1] then
local chain = {}
for _, anc in reverse_ipairs(ancestorChain) do
insert(chain, "<li>" .. anc:makeCategoryLink() .. "</li>")
end
insert(ret, "<ul>\n" .. concat(chain, "\n<ul>\n") .. ("</ul>"):rep(#chain))
else
insert(ret, "không rõ")
end
end
insert(ret, "</td>\n</tr>\n")
local scripts = lang:getScripts()
if scripts[1] then
local script_text = {}
local function makeScriptLine(sc)
local code = sc:getCode()
local url = tostring(mw.uri.fullUrl('Đặc biệt:Search', {
search = 'contentmodel:css insource:"' .. code
.. '" insource:/\\.' .. code .. '/',
ns8 = '1'
}))
local sc_catlink
if code == "Zxxx" then
sc_catlink = "[[:Category:Unwritten languages|unwritten]]"
else
sc_catlink = sc:makeCategoryLink(lang)
end
return sc_catlink
.. ' (<span class="plainlinks" title="Search for stylesheets referencing this script">[' .. url .. ' <code>' .. code .. '</code>]</span>)'
end
local function add_Hrkt(text)
insert(text, "<li>" .. makeScriptLine(Hrkt))
insert(text, "<ul>")
insert(text, "<li>" .. makeScriptLine(Hira) .. "</li>")
insert(text, "<li>" .. makeScriptLine(Kana) .. "</li>")
insert(text, "</ul>")
insert(text, "</li>")
end
for _, sc in ipairs(scripts) do
local text = {}
local code = sc:getCode()
if code == "Hrkt" then
add_Hrkt(text)
else
insert(text, "<li>" .. makeScriptLine(sc))
if code == "Jpan" then
insert(text, "<ul>")
insert(text, "<li>" .. makeScriptLine(Hani) .. "</li>")
add_Hrkt(text)
insert(text, "</ul>")
elseif code == "Kore" then
insert(text, "<ul>")
insert(text, "<li>" .. makeScriptLine(Hang) .. "</li>")
insert(text, "<li>" .. makeScriptLine(Hani) .. "</li>")
insert(text, "</ul>")
end
insert(text, "</li>")
end
insert(script_text, concat(text, "\n"))
end
insert(ret, "<tr>\n<th>[[Wiktionary:Hệ chữ viết|Hệ chữ viết]]</th>\n<td><ul>\n" .. concat(script_text, "\n") .. "</ul></td>\n</tr>\n")
else
insert(ret, "<tr>\n<th>[[Wiktionary:Hệ chữ viết|Hệ chữ viết]]</th>\n<td>chưa được phân loại</td>\n</tr>\n")
end
local function add_module_info(raw_data, heading)
if raw_data then
local scripts = lang:getScriptCodes()
local module_info, add = {}, false
if type(raw_data) == "string" then
insert(module_info,
("[[Mô đun:%s]]"):format(raw_data))
add = true
else
local raw_data_type = type(raw_data)
if raw_data_type == "table" and size(scripts) == 1 and type(raw_data[scripts[1]]) == "string" then
insert(module_info,
("[[Mô đun:%s]]"):format(raw_data[scripts[1]]))
add = true
elseif raw_data_type == "table" then
insert(module_info, "<ul>")
for script, data in sorted_pairs(raw_data) do
if type(data) == "string" and m_sc_getByCode(script) then
insert(module_info, ("<li><code>%s</code>: [[Mô đun:%s]]</li>"):format(script, data))
end
end
insert(module_info, "</ul>")
add = size(module_info) > 2
end
end
if add then
insert(ret, [=[
<tr>
<th>]=] .. heading .. [=[</th>
<td>]=] .. concat(module_info) .. [=[</td>
</tr>
]=])
end
end
end
add_module_info(raw_data.generate_alternants, "Alternant-generating<br>module")
add_module_info(raw_data.translit, "Mô đun<br/>chuyển tự")
add_module_info(raw_data.display_text, "Display text<br>module")
add_module_info(raw_data.strip_diacritics, "Diacritic-stripping<br>module")
add_module_info(raw_data.sort_key, "[[sortkey|Sortkey]]<br>module")
local wikidataItem = lang:getWikidataItem()
if lang:getWikidataItem() and mw.wikibase then
local URL = mw.wikibase.getEntityUrl(wikidataItem)
local link
if URL then
link = '[[d:' .. wikidataItem .. '|' .. wikidataItem .. ']]'
else
link = '<span class="error">Khoản mục Wikidata không hợp lệ: <code>' .. wikidataItem .. '</code></span>'
end
insert(ret, "<tr><th>Khoản mục<br/>Wikidata</th><td>" .. link .. "</td></tr>")
end
insert(ret, "</table>")
return concat(ret)
end
local function NavFrame_for_family_tree(content, title)
return '<div class="NavFrame"><div class="NavHead">'
.. (title or '{{{title}}}') .. '</div>'
.. '<div class="NavContent" style="text-align: left; font-size: calc(1em / 0.95); padding: 0.3em">'
.. content
.. '</div></div>'
end
local function get_description_topright_additional(lang, locations, extinct, setwiki, setwikt, setsister, entryname)
local nameWithLanguage = lang:getCategoryName("nocap")
if lang:getCode() == "und" then
local description =
"Đây là thể loại chính của '''" .. nameWithLanguage .. "''', represented in Wiktionary by the [[Wiktionary:Languages|code]] '''" .. lang:getCode() .. "'''. " ..
"This language contains terms in historical writing, whose meaning has not yet been determined by scholars."
return description, nil, nil
end
local canonicalName = lang:getCanonicalName()
local topright = linkbox(lang, setwiki, setwikt, setsister, entryname)
local description = "Đây là thể loại chính của " .. "'''" .. nameWithLanguage .. "'''."
local location_links = {}
local prep
local saw_embedded_comma = false
for _, location in ipairs(locations) do
local this_prep
if location == "the world" then
this_prep = "across"
insert(location_links, location)
elseif location ~= "UNKNOWN" then
this_prep = "ở"
if location:find(",") then
saw_embedded_comma = true
end
insert(location_links, link_location(location))
end
if this_prep then
if prep and this_prep ~= prep then
error("Can't handle location 'the world' along with another location (clashing prepositions)")
end
prep = this_prep
end
end
local location_desc
if #location_links > 0 then
local location_link_text
if saw_embedded_comma and #location_links >= 3 then
location_link_text = mw.text.listToText(location_links, "; ", "; và ")
else
location_link_text = serial_comma_join(location_links)
end
location_desc = ("Ngôn ngữ này %s %s %s.\n\n"):format(
extinct and "là một ngôn ngữ đã tuyệt chủng trước đây được sử dụng" or "được sử dụng",
prep, location_link_text
)
elseif extinct then
location_desc = "Đây là một ngôn ngữ đã tuyệt chủng.\n\n"
else
location_desc = ""
end
local add = location_desc .. "Thông tin về " .. canonicalName .. ":\n\n" .. infobox(lang)
if lang:hasType("reconstructed") then
add = add .. "\n\n" ..
ucfirst(canonicalName) .. " là một ngôn ngữ tái tạo. Its words and roots are not directly attested in any written works, but have been reconstructed through the ''comparative method'', " ..
"which finds regular similarities between languages that cannot be explained by coincidence or word-borrowing, and extrapolates ancient forms from these similarities.\n\n" ..
"Theo [[Wiktionary:Quy định mục từ|quy định mục từ]], mục từ " .. canonicalName ..
" không nằm ở không gian chính, nhưng có thể áp dụng ở không gian tên '''Từ tái tạo'''."
elseif lang:hasType("appendix-constructed") then
add = add .. "\n\n" ..
ucfirst(canonicalName) .. " is a constructed language that is only in sporadic use. " ..
"According to our [[Wiktionary:Criteria for inclusion|criteria for inclusion]], terms in " .. canonicalName ..
" should '''not''' be present in entries in the main namespace, but may be added to the Appendix: namespace. " ..
"All terms in this language may be available at [[Appendix:" .. ucfirst(canonicalName) .. "]]."
end
local entry_guidelines_page = "Wiktionary:Quy định mục từ " .. canonicalName
local entry_guidelines = new_title(entry_guidelines_page)
if entry_guidelines.exists then
add = add .. "\n\n" ..
"Xin vui lòng xem '''[[" .. entry_guidelines_page .. "]]''' để biết thông tin và cân nhắc đặc biệt khi tạo mục từ " .. nameWithLanguage .. "."
end
local ok, tree_of_descendants = pcall(
require("Module:family tree").render,
lang:getCode(), {
families = "show",
varieties = "show",
pair = "family-first",
must_have_descendants = true,
})
if ok then
if tree_of_descendants then
add = add .. NavFrame_for_family_tree(
tree_of_descendants,
"Family tree")
else
add = add .. "\n\n" .. ucfirst(lang:getCanonicalName())
.. " has no descendants or varieties listed in Wiktionary's language data modules."
end
else
mw.log("error while generating tree: " .. tostring(tree_of_descendants))
end
return description, topright, add
end
local function get_parents(lang, locations, extinct)
local canonicalName = lang:getCanonicalName()
local sortkey = {sort_base = canonicalName, lang = "vi"}
local ret = {{name = "Tất cả ngôn ngữ", sort = sortkey}}
local fam = lang:getFamily()
local famCode = fam and fam:getCode()
-- FIXME: Some of the following categories should be added to this module.
if not fam then
insert(ret, {name = "Category:Unassigned languages", sort = sortkey})
elseif famCode == "qfa-dis" then
insert(ret, {name = "Category:Languages of disputed affiliation", sort = sortkey})
elseif famCode == "qfa-iso" then
insert(ret, {name = "Thể loại:Nhóm ngôn ngữ biệt lập", sort = sortkey})
elseif famCode == "qfa-mix" then
insert(ret, {name = "Category:Mixed languages", sort = sortkey})
elseif famCode == "qfa-unc" then
insert(ret, {name = "Category:Unclassifiable languages", sort = sortkey})
elseif famCode == "crp" then
insert(ret, {name = "Category:Creole or pidgin languages", sort = sortkey})
for _, anc in ipairs(lang:getAncestors()) do
-- Avoid Haitian Creole being categorised in [[:Category:Haitian Creole-based creole or pidgin languages]], as one of its ancestors is an etymology-only variety of it.
-- Use that ancestor's ancestors instead.
if anc:getFullCode() == lang:getCode() then
for _, anc_extra in ipairs(anc:getAncestors()) do
insert(ret, {name = "Thể loại:" .. ucfirst(anc_extra:getFullName()) .. "-based creole or pidgin languages", sort = sortkey})
end
else
insert(ret, {name = "Thể loại:" .. ucfirst(anc:getFullName()) .. "-based creole or pidgin languages", sort = sortkey})
end
end
elseif famCode == "art" then
if lang:hasType("appendix-constructed") then
insert(ret, {name = "Thể loại:Ngôn ngữ nhân tạo chỉ có trong phụ lục", sort = sortkey})
else
insert(ret, {name = "Thể loại:Ngôn ngữ nhân tạo", sort = sortkey})
end
for _, anc in ipairs(lang:getAncestors()) do
if anc:getFullCode() == lang:getCode() then
for _, anc_extra in ipairs(anc:getAncestors()) do
insert(ret, {name = "Thể loại:" .. ucfirst(anc_extra:getFullName()) .. "-based constructed languages", sort = sortkey})
end
else
insert(ret, {name = "Thể loại:" .. ucfirst(anc:getFullName()) .. "-based constructed languages", sort = sortkey})
end
end
else
insert(ret, {name = "Thể loại:" .. fam:getCategoryName(), sort = sortkey})
if lang:hasType("reconstructed") then
insert(ret, {
name = "Thể loại:Ngôn ngữ nhân tạo",
sort = {sort_base = canonicalName:gsub("^Proto%-", ""), lang = "en"}
})
end
end
if lang and type(lang.getPseudoFamilies) == "function" then
for _, pseudofam in ipairs(lang:getPseudoFamilies()) do
end
end
local function add_sc_cat(sc)
local catname
if sc:getCode() == "Zxxx" then
catname = "Unwritten languages"
else
catname = sc:getCategoryName(false, lang)
end
insert(ret, {name = "Thể loại:" .. catname, sort = sortkey})
end
local function add_Hrkt()
add_sc_cat(Hrkt)
add_sc_cat(Hira)
add_sc_cat(Kana)
end
for _, sc in ipairs(lang:getScripts()) do
if sc:getCode() == "Hrkt" then
add_Hrkt()
else
add_sc_cat(sc)
if sc:getCode() == "Jpan" then
add_sc_cat(Hani)
add_Hrkt()
elseif sc:getCode() == "Kore" then
add_sc_cat(Hang)
add_sc_cat(Hani)
end
end
end
if lang:hasTranslit() then
insert(ret, {name = "Thể loại:Ngôn ngữ với chuyển tự tự động", sort = sortkey})
end
local function insert_location_language_cat(location)
local cat = "Ngôn ngữ tại " .. location
insert(ret, {name = "Thể loại:" .. cat, sort = sortkey})
local auto_cat_args = scrape_category_for_auto_cat_args(cat)
local location_parent = auto_cat_args and auto_cat_args.parent
if location_parent then
local split_parents = require(parse_utilities_module).split_on_comma(location_parent)
for _, parent in ipairs(split_parents) do
parent = parent:match("^(.-):.*$") or parent
insert_location_language_cat(parent)
end
end
end
local saw_location = false
for _, location in ipairs(locations) do
if location ~= "UNKNOWN" then
saw_location = true
insert_location_language_cat(location)
end
end
if extinct then
insert(ret, {name = "Thể loại:Ngôn ngữ tuyệt chủng", sort = sortkey})
end
if not saw_location and not (lang:hasType("reconstructed") or (fam and fam:getCode() == "art")) then
-- Constructed and reconstructed languages don't need a location specified and often won't have one,
-- so don't put them in this maintenance category.
insert(ret, {name = "Thể loại:Ngôn ngữ không xếp với tên quốc gia", sort = sortkey})
end
return ret
end
local function get_children()
local ret = {}
-- FIXME: We should work on the children mechanism so it isn't necessary to manually specify these.
for _, label in ipairs({"Phụ lục", "Bảo trì mục từ", "Mục từ", "Cụm từ", "Vần", "Ký hiệu", "Bản mẫu", "Từ nguyên", "Từ theo cách sử dụng"}) do
insert(ret, {name = label, is_label = true})
end
insert(ret, {name = "Từ dẫn xuất từ {{{langname}}}", is_label = true, lang = false})
insert(ret, {name = "{{{langcode}}}:Tất cả chủ đề", sort = "tất cả chủ đề"})
insert(ret, {name = "Biến thể {{{langname}}}"})
insert(ret, {name = "Yêu cầu liên quan đến {{{langname}}}"})
-- insert(ret, {name = "Vần:{{{langname}}}", description = "Liệt kê các mục từ {{{langname}}} theo vần."})
insert(ret, {name = "Thành viên {{{langcode}}}", description = "Wiktionary users categorized by fluency levels in {{{langdisp}}}."})
return ret
end
-- Handle language categories of the form e.g. [[:Category:French language]] and
-- [[:Category:British Sign Language]].
insert(raw_handlers, function(data)
local category = data.category
if not (category:match("^[Tt]iếng") or category:match("[Ll]ect$")) then
return nil
end
local lang = m_languages.getByCanonicalName(category)
if not lang then
local langname = category
if langname then
lang = m_languages.getByCanonicalName(langname)
end
if not lang then
return nil
end
end
local args = require("Module:parameters").process(data.args, {
[1] = {list = true},
["setwiki"] = true,
["setwikt"] = true,
["setsister"] = true,
["entryname"] = true,
["extinct"] = {type = "boolean"},
})
-- If called from inside, don't require any arguments, as they can't be known
-- in general and aren't needed just to generate the first parent (used for
-- breadcrumbs).
if #args[1] == 0 and not data.called_from_inside then
-- At least one location must be specified unless the language is constructed (e.g. Esperanto) or reconstructed (e.g. Proto-Indo-European).
local fam = lang:getFamily()
if not (lang:hasType("reconstructed") or (fam and fam:getCode() == "art")) then
error("At least one location (param 1=) must be specified for language '" .. lang:getCanonicalName() .. "' (code '" .. lang:getCode() .. "'). " ..
"Use the value UNKNOWN if the language's location is truly unknown.")
end
end
local description, topright, additional = "", "", ""
-- If called from inside the category tree system, it's called when generating
-- parents or children, and we don't need to generate the description or additional
-- text (which is very expensive in terms of memory because it calls [[Module:family tree]],
-- which calls [[Module:languages/data/all]]).
if not data.called_from_inside then
description, topright, additional = get_description_topright_additional(
lang, args[1], args.extinct, args.setwiki, args.setwikt, args.setsister, args.entryname
)
end
return {
canonical_name = lang:getCategoryName(),
description = description,
lang = lang:getCode(),
topright = topright,
additional = additional,
breadcrumb = lang:getCanonicalName(),
parents = get_parents(lang, args[1], args.extinct),
extra_children = get_children(lang),
umbrella = false,
can_be_empty = true,
}, true
end)
-- Handle categories such as [[:Category:Languages of Indonesia]].
insert(raw_handlers, function(data)
local location = data.category:match("^Ngôn ngữ tại (.*)$")
if location then
local args = require("Module:parameters").process(data.args, {
["flagfile"] = true,
["commonscat"] = true,
["wp"] = true,
["basename"] = true,
["parent"] = true,
["locationcat"] = true,
["locationlink"] = true,
})
local topright
local basename = args.basename or location:gsub(", .*", "")
local translated_basename = location_translation[basename] or basename
if args.flagfile ~= "-" then
local flagfile_arg = args.flagfile or ("Flag of %s.svg"):format(translated_basename)
local files = require(parse_utilities_module).split_on_comma(flagfile_arg)
local topright_parts = {}
for _, file in ipairs(files) do
local flagfile = "File:" .. file
local flagfile_page = new_title(flagfile)
if flagfile_page and flagfile_page.file.exists then
insert(topright_parts, ("[[%s|right|100px|border]]"):format(flagfile))
elseif args.flagfile then
error(("Explicit flagfile '%s' doesn't exist"):format(flagfile))
end
end
topright = concat(topright_parts)
end
if args.wp then
local wp = require("Module:yesno")(args.wp, "+")
if wp == "+" or wp == true then
wp = data.category
end
if wp then
local wp_topright = ("{{wikipedia|%s}}"):format(wp)
if topright then
topright = topright .. wp_topright
else
topright = wp_topright
end
end
end
if args.commonscat then
local commonscat = require("Module:yesno")(args.commonscat, "+")
if commonscat == "+" or commonscat == true then
commonscat = data.category
end
if commonscat then
local commons_topright = ("{{commonscat|%s}}"):format(commonscat)
if topright then
topright = topright .. commons_topright
else
topright = commons_topright
end
end
end
local bare_location = location:match("^(.*)$") or location
local location_link = args.locationlink or link_location(location)
local bare_basename = basename:match("^(.*)$") or basename
local parents = {}
if args.parent then
local explicit_parents = require(parse_utilities_module).split_on_comma(args.parent)
for i, parent in ipairs(explicit_parents) do
local actual_parent, sort_key = parent:match("^(.-):(.*)$")
if actual_parent then
parent = actual_parent
sort_key = sort_key:gsub("%+", bare_location)
else
sort_key = " " .. bare_location
end
insert(parents, {name = "Ngôn ngữ tại " .. parent, sort = sort_key})
end
else
insert(parents, {name = "Ngôn ngữ theo quốc gia", sort = {sort_base = bare_location, lang = "vi"}})
end
if args.locationcat then
local explicit_location_cats = require(parse_utilities_module).split_on_comma(args.locationcat)
for i, locationcat in ipairs(explicit_location_cats) do
insert(parents, {name = "Thể loại:" .. locationcat, sort = " Languages"})
end
else
local location_cat = ("Thể loại:%s"):format(bare_location)
local location_page = new_title(location_cat)
if location_page and location_page.exists then
insert(parents, {name = location_cat, sort = "Languages"})
end
end
local description = ("Thể loại dành cho ngôn ngữ tại %s."):format(location_link)
return {
topright = topright,
description = description,
parents = parents,
breadcrumb = bare_basename,
additional = "{{{umbrella_msg}}}",
}, true
end
end)
-- Handle categories such as [[:Category:English-based creole or pidgin languages]].
insert(raw_handlers, function(data)
local langname = data.category:match("(.*)%-based creole or pidgin languages$")
if langname then
local lang = m_languages.getByCanonicalName(langname)
if lang then
return {
lang = lang:getCode(),
description = "Languages which developed as a [[creole]] or [[pidgin]] from " .. lang:makeCategoryLink() .. ".",
parents = {{name = "Creole or pidgin languages", sort = {sort_base = "*" .. langname, lang = "en"}}},
breadcrumb = lang:getCanonicalName() .. "-based",
}
end
end
end)
-- Handle categories such as [[:Category:English-based constructed languages]].
insert(raw_handlers, function(data)
local langname = data.category:match("(.*)%-based constructed languages$")
if langname then
local lang = m_languages.getByCanonicalName(langname)
if lang then
return {
lang = lang:getCode(),
description = "Constructed languages which are based on " .. lang:makeCategoryLink() .. ".",
parents = {{name = "Constructed languages", sort = {sort_base = "*" .. langname, lang = "en"}}},
breadcrumb = lang:getCanonicalName() .. "-based",
}
end
end
end)
return {
RAW_CATEGORIES = raw_categories,
RAW_HANDLERS = raw_handlers
}
o8yua4tt1gjwh8ineu6sbc8rebjoj34
Mô đun:families
828
279894
2573975
2448711
2026-10-28T08:27:47Z
Hiyuune
50834
2573975
Scribunto
text/plain
local export = {}
local families_by_name_module = "Module:families/canonical names"
local families_data_module = "Module:families/data"
local json_module = "Module:JSON"
local language_like_module = "Module:language-like"
local languages_module = "Module:languages"
local load_module = "Module:load"
local table_module = "Module:table"
local get_by_code -- Defined below.
local gmatch = string.gmatch
local insert = table.insert
local ipairs = ipairs
local make_object -- Defined below.
local pairs = pairs
local require = require
local setmetatable = setmetatable
local type = type
--[==[
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 category_name_has_suffix(...)
category_name_has_suffix = require(language_like_module).categoryNameHasSuffix
return category_name_has_suffix(...)
end
local function category_name_to_code(...)
category_name_to_code = require(language_like_module).categoryNameToCode
return category_name_to_code(...)
end
local function deep_copy(...)
deep_copy = require(table_module).deepCopy
return deep_copy(...)
end
local function get_lang(...)
get_lang = require(languages_module).getByCode
return get_lang(...)
end
local function keys_to_list(...)
keys_to_list = require(table_module).keysToList
return keys_to_list(...)
end
local function load_data(...)
load_data = require(load_module).load_data
return load_data(...)
end
local function make_lang_object(...)
make_lang_object = require(languages_module).makeObject
return make_lang_object(...)
end
local function to_json(...)
to_json = require(json_module).toJSON
return to_json(...)
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 families_by_name
local function get_families_by_name()
families_by_name, get_families_by_name = load_data(families_by_name_module), nil
return families_by_name
end
local families_data
local function get_families_data()
families_data, get_families_data = load_data(families_data_module), nil
return families_data
end
local families_suffixes
local function get_families_suffixes()
families_suffixes, get_families_suffixes = {
"languages",
"lects"
}, nil
return families_suffixes
end
local Family = {}
Family.__index = Family
--[==[
Return the family code of the family, e.g. {"ine"} for the Indo-European languages.
]==]
function Family:getCode()
return self._code
end
--[==[
Return the canonical name of the family. This is the name used to represent that language family on Wiktionary,
and is guaranteed to be unique to that family alone. Example: {"Indo-European"} for the Indo-European languages.
]==]
function Family: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 family. For families, this is usually the same as the value returned by
{getCategoryName("nocap")}, i.e. it reads <code>"<var>name</var> languages"</code> (e.g.
{"Indo-Iranian languages"}). For full and etymology-only languages, this is the same as the canonical name, and
for scripts, it reads <code>"<var>name</var> script"</code> (e.g. {"Arabic script"}). The displayed text used in
{makeCategoryLink()} is always the same as the display form.
]==]
function Family:getDisplayForm()
local name = self._data[1]
if category_name_has_suffix(name, families_suffixes or get_families_suffixes()) then
name = name
end
return name
end
function Family:getAliases()
Family.getAliases = require(language_like_module).getAliases
return self:getAliases()
end
function Family:getVarieties(flatten)
Family.getVarieties = require(language_like_module).getVarieties
return self:getVarieties(flatten)
end
function Family:getOtherNames()
Family.getOtherNames = require(language_like_module).getOtherNames
return self:getOtherNames()
end
function Family:getAllNames()
Family.getAllNames = require(language_like_module).getAllNames
return self:getAllNames()
end
--[==[
Return a list of the pseudo-families (if any) that a family 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 Family:getPseudoFamilies()
Family.getPseudoFamilies = require(language_like_module).getPseudoFamilies
return self:getPseudoFamilies()
end
--[==[Returns a table of types as a lookup table (with the types as keys).
The possible types are
* {family}: This object is a family.
* {full}: This object is a "full" family. This includes all families but a couple of etymology-only
families for Old and Middle Iranian languages.
* {etymology-only}: This object is an etymology-only family, similar to etymology-only languages. There
are currently only two such families, for Old Iranian languages and Middle Iranian
languages (which do not represent proper clades and have no proto-languages, hence
cannot be full families).
]==]
function Family:getTypes()
local types = self._types
if types == nil then
types = {family = true}
if self:getFullCode() == self:getCode() then
types.full = true
else
types["etymology-only"] = true
end
local rawtypes = self._data.type
if rawtypes then
for t in gmatch(rawtypes, "[^,]+") do
types[t] = true
end
end
self._types = types
end
return types
end
--[==[Given a list of types as strings, returns true if the family has all of them.]==]
function Family:hasType(...)
Family.hasType = require(language_like_module).hasType
return self:hasType(...)
end
--[==[Returns a {Family} object for the superfamily that the family belongs to.]==]
function Family:getFamily()
if self._familyObject == nil then
local familyCode = self:getFamilyCode()
if familyCode then
self._familyObject = get_by_code(familyCode)
else
self._familyObject = false
end
end
return self._familyObject or nil
end
--[==[Returns the code of the family's superfamily.]==]
function Family:getFamilyCode()
if not self._familyCode then
self._familyCode = self._data[3]
end
return self._familyCode
end
--[==[Returns the canonical name of the family's superfamily.]==]
function Family:getFamilyName()
if self._familyName == nil then
local family = self:getFamily()
if family then
self._familyName = family:getCanonicalName()
else
self._familyName = false
end
end
return self._familyName or nil
end
--[==[Check whether the family belongs to {superfamily} (which can be a family code or object), and returns a boolean. If more than one is given, returns {true} if the family belongs to any of them. A family is '''not''' considered to belong to itself.]==]
function Family:inFamily(...)
for _, superfamily in ipairs{...} do
if type(superfamily) == "table" then
superfamily = superfamily:getCode()
end
local family, code = self:getFamily()
while family do
code = family:getCode()
if code == superfamily then
return true
end
family = family:getFamily()
-- If family is parent to itself, return false.
if family and family:getCode() == code then
return false
end
end
return false
end
end
function Family:getParent()
if self._parentObject == nil then
local parentCode = self:getParentCode()
if parentCode then
self._parentObject = get_lang(parentCode, nil, true, true)
else
self._parentObject = false
end
end
return self._parentObject or nil
end
function Family:getParentCode()
if not self._parentCode then
self._parentCode = self._data.parent
end
return self._parentCode
end
function Family:getParentName()
if self._parentName == nil then
local parent = self:getParent()
if parent then
self._parentName = parent:getCanonicalName()
else
self._parentName = false
end
end
return self._parentName or nil
end
function Family:getParentChain()
if not self._parentChain then
self._parentChain = {}
local parent = self:getParent()
while parent do
insert(self._parentChain, parent)
parent = parent:getParent()
end
end
return self._parentChain
end
function Family:hasParent(...)
--checkObject("family", nil, ...)
for _, other_family in ipairs{...} do
for _, parent in ipairs(self:getParentChain()) do
if type(other_family) == "string" then
if other_family == parent:getCode() then return true end
else
if other_family:getCode() == parent:getCode() then return true end
end
end
end
return false
end
--[==[
If the family is etymology-only, this iterates through its parents until a full family is found, and the
corresponding object is returned. If the family is a full family, then it simply returns itself.
]==]
function Family:getFull()
if not self._fullObject then
local fullCode = self:getFullCode()
if fullCode ~= self:getCode() then
self._fullObject = get_lang(fullCode, nil, nil, true)
else
self._fullObject = self
end
end
return self._fullObject
end
--[==[
If the family is etymology-only, this iterates through its parents until a full family is found, and the
corresponding code is returned. If the family is a full family, then it simply returns the family code.
]==]
function Family:getFullCode()
return self._fullCode or self:getCode()
end
--[==[
If the family is etymology-only, this iterates through its parents until a full family is found, and the
corresponding canonical name is returned. If the family is a full family, then it simply returns the canonical name
of the family.
]==]
function Family:getFullName()
if self._fullName == nil then
local full = self:getFull()
if full then
self._fullName = full:getCanonicalName()
else
self._fullName = false
end
end
return self._fullName or nil
end
--[==[
Return a {Language} object (see [[Module:languages]]) for the proto-language of this family, if one exists.
Otherwise, return {nil}.
]==]
function Family:getProtoLanguage()
if self._protoLanguageObject == nil then
self._protoLanguageObject = get_lang(self._data.protoLanguage or self:getCode() .. "-pro", nil, true) or false
end
return self._protoLanguageObject or nil
end
function Family:getProtoLanguageCode()
if self._protoLanguageCode == nil then
local protoLanguage = self:getProtoLanguage()
self._protoLanguageCode = protoLanguage and protoLanguage:getCode() or false
end
return self._protoLanguageCode or nil
end
function Family:getProtoLanguageName()
if not self._protoLanguageName then
self._protoLanguageName = self:getProtoLanguage():getCanonicalName()
end
return self._protoLanguageName
end
function Family:hasAncestor(...)
-- Go up the family tree until a protolanguage is found.
local family = self
local protolang = family:getProtoLanguage()
while not protolang do
family = family:getFamily()
protolang = family:getProtoLanguage()
-- Return false if the family is its own family, to avoid an infinite loop.
if family:getFamilyCode() == family:getCode() then
return false
end
end
-- If the protolanguage is not in the family, it must therefore be ancestral to it. Check if it is a match.
for _, otherlang in ipairs{...} do
if (
type(otherlang) == "string" and protolang:getCode() == otherlang or
type(otherlang) == "table" and protolang:getCode() == otherlang:getCode()
) and not protolang:inFamily(self) then
return true
end
end
-- If not, check the protolanguage's ancestry.
return protolang:hasAncestor(...)
end
local function fetch_descendants(self, format)
local languages = require("Module:languages/code to canonical name")
local etymology_languages = require("Module:etymology languages/code to canonical name")
local families = require("Module:families/code to canonical name")
local descendants = {}
-- Iterate over all three datasets.
for _, data in ipairs{languages, etymology_languages, families} do
for code in pairs(data) do
local lang = get_lang(code, nil, true, true)
if lang:inFamily(self) then
if format == "object" then
insert(descendants, lang)
elseif format == "code" then
insert(descendants, code)
elseif format == "name" then
insert(descendants, lang:getCanonicalName())
end
end
end
end
return descendants
end
function Family:getDescendants()
if not self._descendantObjects then
self._descendantObjects = fetch_descendants(self, "object")
end
return self._descendantObjects
end
function Family:getDescendantCodes()
if not self._descendantCodes then
self._descendantCodes = fetch_descendants(self, "code")
end
return self._descendantCodes
end
function Family:getDescendantNames()
if not self._descendantNames then
self._descendantNames = fetch_descendants(self, "name")
end
return self._descendantNames
end
function Family:hasDescendant(...)
for _, lang in ipairs{...} do
if type(lang) == "string" then
lang = get_lang(lang, nil, true)
end
if lang:inFamily(self) then
return true
end
end
return false
end
--[==[
Return the name of the main category of that family. Example: {"Germanic languages"} for the Germanic languages,
whose category is at [[:Category:Germanic languages]].
Unless optional argument `nocap` is given, the family name at the beginning of the returned value will be
capitalized. This capitalization is correct for category names, but not if the family name is lowercase and
the returned value of this function is used in the middle of a sentence. (For example, the pseudo-family with
the code {qfa-mix} has the name {"mixed"}, which should remain lowercase when used as part of the category name
[[:Category:Terms derived from mixed languages]] but should be capitalized in [[:Category:Mixed languages]].)
If you are considering using {getCategoryName("nocap")}, use {getDisplayForm()} instead.
]==]
function Family:getCategoryName(nocap)
local name = self._data.categoryName or self:getDisplayForm()
if not nocap then
name = mw.getContentLanguage():ucfirst(name)
end
return name
end
function Family:makeCategoryLink()
return "[[:Category:" .. self:getCategoryName() .. "|" .. self:getDisplayForm() .. "]]"
end
--[==[Returns the Wikidata item id for the family or <code>nil</code>. This corresponds to the the second field in the data modules.]==]
function Family:getWikidataItem()
Family.getWikidataItem = require(language_like_module).getWikidataItem
return self:getWikidataItem()
end
--[==[
Returns the name of the Wikipedia article for the family. `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}, {: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 family's category, but this won't normally happen for any other project.
]==]
function Family:getWikipediaArticle(noCategoryFallback, project)
Family.getWikipediaArticle = require(language_like_module).getWikipediaArticle
return self:getWikipediaArticle(noCategoryFallback, project)
end
function Family:makeWikipediaLink()
return "[[w:" .. self:getWikipediaArticle() .. "|" .. self:getCanonicalName() .. "]]"
end
--[==[Returns the name of the Wikimedia Commons category page for the family.]==]
function Family:getCommonsCategory()
Family.getCommonsCategory = require(language_like_module).getCommonsCategory
return self:getCommonsCategory()
end
function Family:toJSON(opts)
local ret = {
canonicalName = self:getCanonicalName(),
categoryName = self:getCategoryName("nocap"),
code = self:getCode(),
parent = self:getParentCode(),
full = self:getFullCode(),
family = self:getFamilyCode(),
protoLanguage = self:getProtoLanguageCode(),
aliases = self:getAliases(),
varieties = self:getVarieties(),
otherNames = self:getOtherNames(),
type = keys_to_list(self:getTypes()),
wikidataItem = self:getWikidataItem(),
wikipediaArticle = self:getWikipediaArticle(true),
}
-- 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 Family:getData()
return self._data
end
function export.makeObject(code, data)
local data_type = type(data)
if data_type ~= "table" then
error(("bad argument #2 to 'makeObject' (table expected, got %s)"):format(data_type))
end
return setmetatable({_data = data, _code = code, _fullCode = code}, Family)
end
make_object = export.makeObject
--[==[
Finds the family whose code matches the one provided. If it exists, it returns a {Family} object representing the
family. Otherwise, it returns {nil}.]==]
function export.getByCode(code)
local data = (families_data or get_families_data())[code]
if data == nil then
return nil
elseif data.parent == nil then
return make_object(code, data)
end
return make_lang_object(code, data)
end
get_by_code = export.getByCode
--[==[
Look for the family whose canonical name (the name used to represent that family on Wiktionary) matches the one
provided. If it exists, it returns a {Family} object representing the family. Otherwise, it returns {nil}. The
canonical name of families should always be unique (it is an error for two families on Wiktionary to share the same
canonical name), so this is guaranteed to give at most one result.]==]
function export.getByCanonicalName(name)
if name == nil then
return nil
end
local code = (families_by_name or get_families_by_name())[name]
if code == nil then
return nil
end
return get_by_code(code)
end
--[==[
Look for the family whose category name (the name used in categories for that family) matches the one provided.
If it exists, it returns a {Family} object representing the family. Otherwise, it returns {nil}. In almost all cases,
the category name for a family is its canonical name plus the word "languages", e.g. "Indo-European" has the category
name "Indo-European languages". Where a canonical name ends with "languages" or "lects", the category name is identical
to the canonical name.]==]
function export.getByCategoryName(name)
if name == nil then
return nil
end
local code = category_name_to_code(
name,
"",
families_by_name or get_families_by_name(),
families_suffixes or get_families_suffixes()
)
if code == nil then
return nil
end
return get_by_code(code)
end
return export
s87w97w2g2x0qtzw3fd1oi4jrup90mn
Mô đun:ca-headword
828
281212
2573984
2568625
2026-10-28T09:10:30Z
Hiyuune
50834
2573984
Scribunto
text/plain
local export = {}
local pos_functions = {}
local force_cat = false -- for testing; if true, categories appear in non-mainspace pages
local require_when_needed = require("Module:utilities/require when needed")
local m_table = require("Module:table")
local com = require("Module:ca-common")
local ca_IPA_module = "Module:ca-IPA"
local ca_verb_module = "Module:ca-verb"
local decorations_module = "Module:decorations"
local en_utilities_module = "Module:en-utilities"
local headword_utilities_module = "Module:headword utilities"
local inflection_utilities_module = "Module:inflection utilities"
local parse_utilities_module = "Module:parse utilities"
local romut_module = "Module:romance utilities"
local m_en_utilities = require_when_needed(en_utilities_module)
local m_headword_utilities = require_when_needed(headword_utilities_module)
local m_string_utilities = require_when_needed("Module:string utilities")
local glossary_link = require_when_needed(headword_utilities_module, "glossary_link")
local lang = require("Module:languages").getByCode("ca")
local langname = lang:getCanonicalName()
local list_to_text = mw.text.listToText
local insert = table.insert
local concat = table.concat
local rfind = m_string_utilities.find
local rmatch = m_string_utilities.match
local rsplit = m_string_utilities.split
local usub = m_string_utilities.sub
local rsub = com.rsub
local function track(page)
require("Module:debug/track")("ca-headword/" .. page)
return true
end
local list_param = {list = true, disallow_holes = true}
local boolean_param = {type = "boolean"}
-----------------------------------------------------------------------------------------
-- Main entry point --
-----------------------------------------------------------------------------------------
-- The main entry point.
-- This is the only function that can be invoked from a template.
function export.show(frame)
local poscat = frame.args[1] or error("Từ loại không được chỉ định. Vui lòng thêm tham số đầu tiên để gọi mô đun.")
local params = {
["head"] = list_param,
["id"] = true,
["splithyph"] = boolean_param,
["nolinkhead"] = boolean_param,
["json"] = boolean_param,
["pagename"] = true, -- for testing
}
if pos_functions[poscat] then
for key, val in pairs(pos_functions[poscat].params) do
params[key] = val
end
end
local args = require("Module:parameters").process(frame:getParent().args, params)
local pagename = args.pagename or mw.loadData("Module:headword/data").pagename
local user_specified_heads = args.head
local heads = user_specified_heads
if args.nolinkhead then
if #heads == 0 then
heads = {pagename}
end
else
local romut = require(romut_module)
local auto_linked_head = romut.add_links_to_multiword_term(pagename, args.splithyph)
if #heads == 0 then
heads = {auto_linked_head}
else
for i, head in ipairs(heads) do
if head:find("^~") then
head = romut.apply_link_modifiers(auto_linked_head, usub(head, 2))
heads[i] = head
end
if head == auto_linked_head then
track("redundant-head")
end
end
end
end
local data = {
lang = lang,
pos_category = pos_functions[poscat] and pos_functions[poscat].pos_category or poscat,
categories = {},
heads = heads,
user_specified_heads = user_specified_heads,
no_redundant_head_cat = #user_specified_heads == 0,
genders = {},
inflections = {},
pagename = pagename,
id = args.id,
force_cat_output = force_cat,
checkredlinks = pos_functions[poscat] and pos_functions[poscat].redlink_pos or true,
}
if pagename:find("^%-") and poscat ~= "Biến thể hình thái hậu tố" then
data.is_suffix = true
data.pos_category = "Hậu tố"
data.checkredlinks = true
local singular_poscat = require(en_utilities_module).singularize(poscat)
insert(data.categories, "Dạng hậu tố " .. singular_poscat .. " " .. langname)
insert(data.inflections, {label = "dạng hậu tố " .. singular_poscat})
end
if pos_functions[poscat] then
pos_functions[poscat].func(args, data)
end
if args.json then
return require("Module:JSON").toJSON(data)
end
local post_note = data.post_note and "; " .. data.post_note or ""
return require("Module:headword").full_headword(data) .. post_note
end
-----------------------------------------------------------------------------------------
-- Utility functions --
-----------------------------------------------------------------------------------------
local function replace_hash_with_lemma(term, lemma)
-- If there is a % sign in the lemma, we have to replace it with %% so it doesn't get interpreted as a capture replace
-- expression.
lemma = lemma:gsub("%%", "%%%%")
-- Assign to a variable to discard second return value.
term = term:gsub("#", lemma)
return term
end
-- Parse and insert an inflection not requiring additional processing into `data.inflections`. The raw arguments come
-- from `args[field]`, which is parsed for inline modifiers. `label` is the label that the inflections are given;
-- `accel` is the accelerator form, or nil.
local function parse_and_insert_inflection(data, args, field, label, accel)
m_headword_utilities.parse_and_insert_inflection {
headdata = data,
forms = args[field],
paramname = field,
splitchar = ",",
label = label,
accel = accel and {form = accel} or nil,
}
end
-- Insert default plurals generated when a given plural had the value of + and default plurals were fetched as a result.
-- `plobj` is the parsed object whose `term` field is "+". `defpls` is the list of default plurals. `dest` is the list
-- into which the plurals are inserted (which inherit their decorations from `plobj`).
local function insert_defpls(defpls, plobj, dest)
if not defpls then
-- Happens e.g. with [[S.A.]] where the default plural algorithm returns nothing.
return
end
if #defpls == 1 then
plobj.term = defpls[1]
insert(dest, plobj)
else
for _, defpl in ipairs(defpls) do
local newplobj = m_table.shallowCopy(plobj)
newplobj.term = defpl
insert(dest, newplobj)
end
end
end
-----------------------------------------------------------------------------------------
-- Adjectives --
-----------------------------------------------------------------------------------------
local function do_adjective(args, data, is_superlative)
local feminines = {}
local masculine_plurals = {}
local feminine_plurals = {}
-- Use "participle" not "past participle" for categories such as 'invariable paticiples'
local category_plpos = data.checkredlinks
if category_plpos == true then
category_plpos = data.pos_category
end
local category_pos = m_en_utilities.singularize(category_plpos)
if args.sp then
local romut = require(romut_module)
if not romut.allowed_special_indicators[args.sp] then
local indicators = {}
for indic, _ in pairs(romut.allowed_special_indicators) do
insert(indicators, "'" .. indic .. "'")
end
table.sort(indicators)
error("Special inflection indicator beginning can only be " ..
list_to_text(indicators) .. ": " .. args.sp)
end
end
local lemma = data.pagename
local function fetch_inflections(field)
local retval = m_headword_utilities.parse_term_list_with_modifiers {
paramname = field,
forms = args[field],
splitchar = ",",
}
if not retval[1] then
return {{term = "+"}}
end
return retval
end
local function insert_inflection(terms, label, accel)
m_headword_utilities.insert_inflection {
headdata = data,
terms = terms,
label = label,
accel = accel and {form = accel} or nil,
}
end
if args.f[1] == "ind" or args.f[1] == "inv" then
-- invariable adjective
insert(data.inflections, {label = glossary_link("không biến cách")})
insert(data.categories, category_plpos .. " không biến cách " .. langname)
if args.sp or args.f[2] or args.pl[1] or args.mpl[1] or args.fpl[1] then
error("Can't specify inflections with an invariable " .. category_pos)
end
elseif args.fonly then
-- feminine-only
if args.f[1] then
error("Can't specify explicit feminines with feminine-only " .. category_pos)
end
if args.pl[1] then
error("Can't specify explicit plurals with feminine-only " .. category_pos .. ", use fpl=")
end
if args.mpl[1] then
error("Can't specify explicit masculine plurals with feminine-only " .. category_pos)
end
local argsfpl = fetch_inflections("fpl")
for _, fpl in ipairs(argsfpl) do
if fpl.term == "+" then
-- Generate default feminine plural.
local defpls = com.make_plural(lemma, "f", args.sp)
if not defpls then
error("Unable to generate default plural of '" .. lemma .. "'")
end
insert_defpls(defpls, fpl, feminine_plurals)
else
fpl.term = replace_hash_with_lemma(fpl.term, lemma)
insert(feminine_plurals, fpl)
end
end
insert(data.inflections, {label = "chỉ có giống cái"})
insert_inflection(feminine_plurals, "giống cái số nhiều", "f|p")
else
-- Gather feminines.
for _, f in ipairs(fetch_inflections("f")) do
if f.term == "mf" then
f.term = lemma
elseif f.term == "+" then
-- Generate default feminine.
f.term = com.make_feminine(lemma, args.sp)
else
f.term = replace_hash_with_lemma(f.term, lemma)
end
insert(feminines, f)
end
local fem_like_lemma = #feminines == 1 and feminines[1].term == lemma and
not m_headword_utilities.termobj_has_decorations(feminines[1])
if fem_like_lemma then
insert(data.categories, category_plpos .. " giống đôi " .. langname)
end
local mpl_field = "mpl"
local fpl_field = "fpl"
if args.pl[1] then
if args.mpl[1] or args.fpl[1] then
error("Can't specify both pl= and mpl=/fpl=")
end
mpl_field = "pl"
fpl_field = "pl"
end
local argsmpl = fetch_inflections(mpl_field)
local argsfpl = fetch_inflections(fpl_field)
for _, mpl in ipairs(argsmpl) do
if mpl.term == "+" then
-- Generate default masculine plural.
local defpls
-- First, some special hacks based on the feminine singular.
if not fem_like_lemma and not args.sp and not lemma:find(" ") then
for _, f in ipairs(feminines) do
if f.term:find("ssa$") then
-- If the feminine ends in -ssa, assume that the -ss- is also in the
-- masculine plural form
defpls = {rsub(f.term, "a$", "os")}
break
elseif f.term == lemma .. "na" then
defpls = {lemma .. "ns"}
break
elseif lemma:find("ig$") and f.term:find("ja$") then
-- Adjectives in -ig have two masculine plural forms, one derived from
-- the m.sg. and the other derived from the f.sg.
defpls = {lemma .. "s", rsub(f.term, "ja$", "jos")}
break
end
end
end
defpls = defpls or com.make_plural(lemma, "m", args.sp)
if not defpls then
error("Unable to generate default plural of '" .. lemma .. "'")
end
insert_defpls(defpls, mpl, masculine_plurals)
else
mpl.term = replace_hash_with_lemma(mpl.term, lemma)
insert(masculine_plurals, mpl)
end
end
for _, fpl in ipairs(argsfpl) do
if fpl.term == "+" then
-- First, some special hacks based on the feminine singular.
if fem_like_lemma and not args.sp and not lemma:find(" ") and lemma:find("[çx]$") then
-- Adjectives ending in -ç or -x behave as mf-type in the singular, but
-- regular type in the plural.
local defpls = com.make_plural(lemma .. "a", "f")
if not defpls then
error("Unable to generate default plural of '" .. lemma .. "a'")
end
insert_defpls(defpls, fpl, feminine_plurals)
else
for _, f in ipairs(feminines) do
-- Generate default feminine plural; f is a table.
local defpls = com.make_plural(f.term, "f", args.sp)
if not defpls then
error("Unable to generate default plural of '" .. f.term .. "'")
end
for _, defpl in ipairs(defpls) do
local fplobj = m_table.shallowCopy(fpl)
fplobj.term = defpl
m_headword_utilities.combine_termobj_decorations(fplobj, f)
insert(feminine_plurals, fplobj)
end
end
end
else
fpl.term = replace_hash_with_lemma(fpl.term, lemma)
insert(feminine_plurals, fpl)
end
end
local fem_pl_like_masc_pl = masculine_plurals[1] and feminine_plurals[1] and
m_table.deepEquals(masculine_plurals, feminine_plurals)
local masc_pl_like_lemma = #masculine_plurals == 1 and masculine_plurals[1].term == lemma and
not m_headword_utilities.termobj_has_decorations(masculine_plurals[1])
if fem_like_lemma and fem_pl_like_masc_pl and masc_pl_like_lemma then
-- actually invariable
insert(data.inflections, {label = glossary_link("không biến cách")})
insert(data.categories, category_plpos .. " không biến cách " .. langname)
else
-- Make sure there are feminines given and not same as lemma.
if not fem_like_lemma then
insert_inflection(feminines, "giống cái", "f|s")
elseif args.gneut then
data.genders = {"gneut"}
else
data.genders = {"mf"}
end
if fem_pl_like_masc_pl then
if args.gneut then
insert_inflection(masculine_plurals, "số nhiều", "p")
else
insert_inflection(masculine_plurals, "giống đực và giống cái số nhiều", "p")
end
else
insert_inflection(masculine_plurals, "giống đực số nhiều", "m|p")
insert_inflection(feminine_plurals, "giống cái số nhiều", "f|p")
end
end
end
parse_and_insert_inflection(data, args, "comp", "so sánh hơn")
parse_and_insert_inflection(data, args, "sup", "so sánh nhất")
parse_and_insert_inflection(data, args, "dim", "giảm nhẹ nghĩa")
parse_and_insert_inflection(data, args, "aug", "tăng kich")
if args.irreg and is_superlative then
insert(data.categories, category_plpos .. " so sánh nhất bất quy tắc " .. langname)
end
end
local function get_adjective_params(adjtype)
local params = {
["sp"] = true, -- special indicator: "first", "first-last", etc.
["f"] = list_param, --feminine form(s)
[1] = {alias_of = "f", list = false},
["pl"] = list_param, --plural override(s)
["mpl"] = list_param, --masculine plural override(s)
["fpl"] = list_param, --feminine plural override(s)
}
if adjtype == "base" then
params["comp"] = list_param --comparative(s)
params["sup"] = list_param --superlative(s)
params["dim"] = list_param --diminutive(s)
params["aug"] = list_param --augmentative(s)
params["fonly"] = boolean_param -- feminine only
params["hascomp"] = {} -- has comparative
end
if adjtype == "sup" then
params["irreg"] = boolean_param
end
return params
end
-- Display additional inflection information for an adjective
pos_functions["Tính từ"] = {
params = get_adjective_params("base"),
func = do_adjective,
}
pos_functions["Phân từ quá khứ"] = {
params = get_adjective_params("part"),
func = do_adjective,
redlink_pos = "Phân từ",
}
pos_functions["Từ hạn định"] = {
params = get_adjective_params("det"),
func = do_adjective,
}
pos_functions["Đại từ"] = {
params = get_adjective_params("pron"),
func = do_adjective,
}
-----------------------------------------------------------------------------------------
-- Nouns --
-----------------------------------------------------------------------------------------
local allowed_genders = m_table.listToSet(
{"m", "f", "mf", "mfbysense", "mfequiv", "gneut", "n", "m-p", "f-p", "mf-p", "mfbysense-p", "mfequiv-p", "gneut-p", "n-p", "?", "?-p"}
)
local function validate_genders(genders)
for _, g in ipairs(genders) do
if type(g) == "table" then
g = g.spec
end
if not allowed_genders[g] then
error("Không rõ giống: " .. g)
end
end
end
local function do_noun(args, data, is_proper)
local is_plurale_tantum = false
local has_singular = false
local category_plpos = data.checkredlinks
if category_plpos == true then
category_plpos = data.pos_category
end
local category_pos = m_en_utilities.singularize(category_plpos)
validate_genders(args[1])
data.genders = args[1]
local saw_m = false
local saw_f = false
local saw_gneut = false
local gender_for_irreg_ending, gender_for_default_plural
-- Check for specific genders and pluralia tantum.
for _, g in ipairs(args[1]) do
if type(g) == "table" then
g = g.spec
end
if g:find("-p$") then
is_plurale_tantum = true
else
has_singular = true
if g == "m" or g == "mf" or g == "mfbysense" then
saw_m = true
end
if g == "f" or g == "mf" or g == "mfbysense" then
saw_f = true
end
if g == "gneut" then
saw_gneut = true
end
end
end
if saw_m and saw_f then
gender_for_irreg_ending = "mf"
elseif saw_f then
gender_for_irreg_ending = "f"
else
gender_for_irreg_ending = "m"
end
gender_for_default_plural =
saw_gneut and "gneut" or gender_for_irreg_ending == "mf" and "m" or gender_for_irreg_ending
local lemma = data.pagename
-- Plural
local plurals = {}
local function insert_noun_inflection(terms, label, accel)
m_headword_utilities.insert_inflection {
headdata = data,
terms = terms,
label = label,
accel = accel and {form = accel} or nil,
}
end
if is_plurale_tantum and not has_singular then
if args[2][1] then
error("Can't specify plurals of plurale tantum " .. category_pos)
end
insert(data.inflections, {label = glossary_link("chỉ có số nhiều")})
else
plurals = m_headword_utilities.parse_term_list_with_modifiers {
paramname = {2, "pl"},
forms = args[2],
splitchar = ",",
}
-- Check for special plural signals
local mode = nil
local pl1 = plurals[1]
if pl1 and #pl1.term == 1 then
mode = pl1.term
if mode == "?" or mode == "!" or mode == "-" or mode == "~" then
pl1.term = nil
if m_headword_utilities.termobj_has_decorations(pl1) then
error(("Can't specify inline modifiers with plural code '%s'"):format(mode))
end
table.remove(plurals, 1) -- Remove the mode parameter
elseif mode ~= "+" and mode ~= "#" then
error(("Unexpected plural code '%s'"):format(mode))
end
end
if is_plurale_tantum then
-- both singular and plural
insert(data.inflections, {label = "đôi khi " .. glossary_link("chỉ có số nhiều") .. ", trong biến thể"})
end
if mode == "?" then
-- Plural is unknown
insert(data.categories, category_plpos .. " số nhiều chưa được chứng thực hoặc không rõ " .. langname)
elseif mode == "!" then
-- Plural is not attested
insert(data.inflections, {label = "số nhiều chưa được chứng thực"})
insert(data.categories, category_plpos .. " số nhiều chưa được chứng thực " .. langname)
if plurals[1] then
error("Can't specify any plurals along with unattested plural code '!'")
end
elseif mode == "-" then
-- Uncountable noun; may occasionally have a plural
insert(data.categories, category_plpos .. " không đếm được " .. langname)
-- If plural forms were given explicitly, then show "usually"
if plurals[1] then
insert(data.inflections, {label = "thường " .. glossary_link("không đếm được")})
insert(data.categories, category_plpos .. " đếm được " .. langname)
else
insert(data.inflections, {label = glossary_link("không đếm được")})
end
else
-- Countable or mixed countable/uncountable
if not plurals[1] and not is_proper then
plurals[1] = {term = "+"}
end
if mode == "~" then
-- Mixed countable/uncountable noun, always has a plural
insert(data.inflections, {label = glossary_link("đếm được") .. " và " .. glossary_link("không đếm được")})
insert(data.categories, category_plpos .. " không đếm được " .. langname)
insert(data.categories, category_plpos .. " đếm được " .. langname)
elseif plurals[1] then
-- Countable nouns
insert(data.categories, category_plpos .. " đếm được " .. langname)
else
-- Uncountable nouns
insert(data.categories, category_plpos .. " không đếm được " .. langname)
end
end
-- Gather plurals, handling requests for default plurals.
local has_default_or_hash = false
for _, pl in ipairs(plurals) do
if pl.term:find("^%+") or pl.term:find("#") then
has_default_or_hash = true
break
end
end
if has_default_or_hash then
local newpls = {}
for _, pl in ipairs(plurals) do
if pl.term == "+" then
local default_pls = com.make_plural(lemma, gender_for_default_plural)
insert_defpls(default_pls, pl, newpls)
elseif pl.term:find("^%+") then
pl.term = require(romut_module).get_special_indicator(pl.term)
local default_pls = com.make_plural(lemma, gender_for_default_plural, pl.term)
insert_defpls(default_pls, pl, newpls)
else
pl.term = replace_hash_with_lemma(pl.term, lemma)
insert(newpls, pl)
end
end
plurals = newpls
end
local pl1 = plurals[1]
if pl1 and not plurals[2] and pl1.term == lemma then
insert(data.inflections, {label = glossary_link("không biến cách"),
q = pl1.q, qq = pl1.qq, l = pl1.l, ll = pl1.ll, refs = pl1.refs
})
insert(data.categories, category_plpos .. " không biến cách " .. langname)
else
insert_noun_inflection(plurals, "số nhiều", "p")
end
if plurals[2] then
insert(data.categories, category_plpos .. " với nhiều dạng số nhiều" .. langname)
end
end
-- Gather masculines/feminines. For each one, generate the corresponding plural. `field` is the name of the field
-- containing the masculine or feminine forms (normally "m" or "f"); `inflect` is a function of one or two arguments
-- to generate the default masculine or feminine from the lemma (the arguments are the lemma and optionally a
-- "special" flag to indicate how to handle multiword lemmas, and the function is normally make_feminine or
-- make_masculine from [[Module:ca-common]]); and `default_plurals` is a list into which the corresponding default
-- plurals of the gathered or generated masculine or feminine forms are stored.
local function handle_mf(field, inflect, default_plurals)
local function call_inflect(special)
if inflect then
-- Generate default feminine.
return inflect(lemma, special)
else
-- FIXME
error("Can't generate default masculine currently")
end
end
local mfs = m_headword_utilities.parse_term_list_with_modifiers {
paramname = field,
forms = args[field],
splitchar = ",",
frob = function(term)
if term == "+" then
-- Generate default masculine/feminine.
term = call_inflect()
else
term = replace_hash_with_lemma(term, lemma)
end
local special = require(romut_module).get_special_indicator(term)
if special then
term = call_inflect(special)
end
return term
end
}
for _, mf in ipairs(mfs) do
local mfpls = com.make_plural(mf.term, gender, special)
if mfpls then
for _, mfpl in ipairs(mfpls) do
local plobj = m_table.shallowCopy(mf)
plobj.term = mfpl
-- Add an accelerator for each masculine/feminine plural whose lemma
-- is the corresponding singular, so that the accelerated entry
-- that is generated has a definition that looks like
-- # {{plural of|ca|MFSING}}
plobj.accel = {form = "p", lemma = mf.term}
table.insert(default_plurals, plobj)
end
end
end
return mfs
end
local feminine_plurals = {}
local feminines = handle_mf("f", com.make_feminine, feminine_plurals)
local masculine_plurals = {}
local masculines = handle_mf("m", com.make_masculine, masculine_plurals)
local function handle_mf_plural(mfplfield, default_plurals, singulars)
local mfpl = m_headword_utilities.parse_term_list_with_modifiers {
paramname = mfplfield,
forms = args[mfplfield],
splitchar = ",",
}
local new_mfpls = {}
local saw_plus
for i, mfpl in ipairs(mfpl) do
local accel
if #mfpl == #singulars then
-- If same number of overriding masculine/feminine plurals as singulars, assume each plural goes with
-- the corresponding singular and use each corresponding singular as the lemma in the accelerator. The
-- generated entry will have
-- # {{plural of|ca|SINGULAR}}
-- as the definition.
accel = {form = "p", lemma = singulars[i].term}
else
accel = nil
end
if mfpl.term == "+" then
-- We should never see + twice. If we do, it will lead to problems since we overwrite the values of
-- default_plurals the first time around.
if saw_plus then
error(("Saw + twice when handling %s="):format(mfplfield))
end
saw_plus = true
if not default_plurals[1] then
-- FIXME: Can this happen? Not in corresponding Spanish code and the old Portuguese code tried to
-- handle this condition by generating the default plural from the lemma.
error("Internal error: Something wrong, no generated default m/f plurals at this stage")
end
for _, defpl in ipairs(default_plurals) do
-- defpl is already a table and has an accel field
m_headword_utilities.combine_termobj_decorations(defpl, mfpl)
insert(new_mfpls, defpl)
end
elseif mfpl.term:find("^%+") then
mfpl.term = require(romut_module).get_special_indicator(mfpl.term)
for _, mf in ipairs(singulars) do
local default_mfpls = com.make_plural(mf.term, gender, mfpl.term)
for _, defp in ipairs(default_mfpls) do
local mfplobj = m_table.shallowCopy(mfpl)
mfplobj.term = defp
mfplobj.accel = accel
m_headword_utilities.combine_termobj_decorations(mfplobj, mf)
insert(new_mfpls, mfplobj)
end
end
else
mfpl.accel = accel
mfpl.term = replace_hash_with_lemma(mfpl.term, lemma)
insert(new_mfpls, mfpl)
end
end
return new_mfpls
end
if args.fpl[1] then
-- Override any existing feminine plurals.
feminine_plurals = handle_mf_plural("fpl", feminine_plurals, feminines)
end
if args.mpl[1] then
-- Override any existing masculine plurals.
masculine_plurals = handle_mf_plural("mpl", masculine_plurals, masculines)
end
local function parse_and_insert_noun_inflection(field, label, accel)
parse_and_insert_inflection(data, args, field, label, accel)
end
insert_noun_inflection(feminines, "giống cái", "f")
insert_noun_inflection(feminine_plurals, "giống cái số nhiều")
insert_noun_inflection(masculines, "giống đực")
insert_noun_inflection(masculine_plurals, "giống đực số nhiều")
parse_and_insert_noun_inflection("dim", "giảm nhẹ nghĩa")
parse_and_insert_noun_inflection("aug", "tăng kích")
parse_and_insert_noun_inflection("pej", "nghĩa xấu")
parse_and_insert_noun_inflection("dem", "demonym")
parse_and_insert_noun_inflection("fdem", "female demonym")
-- Is this a noun with an unexpected ending (for its gender)?
-- Only check if the term is one word (there are no spaces in the term).
local irreg_gender_lemma = rsub(lemma, " .*", "") -- only look at first word
if (gender_for_irreg_ending == "m" or gender_for_irreg_ending == "mf") and irreg_gender_lemma:find("a$") then
insert(data.categories, category_plpos .. " giống đực kết thúc bằng -a " .. langname)
elseif (gender_for_irreg_ending == "f" or gender_for_irreg_ending == "mf") and not (
irreg_gender_lemma:find("a$") or irreg_gender_lemma:find("ió$") or irreg_gender_lemma:find("tat$") or
irreg_gender_lemma:find("tud$") or irreg_gender_lemma:find("[dt]riu$")) then
insert(data.categories, category_plpos .. " giống cái nhưng không kết thúc bằng giống cái " .. langname)
end
end
local function get_noun_params(is_proper)
return {
[1] = {list = "g", disallow_holes = true, required = not is_proper, default = "?", type = "genders",
flatten = true}, -- gender(s)
[2] = {list = "pl", disallow_holes = true}, --plural override(s)
["f"] = list_param, --feminine form(s)
["m"] = list_param, --masculine form(s)
["fpl"] = list_param, --feminine plural override(s)
["mpl"] = list_param, --masculine plural override(s)
["dim"] = list_param, --diminutive(s)
["aug"] = list_param, --diminutive(s)
["pej"] = list_param, --pejorative(s)
["dem"] = list_param, --demonym(s)
["fdem"] = list_param, --female demonym(s)
}
end
pos_functions["Danh từ"] = {
params = get_noun_params(),
func = do_noun,
}
pos_functions["Danh từ riêng"] = {
params = get_noun_params("is proper"),
func = function(args, data)
do_noun(args, data, "is proper")
end,
}
-----------------------------------------------------------------------------------------
-- Verbs --
-----------------------------------------------------------------------------------------
pos_functions["Động từ"] = {
params = {
[1] = true,
["pres"] = list_param, --present
["pres_qual"] = {list = "pres\1_qual", allow_holes = true},
["pres3s"] = list_param, --third-singular present
["pres3s_qual"] = {list = "pres3s\1_qual", allow_holes = true},
["pret"] = list_param, --preterite
["pret_qual"] = {list = "pret\1_qual", allow_holes = true},
["part"] = list_param, --participle
["part_qual"] = {list = "part\1_qual", allow_holes = true},
["short_part"] = list_param, --short participle
["short_part_qual"] = {list = "short_part\1_qual", allow_holes = true},
["noautolinktext"] = boolean_param,
["noautolinkverb"] = boolean_param,
["attn"] = boolean_param,
["pres_1_sg"] = true, -- accept any ignore old-style param
["past_part"] = true, -- accept any ignore old-style param
["root"] = true, -- FIXME: Implement root-stressed vowel quality
},
func = function(args, data, tracking_categories, frame)
local preses, preses_3s, prets, parts, short_parts
if args.attn then
insert(tracking_categories, "Yêu cầu chú ý liên quan đến " .. langname)
return
end
local ca_verb = require(ca_verb_module)
local alternant_multiword_spec = ca_verb.do_generate_forms(args, "ca-verb", data.heads[1])
local specforms = alternant_multiword_spec.forms
local function slot_exists(slot)
return specforms[slot] and #specforms[slot] > 0
end
local function do_finite(slot_tense, label_tense)
-- Use pres_3s if it exists and pres_1s doesn't exist (e.g. impersonal verbs); similarly for pres_3p (only3p verbs);
-- but fall back to pres_1s if neither pres_1s nor pres_3s nor pres_3p exist (e.g. [[empedernir]]).
local has_1s = slot_exists(slot_tense .. "_1s")
local has_3s = slot_exists(slot_tense .. "_3s")
local has_3p = slot_exists(slot_tense .. "_3p")
if has_1s or (not has_3s and not has_3p) then
return {
slot = slot_tense .. "_1s",
label = ("ngôi thứ nhất số ít %s"):format(label_tense),
}, true
elseif has_3s then
return {
slot = slot_tense .. "_3s",
label = ("ngôi thứ ba số ít %s"):format(label_tense),
}, false
else
return {
slot = slot_tense .. "_3p",
label = ("ngôi thứ ba số nhiều %s"):format(label_tense),
}, false
end
end
local did_pres_1s
preses, did_pres_1s = do_finite("pres", "hiện tại")
preses_3s = {
slot = "pres_3s",
label = "hiện tại ở ngôi thứ ba số ít",
}
prets = do_finite("pret", "quá khứ đơn")
parts = {
slot = "pp_ms",
label = "phân từ quá khứ",
}
short_parts = {
slot = "short_pp_ms",
label = "phân từ quá khứ ngắn",
}
if args.pres[1] or args.pres3s[1] or args.pret[1] or args.part[1] or args.short_part[1] then
track("verb-old-multiarg")
end
local function strip_brackets(qualifiers)
if not qualifiers then
return nil
end
local stripped_qualifiers = {}
for _, qualifier in ipairs(qualifiers) do
local stripped_qualifier = qualifier:match("^%[(.*)%]$")
if not stripped_qualifier then
error("Lỗi nội bộ: Qualifier should be surrounded by brackets at this stage: " .. qualifier)
end
insert(stripped_qualifiers, stripped_qualifier)
end
return stripped_qualifiers
end
local function do_verb_form(args, qualifiers, slot_desc, skip_if_empty)
local forms
local to_insert
if #args == 0 then
forms = specforms[slot_desc.slot]
if not forms or #forms == 0 then
if skip_if_empty then
return
end
forms = {{form = "-"}}
end
elseif #args == 1 and args[1] == "-" then
forms = {{form = "-"}}
else
forms = {}
for i, arg in ipairs(args) do
local qual = qualifiers[i]
if qual then
-- FIXME: It's annoying we have to add brackets and strip them out later. The inflection
-- code adds all footnotes with brackets around them; we should change this.
qual = {"[" .. qual .. "]"}
end
local form = arg
if not args.noautolinkverb then
-- [[Module:inflection utilities]] already loaded by [[Module:ca-verb]]
form = require(inflection_utilities_module).add_links(form)
end
insert(forms, {form = form, footnotes = qual})
end
end
if forms[1].form == "-" then
to_insert = {label = "no " .. slot_desc.label}
else
local into_table = {label = slot_desc.label}
for _, form in ipairs(forms) do
local qualifiers = strip_brackets(form.footnotes)
-- Strip redundant brackets surrounding entire form. These may get generated e.g.
-- if we use the angle bracket notation with a single word.
local stripped_form = rmatch(form.form, "^%[%[([^%[%]]*)%]%]$") or form.form
-- Don't include accelerators if brackets remain in form, as the result will be wrong.
-- FIXME: For now, don't include accelerators. We should use the new {{ca-verb form of}}.
-- local this_accel = not stripped_form:find("%[%[") and accel or nil
local this_accel = nil
insert(into_table, {term = stripped_form, q = qualifiers, accel = this_accel})
end
to_insert = into_table
end
insert(data.inflections, to_insert)
end
local skip_pres_if_empty
if alternant_multiword_spec.no_pres1_and_sub then
insert(data.inflections, {label = "không có hiện tại ở ngôi thứ nhất số ít"})
insert(data.inflections, {label = "không có giả định hiện tại"})
end
if alternant_multiword_spec.no_pres_stressed then
insert(data.inflections, {label = "không có trần thuật nhấn mạnh hay giả định"})
skip_pres_if_empty = true
end
if alternant_multiword_spec.only3s then
insert(data.inflections, {label = glossary_link("không ngôi")})
elseif alternant_multiword_spec.only3sp then
insert(data.inflections, {label = "chỉ có ở ngôi thứ ba"})
elseif alternant_multiword_spec.only3p then
insert(data.inflections, {label = "chỉ có ở ngôi thứ ba số ít"})
end
local has_vowel_alt
if alternant_multiword_spec.vowel_alt then
for _, vowel_alt in ipairs(alternant_multiword_spec.vowel_alt) do
if vowel_alt ~= "+" and vowel_alt ~= "í" and vowel_alt ~= "ú" then
has_vowel_alt = true
break
end
end
end
do_verb_form(args.pres, args.pres_qual, preses, skip_pres_if_empty)
-- We want to include both the pres_1s and pres_3s if there is a vowel alternation in the present singular. But we
-- don't want to redundantly include the pres_3s if we already included it.
if did_pres_1s and has_vowel_alt then
do_verb_form(args.pres3s, args.pres3s_qual, preses_3s, skip_pres_if_empty)
end
do_verb_form(args.pret, args.pret_qual, prets)
do_verb_form(args.part, args.part_qual, parts)
do_verb_form(args.short_part, args.short_part_qual, short_parts, "bỏ qua nếu để trống")
-- Add categories.
for _, cat in ipairs(alternant_multiword_spec.categories) do
insert(data.categories, cat)
end
-- If the user didn't explicitly specify head=, or specified exactly one head (not 2+) and we were able to
-- incorporate any links in that head into the 1= specification, use the infinitive generated by
-- [[Module:ca-verb]] in place of the user-specified or auto-generated head. This was copied from
-- [[Module:it-headword]], where doing this gets accents marked on the verb(s). We don't have accents marked on
-- the verb but by doing this we do get any footnotes on the infinitive propagated here. Don't do this if the
-- user gave multiple heads or gave a head with a multiword-linked verbal expression such as Italian
-- '[[dare esca]] [[al]] [[fuoco]]' (FIXME: give Catalan equivalent).
if #data.user_specified_heads == 0 or (
#data.user_specified_heads == 1 and alternant_multiword_spec.incorporated_headword_head_into_lemma
) then
data.heads = {}
for _, lemma_obj in ipairs(alternant_multiword_spec.forms.infinitive_linked) do
local quals, refs = require(inflection_utilities_module).
convert_footnotes_to_qualifiers_and_references(lemma_obj.footnotes)
insert(data.heads, {term = lemma_obj.form, q = quals, refs = refs})
end
end
if args.root then
local m_ca_IPA = require(ca_IPA_module)
local parsed_respellings = {}
local function set_parsed_respelling(dialect, parsed)
-- Validate the individual root vowel specs.
for _, termobj in ipairs(parsed.terms) do
if not rfind(termobj.words[1].term, "^" .. m_ca_IPA.mid_vowel_hint_c .. "$") then
error(("Root vowel spec '%s' should be one of the vowels %s"):format(
termobj.words[1].term, m_ca_IPA.mid_vowel_hints))
end
end
if not dialect then
for _, dial in ipairs(m_ca_IPA.dialects) do
-- Need to clone as we destructively modify each one later with the pronun.
parsed_respellings[dial] = m_table.deepCopy(parsed)
end
elseif m_ca_IPA.dialect_groups[dialect] then
for _, dial in ipairs(m_ca_IPA.dialect_groups[dialect]) do
-- Need to clone as we destructively modify each one later with the pronun.
parsed_respellings[dial] = m_table.deepCopy(parsed)
end
else
parsed_respellings[dialect] = parsed
end
end
local function check_dialect_or_dialect_group(dialect)
if not m_table.contains(m_ca_IPA.dialects, dialect) and not
m_ca_IPA.dialect_groups[dialect] then
local dialect_list = {}
for _, dial in ipairs(m_ca_IPA.dialects) do
insert(dialect_list, "'" .. dial .. "'")
end
dialect_list = list_to_text(dialect_list, nil, " hoặc ")
local dialect_group_list = {}
for dialect_group, _ in pairs(m_ca_IPA.dialect_groups) do
insert(dialect_group_list, "'" .. dialect_group .. "'")
end
dialect_group_list = list_to_text(dialect_group_list, nil, " hoặc ")
error(("Không rõ phương ngữ '%s': Phương ngữ phải là %s hoặc nhóm phương ngữ %s"):format(
dialect, dialect_list, dialect_group_list))
end
end
-- Parse the root vowel specs.
if args.root:find("[<%[]") then
local put = require(parse_utilities_module)
-- Parse balanced segment runs involving either [...] (substitution notation) or <...> (inline
-- modifiers). We do this because we don't want commas or semicolons inside of square or angle brackets
-- to count as respelling delimiters. However, we need to rejoin square-bracketed segments with nearby
-- ones after splitting alternating runs on comma and semicolon.
local segments = put.parse_multi_delimiter_balanced_segment_run(args.root, {{"<", ">"}, {"[", "]"}})
local semicolon_separated_groups = put.split_alternating_runs(segments, "%s*;%s*")
for _, group in ipairs(semicolon_separated_groups) do
local first_element = group[1]
local dialect
if first_element:find("^[a-z]+:") then
-- a dialect-specific spec
local rest
dialect, rest = first_element:match("^([a-z]+):(.*)$")
check_dialect_or_dialect_group(dialect)
group[1] = rest
end
local comma_separated_groups = put.split_alternating_runs_on_comma(group)
-- Process each value.
local outer_container = m_ca_IPA.parse_comma_separated_groups(comma_separated_groups, true, args.root,
"root")
set_parsed_respelling(dialect, outer_container)
end
else
for _, dialect_spec in ipairs(rsplit(args.root, "%s*;%s*")) do
local dialect
if dialect_spec:find("^[a-z]+:") then
-- a dialect-specific spec
local rest
dialect, rest = dialect_spec:match("^([a-z]+):(.*)$")
check_dialect_or_dialect_group(dialect)
dialect_spec = rest
end
local termobjs = {}
for _, word in ipairs(rsplit(dialect_spec, ",")) do
insert(termobjs, {words = {{term = word}}})
end
set_parsed_respelling(dialect, {
terms = termobjs,
})
end
end
-- Convert each canonicalized respelling to phonemic/phonetic IPA.
m_ca_IPA.generate_phonemic_phonetic(parsed_respellings)
-- Group the results.
local grouped_pronuns = m_ca_IPA.group_pronuns_by_dialect(parsed_respellings)
-- Format for display.
for _, grouped_pronun_spec in pairs(grouped_pronuns) do
local pronunciations = {}
local function ins(text)
insert(pronunciations, text)
end
-- Loop through each pronunciation. For each one, format the phonetic version "raw".
for j, pronun in ipairs(grouped_pronun_spec.pronuns) do
-- Add dialect tags to left accent qualifiers if first one
local as = pronun.a
if j == 1 then
if as then
as = m_table.deepCopy(as)
else
as = {}
end
for _, dialect in ipairs(grouped_pronun_spec.dialects) do
insert(as, m_ca_IPA.dialects_to_names[dialect])
end
else
ins(", ")
end
local slash_pron = "/" .. pronun.phonetic:gsub("ˈ", "") .. "/"
if as or pronun.q or pronun.qq or pronun.aa then
ins(require(decorations_module).format_decorations {
lang = lang,
text = slash_pron,
q = pronun.q,
a = as,
qq = pronun.qq,
aa = pronun.aa
})
else
ins(slash_pron)
end
if pronun.refs then
-- FIXME: Copied from [[Module:IPA]]. Should be in a module.
local refs = {}
if #pronun.refs > 0 then
for _, refspec in ipairs(pronun.refs) do
if type(refspec) ~= "table" then
refspec = {text = refspec}
end
local refargs
if refspec.name or refspec.group then
refargs = {name = refspec.name, group = refspec.group}
end
insert(refs, mw.getCurrentFrame():extensionTag("ref", refspec.text, refargs))
end
ins(concat(refs))
end
end
end
grouped_pronun_spec.formatted = concat(pronunciations)
end
-- Concatenate formatted results.
local formatted = {}
for _, grouped_pronun_spec in ipairs(grouped_pronuns) do
insert(formatted, grouped_pronun_spec.formatted)
end
data.post_note = "''trọng âm gốc từ'': " .. concat(formatted, "; ")
end
end
}
-----------------------------------------------------------------------------------------
-- Numerals --
-----------------------------------------------------------------------------------------
-- Display additional inflection information for a numeral
pos_functions["Số từ"] = {
params = {
[1] = true,
[2] = true,
},
func = function(args, data)
if args[1] then
insert(data.genders, "m")
parse_and_insert_inflection(data, args, 1, "giống cái")
parse_and_insert_inflection(data, args, 2, "Biến thể hình thái danh từ")
else
insert(data.genders, "m")
insert(data.genders, "f")
end
end
}
-----------------------------------------------------------------------------------------
-- Phrases --
-----------------------------------------------------------------------------------------
pos_functions["Cụm từ"] = {
params = {
["g"] = {list = true, disallow_holes = true, type = "genders", flatten = true},
["m"] = list_param,
["f"] = list_param,
},
func = function(args, data)
validate_genders(args.g)
data.genders = args.g
parse_and_insert_inflection(data, args, "m", "giống đực")
parse_and_insert_inflection(data, args, "f", "giống cái")
end,
}
-----------------------------------------------------------------------------------------
-- Suffix forms --
-----------------------------------------------------------------------------------------
pos_functions["Biến thể hình thái hậu tố"] = {
params = {
[1] = {required = true, list = true, disallow_holes = true},
["g"] = {list = true, disallow_holes = true, type = "genders", flatten = true},
},
func = function(args, data)
validate_genders(args.g)
data.genders = args.g
local suffix_type = {}
for _, typ in ipairs(args[1]) do
insert(suffix_type, "Biến thể hình thái hậu tố của " .. typ)
end
insert(data.inflections, {label = "mục từ biến thể hình thái của " .. m_table.serialCommaJoin(suffix_type, {conj = "hoặc"})})
end,
}
return export
ok9rbaje3igcwk0bynlah5swmt86tk5
auklet
0
284858
2573941
2160224
2026-10-28T01:22:35Z
Hiyuune
50834
2573941
wikitext
text/x-wiki
=={{langname|en}}==
{{wp|en:}}
==={{section|etym}}===
Từ {{suffix|en|auk|let}}.
==={{section|pron}}===
* {{IPA|en|/ˈɔːklət/|a=RP}}
==={{section|n}}===
{{en-noun}}
# Bất kì loài [[chim biển]] nhỏ nào trong chi {{taxfmt|Aethia|chi}}, {{taxfmt|Cerorhinca|chi}} và {{taxfmt|Ptychoramphus|chi}} của họ {{l|mul|Alcidae}}.
===={{section|drv}}====
{{col-top|2|der}}
* [[Cassin's auklet]] ({{taxfmt|Ptychoramphus aleuticus|loài}})
* [[crested auklet]] ({{taxfmt|Aethia cristatella|loài}})
* {{l|en|least auklet}} ({{taxfmt|Aethia pusilla|loài}})
* {{l|en|parakeet auklet}}/{{l|en|paroquet auklet}} ({{taxfmt|Aethia psittacula|loài}})
* [[rhinoceros auklet]] ({{taxfmt|Cerorhinca monocerata|loài}})
* {{vern|whiskered auklet}} ({{taxfmt|Aethia pygmaea|loài}})
{{col-bottom}}
===={{section|rel}}====
* {{l|en|auk}}
==={{section|ana}}===
* {{anagrams|en|a=aekltu|taulke}}
{{C|en|Alcidae}}
mbiqvp50le1if03vzjvtil4rl9s32ub
rừng đặc dụng
0
285283
2573902
2337504
2026-10-27T15:40:31Z
TheHighFighter2
42988
2573902
wikitext
text/x-wiki
=={{langname|vi}}==
==={{ĐM|pron}}===
{{vi-pron}}
==={{ĐM|noun}}===
{{vi-noun|head=[[rừng]] [[đặc dụng]]}}
# [[rừng|Rừng]] có [[công dụng]] [[đặc biệt]] đối với [[môi trường]], với [[công tác]] [[nghiên cứu]] [[khoa học]], v.v..
#: {{ux|vi|Đất '''rừng đặc dụng''' có vai trò bảo tồn thiên nhiên hoang dã của quốc gia, lưu giữ những loài vật giống quý.}}
===={{ĐM|trans}}====
{{trans-top|Rừng có công dụng đặc biệt}}
* {{dịch|sed}}: {{t|sed|kong xuk tơmeăm}}
{{trans-bottom}}
==={{ĐM|ref}}===
* {{R:Tratu}}
ior1ha7lyo4qjvtrfu99l9j0hr9q3r2
Mô đun:mn-headword
828
291479
2573965
2566639
2026-10-28T07:18:27Z
Hiyuune
50834
2573965
Scribunto
text/plain
local export = {}
local pos_functions = {}
local force_cat = false -- for testing; if true, categories appear in non-mainspace pages
local headword_utilities_module = "Module:headword utilities"
local languages_module = "Module:languages"
local mn = require("Module:mn-common")
local lang = require(languages_module).getByCode("mn")
--[==[
Main entry point. Takes these params:
; {{para|1}}
: The part of speech, pluralized; omit for {{tl|mn-head}}.
; {{para|def}}
: Optional default value for the template page.
]==]
function export.show(frame)
return require(headword_utilities_module).process_headword {
lang = lang,
frame = frame,
pos_functions = pos_functions,
include_tr = true,
include_sc = true,
force_cat = force_cat,
infls = {
{function(data) return data.process_props.generic_pos_template and 2 or 1 end,
docparam = "{{para|2}} for {{tl|mn-head}}, {{para|1}} otherwise",
label = function(_data, vals)
return "chính tả " .. (vals[1] and lang:findBestScript(vals[1].term):getCanonicalName(lang) or "tiếng Mông Cổ")
end,
doclabel = "other-script spellings",
process_after_parse = function(data, vals)
for _, valobj in ipairs(vals) do
local other_sc = lang:findBestScript(valobj.term)
if data.sc:getCode() == other_sc:getCode() then
error(("The headword and alternative spelling %s are both in %s but should be in different scripts"):format(
valobj.term, data.sc:getCanonicalName(lang)
))
end
end
return vals
end,
insert_inflection_props = {
enable_auto_translit = true,
},
addldoc = "These consist of spellings in Mongolian script if the pagename is Cyrillic, and " ..
"vice-versa. An error is thrown if the other-script spelling is in the same script as " ..
"the pagename.",
},
{cat = function(data)
if not data.pagename:find("^%-") and not data.pagename:find(" ") and data.sc:getCode() == "Cyrl" then
-- FIXME, should be done by {{mn-IPA}}
return "Từ " .. #mn.syllables(data.pagename) .. " âm tiết"
end
end,
doc = "If the pagename is not a suffix, does not have a space in it and is in Cyrillic, a category " ..
"such as {{catlink|Mongolian 3-syllable words}} (depending on the number of syllables in the " ..
"word) will be added. (FIXME, this should be done by {{tl|mn-IPA}} instead.)",
},
},
}
end
local valid_declensions = {
r = "thường",
n = "ẩn n",
g = "ẩn g",
m = "hỗn hợp",
}
pos_functions["Danh từ"] = {
infls = {
{"dec", doclabel = "declension", validate = valid_declensions,
fixed_label = function(_data, val)
return "Biến cách " .. valid_declensions[val.term]
end,
cat = function(_data, val)
return "Danh từ biến cách " .. valid_declensions[val.term]
end,
},
{"pl", label = "xác định số nhiều"},
},
}
pos_functions["Danh từ riêng"] = pos_functions["Danh từ"]
pos_functions["Động từ"] = {
infls = {
{"caus", label = "thể sai khiến"},
{"pass", label = "thể bị động"},
},
}
return export
mkyuyalfi3pkhe4kk9sva7iencqkvhu
chạy bằng cơm
0
294510
2573892
2212087
2026-10-27T14:10:18Z
TheHighFighter2
42988
/* Tiếng Việt */
2573892
wikitext
text/x-wiki
=={{langname|vi}}==
==={{ĐM|pron}}===
{{vi-pron}}
==={{ĐM|adj}}===
{{vi-adj}}
# {{label|vi|lóng|nghĩa bóng|hài hước}} [[chạy|Chạy]] bằng [[sức mạnh]] [[cơ bắp]] hoặc [[khả năng]] từ 5 [[giác quan]] của con người; chạy theo cách/bằng sức [[thủ công]].
#: {{ux|vi|Camera '''chạy bằng cơm'''.}}
#* {{quote-journal|vi|title="Bật mí" những chuyến bay "như chuyên cơ" đưa tuyển Việt Nam du đấu|author=Dương Ngọc - Đức Anh|journal=[[w:Báo Người Lao Động|Người Lao Động Online]]|year=4/2/2019|url=https://nld.com.vn/the-thao/bat-mi-nhung-chuyen-bay-nhu-chuyen-co-dua-tuyen-viet-nam-du-dau-20190204005053862.htm|archiveurl=https://web.archive.org/web/20221207003029/https://nld.com.vn/the-thao/bat-mi-nhung-chuyen-bay-nhu-chuyen-co-dua-tuyen-viet-nam-du-dau-20190204005053862.htm|archivedate=7/12/2023|passage=Các bạn rất chu đáo, tươi cười niềm nở và gây bất ngờ khi điều hẳn xe thang "'''chạy bằng cơm'''" để đón chuyến bay.}}
===={{ĐM|derived}}====
* {{vie-l|camera chạy bằng cơm}}
===={{ĐM|trans}}====
{{trans-top|Chạy theo cách/bằng sức thủ công}}
* {{dịch|en}}: {{t+|en|manpowered}}
{{trans-bottom}}
==={{ĐM|see}}===
* {{vi-l|[[chạy]] [[thủ công]]}}
* {{vi-l|[[chạy]] [[bằng]] [[sức]] [[người]]}}
pjz5od7s7uoeqqn7wifpwm4h1agoevk
áo khoác
0
294776
2573874
2317106
2026-10-27T13:17:32Z
TheHighFighter2
42988
/* Dịch */ Chuyển đổi {{langname}} ---> {{dịch}}
2573874
wikitext
text/x-wiki
=={{langname|vi}}==
{{-info-}}
==={{ĐM|etymology}}===
Từ {{etym-com|vi|áo|khoác}}.
==={{ĐM|pron}}===
{{vi-pron}}
==={{ĐM|noun}}===
{{vi-noun|cls=[[chiếc]], [[cái]]}}
# Loại [[áo]] [[mặc]] [[bên ngoài]], được sử dụng bởi cả [[nam]] và [[nữ]], nhằm mục đích [[giữ]] [[ấm]] hoặc tạo tính [[thời trang]].
#* {{quote-journal|vi|title=Tàu Titan nổ tung và thảm họa hàng hải 111 năm trước|author=Phan Quang Vũ|journal=Báo Đại Đoàn Kết|year=18/7/2023|url=https://daidoanket.vn/tau-titan-no-tung-va-tham-hoa-hang-hai-111-nam-truoc-10256098.html|archiveurl=https://web.archive.org/web/20231121060447/https://daidoanket.vn/tau-titan-no-tung-va-tham-hoa-hang-hai-111-nam-truoc-10256098.html|archivedate=21/11/2023|passage=Bà cẩn thận cởi chiếc '''áo khoác''' lông chồn của mình và đưa cho người giúp việc Ellen Bird.}}
===={{ĐM|trans}}====
{{trans-top|Loại áo}}
* {{dịch|en}}: {{t+|en|coat}}, {{t+|en|jacket}}
{{trans-bottom}}
{{C|vi|Trang phục}}
4fp9a5vf6jfnkpupl1jcy1h7euopyzo
laħħam
0
298156
2573882
2290248
2026-10-27T13:57:47Z
Hiyuune
50834
/* Tiếng Malta */ (sử dụng [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]])
2573882
wikitext
text/x-wiki
=={{langname|mt}}==
{{mt-root|l-ħ-m}}
==={{section|etym}}===
{{ety|mt|:denominal|laħam|text=+|tree=1}}
==={{section|pron}}===
* {{IPA|mt|/ˈlaħ.ħam/}}
* {{rhymes|mt|aħħam|s=2}}
==={{section|v}}===
{{mt-head|verb|jlaħħam|mlaħħam|vn=tilħim}}
# [[vỗ béo|Vỗ béo]].
# Là [[hiện thân]] của.
===={{section|forms}}====
{{mt-conj-auto|II}}
0zwl33fza3lmdzqfw9wb640ubnektti
czarnoziem
0
299411
2573987
2146392
2026-10-28T09:22:29Z
Hiyuune
50834
2573987
wikitext
text/x-wiki
=={{langname|pl}}==
==={{section|etym}}===
{{ety|pl|:af|czarny|-o-|ziemia|text=+|tree=1}}
==={{section|pron}}===
{{pl-pr|a=Pl-czarnoziem.ogg}}
==={{section|n}}===
{{pl-noun|m-in}}
# {{i|thường ở dạng số nhiều}} [[đất đen|Đất đen]].
===={{section|decl}}====
{{pl-ndecl|<m.inan>}}
==={{section|further}}===
* {{R:pl:WSJP}}
* {{R:pl:PWN}}
dlrs1clf5jsf8lkaaex8yn14tdhdo2a
auq
0
299965
2573950
2147535
2026-10-28T06:30:06Z
Hiyuune
50834
2573950
wikitext
text/x-wiki
=={{langname|mul}}==
==={{section|symbol}}===
{{mul-symbol}}
# {{ISO 639|3}}
=={{langname|zhn}}==
==={{section|pron}}===
* {{IPA|zhn|/ʔaːu¹¹/|a=Liên Thành,Quảng Nam}}
==={{section|n}}===
{{head|zhn|Danh từ}}
# [[ông]], [[ngài]].
mrxkaqrg5hi4zed4dygmp7j5lqb7d0s
augusta
0
319308
2573940
2191532
2026-10-28T01:19:36Z
Hiyuune
50834
2573940
wikitext
text/x-wiki
{{also|Augusta|augustā|Augustą}}
=={{langname|pt}}==
==={{section|adj}}===
{{head|pt|Biến thể hình thái tính từ}}
# {{adj form of|pt|augusto||f|s}}
=={{langname|ca}}==
==={{section|pron}}===
* {{ca-IPA}}
==={{section|adj}}===
{{head|ca|Biến thể hình thái tính từ}}
# {{feminine singular of|ca|august}}
=={{langname|krl}}==
{{krl-regional|-|augusta}}
{{enum|krl|ijulʹa|senʹtʹabri|Tháng trong năm}}
==={{section|etym}}===
Vay mượn từ {{bor|krl|ru|август}}.
==={{section|pron}}===
* {{IPA|krl|/ˈɑu̯ɡustɑ/}}
* {{hyph|krl|au|gus|ta}}
==={{section|n}}===
{{krl-noun|augustan|augustua}}
# {{lb|krl|Nam Karelia}} [[tháng Tám|Tháng Tám]].
===={{section|decl}}====
{{krl-decl-T/kala|st|st}}
{{krl-decl-poss}}
===={{section|syn}}====
* {{l|krl|elokuu}}
==={{section|ref}}===
* {{R:krl:Punzhina:1994|+}}
{{C|krl|Tháng}}
=={{langname|la}}==
==={{section|pron}}===
* {{la-IPA|augustā}}
==={{section|v}}===
{{head|la|Biến thể hình thái động từ|head=augustā}}
# {{inflection of|la|augustō||impr|pres|actv|ở|2|s}}
==={{section|adj}}===
{{head|la|Biến thể hình thái tính từ}}
# {{inflection of|la|augustus||nom//voc|ở|f|s|;|nom//acc//voc|ở|n|p}}
==={{section|adj}}===
{{head|la|Biến thể hình thái tính từ|head=augustā}}
# {{inflection of|la|augustus||abl|ở|f|s}}
==={{section|ref}}===
* {{R:du Cange}}
=={{langname|lv}}==
==={{section|n}}===
{{head|lv|Biến thể hình thái danh từ|g=m}}
# {{infl of|lv|augusts||gen|s}}
=={{langname|es}}==
==={{section|pron}}===
{{es-pr}}
==={{section|adj}}===
{{head|es|Biến thể hình thái tính từ}}
# {{adj form of|es|augusto||f|s}}
=={{langname|it}}==
==={{section|adj}}===
{{head|it|Biến thể hình thái tính từ}}
# {{adj form of|it|augusto||f|s}}
i6q70hm58bbz4w0fbu9ah9wxr2vd36k
ausiasmarquianes
0
319931
2573945
2192631
2026-10-28T02:27:14Z
Hiyuune
50834
2573945
wikitext
text/x-wiki
=={{langname|ca}}==
==={{section|pron}}===
* {{ca-IPA}}
* {{rhymes|ca|anes|s=7}}
==={{section|adj}}===
{{head|ca|Biến thể hình thái tính từ}}
# {{feminine plural of|ca|ausiasmarquiana}}
pluds2xo5eozx8j8yp3sphxdr0d44ir
Aurelianum
0
324207
2573952
2199906
2026-10-28T06:33:32Z
Hiyuune
50834
2573952
wikitext
text/x-wiki
=={{langname|la}}==
==={{section|pron}}===
* {{la-IPA|Aurēliānum}}
==={{section|pronoun}}
{{head|la|Biến thể hình thái đại từ|head=Aurēliānum}}
# {{inflection of|la|Aurelianus#Proper_noun|Aurēliānus||acc|ở|m|s}}
==={{section|adj}}===
{{head|la|Biến thể hình thái tính từ|head=Aurēliānum}}
# {{inflection of|la|Aurelianus#Adjective|Aurēliānus||nom//acc//voc|ở|n|s|;|acc|ở|m|s}}
qzj7jdvmfp0igeyant87j8b27eky73t
2573953
2573952
2026-10-28T06:33:44Z
Hiyuune
50834
/* {{langname|la}} */
2573953
wikitext
text/x-wiki
=={{langname|la}}==
==={{section|pron}}===
* {{la-IPA|Aurēliānum}}
==={{section|pronoun}}===
{{head|la|Biến thể hình thái đại từ|head=Aurēliānum}}
# {{inflection of|la|Aurelianus#Proper_noun|Aurēliānus||acc|ở|m|s}}
==={{section|adj}}===
{{head|la|Biến thể hình thái tính từ|head=Aurēliānum}}
# {{inflection of|la|Aurelianus#Adjective|Aurēliānus||nom//acc//voc|ở|n|s|;|acc|ở|m|s}}
01r5b0n35dy6pfp1jzfbp1tetyxynmf
autoblindomitragliatrici
0
324284
2573947
2200007
2026-10-28T02:45:56Z
Hiyuune
50834
2573947
wikitext
text/x-wiki
=={{langname|it}}==
==={{section|pron}}===
{{ita-pr|àutoblìndomitragliatrìci}}
==={{section|n}}===
{{head|it|Biến thể hình thái danh từ|g=f}}
# {{plural of|it|autoblindomitragliatrice}}
j58apd1fqugw23e1riepyran49v3d6f
abjugate
0
324524
2573850
2200407
2026-10-27T12:31:42Z
Hiyuune
50834
2573850
wikitext
text/x-wiki
=={{langname|la}}==
==={{section|v}}===
{{head|la|Biến thể hình thái động từ|head=abjugāte}}
# {{inflection of|la|abjugō||impr|pres|actv|ở|2|p}}
dmvbltqlwoya14cnagggijlw34q61yo
ta-neūi-sö-mat
0
324895
2573918
2207391
2026-10-27T16:02:51Z
WhoAlone
40420
2573918
wikitext
text/x-wiki
== {{langname|caq}} ==
=== {{ĐM|noun}} ===
{{head|caq|Danh từ}}
# Ngôi [[sao]].
=== {{ĐM|ref}} ===
* G. Whitehead (1925). ''Dictionary of the Car Nicobarese Language''.
{{c|caq|Thiên thể}}
2ujce6vct51g3x66t7nz5ww01newpr6
ta-ngëk
0
324897
2573916
2201306
2026-10-27T16:01:29Z
WhoAlone
40420
2573916
wikitext
text/x-wiki
== {{langname|caq}} ==
=== {{ĐM|noun}} ===
{{head|caq|Danh từ}}
# [[xương|Xương]].
=== {{ĐM|ref}} ===
* G. Whitehead (1925). ''Dictionary of the Car Nicobarese Language''.
{{c|caq|Bộ Xương}}
1clzzkshu9wnufznacgqwgqtcg6jpnm
2573917
2573916
2026-10-27T16:01:41Z
WhoAlone
40420
2573917
wikitext
text/x-wiki
== {{langname|caq}} ==
=== {{ĐM|noun}} ===
{{head|caq|Danh từ}}
# [[xương|Xương]].
=== {{ĐM|ref}} ===
* G. Whitehead (1925). ''Dictionary of the Car Nicobarese Language''.
{{c|caq|Bộ xương}}
c54lhjpzzgw7vt00l4fm2okrfwf06yp
ta-löö-kö
0
324898
2573920
2201138
2026-10-27T16:03:16Z
WhoAlone
40420
2573920
wikitext
text/x-wiki
{{-caq-}}
{{-noun-}}
{{head|caq|Danh từ}}
# Con [[vẹt]].
# [[đường đi]].
{{-ref-}}
* G. Whitehead (1925). ''Dictionary of the Car Nicobarese Language''.
{{c|caq|Lớp Chim|Giao thông}}
5mntgohhgh1bjlkrfm351ay2bci4gj2
tāl
0
324899
2573911
2201139
2026-10-27T16:00:49Z
WhoAlone
40420
2573911
wikitext
text/x-wiki
{{-caq-}}
{{-noun-}}
{{head|caq|Danh từ}}
# [[hạt]], [[đậu]].
{{-ref-}}
* G. Whitehead (1925). ''Dictionary of the Car Nicobarese Language''.
{{c|caq|Thực vật}}
fw3ragkq9t8al9teetdejk8auqzk2g7
ta-lë
0
324900
2573921
2201140
2026-10-27T16:03:31Z
WhoAlone
40420
2573921
wikitext
text/x-wiki
{{-caq-}}
{{-noun-}}
{{head|caq|Danh từ}}
# [[lá]] [[dừa]].
{{-ref-}}
* G. Whitehead (1925). ''Dictionary of the Car Nicobarese Language''.
{{c|caq|Thực vật}}
movypovfaoyssgjqxzpoyo9nr1tcf60
tin-mël
0
324955
2573910
2201209
2026-10-27T16:00:37Z
WhoAlone
40420
2573910
wikitext
text/x-wiki
{{-caq-}}
{{-pron-}}
* {{IPA4|caq|/tin-mɛːl/}}
{{-noun-}}
{{head|caq|Danh từ}}
# [[cỏ]].
{{-ref-}}
* G. Whitehead (1925). ''Dictionary of the Car Nicobarese Language''.
{{c|caq|Cỏ}}
ovdwml802rown68yhadl1i3l55sgpxp
informaziotaraino
0
329325
2573967
2212341
2026-10-28T07:19:35Z
Hiyuune
50834
/* Tiếng Basque */ (sử dụng [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]])
2573967
wikitext
text/x-wiki
=={{langname|eu}}==
==={{ĐM|noun}}===
{{head|eu|Biến thể hình thái danh từ}}
# {{infl of|eu|informazio||term|indef}}
rteguqa923yadfp0bmq50hgp7ftkg7w
òk
0
340325
2573923
2230482
2026-10-27T16:04:00Z
WhoAlone
40420
2573923
wikitext
text/x-wiki
=={{langname|caq}}==
==={{ĐM|pron}}===
* {{IPA4|caq|/ʔɔˑk/}}
==={{ĐM|verb}}===
{{head|caq|Động từ}}
# [[uống]].
==={{ĐM|noun}}===
{{head|caq|Danh từ}}
# [[dừa]] [[mềm]].
==={{ĐM|ref}}===
* V.R. Rajasingh. (2019) ''[https://drive.google.com/open?id=1sJ_kFSvTHaxp24iH3Sx90jPm83fBXV8l Nicobarese-Hindi-English Dictionary (Pū-Sanënyö-Luröö-Mūöt-Lamôngsĕ-Tökahāṅilāhngö-Hindi-English)]''
{{c|caq|Uống}}
nvnlvjdhmh1tw4z410btv438yf6z1md
ôl kā
0
340326
2573924
2230483
2026-10-27T16:04:17Z
WhoAlone
40420
2573924
wikitext
text/x-wiki
=={{langname|caq}}==
==={{ĐM|pron}}===
* {{IPA4|caq|/ʔɔˑl kɑː/}}
==={{ĐM|noun}}===
{{head|caq|Danh từ}}
# [[na]], [[mãng cầu]].
==={{ĐM|ref}}===
* V.R. Rajasingh. (2019) ''[https://drive.google.com/open?id=1sJ_kFSvTHaxp24iH3Sx90jPm83fBXV8l Nicobarese-Hindi-English Dictionary (Pū-Sanënyö-Luröö-Mūöt-Lamôngsĕ-Tökahāṅilāhngö-Hindi-English)]''
{{c|caq|Trái cây}}
ahhvjakrtz66j0ibdmfuvc9xmal8aha
Mô đun:family tree
828
348445
2573983
2562233
2026-10-28T09:08:59Z
Hiyuune
50834
2573983
Scribunto
text/plain
--[=[
Authors: [[User:AryamanA]], [[User:Erutuon]], [[User:Fenakhay]], [[User:JohnC5]], [[User:kc_kennylau]], [[User:Suzukaze-c]], [[User:Theknightwho]]
]=]
local export = {}
local M = require("Module:module loader").init{
require = {
languages = "Module:languages",
table = "Module:table",
yesno = "Module:yesno",
parameters = "Module:parameters",
TemplateStyles = "Module:TemplateStyles",
},
loadData = {
language_names = "Module:languages/code to canonical name",
etymology_names = "Module:etymology languages/code to canonical name",
family_names = "Module:families/code to canonical name",
},
}
local STYLESHEET = "Module:family tree/style.css"
local function new_cache()
local cache = { language = {}, family = {} }
return function(namespace, code, fetch, ...)
local slot = cache[namespace]
local obj = slot[code]
if obj == nil then
obj = fetch(...) or false
slot[code] = obj
end
return obj or nil
end
end
local function kind_of(obj)
if not obj:hasType("language") then
return "family"
elseif obj:hasType("etymology-only") then
return "variety"
end
return "language"
end
local function sortkey_of(obj)
return (obj:getCanonicalName():gsub("^Proto%-", ""))
end
local placement_rules = {
{
name = "etymology-only language under its parent",
place = function(obj, get)
if not (obj:hasType("language") and obj:hasType("etymology-only")) then
return nil
end
local parent_code = obj:getParentCode()
if not parent_code then
return nil
end
local parent = get("language", parent_code, obj.getParent, obj)
if parent
and M.table.deepEquals(parent:getAncestorCodes(), obj:getAncestorCodes())
and parent:getFamilyCode() == obj:getFamilyCode() then
return { parent:getCode() }
end
end,
},
{
name = "language under its declared ancestors",
place = function(obj)
if not obj:hasType("language") then
return nil
end
local ancestors = obj:getAncestorCodes()
if ancestors[1] then
return M.table.shallowCopy(ancestors)
end
end,
},
{
name = "family under its ancestral proto-language",
place = function(obj)
if obj:hasType("language") then
return nil
end
local proto = obj:getProtoLanguageCode()
if proto and obj:hasAncestor(proto) then
return { proto }
end
end,
},
{
name = "node under its family's proto-language",
place = function(obj, get)
local family_code = obj:getFamilyCode()
if not family_code then
return nil
end
local family = get("family", family_code, obj.getFamily, obj)
local proto_code = family and family:getProtoLanguageCode()
local proto = proto_code and get("language", proto_code, family.getProtoLanguage, family)
if proto and proto:inFamily(family) and proto:getCode() ~= obj:getCode() then
return { proto:getCode() }
end
end,
},
{
name = "node under its family",
place = function(obj)
local family_code = obj:getFamilyCode()
if family_code then
return { family_code }
end
end,
},
}
local function parents_of(obj, get)
for _, rule in ipairs(placement_rules) do
local parents = rule.place(obj, get)
if parents then
return parents, rule.name
end
end
return {}
end
function export.build(root_code)
local get = new_cache()
local root = M.languages.getByCode(root_code, nil, true, true)
if not root then
return nil
end
local descendants, in_tree, children_of, info = {}, {}, {}, {}
local function include(code)
if not in_tree[code] then
in_tree[code] = true
descendants[#descendants + 1] = code
end
end
include(root:getCode())
for _, code in ipairs(root:getDescendantCodes()) do
include(code)
end
for _, code in ipairs(descendants) do
local obj = get("language", code, M.languages.getByCode, code, nil, true, true)
local kind = kind_of(obj)
info[code] = {
kind = kind,
category = obj:getCategoryName(),
sortkey = sortkey_of(obj),
protolanguage = kind == "family" and obj:getProtoLanguageCode() or nil,
reconstructed = obj:hasType("reconstructed"),
}
for _, parent_code in ipairs(parents_of(obj, get)) do
if in_tree[parent_code] and parent_code ~= code then
local siblings = children_of[parent_code]
if not siblings then
siblings = {}
children_of[parent_code] = siblings
end
table.insert(siblings, code)
end
end
end
local protolanguage_of = {}
for code, data in pairs(info) do
if data.protolanguage and in_tree[data.protolanguage] then
protolanguage_of[data.protolanguage] = code
end
end
local function by_sortkey(a, b)
local ka, kb = info[a].sortkey, info[b].sortkey
if ka == kb then
return a < b
end
return ka < kb
end
for _, siblings in pairs(children_of) do
table.sort(siblings, by_sortkey)
end
local function roles_of(kind, is_family_ancestor, reconstructed)
local roles = {}
if kind == "family" then
roles[#roles + 1] = "family"
end
if is_family_ancestor and reconstructed then
roles[#roles + 1] = "proto"
end
if kind == "variety" then
roles[#roles + 1] = "variety"
end
return roles
end
local function materialise(code, path)
local data = info[code]
local node = {
code = code,
kind = data.kind,
category = data.category,
sortkey = data.sortkey,
protolanguage = data.protolanguage,
protolanguage_of = protolanguage_of[code],
roles = roles_of(data.kind, protolanguage_of[code] ~= nil, data.reconstructed),
children = {},
}
local child_codes = children_of[code]
if child_codes and not path[code] then
path[code] = true
for _, child_code in ipairs(child_codes) do
table.insert(node.children, materialise(child_code, path))
end
path[code] = nil
end
return node
end
return materialise(root:getCode(), {})
end
local function is_protolanguage_pair(node)
local only_child = node.children[1]
return only_child ~= nil and node.children[2] == nil
and node.kind == "family" and node.protolanguage == only_child.code
end
local function has_non_variety_descendant(node)
for _, child in ipairs(node.children) do
if child.kind ~= "variety" or has_non_variety_descendant(child) then
return true
end
end
return false
end
function export.layout(node, options)
if options.varieties == "hide" and node.kind == "variety" and not has_non_variety_descendant(node) then
return nil
end
if options.families == "hide" and is_protolanguage_pair(node) then
return export.layout(node.children[1], options)
end
-- FIXME!!! Why do we copy the same data from `info` to `node` to `out`?
local out = {
code = node.code,
kind = node.kind,
category = node.category,
sortkey = node.sortkey,
protolanguage = node.protolanguage,
protolanguage_of = node.protolanguage_of,
roles = node.roles,
children = {},
}
for _, child in ipairs(node.children) do
local laid_out = export.layout(child, options)
if laid_out then
table.insert(out.children, laid_out)
end
end
if options.pair ~= "none" and is_protolanguage_pair(out) then
local proto = out.children[1]
out.pair = options.pair
out.partner = proto
out.children = proto.children
proto.children = {}
end
return out
end
local function canonical_name(node)
if node.kind == "family" then
return M.family_names[node.code]
elseif node.kind == "variety" then
return M.etymology_names[node.code]
end
return M.language_names[node.code]
end
local BADGE = {
family = { icon = "F", tooltip = "Language family", colour = "green" },
proto = { icon = "P", tooltip = "Proto-language", colour = "purple" },
variety = { icon = "V", tooltip = "Variety", colour = "blue" },
}
local function add_label(parent, node, options)
local name = canonical_name(node) or node.code
local span = parent:tag("span"):addClass("familytree-" .. node.kind)
if node.kind == "family" and node.protolanguage then
span:addClass("familytree-hasprotolang")
end
for _, role in ipairs(node.roles) do
if role == "proto" then
span:addClass("familytree-protolang")
end
end
span:wikitext(('[[:Category:%s|%s <span class="familytree-code">(%s)</span>]]')
:format(node.category, name, node.code))
for _, role in ipairs(node.roles) do
local b = BADGE[role]
span:wikitext(" ")
:tag("span")
:addClass("familytree-icon")
:attr("data-wikt-palette-p", b.colour)
:attr("title", b.tooltip)
:wikitext(b.icon)
end
if options.lemmas and node.kind == "language" then
span:wikitext(" (" .. mw.site.stats.pagesInCategory(name .. " lemmas", "pages") .. ")")
end
return span
end
local function all_children_are_varieties(node)
if not node.children[1] then
return false
end
for _, child in ipairs(node.children) do
if child.kind ~= "variety" then
return false
end
if child.children[1] and not all_children_are_varieties(child) then
return false
end
end
return true
end
local function pair_nodes(node)
if node.pair == "proto-first" then
return node.partner, node
end
return node, node.partner
end
local TOGGLE_EXPAND, TOGGLE_COLLAPSE = "[+]", "[-]"
local function render_node(list, node, options, state)
local li = list:tag("li")
if not node.children[1] and not node.partner then
add_label(li, node, options)
return
end
state.toggles = state.toggles + 1
local id = "familytree" .. state.toggles
local first, second = node, nil
if node.partner then
first, second = pair_nodes(node)
end
li:addClass("familytree-branch")
li:tag("span")
:addClass("familytree-toggle")
:addClass("mw-customtoggle-" .. id)
:wikitext(TOGGLE_COLLAPSE)
add_label(li, first, options)
local ul = li:tag("ul"):addClass("mw-collapsible")
if all_children_are_varieties(node) then
ul:addClass("familytree-only-etym-children")
end
ul:attr("id", "mw-customcollapsible-" .. id)
:attr("data-expandtext", TOGGLE_EXPAND)
:attr("data-collapsetext", TOGGLE_COLLAPSE)
if second then
add_label(ul:tag("li"):addClass("familytree-pair"), second, options)
end
for _, child in ipairs(node.children) do
render_node(ul, child, options, state)
end
end
function export.render_tree(root, options)
local state = { toggles = 1 }
local id = "familytree1"
local first, second = root, nil
if root.partner then
first, second = pair_nodes(root)
end
local div = mw.html.create("div"):addClass("familytree")
local li = div:tag("ul"):tag("li"):addClass("familytree-root")
add_label(li, first, options)
li:tag("span")
:addClass("familytree-toptoggle")
:addClass("mw-customtoggle-" .. id)
:css("display", "none")
:wikitext(options.collapsed and "Expand" or "Collapse")
if second then
add_label(li:tag("div"):addClass("familytree-pair"), second, options)
end
local ul = li:tag("ul"):addClass("mw-collapsible")
if options.collapsed then
ul:addClass("mw-collapsed")
end
ul:attr("id", "mw-customcollapsible-" .. id)
:attr("data-expandtext", "Expand")
:attr("data-collapsetext", "Collapse")
for _, child in ipairs(root.children) do
render_node(ul, child, options, state)
end
return tostring(div) .. M.TemplateStyles(STYLESHEET)
end
local DEFAULT_OPTIONS = {
families = "hide",
varieties = "hide",
pair = "none",
collapsed = false,
lemmas = false,
must_have_descendants = false,
}
local function with_defaults(options)
local out = {}
for key, default in pairs(DEFAULT_OPTIONS) do
local value = options[key]
if value == nil then
value = default
end
out[key] = value
end
return out
end
function export.render(code, options)
options = with_defaults(options or {})
local root = export.build(code)
if not root then
error(("The code %q is not a language, family or etymology-only language."):format(code))
end
root = export.layout(root, options)
if not root or (options.must_have_descendants and not root.children[1] and not root.partner) then
return nil
end
return export.render_tree(root, options)
end
function export.show(frame)
local boolean = { type = "boolean" }
local args = M.parameters.process(frame.args, {
[1] = { required = true },
families = { set = { "show", "hide" } },
varieties = { set = { "show", "hide" } },
pair = { set = { "family-first", "proto-first", "none" } },
collapsed = boolean,
lemmas = boolean,
})
return export.render(args[1], {
families = args.families,
varieties = args.varieties,
pair = args.pair,
collapsed = args.collapsed,
lemmas = args.lemmas,
}) or ""
end
return export
6upt9g3mxc9hqpl8l9pnvkvshvduv89
Vanuatu
0
349060
2573891
2252090
2026-10-27T14:08:10Z
Hiyuune
50834
/* Cách phát âm */ (sử dụng [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]])
2573891
wikitext
text/x-wiki
{{also|Vanuatú}}
{{minitoc}}
=={{langname|af}}==
{{wp|lang=af}}
==={{ĐM|pr-noun}}===
{{af-proper noun}}
# {{transclude|af|Vanuatu|id=Q686}}
===={{ĐM|rel}}====
* {{l|af|Vanuatuaan}}
* {{l|af|Vanuatuaans}}
* {{l|af|Vanuatuer}}
* {{l|af|Vanuatus}}
=={{langname|en}}==
{{wp|lang=en}}
==={{ĐM|etym}}===
{{bor+|en|bi|Vanuatu}}.
==={{ĐM|pron}}===
* {{IPA|en|/ˌvænuːˈaːtuː/|a=AU}}
* {{IPA|en|/væn.uːˈɑː.tuː/|a=RP}}
* {{IPA|en|/vɑ.nuˈɑ.tu/|a=GA}}
* {{audio|en|en-us-Vanuatu.ogg|a=US}}
* {{audio|en|LL-Q1860 (eng)-Soundguys-Vanuatu.wav |a=UK}}
==={{ĐM|pr-noun}}===
{{en-proper noun}}
# {{senseid|vi|Q686}} Một [[quốc gia]] và [[quần đảo]] ở [[:w:vi:Châu Đại Dương|Châu Đại Dương]], tên chính thức: [[cộng hòa|Cộng hòa]] [[Vanuatu]]; [[thủ đô]] và [[thành phố]] lớn nhất: [[Port Vila]].
===={{ĐM|holonym}}====
* {{l|en|Melanesia}}
===={{ĐM|drv}}====
* [[Vanuatuan]]
* [[ni-Vanuatu]]
==={{ĐM|see}}===
* [[:Phụ lục:Các quốc gia và vùng lãnh thổ trên thế giới]]
=={{langname|pl}}==
{{wp|lang=pl}}
==={{ĐM|etym}}===
{{ubor|pl|bi|Vanuatu}}.
==={{ĐM|pron}}===
{{pl-pr|a=LL-Q809 (pol)-Olaf-Vanuatu.wav}}
==={{ĐM|pr-noun}}===
{{pl-prop}} {{g|n}}
# {{transclude|pl|Vanuatu|id=Q686}}
===={{ĐM|drv}}====
{{col4|pl|title=Danh từ|Vanuatczyk|Vanuatka}}
==={{ĐM|see}}===
{{danh sách:quốc gia châu Đại Dương/pl}}
==={{ĐM|further}}===
* {{R:pl:PWN}}
=={{langname|bi}}==
==={{ĐM|pron}}===
{{rfp|bi}}
{{rfap|bi}}
==={{ĐM|pr-noun}}===
{{head|bi|Danh từ riêng}}
# {{transclude|bi|Vanuatu|id=Q686}}
=={{langname|pt}}==
==={{ĐM|pron}}===
{{pt-IPA|Vànuàtu}}
* {{hyphenation|pt|Va|nu|a|tu}}
==={{ĐM|pr-noun}}===
{{pt-proper noun|m}}
# {{transclude|pt|Vanuatu|id=Q686}}
=={{langname|fo}}==
==={{ĐM|etym}}===
{{bor+|fo|bi|Vanuatu}}.
==={{ĐM|pron}}===
* {{IPA4|fo|/van.uˈɑː.tu/}}
* {{rhymes|fo|ɑːtu|s=4}}
* {{hyph|fo|Van|u|a|tu}}
==={{ĐM|pr-noun}}===
{{head|fo|Danh từ riêng|g=n}}
# {{transclude|fo|Vanuatu|id=Q686}}
===={{ĐM|drv}}====
* {{l|fo|vanuatiskur}}
* {{l|fo|vanuatubúgvi}}
=={{langname|fj}}==
{{wikipedia|lang=fj}}
==={{ĐM|pron}}===
* {{IPA4|fj|/βa.nu.a.tu/}}
==={{ĐM|pr-noun}}===
{{head|fj|Danh từ riêng}}
# {{transclude|fj|Vanuatu|id=Q686}}
=={{langname|nl}}==
{{wp|lang=nl}}
==={{ĐM|etym}}===
{{bor+|nl|bi|Vanuatu}}.
==={{ĐM|pron}}===
* {{IPA4|nl|/ˌvaː.nuˈʋaː.tu/}}
* {{audio|nl|Nl-Vanuatu.ogg}}
* {{rhymes|nl|u|s=3}}
* {{hyph|nl|Van|ua|tu}}
==={{ĐM|pr-noun}}===
{{nl-proper noun|n}}
# {{transclude|nl|Vanuatu|id=Q686}}
=={{langname|hu}}==
{{wp|lang=hu}}
==={{ĐM|pron}}===
{{bor+|hu|bi|Vanuatu}}.
==={{ĐM|pron}}===
* {{hu-IPA}}
* {{rhymes|hu|tu|s=4}}
* {{hyph|hu|Va|nu|a|tu}}
==={{ĐM|pr-noun}}===
{{head|hu|Danh từ riêng}}
# {{transclude|hu|Vanuatu|id=Q686}}
===={{ĐM|decl}}====
{{hu-infl-nom|Vanuatu|o|n=sg}}{{hu-pos-tok|Vanuatu|n=sg}}
===={{ĐM|drv}}====
* {{l|hu|vanuatui}}
==={{ĐM|see}}===
{{danh sách:quốc gia châu Đại Dương/hu}}
=={{langname|nb}}==
{{wikipedia|lang=no}}
==={{ĐM|pr-noun}}===
{{head|nb|Danh từ riêng}}
# {{transclude|nb|Vanuatu|id=Q686}}
===={{ĐM|rel}}====
* {{l|nb|vanuater}}, {{l|nb|vanuatuer}}
* {{l|nb|vanuatisk}}
=={{langname|nn}}==
{{wikipedia|lang=nn}}
==={{ĐM|pr-noun}}===
{{head|nn|Danh từ riêng}}
# {{transclude|nn|Vanuatu|id=Q686}}
===={{ĐM|rel}}====
* {{l|nn|vanuatuar}}
* {{l|nn|vanuatisk}}, {{l|nn|vanuatuisk}}
=={{langname|fi}}==
{{wikipedia|lang=fi}}
==={{ĐM|etym}}===
{{der+|fi|bi|Vanuatu}}.
==={{ĐM|pron}}===
{{fi-p}}
==={{ĐM|pr-noun}}===
{{fi-proper noun}}
# {{transclude|fi|Vanuatu|id=Q686}}
===={{ĐM|decl}}====
{{fi-decl-valo|n=sg|Vanuat|||u|a}}
===={{ĐM|drv}}====
{{col4|fi
|vanuatulainen
}}
=={{langname|fr}}==
{{wikipedia|lang=fr}}
==={{ĐM|pron}}===
* {{fr-IPA}}
* {{audio|fr|LL-Q150 (fra)-GrandCelinien-Vanuatu.wav}}
==={{ĐM|pr-noun}}===
{{fr-proper noun|m}}
# {{transclude|fr|Vanuatu|id=Q686}}
=={{langname|cs}}==
{{wp|lang=cs}}
==={{ĐM|pron}}===
* {{cs-IPA}}
==={{ĐM|pr-noun}}===
{{cs-proper noun|n|indecl=1|adj=vanuatský|dem=Vanuaťan}}
# {{transclude|cs|Vanuatu|id=Q686}}
==={{ĐM|further}}===
* {{R:cs:IJP}}
=={{langname|sk}}==
==={{ĐM|etym}}===
{{bor+|sk|bi|Vanuatu}}.
==={{ĐM|pron}}===
* {{IPA4|sk|/ˈvanuatu/}}
==={{ĐM|pr-noun}}===
{{sk-proper noun}} {{g|n}}
# {{transclude|sk|Vanuatu|id=Q686}}
===={{ĐM|drv}}====
* {{l|sk|Vanuatčan|g=m}}
* {{l|sk|Vanuatčanka|g=f}}
* {{l|sk|vanuatský}}
==={{ĐM|further}}===
* {{R:sk:SDK}}
=={{langname|es}}==
{{wp|lang=es}}
==={{ĐM|etym}}===
{{bor+|es|bi|Vanuatu}}.
==={{ĐM|pron}}===
{{es-pr|+<audio:LL-Q1321 (spa)-Rodelar-Vanuatu.wav>}}
==={{ĐM|pr-noun}}===
{{es-proper noun|m}}
# {{transclude|es|Vanuatu|id=Q686}}
===={{ĐM|drv}}====
{{col4|es|vanuatuense}}
=={{langname|tr}}==
{{wp|lang=tr}}
==={{ĐM|pron}}===
* {{audio|tr|LL-Q256 (tur)-ToprakM-Vanuatu.wav}}
==={{ĐM|pr-noun}}===
{{tr-proper noun}}
# {{transclude|tr|Vanuatu|id=Q686}}
===={{ĐM|decl}}====
{{tr-decl-noun-unc
|{{PAGENAME}}
|{{PAGENAME}}'yu
|{{PAGENAME}}'ya
|{{PAGENAME}}'da
|{{PAGENAME}}'dan
|{{PAGENAME}}'nun
}}
{{tr-poss-prop-v-ou}}
===={{ĐM|drv}}====
* {{l|tr|{{pagename}}lu}}
==={{ĐM|see}}===
{{danh sách:quốc gia châu Đại Dương/tr}}
=={{langname|sv}}==
{{wikipedia|lang=sv}}
==={{ĐM|pron}}===
* {{audio|sv|LL-Q9027 (swe)-Moonhouse-Vanuatu.wav}}
==={{ĐM|pr-noun}}===
{{sv-proper noun|n}}
# {{transclude|sv|Vanuatu|id=Q686}}
===={{ĐM|rel}}====
* {{l|sv|vanuatisk}}
* {{l|sv|vanuatiska}}
* {{l|sv|vanuatuer}}
==={{ĐM|further}}===
* {{R:svenska.se}}
* {{R:Utrikes namnbok|12}}
* [https://web.archive.org/web/20250621131327/https://mesh.kib.ki.se/term/D009514/vanuatu Svensk MeSH]
=={{langname|nhn}}==
==={{ĐM|pr-noun}}===
{{head|nhn|Danh từ riêng}}
# {{transclude|nhn|Vanuatu|id=Q686}}
=={{langname|it}}==
{{wikipedia|lang=it}}
==={{ĐM|pr-noun}}===
{{it-proper noun|m}}
# {{transclude|it|Vanuatu|id=Q686}}
=={{langname|de}}==
{{wikipedia|lang=de}}
==={{ĐM|pron}}===
* {{IPA4|de|/vanuˈaːtuː/}}
* {{audio|de|De-Vanuatu.ogg}}
==={{ĐM|pr-noun}}===
{{de-proper noun|toponym}}
# {{transclude|de|Vanuatu|id=Q686}}
===={{ĐM|decl}}====
{{de-ndecl|n.sg}}
nh2xc6brnymzhgfab3l860lahshmef5
Thể loại:Ngôn ngữ với chuyển tự tự động
14
358622
2573968
2267584
2026-10-28T07:37:12Z
Hiyuune
50834
2573968
wikitext
text/x-wiki
{{auto cat}}
[[Thể loại:Tất cả ngôn ngữ]]
[[Thể loại:Mô đun chuyển tự]]
4t8xgpdwpofporlgq9krn6d6y4mmf4d
Mô đun:category tree/utilities
828
360649
2573960
2273993
2026-10-28T07:07:29Z
Hiyuune
50834
2573960
Scribunto
text/plain
local export = {}
local en_utilities_module = "Module:en-utilities"
local m_string_utilities = require("Module:string utilities")
local m_table = require("Module:table")
local umatch = m_string_utilities.match
local uupper = m_string_utilities.upper
local ulower = m_string_utilities.lower
local pattern_escape = m_string_utilities.pattern_escape
local list_to_set = m_table.listToSet
local dump = mw.dumpObject
local insert = table.insert
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
-- If true, output debugging information indicating which specs are being processed and which labels and label objects
-- generated.
local debug_add_labels_from_specs = false
--[==[
Generate labels for inflection classes. `data` is a table with the following fields:
* `labels`: The table into which the labels are written.
* `pos`: The singular part of speech, e.g. "noun".
* `stem_classes`: Table of possible stem classes and associated properties. See below.
* `principal_parts`: List of the principal part fields and descriptions. Each list element is a two-element list
consisting of {"FIELD", "DESCRIPTION"} where FIELD is the field in the element in `stem_classes` (e.g. "nom_sg",
"gen_sg", "pl", "sup") containing the detailed description of what this principal part looks like, and DESCRIPTION
is the corresponding English description of the principal part (e.g. "nominative singular").
* `mark_up_spec`: Optional function to add markup to a spec. Takes two arguments, the spec and a flag `nolink`; if the
flag is true, links should not be present in the resulting markup. The default just converts literal text enclosed
in <...> into italics.
* `make_spec_bare`: Optional function to make a spec (stem class or sortkey) free of markup. The default just converts
literal text enclosed in <...> into bare text.
* `addl`: Optional additional text to be displayed in the footer of each category page.
`stem_classes` is a table describing the various stem classes and how to format the category description of each. It is
a table with keys specifying the stem classes and values consisting of an object containing properties of the stem
class. If the stem class contains the word 'GENDER' in it, it expands into labels both for a parent category that
subsumes several genders (obtained by removing the word 'GENDER' and following whitespace) as well as gender-specific
children categories (obtained by replacing the word 'GENDER' with the genders specified in the `possible_genders`
field). The stem class can contain literal text (e.g. suffixes), which will be marked up appropriately (e.g. italicized)
in breadcrumbs and titles. The fields of the property object for a given stem class are as follows:
* `gender`: The description of the gender(s) of the stem class. If preceded by ~, the description is preceded by
"most commonly". This appears in the `additional` field of the label properties. It is not used in gender-specific
children categories; instead the gender of that category is used.
* `possible_genders`: The possible genders this class occurs in. If this is specified, the word 'GENDER' must occur in
the stem class, and gender-specific variants of the stem class (with GENDER replaced by the possible genders) are
handled along with a parent category subsuming all genders.
* `PRINCIPAL_PART`: The ending for the specified principal part. Use <...> to enclose literal Latin-script text (e.g.
suffixes), which will be italicized. There will be one field for each principal part listed in the `principal_parts`
table described above.
* `GENDER_PRINCIPAL_PART`: The ending for the GENDER variant of the specified principal part. If not specified, the
value of `PRINCIPAL_PART` is used.
* `breadcrumb`: The breadcrumb for the category, appearing in the trail of breadcrumbs at the top of the page. If this
stem has gender-specific variants, the breadcrumb specified here is used only for the parent category, while the
gender-specific child categories use the gender as the breadcrumb. If not specified, it defaults to `sortkey`. If that
is also not specified, or if the breadcrumb has the value "+", the stem class (without the word 'GENDER') is used.
(Use "+" when a sortkey is specified but the stem class should be used as the breadcrumb.)
* `parent`: The parent category or categories. If specified, the actual category label is formed by appending the part
of speech (e.g. "nouns"). Defaults to "POS by inflection type" where POS is the part of speech. Note that
gender-specific child categories do not use this, but always have the gender-subsuming parent stem class category as
their parent.
* `sortkey`: The sort key used for sorting this category among its parent's children. Defaults to the stem class
(without the word 'GENDER'). Note that gender-specific child categories do nto use this, but always use the gender
as the sort key.
* `addl`: Optional additional text to be displayed in the footer of the category page.
* `GENDER_addl`: Optional additional text to be displayed in the footer of a gender-specific category page, defaulting
to `addl`. Use the value `false` to cancel out a non-gender-specific value.
]==]
function export.add_inflection_labels(data)
for _, reqfield_spec in ipairs {
{"labels", "table of labels"},
{"pos", "singular part of speech"},
{"stem_classes", "table of possible stem classes and associated properties"},
{"principal_parts", "list of principal part fields and names"},
} do
local reqfield, gloss = unpack(reqfield_spec)
if not data[reqfield] then
error(("Internal error: Missing field '%s', which should containing the %s"):format(reqfield, gloss))
end
end
local function default_mark_up_spec(spec, _nolink)
return (spec:gsub("<(.-)>", "''%1''"))
end
local function default_make_spec_bare(spec)
return (spec:gsub("<(.-)>", "%1"))
end
local function mark_up_spec(spec, nolink)
return (data.mark_up_spec or default_mark_up_spec)(spec, nolink)
end
local function make_spec_bare(spec)
return (data.make_spec_bare or default_make_spec_bare)(spec)
end
local plpos = require(en_utilities_module).pluralize(data.pos)
for full_infl, spec in pairs(data.stem_classes) do
local subgenders = spec.possible_genders
-- Get the stem type.
local infl
if subgenders then
if not full_infl:find("GENDER") then
error(("Internal error: Declension spec '%s' needs to have the word 'GENDER' in it, in all caps"):format(full_infl))
end
infl = full_infl:gsub("GENDER ", "")
else
infl = full_infl
end
-- Get the breadcrumb.
local breadcrumb = spec.breadcrumb or spec.sortkey or "+"
if breadcrumb == "+" then
breadcrumb = infl
end
-- Generate the parents.
local parents = {}
local spec_parents = spec.parent
if type(spec_parents) == "string" then
spec_parents = {spec_parents}
end
local parent_sort = make_spec_bare(spec.sortkey or infl)
if spec_parents then
for _, parent in ipairs(spec_parents) do
table.insert(parents, {name = make_spec_bare(parent) .. " " .. plpos, sort = parent_sort})
end
else
table.insert(parents, {name = plpos .. " by inflection type", sort = parent_sort})
end
-- Generate the additional text, including principal part descriptions and footer.
local function create_addl(gender_spec, subgender_prefix)
local addl_parts = {}
local function ins(txt)
table.insert(addl_parts, txt)
end
local function insert_header(gender_spec)
if gender_spec then
local most_commonly, gender = gender_spec:match("^(~)(.*)$")
most_commonly = most_commonly and "most commonly " or ""
gender = gender or gender_spec
ins(("These %s are %s%s, typically with the following endings:\n"):format(plpos, most_commonly,
mark_up_spec(gender)))
else
ins(("These %s typically have the following endings:\n"):format(plpos))
end
end
local function process_ending(field, desc, is_last)
local ending_spec = spec[subgender_prefix .. field] or spec[field]
if not ending_spec then
error(("Internal error: for inflection '%s', field '%s' for principal part '%s' is missing"):format(
full_infl, field, desc))
end
return ("* in the %s: %s%s"):format(desc, mark_up_spec(ending_spec), is_last and "." or ";\n")
end
local function insert_principal_part_info()
for i, ppart_spec in ipairs(data.principal_parts) do
local ppart_field, ppart_desc = unpack(ppart_spec)
ins(process_ending(ppart_field, ppart_desc, i == #data.principal_parts))
end
end
insert_header(gender_spec)
insert_principal_part_info()
local spec_addl = spec[subgender_prefix .. "addl"]
if spec_addl == nil then
spec_addl = spec.addl
end
if data.addl or spec_addl then
local footer_parts = {}
if data.addl then
table.insert(footer_parts, mark_up_spec(data.addl))
end
if spec_addl then
table.insert(footer_parts, mark_up_spec(spec_addl))
end
ins("\n" .. table.concat(footer_parts, " "))
end
return table.concat(addl_parts)
end
data.labels[make_spec_bare(infl) .. " " .. plpos] = {
description = "{{{langname}}} " .. mark_up_spec(infl) .. " " .. plpos .. ".",
displaytitle = "{{{langname}}} " .. mark_up_spec(infl, "nolink") .. " " .. plpos,
additional = create_addl(spec.gender, ""),
breadcrumb = mark_up_spec(breadcrumb, "nolink"),
parents = parents,
}
if subgenders then
for _, subgender in ipairs(subgenders) do
local gender_infl = full_infl:gsub("GENDER", subgender)
data.labels[make_spec_bare(gender_infl) .. " " .. plpos] = {
description = "{{{langname}}} " .. mark_up_spec(gender_infl) .. " " .. plpos .. ".",
displaytitle = "{{{langname}}} " .. mark_up_spec(gender_infl, "nolink") .. " " .. plpos,
additional = create_addl(subgender, subgender .. "_"),
breadcrumb = subgender,
parents = {{
name = make_spec_bare(infl) .. " " .. plpos,
sort = subgender,
}},
}
end
end
end
end
local recognized_label_fields = list_to_set {
"additional",
"breadcrumb",
"breadcrumb_base",
"breadcrumb_key",
"breadcrumb_and_first_sort_base",
"breadcrumb_and_first_sort_key",
"umbrella",
"umbrella_parents",
}
--[==[
Add category labels to `labels` from the specs in `specs`. The intent is to allow easy specification of iteration over
part-of-speech and similar variables to construct multiple similar labels. `specs` is a list of labels specs, each of
which is a table with three positional fields and additional named fields. The positional fields are as follows:
* `[1]` = the label itself;
* `[2]` = the description, without any preceding `{{{langname}}}` or following period;
* `[3]` = the value of `parents`.
If the language name (i.e. `{{{langname}}}`) appears other than at the beginning of the description, include it in the
description and it will not be prepended. Note that in such a case, `{{{langname}}}` should be in an appositive
construction, e.g. `{{{langname}}} words`, because when constructing the umbrella description, a non-initial occurrence
of `{{{langname}}}` will be replaced with `specific-language`.
In addition, the table can contain named fields. There are three types of named fields:
# Fields referring to other properties of the label. These are such as `additional`, `breadcrumb`, `breadcrumb_base`,
`breadcrumb_key`, `umbrella`, etc. These will be stored directly the label, but will be processed for variable
references (see below).
# ''Iteration variables''. Any string anywhere in a positional or named field can contain ''variable references'', e.g.
`{pos}`, which refer to variables that will be iterated over. The values to be iterated over are specified by
''variable-list'' fields, e.g. `pos = {"nouns", "verbs", "adjectives"}`. If there are multiple iteration variables,
each will be iterated over, meaning that e.g. if there are three variables with respectively K, M and N values, there
will be K*M*N labels. Each value of an iteration variable list is either a string (the actual value) or a table where
the `[1]` (the first positional field) is the value and named fields specify values of ''ancillary'' variables, which
are not iterated over but which are appropriately substituted when the variable being iterated over takes the
specified value. For example, the field
`sufpos = {{"prefixes", begin_end = "beginnings"}, {"suffixes", begin_end = "endings"}}` specifies that the iteration
variable `sufpos` will be iterated over with the values `prefixes` and `suffixes`, and when this happens, the
ancillary variable `begin_end` will have the value `beginnings` when `sufpos` has the value `prefixes`, and will have
the value `endings` when `sufpos` has the value `suffixes`. Ancillary variables can be referred to in the same fashion
as iteration variables, e.g. `{begin_end}` and `{sufpos}`.
# Ancillary default values. Fields beginning with `default_` supply the default value for ancillary variables whose
value is not explicitly given for a specific value of an iteration variable. For example, if the iteration variable
`pos` is specified using `pos = {"nouns", "proper nouns", {"suffixes", posdesc = "suffixes that form nouns"}}`, the
ancillary variable `posdesc` will have the value `suffixes that form nouns` when `pos` has the value `suffixes`, but
is otherwise unspecified. The value for the other iteration values is specified through a default, such as
`default_posdesc = "{pos}"`, which says that the value should be `"{pos}"` (i.e. the same as the iteration value) for
other iteration values. Defaults can also be specified at top level using the `defaults` field. If an ancillary
variable is left unspecified, and there is no default value either in the label spec or at top level, an error will
be thrown if/when a reference to the specified ancillary variable occurs.
]==]
function export.add_labels_from_specs(labels, specs)
for _, spec in ipairs(specs.specs) do
if debug_add_labels_from_specs then
mw.log(("Processing spec: %s"):format(dump(spec)))
end
local label_spec = spec[1]
local description_spec = spec[2]
if not description_spec:find("{{{langname}}}") then
description_spec = "{{{langname}}} " .. description_spec
end
description_spec = description_spec .. "."
local parents_spec = spec[3]
local additional_label_properties = {}
local ancillary_defaults = {}
local iteration_vars = {}
for k, v in pairs(spec) do
if type(k) == "number" then
if k >= 1 and k <= 3 then
-- OK
else
error(("Internal error: Unrecognized positional field %s=%s in spec %s"):format(k, dump(v), dump(spec)))
end
elseif recognized_label_fields[k] then
additional_label_properties[k] = v
elseif k:find("^default_") then
local ancvar = k:gsub("^default_", "")
ancillary_defaults[ancvar] = v
else
if not v[1] then
error(("Internal error: Iteration variable %s=%s is not in list form in spec %s"):format(k, dump(v), dump(spec)))
end
insert(iteration_vars, {k, v})
end
end
local function expand_variables_in_string(str, iteration_values)
if not str:find("{") then
return str
end
str = str:gsub("{{{", "\1"):gsub("}}}", "\2"):gsub("{{", "\3"):gsub("}}", "\4")
if str:find("{") then
str = str:gsub("{([^{}]*)}", function(var)
if iteration_values[var] then
return expand_variables_in_string(iteration_values[var], iteration_values)
else
error(("Internal error: Unable to expand variable {%s} in %s in spec %s"
):format(var, dump(str), dump(spec)))
end
end)
end
str = str:gsub("\1", "{{{"):gsub("\2", "}}}"):gsub("\3", "{{"):gsub("\4", "}}")
return str
end
local function expand_variables(val, iteration_values)
if type(val) == "string" then
return expand_variables_in_string(val, iteration_values)
elseif type(val) ~= "table" then
return val
else
local newval = {}
for k, v in pairs(val) do
newval[k] = expand_variables(v, iteration_values)
end
return newval
end
end
local function construct_iteration_values(explicit_values)
local iteration_values = {}
local ancillary_values = {}
for k, v in pairs(explicit_values) do
if type(v) == "string" then
iteration_values[k] = v
else
iteration_values[k] = v[1]
for kk, vv in pairs(v) do
if type(kk) == "number" then
if kk == 1 then
-- OK
else
error(("Internal error: Unrecognized positional field %s=%s in iteration value %s=%s in spec %s"
):format(kk, dump(vv), k, dump(v), dump(spec)))
end
elseif ancillary_values[kk] then
error(("Trying to set ancillary variable %s=%s in iteration value %s=%s and encountered already-set variable; presumably set in different iteration variables; spec %s"
):format(kk, dump(vv), k, dump(v), dump(spec)))
elseif type(vv) ~= "string" then
error(("Internal error: Ancillary value %s=%s not a string in iteration value %s=%s; spec %s"
):format(kk, dump(vv), k, dump(v), dump(spec)))
else
ancillary_values[kk] = vv
end
end
end
end
local merged_ancillary_values = {}
-- First merge the overall defaults.
if specs.defaults then
for k, v in pairs(specs.defaults) do
if type(v) ~= "string" then
error(("Internal error: Top-level ancillary default %s=%s not a string"):format(k, dump(v)))
end
merged_ancillary_values[k] = v
end
end
-- Now merge the per-spec defaults.
for k, v in pairs(ancillary_defaults) do
if type(v) ~= "string" then
error(("Internal error: Ancillary default %s=%s not a string; spec %s"):format(k, dump(v), dump(spec)))
end
merged_ancillary_values[k] = v
end
-- Now merge the per-iteration-value values; we already checked above that they are strings.
for k, v in pairs(ancillary_values) do
merged_ancillary_values[k] = v
end
-- Now merge the ancillary values into the iteration values.
for k, v in pairs(merged_ancillary_values) do
if iteration_values[k] then
error(("Trying to set ancillary variable %s=%s in iteration values structure and encountered already-set variable with value %s; naming clash between iteration and ancillary variables; spec %s"
):format(k, dump(v), dump(iteration_values[k]), dump(spec)))
end
iteration_values[k] = v
end
return iteration_values
end
local function add_label(explicit_values)
local iteration_values = construct_iteration_values(explicit_values)
local label_obj = {
description = expand_variables(description_spec, iteration_values),
parents = expand_variables(parents_spec, iteration_values),
}
for k, v in pairs(additional_label_properties) do
label_obj[k] = expand_variables(v, iteration_values)
end
local label = expand_variables(label_spec, iteration_values)
if debug_add_labels_from_specs then
mw.log(("Adding label %s = %s"):format(dump(label), dump(label_obj)))
end
labels[label] = label_obj
end
local iteration_indices = {}
local iteration_lengths = {}
local num_vars = #iteration_vars
if num_vars == 0 then
add_label({})
else
for i = 1, num_vars do
insert(iteration_indices, 1)
insert(iteration_lengths, #iteration_vars[i][2])
end
-- Loop over the Cartesian product of all iteration variables. In each iteration through the loop, we
-- process one set of indices.
while true do
local explicit_values = {}
for i, iteration_var in ipairs(iteration_vars) do
local var, values = unpack(iteration_var)
explicit_values[var] = values[iteration_indices[i]]
end
add_label(explicit_values)
-- Now increment the iteration indices using the odometer algorithm.
local next_var_index = 1
while true do
iteration_indices[next_var_index] = iteration_indices[next_var_index] + 1
if iteration_indices[next_var_index] > iteration_lengths[next_var_index] then
iteration_indices[next_var_index] = 1
next_var_index = next_var_index + 1
if next_var_index > num_vars then
break
end
else
break
end
end
if next_var_index > num_vars then
break
end
end
end
end
end
--[==[
Set `breadcrumb_base` on the labels in `label` by chopping off the parent (or the parent's parent, etc.) from the
beginning or end of the label, if possible. This will not override existing breadcrumbs (using any of the breadcrumb or
breadcrumb-related settings). `addl_terms_to_remove_for_breadcrumb_base` is a list of additional terms that will be
chopped off of either end of the label to form the `breadcrumb_base`. (For example,
[[Module:category tree/affixes and compounds]] specifies { {"compounds"}} because the parent is {"compound terms"} but
many children end in {"compounds"}, and [[Module:category tree/entry maintenance]] specifies { {"entries", "terms"}},
which occurs at the beginning of many entry-maintenance categories.) `addl_terms_to_remove_for_sort` is a list of
additional terms to remove from the beginning of the computed `breadcrumb_base` to form the value of `sort` in the
first parent. If such a term is removed, `sort` will be set in the first parent if not already set, which will override
the sort base coming from `breadcrumb_base`. (For example, [[Module:category tree/entry maintenance]] specifies
{ {"with"}}. The effect of this combined with the above setting is that a label like
{"entries with incorrect language header"} with first parent {"entry maintenance"} will have a breadcrumb
{"with incorrect language header"} and a sort key {"incorrect language header"}.
]==]
function export.add_partial_breadcrumbs(labels_cats, addl_terms_to_remove_for_breadcrumb_base, addl_terms_to_remove_for_sort)
for label, data in pairs(labels_cats) do
if data.breadcrumb == nil and data.breadcrumb_key == nil and data.breadcrumb_base == nil and
data.breadcrumb_and_first_sort_key == nil and data.breadcrumb_and_first_sort_base == nil then
local pardata = data
local function make_anycase_term_to_remove(term)
local first, rest = umatch(term, "^(.)(.*)$")
return ("[%s%s%s]%s"):format(first, uupper(first), ulower(first), pattern_escape(rest))
end
-- Check a term against the beginning and end of the label and if found, set the breadcrumb base
-- accordingly. Return true if the breadcrumb_base was set, otherwise false. If additional terms to check
-- for sortkey removal are given, we check those terms at the beginning of the chopped breadcrumb_base or
-- the label (regardless of whether we were able to remove any terms from the breadcrumb), and if we were
-- able to remove any terms, attempt to set the sortkey.
local function check_breadcrumb_base_term(term_to_remove)
local breadcrumb_base
local term_to_remove_pattern = make_anycase_term_to_remove(term_to_remove)
-- Check the end of the label for the term to remove.
breadcrumb_base = umatch(label, ("^(.-) %s$"):format(term_to_remove_pattern))
if not breadcrumb_base then
-- If not found, check the beginning of the label for the term to remove.
breadcrumb_base = umatch(label, ("^%s (.-)$"):format(term_to_remove_pattern))
end
local breadcrumb_base_set = not not breadcrumb_base
breadcrumb_base = breadcrumb_base or label
-- If additional terms to remove to form the sortkey, do it now.
if addl_terms_to_remove_for_sort then
for _, addl_term in ipairs(addl_terms_to_remove_for_sort) do
local sort = umatch(breadcrumb_base, ("^(.-) %s$"):format(make_anycase_term_to_remove(addl_term)))
if sort then
-- Attempt to set the `sort` on the first parent, but don't override an existing value.
local parents = data.parents
if type(parents) == "string" or type(parents) == "table" and not parents[1] then
parents = {parents}
end
if parents and parents[1] then
local parent = parents[1]
if type(parent) == "string" then
parent = {name = parent}
end
if type(parent) == "table" and not parent.sort then
parent.sort = sort
parents[1] = parent
data.parents = parents
end
end
break
end
end
end
if breadcrumb_base_set then
data.breadcrumb_base = breadcrumb_base
return true
end
return false
end
while true do
-- Loop over first parents and first parents of first parents until there are no more.
local parents = pardata.parents
if type(parents) == "string" or type(parents) == "table" and not parents[1] then
parents = {parents}
end
if not parents or not parents[1] then
break
end
local parent = parents[1]
if type(parent) == "table" then
parent = parent.name
end
if type(parent) == "string" then
if check_breadcrumb_base_term(parent) then
break
end
pardata = labels_cats[parent]
if not pardata then
break
end
else
break
end
end
-- Also check additional specified terms to remove from the label to form the breadcrumb.
if addl_terms_to_remove_for_breadcrumb_base then
for _, addl_term in ipairs(addl_terms_to_remove_for_breadcrumb_base) do
if check_breadcrumb_base_term(addl_term) then
break
end
end
end
end
end
end
-- FIXME: Older entry point.
export.add_partial_breadcrumbs_to_labels = export.add_partial_breadcrumbs
--[==[
Finalize the labels in `labels` according to the specs in `data`. This does two things:
# Set `breadcrumb_base` on the labels in `label` by chopping off the parent (or the parent's parent, etc.) from the
beginning or end of the label, if possible. This will not override existing breadcrumbs (using any of the breadcrumb
or breadcrumb-related settings). `data.addl_terms_to_remove_for_breadcrumb_base` is a list of additional terms that
will be chopped off of either end of the label to form the `breadcrumb_base`. (For example,
[[Module:category tree/affixes and compounds]] specifies { {"compounds"}} because the parent is {"compound terms"} but
many children end in {"compounds"}, and [[Module:category tree/entry maintenance]] specifies { {"entries", "terms"}},
which occurs at the beginning of many entry-maintenance categories.) `data.addl_terms_to_remove_for_sort` is a list of
additional terms to remove from the beginning of the computed `breadcrumb_base` to form the value of `sort` in the
first parent. If such a term is removed, `sort` will be set in the first parent if not already set, which will
override the sort base coming from `breadcrumb_base`. (For example, [[Module:category tree/entry maintenance]]
specifies { {"with"}}. The effect of this combined with the above setting is that a label like
{"entries with incorrect language header"} with first parent {"entry maintenance"} will have a breadcrumb
{"with incorrect language header"} and a sort key {"incorrect language header"}.
# Add an `umbrella_parents` key if not already present and `data.default_umbrella_parents` is specified.
]==]
function export.finalize_labels(labels, data)
data = data or {}
export.add_partial_breadcrumbs(labels,
data.addl_terms_to_remove_for_breadcrumb_base, data.addl_terms_to_remove_for_sort)
if data.default_umbrella_parents then
-- Add 'umbrella_parents' key if not already present.
for label, labdat in pairs(labels) do
if not labdat.umbrella_parents then
labdat.umbrella_parents = data.default_umbrella_parents
end
end
end
end
function export.finalize_raw_categories(raw_categories, data)
data = data or {}
export.add_partial_breadcrumbs(raw_categories,
data.addl_terms_to_remove_for_breadcrumb_base, data.addl_terms_to_remove_for_sort)
end
return export
d234ry6bei5t65m2qwyp6geh1342cfd
đá lưỡi
0
376181
2573894
2313071
2026-10-27T14:14:48Z
TheHighFighter2
42988
/* Dịch */
2573894
wikitext
text/x-wiki
=={{langname|vi}}==
[[Tập tin:Beijo.jpg|nhỏ|Cặp đôi trẻ đang đá lưỡi.]]
==={{ĐM|etym}}===
{{com+|vi|đá|lưỡi}}.
==={{ĐM|pron}}===
{{vi-IPA}}
==={{ĐM|verb}}===
{{vi-verb}}
# {{lb|vi|slang}} [[hôn|Hôn]] sâu vào [[miệng]], lưỡi chạm và quấn lấy nhau; [[w:Hôn kiểu Pháp|hôn theo kiểu Pháp]].
===={{ĐM|trans}}====
{{trans-top|Hôn sâu vào miệng}}
* {{dịch|en}}: {{t+|en|French kiss}}, {{t+|en|french}}
* {{dịch|fr}}: {{qualifier|thông tục}} {{t+|fr|rouler un patin}}, {{t+|fr|rouler une pelle}}, {{t+|fr|rouler une galoche}}, {{t+|fr|galocher}}, {{qualifier|Québec}} {{t+|fr|frencher}}
{{trans-bottom}}
===={{ĐM|rel}}====
{{col|vi|cháo lưỡi}}
4ycg0v852pbadx3w285enc69ohfildu
áo ngực
0
376409
2573927
2313853
2026-10-27T16:06:11Z
TheHighFighter2
42988
/* Dịch */
2573927
wikitext
text/x-wiki
=={{langname|vi}}==
{{wp}}
[[Tập tin:Moderner Schalen-BH, schwarz.jpg|nhỏ|Áo ngực]]
==={{ĐM|etym}}===
Từ {{compound|vi|áo|ngực}}.
==={{ĐM|pron}}===
{{vi-IPA}}
==={{ĐM|etym}}===
{{vi-noun|cls=[[cái]], [[chiếc]]}}
# Loại [[đồ lót]] [[nữ]], mặc để [[che]] và [[nâng đỡ]] [[ngực]].
#: {{syn|vi|áo lót|áo con|áo chíp|áo vú|nịt vú|xu chiêng|coóc xê}}
===={{ĐM|trans}}====
{{trans-top|Loại đồ lót}}
* {{dịch|af}}: {{t+|af|bra}}, {{t|af|buustelyfie}}
* {{dịch|sq}}: {{t|sq|gjimbajtëse|f}}, {{t|sq|sutienë|f}}
* {{dịch|en}}: {{t+|en|bra}}, {{t+|en|brassiere}}
* Tiếng Apache:
*: {{dịch|apw}}: {{t|apw|ibeʼbenastʼiʼí}}
* {{dịch|ar}}: {{t|ar|حَمَّالَة صَدْر|f|tr=ḥammālat ṣadr}}, {{t|ar|مِشَدّ صَدْر|m}}
*: {{dịch|arz}}: {{t|arz|ستيان|m|tr=sutyān}}
*: {{dịch|acw}}: {{t|acw|سُتيانة|f|tr=sutyāna}}
*: {{dịch|ajp}}: {{t|ajp|ستيانة|f|tr=sityāne}}
* {{dịch|hy}}: {{t+|hy|կրծկալ}}, {{t+|hy|լիֆչիկ}}
* {{dịch|ast}}: {{t|ast|apertador|m}}, {{t|ast|sostén|m}}
* {{dịch|az}}: {{t|az|lifçik}}, {{t|az|büstqalter}}
* {{dịch|ba}}: {{t|ba|күкрәксә}}
* {{dịch|eu}}: {{t+|eu|bularretako}}
* {{dịch|be}}: {{t|be|бюстга́льтар|m}}, {{t|be|ста́нік|m}}, {{t|be|лі́фчык|m}}
* {{dịch|bn}}: {{t+|bn|বক্ষবন্ধনী}}, {{t|bn|কাচুলি}}, {{t|bn|ব্রা}}
* {{dịch|bg}}: {{t+|bg|сутие́н|m}}
* {{dịch|ca}}: {{t+|ca|sostenidors|m-p}}
* {{dịch|ceb}}: {{t|ceb|bra}}
* {{dịch|he}}: {{t+|he|חזיה|f|tr=khaziyá|alt=חֲזִיָה \ חזייה}}
* {{dịch|da}}: {{t+|da|brystholder}}, {{t+|da|bh|c}}
* {{dịch|de}}: {{t+|de|Büstenhalter|m}}, {{qualifier|viết tắt}} {{t+|de|BH|m}}, {{t|de|Busenhalter|m}}
* {{dịch|et}}: {{t+|et|rinnahoidja}}
* {{dịch|fo}}: {{t|fo|brósthaldari|m}}
* {{dịch|ka}}: {{t|ka|ლიფი}}, {{t+|ka|აზღუდი}}, {{t|ka|ბიუსტჰალტერი}}
* {{dịch|nl}}: {{t+|nl|bustehouder|m}}, {{t+|nl|beha|m}}
* {{dịch|hi}}: {{t|hi|ब्रेजिअर}}, {{t|hi|ब्रा}}, {{t+|hi|अँगिया|f}}, {{t|hi|ब्रेजियर}}
* {{dịch|hu}}: {{t+|hu|melltartó}}, {{t+|hu|mt}} {{qualifier|viết tắt}}
* {{dịch|el}}: {{t+|el|στηθόδεσμος|m}}
*: {{dịch|grc}}: {{t|grc|στρόφιον|n}}
* {{dịch|is}}: {{t|is|brjóstahaldari}}
* {{dịch|id}}: {{t+|id|kutang}}, {{t+|id|beha}}
* {{dịch|ga}}: {{t+|ga|cíochbheart|m}}
* {{dịch|kk}}: {{t|kk|емшекқап}}
* {{dịch|km}}: {{t|km|អាវទ្រនាប់}}, {{t|km|អាវស៊ូជាង}}
* {{dịch|ky}}: {{t|ky|лифчик}}, {{t|ky|бюстгальтер}}
* {{dịch|lo}}: {{t|lo|ເສື້ອຊ້ອນ}}, {{t|lo|ອ້ຽງ}}
* {{dịch|la}}: {{t|la|strophium|n}}, {{t|la|cestus|m}}
* {{dịch|lv}}: {{t|lv|krūšturis|m}}
* {{dịch|lt}}: {{t|lt|liemenėlė|f}}
* {{dịch|lb}}: {{t|lb|Soutien|m}}
* {{dịch|mk}}: {{t|mk|гра́дник|m}}
* {{dịch|ms}}: {{t|ms|bra}}, {{t+|ms|coli}}
* {{dịch|mt}}: {{t|mt|riċipettu|m}}
* {{dịch|gv}}: {{t|gv|coodagh keeagh}}
* {{dịch|mi}}: {{t|mi|pari}}, {{t|mi|kopeū}}
* {{dịch|my}}: {{t+|my|ဘရာစီယာ}}
* {{dịch|mn}}:
*: {{t|mn|хөхний даруулга}} {{t|mn|ᠬᠥᠬᠦᠨ ᠦ ᠳᠠᠷᠤᠭᠤᠯᠭᠠ|}} {{qualifier|chính thức}}
*: {{t+|mn|лифчик|m}} {{t|mn|ᠯᠢᠹᠼᠢᠺ|}} {{qualifier|phổ biến}}
*: {{t+|mn|хөхөвч|m}} {{t|mn|ᠬᠥᠬᠦᠪᠴᠢ|}} {{qualifier|tiêu chuẩn tại Nội Mông}}
* {{dịch|ja}}: {{t+|ja|ブラジャー|tr=burajā}}, {{t+|ja|ブラ|tr=bura}}, {{t|ja|乳押さえ|tr=ちちおさえ, chichiosae}} {{qualifier|không còn dùng}}
* {{dịch|fr}}: {{t+|fr|soutien-gorge|m}}, {{qualifier|phổ biến}} {{t+|fr|soutif}}, {{qualifier|lỗi thời}} {{t+|fr|brassière|f}}
* {{dịch|fi}}: {{t+|fi|rintaliivit|p}}
* {{dịch|cs}}: {{t+|cs|podprsenka|f}}
* {{dịch|tl}}: {{t+|tl|bra}}
* {{dịch|tg}}: {{t+|tg|лифчик}}
* {{dịch|ta}}: {{t+|ta|மார்புக்கச்சை}}
* {{dịch|tt}}: {{t+|tt|күкрәкчә}}
* {{dịch|te}}: {{t+|te|బ్రా}}
* {{dịch|th}}: {{t+|th|ยกทรง}}
* {{dịch|ko}}: {{t+|ko|브래지어}}, {{t+|ko|브라}}
* {{dịch|zh}}:
*: {{dịch|nan-hbl}}: {{t|nan-hbl|奶帕仔|tr=ling-phè-á / ni-phè-á}}
*: {{dịch|cmn}}: {{t+|cmn|奶罩|tr=nǎizhào}}, {{t+|cmn|胸罩|tr=xiōngzhào}}, {{t+|cmn|乳罩|tr=rǔzhào}}
* {{dịch|uk}}: {{t+|uk|бюстга́льтер|m}}, {{t+|uk|лі́фчик|m}}
* {{dịch|uz}}: {{t+|uz|siynaband}}, {{t+|uz|ko'krakpech}}, {{t+|uz|lifchik}}, {{t+|uz|byustgalter}}
* {{dịch|it}}: {{t+|it|reggiseno|m}}, {{t+|it|reggipetto|m}}
* {{dịch|yi}}: {{t+|yi|סטאַניק}}
* {{dịch|zza}}: {{t+|zza|sıtyen}}
{{trans-bottom}}
{{C|vi|Đồ lót}}
pxr5xxbuujxtj3e00tz8x6t03oaakc5
Mô đun:bibliography/ref
828
385205
2573888
2365264
2026-10-27T14:05:45Z
Hiyuune
50834
2573888
Scribunto
text/plain
local export = {}
local tag_text = require("Module:script utilities").tag_text
local get_lang = require("Module:languages").getByCode
local shared = require("Module:bibliography/shared")
local links = require("Module:bibliography/shared/links").ref
-- Understands if a number refers to one or multiple instances of something (e.g. pages, sections) and pluralises the label accordingly.
local function format_number_label(value, label)
if type(label) == "table" then
label = label[(value:find("–") or value:find("f%.$") or value:find(", ")) and 2 or 1]
end
return label..value
end
-- Handles the set_page, set_id, etc. functions within page_url, entry_url, etc. nodes.
local function apply_param_setter(arg_name, setter, input_data)
local input_value = input_data[arg_name]
-- simple addition or subtraction
if type(setter) == "number" and input_value then
return input_value + setter
-- operations as strings
-- TODO: needs to be stricter with invalid inputs
elseif type(setter) == "string" and input_value then
-- separate the different actions within the setter string (operations, alterations)
local operations = setter:match("^([0-9+%-*/ ]*)")
local esc_operations = "^"..operations:gsub("([^%w ])", "%%%1")
local alterations = setter:match(esc_operations.."(>[0-9+%-*/ >]+)")
assert(alterations or not setter:match(esc_operations.."."), "Invalid string as set_page: "..setter)
-- apply arithmetic operations
local function calculate_operation(num, operations)
assert(operations:gsub("([+%-*/]) *(%d+)", function(operation, operation_num)
num = ({
["+"] = function(a, b) return a + b end,
["-"] = function(a, b) return a - b end,
["*"] = function(a, b) return a * b end,
["/"] = function(a, b) return math.floor(a / b) end,
})[operation](num, tonumber(operation_num))
return ""
end):match("^ *$"), "Invalid operation in set_page: "..setter)
return num
end
input_value = calculate_operation(input_value, operations)
-- apply alterations (e.g. for missing or duplicate pages)
if alterations then
assert(alterations:gsub("> *(%d+) *([0-9+%-*/ ]*)", function(alteration_from, alteration_operation)
if input_data[arg_name] > tonumber(alteration_from) then
input_value = calculate_operation(input_value, alteration_operation)
end
return ""
end):match("^ *$"), "Invalid alteration in set_page: "..setter)
end
return input_value
-- full function for complex operation
elseif type(setter) == "function" then
return setter(input_data)
end
end
local is_numerical = {
["page"] = true,
}
-- Override t's values with p's.
local function sum_table(t, p)
if not p then
return t
elseif type(t) == "table" and type(p) == "table" then
for k, v in pairs(p) do
t[k] = v
end
return t
else
return p
end
end
local function retrieve_fields(info_types, source, input_data)
-- Overwrite or sum together with the fields specified in the current node, if present.
for info_type_key, info_type in pairs(info_types) do
local current_given_value = source[info_type.field_name or info_type_key]
-- If the `sum_table` field is true, then sum the tables together.
-- If not, simply overwrite them.
if info_type.sum_table then
info_types[info_type_key].value = sum_table(info_type.value, current_given_value)
else
info_types[info_type_key].value = current_given_value or info_type.value
end
end
-- Checks in the node's children. This kind of treatment allows for both
-- edition nodes within volume nodes and volume nodes within edition nodes.
for _, arg in ipairs {"volume", "edition"} do
local source_args = source[arg.."s"]
if source_args then
if input_data[arg] then
-- Retrieve element.
local item = source_args[input_data[arg]]
-- Handle aliases.
if type(item) == "string" then
item = source_args[item]
end
if item then
-- Search for the fields in the newly found node.
info_types = retrieve_fields(info_types, item, input_data)
else
-- Item not in the list.
end
end
break
end
end
return info_types
end
-- Recursive function that searches for the link data inside the source node.
-- The `url_config` is the table containing information about how to make the URL, while `link_node` is
-- the table with the links that are displayed on the bibliography page which are used only in case that
-- the links aren't explicit in `url_config`, e.g. the common `{ gb = true }` syntax. Both `url_config` and
-- `link_node` can either be about the entire source or volume/edition-dependent.
local function get_url_config_and_link_node(source, input_data, param_name_url)
local data = retrieve_fields({
url_config = {
sum_table = true,
field_name = param_name_url,
},
link_node = {
sum_table = true,
field_name = "link",
},
}, source, input_data)
return data.url_config.value, data.link_node.value
end
-- Duplicate the table, and convert string to integers whenever needed.
local function numerify_fields(const_data, source)
local input_data = {}
for k, v in pairs(const_data) do
if not source.unprocess_numbers and is_numerical[k] and type(v) == "string" and v:match("^%d+") then
v = tonumber(v:match("^%d+"))
end
input_data[k] = v
end
return input_data
end
-- Handles page_url, entry_url, etc.
local function param_url(source, input_data, param_name)
-- TODO: Is this check needed? Seems impossible to occur.
if not input_data then return nil end
input_data = numerify_fields(input_data, source)
-- For an explanation of what `url_config` and `link_node` are, see above.
local url_config, link_node = get_url_config_and_link_node(source, input_data, param_name.."_url")
-- If there is no url_config, get out of the function.
if not url_config or (type(url_config) == "table" and next(url_config) == nil) then
return nil
end
-- Utility function that recovers the ID.
local function resolve_value(site_key)
local value = url_config[site_key]
-- recycle data from stable links
if value == true then
assert(link_node and link_node[site_key], 'Cannot use the "'..site_key..' = true" syntax because I could not find it defined in as a field of "link"')
value = link_node[site_key]
end
if not value then error("Could not retrieve the "..site_key.." data for "..param_name) end
-- if a function, runs it
if type(value) == "string" then
return value
elseif type(value) == "function" then
return value(input_data)
end
error("Could not define "..param_name.." URL from the site "..site_key)
end
if type(url_config) ~= "table" then
url_config = { url = url_config }
else
-- Resolve the values before applying the set parameters.
for key, _ in pairs(links) do
if url_config[key] then
url_config[key] = resolve_value(key)
if not url_config[key] then return nil end
break
end
end
if type(url_config.pref) == "function" then
url_config.pref = url_config.pref(input_data)
end
-- applies setters
for set_arg, set_value in pairs(url_config) do
if set_arg:match("^set_") then
set_arg = set_arg:gsub("^set_", "")
input_data[set_arg] = apply_param_setter(set_arg, set_value, input_data)
end
end
end
-- percent encodes entry name
if input_data.entry then
input_data.entry = mw.uri.encode(input_data.entry)
end
for key, value in pairs(links) do
if url_config[key] then
return value(url_config, input_data)
end
end
if url_config.url then
-- Apply replacements to URL.
local no_replacement = true
local ret = resolve_value("url")
if not ret then return nil end
if type(url_config.url) == "string" then
ret = ret:gsub("%$([A-Z|]*)%$", function (wanted_param_list)
local param_ret
-- Loop through all piped elements until you find a replacement.
for wanted_param in wanted_param_list:gmatch("[^|]+") do
param_ret = input_data[wanted_param:lower()]
if param_ret then
no_replacement = false
break
end
end
return param_ret
end)
-- If it couldn't replace the element with its value, do not show a link.
if no_replacement then
return
end
end
return ret
end
error("No URL could be made")
end
local function sort_into_indices(args)
local cits = {}
local i = 0
while true do i = i + 1
-- This is the table that stores all the information for a particular index.
local cit_data = {}
local is_data_over = true
for arg_name, arg in pairs(args) do
-- Get only the values of the current index for list parameters.
if type(arg) == "table" then
if arg[i] then
is_data_over = false
cit_data[arg_name] = arg[i]
end
-- Get the general non-index-specific parameters as they are.
else
cit_data[arg_name] = arg
end
end
-- If there weren't any list parameters for this index, the data is over.
if is_data_over then break end
-- Add to the returned list.
table.insert(cits, cit_data)
end
return cits
end
function export.show(frame)
local args = frame:getParent().args
-- Handle {{bibquote}} rather than {{bibref}}.
local is_quote = not not frame.args.quote
-- Get the submodule language and source ID from the raw args and retrieves
-- the source table from the submodule.
local input_lang, input_sid = args[1], args[2]
args[1], args[2] = nil, nil
local submodule_data = shared.require_submodule(input_lang)
local source, tracking_lang, sid = shared.extract_source(submodule_data, input_lang, input_sid)
-- Universal parameters.
local list_allow_holes = { list = true, allow_holes = true }
local list_entry_alias = { list = true, alias_of = "entry" }
local list_page_alias = { list = true, alias_of = "page" }
local list_section_alias = { list = true, alias_of = "section" }
local volume_alias = { list = true, alias_of = "volume" }
local params = {
-- language and source ID
[1] = true, [2] = true,
-- headword
["entry"] = list_allow_holes,
["head"] = list_entry_alias,
[3] = list_entry_alias,
["h"] = list_entry_alias,
["headword"] = list_entry_alias,
-- page
["page"] = list_allow_holes,
["p"] = list_page_alias,
["pages"] = list_page_alias,
-- section
["section"] = list_allow_holes,
["§"] = list_section_alias,
["s"] = list_section_alias,
-- volume
["volume"] = list_allow_holes,
["vol"] = volume_alias,
["v"] = volume_alias,
-- edition
["edition"] = list_allow_holes,
["ed"] = { list = true, alias_of = "edition" },
-- sub entries
["sub"] = list_allow_holes,
-- num
["num"] = list_allow_holes,
-- entry ID
["id"] = list_allow_holes,
}
-- Source-specific parameters and displays.
local own_display = {}
if source.own_args then
for arg_name, arg_data in pairs(source.own_args) do
-- The `numerical` field means that it will be converted to numbers for the calculations, just
-- like pages and volumes are by default.
if arg_data.numerical then
is_numerical[arg_name] = true
arg_data.numerical = nil
end
-- Lists will be automatically assumed to allow holes, unless specified otherwise.
if arg_data.list and arg_data.allow_holes == nil then
arg_data.allow_holes = true
end
if arg_data.display then
local display_after = arg_data.display.after or "entry"
arg_data.display.after = nil
arg_data.display.name = arg_name
if not own_display[display_after] then own_display[display_after] = {} end
table.insert(own_display[display_after], arg_data.display)
end
params[arg_name] = arg_data
end
end
args = require("Module:parameters").process(frame:getParent().args, params)
args.pagename = mw.loadData("Module:headword/data").pagename
-- Display the source, and handle arguments if different.
args.sid_display = shared.format(sid, source.is_author)
args.sid = shared.strip_markup(sid)
if source.default_pagename then
if #args.entry == 0 then
args.entry[1] = args.pagename
args.entry.maxindex = 1
elseif #args.entry == 1 and args.entry[1] == "-" then
args.entry[1] = nil
args.entry.maxindex = 0
end
end
-- Handle .composed parameter.
if source.composed then
args.sid_display = args.sid_display.." ["..source.composed.."]"
end
-- Sort out aliases of volumes and editions.
if (source.editions or source.volumes) and (args.edition or args.volume) then
-- `voled` means volume/edition.
local reorganise_voled_function = {
volume = function (voled) return voled.number or voled.volume end,
edition = function (voled) return voled.edition end
}
local function recursive_reorganise(node)
for _, arg in ipairs {"volume", "edition"} do
local source_args = node[arg.."s"]
if source_args then
local reorganised = {}
for voled_id, voled in pairs(source_args) do
if voled_id == "hide_numbers" then
-- TODO: Temporary solution, sort out this situation.
reorganised.hide_numbers = voled
else
-- Sort out aliases.
if voled.alias then
if type(voled.alias) == "string" then
reorganised[voled.alias] = voled_id
elseif type(voled.alias) == "table" then
for _, alias in ipairs(voled.alias) do
reorganised[alias] = voled_id
end
end
end
-- Handle recursive subgroupings.
recursive_reorganise(voled)
-- Reorganise under proper name.
reorganised[tostring(reorganise_voled_function[arg](voled) or voled_id)] = voled
end
end
node[arg.."s"] = reorganised
break -- there can't be both volumes and editions
end
end
end
recursive_reorganise(source)
end
-- apply the set_args parameter
if source.set_args then
local function apply_general_setter(setter)
if type(setter) == "function" then
setter(args)
elseif type(setter) == "string" then
require("Module:bibliography/presets")[setter](args)
elseif type(setter) == "table" then
if #setter > 0 then
for _, setter_child in ipairs(setter) do
apply_general_setter(setter_child)
end
-- TODO: understand whether this is really needed or just complicates syntax
elseif setter.from and setter.to and setter.setter then
for index, from in ipairs(args[setter.from]) do
if not args[setter.to][index] then
args[setter.to][index] = setter.setter(from)
elseif args[setter.to][index] == "-" then
args[setter.to][index] = nil
end
end
end
end
end
apply_general_setter(source.set_args)
end
local work_link = "Appendix:Bibliography/"..(submodule_data.bib_page or get_lang(input_lang, "Could not deduce the page to link to from invalid language code.", true, true):getCanonicalName()).."#bib-"..args.sid
local cits = sort_into_indices(args)
local ret
if is_quote then
local ret_list = {}
local info_type_data = retrieve_fields(
{
year = {},
author = {},
show_title = {},
show_author = {},
},
source,
-- TODO: Understand what should happen when multiple indices are given.
numerify_fields(cits[1], source)
)
-- Year.
if info_type_data.year.value then
table.insert(ret_list, "'''"..info_type_data.year.value .."'''")
end
-- Author.
local show_author = info_type_data.show_author.value or info_type_data.author.value
if show_author then
table.insert(ret_list, show_author)
end
-- Title, or rather, visible human identifier.
local show_title = info_type_data.show_title.value or source.title
if show_title then
table.insert(ret_list, "<cite>[["..work_link.."|"..show_title.."]]</cite>")
end
-- Concat with commas.
ret = table.concat(ret_list, ", ")
else
ret = "[["..work_link.."|"..args.sid_display.."]]"
end
-- tracks the source being cited
require("Module:debug/track")(shared.tracking_path(tracking_lang, args.sid))
--[[ INDICES ITERATION ]]--
-- Keep iterating through the indices until list parameters (i.e. page, entry) are over.
local carryon_data = {}
local following_separator = ","
for i, cit_data in ipairs(cits) do
--[[ FORMATTING INFRASTRUCTURE ]]--
-- This function formats a "display table", which is a Lua table containing all the information (label,
-- separator, linkability, etc.) of a particular citation element (page, entry, section, etc.).
local function format_display_table(arg_data)
-- The `name` is not the one displayed (that would be label), but rather the name of the parameters
-- when the template is called, e.g. {{R|...|<NAME>=...}}, and also what is used within the bibliography
-- module in fields such as <NAME>_url and set_<NAME>.
local arg_name = arg_data.name
-- The `value` field is the actual value specified for the element at a given index, for example the page
-- number itself, or the headword. If it isn't present in this index, simply skip over. The variable starts
-- from this raw value but eventually becomes the fully formatted element to be displayed.
local value = cit_data[arg_name]
if not value then return end
-- The `label` is what is displayed before the value. It can be either a string or a list of two strings,
-- the first displayed in case the value is singular, the second in case the value is plural.
if arg_data.label then
value = format_number_label(value, arg_data.label)
end
-- The `url` here is a boolean flag. If true the module will try to get an outcome from the data that
-- was specified in <NAME>_url and make the value along with the preceding label link to what it got.
if arg_data.url then
local value_url = param_url(source, cit_data, arg_name)
-- If no URL was able to be generated for whatever reason, then do not link the element.
if value_url then value = "["..value_url.." "..value.."]" end
end
-- The `formatter` is a function that can be used in cases where we want more control over the final
-- formatted element rather than just a label, e.g. if we want to also add something after, or set to
-- a particular font, etc. The formatter takes two arguments, the first the formatted element and the
-- second is the index's data.
if arg_data.formatter then
value = arg_data.formatter(value, cit_data) or value
end
-- Separate elements by a space, unless specified otherwise.
if not arg_data.nospace then
value = " "..value
end
-- The `separator` field is a string that is appended before the element, unless there is a more
-- important separator that we know we must do from the last element (see below).
if following_separator then
value = following_separator..value
following_separator = nil
elseif arg_data.separator then
value = arg_data.separator..value
end
-- The `following_separator` field overwrites the `separator` field of the following field if present.
following_separator = arg_data.following_separator
-- Append the formatted element to the final string to be returned.
ret = ret..value
end
-- This function iterates through all the "display lists" for source-specific elements.
local function format_own_display_table_list(own_display_table_list)
if own_display_table_list then
for _, own_display_table in ipairs(own_display_table_list) do
format_display_table(own_display_table)
end
end
end
-- All indices are always separated by a comma (or semicolon if the volume changed, see below), which is
-- why the first element of and index never displays their separator.
following_separator = ((cit_data.volume or cit_data.edition) and i > 1) and ";" or ","
-- Volumes and editions work a bit like list parameters and a bit like general parameters, meaning that
-- while you can specify different volume/edition numbers for different indices, if you specify it for an
-- index and not for the following the module will assume that all the following page numbers are from that
-- volume/edition. When a new volume/edition is specified from an earlier one than the separator between
-- the previous index and the new one is a semicolon instead of a comma to better show this (see above).
local function format_volume_or_edition(name)
if cit_data[name] then
local format_table = {
name = name,
following_separator = ",",
}
if name == "volume" then
format_table.label = "tập "
elseif name == "edition" then
format_table.formatter = function(data)
local edition_num = tonumber(data)
if edition_num and not source.year_editions then
data = shared.ordinal_suffix(edition_num)
end
return "phiên bản " .. data
end
end
format_display_table(format_table)
carryon_data[name] = cit_data[name]
elseif carryon_data[name] then
cit_data[name] = carryon_data[name]
end
end
if cit_data.volume and cit_data.edition and source.editions then
format_volume_or_edition("edition")
format_volume_or_edition("volume")
else
format_volume_or_edition("volume")
format_volume_or_edition("edition")
end
-- Formats the other elements.
format_own_display_table_list(own_display.volume)
format_display_table {
name = "section",
label = {"§", "§§"},
url = true,
}
format_own_display_table_list(own_display.section)
format_display_table {
name = "page",
label = {"trang ", "trang "},
url = true,
}
format_own_display_table_list(own_display.page)
format_display_table {
name = "entry",
separator = ":",
url = true,
formatter = function(entry, cit_data)
local sub = cit_data.sub
-- Format text according to the `entry_lang` field.
-- TODO: This should also allow for specification of script.
-- TODO: Why is the language retrieved each time?
if source.entry_lang then
entry = tag_text(entry, get_lang(source.entry_lang))
if sub then sub = tag_text(sub, get_lang(source.entry_lang)) end
end
-- The `num` parameter works like the ^ syntax below.
if cit_data.num then
entry = entry.."<sup>"..cit_data.num.."</sup>"
end
-- The `sub` parameter appends another headword separated by arrow.
if sub then entry = entry.."” → “"..sub end
-- The <ENTRY>^<NUMBER> syntax is formatted with the number as superscript.
entry = entry:gsub("([^\\])%^(%d+)", "%1<sup>%2</sup>")
return "“"..entry.."”"
end,
}
format_own_display_table_list(own_display.entry)
end
return ret
end
return export
ex9iho4ohfyan3doiqzr2dflbqurik4
2573889
2573888
2026-10-27T14:07:03Z
Hiyuune
50834
2573889
Scribunto
text/plain
local export = {}
local tag_text = require("Module:script utilities").tag_text
local get_lang = require("Module:languages").getByCode
local shared = require("Module:bibliography/shared")
local links = require("Module:bibliography/shared/links").ref
-- Understands if a number refers to one or multiple instances of something (e.g. pages, sections) and pluralises the label accordingly.
local function format_number_label(value, label)
if type(label) == "table" then
label = label[(value:find("–") or value:find("f%.$") or value:find(", ")) and 2 or 1]
end
return label..value
end
-- Handles the set_page, set_id, etc. functions within page_url, entry_url, etc. nodes.
local function apply_param_setter(arg_name, setter, input_data)
local input_value = input_data[arg_name]
-- simple addition or subtraction
if type(setter) == "number" and input_value then
return input_value + setter
-- operations as strings
-- TODO: needs to be stricter with invalid inputs
elseif type(setter) == "string" and input_value then
-- separate the different actions within the setter string (operations, alterations)
local operations = setter:match("^([0-9+%-*/ ]*)")
local esc_operations = "^"..operations:gsub("([^%w ])", "%%%1")
local alterations = setter:match(esc_operations.."(>[0-9+%-*/ >]+)")
assert(alterations or not setter:match(esc_operations.."."), "Invalid string as set_page: "..setter)
-- apply arithmetic operations
local function calculate_operation(num, operations)
assert(operations:gsub("([+%-*/]) *(%d+)", function(operation, operation_num)
num = ({
["+"] = function(a, b) return a + b end,
["-"] = function(a, b) return a - b end,
["*"] = function(a, b) return a * b end,
["/"] = function(a, b) return math.floor(a / b) end,
})[operation](num, tonumber(operation_num))
return ""
end):match("^ *$"), "Invalid operation in set_page: "..setter)
return num
end
input_value = calculate_operation(input_value, operations)
-- apply alterations (e.g. for missing or duplicate pages)
if alterations then
assert(alterations:gsub("> *(%d+) *([0-9+%-*/ ]*)", function(alteration_from, alteration_operation)
if input_data[arg_name] > tonumber(alteration_from) then
input_value = calculate_operation(input_value, alteration_operation)
end
return ""
end):match("^ *$"), "Invalid alteration in set_page: "..setter)
end
return input_value
-- full function for complex operation
elseif type(setter) == "function" then
return setter(input_data)
end
end
local is_numerical = {
["page"] = true,
}
-- Override t's values with p's.
local function sum_table(t, p)
if not p then
return t
elseif type(t) == "table" and type(p) == "table" then
for k, v in pairs(p) do
t[k] = v
end
return t
else
return p
end
end
local function retrieve_fields(info_types, source, input_data)
-- Overwrite or sum together with the fields specified in the current node, if present.
for info_type_key, info_type in pairs(info_types) do
local current_given_value = source[info_type.field_name or info_type_key]
-- If the `sum_table` field is true, then sum the tables together.
-- If not, simply overwrite them.
if info_type.sum_table then
info_types[info_type_key].value = sum_table(info_type.value, current_given_value)
else
info_types[info_type_key].value = current_given_value or info_type.value
end
end
-- Checks in the node's children. This kind of treatment allows for both
-- edition nodes within volume nodes and volume nodes within edition nodes.
for _, arg in ipairs {"volume", "edition"} do
local source_args = source[arg.."s"]
if source_args then
if input_data[arg] then
-- Retrieve element.
local item = source_args[input_data[arg]]
-- Handle aliases.
if type(item) == "string" then
item = source_args[item]
end
if item then
-- Search for the fields in the newly found node.
info_types = retrieve_fields(info_types, item, input_data)
else
-- Item not in the list.
end
end
break
end
end
return info_types
end
-- Recursive function that searches for the link data inside the source node.
-- The `url_config` is the table containing information about how to make the URL, while `link_node` is
-- the table with the links that are displayed on the bibliography page which are used only in case that
-- the links aren't explicit in `url_config`, e.g. the common `{ gb = true }` syntax. Both `url_config` and
-- `link_node` can either be about the entire source or volume/edition-dependent.
local function get_url_config_and_link_node(source, input_data, param_name_url)
local data = retrieve_fields({
url_config = {
sum_table = true,
field_name = param_name_url,
},
link_node = {
sum_table = true,
field_name = "link",
},
}, source, input_data)
return data.url_config.value, data.link_node.value
end
-- Duplicate the table, and convert string to integers whenever needed.
local function numerify_fields(const_data, source)
local input_data = {}
for k, v in pairs(const_data) do
if not source.unprocess_numbers and is_numerical[k] and type(v) == "string" and v:match("^%d+") then
v = tonumber(v:match("^%d+"))
end
input_data[k] = v
end
return input_data
end
-- Handles page_url, entry_url, etc.
local function param_url(source, input_data, param_name)
-- TODO: Is this check needed? Seems impossible to occur.
if not input_data then return nil end
input_data = numerify_fields(input_data, source)
-- For an explanation of what `url_config` and `link_node` are, see above.
local url_config, link_node = get_url_config_and_link_node(source, input_data, param_name.."_url")
-- If there is no url_config, get out of the function.
if not url_config or (type(url_config) == "table" and next(url_config) == nil) then
return nil
end
-- Utility function that recovers the ID.
local function resolve_value(site_key)
local value = url_config[site_key]
-- recycle data from stable links
if value == true then
assert(link_node and link_node[site_key], 'Cannot use the "'..site_key..' = true" syntax because I could not find it defined in as a field of "link"')
value = link_node[site_key]
end
if not value then error("Could not retrieve the "..site_key.." data for "..param_name) end
-- if a function, runs it
if type(value) == "string" then
return value
elseif type(value) == "function" then
return value(input_data)
end
error("Could not define "..param_name.." URL from the site "..site_key)
end
if type(url_config) ~= "table" then
url_config = { url = url_config }
else
-- Resolve the values before applying the set parameters.
for key, _ in pairs(links) do
if url_config[key] then
url_config[key] = resolve_value(key)
if not url_config[key] then return nil end
break
end
end
if type(url_config.pref) == "function" then
url_config.pref = url_config.pref(input_data)
end
-- applies setters
for set_arg, set_value in pairs(url_config) do
if set_arg:match("^set_") then
set_arg = set_arg:gsub("^set_", "")
input_data[set_arg] = apply_param_setter(set_arg, set_value, input_data)
end
end
end
-- percent encodes entry name
if input_data.entry then
input_data.entry = mw.uri.encode(input_data.entry)
end
for key, value in pairs(links) do
if url_config[key] then
return value(url_config, input_data)
end
end
if url_config.url then
-- Apply replacements to URL.
local no_replacement = true
local ret = resolve_value("url")
if not ret then return nil end
if type(url_config.url) == "string" then
ret = ret:gsub("%$([A-Z|]*)%$", function (wanted_param_list)
local param_ret
-- Loop through all piped elements until you find a replacement.
for wanted_param in wanted_param_list:gmatch("[^|]+") do
param_ret = input_data[wanted_param:lower()]
if param_ret then
no_replacement = false
break
end
end
return param_ret
end)
-- If it couldn't replace the element with its value, do not show a link.
if no_replacement then
return
end
end
return ret
end
error("No URL could be made")
end
local function sort_into_indices(args)
local cits = {}
local i = 0
while true do i = i + 1
-- This is the table that stores all the information for a particular index.
local cit_data = {}
local is_data_over = true
for arg_name, arg in pairs(args) do
-- Get only the values of the current index for list parameters.
if type(arg) == "table" then
if arg[i] then
is_data_over = false
cit_data[arg_name] = arg[i]
end
-- Get the general non-index-specific parameters as they are.
else
cit_data[arg_name] = arg
end
end
-- If there weren't any list parameters for this index, the data is over.
if is_data_over then break end
-- Add to the returned list.
table.insert(cits, cit_data)
end
return cits
end
function export.show(frame)
local args = frame:getParent().args
-- Handle {{bibquote}} rather than {{bibref}}.
local is_quote = not not frame.args.quote
-- Get the submodule language and source ID from the raw args and retrieves
-- the source table from the submodule.
local input_lang, input_sid = args[1], args[2]
args[1], args[2] = nil, nil
local submodule_data = shared.require_submodule(input_lang)
local source, tracking_lang, sid = shared.extract_source(submodule_data, input_lang, input_sid)
-- Universal parameters.
local list_allow_holes = { list = true, allow_holes = true }
local list_entry_alias = { list = true, alias_of = "entry" }
local list_page_alias = { list = true, alias_of = "page" }
local list_section_alias = { list = true, alias_of = "section" }
local volume_alias = { list = true, alias_of = "volume" }
local params = {
-- language and source ID
[1] = true, [2] = true,
-- headword
["entry"] = list_allow_holes,
["head"] = list_entry_alias,
[3] = list_entry_alias,
["h"] = list_entry_alias,
["headword"] = list_entry_alias,
-- page
["page"] = list_allow_holes,
["p"] = list_page_alias,
["pages"] = list_page_alias,
-- section
["section"] = list_allow_holes,
["§"] = list_section_alias,
["s"] = list_section_alias,
-- volume
["volume"] = list_allow_holes,
["vol"] = volume_alias,
["v"] = volume_alias,
-- edition
["edition"] = list_allow_holes,
["ed"] = { list = true, alias_of = "edition" },
-- sub entries
["sub"] = list_allow_holes,
-- num
["num"] = list_allow_holes,
-- entry ID
["id"] = list_allow_holes,
}
-- Source-specific parameters and displays.
local own_display = {}
if source.own_args then
for arg_name, arg_data in pairs(source.own_args) do
-- The `numerical` field means that it will be converted to numbers for the calculations, just
-- like pages and volumes are by default.
if arg_data.numerical then
is_numerical[arg_name] = true
arg_data.numerical = nil
end
-- Lists will be automatically assumed to allow holes, unless specified otherwise.
if arg_data.list and arg_data.allow_holes == nil then
arg_data.allow_holes = true
end
if arg_data.display then
local display_after = arg_data.display.after or "entry"
arg_data.display.after = nil
arg_data.display.name = arg_name
if not own_display[display_after] then own_display[display_after] = {} end
table.insert(own_display[display_after], arg_data.display)
end
params[arg_name] = arg_data
end
end
args = require("Module:parameters").process(frame:getParent().args, params)
args.pagename = mw.loadData("Module:headword/data").pagename
-- Display the source, and handle arguments if different.
args.sid_display = shared.format(sid, source.is_author)
args.sid = shared.strip_markup(sid)
if source.default_pagename then
if #args.entry == 0 then
args.entry[1] = args.pagename
args.entry.maxindex = 1
elseif #args.entry == 1 and args.entry[1] == "-" then
args.entry[1] = nil
args.entry.maxindex = 0
end
end
-- Handle .composed parameter.
if source.composed then
args.sid_display = args.sid_display.." ["..source.composed.."]"
end
-- Sort out aliases of volumes and editions.
if (source.editions or source.volumes) and (args.edition or args.volume) then
-- `voled` means volume/edition.
local reorganise_voled_function = {
volume = function (voled) return voled.number or voled.volume end,
edition = function (voled) return voled.edition end
}
local function recursive_reorganise(node)
for _, arg in ipairs {"volume", "edition"} do
local source_args = node[arg.."s"]
if source_args then
local reorganised = {}
for voled_id, voled in pairs(source_args) do
if voled_id == "hide_numbers" then
-- TODO: Temporary solution, sort out this situation.
reorganised.hide_numbers = voled
else
-- Sort out aliases.
if voled.alias then
if type(voled.alias) == "string" then
reorganised[voled.alias] = voled_id
elseif type(voled.alias) == "table" then
for _, alias in ipairs(voled.alias) do
reorganised[alias] = voled_id
end
end
end
-- Handle recursive subgroupings.
recursive_reorganise(voled)
-- Reorganise under proper name.
reorganised[tostring(reorganise_voled_function[arg](voled) or voled_id)] = voled
end
end
node[arg.."s"] = reorganised
break -- there can't be both volumes and editions
end
end
end
recursive_reorganise(source)
end
-- apply the set_args parameter
if source.set_args then
local function apply_general_setter(setter)
if type(setter) == "function" then
setter(args)
elseif type(setter) == "string" then
require("Module:bibliography/presets")[setter](args)
elseif type(setter) == "table" then
if #setter > 0 then
for _, setter_child in ipairs(setter) do
apply_general_setter(setter_child)
end
-- TODO: understand whether this is really needed or just complicates syntax
elseif setter.from and setter.to and setter.setter then
for index, from in ipairs(args[setter.from]) do
if not args[setter.to][index] then
args[setter.to][index] = setter.setter(from)
elseif args[setter.to][index] == "-" then
args[setter.to][index] = nil
end
end
end
end
end
apply_general_setter(source.set_args)
end
local work_link = "Appendix:Bibliography/"..(submodule_data.bib_page or get_lang(input_lang, "Could not deduce the page to link to from invalid language code.", true, true):getCanonicalName()).."#bib-"..args.sid
local cits = sort_into_indices(args)
local ret
if is_quote then
local ret_list = {}
local info_type_data = retrieve_fields(
{
year = {},
author = {},
show_title = {},
show_author = {},
},
source,
-- TODO: Understand what should happen when multiple indices are given.
numerify_fields(cits[1], source)
)
-- Year.
if info_type_data.year.value then
table.insert(ret_list, "'''"..info_type_data.year.value .."'''")
end
-- Author.
local show_author = info_type_data.show_author.value or info_type_data.author.value
if show_author then
table.insert(ret_list, show_author)
end
-- Title, or rather, visible human identifier.
local show_title = info_type_data.show_title.value or source.title
if show_title then
table.insert(ret_list, "<cite>[["..work_link.."|"..show_title.."]]</cite>")
end
-- Concat with commas.
ret = table.concat(ret_list, ", ")
else
ret = "[["..work_link.."|"..args.sid_display.."]]"
end
-- tracks the source being cited
require("Module:debug/track")(shared.tracking_path(tracking_lang, args.sid))
--[[ INDICES ITERATION ]]--
-- Keep iterating through the indices until list parameters (i.e. page, entry) are over.
local carryon_data = {}
local following_separator = ","
for i, cit_data in ipairs(cits) do
--[[ FORMATTING INFRASTRUCTURE ]]--
-- This function formats a "display table", which is a Lua table containing all the information (label,
-- separator, linkability, etc.) of a particular citation element (page, entry, section, etc.).
local function format_display_table(arg_data)
-- The `name` is not the one displayed (that would be label), but rather the name of the parameters
-- when the template is called, e.g. {{R|...|<NAME>=...}}, and also what is used within the bibliography
-- module in fields such as <NAME>_url and set_<NAME>.
local arg_name = arg_data.name
-- The `value` field is the actual value specified for the element at a given index, for example the page
-- number itself, or the headword. If it isn't present in this index, simply skip over. The variable starts
-- from this raw value but eventually becomes the fully formatted element to be displayed.
local value = cit_data[arg_name]
if not value then return end
-- The `label` is what is displayed before the value. It can be either a string or a list of two strings,
-- the first displayed in case the value is singular, the second in case the value is plural.
if arg_data.label then
value = format_number_label(value, arg_data.label)
end
-- The `url` here is a boolean flag. If true the module will try to get an outcome from the data that
-- was specified in <NAME>_url and make the value along with the preceding label link to what it got.
if arg_data.url then
local value_url = param_url(source, cit_data, arg_name)
-- If no URL was able to be generated for whatever reason, then do not link the element.
if value_url then value = "["..value_url.." "..value.."]" end
end
-- The `formatter` is a function that can be used in cases where we want more control over the final
-- formatted element rather than just a label, e.g. if we want to also add something after, or set to
-- a particular font, etc. The formatter takes two arguments, the first the formatted element and the
-- second is the index's data.
if arg_data.formatter then
value = arg_data.formatter(value, cit_data) or value
end
-- Separate elements by a space, unless specified otherwise.
if not arg_data.nospace then
value = " "..value
end
-- The `separator` field is a string that is appended before the element, unless there is a more
-- important separator that we know we must do from the last element (see below).
if following_separator then
value = following_separator..value
following_separator = nil
elseif arg_data.separator then
value = arg_data.separator..value
end
-- The `following_separator` field overwrites the `separator` field of the following field if present.
following_separator = arg_data.following_separator
-- Append the formatted element to the final string to be returned.
ret = ret..value
end
-- This function iterates through all the "display lists" for source-specific elements.
local function format_own_display_table_list(own_display_table_list)
if own_display_table_list then
for _, own_display_table in ipairs(own_display_table_list) do
format_display_table(own_display_table)
end
end
end
-- All indices are always separated by a comma (or semicolon if the volume changed, see below), which is
-- why the first element of and index never displays their separator.
following_separator = ((cit_data.volume or cit_data.edition) and i > 1) and ";" or ","
-- Volumes and editions work a bit like list parameters and a bit like general parameters, meaning that
-- while you can specify different volume/edition numbers for different indices, if you specify it for an
-- index and not for the following the module will assume that all the following page numbers are from that
-- volume/edition. When a new volume/edition is specified from an earlier one than the separator between
-- the previous index and the new one is a semicolon instead of a comma to better show this (see above).
local function format_volume_or_edition(name)
if cit_data[name] then
local format_table = {
name = name,
following_separator = ",",
}
if name == "volume" then
format_table.label = "tập "
elseif name == "edition" then
format_table.formatter = function(data)
local edition_num = tonumber(data)
if edition_num and not source.year_editions then
data = shared.ordinal_suffix(edition_num)
end
return "ấn bản " .. data
end
end
format_display_table(format_table)
carryon_data[name] = cit_data[name]
elseif carryon_data[name] then
cit_data[name] = carryon_data[name]
end
end
if cit_data.volume and cit_data.edition and source.editions then
format_volume_or_edition("edition")
format_volume_or_edition("volume")
else
format_volume_or_edition("volume")
format_volume_or_edition("edition")
end
-- Formats the other elements.
format_own_display_table_list(own_display.volume)
format_display_table {
name = "section",
label = {"§", "§§"},
url = true,
}
format_own_display_table_list(own_display.section)
format_display_table {
name = "page",
label = {"trang ", "trang "},
url = true,
}
format_own_display_table_list(own_display.page)
format_display_table {
name = "entry",
separator = ":",
url = true,
formatter = function(entry, cit_data)
local sub = cit_data.sub
-- Format text according to the `entry_lang` field.
-- TODO: This should also allow for specification of script.
-- TODO: Why is the language retrieved each time?
if source.entry_lang then
entry = tag_text(entry, get_lang(source.entry_lang))
if sub then sub = tag_text(sub, get_lang(source.entry_lang)) end
end
-- The `num` parameter works like the ^ syntax below.
if cit_data.num then
entry = entry.."<sup>"..cit_data.num.."</sup>"
end
-- The `sub` parameter appends another headword separated by arrow.
if sub then entry = entry.."” → “"..sub end
-- The <ENTRY>^<NUMBER> syntax is formatted with the number as superscript.
entry = entry:gsub("([^\\])%^(%d+)", "%1<sup>%2</sup>")
return "“"..entry.."”"
end,
}
format_own_display_table_list(own_display.entry)
end
return ret
end
return export
bx2rf3ta4c8hj3d9mssbffzl4td5qum
Mô đun:bibliography/shared/links
828
385291
2573885
2331128
2026-10-27T14:03:45Z
Hiyuune
50834
2573885
Scribunto
text/plain
-- Some of the code is duplicated from [[Module:quote]].
local export = {}
local m_check = require("Module:check isxn")
local ulen = require("Module:string utilities").len
export.bib_page = {
-- Identifiers
{
key = "isbn",
formatter = function (isbn)
return "[[Special:BookSources/"..isbn.."|→ISBN]]"..m_check.check_isbn(isbn, ' <span class="error">Invalid ISBN</span>[[Thể loại:Trang có lỗi ISBN]]')
end,
},
{
key = "issn",
formatter = function (issn)
return "[https://www.worldcat.org/issn/"..issn.." →ISSN]"..m_check.check_issn(issn, ' <span class="error">Invalid ISSN</span>[[Thể loại:Trang có lỗi ISSN]]')
end,
},
{ key = "doi", formatter = "[https://doi.org/%s →DOI]" },
{ key = "jstor", formatter = "[https://www.jstor.org/stable/%s →JSTOR]" },
{ key = "bibcode", formatter = "[https://adsabs.harvard.edu/abs/%s →Bibcode]" },
{ key = "oclc", formatter = "[https://search.worldcat.org/title/%s →OCLC]" },
{
key = "lccn",
formatter = function (text)
text = text:gsub(" ", "")
if text:find("-") then
local prefix, part1, part2 = text:match("^(.-)(%d+)%-(%d+)$")
if prefix then
if ulen(part2) < 6 then
part2 = ("0"):rep(6 - ulen(part2)) .. part2
end
text = prefix .. part1 .. part2
end
end
return "[https://lccn.loc.gov/" .. mw.uri.encode(text) .. " →LCCN]"
end,
},
{ key = "ol", formatter = "[https://openlibrary.org/works/OL%s/ →OL]" },
{ key = "pmid", formatter = "[https://www.ncbi.nlm.nih.gov/pubmed/%s →PMID]" },
{ key = "pmcid", formatter = "[https://www.ncbi.nlm.nih.gov/pmc/articles/%s/ →PMCID]" },
{ key = "ssrn", formatter = "[https://ssrn.com/abstract=", " →SSRN]" },
-- General digitalisation projects
{ key = "ia", formatter = "[https://archive.org/details/%s →IA]" },
{ key = "gb", formatter = "[https://books.google.com/?id=%s →GB]" },
{ key = "hathi", formatter = "[https://babel.hathitrust.org/cgi/pt?id=%s →HathiTrust]" },
-- Libraries
{ key = "hazu", formatter = "[https://dizbi.hazu.hr/a/?pr=iiif.v.a&id=%s →HAZU]" },
{ key = "mdz", formatter = "[https://www.digitale-sammlungen.de/de/view/%s →MDZ]" },
{ key = "anemi", formatter = "[https://anemi.lib.uoc.gr/metadata/%s.tkl →Anemi]" },
{ key = "gallica", formatter = "[https://gallica.bnf.fr/ark:/12148/%s → Gallica]" },
-- Miscellanea
{ key = "academia", formatter = "[https://www.academia.edu/%s →Academia]" },
{ key = "url", formatter = "[%s →URL]" },
}
export.ref = {
ia = function(config, data)
return "https://archive.org/details/"..config.ia
.."/page/"..(config.pref or "n")
..data.page.."/mode/1up?view=theater"
end,
gb = function(config, data)
return "https://books.google.com/?id="..config.gb.."&pg="..(config.pref or "PA")..data.page
end,
hazu = function(config, data)
return "https://dizbi.hazu.hr/a/?pr=iiif.v.a&id="..config.hazu.."&tify={%22pages%22:%5B"..data.page.."%5D,%22view%22:%22thumbnails%22}"
end,
mdz = function(config, data)
return "https://www.digitale-sammlungen.de/de/view/"..config.mdz.."?page="..data.page
end,
gallica = function(config, data)
return "https://gallica.bnf.fr/ark:/12148/"..config.gallica.."/f"..data.page
end,
hathi = function(config, data)
return "https://babel.hathitrust.org/cgi/pt?id="..config.hathi.."&seq="..data.page
end,
}
return export
na5bnya2wwo4ohdngj3nilggdaolln2
2573886
2573885
2026-10-27T14:04:15Z
Hiyuune
50834
Hiyuune đã đổi [[Mô đun:bibliography/links]] thành [[Mô đun:bibliography/shared/links]]
2573885
Scribunto
text/plain
-- Some of the code is duplicated from [[Module:quote]].
local export = {}
local m_check = require("Module:check isxn")
local ulen = require("Module:string utilities").len
export.bib_page = {
-- Identifiers
{
key = "isbn",
formatter = function (isbn)
return "[[Special:BookSources/"..isbn.."|→ISBN]]"..m_check.check_isbn(isbn, ' <span class="error">Invalid ISBN</span>[[Thể loại:Trang có lỗi ISBN]]')
end,
},
{
key = "issn",
formatter = function (issn)
return "[https://www.worldcat.org/issn/"..issn.." →ISSN]"..m_check.check_issn(issn, ' <span class="error">Invalid ISSN</span>[[Thể loại:Trang có lỗi ISSN]]')
end,
},
{ key = "doi", formatter = "[https://doi.org/%s →DOI]" },
{ key = "jstor", formatter = "[https://www.jstor.org/stable/%s →JSTOR]" },
{ key = "bibcode", formatter = "[https://adsabs.harvard.edu/abs/%s →Bibcode]" },
{ key = "oclc", formatter = "[https://search.worldcat.org/title/%s →OCLC]" },
{
key = "lccn",
formatter = function (text)
text = text:gsub(" ", "")
if text:find("-") then
local prefix, part1, part2 = text:match("^(.-)(%d+)%-(%d+)$")
if prefix then
if ulen(part2) < 6 then
part2 = ("0"):rep(6 - ulen(part2)) .. part2
end
text = prefix .. part1 .. part2
end
end
return "[https://lccn.loc.gov/" .. mw.uri.encode(text) .. " →LCCN]"
end,
},
{ key = "ol", formatter = "[https://openlibrary.org/works/OL%s/ →OL]" },
{ key = "pmid", formatter = "[https://www.ncbi.nlm.nih.gov/pubmed/%s →PMID]" },
{ key = "pmcid", formatter = "[https://www.ncbi.nlm.nih.gov/pmc/articles/%s/ →PMCID]" },
{ key = "ssrn", formatter = "[https://ssrn.com/abstract=", " →SSRN]" },
-- General digitalisation projects
{ key = "ia", formatter = "[https://archive.org/details/%s →IA]" },
{ key = "gb", formatter = "[https://books.google.com/?id=%s →GB]" },
{ key = "hathi", formatter = "[https://babel.hathitrust.org/cgi/pt?id=%s →HathiTrust]" },
-- Libraries
{ key = "hazu", formatter = "[https://dizbi.hazu.hr/a/?pr=iiif.v.a&id=%s →HAZU]" },
{ key = "mdz", formatter = "[https://www.digitale-sammlungen.de/de/view/%s →MDZ]" },
{ key = "anemi", formatter = "[https://anemi.lib.uoc.gr/metadata/%s.tkl →Anemi]" },
{ key = "gallica", formatter = "[https://gallica.bnf.fr/ark:/12148/%s → Gallica]" },
-- Miscellanea
{ key = "academia", formatter = "[https://www.academia.edu/%s →Academia]" },
{ key = "url", formatter = "[%s →URL]" },
}
export.ref = {
ia = function(config, data)
return "https://archive.org/details/"..config.ia
.."/page/"..(config.pref or "n")
..data.page.."/mode/1up?view=theater"
end,
gb = function(config, data)
return "https://books.google.com/?id="..config.gb.."&pg="..(config.pref or "PA")..data.page
end,
hazu = function(config, data)
return "https://dizbi.hazu.hr/a/?pr=iiif.v.a&id="..config.hazu.."&tify={%22pages%22:%5B"..data.page.."%5D,%22view%22:%22thumbnails%22}"
end,
mdz = function(config, data)
return "https://www.digitale-sammlungen.de/de/view/"..config.mdz.."?page="..data.page
end,
gallica = function(config, data)
return "https://gallica.bnf.fr/ark:/12148/"..config.gallica.."/f"..data.page
end,
hathi = function(config, data)
return "https://babel.hathitrust.org/cgi/pt?id="..config.hathi.."&seq="..data.page
end,
}
return export
na5bnya2wwo4ohdngj3nilggdaolln2
ponvttv
0
388508
2573909
2337010
2026-10-27T15:57:22Z
TheHighFighter2
42988
/* Tiếng Muscogee */
2573909
wikitext
text/x-wiki
=={{langname|mus}}==
==={{ĐM|pron}}===
* {{mus-IPA|ponv́ttv}}
* {{hyph|mus|po|nvt|tv}}
==={{ĐM|noun}}===
{{head|mus|noun}}
# [[động vật|Động vật]].
#* {{RQ:mus:Robertson:1871|{{smc|Nokuse}} '''ponvttv''' cvpvkket os, momet eto ofvn aret omes.|{{smc|Gấu}} là loài '''động vật''' hung dữ, sống ở trong rừng.|page=26}}
===={{ĐM|decl}}====
{{mus-decl}}
{{mus-poss}}
===={{ĐM|der}}====
{{col3|mus|ponvttv-lvste|ponvttv-vpeswv|ponvttv-yvmvse}}
==={{ĐM|ref}}===
* {{R:mus:DCM|+|page=100}}
{{C|mus|Sinh vật}}
syy4ypl7lm21a6q9dcxy8gyimc3g3cj
bất thành văn
0
394597
2573875
2348215
2026-10-27T13:19:40Z
TheHighFighter2
42988
/* Dịch */ Chuyển đổi {{langname}} ---> {{dịch}}
2573875
wikitext
text/x-wiki
=={{langname|vi}}==
==={{ĐM|etym}}===
{{vi-etym-sino|不成文}}.
==={{ĐM|pron}}===
{{vi-IPA}}
==={{ĐM|adj}}===
{{vi-adj}}
# Không được [[ghi]] lại bằng [[chữ viết]], thành [[văn bản]].
#: {{ant|vi|thành văn}}
#: {{ux|vi|Luật '''bất thành văn'''.}}
===={{ĐM|trans}}====
{{trans-top|Không được ghi lại bằng chữ viết, thành văn bản}}
* {{dịch|en}}: {{t+|en|unwritten}}
* {{dịch|zh}}:
*: {{dịch|cmn}}: {{t+|cmn|不成文|tr=bùchéngwén de|alt=不成文的}}
{{trans-bottom}}
==={{ĐM|see}}===
* {{l|vi|truyền miệng}}
* {{l|vi|ngầm định}}
==={{ĐM|ref}}===
* {{R:Tratu}}
0j7z0kiifx9g4wqsvaxfdhfcxl16rw6
virus Hanta
0
396630
2573901
2352936
2026-10-27T15:38:02Z
TheHighFighter2
42988
/* Dịch */
2573901
wikitext
text/x-wiki
=={{langname|vi}}==
{{wp}}
[[File:Sin Nombre hanta virus TEM PHIL 1136 lores.jpg|thumb|Ảnh hiển vi của '''virus Hanta''']]
==={{ĐM|alt}}===
* {{alt|vi|virus hanta}}
* {{alt|vi|vi rút Hanta}}
* {{alt|vi|virút Hanta}}
==={{ĐM|pron}}===
{{vi-pron|vi rút Han ta}}
==={{ĐM|n}}===
{{vi-noun|head=[[virus]] [[Hantan|Hanta]]}}
# {{lb|vi|virology}} Nhóm virus thuộc chi {{taxlink|Orthohantavirus|chi}}, lây truyền sang người thông qua đường hô hấp khi hít phải các hạt [[khí dung]] phát tán từ [[chất thải]] của động vật gặm nhấm hoặc qua vết cắn của chúng.
#* {{quote-newspaper|vi|date=11-02-2008|author=M.T. (theo ''benravenspell'')|title=Chuyện thú vị về họ nhà Tý|url=https://vnexpress.net/chuyen-thu-vi-ve-ho-nha-ty-2099241.html|work=VnExpress|passage=Chuột nai có thể mang theo '''virus hanta''' gây các triệu chứng cúm và có thể dẫn tới tử vong. Nếu bạn nhìn thấy một con chuột ướt sũng và run rẩy, đừng có chạm vào. Có thể nó đang ốm vì '''virus hanta'''.}}
#* {{quote-newspaper|vi|date=05-05-2026|author=Minh Tâm|title=WHO xác nhận ổ dịch virus Hanta trên tàu du lịch, 3 người đã tử vong|url=https://www.vietnamplus.vn/who-xac-nhan-o-dich-virus-hanta-tren-tau-du-lich-3-nguoi-da-tu-vong-post1108677.vnp|work=VietnamPlus|passage=WHO ghi nhận 7 ca liên quan '''virus Hanta''' trên tàu du lịch ngoài khơi Cape Verde, gồm 2 ca đã xác nhận, 5 ca nghi nhiễm và 3 trường hợp tử vong; các biện pháp giám sát, truy vết đang được triển khai.}}
===={{ĐM|trans}}====
{{trans-top|Nhóm virus}}
* {{dịch|en}}: {{t+|en|hantavirus}}
* {{dịch|ar}}: {{t|ar|فِيرُوس هَانْتا|m|tr=fīrūs hānta}}, {{t|ar|ڤِيرُوْس هَانْتا|tr=vīrūs hānta}}
* {{dịch|pl}}: {{t|pl|hantawirus|m}}
* {{dịch|pt}}: {{t|pt|hantavírus|m}}
* {{dịch|de}}: {{t+|de|Hantavirus|n}}
* {{dịch|nl}}: {{t+|nl|hantavirus|n}}
* {{dịch|hi}}: {{t|hi|हंटावायरस}}
* {{dịch|el}}: {{t+|el|χανταϊός|m}}
* {{dịch|nv}}: {{t|nv|naʼatsʼǫǫsí binaalniih}}
* {{dịch|ru}}: {{t+|ru|ха̀нтави́рус|m}}
* {{dịch|ja}}: {{t|ja|ハンタウイルス|tr=hantauirusu}}
* {{dịch|sa}}: {{t+|sa|हन्तविषाणुः|m}}
* {{dịch|fr}}: {{t+|fr|hantavirus|m}}
* {{dịch|fi}}: {{t+|fi|hantavirus}}
* {{dịch|es}}: {{t|es|hantavirus|m}}
* {{dịch|tr}}: {{t|tr|hantavirüs}}
* {{dịch|sv}}: {{t+|sv|hantavirus|n}}
* {{dịch|ko}}: {{t|ko|한타바이러스}}
* {{dịch|zh}}:
*: {{dịch|cmn}}: {{t+|cmn|漢坦病毒}} {{qualifier|Trung Quốc đại lục, Hồng Kông}}, {{t+|cmn|漢他病毒}} {{qualifier|Đài Loan}}
* {{dịch|it}}: {{t+|it|hantavirus|m}}
{{trans-bottom}}
pmm8esxtuuvidhin6smm2xui44gzewb
bật đèn xanh
0
397981
2573896
2360318
2026-10-27T14:18:00Z
TheHighFighter2
42988
/* Dịch */
2573896
wikitext
text/x-wiki
=={{langname|vi}}==
{{wp}}
==={{ĐM|etym}}===
Từ {{com|vi|bật|đèn xanh}}.
==={{ĐM|pron}}===
{{vi-pron}}
==={{ĐM|v}}===
{{vi-verb|head=[[bật]] [[đèn xanh]]}}
# [[ra hiệu|Ra hiệu]] [[cho phép]] làm một việc gì đó.
===={{ĐM|trans}}====
{{trans-top|Ra hiệu cho phép làm}}
* {{dịch|en}}: {{t+|en|greenlight}}
{{trans-bottom}}
==={{ĐM|ref}}===
* {{R:Tratu}}
8wwhjqscuprhesnei0mu274faozgs50
lá cờ đầu
0
400069
2573928
2362721
2026-10-27T16:09:51Z
TheHighFighter2
42988
2573928
wikitext
text/x-wiki
=={{langname|vi}}==
==={{ĐM|pron}}===
{{vi-pron}}
==={{ĐM|n}}===
{{vi-noun}}
# Người hay đơn vị [[tiên tiến]] có tác dụng [[nêu gương]], [[dẫn đầu]] [[phong trào]].
===={{ĐM|trans}}====
{{trans-top|Người hay đơn vị tiên tiến có tác dụng nêu gương, dẫn đầu phong trào}}
* {{dịch|sed}}: {{t|sed|khu djâ troăng a hdrối}}
{{trans-bottom}}
==={{ĐM|ref}}===
* {{R:Tratu}}
jaupmrspj7toa1nvmhfxfl2ijojcblf
संस्कृति
0
405580
2573880
2565410
2026-10-27T13:56:09Z
Hiyuune
50834
/* Biến cách */ (sử dụng [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]])
2573880
wikitext
text/x-wiki
=={{langname|ne}}==
==={{section|etym}}===
{{lbor|ne|sa|संस्कृति}}.
==={{section|pron}}===
* {{ne-IPA|संस्कृति|सँस्कृति}}
==={{section|n}}===
{{ne-noun}}
# [[văn hóa|Văn hóa]].
=={{langname|sa}}==
==={{section|alternative script}}===
{{sa-alt}}
==={{section|etym}}===
Từ {{affix|sa|सम्-<alt:सं->|स्कृ|-ति}}.
==={{section|pron}}===
{{sa-IPA|सम्स्कृति|a=1}}
==={{section|n}}===
{{sa-noun|tr=sáṃskṛti|f}}
# Sự [[sửa soạn]]; sự [[chuẩn bị]].
# Sự [[hình thành]]; sự [[tạo thành]]; sự [[lập]] [[nên]].
# Sự [[hiến dân]]; sự [[cúng tế]].
# Sự [[quyết định]]; sự [[kiên quyết]].
===={{section|decl}}====
{{sa-ndecl|sáṃskṛti<f>}}
==={{section|pr-noun}}===
{{sa-noun|tr=sáṃskṛti|m}}
# {{non-gloss|Tên gọi của [[Krishna]]}}
# {{non-gloss|Tên gọi của [[đức vua]]}}
===={{section|decl}}====
{{sa-decl-noun-m|sáṃskṛti|n=s}}
szr4k4qglw3z86bvf5jcnm3y22w5w6s
2573881
2573880
2026-10-27T13:56:19Z
Hiyuune
50834
/* Biến cách */ (sử dụng [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]])
2573881
wikitext
text/x-wiki
=={{langname|ne}}==
==={{section|etym}}===
{{lbor|ne|sa|संस्कृति}}.
==={{section|pron}}===
* {{ne-IPA|संस्कृति|सँस्कृति}}
==={{section|n}}===
{{ne-noun}}
# [[văn hóa|Văn hóa]].
=={{langname|sa}}==
==={{section|alternative script}}===
{{sa-alt}}
==={{section|etym}}===
Từ {{affix|sa|सम्-<alt:सं->|स्कृ|-ति}}.
==={{section|pron}}===
{{sa-IPA|सम्स्कृति|a=1}}
==={{section|n}}===
{{sa-noun|tr=sáṃskṛti|f}}
# Sự [[sửa soạn]]; sự [[chuẩn bị]].
# Sự [[hình thành]]; sự [[tạo thành]]; sự [[lập]] [[nên]].
# Sự [[hiến dân]]; sự [[cúng tế]].
# Sự [[quyết định]]; sự [[kiên quyết]].
===={{section|decl}}====
{{sa-ndecl|sáṃskṛti<f>}}
==={{section|pr-noun}}===
{{sa-noun|tr=sáṃskṛti|m}}
# {{non-gloss|Tên gọi của [[Krishna]]}}
# {{non-gloss|Tên gọi của [[đức vua]]}}
===={{section|decl}}====
{{sa-ndecl|sáṃskṛti<m>}}
deqm08yufwfaaatatrrdvayaqtj1npg
lúa chín cúi đầu
0
406596
2573989
2439317
2026-10-28T10:15:08Z
TheHighFighter2
42988
/* Dịch */
2573989
wikitext
text/x-wiki
=={{langname|vi}}==
==={{ĐM|pron}}===
{{vi-pron}}
==={{ĐM|phrase}}===
{{vi-phrase}}
# Người có [[tri thức]], [[trưởng thành]] hoặc [[thành công]] thì càng trở nên [[khiêm tốn]].
===={{ĐM|trans}}====
{{trans-top|Người có tri thức, trưởng thành hoặc thành công thì càng trở nên khiêm tốn}}
* {{dịch|az}}: {{t|az|ağac bar verdikcə başını aşağı əyər}}
* {{dịch|ja}}: {{t|ja|実るほど頭の下がる稲穂かな|tr=みのるほどあたまのさがるいなほかな, minoru hodo atama no sagaru inaho kana}}
* {{dịch|ko}}: {{t|ko|벼는 익을수록 고개를 숙인다}}
{{trans-bottom}}
0lhflnoe176h4rmir3q6gda5asl56vc
විද්වාංස්
0
407436
2573879
2567140
2026-10-27T13:54:28Z
Hiyuune
50834
/* Phân từ */ (sử dụng [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]])
2573879
wikitext
text/x-wiki
=={{langname|sa}}==
==={{section|alt}}===
{{sa-alt|Deva=विद्वांस्}}
==={{section|ptcp}}===
{{sa-part|विद्}}
# {{sa-sc}}
==={{section|n}}===
{{sa-noun|m}}
# {{sa-sc}}
#* {{quote-web
|sa
|url=https://www.unicode.org/L2/L2018/18060-sinhala-candrabindu.pdf
|format=PDF
|title=Proposal to encode the CANDRABINDU for Sinhala
|page = 3
|passage = '''විද්වාඁල්ලි'''ඛති
|transliteration = '''vidvāl̐'''likhati
|author1=Srinidhi A
|author2=Sridatta A
|date=2018-02-05
|work=Unicode Document Register L2018/18060
}}
h05utuxwdmdq1b8xi0fz198bqof6mf2
chất độc da cam
0
412290
2573893
2514284
2026-10-27T14:12:02Z
TheHighFighter2
42988
/* Dịch */
2573893
wikitext
text/x-wiki
=={{langname|vi}}==
{{wp|vi:}}
[[File:US-Huey-helicopter-spraying-Agent-Orange-in-Vietnam.jpg|nhỏ|Trực thăng Mỹ rải chất độc da cam xuống một vùng rừng tại Việt Nam.]]
==={{ĐM|alt}}===
* {{alt|vi|CĐDC||q1=viết tắt chữ cái đầu}}
==={{ĐM|etym}}===
Từ {{com|vi|chất độc|da cam}}, do những chiếc thùng phuy vận chuyển loại hóa chất này có vẽ các sọc da cam ở bên ngoài thùng.
==={{ĐM|pron}}===
{{vi-IPA}}
==={{ĐM|noun}}===
{{vi-noun|head=[[chất độc]] [[da cam]]}}
# Loại [[thuốc diệt cỏ|hóa chất diệt cỏ]] và làm rụng lá cây được quân đội Hoa Kỳ sử dụng trong thời kỳ [[chiến tranh Việt Nam|chiến tranh tại Việt Nam]].
#: {{syn|vi|chất độc màu da cam}}
#* {{quote-web|vi|date=09-07-2004|author=Phương Vũ|title=10/8 - ngày vì nạn nhân chất độc da cam VN|site=VnExpress|url=https://vnexpress.net/10-8-ngay-vi-nan-nhan-chat-doc-da-cam-vn-2009966.html|passage=Chiều nay, Mặt trận tổ quốc (MTTQ) Việt Nam vừa ra quyết định lấy ngày 10/8 hằng năm, ngày đầu tiên quân đội Mỹ sử dụng chất độc hóa học tại Việt Nam, là ngày vì nạn nhân '''chất độc da cam''' Việt Nam; đồng thời ra tuyên bố ủng hộ các nạn nhân.}}
===={{ĐM|trans}}====
{{trans-top|Loại hóa chất}}
* {{dịch|en}}: {{t+|en|Agent Orange}}
* {{dịch|pt}}: {{t+|pt|agente laranja|m}}
* {{dịch|ca}}: {{t|ca|agent taronja|m}}
* {{dịch|de}}: {{t+|de|Agent Orange|n}}
* {{dịch|mn}}: {{t|mn|Агент Оранж}}
* {{dịch|ru}}: {{t|ru|аге́нт о́ранж|m}}, {{t|ru|э́йджент о́рандж|m}}
* {{dịch|fr}}: {{t+|fr|agent orange|m}}
* {{dịch|es}}: {{t|es|Agente Naranja|m}}
* {{dịch|zh}}:
*: {{dịch|cmn}}: {{t+|cmn|橙劑|tr=chéngjì}}, {{t|cmn|橘劑|tr=jújì}}
* {{dịch|it}}: {{t|it|Agente Arancio|f}}
{{trans-bottom}}
{{C|vi|Chiến tranh Việt Nam}}
99esnou2ds63i704qozg3rib9bxt7xa
hậu thuộc địa
0
414059
2573867
2559609
2026-10-27T13:04:43Z
TheHighFighter2
42988
/* Dịch */
2573867
wikitext
text/x-wiki
=={{langname|vi}}==
==={{ĐM|etym}}===
Từ {{pre|vi|hậu|thuộc địa}}.
==={{ĐM|pron}}===
{{vi-pron}}
==={{ĐM|adj}}===
{{vi-adj|head=[[hậu]] [[thuộc địa]]}}
# Thuộc về giai đoạn sau khi một quốc gia hoặc vùng lãnh thổ [[chấm dứt]] chế độ [[thuộc địa]].
#: {{syn|vi|hậu thực dân}}
#: {{ant|vi|tiền thuộc địa}}
#* {{quote-web|vi|date=18-10-2008|author=B.T.|title=Giải Man Booker 2006 ra mắt độc giả VN|site=VnExpress|url=https://vnexpress.net/giai-man-booker-2006-ra-mat-doc-gia-vn-1972539.html|passage=Điều đó ít nhiều đến từ sự tương đồng giữa hai quốc gia châu Á, hai nền văn minh nông nghiệp, hai nền kinh tế đang phát triển, hai xã hội '''hậu thuộc địa'''.}}
===={{ĐM|trans}}====
{{trans-top|Thuộc về giai đoạn sau khi một quốc gia hoặc vùng lãnh thổ chấm dứt chế độ thuộc địa}}
* {{dịch|en}}: {{t+|en|postcolonial}}
* {{dịch|de}}: {{t+|de|postkolonial}}
* {{dịch|nl}}: {{t+|nl|postkoloniaal}}
* {{dịch|hi}}: {{t|hi|उत्तर औपनिवेशिक}}
* {{dịch|id}}: {{t|id|pascakolonial}}
* {{dịch|fr}}: {{t+|fr|postcolonial}}
* {{dịch|eo}}: {{t|eo|postkolonia}}
* {{dịch|ro}}: {{t|ro|postcolonial}}
* {{dịch|sv}}: {{t+|sv|postkolonial}}
* {{dịch|it}}: {{t+|it|postcoloniale}}
{{trans-bottom}}
5ip4ehzzgky0mq13r17k8vpcx382ueu
chế độ máy bay
0
416037
2573876
2563005
2026-10-27T13:22:28Z
TheHighFighter2
42988
Chuyển đổi {{langname}} ---> {{dịch}}
2573876
wikitext
text/x-wiki
=={{langname|vi}}==
[[File:Smartphone display screen.jpg|nhỏ|Điện thoại đang bật '''chế độ máy bay''']]
==={{ĐM|etym}}===
Từ {{com|vi|chế độ|máy bay}}. {{calque|vi|en|airplane mode}}.
==={{ĐM|pron}}===
{{vi-pron}}
==={{ĐM|n}}===
{{vi-noun|head=[[chế độ]] [[máy bay]]}}
# [[cài đặt|Cài đặt]] có trên nhiều thiết bị điện tử giúp [[vô hiệu hóa]] [[sóng vô tuyến]].
#* {{quote-web|vi|date=19-7-2015|author=Hương Chi|title=Chàng trai chơi piano vòng quanh thế giới|site=VnExpress|url=https://vnexpress.net/chang-trai-choi-piano-vong-quanh-the-gioi-3248129.html|passage=Negrin đi du lịch và có thể không kết nối với mọi người hàng tháng trời, vì anh sử dụng loại điện thoại chỉ hoạt động khi có wifi. Anh cũng thường để điện thoại '''chế độ máy bay''' để tập đàn.}}
===={{ĐM|trans}}====
{{trans-top|Cài đặt có trên nhiều thiết bị điện tử}}
* {{dịch|ar}}: {{t|ar|وضع الطيران}}, {{t|ar|وضع الطائرة}}
* {{dịch|en}}: {{t+|en|airplane mode}}, {{t+|en|aeroplane mode}}, {{t+|en|flight mode}}
* {{dịch|pl}}: {{t+|pl|tryb samolotowy|m}}
* {{dịch|fa}}: {{t|fa|حالت هواپیما}}, {{t|fa|حالت پرواز}}
* {{dịch|pt}}: {{t|pt|modo avião|m}}, {{t|pt|modo de voo|m}}
* {{dịch|bg}}: {{t|bg|самоле́тен режи́м|m}}
* {{dịch|he}}: {{t|he|מַצַּב טִיסָה|m|tr=matsáv tisá}}
* {{dịch|de}}: {{t|de|Flugmodus|m}}, {{t|de|Flugzeugmodus|m}}
* {{dịch|gl}}: {{t|gl|modo avión|m}}
* {{dịch|ka}}: {{t|ka|თვითმფრინავის რეჟიმი}}, {{t|ka|ფრენის რეჟიმი}}
* {{dịch|nl}}: {{t|nl|vliegtuigstand|m}}, {{t+|nl|vliegtuigmodus|m}}
* {{dịch|hu}}: {{t|hu|repülőgép mód}}
* {{dịch|ru}}: {{t|ru|режи́м полёта|m}}
* {{dịch|ja}}: {{t|ja|航空機モード|tr=こうくうきモード, kōkūki mōdo}}, {{t+|ja|機内モード|tr=きないモード, kinai mōdo}}
* {{dịch|fr}}: {{t+|fr|mode avion|m}}
* {{dịch|fi}}: {{t|fi|lentotila}}
* {{dịch|es}}: {{t|es|modo avión|m}}
* {{dịch|tr}}: {{t|tr|uçuş modu}}
* {{dịch|sv}}: {{t|sv|flygplansläge}}
* {{dịch|ko}}: {{t|ko|비행기 모드}}
* {{dịch|zh}}:
*: {{dịch|cmn}}: {{t|cmn|飛行模式}}
* {{dịch|ur}}: {{t|ur|پرواز حالت|tr=parvāz-hālat}}, {{t|ur|جہاز حالت|tr=jahāz hālat}}
* {{dịch|it}}: {{t|it|modalità aereo|f}}
{{trans-bottom}}
{{c|vi|Điện thoại di động}}
2oeuprmo866rvso0xnl1wibgksjwpwe
ngủ nướng
0
418717
2573929
2567701
2026-10-27T16:11:16Z
TheHighFighter2
42988
2573929
wikitext
text/x-wiki
=={{langname|vi}}==
==={{ĐM|etym}}===
Từ {{af|vi|ngủ|nướng}}.
==={{ĐM|pron}}===
{{vi-pron}}
==={{ĐM|verb}}===
{{vi-verb}}
# [[tiếp tục|Tiếp tục]] [[ngủ]] thêm sau khi đã [[tỉnh giấc]], vượt quá [[khung]] [[giờ]] [[thức dậy]] [[thường ngày]].
#: {{ux|vi|Hôm nay chủ nhật, ở nhà '''ngủ nướng'''.}}
===={{ĐM|syn}}====
* {{l|vi|nướng}} {{q|nói tắt}}
===={{ĐM|trans}}====
{{trans-top|Tiếp tục ngủ thêm sau khi đã tỉnh giấc}}
* {{dịch|en}}: {{t+|en|sleep in}}
{{trans-bottom}}
{{C|vi|Ngủ}}
e70808i4k621ot274ttvw4xxzw3f66z
tai trong
0
420238
2573871
2570998
2026-10-27T13:10:29Z
TheHighFighter2
42988
/* Dịch */ Chuyển đổi {{langname}} ---> {{dịch}}
2573871
wikitext
text/x-wiki
=={{langname|vi}}==
{{wp}}
==={{ĐM|pron}}===
{{vi-pron}}
==={{ĐM|n}}===
{{vi-noun}}
# {{nhãn|vi|giải phẫu}} Phần tai nằm trong [[xương thái dương]] bao gồm các [[ống bán khuyên]], [[tiền đình]] và [[ốc tai]], chịu trách nhiệm về thính giác và thăng bằng.
===={{ĐM|trans}}====
{{trans-top|Giải phẫu học}}
* {{dịch|en}}: {{t+|en|inner ear}}
{{trans-bottom}}
a5jjf02lcncvqmveigpbrrcpb1p5lzm
học máy
0
420587
2573990
2571486
2026-10-28T10:18:17Z
TheHighFighter2
42988
/* Dịch */
2573990
wikitext
text/x-wiki
=={{langname|vi}}==
==={{ĐM|etym}}===
{{cal|vi|en|machine learning}}.
==={{ĐM|pron}}===
{{vi-IPA}}
==={{ĐM|n}}===
{{vi-noun}}
# {{lb|vi|artificial intelligence}} Khả năng [[máy tính]] tự [[học]] từ [[dữ liệu]] [[huấn luyện]] mà không cần phải được [[lập trình]] chi tiết cho từng trường hợp.
#* {{quote-web|vi|date=16-5-2017|author=Hòa Việt|title=Mỹ muốn phát triển robot chuyên săn tìm phiến quân IS|site=VnExpress|url=https://vnexpress.net/my-muon-phat-trien-robot-chuyen-san-tim-phien-quan-is-3585657.html|passage=Trong dự án này, Lầu Năm Góc muốn tích hợp trí thông minh nhân tạo và khả năng '''học máy''' một cách hiệu quả hơn vào các hoạt động tác chiến trên chiến trường, nhằm duy trì ưu thế trước đối phương.}}
# {{lb|vi|artificial intelligence}} Lĩnh vực nghiên cứu liên quan đến thiết kế, phát triển các [[thuật toán]] và kỹ thuật cho phép máy tính học từ dữ liệu huấn luyện.
#* {{quote-web|vi|date=24-06-2023|author=Hàn Mai|title=Loạt bảo tàng mới đầy thú vị trên thế giới trong năm 2023|work=Du lịch TP.HCM|url=https://tuoitre.vn/dulichtphcm/loat-bao-tang-moi-day-thu-vi-tren-the-gioi-trong-nam-2023-10755446.htm|passage=RAIM là một bảo tàng giáo dục, nhằm mục đích giới thiệu cho công chúng về '''học máy''' và công nghệ robot.}}
===={{ĐM|syn}}====
* {{l|vi|máy học}}
===={{ĐM|trans}}====
{{trans-top|Lĩnh vực nghiên cứu}}
* {{dịch|af}}: {{t|af|masjienleer}}
* {{dịch|en}}: {{t+|en|machine learning}}
* {{dịch|hy}}: {{t|hy|մեքենայական ուսուցում}}
* {{dịch|ast}}: {{t|ast|deprendizaxe automáticu}}
* {{dịch|az}}: {{t|az|maşın öyrənməsi}}
* {{dịch|pl}}: {{t|pl|uczenie maszynowe|n}}
* {{dịch|fa}}: {{t|fa|یادگیری ماشین}}
* {{dịch|bn}}: {{t|bn|মেশিন লার্নিং}}
* {{dịch|pt}}: {{t|pt|aprendizagem automática|f}}, {{t|pt|aprendizado de máquina|m}}
* {{dịch|bg}}: {{t|bg|маши́нно обуче́ние|n}}
* {{dịch|ca}}: {{t|ca|aprenentatge automàtic|m}}
* {{dịch|he}}: {{t|he|למידת מכונה|f|tr=lmidát mkhoná}}, {{t|he|משין לרנינג|m|tr=mashín lérning}}
* {{dịch|ug}}: {{t|ug|ماشنىلىق ئۆگىنىش}}
* {{dịch|da}}: {{t|da|maskinlæring|c}}
* {{dịch|de}}: {{t+|de|maschinelles Lernen|n}}
* {{dịch|et}}: {{t|et|masinõppimine}}
* {{dịch|ka}}: {{t|ka|მანქანური სწავლა}}, {{t|ka|მანქანური დასწავლა}}
* {{dịch|nl}}: {{t|nl|machinelearning|m|f|n}}
* {{dịch|hu}}: {{t+|hu|gépi tanulás}}
* {{dịch|el}}: {{t+|el|μηχανική μάθηση|f}}
* {{dịch|is}}: {{t|is|vélrænt nám|n}}
* {{dịch|id}}: {{t|id|pembelajaran mesin}}
* {{dịch|kn}}: {{t+|kn|ಯಂತ್ರ ಕಲಿಕೆ}}
* {{dịch|kk}}: {{t|kk|машинамен оқыту}}
* {{dịch|lt}}: {{t|lt|mašininis mokymasis|m}}
* {{dịch|ms}}: {{t|ms|pembelajaran mesin}}
* {{dịch|ml}}: {{t|ml|യന്ത്രപഠനം}}
* {{dịch|ru}}: {{t+|ru|маши́нное обуче́ние|n}}
* {{dịch|ja}}: {{t+|ja|機械学習|tr=[[きかいがくしゅう]], [[kikaigakushū]]}}
* {{dịch|fr}}: {{t+|fr|apprentissage automatique|m}}, {{t+|fr|machine learning|m}} {{qualifier|từ ngữ đặc Anh}}
* {{dịch|fi}}: {{t+|fi|koneoppiminen}}
* {{dịch|eo}}: {{t|eo|maŝina lernado}}
* {{dịch|sat}}: {{t|sat|ᱢᱮᱥᱤᱱ ᱞᱚᱨᱱᱤᱝ}}
* {{dịch|cs}}: {{t|cs|strojové učení|n}}
* {{dịch|scn}}: {{t|scn|apprinnimentu autumàticu|m}}
* {{dịch|ta}}: {{t+|ta|பொறிக் கற்றல்}}
* {{dịch|es}}: {{t|es|aprendizaje automático|m}}
* {{dịch|th}}: {{t|th|การเรียนรู้ของเครื่อง}}
* {{dịch|sv}}: {{t+|sv|maskininlärning|c}}
* {{dịch|ko}}: {{t|ko|기계 학습(機械學習)}}
* {{dịch|zh}}:
*: {{dịch|nan-hbl}}: {{t|nan-hbl|機械學習|tr=ki-hāi ha̍k-si̍p}}
*: {{dịch|cmn}}: {{t+|cmn|機器學習|tr=jīqì xuéxí}}
* {{dịch|cy}}: {{t|cy|dysgu peirianyddol}}
* {{dịch|it}}: {{t|it|apprendimento automatico|m}}
{{trans-bottom}}
lkhdpxsnj81p3s1bmulwugy7o1ikur8
kiến chúa
0
420815
2573868
2572204
2026-10-27T13:06:31Z
TheHighFighter2
42988
/* Dịch */ Chuyển đổi {{langname}} ---> {{dịch}}
2573868
wikitext
text/x-wiki
=={{langname|vi}}==
{{wp}}
==={{section|etym}}===
Từ {{ghép|vi|kiến|chúa}}.
==={{section|pron}}===
{{vi-pron}}
==={{section|n}}===
{{vi-noun|cls=con}}
# [[kiến|Kiến]] [[cái]] đã [[trưởng thành]], đứng đầu trong bầy kiến.
===={{section|trans}}====
{{trans-top|Kiến cái đã trưởng thành}}
* {{dịch|en}}: {{t|en|queen ant}}
{{trans-bottom}}
a8hsgvnocv0ssf6v9rq2svnlen0gme3
lối sống
0
421021
2573869
2572767
2026-10-27T13:07:31Z
TheHighFighter2
42988
/* Dịch */ Chuyển đổi {{langname}} ---> {{dịch}}
2573869
wikitext
text/x-wiki
=={{langname|vi}}==
{{wp}}
==={{đm|etym}}===
Từ {{com|vi|lối|sống}}.
==={{đm|pron}}===
{{vi-IPA}}
==={{đm|noun}}===
{{vi-noun}}
# [[cách thức|Cách thức]] [[sinh hoạt]], [[ứng xử]] biểu hiện [[thái độ]] và các [[giá trị]] của một cá nhân hay một nhóm người.
#: {{ux|vi|'''Lối sống''' lành mạnh và '''lối sống''' không lành mạnh.}}
===={{đm|trans}}====
{{trans-top|Cách thức sinh hoạt, ứng xử}}
* {{dịch|en}}: {{t+|en|way of life}}, {{t+|en|lifestyle}}
{{trans-bottom}}
5ar3jkec31ewebo83wwvx8yregcxms6
đụt
0
421468
2573933
2573534
2026-10-27T23:21:44Z
Hiyuune
50834
2573933
wikitext
text/x-wiki
{{also|Phụ lục:Biến thể của "dut"}}
=={{langname|vi}}==
==={{ĐM|pron}}===
{{vi-pron}}
==={{ĐM|noun}}===
{{vi-noun|cls=[[cái]], [[chiếc]]}}
# [[đồ|Đồ]] [[đan]] bằng [[tre]] [[đặt]] [[tiếp]] vào [[lưới]] [[ống]] (lưới [[đáy]]) để [[chứa]] [[cá]] [[đánh bắt]] được.
==={{ĐM|verb}}===
{{vi-verb}}
# {{lb|vi|phương ngữ}} {{like-entry|trú}}
#:{{ux|vi|đứng trong mái hiên '''đụt''' mưa}}
==={{ĐM|adj}}===
{{vi-adj|突}}
# {{lb|vi|khẩu ngữ}} [[kém|Kém]] [[trí khôn]] và [[hèn yếu]], [[dễ dàng]] [[chịu]] [[lép vế]].
#: {{syn|vi|đần|ngu}}
#:{{ux|vi|người đâu mà '''đụt''' thế không biết!}}
=== {{ĐM|ref}} ===
* {{R:Tratu}}
4v99kjrlnurv66w4dpq346q8kyglshd
chơn chánh
0
421560
2573899
2573753
2026-10-27T15:14:23Z
TheHighFighter2
42988
/* Tiếng Việt */
2573899
wikitext
text/x-wiki
=={{langname|vi}}==
==={{section|pron}}===
{{vi-pron}}
==={{section|adj}}===
{{vi-adj}}
# {{lb|vi|ban đầu|miền Nam|hiện nay|chủ yếu dùng trong|_|Phật giáo}} {{alt form|vi|chân chính}}.
bbd00xfphub2vikopx9nhpkyxecghb3
tánh tình
0
421563
2573898
2573758
2026-10-27T14:57:48Z
TheHighFighter2
42988
/* Tiếng Việt */
2573898
wikitext
text/x-wiki
=={{langname|vi}}==
==={{section|pron}}===
{{vi-pron}}
==={{section|noun}}===
{{vi-noun}}
# {{alternative form of|vi|tính tình|from=southern Vietnam}}.
ctqymzytces1ezdjqpd5zb7i4p1j3jq
độc thủ
0
421565
2573864
2573761
2026-10-27T12:59:56Z
TheHighFighter2
42988
/* Tiếng Việt */ Từ điển tiếng Việt của Viện Ngôn ngữ có lấy vd "Giáng một miếng độc thủ hạ gục đối phương.", do đó 'độc thủ' không chỉ có 'đầu độc' không
2573864
wikitext
text/x-wiki
=={{langname|vi}}==
==={{section|etym}}===
{{vi-etym-sino|毒手}}.
==={{section|pron}}===
{{vi-pron}}
==={{section|noun}}===
{{vi-noun}}
# [[thủ đoạn|Thủ đoạn]], miếng đòn [[hiểm ác]] hại người.
#: {{ux|vi|Ngón '''độc thủ'''.}}
#: {{ux|vi|Hạ '''độc thủ'''.}}
==={{section|ref}}===
{{R:Tratu}}
hcwr0t74rgq7xn6u6rgivhiu3nue33x
Bản mẫu:dịch
10
421591
2573848
2026-10-27T12:13:56Z
TheHighFighter2
42988
/* */ Tạo bản mẫu mới để thay thế langname ở phần dịch
2573848
wikitext
text/x-wiki
{{#if:{{#invoke:languages/templates|getByCode|{{{1|und}}}|getCanonicalName}}<!--
-->|<!--
-->{{langname|{{{1|und}}}}}<!--
-->|<!--
-->{{{1}}}<!--
-->}}<!--
--><noinclude>{{documentation}}</noinclude>
2vg2xfcwaxouplksz79l29hf48y958n
oestruations
0
421592
2573863
2026-10-27T12:52:08Z
Hiyuune
50834
Trang mới: “=={{langname|en}}== ==={{section|n}}=== {{head|en|Biến thể hình thái danh từ}} # {{plural of|en|oestruation}}”
2573863
wikitext
text/x-wiki
=={{langname|en}}==
==={{section|n}}===
{{head|en|Biến thể hình thái danh từ}}
# {{plural of|en|oestruation}}
7gp41sg1gj6xwbs13h9pm47p3ui7qvd
Bản mẫu:dsb-decl-noun-24
10
421593
2573866
2026-10-27T13:00:54Z
Hiyuune
50834
Trang mới: “{{#invoke:checkparams|error}}<!-- Validate template parameters --> {{dsb-decl-noun | name= {{{1}}}{{{2|}}}o | {{l-self|dsb|{{{1}}}{{{2|}}}o}} | {{l-self|dsb|{{{1}}}{{#switch:{{{2|}}}|d=ź|zd=zdź|ł=l|t=ś|st=sć|#default={{{2|}}}j}}e}} | {{l-self|dsb|{{{1}}}{{{2|}}}a}} | {{l-self|dsb|{{{1}}}{{{2|}}}a}} | {{l-self|dsb|{{{1}}}{{{2|}}}owu}} | {{l-self|dsb|{{{1}}}{{{2|}}}ow}} | {{l-self|dsb|{{{1}}}{{{2|}}}oju}}, {{l-self|dsb|{{{1}}}{{{2|}}}u}} | {{l-self|dsb|{{{1}}}{{{2|}…”
2573866
wikitext
text/x-wiki
{{#invoke:checkparams|error}}<!-- Validate template parameters
--> {{dsb-decl-noun
| name= {{{1}}}{{{2|}}}o
| {{l-self|dsb|{{{1}}}{{{2|}}}o}}
| {{l-self|dsb|{{{1}}}{{#switch:{{{2|}}}|d=ź|zd=zdź|ł=l|t=ś|st=sć|#default={{{2|}}}j}}e}}
| {{l-self|dsb|{{{1}}}{{{2|}}}a}}
| {{l-self|dsb|{{{1}}}{{{2|}}}a}}
| {{l-self|dsb|{{{1}}}{{{2|}}}owu}}
| {{l-self|dsb|{{{1}}}{{{2|}}}ow}}
| {{l-self|dsb|{{{1}}}{{{2|}}}oju}}, {{l-self|dsb|{{{1}}}{{{2|}}}u}}
| {{l-self|dsb|{{{1}}}{{{2|}}}oma}}
| {{l-self|dsb|{{{1}}}{{{2|}}}am}}
| {{l-self|dsb|{{{1}}}{{{2|}}}o}}
| {{l-self|dsb|{{{1}}}{{#switch:{{{2|}}}|d=ź|zd=zdź|ł=l|t=ś|st=sć|#default={{{2|}}}j}}e}}
| {{l-self|dsb|{{{1}}}{{{2|}}}a}}
| {{l-self|dsb|{{{1}}}{{{2|}}}om}}
| {{l-self|dsb|{{{1}}}{{{2|}}}oma}}
| {{l-self|dsb|{{{1}}}{{{2|}}}ami}}
| {{l-self|dsb|{{{1}}}{{#switch:{{{2|}}}|d=ź|zd=zdź|ł=l|t=ś|st=sć|#default={{{2|}}}j}}e}}
| {{l-self|dsb|{{{1}}}{{{2|}}}oma}}
| {{l-self|dsb|{{{1}}}{{{2|}}}ach}}
}}<noinclude>{{documentation}}</noinclude>
f7xzvekt0wdwhpvhn4qjptj5l3rpfjh
Mô đun:bibliography/shared
828
421594
2573884
2026-10-27T14:01:51Z
Hiyuune
50834
Trang mới: “local export = {} -- checks whether an ID is an abbreviation or not function export.is_abbrev(sid, is_author) if type(is_author) == "boolean" then return not is_author else return not sid:match(":%d+[a-z]?$") or sid:match("^[A-Z]+:%d+[a-z]?$") end end -- returns the formatted short citation function export.format(sid, is_author) if export.is_abbrev(sid, is_author) then if not sid:match(":%d+[a-z]?$") then local volume_number, preceding_author = sid:match("…”
2573884
Scribunto
text/plain
local export = {}
-- checks whether an ID is an abbreviation or not
function export.is_abbrev(sid, is_author)
if type(is_author) == "boolean" then
return not is_author
else
return not sid:match(":%d+[a-z]?$") or sid:match("^[A-Z]+:%d+[a-z]?$")
end
end
-- returns the formatted short citation
function export.format(sid, is_author)
if export.is_abbrev(sid, is_author) then
if not sid:match(":%d+[a-z]?$") then
local volume_number, preceding_author = sid:match(" %d+$"), sid:match("^.+%f[:]")
if volume_number then sid = sid:gsub(volume_number.."$", "") end
if preceding_author then sid = sid:gsub("^"..preceding_author..":", "") end
sid = "''"..sid:gsub("%^(.+)", "<sup>%1</sup>").."''"
if volume_number then sid = sid..volume_number end
if preceding_author then sid = preceding_author.." "..sid end
return sid
else
-- TODO: clarify situation, hacky solution
return sid:gsub("^(.+):(%d+[a-z]?)$", "''%1'' (%2)")
end
else
return sid:gsub("^(.+):(%d+[a-z]?)$", "%1 (%2)"):gsub("et al.", "''et al.''")
end
end
-- Returns the abbreviation without formatting characters to be used in links and parameters.
function export.strip_markup(sid)
if export.is_abbrev(sid) then
return sid:gsub("%^", "")
end
return sid
end
-- Returns the tracking path for sources.
function export.tracking_path(lang, sid)
return "bibliography/"..lang.."/"..sid
end
-- Container for requiring submodules, in case the path changes.
function export.require_submodule(lang)
return require("Module:bibliography/data/"..lang)
end
-- Applies default_value tables to content tables.
local function apply_default(starting, default)
for index, default_value in pairs(default) do
if type(default_value) == "table" then
if starting[index] ~= nil then
if type(starting[index]) ~= "table" then
-- Assume it is formattable text.
starting[index] = { text = starting[index] }
end
apply_default(starting[index], default_value)
end
elseif starting[index] == nil then
starting[index] = default_value
end
end
end
function export.handle_alias(node, input, first_check, err_msg)
-- Simply return the source if easily accessible. It should ideally be like this in the
-- majority of cases, as the alternative takes time, which is not optimal for a templates
-- which is to be repeated many times in a single page.
if node[input] then
return input, node[input]
end
-- Skim the whole node for compatible children.
for id, child in pairs(node) do
if first_check and first_check(id, child) then
return id, child
end
-- Check each alias.
local alias = child.alias
if alias then
-- Deal with both single aliases and lists thereof.
if type(alias) == "string" then
if alias == input then
return id, child
end
elseif type(alias) == "table" then
for _, v in ipairs(alias) do
if v == input then
return id, child
end
end
end
end
end
-- No node found, print error message if present.
if err_msg then
error(err_msg)
end
end
-- Load sources data from submodule.
function export.extract_source(submodule_data, tracking_lang, input_sid)
-- Find the source in among data.
local sid, source = export.handle_alias(
-- Node to search into.
submodule_data.sources,
-- Value being searched.
input_sid,
-- Before looking for aliases, try stripping markup.
function (id, child)
return export.is_abbrev(id) and (export.strip_markup(id) == input_sid)
end,
-- Error message if everything fails.
"Could not find the source with the ID "..input_sid.." in the database [[Module:bibliography/data/"..tracking_lang.."]]"
)
-- Handle importing.
if source.import_from then
local source_header = source.header
source, tracking_lang, sid = export.extract_source(
export.require_submodule(source.import_from),
source.import_from, sid
)
source.header = source_header
-- Deals with default values.
elseif submodule_data.default_values then
apply_default(source, submodule_data.default_values)
end
return source, tracking_lang, sid
end
-- Adds the ordinal suffix to cardinal number.
function export.ordinal_suffix(i)
local j = i % 10
local k = i % 100
if j == 1 and k ~= 11 then
return i.."st"
elseif j == 2 and k ~= 12 then
return i.."nd"
elseif j == 3 and k ~= 13 then
return i.."rd"
end
return i.."th"
end
return export
hs7noq072ax8cdq6y5gjddltobogbvl
Mô đun:bibliography/links
828
421595
2573887
2026-10-27T14:04:15Z
Hiyuune
50834
Hiyuune đã đổi [[Mô đun:bibliography/links]] thành [[Mô đun:bibliography/shared/links]]
2573887
Scribunto
text/plain
return require [[Mô đun:bibliography/shared/links]]
23mcllibcbjnhplim27u27o2q9g8sd1
ଶ
0
421596
2573907
2026-10-27T15:49:03Z
Lcsnes
40261
Trang mới: “{{minitoc}} {{-Orya-}} {{character info}} ==={{ĐM|letter}}=== {{head|mul|Chữ cái|tr=sha}} #Chữ cái thứ 46 trong bảng chữ cái Oriya. =={{langname|agi}}== {| class="floatright wikitable" style="text-align:center;" |- ! Devanagari | {{l|agi|श}} |- ! Oriya | {{pn}} |- |} ==={{ĐM|pron}}=== *{{IPA4|agi|[ʃə]}} ==={{ĐM|letter}}=== {{head|agi|Chữ cái|tr=śa}} #Chữ cái Oriya biểu thị phụ âm '''{{IPAchar|/ʃ/}}''' trong tiếng Agariya. =={{langnam…”
2573907
wikitext
text/x-wiki
{{minitoc}}
{{-Orya-}}
{{character info}}
==={{ĐM|letter}}===
{{head|mul|Chữ cái|tr=sha}}
#Chữ cái thứ 46 trong bảng chữ cái Oriya.
=={{langname|agi}}==
{| class="floatright wikitable" style="text-align:center;"
|-
! Devanagari
| {{l|agi|श}}
|-
! Oriya
| {{pn}}
|-
|}
==={{ĐM|pron}}===
*{{IPA4|agi|[ʃə]}}
==={{ĐM|letter}}===
{{head|agi|Chữ cái|tr=śa}}
#Chữ cái Oriya biểu thị phụ âm '''{{IPAchar|/ʃ/}}''' trong tiếng Agariya.
=={{langname|aav-bhu}}==
{| class="floatright wikitable" style="text-align:center;"
|-
! Devanagari
| [[श#Tiếng_Bhumij|श]]
|-
! Oriya
| {{pn}}
|-
! Bengal
| [[শ#Tiếng_Bhumij|শ]]
|-
|}
==={{ĐM|pron}}===
*{{IPA4|aav-bhu|[s̪ɔ]}}
==={{ĐM|letter}}===
{{head|aav-bhu|Chữ cái|tr=śa}}
#{{n-g|Phụ âm thứ 30 trong tiếng Bhumij, viết bằng chữ Oriya.}}
==={{ĐM|see}}===
*{{list helper 2|title=Chữ Oriya tiếng Bhumij|list={{l-self|aav-bhu|ଅ|tr=-}}, {{l-self|aav-bhu|ଆ|tr=-}}, {{l-self|aav-bhu|ଇ|tr=-}}, {{l-self|aav-bhu|ଈ|tr=-}}, {{l-self|aav-bhu|ଉ|tr=-}}, {{l-self|aav-bhu|ଊ|tr=-}}, {{l-self|aav-bhu|ଏ|tr=-}}, {{l-self|aav-bhu|ଐ|tr=-}}, {{l-self|aav-bhu|ଓ|tr=-}}, {{l-self|aav-bhu|ଔ|tr=-}}, {{l-self|aav-bhu|କ|tr=-}}, {{l-self|aav-bhu|ଖ|tr=-}}, {{l-self|aav-bhu|ଗ|tr=-}}, {{l-self|aav-bhu|ଘ|tr=-}}, {{l-self|aav-bhu|ଙ|tr=-}}, {{l-self|aav-bhu|ଚ|tr=-}}, {{l-self|aav-bhu|ଛ|tr=-}}, {{l-self|aav-bhu|ଜ|tr=-}}, {{l-self|aav-bhu|ଝ|tr=-}}, {{l-self|aav-bhu|ଞ|tr=-}}, {{l-self|aav-bhu|ଟ|tr=-}}, {{l-self|aav-bhu|ଠ|tr=-}}, {{l-self|aav-bhu|ଡ|tr=-}}, {{l-self|aav-bhu|ଢ|tr=-}}, {{l-self|aav-bhu|ଣ|tr=-}}, {{l-self|aav-bhu|ତ|tr=-}}, {{l-self|aav-bhu|ଥ|tr=-}}, {{l-self|aav-bhu|ଦ|tr=-}}, {{l-self|aav-bhu|ଧ|tr=-}}, {{l-self|aav-bhu|ନ|tr=-}}, {{l-self|aav-bhu|ପ|tr=-}}, {{l-self|aav-bhu|ଫ|tr=-}}, {{l-self|aav-bhu|ବ|tr=-}}, {{l-self|aav-bhu|ଵ|tr=-}}, {{l-self|aav-bhu|ମ|tr=-}}, {{l-self|aav-bhu|ୟ|tr=-}}, {{l-self|aav-bhu|ର|tr=-}}, {{l-self|aav-bhu|ଲ|tr=-}}, {{l-self|aav-bhu|ୱ|tr=-}}, {{l-self|aav-bhu|ଶ|tr=-}}, {{l-self|aav-bhu|ଷ|tr=-}}, {{l-self|aav-bhu|ସ|tr=-}}, {{l-self|aav-bhu|ହ|tr=-}}, {{l-self|aav-bhu|ଡ଼|tr=-}}, {{l-self|aav-bhu|ଢ଼|tr=-}}}}
=={{langname|hne}}==
{| class="floatright wikitable" style="text-align:center;"
|-
! Devanagari
| {{l|hne|श}}
|-
! Oriya
| {{pn}}
|-
|}
==={{ĐM|pron}}===
*{{IPA4|hne|[ʃə]}}
==={{ĐM|letter}}===
{{head|hne|Chữ cái|tr=śa}}
#{{n-g|Phụ âm thứ 30 trong tiếng Chhattisgarh, viết bằng chữ Oriya.}}
==={{ĐM|see}}===
*{{list helper 2|title=Chữ Oriya tiếng Chhattisgarh|list={{l-self|hne|ଅ}}, {{l-self|hne|ଆ}}, {{l-self|hne|ଇ}}, {{l-self|hne|ଈ}}, {{l-self|hne|ଉ}}, {{l-self|hne|ଊ}}, {{l-self|hne|ଋ}}, {{l-self|hne|ଏ}}, {{l-self|hne|ଐ}}, {{l-self|hne|ଓ}}, {{l-self|hne|ଔ}}, {{l-self|hne|ଅଂ}}, {{l-self|hne|ଅଃ}}, {{l-self|hne|ଅଁ}}, {{l-self|hne|କ}}, {{l-self|hne|ଖ}}, {{l-self|hne|ଗ}}, {{l-self|hne|ଘ}}, {{l-self|hne|ଙ}}, {{l-self|hne|ଚ}}, {{l-self|hne|ଛ}}, {{l-self|hne|ଜ}}, {{l-self|hne|ଝ}}, {{l-self|hne|ଞ}}, {{l-self|hne|ଠ}}, {{l-self|hne|ଡ}}, {{l-self|hne|ଢ}}, {{l-self|hne|ଣ}}, {{l-self|hne|ତ}}, {{l-self|hne|ଥ}}, {{l-self|hne|ଦ}}, {{l-self|hne|ଦ}}, {{l-self|hne|ଧ}}, {{l-self|hne|ନ}}, {{l-self|hne|ପ}}, {{l-self|hne|ଫ}}, {{l-self|hne|ବ}}, {{l-self|hne|ଵ}}, {{l-self|hne|ମ}}, {{l-self|hne|ୟ}}, {{l-self|hne|ର}}, {{l-self|hne|ଶ}}, {{l-self|hne|ଷ}}, {{l-self|hne|ସ}}, {{l-self|hne|ହ}}, {{l-self|hne|ଡ଼}}, {{l-self|hne|ଢ଼}}}}
==={{ĐM|ref}}===
*{{Chú thích web|hne|author=Biblica, Inc.|work=पबितर बाईबल,समकालीन छत्तीसगढ़ी अनुवाद|year=2025|entry=निरगमन 1|entryurl=https://www.bible.com/bible/1630/JOL.1.%25E0%25A4%258F%25E0%25A4%25B8%2520%25E0%25A4%25B8%25E0%25A5%2580%2520%25E0%25A4%258F}}
=={{langname|gaq}}==
==={{ĐM|pron}}===
*{{IPA4|gaq|[ʃa]}}
==={{ĐM|letter}}===
{{head|gaq|Chữ cái|tr=śa}}
#Chữ cái Oriya biểu thị phụ âm '''{{IPAchar|/ʃ/}}''' trong tiếng Gata'.
#:{{quote|gaq|ପେଇଂ ଜଦି ସତେଆଃ ମୋ'''ଶା'''କେ ବିସ୍ବାସ୍ ପେପାଡିଙ୍ଗ୍ ଆନେଙ୍ଗ୍ ଡିଗ୍ ବିସ୍ବାସ୍ ପେପାଡିଙ୍ଗ୍ ଡାଗ୍ଲା ମୋ'''ଶା''' ନେଙ୍ଗ୍ ବିସୟ୍ରେ ଗୁଆର୍ ବକେ|tr=Pēiṅ jadi satēāḥ mō'''śā'''kē bisbāsa pēpāḍiṅ ānēṅ ḍig bisbāsa pēpāḍiṅ ḍāglā mō'''śā''' nēṅ bisayrē guār bakē|Vì nếu các ngươi tin Môi-se, cũng sẽ tin ta; bởi ấy là về ta mà người đã chép. ([[s:Giăng/5#5:46|Giăng 5:46]])}}
==={{ĐM|see}}===
*{{list helper 2|title=Chữ Oriya tiếng Gata'|list={{l-self|gaq|ଅ}}, {{l-self|gaq|ଆ}}, {{l-self|gaq|ଇ}}, {{l-self|gaq|ଉ}}, {{l-self|gaq|ଏ}}, {{l-self|gaq|ଓ}}, {{l-self|gaq|କ}}, {{l-self|gaq|ଗ}}, {{l-self|gaq|ଙ}}, {{l-self|gaq|ଚ}}, {{l-self|gaq|ଜ}}, {{l-self|gaq|ଟ}}, {{l-self|gaq|ଡ}}, {{l-self|gaq|ତ}}, {{l-self|gaq|ଦ}}, {{l-self|gaq|ନ}}, {{l-self|gaq|ପ}}, {{l-self|gaq|ବ}}, {{l-self|gaq|ମ}}, {{l-self|gaq|ର}}, {{l-self|gaq|ଡ଼}}, {{l-self|gaq|ଲ}}, {{l-self|gaq|ସ}}, {{l-self|gaq|ହ}}}}
==={{ĐM|ref}}===
*{{Chú thích web|gaq|author=The Word for the World International|entryurl=https://www.bible.com/bible/2781/JHN.5.NTDAA20|entry=ଜହନ୍ 5|work=ସତ୍ ଗାଲି, ଯୀଶୁ ମାପ୍ରୁନେ ତ୍ମି ନିଅମ୍|year=2020}}
=={{langname|hlb}}==
{| class="floatright wikitable" style="text-align:center;"
|-
! Devanagari
| {{l|hlb|श}}
|-
! Oriya
| {{pn}}
|-
|}
==={{ĐM|pron}}===
*{{IPA4|hlb|[ʃa]}}
==={{ĐM|letter}}===
{{head|hlb|Chữ cái|tr=śa}}
#Chữ cái Oriya biểu thị phụ âm '''{{IPAchar|/ʃ/}}''' trong tiếng Halba.
#:{{ux|hlb|ପ୍ରକା'''ଶି'''ତବାକ୍ୟ|tr={{l|hlb|प्रका'''शि'''तवाक्य}}|[[s:Khải huyền|Khải huyền]]|inline=1}}
==={{ĐM|see}}===
*{{list helper 2|title=Chữ Oriya tiếng Halba|list={{l-self|hlb|ଅ}}, {{l-self|hlb|ଆ}}, {{l-self|hlb|ଇ}}, {{l-self|hlb|ଈ}}, {{l-self|hlb|ଉ}}, {{l-self|hlb|ଊ}}, {{l-self|hlb|ଏ}}, {{l-self|hlb|ଓ}}, {{l-self|hlb|ଔ}}, {{l-self|hlb|ଅଂ}}, {{l-self|hlb|କ}}, {{l-self|hlb|ଖ}}, {{l-self|hlb|ଗ}}, {{l-self|hlb|ଘ}}, {{l-self|hlb|ଚ}}, {{l-self|hlb|ଛ}}, {{l-self|hlb|ଜ}}, {{l-self|hlb|ଝ}}, {{l-self|hlb|ଟ}}, {{l-self|hlb|ଠ}}, {{l-self|hlb|ଡ}}, {{l-self|hlb|ଢ}}, {{l-self|hlb|ତ}}, {{l-self|hlb|ଥ}}, {{l-self|hlb|ଦ}}, {{l-self|hlb|ଧ}}, {{l-self|hlb|ନ}}, {{l-self|hlb|ପ}}, {{l-self|hlb|ଫ}}, {{l-self|hlb|ବ}}, {{l-self|hlb|ଭ}}, {{l-self|hlb|ମ}}, {{l-self|hlb|ର}}, {{l-self|hlb|ଲ}}, {{l-self|hlb|ସ}}, {{l-self|hlb|ହ}}, {{l-self|hlb|ୟ}}, {{l-self|hlb|ଵ}}, {{l-self|hlb|ଡ଼}}, {{l-self|hlb|ଢ଼}}}}
==={{ĐM|ref}}===
*{{Chú thích web|hlb|author=Beyond Translation|entryurl=https://www.bible.com/bible/3612/REV.1.HLBNT|entry=प्रकाशितवाक्य 1|work=Halbi|year=2026}}
*{{Chú thích web|author=Fran Woods|entryurl=https://www.webonary.org/halbi/browse/browse-vernacular/?key=hlb&letter=%E0%A4%AE|entry=म|work=Halbi – English Dictionary|publisher=SIL International|year=2019}}
=={{langname|hoc}}==
{| class="floatright wikitable" style="text-align:center;"
|-
! Devanagari
| [[श#Tiếng_Ho|श]]
|-
! Bengal
| [[শ#Tiếng_Ho|শ]]
|-
! Oriya
| {{pn}}
|-
! Telugu
| [[శ#Tiếng_Ho|శ]]
|-
|}
==={{ĐM|pron}}===
*{{IPA4|hoc|[ʃɐ]}}
==={{ĐM|letter}}===
{{head|hoc|Chữ cái|tr=śa}}
#Chữ cái Oriya biểu thị phụ âm '''{{IPAchar|/ʃ/}}''' trong tiếng Ho.
==={{ĐM|see}}===
*{{list helper 2|title=Chữ Oriya tiếng Ho|list={{l-self|hoc|ଅ}}, {{l-self|hoc|ଆ}}, {{l-self|hoc|ଇ}}, {{l-self|hoc|ଈ}}, {{l-self|hoc|ଉ}}, {{l-self|hoc|ଊ}}, {{l-self|hoc|ଏ}}, {{l-self|hoc|ଐ}}, {{l-self|hoc|ଓ}}, {{l-self|hoc|ଔ}}, {{l-self|hoc|କ}}, {{l-self|hoc|କ୍ଷ}}, {{l-self|hoc|ଖ}}, {{l-self|hoc|ଗ}}, {{l-self|hoc|ଘ}}, {{l-self|hoc|ଙ}}, {{l-self|hoc|ଚ}}, {{l-self|hoc|ଛ}}, {{l-self|hoc|ଜ}}, {{l-self|hoc|ଝ}}, {{l-self|hoc|ଟ}}, {{l-self|hoc|ଠ}}, {{l-self|hoc|ଡ}}, {{l-self|hoc|ଡ଼}}, {{l-self|hoc|ଢ}}, {{l-self|hoc|ଣ}}, {{l-self|hoc|ତ}}, {{l-self|hoc|ଥ}}, {{l-self|hoc|ଦ}}, {{l-self|hoc|ଧ}}, {{l-self|hoc|ନ}}, {{l-self|hoc|ପ}}, {{l-self|hoc|ଫ}}, {{l-self|hoc|ବ}}, {{l-self|hoc|ଭ}}, {{l-self|hoc|ମ}}, {{l-self|hoc|ଯ}}, {{l-self|hoc|ୟ}}, {{l-self|hoc|ର}}, {{l-self|hoc|ଲ}}, {{l-self|hoc|ଳ}}, {{l-self|hoc|ୱ}}, {{l-self|hoc|ଶ}}, {{l-self|hoc|ଷ}}, {{l-self|hoc|ସ}}, {{l-self|hoc|ହ}}}}
==={{ĐM|ref}}===
*{{Chú thích web|hoc|author=Indian Evangelical Mission|entryurl=https://live.bible.is/bible/HOCIEM/REV/1|entry=ଜୀସୁ ମାସିଦ ଜହନ୍ତାଃରେଃ ଉଦୁବ୍-ସଦର୍କେଡ୍ ଉନୁଦୁବ୍କ 1|work=HOCIEM|year=2016|publisher=The Bible Society of India}}
=={{langname|jun}}==
==={{ĐM|pron}}===
*{{IPA4|jun|[ʃa]}}
==={{ĐM|letter}}===
{{head|jun|Chữ cái|tr=sa}}
#Chữ cái Oriya biểu thị phụ âm '''{{IPAchar|/ʃ/}}''' trong tiếng Juang.
#:{{quote|jun|[[ଦେଶ#Tiếng_Juang|ଦେ'''ଶ''']]|tr=de'''sɔ'''|[[đất nước]]|inline=1}}
==={{ĐM|see}}===
*{{list helper 2|title=Chữ Oriya tiếng Juang|list={{l-self|jun|ଅ}}, {{l-self|jun|ଆ}}, {{l-self|jun|ଇ}}, {{l-self|jun|ଉ}}, {{l-self|jun|ଏ}}, {{l-self|jun|ଓ}}, {{l-self|jun|କ}}, {{l-self|jun|ଖ}}, {{l-self|jun|ଗ}}, {{l-self|jun|ଘ}}, {{l-self|jun|ଙ}}, {{l-self|jun|ଚ}}, {{l-self|jun|ଛ}}, {{l-self|jun|ଜ}}, {{l-self|jun|ଝ}}, {{l-self|jun|ଞ}}, {{l-self|jun|ଟ}}, {{l-self|jun|ଠ}}, {{l-self|jun|ଡ}}, {{l-self|jun|ଢ}}, {{l-self|jun|ଣ}}, {{l-self|jun|ତ}}, {{l-self|jun|ଥ}}, {{l-self|jun|ଦ}}, {{l-self|jun|ଧ}}, {{l-self|jun|ନ}}, {{l-self|jun|ପ}}, {{l-self|jun|ଫ}}, {{l-self|jun|ବ}}, {{l-self|jun|ଭ}}, {{l-self|jun|ମ}}, {{l-self|jun|ୟ}}, {{l-self|jun|ର}}, {{l-self|jun|ଡ଼}}, {{l-self|jun|ଲ}}, {{l-self|jun|ଳ}}, {{l-self|jun|ସ}}, {{l-self|jun|ହ}}}}
==={{ĐM|ref}}===
*{{Chú thích web|en|author=Patricia J. Donegan and David Stampe|work=[https://web.archive.org/web/20060901080922/http://www.ling.hawaii.edu/austroasiatic/AA/Munda/Dictionaries/Juang Online Juang Dictionary]|year=2004}}
*{{Chú thích web|jun|author=Wycliffe India|entryurl=https://live.bible.is/bible/junwin/mat/1|entry=ମାତିଉ ଲେମେକେତା ଡିଅ ଗାତା 1|work=JUNWIN|year=2016}}
=={{langname|khr}}==
{| class="floatright wikitable" style="text-align:center;"
|-
! Devanagari
| {{l|khr|श}}
|-
! Oriya
| {{pn}}
|-
! Bengal
| {{l|khr|শ}}
|-
|}
==={{ĐM|pron}}===
*{{IPA4|khr|[sa]}}
==={{ĐM|letter}}===
{{head|khr|Chữ cái|tr=śa}}
#{{n-g|Phụ âm thứ 30 trong bảng chữ cái Oriya tiếng Kharia.}}
#:{{ux|khr|'''ଶି'''କାରୀ|tr={{l|khr|'''शि'''कारी}}|[[thợ]] [[săn]]|inline=1}}
==={{ĐM|see}}===
*{{list helper 2|title=Chữ Oriya tiếng Kharia|list={{l-self|khr|ଅ}}, {{l-self|khr|ଆ}}, {{l-self|khr|ଇ}}, {{l-self|khr|ଈ}}, {{l-self|khr|ଉ}}, {{l-self|khr|ଊ}}, {{l-self|khr|ଏ}}, {{l-self|khr|ଐ}}, {{l-self|khr|ଓ}}, {{l-self|khr|ଔ}}, {{l-self|khr|କ}}, {{l-self|khr|ଖ}}, {{l-self|khr|ଗ}}, {{l-self|khr|ଘ}}, {{l-self|khr|ଙ}}, {{l-self|khr|ଚ}}, {{l-self|khr|ଛ}}, {{l-self|khr|ଜ}}, {{l-self|khr|ଝ}}, {{l-self|khr|ଞ}}, {{l-self|khr|ଟ}}, {{l-self|khr|ଠ}}, {{l-self|khr|ଡ}}, {{l-self|khr|ଢ}}, {{l-self|khr|ଣ}}, {{l-self|khr|ତ}}, {{l-self|khr|ଥ}}, {{l-self|khr|ଦ}}, {{l-self|khr|ଧ}}, {{l-self|khr|ନ}}, {{l-self|khr|ପ}}, {{l-self|khr|ଫ}}, {{l-self|khr|ବ}}, {{l-self|khr|ଭ}}, {{l-self|khr|ମ}}, {{l-self|khr|ୟ}}, {{l-self|khr|ର}}, {{l-self|khr|ଲ}}, {{l-self|khr|ୱ}}, {{l-self|khr|ଶ}}, {{l-self|khr|ଷ}}, {{l-self|khr|ସ}}, {{l-self|khr|ହ}}, {{l-self|khr|ଡ଼}}, {{l-self|khr|ଢ଼}}}}
=={{langname|xis}}==
==={{ĐM|pron}}===
*{{IPA4|xis|[sɔ]}}
==={{ĐM|letter}}===
{{head|xis|Chữ cái|tr=śa}}
#Phụ âm thứ 32 trong tiếng Kisan, viết bằng chữ Oriya.
#:{{ux|xis|ପ୍ରକା'''ଶି'''ତ୍|tr=prakā'''śi'''t|[[s:Khải huyền|Khải huyền]]|inline=1}}
==={{ĐM|see}}===
*{{letters|lang=xis|sc=Orya|qual=Chữ Oriya tiếng Kisan|ଅ|ଆ|ଇ|ଈ|ଉ|ଊ|ଋ|ୠ|ଌ|ୡ|ଏ|ଐ|ଓ|ଔ|କ|ଖ|ଗ|ଘ|ଙ|ଚ|ଛ|ଜ|ଝ|ଞ|ଟ|ଠ|ଡ|ଢ|ଣ|ତ|ଥ|ଦ|ଧ|ନ|ପ|ଫ|ବ|ଵ|ଭ|ମ|ଯ|ୟ|ର|ଳ|ଲ|ଶ|ଷ|ସ|ହ|ୱ}}
==={{ĐM|ref}}===
*{{Chú thích web|xis|author=Wycliffe Bible Translators, Inc.|entryurl=https://www.bible.com/bible/3191/REV.1.XIS|entry=ପ୍ରକାଶିତ୍ 1|work=Kisan Bible|year=2024}}
=={{langname|kfc}}==
{| class="floatright wikitable" style="text-align:center;"
|-
! Telugu
| [[శ#Tiếng_Konda-Dora|శ]]
|-
! Oriya
| {{pn}}
|-
|}
==={{ĐM|pron}}===
*{{IPA4|kfc|[ʃɔ]}}
==={{ĐM|letter}}===
{{head|kfc|Chữ cái|tr=śa}}
#Chữ cái Oriya biểu thị phụ âm '''{{IPAchar|/ʃ/}}''' trong tiếng Konda-Dora.
#:{{ux|kfc|ଉପଦେ'''ଶ'''କ|tr=upade'''śa'''ka|[[s:Truyền đạo|Truyền đạo]]|inline=1}}
==={{ĐM|ref}}===
*{{Chú thích web|kfc|author=The Word for the World International|entryurl=https://www.bible.com/bible/2784/ECC.1.KOO25|entry=ଉପଦେଶକ 1|work=Konda Porja Bible, 2025|year=2025}}
=={{langname|kff}}==
{| class="floatright wikitable" style="text-align:center;"
|-
! Telugu
| [[శ#Tiếng_Koya|శ]]
|-
! Oriya
| {{pn}}
|-
! Devanagari
| [[श#Tiếng_Koya|श]]
|-
|}
==={{ĐM|pron}}===
*{{IPA4|kff|[ʃɔ]}}
==={{ĐM|letter}}===
{{head|kff|Chữ cái|tr=śa}}
#Chữ cái Oriya biểu thị phụ âm '''{{IPAchar|/ʃ/}}''' trong tiếng Koya.
#:{{ux|kff|ହିତୋପଦେ'''ଶ'''|tr=hitopade'''śa'''|[[s:Châm ngôn|Châm ngôn]]|inline=1}}
==={{ĐM|ref}}===
*{{Chú thích web|kff|author=The Word for the World International|entryurl=https://www.bible.com/bible/2785/PRO.1.KAA25|entry=ହିତୋପଦେଶ 1|work=Koya Bible|year=2025}}
=={{langname|uki}}==
==={{ĐM|pron}}===
*{{IPA4|uki|[ʃ]}}
==={{ĐM|letter}}===
{{head|uki|Chữ cái}}
#Chữ cái thứ 44 trong bảng chữ cái Oriya tiếng Kui.
==={{ĐM|see}}===
*{{letters|lang=uki|sc=Orya|qual=Chữ Oriya tiếng Kui|ଅ|ଆ|ଇ|ଈ|ଉ|ଊ|ଋ|ୠ|ଌ|ୡ|ଏ|ଐ|ଓ|ଔ|କ|ଖ|ଗ|ଘ|ଙ|ଚ|ଛ|ଜ|ଝ|ଞ|ଟ|ଠ|ଡ|ଢ|ଣ|ତ|ଥ|ଦ|ଧ|ନ|ପ|ଫ|ବ|ଵ|ଭ|ମ|ଯ|ୟ|ର|ଳ|ଲ|ଶ|ଷ|ସ|ହ|ୱ}}
==={{ĐM|ref}}===
*[https://www.kerandikui.in/en/read/kerandi-kui-bible Kerandi Kui Bible]
=={{langname|dwk}}==
==={{ĐM|pron}}===
*{{IPA4|dwk|[ʃ]}}
==={{ĐM|letter}}===
{{head|dwk|Chữ cái}}
#Phụ âm thứ 32 trong tiếng Kui Dawik, viết bằng chữ Oriya.
==={{ĐM|see}}===
*{{letters|lang=dwk|sc=Orya|qual=Chữ Oriya tiếng Kui Dawik|ଅ|ଆ|ଇ|ଈ|ଉ|ଊ|ଋ|ୠ|ଌ|ୡ|ଏ|ଐ|ଓ|ଔ|କ|ଖ|ଗ|ଘ|ଙ|ଚ|ଛ|ଜ|ଝ|ଞ|ଟ|ଠ|ଡ|ଢ|ଣ|ତ|ଥ|ଦ|ଧ|ନ|ପ|ଫ|ବ|ଵ|ଭ|ମ|ଯ|ୟ|ର|ଳ|ଲ|ଶ|ଷ|ସ|ହ|ୱ}}
==={{ĐM|ref}}===
*{{Chú thích web|dwk|author=New Life Computer Institute|entryurl=https://www.bible.com/bible/4238/PHM.1.DWK|entry=ପିଲିମନ 1|work=Dawik Kui|year=2025}}
=={{langname|kxv}}==
==={{ĐM|pron}}===
*{{IPA4|kxv|[ʃ]}}
==={{ĐM|letter}}===
{{head|kxv|Chữ cái}}
#Phụ âm thứ 32 trong tiếng Kuvi, viết bằng chữ Oriya.
==={{ĐM|see}}===
*{{letters|lang=kxv|sc=Orya|qual=Chữ Oriya tiếng Kuvi|ଅ|ଆ|ଇ|ଈ|ଉ|ଊ|ଋ|ୠ|ଌ|ୡ|ଏ|ଐ|ଓ|ଔ|କ|ଖ|ଗ|ଘ|ଙ|ଚ|ଛ|ଜ|ଝ|ଞ|ଟ|ଠ|ଡ|ଢ|ଣ|ତ|ଥ|ଦ|ଧ|ନ|ପ|ଫ|ବ|ଵ|ଭ|ମ|ଯ|ୟ|ର|ଳ|ଲ|ଶ|ଷ|ସ|ହ|ୱ}}
==={{ĐM|ref}}===
*{{Chú thích web|kxv|author=The Word for the World International|entryurl=https://www.bible.com/bible/2787/TIT.2.NTKII20|entry=ତିତସ 2|work=ପୁଃନି ମେ଼ରା, କୁୱି ପୁଃନି ମେ଼ରା|year=2020}}
=={{langname|lbm}}==
{| class="floatright wikitable" style="text-align:center;"
|-
! Devanagari
| [[श#Tiếng_Lodha|श]]
|-
! Oriya
| {{pn}}
|-
|}
==={{ĐM|pron}}===
*{{IPA4|lbm|[ʃ]}}
==={{ĐM|letter}}===
{{head|lbm|Chữ cái}}
#Chữ Oriya biểu thị phụ âm '''{{IPAchar|/ʃ/}}''' trong tiếng Lodha.
#:{{ux|lbm|ମାର୍କ ନେଖ୍ଛେ ଯୀ'''ଶୁ''' ଖ୍ରୀଷ୍ଟର୍ ଭାଲ୍ଖବର|[[s:Mác|Mác]]|inline=1}}
==={{ĐM|ref}}===
*{{Chú thích web|lbm|author=The Word for the World International|entryurl=https://live.bible.is/bible/LBMLHV/MRK/1|entry=ମାର୍କ ନେଖ୍ଛେ ଯୀଶୁ ଖ୍ରୀଷ୍ଟର୍ ଭାଲ୍ଖବର 1|work=Bible.is|year=2020}}
=={{langname|mjx}}==
{| class="floatright wikitable" style="text-align:center;"
|-
! Devanagari
| [[श#Tiếng_Mahal|श]]
|-
! Bengal
| [[শ#Tiếng_Mahal|শ]]
|-
! Oriya
| {{pn}}
|-
|}
==={{ĐM|pron}}===
*{{IPA4|mjx|[ʃ]}}
==={{ĐM|letter}}===
{{head|mjx|Chữ cái|tr=ś}}
#Chữ Oriya biểu thị phụ âm '''{{IPAchar|/ʃ/}}''' trong tiếng Mahal.
#:{{ux|mjx|'''ଶି'''ଳ୍ପପତି|tr={{l|mjx|'''শি'''ল্পপতি}}|[[nhà]] [[tư bản]] [[công nghiệp]]|inline=1}}
==={{ĐM|see}}===
*{{list helper 2|title=Chữ Oriya tiếng Mahal|list={{l-self|mjx|ଅ}}, {{l-self|mjx|ଆ}}, {{l-self|mjx|ଇ}}, {{l-self|mjx|ଈ}}, {{l-self|mjx|ଉ}}, {{l-self|mjx|ଊ}}, {{l-self|mjx|ଏ}}, {{l-self|mjx|ଐ}}, {{l-self|mjx|ଓ}}, {{l-self|mjx|ଔ}}, {{l-self|mjx|ଅଁ}}, {{l-self|mjx|ଇଁ}}, {{l-self|mjx|ଉଁ}}, {{l-self|mjx|ଏଂ}}, {{l-self|mjx|ଔଂ}}, {{l-self|mjx|କ}}, {{l-self|mjx|ଖ}}, {{l-self|mjx|ଗ}}, {{l-self|mjx|ଘ}}, {{l-self|mjx|ଙ}}, {{l-self|mjx|ଚ}}, {{l-self|mjx|ଛ}}, {{l-self|mjx|ଜ}}, {{l-self|mjx|ଝ}}, {{l-self|mjx|ଞ}}, {{l-self|mjx|ଟ}}, {{l-self|mjx|ଠ}}, {{l-self|mjx|ଡ}}, {{l-self|mjx|ଡ଼}}, {{l-self|mjx|ଢ}}, {{l-self|mjx|ଣ}}, {{l-self|mjx|ତ}}, {{l-self|mjx|ଥ}}, {{l-self|mjx|ଦ}}, {{l-self|mjx|ଧ}}, {{l-self|mjx|ନ}}, {{l-self|mjx|ପ}}, {{l-self|mjx|ଫ}}, {{l-self|mjx|ବ}}, {{l-self|mjx|ଭ}}, {{l-self|mjx|ମ}}, {{l-self|mjx|ଯ}}, {{l-self|mjx|ର}}, {{l-self|mjx|ଲ}}, {{l-self|mjx|ସ}}, {{l-self|mjx|ହ}}, {{l-self|mjx|ୱ}}}}
=={{langname|mha}}==
==={{ĐM|pron}}===
*{{IPA4|mha|[ʃ]}}
==={{ĐM|letter}}===
{{head|mha|Chữ cái}}
#Chữ cái thứ 44 trong bảng chữ cái Oriya tiếng Manda.
===={{ĐM|see}}====
*{{letters|lang=mha|sc=Orya|qual=Chữ Oriya tiếng Manda|ଅ|ଆ|ଇ|ଈ|ଉ|ଊ|ଋ|ୠ|ଌ|ୡ|ଏ|ଐ|ଓ|ଔ|କ|ଖ|ଗ|ଘ|ଙ|ଚ|ଛ|ଜ|ଝ|ଞ|ଟ|ଠ|ଡ|ଢ|ଣ|ତ|ଥ|ଦ|ଧ|ନ|ପ|ଫ|ବ|ଵ|ଭ|ମ|ଯ|ୟ|ର|ଳ|ଲ|ଶ|ଷ|ସ|ହ|ୱ}}
==={{ĐM|ref}}===
*{{Chú thích sách|en|author=B Ramakrishna Reddy|title=Manda-English Dictionary|pageurl=https://archive.org/details/dli.language.0615/page/n155|page=140|publisher=Bhasha Research and Publication Centre - Vadodara and Central Institute of Indian Languages|year=2009}}
=={{langname|unx}}==
{| class="floatright wikitable" style="text-align:center;"
|-
! Devanagari
| [[श#Tiếng_Munda|श]]
|-
! Bengal
| [[শ#Tiếng_Munda|শ]]
|-
! Oriya
| {{pn}}
|-
! Latinh
| [[Sh#Tiếng_Munda|Sh]] [[sh#Tiếng_Munda|sh]]
|-
|}
==={{ĐM|pron}}===
*{{IPA4|unx|[ɕa]}}
==={{ĐM|letter}}===
{{head|unx|Chữ cái|tr=śa}}
#{{n-g|Phụ âm thứ 34 trong tiếng Munda, viết bằng chữ Oriya.}}
#:{{quote|unx|ୟୀ'''ଶୁ''' ନେ ଗେଲ୍ବାର୍ ଚେଲାକକେ କୁଲ୍କେଦ୍ତେ ଇନ୍କୁକେ ଆଚୁକତାନ୍ଲଃ ମେତାଦ୍କଆଏ, “ସାଅଁସାର୍ ହଡ଼କତାଃତେ ଆଲ୍ପେ ସେନା ଚାଏ '''ଶ'''ମିରୋନିକଆଃ ଜେତା ସାହାର୍ତେ ଆଲ୍ପେ ବଲଆ ।|tr=yī'''śu''' ne gelbār celākakē kulkedte inkukē ācuktānlaḥ metādkāe, “sā̃sār haḍkatāḥte alpē senā cāe '''śa'''mirōnikāḥ jetā sāhārte alpē balā.|Ấy đó là mười hai sứ-đồ Đức Chúa Jêsus sai đi, và có truyền rằng: Đừng đi đến dân ngoại, cũng đừng vào một thành nào của dân Sa-ma-ri cả; ([[s:Ma-thi-ơ/10#10:5|Ma-thi-ơ 10:5]])}}
==={{ĐM|see}}===
*{{list helper 2|title=Chữ Oriya tiếng Munda|list={{l-self|unx|ଅ}}, {{l-self|unx|ଆ}}, {{l-self|unx|ଇ}}, {{l-self|unx|ଈ}}, {{l-self|unx|ଉ}}, {{l-self|unx|ଊ}}, {{l-self|unx|ଏ}}, {{l-self|unx|ଐ}}, {{l-self|unx|ଓ}}, {{l-self|unx|ଔ}}, {{l-self|unx|କ}}, {{l-self|unx|କ୍ଷ}}, {{l-self|unx|ଖ}}, {{l-self|unx|ଗ}}, {{l-self|unx|ଘ}}, {{l-self|unx|ଙ}}, {{l-self|unx|ଚ}}, {{l-self|unx|ଛ}}, {{l-self|unx|ଜ}}, {{l-self|unx|ଝ}}, {{l-self|unx|ଞ}}, {{l-self|unx|ଟ}}, {{l-self|unx|ଠ}}, {{l-self|unx|ଡ}}, {{l-self|unx|ଡ଼}}, {{l-self|unx|ଢ}}, {{l-self|unx|ଣ}}, {{l-self|unx|ତ}}, {{l-self|unx|ଥ}}, {{l-self|unx|ଦ}}, {{l-self|unx|ଧ}}, {{l-self|unx|ନ}}, {{l-self|unx|ପ}}, {{l-self|unx|ଫ}}, {{l-self|unx|ବ}}, {{l-self|unx|ଭ}}, {{l-self|unx|ମ}}, {{l-self|unx|ଯ}}, {{l-self|unx|ୟ}}, {{l-self|unx|ର}}, {{l-self|unx|ଲ}}, {{l-self|unx|ଳ}}, {{l-self|unx|ୱ}}, {{l-self|unx|ଶ}}, {{l-self|unx|ଷ}}, {{l-self|unx|ସ}}, {{l-self|unx|ହ}}}}
==={{ĐM|ref}}===
*{{Chú thích web|unx|author=The Word for the World International|entryurl=https://www.bible.com/bible/2792/MAT.1.NTRUU20|entry=ମାଥିଉ 1|work=ପାର୍ମେଶ୍ୱାର୍ଆଃ ଜୀନିଦ୍ ବାଚାନ୍, ନାୱା ନିୟାମ୍|year=2020}}
=={{langname|unr}}==
{| class="floatright wikitable" style="text-align:center;"
|-
! Devanagari
| [[श#Tiếng_Mundari|श]]
|-
! Bengal
| [[শ#Tiếng_Mundari|শ]]
|-
! Oriya
| {{pn}}
|-
! Latinh
| [[Sh#Tiếng_Mundari|Sh]] [[sh#Tiếng_Mundari|sh]]
|-
|}
==={{ĐM|pron}}===
*{{IPA4|unr|[ɕa]}}
==={{ĐM|letter}}===
{{head|unr|Chữ cái|tr=śa}}
#{{n-g|Phụ âm thứ 34 trong tiếng Mundari, viết bằng chữ Oriya.}}
#:{{ux|unr|ଓଟେ ଦି'''ଶୁ'''ମ|tr={{l|unr|ओटे दि'''शु'''म}}|[[đất]]|inline=1}}
==={{ĐM|see}}===
*{{list helper 2|title=Chữ Oriya tiếng Mundari|list={{l-self|unr|ଅ}}, {{l-self|unr|ଆ}}, {{l-self|unr|ଇ}}, {{l-self|unr|ଈ}}, {{l-self|unr|ଉ}}, {{l-self|unr|ଊ}}, {{l-self|unr|ଏ}}, {{l-self|unr|ଐ}}, {{l-self|unr|ଓ}}, {{l-self|unr|ଔ}}, {{l-self|unr|କ}}, {{l-self|unr|କ୍ଷ}}, {{l-self|unr|ଖ}}, {{l-self|unr|ଗ}}, {{l-self|unr|ଘ}}, {{l-self|unr|ଙ}}, {{l-self|unr|ଚ}}, {{l-self|unr|ଛ}}, {{l-self|unr|ଜ}}, {{l-self|unr|ଝ}}, {{l-self|unr|ଞ}}, {{l-self|unr|ଟ}}, {{l-self|unr|ଠ}}, {{l-self|unr|ଡ}}, {{l-self|unr|ଡ଼}}, {{l-self|unr|ଢ}}, {{l-self|unr|ଣ}}, {{l-self|unr|ତ}}, {{l-self|unr|ଥ}}, {{l-self|unr|ଦ}}, {{l-self|unr|ଧ}}, {{l-self|unr|ନ}}, {{l-self|unr|ପ}}, {{l-self|unr|ଫ}}, {{l-self|unr|ବ}}, {{l-self|unr|ଭ}}, {{l-self|unr|ମ}}, {{l-self|unr|ଯ}}, {{l-self|unr|ୟ}}, {{l-self|unr|ର}}, {{l-self|unr|ଲ}}, {{l-self|unr|ଳ}}, {{l-self|unr|ୱ}}, {{l-self|unr|ଶ}}, {{l-self|unr|ଷ}}, {{l-self|unr|ସ}}, {{l-self|unr|ହ}}}}
==={{ĐM|ref}}===
*{{Chú thích web|author=Enike Amina Wani, Martin Lomu Goke & Tim Stirtz|work=Mundari-English Dictionary|year=2013|publisher=SIL International|entryurl=https://www.webonary.org/mundari/overview/copyright/beli-english/?letter=R&key=qaa-Latg-SD-x-mqu|entry=R}}
*{{Chú thích web|unr|author=The Word for the World International|entryurl=https://live.bible.is/bible/UNRWFW/MAT/1|entry=ମାଥିଉ 1|work=UNRWFW|year=2018}}
=={{langname|or}}==
==={{ĐM|pron}}===
*{{or-IPA}}
*{{audio|or|Or-ଶ.oga|Âm thanh}}
*{{audio|or|LL-Q4850727-Psubhashish-ଶ.wav|Âm thanh}}
*{{audio|or|Or-ଶ.wav|Âm thanh}}
==={{ĐM|letter}}===
{{head|or|Chữ cái}}
#Chữ cái thứ 46 trong bảng chữ cái Oriya.
#:{{ux|or|[[ତେତିଶ#Tiếng_Oriya|ତେତି'''ଶ''']]|[[ba mươi]] [[ba]]|inline=1}}
==={{ĐM|see}}===
*{{letters|lang=or|sc=Orya|qual=Bảng chữ Oriya|ଅ|ଆ|ଇ|ଈ|ଉ|ଊ|ଋ|ୠ|ଌ|ୡ|ଏ|ଐ|ଓ|ଔ|କ|ଖ|ଗ|ଘ|ଙ|ଚ|ଛ|ଜ|ଝ|ଞ|ଟ|ଠ|ଡ|ଢ|ଣ|ତ|ଥ|ଦ|ଧ|ନ|ପ|ଫ|ବ|ଵ|ଭ|ମ|ଯ|ୟ|ର|ଳ|ଲ|ଶ|ଷ|ସ|ହ|ୱ}}
=={{langname|sa}}==
==={{ĐM|alt sc}}===
{{sa-alt|Deva=श}}
==={{ĐM|pron}}===
{{san-IPA|श}}
==={{ĐM|letter}}===
{{head|sa|Chữ cái}}
#Chữ cái thứ 44 trong bảng chữ cái Oriya tiếng Phạn.
#:{{ux|sa|ପ୍ରକା'''ଶି'''ତଂ|tr=prakā'''śi'''taṃ|[[s:Khải huyền|Khải huyền]]|inline=1}}
===={{ĐM|see}}====
*{{letters|lang=sa|sc=Orya|qual=Chữ Oriya tiếng Phạn|ଅ|ଆ|ଇ|ଈ|ଉ|ଊ|ଋ|ୠ|ଌ|ୡ|ଏ|ଐ|ଓ|ଔ|କ|ଖ|ଗ|ଘ|ଙ|ଚ|ଛ|ଜ|ଝ|ଞ|ଟ|ଠ|ଡ|ଢ|ଣ|ତ|ଥ|ଦ|ଧ|ନ|ପ|ଫ|ବ|ଵ|ଭ|ମ|ଯ|ୟ|ର|ଳ|ଲ|ଶ|ଷ|ସ|ହ|ୱ}}
==={{ĐM|ref}}===
*{{Chú thích web|sa|author=Sanskrit Bible|entryurl=https://www.bible.com/bible/2118/REV.1.SANOR|entry=ପ୍ରକାଶିତଂ 1|work=ସତ୍ୟୱେଦଃ|year=1851}}
=={{langname|sck}}==
{| class="floatright wikitable" style="text-align:center;"
|-
! Devanagari
| {{l|sck|श}}
|-
! Oriya
| {{pn}}
|-
! Bengal
| {{l|sck|শ}}
|-
! Kaithi
| {{l|sck|𑂬}}
|-
|}
==={{ĐM|pron}}===
*{{IPA4|sck|[sa]}}
==={{ĐM|letter}}===
{{head|sck|Chữ cái|tr=śa}}
#{{n-g|Phụ âm thứ 30 trong tiếng Sadri, viết bằng chữ Oriya.}}
#:{{ux|sck|ପ୍ରକା'''ଶି'''ତବାକ୍ୟ|tr=prakā'''śi'''tavākya|[[s:Khải huyền|Khải huyền]]|inline=1}}
==={{ĐM|see}}===
*{{list helper 2|title=Chữ Oriya tiếng Sadri|list={{l-self|sck|ଅ}}, {{l-self|sck|ଆ}}, {{l-self|sck|ଇ}}, {{l-self|sck|ଈ}}, {{l-self|sck|ଉ}}, {{l-self|sck|ଊ}}, {{l-self|sck|ଏ}}, {{l-self|sck|ଐ}}, {{l-self|sck|ଓ}}, {{l-self|sck|ଔ}}, {{l-self|sck|କ}}, {{l-self|sck|ଖ}}, {{l-self|sck|ଗ}}, {{l-self|sck|ଘ}}, {{l-self|sck|ଙ}}, {{l-self|sck|ଚ}}, {{l-self|sck|ଛ}}, {{l-self|sck|ଜ}}, {{l-self|sck|ଝ}}, {{l-self|sck|ଞ}}, {{l-self|sck|ଟ}}, {{l-self|sck|ଠ}}, {{l-self|sck|ଡ}}, {{l-self|sck|ଢ}}, {{l-self|sck|ଣ}}, {{l-self|sck|ତ}}, {{l-self|sck|ଥ}}, {{l-self|sck|ଦ}}, {{l-self|sck|ଧ}}, {{l-self|sck|ନ}}, {{l-self|sck|ପ}}, {{l-self|sck|ଫ}}, {{l-self|sck|ବ}}, {{l-self|sck|ଵ}}, {{l-self|sck|ମ}}, {{l-self|sck|ୟ}}, {{l-self|sck|ର}}, {{l-self|sck|ଲ}}, {{l-self|sck|ୱ}}, {{l-self|sck|ଶ}}, {{l-self|sck|ଷ}}, {{l-self|sck|ସ}}, {{l-self|sck|ହ}}, {{l-self|sck|ଡ଼}}, {{l-self|sck|ଢ଼}}, {{l-self|sck|କ୍ଷ}}}}
==={{ĐM|ref}}===
*{{Chú thích web|hi|author=Toby Anderson|year=2020|entryurl=https://www.webonary.org/sadri/browse/browse-vernacular-english/?key=sck&letter=%E0%A4%AA|entry=प|publisher=SIL International|work=Sadri Dictionary}}
*{{Chú thích web|sck|author=Beyond Translation|work=सादरी नवाँ नियम|year=2026|entry=प्रकाशितवाक्य 1|entryurl=https://www.bible.com/bible/3683/REV.1.SCK}}
[[Thể loại:Khối ký tự Oriya|36]]
p04vaove4jgm3owpre5mfcjy1qvwf91
Bản mẫu:smc
10
421597
2573908
2026-10-27T15:51:20Z
TheHighFighter2
42988
/* */
2573908
wikitext
text/x-wiki
#đổi [[Bản mẫu:smallcaps]]
1sone4o5e7qp2yfxlgufbo29wc5j60b
Thể loại:caq:Cỏ
14
421598
2573913
2026-10-27T16:01:03Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2573913
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:caq:Thực vật
14
421599
2573914
2026-10-27T16:01:05Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2573914
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:caq:Nhánh Thài lài
14
421600
2573915
2026-10-27T16:01:16Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2573915
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:caq:Bộ xương
14
421601
2573919
2026-10-27T16:02:57Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2573919
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:caq:Giao thông
14
421602
2573922
2026-10-27T16:03:34Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2573922
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:caq:Trái cây
14
421603
2573925
2026-10-27T16:04:46Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2573925
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:caq:Uống
14
421604
2573926
2026-10-27T16:04:48Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2573926
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Etelä-Vietnam
0
421605
2573931
2026-10-27T23:18:06Z
Hiyuune
50834
/* */
2573931
wikitext
text/x-wiki
=={{langname|fi}}==
==={{section|etym}}===
{{com|fi|etelä|Vietnam}}
==={{section|pron}}===
{{fi-p}}
==={{section|pr-noun}}===
{{head|fi|proper noun|head=[[etelä|Etelä]]-[[Vietnam]]}}
# {{tcl|ja|Việt Nam Cộng hòa|id=Q180573}}
===={{section|decl}}====
{{fi-decl-risti|n=sg|Etelä-Vietnam|||a|i=0}}
8nv5z5worycqmhd39csrrsx20vhjimc
2573932
2573931
2026-10-27T23:18:30Z
Hiyuune
50834
2573932
wikitext
text/x-wiki
=={{langname|fi}}==
==={{section|etym}}===
{{com|fi|etelä|Vietnam}}
==={{section|pron}}===
{{fi-p}}
==={{section|pr-noun}}===
{{head|fi|Danh từ riêng|head=[[etelä|Etelä]]-[[Vietnam]]}}
# {{tcl|ja|Việt Nam Cộng hòa|id=Q180573}}
===={{section|decl}}====
{{fi-decl-risti|n=sg|Etelä-Vietnam|||a|i=0}}
92wqz8zu3j6q34px43ibkb399ygzo8u
Từ tái tạo:Tiếng Slav nguyên thủy/vino
0
421606
2573935
2026-10-27T23:27:09Z
Hiyuune
50834
Trang mới: “{{reconstructed}} =={{langname|sla-pro}}== ==={{section|etym}}=== {{etymon|sla-pro|id=rượu vang|:der|la:vīnum}} Từ {{der|sla-pro|la|vīnum}} (có khả năng thông qua {{cog|gem-pro|*wīną}}), từ {{der|sla-pro|itc-pro|*wīnom}}, cuối cùng từ {{der|sla-pro|ine-pro|*wóyh₁nom}}.<ref>{{R:ru:Vasmer|вино́}}</ref> ==={{section|n}}=== {{sla-noun|n|head=*vīnò}} # [[rượu vang|Rượu vang]]. ===={{section|desc}}==== {{hrow}} * Slav Đông: ** {{desct…”
2573935
wikitext
text/x-wiki
{{reconstructed}}
=={{langname|sla-pro}}==
==={{section|etym}}===
{{etymon|sla-pro|id=rượu vang|:der|la:vīnum}}
Từ {{der|sla-pro|la|vīnum}} (có khả năng thông qua {{cog|gem-pro|*wīną}}), từ {{der|sla-pro|itc-pro|*wīnom}}, cuối cùng từ {{der|sla-pro|ine-pro|*wóyh₁nom}}.<ref>{{R:ru:Vasmer|вино́}}</ref>
==={{section|n}}===
{{sla-noun|n|head=*vīnò}}
# [[rượu vang|Rượu vang]].
===={{section|desc}}====
{{hrow}}
* Slav Đông:
** {{desctree|orv|вино|alt=вино́}}
** {{desc|zle-ono|вино}}
* Slav Nam:
** {{desc|cu|-}}
**: {{desc|cu|вино|sclang=1}}
**: {{desc|cu|ⰲⰻⱀⱁ|sclang=1}}
** {{desc|bg|ви́но}}
** {{desc|mk|вино}}
** {{desc|sh|-}}
**: {{desc|sh|ви́но|sclang=1}}
**: {{desc|sh|víno|sclang=1}}
** {{desc/sl-tonal|víno}}
* Slav Tây:
** {{desc|zlw-ocs|víno}}
*** {{desc|cs|víno}}
** {{desc|pox|*vainĕ}}
** {{desc|zlw-opl|wino}}
*** {{desc|pl|wino}}
** Pomerania:
*** {{desc|csb|wino}}
*** {{desc|zlw-slv|wjynô}}
** Slovak cổ:
*** {{desc|rsk|вино}}
*** {{desc|sk|víno}}
** Sorb:
*** {{desc|hsb|wino}}
*** {{desc|dsb|wino}}
==={{section|ref}}===
{{reflist}}
[[en:Reconstruction:Proto-Slavic/vino]]
gjiwo66c0founvawns3tpxvzp5n5jvh
quần què
0
421607
2573951
2026-10-28T06:30:12Z
~2026-57721-42
62144
Trang mới: “=={{langname|vi}}== ==={{ĐM|etym}}=== {{com|vi|quần|què}}. ==={{ĐM|pron}}=== {{vi-IPA}} ==={{ĐM|noun}}=== {{vi-noun}} # Một từ chửi.”
2573951
wikitext
text/x-wiki
=={{langname|vi}}==
==={{ĐM|etym}}===
{{com|vi|quần|què}}.
==={{ĐM|pron}}===
{{vi-IPA}}
==={{ĐM|noun}}===
{{vi-noun}}
# Một từ chửi.
s47zypdmwl0xc6mujrlzyi3l8w8l9nn
Thể loại:Liên kết có phân mảnh thủ công tiếng Latinh
14
421608
2573954
2026-10-28T06:34:15Z
Hiyuune
50834
Trang mới: “{{auto cat}}”
2573954
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
gardinus
0
421609
2573955
2026-10-28T06:38:05Z
Hiyuune
50834
Trang mới: “=={{langname|la}}== ==={{ĐM|alt}}=== * {{alter|la|gardīnum|jardīnus}} ==={{ĐM|etym}}=== {{etymon|la|:bor|frk:*gardō<id:hàng rào>|id=vườn}} Từ {{bor|la|frk|*gardō}}, từ {{der|la|gem-pro|*gardô}}, {{m|gem-pro|*gardaz}}. ==={{section|pron}}=== * {{la-IPA|gardīnus|eccl=yes}} ==={{section|n}}=== {{la-noun|gardīnus<2.2nd>}} # {{lb|la|Medieval Latin}} [[vườn]], [[công viên]] ===={{section|decl}}==== {{la-ndecl|gardīnus<2.2nd>}} ===={{section|desc}}…”
2573955
wikitext
text/x-wiki
=={{langname|la}}==
==={{ĐM|alt}}===
* {{alter|la|gardīnum|jardīnus}}
==={{ĐM|etym}}===
{{etymon|la|:bor|frk:*gardō<id:hàng rào>|id=vườn}}
Từ {{bor|la|frk|*gardō}}, từ {{der|la|gem-pro|*gardô}}, {{m|gem-pro|*gardaz}}.
==={{section|pron}}===
* {{la-IPA|gardīnus|eccl=yes}}
==={{section|n}}===
{{la-noun|gardīnus<2.2nd>}}
# {{lb|la|Medieval Latin}} [[vườn]], [[công viên]]
===={{section|decl}}====
{{la-ndecl|gardīnus<2.2nd>}}
===={{section|desc}}====
* {{desctree|fro|gardin}}
* {{desctree|scn|jardinu}}
==={{section|ref}}===
* {{R:la:du Cange}}
4qc2lfap87iq496vgaoaih9fnpkrsdn
ལེང
0
421610
2573956
2026-10-28T06:40:10Z
Hiyuune
50834
Trang mới: “=={{langname|xkz}}== ==={{section|etym}}=== Từ {{inh|xkz|sit-bdi-pro|*liŋ}}, từ {{inh|xkz|sit-pro|*mV-liŋ}}. Các từ cùng gốc bao gồm {{cog|dz|གཞི}} và {{cog|bo|ཞིང}}. ==={{section|pron}}=== * {{xkz-IPA}} ==={{section|n]}=== {{head|xkz|Danh từ}} # [[vườn]]. ==={{section|ref}}=== * {{R:xkz:Hyslop:2016|page=210}}”
2573956
wikitext
text/x-wiki
=={{langname|xkz}}==
==={{section|etym}}===
Từ {{inh|xkz|sit-bdi-pro|*liŋ}}, từ {{inh|xkz|sit-pro|*mV-liŋ}}. Các từ cùng gốc bao gồm {{cog|dz|གཞི}} và {{cog|bo|ཞིང}}.
==={{section|pron}}===
* {{xkz-IPA}}
==={{section|n]}===
{{head|xkz|Danh từ}}
# [[vườn]].
==={{section|ref}}===
* {{R:xkz:Hyslop:2016|page=210}}
4sw3rib1shrn5tw8z7f40b63l09ev0n
2573957
2573956
2026-10-28T06:40:22Z
Hiyuune
50834
/* Cách phát âm */ (sử dụng [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]])
2573957
wikitext
text/x-wiki
=={{langname|xkz}}==
==={{section|etym}}===
Từ {{inh|xkz|sit-bdi-pro|*liŋ}}, từ {{inh|xkz|sit-pro|*mV-liŋ}}. Các từ cùng gốc bao gồm {{cog|dz|གཞི}} và {{cog|bo|ཞིང}}.
==={{section|pron}}===
* {{xkz-IPA}}
==={{section|n}}===
{{head|xkz|Danh từ}}
# [[vườn]].
==={{section|ref}}===
* {{R:xkz:Hyslop:2016|page=210}}
9awts8brge7z1o5kwb6u135puv8krev
Từ tái tạo:Tiếng Bod nguyên thủy/liŋ
0
421611
2573958
2026-10-28T06:43:38Z
Hiyuune
50834
Trang mới: “{{reconstructed}} =={{langname|sit-bdi-pro}}== ==={{section|etym}}=== Từ {{inh|sit-bdi-pro|sit-pro|*mV-liŋ}}. ==={{section|n}}=== {{head|sit-bdi-pro|Danh từ|head=*liŋ}}<ref>{{R:sit:Bodt 2023|86}}</ref> # [[cánh đồng]]. ===={{section|desc}}==== * Tạng ** {{desc|bo|ཞིང}} * Dakpa-Dzala ** {{desc|dka|ལེང}} * Bod Đông ** {{desc|xkf|ལེང}} ** {{desc|xkz|ལེང|t=garden|der2=1|ས་ལེང}} ==={{section|ref}}=== {{reflist}} en:Recons…”
2573958
wikitext
text/x-wiki
{{reconstructed}}
=={{langname|sit-bdi-pro}}==
==={{section|etym}}===
Từ {{inh|sit-bdi-pro|sit-pro|*mV-liŋ}}.
==={{section|n}}===
{{head|sit-bdi-pro|Danh từ|head=*liŋ}}<ref>{{R:sit:Bodt 2023|86}}</ref>
# [[cánh đồng]].
===={{section|desc}}====
* Tạng
** {{desc|bo|ཞིང}}
* Dakpa-Dzala
** {{desc|dka|ལེང}}
* Bod Đông
** {{desc|xkf|ལེང}}
** {{desc|xkz|ལེང|t=garden|der2=1|ས་ལེང}}
==={{section|ref}}===
{{reflist}}
[[en:Reconstruction:Proto-Bodish/liŋ]]
s3ijn3zdo7gk68o0l6aqpmbeap63ayb
2573988
2573958
2026-10-28T10:08:29Z
TheHighFighter2
42988
/* Hậu duệ */
2573988
wikitext
text/x-wiki
{{reconstructed}}
=={{langname|sit-bdi-pro}}==
==={{section|etym}}===
Từ {{inh|sit-bdi-pro|sit-pro|*mV-liŋ}}.
==={{section|n}}===
{{head|sit-bdi-pro|Danh từ|head=*liŋ}}<ref>{{R:sit:Bodt 2023|86}}</ref>
# [[cánh đồng]].
===={{section|desc}}====
* Tạng
** {{desc|bo|ཞིང}}
* Dakpa-Dzala
** {{desc|dka|ལེང}}
* Bod Đông
** {{desc|xkf|ལེང}}
** {{desc|xkz|ལེང|t=vườn|der2=1|ས་ལེང}}
==={{section|ref}}===
{{reflist}}
[[en:Reconstruction:Proto-Bodish/liŋ]]
g8ui2wairyx9bevp8avzrphhn92yehi
Mô đun:category tree/hierarchy
828
421612
2573961
2026-10-28T07:10:59Z
Hiyuune
50834
Trang mới: “local export = {} local function link(name) return "[[:Thể loại:" .. mw.language.new("vi"):ucfirst(name) .. " theo ngôn ngữ|" .. name .. "]]" end local function link_already_seen(name) return ("''%s''"):format(name) end function export.show(frame) return require("Module:hierarchy").show { labels = require("Module:category tree/data").LABELS, parents_field = "parents", link = link, link_already_seen = link_already_seen, } end return export”
2573961
Scribunto
text/plain
local export = {}
local function link(name)
return "[[:Thể loại:" .. mw.language.new("vi"):ucfirst(name) .. " theo ngôn ngữ|" .. name .. "]]"
end
local function link_already_seen(name)
return ("''%s''"):format(name)
end
function export.show(frame)
return require("Module:hierarchy").show {
labels = require("Module:category tree/data").LABELS,
parents_field = "parents",
link = link,
link_already_seen = link_already_seen,
}
end
return export
r3cc2hlmrm92rrfzugphab247yijc5y
Mô đun:hierarchy
828
421613
2573962
2026-10-28T07:12:36Z
Hiyuune
50834
Trang mới: “local export = {} local insert = table.insert local concat = table.concat local ulower = mw.ustring.lower local dump = mw.dumpObject local function color(text) return "<span style=\"color:var(--wikt-palette-green-9,green);\">" .. text .. "</span>" end local function red(text) return "<span style=\"color:var(--wikt-palette-red-9,red);\">" .. text .. "</span>" end function export.show(data) local labels, parents_field, link, link_already_seen, exclude = data.labels,…”
2573962
Scribunto
text/plain
local export = {}
local insert = table.insert
local concat = table.concat
local ulower = mw.ustring.lower
local dump = mw.dumpObject
local function color(text)
return "<span style=\"color:var(--wikt-palette-green-9,green);\">" .. text .. "</span>"
end
local function red(text)
return "<span style=\"color:var(--wikt-palette-red-9,red);\">" .. text .. "</span>"
end
function export.show(data)
local labels, parents_field, link, link_already_seen, exclude =
data.labels, data.parents_field, data.link, data.link_already_seen, data.exclude
exclude = exclude or {}
link_already_seen = link_already_seen or link
local roots = {}
local unlocatable_parents = {}
-- First compute the children of each label, inverting the parents mapping.
local children = {}
for key, val in pairs(labels) do
local parents = val[parents_field]
local has_locatable_parents = false
if parents then
if type(parents) == "string" then
parents = {parents}
end
if not parents[1] then
parents = {parents}
end
for i, parent in ipairs(parents) do
if type(parent) == "table" then
parent = parent.name or parent.template or ("error: "..key)
end
if not exclude[parent] then
--mw.log(key, val, upper_rank, dump(children[parent]))
if not children[parent] then
children[parent] = {}
end
children[parent][key] = i == 1
end
if not labels[parent] then
unlocatable_parents[parent] = true
else
has_locatable_parents = true
end
end
if not has_locatable_parents then
roots[key] = true
end
end
end
-- Now compute the max depth of each child, and the parent at that max depth. We do this so that if a given node
-- has multiple children, it's shown at the max depth so we have as few disconnected trees as possible.
local cycles = {}
local seen = {}
local function traverse_for_cycles(key)
if seen[key] then
cycles[key] = true
seen[key] = false
return
end
if children[key] == nil then
mw.log(("Key %s not in children[] table"):format(key))
return
end
seen[key] = true
for child, _ in pairs(children[key]) do
traverse_for_cycles(child)
end
seen[key] = false
end
for root, _ in pairs(roots) do
traverse_for_cycles(root)
end
-- Now draw the tree.
local result = {}
local function ins(txt)
insert(result, txt)
end
local function compare_caseless(a, b)
return ulower(a) < ulower(b)
end
local function insert_node(node, parent)
local function insert_cycle_indicator()
if cycles[node] then
ins(" " .. red("[CYCLE]"))
end
end
if parent and not children[parent] then
error(("Internal error: Key %s not in children[] table"):format(parent))
end
if not parent or children[parent][node] then
ins(link(node))
insert_cycle_indicator()
ins("\n")
return true
end
local numchild = 0
if children[node] then
for child, _ in pairs(children[node]) do
numchild = numchild + 1
end
end
ins(link_already_seen(node))
insert_cycle_indicator()
if numchild > 0 then
ins((" (%s child%s)"):format(numchild, numchild > 1 and "ren" or ""))
end
ins("\n")
return false
end
local function traverse_for_draw(key, depth, prefix)
local children_list = {}
if not children[key] then
mw.log(("Key %s not in children[] table, depth=%s"):format(key, depth))
return
end
for child, _ in pairs(children[key]) do
insert(children_list, child)
end
table.sort(children_list, compare_caseless)
local numchild = #children_list
for i, child in ipairs(children_list) do
if i < numchild then
ins(color(prefix .. "├──"))
if insert_node(child, key) then
traverse_for_draw(child, depth + 1, prefix .. "│ ")
end
else
ins(color(prefix .. "└──"))
if insert_node(child, key) then
traverse_for_draw(child, depth + 1, prefix .. " ")
end
end
end
end
local root_list = {}
for root, _ in pairs(roots) do
insert(root_list, root)
end
table.sort(root_list, compare_caseless)
for _, root in ipairs(root_list) do
insert_node(root, nil)
traverse_for_draw(root, 0, "")
end
return "<pre>\n" .. concat(result) .. "</pre>"
end
return export
3394hbjtn17vt3gurrn1eduqi2j7mfz
Mô đun:category tree/hierarchy/tài liệu
828
421614
2573963
2026-10-28T07:13:06Z
Hiyuune
50834
Trang mới: “{{#invoke:category tree/hierarchy|show}}”
2573963
wikitext
text/x-wiki
{{#invoke:category tree/hierarchy|show}}
q1uqgxmvavdaazm68qn9d2xjh2fkahq
一犬
0
421615
2573972
2026-10-28T08:24:18Z
Kelly zhrm
58416
Trang mới: “=={{langname|ja}}== {{ja-kanjitab|いち|k1=いっ|けん|yomi=o}} ==={{ĐM|pron}}=== {{ja-pron|いっけん|acc=0|acc_ref=DJR}} ==={{ĐM|n}}=== {{ja-noun|いっけん}} # Một con [[chó]]. ===={{ĐM|drv}}==== * {{ja-r|一犬虚に吠ゆれば万犬実を伝う|いっけん きょ に ほゆれば ばんけん じつ を つたう}} ==={{ĐM|ref}}=== {{reflist}}”
2573972
wikitext
text/x-wiki
=={{langname|ja}}==
{{ja-kanjitab|いち|k1=いっ|けん|yomi=o}}
==={{ĐM|pron}}===
{{ja-pron|いっけん|acc=0|acc_ref=DJR}}
==={{ĐM|n}}===
{{ja-noun|いっけん}}
# Một con [[chó]].
===={{ĐM|drv}}====
* {{ja-r|一犬虚に吠ゆれば万犬実を伝う|いっけん きょ に ほゆれば ばんけん じつ を つたう}}
==={{ĐM|ref}}===
{{reflist}}
f5cxj5wemqix4oxoac3oowudo5372zz
いっけん
0
421616
2573973
2026-10-28T08:24:51Z
Kelly zhrm
58416
Trang mới: “=={{langname|ja}}== {{ja-see|一犬|一件|一見|一軒|一間}}”
2573973
wikitext
text/x-wiki
=={{langname|ja}}==
{{ja-see|一犬|一件|一見|一軒|一間}}
5dnwzskdtrc9by6vc9zxlca5blck1so
ikken
0
421617
2573974
2026-10-28T08:25:34Z
Kelly zhrm
58416
Trang mới: “=={{langname|ja}}== ==={{ĐM|rom}}=== {{ja-romaji}} # {{ja-romanization of|いっけん}}”
2573974
wikitext
text/x-wiki
=={{langname|ja}}==
==={{ĐM|rom}}===
{{ja-romaji}}
# {{ja-romanization of|いっけん}}
pd5wu29s2343m073jh864a313mazhkb
一軒
0
421618
2573976
2026-10-28T08:28:53Z
Kelly zhrm
58416
Trang mới: “=={{langname|ja}}== {{ja-kanjitab|いち|k1=いっ|けん|y=goon,kanon}} ==={{ĐM|pron}}=== {{ja-pron|いっけん}} ==={{ĐM|n}}=== {{ja-noun|いっけん}} # Một [[ngôi nhà]]. ===={{ĐM|rel}}==== * {{ja-r|一軒家|いっけんや}} {{c|ja|Tòa nhà|Một}}”
2573976
wikitext
text/x-wiki
=={{langname|ja}}==
{{ja-kanjitab|いち|k1=いっ|けん|y=goon,kanon}}
==={{ĐM|pron}}===
{{ja-pron|いっけん}}
==={{ĐM|n}}===
{{ja-noun|いっけん}}
# Một [[ngôi nhà]].
===={{ĐM|rel}}====
* {{ja-r|一軒家|いっけんや}}
{{c|ja|Tòa nhà|Một}}
pjnkihxtqsnyqm6d1qq9wun9ep3j8s6
Mô đun:vi-sortkey/tài liệu
828
421619
2573985
2026-10-28T09:16:45Z
Hiyuune
50834
Trang mới: “==Ví dụ== {{#invoke:vi-sortkey|showSortkey|Tuyên ngôn toàn thế giới về nhân quyền của Liên Hợp Quốc|công bằng|Đại|Ác-si-mét|tâm lý}} {{#invoke:vi-sortkey|showSorting|ngôn|ngon|ngoy|ngón|ngôn|ngôi|ngọn|nghệ|nghề}} {{top5}} {{#invoke:vi-sortkey|showSorting|à|àc|àn|ằ|ằc|ằn|ầ|ầc|ần|è|èc|èn|ề|ềc|ền|ì|ìc|ìn|ò|òc|òn|ồ|ồc|ồn|ờ|ờc|ờn|ù|ùc|ùn|ừ|ừc|ừn|ỳ|ỳc|ỳn|ả|ảc|ản|ẳ|ẳc|…”
2573985
wikitext
text/x-wiki
==Ví dụ==
{{#invoke:vi-sortkey|showSortkey|Tuyên ngôn toàn thế giới về nhân quyền của Liên Hợp Quốc|công bằng|Đại|Ác-si-mét|tâm lý}}
{{#invoke:vi-sortkey|showSorting|ngôn|ngon|ngoy|ngón|ngôn|ngôi|ngọn|nghệ|nghề}}
{{top5}}
{{#invoke:vi-sortkey|showSorting|à|àc|àn|ằ|ằc|ằn|ầ|ầc|ần|è|èc|èn|ề|ềc|ền|ì|ìc|ìn|ò|òc|òn|ồ|ồc|ồn|ờ|ờc|ờn|ù|ùc|ùn|ừ|ừc|ừn|ỳ|ỳc|ỳn|ả|ảc|ản|ẳ|ẳc|ẳn|ẩ|ẩc|ẩn|ẻ|ẻc|ẻn|ể|ểc|ển|ỉ|ỉc|ỉn|ỏ|ỏc|ỏn|ổ|ổc|ổn|ở|ởc|ởn|ủ|ủc|ủn|ử|ửc|ửn|ỷ|ỷc|ỷn|ã|ãc|ãn|ẵ|ẵc|ẵn|ẫ|ẫc|ẫn|ẽ|ẽc|ẽn|ễ|ễc|ễn|ĩ|ĩc|ĩn|õ|õc|õn|ỗ|ỗc|ỗn|ỡ|ỡc|ỡn|ũ|ũc|ũn|ữ|ữc|ữn|ỹ|ỹc|ỹn|á|ác|án|ắ|ắc|ắn|ấ|ấc|ấn|é|éc|én|ế|ếc|ến|í|íc|ín|ó|óc|ón|ố|ốc|ốn|ớ|ớc|ớn|ú|úc|ún|ứ|ức|ứn|ý|ýc|ýn|ạ|ạc|ạn|ặ|ặc|ặn|ậ|ậc|ận|ẹ|ẹc|ẹn|ệ|ệc|ện|ị|ịc|ịn|ọ|ọc|ọn|ộ|ộc|ộn|ợ|ợc|ợn|ụ|ục|ụn|ự|ực|ựn|ỵ|ỵc|ỵn|a|ac|an|ă|ăc|ăn|â|âc|ân|b|bc|bn|c|cc|cn|d|dc|dn|đ|đc|đn|e|ec|en|ê|êc|ên|g|gc|gn|h|hc|hn|i|ic|in|k|kc|kn|l|lc|ln|m|mc|mn|n|nc|nn|o|oc|on|ô|ôc|ôn|ơ|ơc|ơn|p|pc|pn|q|qc|qn|r|rc|rn|s|sc|sn|t|tc|tn|u|uc|un|ư|ưc|ưn|v|vc|vn|x|xc|xn|y|yc|yn}}
{{bottom}}
1n01ibxyn3s7hpd01p893zslhzp84cc
szemfog
0
421620
2573991
2026-10-28T10:22:54Z
Kelly zhrm
58416
Trang mới: “=={{langname|hu}}== ==={{ĐM|etym}}=== Từ {{af|hu|szem|fog|t1=mắt|t2=răng|pos=danh từ}}. So sánh với {{noncog|en|eye tooth}}, {{noncog|de|Augenzahn}}. ==={{ĐM|pron}}=== * {{hu-IPA}} * {{audio|hu|LL-Q9067 (hun)-Ewithu-szemfog.wav}} * {{hyphenation|hu|szem|fog}} * {{rhymes|hu|oɡ|s=2}} ==={{ĐM|n}}=== {{hu-noun|ak}} # [[răng nanh|Răng nanh]]. #: {{cot|hu|metszőfog|őrlőfog}} ===={{ĐM|decl}}==== {{hu-infl-nom|szemfoga|o}}{{hu-pos-atok|szemfog||ai}} ==={…”
2573991
wikitext
text/x-wiki
=={{langname|hu}}==
==={{ĐM|etym}}===
Từ {{af|hu|szem|fog|t1=mắt|t2=răng|pos=danh từ}}. So sánh với {{noncog|en|eye tooth}}, {{noncog|de|Augenzahn}}.
==={{ĐM|pron}}===
* {{hu-IPA}}
* {{audio|hu|LL-Q9067 (hun)-Ewithu-szemfog.wav}}
* {{hyphenation|hu|szem|fog}}
* {{rhymes|hu|oɡ|s=2}}
==={{ĐM|n}}===
{{hu-noun|ak}}
# [[răng nanh|Răng nanh]].
#: {{cot|hu|metszőfog|őrlőfog}}
===={{ĐM|decl}}====
{{hu-infl-nom|szemfoga|o}}{{hu-pos-atok|szemfog||ai}}
==={{ĐM|further}}===
* {{R:ErtSz}}
{{C|hu|Răng}}
gqpoml2bdhylre82qe7m1kj9gesu9tv
2573992
2573991
2026-10-28T10:23:13Z
Kelly zhrm
58416
2573992
wikitext
text/x-wiki
=={{langname|hu}}==
==={{ĐM|etym}}===
Từ {{af|hu|szem|fog|t1=mắt|t2=răng|pos=Danh từ}}. So sánh với {{noncog|en|eye tooth}}, {{noncog|de|Augenzahn}}.
==={{ĐM|pron}}===
* {{hu-IPA}}
* {{audio|hu|LL-Q9067 (hun)-Ewithu-szemfog.wav}}
* {{hyphenation|hu|szem|fog}}
* {{rhymes|hu|oɡ|s=2}}
==={{ĐM|n}}===
{{hu-noun|ak}}
# [[răng nanh|Răng nanh]].
#: {{cot|hu|metszőfog|őrlőfog}}
===={{ĐM|decl}}====
{{hu-infl-nom|szemfoga|o}}{{hu-pos-atok|szemfog||ai}}
==={{ĐM|further}}===
* {{R:ErtSz}}
{{C|hu|Răng}}
jl0liyfk3ri5e9ymdun5b6gnimzhquv
Bản mẫu:bg-adecl
10
421621
2573993
2026-10-28T11:06:56Z
Hiyuune
50834
/* */
2573993
wikitext
text/x-wiki
{{#invoke:bg-nominal|show_adj}}<!--
--><noinclude>{{documentation}}</noinclude>
1z0yuskbuvz2lnqskvmfduhuy1r15ba
авиосимулаторен
0
421622
2573994
2026-10-28T11:07:13Z
Hiyuune
50834
/* */
2573994
wikitext
text/x-wiki
=={{langname|bg}}==
==={{section|etym}}===
{{af|bg|авиосимула́тор|-ен}}.
==={{section|pron}}===
{{bg-pr|авиосимула́торен|a=[KK]}}
==={{section|adj}}===
{{bg-adj|авиосимула́торен}}
# {{lb|bg|relational}} [[mô phỏng|Mô phỏng]] [[máy bay]].
===={{section|decl}}====
{{bg-adecl|авиосимула́торен<!>}}
===={{section|rel}}====
* {{l|bg|авиосимула́тор}}
==={{section|ref}}===
* {{R:bg:BERON}}
* {{R:bg:Infolex}}
* {{R:bg:LexIt}}
* {{R:bg:RBE}}
* {{R:bg:RBE2}}
1lvlq6wtdja6mgb8bcpbtw52o1n8iwt