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&nbsp; |- | 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('"', "&quot;") -- & in attributes is automatically escaped. -- :gsub("&", "&amp;") :gsub("<", "&lt;") :gsub(">", "&gt;") 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('"', "&quot;") -- & in attributes is automatically escaped. -- :gsub("&", "&amp;") :gsub("<", "&lt;") :gsub(">", "&gt;") 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('"', "&quot;") -- & in attributes is automatically escaped. -- :gsub("&", "&amp;") :gsub("<", "&lt;") :gsub(">", "&gt;") 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