Wikiwordbōc angwiktionary https://ang.wiktionary.org/wiki/H%C4%93afodtramet MediaWiki 1.47.0-wmf.22 case-sensitive Media Syndrig Mōtung Brūcend Brūcendmōtung Wikiwordbōc Wikiwordbōcmōtung Ymele Ymelmōtung MediaWiki MediaWikimōtung Bysen Bysenmōtung Help Helpmōtung Flocc Floccmōtung Ætēaca Ætēacmōtung TimedText TimedText talk Module Module talk Event Event talk death 0 2061 54960 53046 2026-09-30T17:20:03Z Deadend0914 7211 /* {{sprǣc|en}} */ 54960 wikitext text/x-wiki =={{sprǣc|en}}== ===Rihtstefn=== * /dɛθ/ ===Wordstǣr=== *Of Engliscum [[dēaþ]] ===Nama=== {{en-noun|~}} # [[dēaþ]], [[līflēast]] # [[cwealm]] #: ''The king of '''death''' is now here.'' [[Flocc:Nīwenglisce naman]] mx04igg3ebkmbupikyzhaw9iuq3nla8 55007 54960 2026-09-30T18:33:09Z Deadend0914 7211 /* Nama */ 55007 wikitext text/x-wiki =={{sprǣc|en}}== ===Rihtstefn=== * /dɛθ/ ===Wordstǣr=== *Of Engliscum [[dēaþ]] ===Nama=== {{en-noun|~}} # [[dēaþ]], [[līflēast]] # [[cwealm]] #* ''The king of '''death''' is now here.'' #*: ''Sē cyning of '''dēaþ''' is nū hēr.'' [[Flocc:Nīwenglisce naman]] ahdtht0ijbyjpi3f1jcdwip7lcggcud 55017 55007 2026-09-30T18:59:59Z Deadend0914 7211 55017 wikitext text/x-wiki =={{sprǣc|en}}== ===Rihtstefn=== * /dɛθ/ ===Wordstǣr=== *Of Engliscum [[dēaþ]] ===Nama=== {{en-noun|~}} # [[dēaþ]], [[līflēast]] # [[cwealm]] #* ''The king of '''death''' is now here.'' #*: ''Sē cyning of '''dēaþ''' is nū hēr.'' nyadzzuj1rx86hgnwmp4e7kihwp5110 muoter 0 2655 55033 51432 2026-09-30T19:32:04Z Deadend0914 7211 /* Ealdhēahþēodisc */ 55033 wikitext text/x-wiki ==Ealdhēahþēodisc== ===Rihtstefn=== * IPA: // ===Wordstǣr=== {{inh+|goh|gmw-pro|*mōder}} === {{Wordcynn|Nama}} {{f}} === {{goh-noun|f}} {{EHTH-Naman-Tabule| Hwā oþþe hwæt? (Ānfeald)=diu muoter |Hwā oþþe hwæt? (Manigfeald)=deo muoter |Hwæs? (Ānfeald)=dëra muoter |Hwæs? (Manigfeald)=dëro muotero |Hwǣm? (Ānfeald)=dëru muoter |Hwǣm? (Manigfeald)=dēm muoterum |Hwȳ? (Ānfeald)=- |Hwȳ? (Manigfeald)=- |Hwone? (Ānfeald)=dea muoter |Hwone? (Manigfeald)=deo muoter }} #[[mōdor]] [[Flocc:Eald Hēah Þēodisc nama]] [[Flocc:Ealde Hēah Þēodisce lēode]] mp2h78ixl0iewu2spohr0j6mnn09r1w 55034 55033 2026-09-30T19:32:24Z Deadend0914 7211 55034 wikitext text/x-wiki ==Ealdhēahþēodisc== ===Rihtstefn=== * IPA: // ===Wordstǣr=== {{inh+|goh|gmw-pro|*mōder}} === {{Wordcynn|Nama}} {{f}} === {{goh-noun|f}} {{EHTH-Naman-Tabule| Hwā oþþe hwæt? (Ānfeald)=diu muoter |Hwā oþþe hwæt? (Manigfeald)=deo muoter |Hwæs? (Ānfeald)=dëra muoter |Hwæs? (Manigfeald)=dëro muotero |Hwǣm? (Ānfeald)=dëru muoter |Hwǣm? (Manigfeald)=dēm muoterum |Hwȳ? (Ānfeald)=- |Hwȳ? (Manigfeald)=- |Hwone? (Ānfeald)=dea muoter |Hwone? (Manigfeald)=deo muoter }} #[[mōdor]] dwfx29yijnyqtmm4o7fffcnghpo45yx twēgen 0 3137 55101 54628 2026-10-01T00:10:29Z Deadend0914 7211 /* Wordstǣr */ 55101 wikitext text/x-wiki ==Englisc== ===Wordstǣr=== {{etymon|ang|:der|gmw-pro:*twai-}} {{dercat|ang|gem-pro|ine-pro|inh=2}} Of {{inh|ang|gmw-pro|*twai-}} === {{Wordcynn|Getæl}} === {{Getæl-Naman2-Tabule| Hwá oþþe hwæt? (Werlic)=twēgen |Hwá oþþe hwæt? (Wīflic)=[[twā]] |Hwá oþþe hwæt? (Nāhwæðer)=[[tū]] |Hwæs? (Werlic)=twēgra |Hwæs? (Wīflic)=twēgra |Hwæs? (Nāhwæðer)=twēgra |Hwǽm? (Werlic)=twām, twǣm |Hwǽm? (Wīflic)=twām, twǣm |Hwǽm? (Nāhwæðer)=twām, twǣm |Hwý? (Werlic)=twām, twǣm |Hwý? (Wīflic)=twām, twǣm |Hwý? (Nāhwæðer)=twām, twǣm |Hwone? (Werlic)=twēgen |Hwone? (Wīflic)=[[twā]] |Hwone? (Nāhwæðer)=[[tū]] }} '''twēgen''' [[Category:Hēafodgetalu]] *[[w:Arabic numerals|Arabisc getalu]]: 2 *[[w:Chinese numerals|Cīnisc getalu]]: 貳, 二 *[[w:Roman numerals|Rōmānisc getalu]]: II, ii **Beforan: [[ān]], 1 **Æfter: [[þrīe]], 3 # Þæt rím æfter þrīe and beforan ān ====Wendunga==== '''ān and ān''' *Denisc: {{Wendung|da|to}} *[[Fresisc]]: {{Wendung|fy|twa}} *Giddisc: (tsvay) *Īslendisc: {{Wendung|is|tveir}} ''wr'', {{Wendung|is|tvær}} ''wf'', {{Wendung|is|tvö}} ''nā'' *Lǣden: {{Wendung|la|duo}} *[[Nīwenglisc]]: {{Wendung|en|two}} *Niðerlendisc: {{Wendung|nl|twee}} *Norþwegisc: {{Wendung|no|to}} *Swēonisc: {{Wendung|sw|två}} *Þēodisc: {{Wendung|de|zwei}}, {{Wendung|de|zwo}} <!-- === {{Wordcynn|Nama}} === {{Getæl-Naman2-Tabule| Hwá oþþe hwæt? (Werlic)=[[twēgen]] |Hwá oþþe hwæt? (Wīflic)=[[twā]] |Hwá oþþe hwæt? (Nāhwæðer)=[[tū]] |Hwæs? (Werlic)=twēgra |Hwæs? (Wīflic)=twēgra |Hwæs? (Nāhwæðer)=twēgra |Hwǽm? (Werlic)=twām, twǣm |Hwǽm? (Wīflic)=twām, twǣm |Hwǽm? (Nāhwæðer)=twām, twǣm |Hwý? (Werlic)=twām, twǣm |Hwý? (Wīflic)=twām, twǣm |Hwý? (Nāhwæðer)=twām, twǣm |Hwone? (Werlic)=twēgen |Hwone? (Wīflic)=[[twā]] |Hwone? (Nāhwæðer)=[[tū]] }} '''two''' (''plural:'' '''[[twos]]''') # The digit/figure 2. #:''The number 2202 contains three twos.'' ====Wendunga==== --> ===Tōgeīecendlic=== '''twēgen''' ''werlic'' # twēgen '''twi-''' (in gesamnuge swā tōgeīecendlic) # twinihte, in ielde twēgra daga # twiwintre, in ielde twēgra gēara ===Sēo ēac=== *[[2]] [[Category:100 Engliscra grundworda]] [[Category:Cartangamen]] 03ym54oshohc4qysezo11gbidpr16rl 55102 55101 2026-10-01T00:13:43Z Deadend0914 7211 55102 wikitext text/x-wiki ==Englisc== ===Wordstǣr=== Of {{inh|ang|gmw-pro|*twai-}} === {{Wordcynn|Getæl}} === {{Getæl-Naman2-Tabule| Hwá oþþe hwæt? (Werlic)=twēgen |Hwá oþþe hwæt? (Wīflic)=[[twā]] |Hwá oþþe hwæt? (Nāhwæðer)=[[tū]] |Hwæs? (Werlic)=twēgra |Hwæs? (Wīflic)=twēgra |Hwæs? (Nāhwæðer)=twēgra |Hwǽm? (Werlic)=twām, twǣm |Hwǽm? (Wīflic)=twām, twǣm |Hwǽm? (Nāhwæðer)=twām, twǣm |Hwý? (Werlic)=twām, twǣm |Hwý? (Wīflic)=twām, twǣm |Hwý? (Nāhwæðer)=twām, twǣm |Hwone? (Werlic)=twēgen |Hwone? (Wīflic)=[[twā]] |Hwone? (Nāhwæðer)=[[tū]] }} '''twēgen''' [[Category:Hēafodgetalu]] *[[w:Arabic numerals|Arabisc getalu]]: 2 *[[w:Chinese numerals|Cīnisc getalu]]: 貳, 二 *[[w:Roman numerals|Rōmānisc getalu]]: II, ii **Beforan: [[ān]], 1 **Æfter: [[þrīe]], 3 # Þæt rím æfter þrīe and beforan ān ====Wendunga==== '''ān and ān''' *Denisc: {{Wendung|da|to}} *[[Fresisc]]: {{Wendung|fy|twa}} *Giddisc: (tsvay) *Īslendisc: {{Wendung|is|tveir}} ''wr'', {{Wendung|is|tvær}} ''wf'', {{Wendung|is|tvö}} ''nā'' *Lǣden: {{Wendung|la|duo}} *[[Nīwenglisc]]: {{Wendung|en|two}} *Niðerlendisc: {{Wendung|nl|twee}} *Norþwegisc: {{Wendung|no|to}} *Swēonisc: {{Wendung|sw|två}} *Þēodisc: {{Wendung|de|zwei}}, {{Wendung|de|zwo}} <!-- === {{Wordcynn|Nama}} === {{Getæl-Naman2-Tabule| Hwá oþþe hwæt? (Werlic)=[[twēgen]] |Hwá oþþe hwæt? (Wīflic)=[[twā]] |Hwá oþþe hwæt? (Nāhwæðer)=[[tū]] |Hwæs? (Werlic)=twēgra |Hwæs? (Wīflic)=twēgra |Hwæs? (Nāhwæðer)=twēgra |Hwǽm? (Werlic)=twām, twǣm |Hwǽm? (Wīflic)=twām, twǣm |Hwǽm? (Nāhwæðer)=twām, twǣm |Hwý? (Werlic)=twām, twǣm |Hwý? (Wīflic)=twām, twǣm |Hwý? (Nāhwæðer)=twām, twǣm |Hwone? (Werlic)=twēgen |Hwone? (Wīflic)=[[twā]] |Hwone? (Nāhwæðer)=[[tū]] }} '''two''' (''plural:'' '''[[twos]]''') # The digit/figure 2. #:''The number 2202 contains three twos.'' ====Wendunga==== --> ===Tōgeīecendlic=== '''twēgen''' ''werlic'' # twēgen '''twi-''' (in gesamnuge swā tōgeīecendlic) # twinihte, in ielde twēgra daga # twiwintre, in ielde twēgra gēara ===Sēo ēac=== *[[2]] [[Category:100 Engliscra grundworda]] [[Category:Cartangamen]] 4hv359uq8e9d3e3pz618jvj6okcgso7 Affrica 0 5199 55088 54528 2026-09-30T23:44:02Z Deadend0914 7211 /* {{f}} {{Wordcynn|Nama}} */ 55088 wikitext text/x-wiki =={{sprǣc|ang}}== === Rihtstefn === *[[w:SAMPA|SAMPA]]: /'/ *[[w:IPA|IPA]]: /'ɑf·fri·kɑ/ === {{f}} {{Wordcynn|Nama}} === #se worulddǣl súþ of Európe and betwēonum þām [[Indiscum gārsecge]] and þām [[Altantiscum gārsecge]] ====Wendunga==== *[[Nīwenglisc]]: {{Wendung|en|Africa}} *[[Þēodisc]]: {{Wendung|de|Afrika}} *[[Hebrēisc]]: {{Wendung|he|אפריקה}} *[[Crēacisc]]: [[Αφρική]] 6w3b9m7safds9uvgzmwynxt4ozm9uee 55089 55088 2026-09-30T23:44:37Z Deadend0914 7211 55089 wikitext text/x-wiki =={{sprǣc|ang}}== === Rihtstefn === *[[w:SAMPA|SAMPA]]: /'/ *[[w:IPA|IPA]]: /'ɑf·fri·kɑ/ ===Wiflic Nama=== #se worulddǣl súþ of Európe and betwēonum þām [[Indiscum gārsecge]] and þām [[Altantiscum gārsecge]] ====Wendunga==== *[[Nīwenglisc]]: {{Wendung|en|Africa}} *[[Þēodisc]]: {{Wendung|de|Afrika}} *[[Hebrēisc]]: {{Wendung|he|אפריקה}} *[[Crēacisc]]: [[Αφρική]] cy20nt90lgqwg85pjmedei85j63782u 55090 55089 2026-09-30T23:44:52Z Deadend0914 7211 /* Wiflic Nama */ 55090 wikitext text/x-wiki =={{sprǣc|ang}}== === Rihtstefn === *[[w:SAMPA|SAMPA]]: /'/ *[[w:IPA|IPA]]: /'ɑf·fri·kɑ/ ===Wiflic Nama=== #se [[worulddǣl]] súþ of Európe and betwēonum þām [[Indiscum gārsecge]] and þām [[Altantiscum gārsecge]] ====Wendunga==== *[[Nīwenglisc]]: {{Wendung|en|Africa}} *[[Þēodisc]]: {{Wendung|de|Afrika}} *[[Hebrēisc]]: {{Wendung|he|אפריקה}} *[[Crēacisc]]: [[Αφρική]] f4udrhbb32n4k5y639q2330nlu3vda9 55091 55090 2026-09-30T23:45:35Z Deadend0914 7211 /* {{sprǣc|ang}} */ 55091 wikitext text/x-wiki =={{sprǣc|ang}}== === Rihtstefn === *[[w:SAMPA|SAMPA]]: /'/ *[[w:IPA|IPA]]: /'ɑf·fri·kɑ/ ===Agennama=== {{ang-noun|f|head=Affrica}} #se [[worulddǣl]] súþ of Európe and betwēonum þām [[Indiscum gārsecge]] and þām [[Altantiscum gārsecge]] ====Wendunga==== *[[Nīwenglisc]]: {{Wendung|en|Africa}} *[[Þēodisc]]: {{Wendung|de|Afrika}} *[[Hebrēisc]]: {{Wendung|he|אפריקה}} *[[Crēacisc]]: [[Αφρική]] 87ehgngqwmfhn09bj7xcb6dnkl1baf3 bed 0 5800 55013 52440 2026-09-30T18:42:05Z Deadend0914 7211 /* Nama */ 55013 wikitext text/x-wiki =={{sprǣc|en}}== ===Rihtstefn=== IPA:/bɛd/ ===Nama=== {{en-noun|~}} #[[bedd]] ====Fruma==== *Ofgangen of Engliscum [[bedd]] ond1wwxfobkqkmsy6okvz8q6yyb7zcm dēaþ 0 6423 55067 54946 2026-09-30T22:32:38Z Deadend0914 7211 55067 wikitext text/x-wiki =={{sprǣc|ang}}== ===Āwendednessa=== * deað * dǣþ ===Rihtstefn=== * /dæːɑθ/ ===Wordstǣr=== Of {{inh|ang|gmw-pro|*dauþu}}, of {{inh|ang|gem-pro|*dauþuz}}, of {{inh|ang|ine-pro|*dʰautus}}. ====Sweostorword==== *Denisc: {{Wendung|da|død}} *[[Fresisc]]: {{Wendung|fy|dea}} ''wr'' *Īslendisc: {{Wendung|is|dauði}} ''wr'' *Niðerlendisc: {{Wendung|nl|dood}} ''wr'' *Norþwegisc: {{Wendung|no|død}} ''wr'' *[[Seaxisc]]: {{Wendung|nds|Dood}} ''wr'' *Swēonisc: {{Wendung|sv|död}} ''gm'' *Þēodisc: {{Wendung|de|Tod}} ''wr'' ===Werlīc Nama=== {{ang-noun|m|head=dēaþ}} # Þæs līfes ende. #: '''''Dēaþ''' nis nān belimp on līfe. '''Dēaðes''' man ne gebītt. Gif man ēcnesse understent nā tō endelēasre tīdlengðe, ac tō tīdlēasnesse, þonne leofaþ sē on ēcnesse þe leofaþ on andweardnesse. Ūre līf is endelēas ealswā ūre gesihtfeld gemǣrelēas biþ.'' ====Nīwenglisc Andgiet==== # [[death]] ====Declīnung==== {{ang-decl-noun-a-m|dēaþ}} ====Gesetnyssum==== [[ær]]-dēaþ - early death, death before one's time, premature death <br> dēaþ-[[bēam]] "wr" - death tree <br> dēaþ-[[bedd]] "nw" - death bed, grave, bed of death, <br> dēaþ-[[cwalu]] "wf" - plague death, violent death, <br> dēaþ-[[dæg]] "wr" - death day, day of death, <br> dēaþ-denu "wf" - valley of death, <br> fær-dēaþ, dēaþ-ræs "wr" - sudden death, <br> dēaþ-scūa "wr", dēaþ-scufa "wr" - death shadow, shadow of death, <br> dēaþ-[[scyld]] "wf" - crime worthy of death <br> dēaþ-stede "wr" - place of death <br> dēaþ-[[wang]] "wr" - death plain, <br> dēaþ-wīc "nw" - death's place, dwelling of the dead, <br> dēaþ-[[wyrd]] "wf" - death fate, fate of death <br> [[ende]]-dēað -death as the end of life <br> [[gúð]]-dēað - battle death, death in battle <br> [[mere]]-dēaþ - sea death, death at sea, <br> [[swylt]]-dēaþ - death (die-death) <br> [[wæl]]-dēaþ - death in battle, <br> [[wundor]]-dēaþ - wondrous death <br> ===Tudru=== * {{desctree|enm|deth}} ====Wendunga==== *Frencisc: {{Wendung|fr|mort}} ''wf'' *Grēcisc: {{Wendung|el|θάνατος}} [ˈθa.na.to̞s] ''wr'', {{Wendung|el|θανατάς}} [θa.na.ˈtas] ''wr'', {{Wendung|el|πεθαμός}} [pe̞.θa.ˈmo̞s] ''wr'', {{Wendung|el|αποθαμός}} [a.po̞.θa.ˈmo̞s] {{m}}, {{Wendung|el|χάρος}} [ˈxa.ro̞s] ''wr'' *Hebrēisc: {{Wendung|he|מוות}} (mavet) ''wf'' *Italisc: {{Wendung|it|morte}} ''wf'' *Lǣden: {{Wendung|la|mors}} ''wf'', {{Wendung|la|exitium}} ''nā'', {{Wendung|la|quietus}} ''wr'' *Persisc: {{Wendung|fa|مرگ}} *Scyttisc Gǣlisc: {{Wendung|gd|bàs}} ''wr'' jxw3rdcuelrio4145wz51emizztlc9m bedd 0 7606 55008 53211 2026-09-30T18:34:52Z Deadend0914 7211 /* {{n}} {{Wordcynn|Nama}} */ 55008 wikitext text/x-wiki ==Englisc== [[File:Illustrated catalogue of the exceedingly rare and valuable art treasures and antiquities formerly contained in the famous Davanzati Palace, Florence, Italy (1916) (14780435285).jpg|thumb|Ān bedd]] ===Rihtstefn=== *[[IPA]]: // ==={{n}} {{Wordcynn|Nama}}=== {{ang-noun|n|head=dēaþ}} # Ān þing þe is for swefende and is ġewunelīċe hnesce. [[Category:Englisc nama]] 3y8401kwnfhc8dh0ebu2udxkv7y14yt 55009 55008 2026-09-30T18:35:55Z Deadend0914 7211 55009 wikitext text/x-wiki ==Englisc== [[File:Illustrated catalogue of the exceedingly rare and valuable art treasures and antiquities formerly contained in the famous Davanzati Palace, Florence, Italy (1916) (14780435285).jpg|thumb|Ān bedd]] ===Rihtstefn=== *[[IPA]]: // ==={{n}} {{Wordcynn|Nama}}=== {{ang-noun|n|head=dēaþ}} # Ān þing þe is for swefende and is ġewunelīċe hnesce. ===Declīnung=== {{ang-decl-noun-a-n|bedd}} ===Wendunga=== * Nīwenglisc: [[bed]] onal7ghhmoz626pn7m0udltvzdc8l5t 55086 55009 2026-09-30T23:04:34Z Deadend0914 7211 /* {{n}} {{Wordcynn|Nama}} */ 55086 wikitext text/x-wiki ==Englisc== [[File:Illustrated catalogue of the exceedingly rare and valuable art treasures and antiquities formerly contained in the famous Davanzati Palace, Florence, Italy (1916) (14780435285).jpg|thumb|Ān bedd]] ===Rihtstefn=== *[[IPA]]: // ==={{n}} {{Wordcynn|Nama}}=== {{ang-noun|n|head=bedd}} # Ān þing þe is for swefende and is ġewunelīċe hnesce. ===Declīnung=== {{ang-decl-noun-a-n|bedd}} ===Wendunga=== * Nīwenglisc: [[bed]] ga43d3kbe0chczb833zxrr7v5dyqi4l ærn 0 7628 55103 53263 2026-10-01T00:18:19Z Deadend0914 7211 /* {{n}} {{Wordcynn|Nama}} */ 55103 wikitext text/x-wiki ==Englisc== [[File:Spannarboda banvaktstuga_2.jpg|thumb|ān ærn]] ===Rihtstefn=== *[[IPA]]: // ==={{n}} {{Wordcynn|Nama}}=== # ān [[timbrung]] for [[menn]] tō libbenne in [[Category:Englisc nama]] [[Category:Englisc nāhwæðer nama]] 800686u5hogc60aibl6w785oseg4zk3 Module:en-headword 828 8056 55004 54932 2026-09-30T18:15:18Z Deadend0914 7211 55004 Scribunto text/plain local export = {} local pos_functions = {} --[==[ Author from 2020 on: mostly Benwing2, with significant contributions from Theknightwho. Based on a prior version by Rua (by now mostly rewritten), with contributions from Erutuon and others (see history for full attribution). ]==] local force_cat = false -- for testing; if true, categories appear in non-mainspace pages local require = require local require_when_needed = require("Module:require when needed") local en_utilities_module = "Module:en-utilities" local headword_utilities_module = "Module:headword utilities" local headword_module = "Module:headword" local inflection_utilities_module = "Module:inflection utilities" local parse_utilities_module = "Module:parse utilities" local JSON_module = "Module:JSON" local labels_module = "Module:labels" local links_module = "Module:links" local parameters_module = "Module:parameters" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local utilities_module = "Module:utilities" local yesno_module = "Module:yesno" local iut = require_when_needed(inflection_utilities_module) local put = require_when_needed(parse_utilities_module) local m_headword_utilities = require_when_needed(headword_utilities_module) local add_links_to_multiword_term = require_when_needed(headword_utilities_module, "add_links_to_multiword_term") local add_suffix = require_when_needed(en_utilities_module, "add_suffix") local apply_link_modifiers = require_when_needed(headword_utilities_module, "apply_link_modifiers") local concat = table.concat local deepEquals = require_when_needed(table_module, "deepEquals") local dump = mw.dumpObject local format_categories = require_when_needed(utilities_module, "format_categories") local full_headword = require_when_needed(headword_module, "full_headword") local get_label_info = require_when_needed(labels_module, "get_label_info") local get_link_page = require_when_needed(links_module, "get_link_page") local glossary_link = require_when_needed(headword_utilities_module, "glossary_link") local insert = table.insert local insertIfNot = require_when_needed(table_module, "insertIfNot") local ipairs = ipairs local is_regular_plural = require_when_needed(en_utilities_module, "is_regular_plural") local list_to_set = require_when_needed(table_module, "listToSet") local pairs = pairs local process_params = require_when_needed(parameters_module, "process") local remove = table.remove local remove_links = require_when_needed(links_module, "remove_links") local replacement_escape = require_when_needed(string_utilities_module, "replacement_escape") local shallowCopy = require_when_needed(table_module, "shallowCopy") local singularize = require_when_needed(en_utilities_module, "singularize") local split = require_when_needed(string_utilities_module, "split") local toJSON = require_when_needed(JSON_module, "toJSON") local toNFD = mw.ustring.toNFD local type = type local ulen = require_when_needed(string_utilities_module, "len") local ulower = require_when_needed(string_utilities_module, "lower") local umatch = require_when_needed(string_utilities_module, "match") local u = require_when_needed(string_utilities_module, "char") local ugsub = require_when_needed(string_utilities_module, "gsub") local lang = require("Module:languages").getByCode("en") local langname = lang:getCanonicalName() local list_param = {list = true, disallow_holes = true} local list_allow_holes = {list = true, allow_holes = true} local boolean_param = {type = "boolean"} local function ine(val) if val == "" then return nil else return val end end local function track(page) require("Module:debug/track")("en-headword/" .. page) return true end ------------------------------------------- UTILITY FUNCTIONS ------------------------------------------ -- Parse and return an inflection not requiring additional processing. The raw arguments come from `args[field]`, which -- is parsed for inline modifiers. local function parse_inflection(args, field, is_head) local argfield = field if type(argfield) == "table" then argfield = argfield[1] end return m_headword_utilities.parse_term_list_with_modifiers { paramname = field, forms = args[argfield], splitchar = ",", is_head = is_head, } end -- Insert the parsed inflections in `terms` (as parsed by `parse_inflection`) into `data.inflections`, with label -- `label` and optional accelerator spec `accel`. local function insert_inflection(data, terms, label, accel, no_label) for _, termobj in ipairs(terms) do m_headword_utilities.remove_termobj_field_modifiers(termobj) end m_headword_utilities.insert_inflection { headdata = data, terms = terms, label = label, no_label = no_label, accel = accel and {form = accel} or nil, } end -- Insert a fixed label `label` into the inflections for `data`. If `originating_term` is supplied, copy the decorations -- from it into the fixed label. local function insert_fixed_inflection(data, label, originating_term) m_headword_utilities.insert_fixed_inflection { headdata = data, originating_term = originating_term, label = label, } 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 -- These functions are used directly in the <> format as well as in the utility functions #2 below. local function compute_double_last_cons_stem(term) local last_cons = term:match("([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])$") if not last_cons then error("Verb stem '" .. term .. "' must end in a consonant to use ++") end return term .. last_cons end local function compute_plusplus_s_form(term, default_s_form) if term:find("[szx]$") then -- regas -> regasses, derez -> derezzes return compute_double_last_cons_stem(term) .. "es" else return default_s_form end end -- The main entry point. -- This is the only function that can be invoked from a template. function export.show(frame) local iparams = { [1] = true, } local iargs = require("Module:parameters").process(frame.args, iparams) local parargs = frame:getParent().args local poscat = iargs[1] local pos_in_1 = not poscat if pos_in_1 then poscat = ine(parargs[1]) or mw.title.getCurrentTitle().fullText == "Template:en-head" and "interjection" or error("Part of speech must be specified in 1=") poscat = require(headword_module).canonicalize_pos(poscat) end local indexing_poscat = pos_in_1 and "head" or poscat local params = { ["head"] = list_param, ["id"] = true, ["json"] = boolean_param, ["sort"] = true, ["splithyph"] = boolean_param, ["nosplithyph"] = boolean_param, ["hyphspace"] = boolean_param, ["nolink"] = boolean_param, ["nolinkhead"] = {type = "boolean_param", alias_of = "nolink"}, ["suffix"] = boolean_param, ["nosuffix"] = boolean_param, ["nomultiwordcat"] = boolean_param, ["abbr"] = list_param, ["the"] = true, ["def"] = {alias_of = "the"}, ["pagename"] = true, -- for testing } if pos_in_1 then params[1] = {required = true} -- required but ignored as already processed above end local pos_data = pos_functions[indexing_poscat] local pos_func if pos_data then local pos_params = pos_data.params if pos_params then for key, val in pairs(pos_params) do params[key] = val end end pos_func = pos_data.func end local args = process_params(parargs, params) -- Account for unsupported titles, e.g. 'C|N>K' instead of 'Unsupported titles/C through N to K'. local pagename = args.pagename or mw.loadData("Module:headword/data").pagename local user_specified_heads = parse_inflection(args, "head", "is_head") local heads = user_specified_heads local autohead if args.nolink or not pagename:find("[ '%-]") then autohead = pagename else local en_no_split_apostrophe_words = list_to_set { "one's", "someone's", "he's", "she's", "it's", } local en_include_hyphen_prefixes = list_to_set { -- We don't include things that are also words even though they are often (perhaps mostly) prefixes, e.g. -- "be", "counter", "cross", "extra", "half", "mid", "over", "pan", "under". "acro", "acousto", "Afro", "agro", "anarcho", "angio", "Anglo", "ante", "anti", "arch", "auto", "bi", "bio", "cis", "co", "cryo", "crypto", "de", "demi", "eco", "electro", "Euro", "ex", "Greco", "hemi", "hydro", "hyper", "hypo", "infra", "Indo", "inter", "intra", "Judeo", "macro", "meta", "micro", "mini", "multi", "neo", "neuro", "non", "para", "peri", "post", "pre", "pro", "proto", "pseudo", "re", "semi", "sub", "super", "trans", "un", "vice", } local function is_english(term) local title = mw.title.new(term) if title and title.exists then local content = title:getContent() if content and content:find("==English==\n") then return true end end return false end local function en_split_hyphen_when_space(word) if not word:find("-", nil, true) then return nil end if args.hyphspace then return "[[" .. word:gsub("%-+", " ") .. "|" .. word .. "]]" end if args.nosplithyph then return "[[" .. word .. "]]" end if not args.splithyph then local space_word = word:gsub("%-+", " ") if is_english(space_word) then return "[[" .. space_word .. "|" .. word .. "]]" end if is_english(word) then return "[[" .. word .. "]]" end end return nil end local function en_split_apostrophe(word) local base = word:match("^(.*)'s$") if base then return "[[" .. base .. "]][[-'s|'s]]" end -- Only treat final apostrophe as possessive if preceded by something that looks like a plural ending in /z/. -- In particular we don't want to do it for words like [[truckin']]. base = word:match("^(.*[sxz])'$") if base then if base:find("s$") then local sg = singularize(base) if is_english(sg) then return "[[" .. sg .. "|" .. base .. "]][[-'|']]" end end return "[[" .. base .. "]][[-'|']]" end return "[[" .. word .. "]]" end autohead = add_links_to_multiword_term(pagename, { split_hyphen_when_space = en_split_hyphen_when_space, split_apostrophe = en_split_apostrophe, no_split_apostrophe_words = en_no_split_apostrophe_words, include_hyphen_prefixes = en_include_hyphen_prefixes, }) end if not heads[1] then heads = {{term = autohead}} else for _, headobj in ipairs(heads) do local head = headobj.term if head:find("^~") then head = apply_link_modifiers(autohead, head:sub(2), lang) headobj.term = head elseif head:find("^[!?]$") then -- If explicit head= just consists of ! or ?, add it to the end of the default head. headobj.term = autohead .. head end if head == autohead then track("redundant-head") end end end -- handle the=/def= if args.the == "~" then local newheads = {} for _, headobj in ipairs(heads) do local barehead = shallowCopy(headobj) insert(newheads, barehead) headobj.term = "the " .. headobj.term insert(newheads, headobj) end heads = newheads elseif args.the then local the = require(yesno_module)(args.the) if the then for _, headobj in ipairs(heads) do headobj.term = "the " .. headobj.term end end end local data = { lang = lang, pos_category = poscat, categories = {}, heads = heads, user_specified_heads = user_specified_heads, -- We use our own splitting algorithm so the redundant head cat will be inaccurate. no_redundant_head_cat = true, inflections = {}, nomultiwordcat = args.nomultiwordcat, sort_key = args.sort, pagename = pagename, id = args.id, force_cat_output = force_cat, } local function inscat(cat) insert(data.categories, langname .. " " .. cat) end local is_suffix = false if args.suffix or not args.nosuffix and pagename:find("^%-") and not pagename:find("^%-%-") and poscat ~= "suffix forms" then is_suffix = true data.pos_category = "suffixes" local singular_poscat = singularize(poscat) inscat(singular_poscat .. "-forming suffixes") insert(data.inflections, {label = singular_poscat .. "-forming suffix"}) end if pos_func then pos_func(args, data, is_suffix) end local extra_categories = {} if pagename:find("[Qq]") then -- Check for q not followed by u. We want to exclude things like [[13q deletion syndrome]] and [[BFOQ]] that -- don't have a lowercase letter on either side, as well as things like [[& seq.]] and [[acq.]] that are -- abbreviations for words containing a following u. -- -- Approximate range of combining diacritics; we want to remove them so the checks below for -- a lowercase letter next to the q aren't tripped up by diacritics on the letter. local u300 = u(0x0300) local u36F = u(0x036F) local pagename_no_diacritics = ugsub(toNFD(pagename), "[" .. u300 .. "-" .. u36F .. "]", "") if pagename_no_diacritics:find("[Qq][a-tv-z]") or pagename_no_diacritics:find("[a-z]q[^u.]") or pagename_no_diacritics:find("[a-z]q$") then inscat("words containing Q not followed by U") end end -- toNFD performs decomposition, so letters that decompose to an ASCII -- vowel and a diacritic, such as é, are counted as vowels and do not do not -- need to be included in the pattern. if not umatch(ulower(toNFD(pagename)), "[aeiouyæœøəªºαεηιουω]") then inscat("words spelled wiþūtan vowels") end if pagename:find("yre$") then inscat('words ending in "-yre"') end if not pagename:find(" ") and ulen(pagename) >= 25 then insert(extra_categories, "Long " .. langname .. " words") end if pagename:find("^[^aeiou ]*a[^aeiou ]*e[^aeiou ]*i[^aeiou ]*o[^aeiou ]*u[^aeiou ]*$") then inscat("words that use all vowels in alphabetical order") end parse_and_insert_inflection(data, args, "abbr", "abbreviation") if args.json then return toJSON(data) end return full_headword(data) .. (extra_categories[1] and format_categories(extra_categories, lang, args.sort) or "") end local function make_default_comparative(word) if word == "good" or word == "well" then return {"better"} elseif word == "bad" or word == "badly" then return {"worse"} elseif word == "far" then return {"further", "farther"} else return {add_suffix(word, "r")} end end local function make_default_superlative(word) if word == "good" or word == "well" then return {"best"} elseif word == "bad" or word == "badly" then return {"worst"} elseif word == "far" then return {"furthest", "farthest"} else return {add_suffix(word, "st.superlative")} end end -- This function does the common work between adjectives and adverbs. local function process_comparative_args(data, args, plpos) local pagename = data.pagename local comps = parse_inflection(args, 1) local sups = parse_inflection(args, "sup") local outcomps, outsups if args.componly then if comps[1] then error("Can't specify comparatives of comparative-only " .. plpos) end insert(data.inflections, {label = glossary_link("comparative") .. " form only"}) insert(data.categories, langname .. " comparative-only " .. plpos) -- Set to empty list so we don't get any comparatives output, but process superlatives if specified. outcomps = {} if not sups[1] then -- Set to empty list so we don't get any superlatives output unless explicitly given. outsups = {} end elseif args.suponly then if comps[1] or sups[1] then error("Can't specify comparatives or superlatives of or superlative-only " .. plpos) end insert(data.inflections, {label = glossary_link("superlative") .. " form only"}) insert(data.categories, langname .. " superlative-only " .. plpos) return end -- If the first parameter is ?, then don't show anything, just return. if comps[1] and comps[1].term == "?" then if comps[2] then error("Can't specify additional comparatives along wiþ '?'") end if sups[1] then error("Can't specify superlatives along wiþ '?' for the comparative") end return end if comps[1] and comps[1].term == "-" then local hyphencomp = remove(comps, 1) -- Remove the "-" but retain for decorations. -- Not (generally) comparable; may occasionally have a comparative if comps[1] then insert_fixed_inflection(data, "not generally <<comparable>>", hyphencomp) elseif not sups[1] then insert_fixed_inflection(data, "not <<comparable>>", hyphencomp) insert(data.categories, langname .. " uncomparable " .. plpos) return else -- No comparative, but a superlative. insert_inflection() will correctly generate 'no comparative' if we -- pass in "-" as the value. outcomps = {hyphencomp} end elseif not comps[1] then comps = {{term = "more"}} end if not outcomps then -- not if we set `outcomps` to "-" above or processed a comparative-only term outcomps = {} -- Go over each parameter given and create a comparative and superlative form. for _, compobj in ipairs(comps) do local comp = compobj.term if comp == "-" then error("Comparative of '-' only allowed as first comparative") end if comp == "+" then comp = "+more" elseif comp == "more" and pagename ~= "many" and pagename ~= "much" then comp = "+more" elseif comp == "further" and pagename ~= "far" then comp = "+further" elseif comp == "better" and pagename ~= "good" and pagename ~= "well" then comp = "+better" elseif comp:find("~") then comp = comp:gsub("~", replacement_escape(pagename)) end compobj.origterm = comp if comp == "+more" then comp = "more [[" .. pagename .. "]]" elseif comp == "+further" then comp = {"further [[" .. pagename .. "]]", "farther [[" .. pagename .. "]]"} elseif comp == "+better" then comp = "better [[" .. pagename .. "]]" elseif comp == "er" then -- Add -er. comp = add_suffix(pagename, "r") elseif comp == "ier" then if pagename:sub(-1) ~= "y" then error("Can't specify 'ier' comparative unless the term ends wiþ 'y': " .. pagename) end comp = pagename:gsub("e?y$", "ier") elseif comp:find("^%+") then local special = m_headword_utilities.get_special_indicator(comp, "noerror") if special then comp = m_headword_utilities.handle_multiword(pagename, special, make_default_comparative) end end if type(comp) == "table" and not comp[2] then comp = comp[1] end if type(comp) == "table" then for i = 1, #comp - 1 do local outobj = shallowCopy(compobj) outobj.term = comp[i] insert(outcomps, outobj) end compobj.term = comp[#comp] insert(outcomps, compobj) else compobj.term = comp insert(outcomps, compobj) end end end if sups[1] and sups[1].term == "-" then if sups[2] then error("Can't specify '-' as superlative followed by further values") end -- No superlative. insert_inflection() will correctly generate 'no superlative' if we pass in "-" as the value. outsups = sups else if not sups[1] then sups = {{term = "+"}} end end -- `outsups` will be set if we set `outsups` to "-" above or processed a comparative-only term without superlatives. if not outsups then outsups = {} local function process_sup(sup, special, supobj, compobj) if special then sup = m_headword_utilities.handle_multiword(pagename, special, make_default_superlative) elseif sup == "-" or sup == "+" then error(("Internal error: Superlative value of '%s' should have been handled earlier"):format(sup)) elseif sup == "+most" then sup = "most [[" .. pagename .. "]]" elseif sup == "+furthest" then sup = {"furthest [[" .. pagename .. "]]", "farthest [[" .. pagename .. "]]"} elseif sup == "+best" then sup = "best [[" .. pagename .. "]]" elseif sup == "est" then -- Add -est. sup = add_suffix(pagename, "st.superlative") elseif sup == "iest" then if pagename:sub(-1) ~= "y" then error("Can't specify 'iest' superlative unless the term ends wiþ 'y': " .. pagename) end sup = pagename:gsub("e?y$", "iest") end if type(sup) == "table" and not sup[2] then sup = sup[1] end if compobj then supobj = shallowCopy(supobj) supobj = m_headword_utilities.combine_termobj_decorations(supobj, compobj) end if type(sup) == "table" then for i = 1, #sup - 1 do local outobj = shallowCopy(supobj) outobj.term = sup[i] insert(outsups, outobj) end supobj.term = sup[#sup] insert(outsups, supobj) else supobj.term = sup insert(outsups, supobj) end end for _, supobj in ipairs(sups) do local sup = supobj.term if sup == "-" then error("Superlative of '-' only allowed as first superlative") end if sup == "+" then if not comps[1] then error("Superlative of '+' can't be specified when there are no comparatives") end for _, compobj in ipairs(comps) do local comp = compobj.origterm local special if comp == "+more" then sup = "+most" elseif comp == "+further" then sup = "+furthest" elseif comp == "+better" then sup = "+best" elseif comp == "er" then sup = "est" elseif comp == "ier" then sup = "iest" else if comp:find("^%+") then special = m_headword_utilities.get_special_indicator(comp, "noerror") end if not special then -- If the full comparative was given, then derive the superlative by replacing -er with -- -est. if comp:sub(-2) == "er" then sup = comp:sub(1, -3) .. "est" else error(("The superlative cannot be derived automatically from comparative '%s' because it doesn't end in -er"):format(comp)) end end end process_sup(sup, special, supobj, compobj) end else local special = m_headword_utilities.get_special_indicator(sup, "noerror") -- Do some work here rather than in process_sup() so we don't end up double-processing a term with a '~' -- in it or a term that happens to be 'most' or similar after substitution of ~ in the comparative. if not special then if sup == "most" and pagename ~= "many" and pagename ~= "much" then sup = "+most" elseif sup == "furthest" and pagename ~= "far" then sup = "+furthest" elseif sup == "best" and pagename ~= "good" and pagename ~= "well" then sup = "+best" elseif sup:find("~") then sup = sup:gsub("~", replacement_escape(pagename)) end end process_sup(sup, special, supobj) end end end insert_inflection(data, outcomps, "<<comparative>>", "comparative") insert_inflection(data, outsups, "<<superlative>>", "superlative") end pos_functions["adjectives"] = { params = { [1] = list_param, ["comp_qual"] = {list = "comp\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the comparative value", }, ["sup"] = list_param, ["sup_qual"] = {list = "sup\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the superlative value", }, ["componly"] = boolean_param, ["suponly"] = boolean_param, }, func = function(args, data) -- Process the comparatives and superlatives. process_comparative_args(data, args, "adjectives") end, } pos_functions["adverbs"] = { params = { [1] = list_param, ["comp_qual"] = {list = "comp\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the comparative value", }, ["sup"] = list_param, ["sup_qual"] = {list = "sup\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the superlative value", }, ["componly"] = boolean_param, ["suponly"] = boolean_param, }, func = function(args, data) -- Process the comparatives and superlatives. process_comparative_args(data, args, "adverbs") end, } local function escape(str) return (str:gsub("\\([:#])", "\\\\%1") :gsub("[:#]", "\\%0")) end local function canonicalize_plural(pl, pagename, pos) if pl == "+" then return escape(add_suffix(pagename, "s.plural", pos)) elseif pl == "++" then return escape(compute_plusplus_s_form(pagename, add_suffix(pagename, "s.plural", pos))) elseif pl == "*" then return escape(pagename) elseif pl == "ies" then if pagename:sub(-1) == "y" then return escape(pagename:gsub("e?y$", pl)) end error("Can't specify 'ies' plural unless the term ends wiþ 'y'.") elseif pl == "s" or pl == "es" or pl == "'s" then return escape(pagename .. pl) end end local function do_nouns(args, data, pos) local pagename = data.pagename pos = pos or "noun" local plurals = parse_inflection(args, 1) local function insert_plurale_tantum_inflections(is_plural_only, originating_label) if args.sg[1] then insert_fixed_inflection(data, "normally plural", originating_label) parse_and_insert_inflection(data, args, "sg", "singular") elseif is_plural_only then insert_fixed_inflection(data, "plural only", originating_label) end if args.attr[1] then parse_and_insert_inflection(data, args, "attr", "attributive") end end local function first_pl_term() return plurals[1] and plurals[1].term or nil end if first_pl_term() == "p" then -- plurale tantum if plurals[2] then error("Wiþ plurale tantum nama, can't specify more than one plural") end data.genders = {"p"} -- this should auto-insert the correct 'pluralia tantum' category insert_plurale_tantum_inflections("plural only", plurals[1]) return end local function inscat(cat) insert(data.categories, langname .. " " .. cat) end local need_default_plural = pos == "noun" if first_pl_term() == "sp" then -- construed as singular or plural sp = remove(plurals, 1) -- Remove the "sp" but retain it for its decorations. inscat("naman construed as singular or plural") data.genders = {"s", "p"} -- this should auto-insert the correct 'pluralia tantum' category insert_plurale_tantum_inflections(nil, sp) need_default_plural = false elseif first_pl_term() == "-" then -- Untellendlīc noun; may occasionally have a plural local hyphpl = remove(plurals, 1) -- Remove the "-" but retain for decorations. inscat("uncountable nouns") -- If plural forms were given explicitly, then show "oft" if plurals[1] then insert_fixed_inflection(data, "oft <<untellendlīc>>", hyphpl) else insert_fixed_inflection(data, "<<untellendlīc>>", hyphpl) end need_default_plural = false elseif first_pl_term() == "#" then -- oft tellendlīc (e.g., "grilled cheese") local hashpl = remove(plurals, 1) -- Remove the "#" but retain for decorations. insert_fixed_inflection(data, "oft <<tellendlīc>>", hashpl) inscat("uncountable nouns") inscat("countable nouns") -- If no plural was given, add a default one now if not plurals[1] then plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end elseif first_pl_term() == "~" then -- Mixed tellendlīc/untellendlīc noun, always has a plural local tildepl = remove(plurals, 1) -- Remove the "~" but retain for decorations. insert_fixed_inflection(data, "<<tellendlīc>> and <<untellendlīc>>", tildepl) inscat("uncountable nouns") inscat("countable nouns") -- If no plural was given, add a default one now if not plurals[1] then plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end end -- Plural is unknown if first_pl_term() == "?" then local questionpl = remove(plurals, 1) -- Remove the "?" but retain for decorations. -- Not desired; see [[Wiktionary:Tea_room/2021/August#"Plural unknown or uncertain"]] -- insert_fixed_inflection(data, "manigfeald unġecnāwen oþþe ungewiss", questionpl) inscat("naman wiþ unġecnāwen oþþe ungewiss manigfealde") if plurals[1] then error("Ne cann āmearcian sweotole manigfealde andlang wiþ '?' of unġecnāwen/ungewiss manigfeald") end return end -- Plural is not attested if first_pl_term() == "!" then local exclampl = remove(plurals, 1) -- Remove the "!" but retain for decorations. insert_fixed_inflection(data, "plural not attested", exclampl) inscat("naman wiþ unattested manigfealde") if plurals[1] then error("Ne cann āmearcian sweotole manigfealde andlang wiþ '!' of gecȳþed manigfeald") end return end -- If no plural was given, maybe add a default one, otherwise (when "-" was given or proper noun) return. if not plurals[1] then if not need_default_plural then inscat("uncountable nouns") return end plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end -- There are plural forms to show, so show them. inscat("countable nouns") local irregular, indeclinable for i, pl in ipairs(plurals) do local canon_pl = canonicalize_plural(pl.term, pagename, pos) if canon_pl then pl.term = canon_pl end local pl_term = get_link_page(pl.term, lang) if not (pagename:find(" ") or is_regular_plural(pl_term, pagename)) then irregular = true if pl_term == pagename then indeclinable = true end end end if irregular then inscat("naman wiþ fremde manigfealde") end if indeclinable then inscat("indeclinable nouns") end insert_inflection(data, plurals, "plural", "p") end -- Return the parameters to be used for nouns and proper nouns. Currently the same. local noun_params = { [1] = list_param, ["pl\1qual"] = {list = true, allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the plural", }, -- The following four only used for pluralia tantum (1=p) ["sg"] = list_param, ["attr"] = list_param, } pos_functions["nouns"] = { params = noun_params, func = do_nouns, } pos_functions["proper nouns"] = { params = noun_params, func = function(args, data) return do_nouns(args, data, "proper nouns") end, } local function base_default_verb_forms(verb) return escape(add_suffix(verb, "s.verb")), escape(add_suffix(verb, "ing")), escape(add_suffix(verb, "d")) end local function default_verb_forms(verb) local full_s_form, full_ing_form, full_ed_form = base_default_verb_forms(verb) if verb:find(" ") then local first, rest = verb:match("^(.-)( .*)$") local first_s_form, first_ing_form, first_ed_form = base_default_verb_forms(first) return full_s_form, full_ing_form, full_ed_form, first_s_form .. rest, first_ing_form .. rest, first_ed_form .. rest, first, rest else return full_s_form, full_ing_form, full_ed_form, nil, nil, nil, nil, nil end end local function compute_double_last_cons_stem_of_split_verb(verb, ending) local first, rest = verb:match("^(.-)( .*)$") if not first then error("Word '" .. verb .. "' mōt hæfþ rum in sīde tō notienne **") end local last_cons = first:match("([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])$") if not last_cons then error("Fyrest word '" .. first .. "' mōt ende wiþ samod swēgend tō notienne **") end return first .. last_cons .. ending .. rest end local function check_non_nil_star_form(form, pagename) if form == nil then error("Word '" .. pagename .. "' mōt hæfþ rum in sīde tō notienne *, **, *l, *! or *'") end return form end local function sub_tilde(form, pagename) if not form then return nil end if form:find("~") then form = form:gsub("~", replacement_escape(pagename)) end return form end local deprecated_qual_replaced_by_inline_modifier = { list = true, allow_holes = true, replaced_by = false, instead = "use an inline modifier <q:...> or <l:...> on the value" } pos_functions["verbs"] = { params = { [1] = {list = "pres_3sg", disallow_holes = true}, ["pres_3sg\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [2] = {list = "pres_ptc", disallow_holes = true}, ["pres_ptc\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [3] = {list = "past", disallow_holes = true}, ["past\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [4] = {list = "past_ptc", allow_holes = true}, ["past_ptc\1_qual"] = deprecated_qual_replaced_by_inline_modifier, ["noautolinkverb"] = boolean_param, ["angle_bracket"] = boolean_param, }, func = function(args, data) -- Get parameters local par1s local par2s = parse_inflection(args, {2, "pres_ptc"}) local par3s = parse_inflection(args, {3, "past"}) local par4s = parse_inflection(args, {4, "past_ptc"}) local pres_3sgs, pres_ptcs, pasts, past_ptcs local pagename = data.pagename ------------------------------------------- UTILITY FUNCTIONS #2 ------------------------------------------ -- These functions are used in both in the separate-parameter format and in the override params such as past_ptc2=. local full_default_s, full_default_ing, full_default_ed, split_default_s, split_default_ing, split_default_ed local lemma local function set_lemma_and_default_forms(the_lemma) lemma = the_lemma full_default_s, full_default_ing, full_default_ed, split_default_s, split_default_ing, split_default_ed, lemma_first, lemma_rest = default_verb_forms(the_lemma) end local function canonicalize_s_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_s elseif form == "*" then return check_non_nil_star_form(split_default_s, lemma) elseif form == "++" then return compute_plusplus_s_form(lemma, full_default_s) elseif form == "**" then if lemma:find("^[^ ]*[szx] ") then return compute_double_last_cons_stem_of_split_verb(lemma, "es") else return check_non_nil_star_form(split_default_s, lemma) end elseif form == "+!" then return lemma .. "s" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "s" .. lemma_rest elseif form == "+'" then return lemma .. "'s" elseif form == "*'" then return check_non_nil_star_form(lemma_first) .. "'s" .. lemma_rest elseif form == "+l" then if lemma:find("[szx]$") then return {{term = full_default_s, l = {"US"}}, {term = compute_plusplus_s_form(lemma, full_default_s), l = {"UK"}}} else return compute_plusplus_s_form(lemma, full_default_s) end elseif form == "*l" then if lemma:find("^[^ ]*[szx] ") then return {{term = check_non_nil_star_form(split_default_s, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "es"), l = {"UK"}}} else return check_non_nil_star_form(split_default_s, lemma) end else return sub_tilde(form, lemma) end end local function canonicalize_ing_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_ing elseif form == "*" then return check_non_nil_star_form(split_default_ing, lemma) elseif form == "++" then return compute_double_last_cons_stem(lemma) .. "ing" elseif form == "**" then return compute_double_last_cons_stem_of_split_verb(lemma, "ing") elseif form == "+!" then return lemma .. "ing" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "ing" .. lemma_rest elseif form == "+'" then return lemma .. "'ing" elseif form == "*'" then return check_non_nil_star_form(lemma_first) .. "'ing" .. lemma_rest elseif form == "+l" then return {{term = full_default_ing, l = {"US"}}, {term = compute_double_last_cons_stem(lemma) .. "ing", l = {"UK"}}} elseif form == "*l" then return {{term = check_non_nil_star_form(split_default_ing, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "ing"), l = {"UK"}}} else return sub_tilde(form, lemma) end end local function canonicalize_ed_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_ed elseif form == "*" then return check_non_nil_star_form(split_default_ed, lemma) elseif form == "++" then return compute_double_last_cons_stem(lemma) .. "ed" elseif form == "+!" then return lemma .. "ed" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "ed" .. lemma_rest elseif form == "+'" then return {{term = lemma .. "'d"}, {term = lemma .. "'ed"}} elseif form == "*'" then return {{term = check_non_nil_star_form(lemma_first) .. "'d" .. lemma_rest}, {term = check_non_nil_star_form(lemma_first) .. "'ed" .. lemma_rest}} elseif form == "**" then return compute_double_last_cons_stem_of_split_verb(lemma, "ed") elseif form == "+l" then return {{term = full_default_ed, l = {"US"}}, {term = compute_double_last_cons_stem(lemma) .. "ed", l = {"UK"}}} elseif form == "*l" then return {{term = check_non_nil_star_form(split_default_ed, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "ed"), l = {"UK"}}} else return sub_tilde(form, lemma) end end -- FIXME: options should be "+", "*", "++", "**", "+n", "*n", "++n" and "**n", but not "n" local function canonicalize_en_form(form) if form == "n" then track("n4") return add_suffix(lemma, "n") end return canonicalize_ed_form(form) end --------------------------------- MAIN PARSING/CONJUGATING CODE -------------------------------- local is_angle_bracket = args.angle_bracket if is_angle_bracket then if par2s[1] or par3s[1] or par4s[1] then error("Can't specify explicit values for 2=, 3= or 4= along wiþ the angle-bracket format") end elseif is_angle_bracket == nil and not par2s[1] and not par3s[1] and not par4s[1] and not args[1][2] and args[1][1] and args[1][1]:find("<") then if put.term_contains_top_level_html(args[1][1]) then -- Often, term_contains_top_level_html() returns true on the angle-bracket format, which would -- make the pcall() below succeed but leave the angle brackets as-is. Check for this and only do the -- pcall() if term_contains_top_level_html() returns false. is_angle_bracket = true else -- If it's ambiguous whether it's an angle-bracket format or separate params with an inline modifier, -- try to parse as the latter. If an error occurs, treat as the former. local ok ok, par1s = pcall(parse_inflection, args, {1, "pres_3sg"}) if not ok then par1s = nil is_angle_bracket = true end end end if is_angle_bracket then -------------------------- ANGLE-BRACKET FORMAT -------------------------- -- (0) Expand multiword term with angle brackets just on the first word. local arg11 = args[1][1] if arg11:find("^<.*>$") and pagename:find(" ") then local first, rest = pagename:match("^(.-)( .*)$") arg11 = first .. arg11 .. rest end -- (1) Parse the indicator specs inside of angle brackets. local function parse_indicator_spec(angle_bracket_spec) local inside = angle_bracket_spec:match("^<(.*)>$") assert(inside) local segments = put.parse_balanced_segment_run(inside, "[", "]") local comma_separated_groups = put.split_alternating_runs(segments, ",") if #comma_separated_groups > 4 then error("Too many comma-separated parts in indicator spec, expected at most 4: " .. angle_bracket_spec) end local function fetch_footnotes(separated_group) local footnotes for j = 2, #separated_group - 1, 2 do if separated_group[j + 1] ~= "" then error("Extraneous text after bracketed footnotes: '" .. concat(separated_group) .. "'") end if not footnotes then footnotes = {} end insert(footnotes, separated_group[j]) end return footnotes end local function fetch_specs(comma_separated_group) if not comma_separated_group then return {{term = "+"}} end local specs = {} local colon_separated_groups = put.split_alternating_runs(comma_separated_group, ":") for _, colon_separated_group in ipairs(colon_separated_groups) do local form = colon_separated_group[1] if form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then error("*, **, *l, *! and *' not allowed inside of indicator specs: " .. angle_bracket_spec) end if form == "" then form = "+" end local termobj = { term = form } local footnotes = fetch_footnotes(colon_separated_group) if footnotes then for _, footnote in ipairs(footnotes) do m_headword_utilities.add_footnote_to_termobj(termobj, footnote) end end insert(specs, termobj) end return specs end local s_specs = fetch_specs(comma_separated_groups[1]) local ing_specs = fetch_specs(comma_separated_groups[2]) local ed_specs = fetch_specs(comma_separated_groups[3]) local en_specs = fetch_specs(comma_separated_groups[4]) return { forms = {}, s_specs = s_specs, ing_specs = ing_specs, ed_specs = ed_specs, en_specs = en_specs, } end local parse_props = { parse_indicator_spec = parse_indicator_spec, } local alternant_multiword_spec = iut.parse_inflected_text(arg11, parse_props) -- (2) Check for user-specified brackets; remove any links from the lemma, but remember the original -- form so we can use it below in the 'lemma_linked' form. -- Check to see if there are brackets in the pre-text or post-text. If so, use the linked lemma (with the -- verb autolinked unless noautolinkverb is given). Otherwise, use the default headword algorithm. local function check_bracket(val) if val:find("%[%[") then alternant_multiword_spec.saw_bracket = true end end for _, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do check_bracket(alternant_or_word_spec.before_text) if alternant_or_word_spec.alternants then for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do for _, word_spec in ipairs(multiword_spec.word_specs) do check_bracket(word_spec.before_text) end check_bracket(multiword_spec.post_text) end end end check_bracket(alternant_multiword_spec.post_text) iut.map_word_specs(alternant_multiword_spec, function(base) if base.lemma == "" then base.lemma = pagename end base.orig_lemma = base.lemma base.lemma = remove_links(base.lemma) if args.noautolinkverb or base.orig_lemma:find("%[%[") then base.linked_lemma = base.orig_lemma else base.linked_lemma = "[[" .. base.orig_lemma .. "]]" end end) -- (3) Conjugate the verbs according to the indicator specs parsed above. local all_verb_slots = { lemma = "infinitive", lemma_linked = "infinitive", s_form = "3|s|pres", ing_form = "pres|ptcp", ed_form = "past", en_form = "past|ptcp", } local function conjugate_verb(base) local function process_specs(slot, specs, canon_func, default_values, default_already_formobj) local function insert_termobj_into_slot(termobj) local formobj = m_headword_utilities.convert_termobj_to_formobj(termobj) -- If the form is -, don't insert any forms, which will result in there being no overall forms -- (in fact it will be nil). We check for that down below and substitute a single "-" as the -- form, which in turn gets turned into special labels like "no present participle". if formobj.form == "-" then if formobj.footnotes then error("Unable to preserve footnotes specified on missing form '-': FIXME: " .. dump(formobj.footnotes)) end else iut.insert_form(base.forms, slot, formobj) end end local function canonicalize_and_insert(arg) local canon_arg = canon_func(arg) if type(canon_arg) == "string" then arg.term = canon_arg insert_termobj_into_slot(arg) else for _, canon in ipairs(canon_arg) do m_headword_utilities.combine_termobj_decorations(canon, arg) insert_termobj_into_slot(canon) end end end for _, arg in ipairs(specs) do if arg.term == "+" then if default_values then -- will be nil if past tense specified as - and no past ptc given for _, val in ipairs(default_values) do val = shallowCopy(val) if default_already_formobj then local argformobj = m_headword_utilities.convert_termobj_to_formobj(arg) val.footnotes = iut.combine_footnotes(val.footnotes, argformobj.footnotes) iut.insert_form(base.forms, slot, val) else m_headword_utilities.combine_termobj_decorations(val, arg) canonicalize_and_insert(val) end end end else canonicalize_and_insert(arg) end end end set_lemma_and_default_forms(base.lemma) local all_part_default_specs = {} local function process_and_canonicalize_s_form(arg) local form = arg.term if form == "+" then error("Internal error: '+' should have been converted to '^' by now") end if form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then error(("Internal error: '%s' should have already thrown an error"):format(form)) end if form == "^" or form == "++" or form == "+l" or form == "+!" or form == "+'" then insert(all_part_default_specs, shallowCopy(arg)) end return canonicalize_s_form(form) end process_specs("s_form", base.s_specs, process_and_canonicalize_s_form, {{term = "^"}}) if not all_part_default_specs[1] then all_part_default_specs[1] = {term = "^"} end process_specs("ing_form", base.ing_specs, function(arg) return canonicalize_ing_form(arg.term) end, all_part_default_specs) process_specs("ed_form", base.ed_specs, function(arg) return canonicalize_ed_form(arg.term) end, all_part_default_specs) process_specs("en_form", base.en_specs, function(arg) return canonicalize_en_form(arg.term) end, base.forms.ed_form, "default already formobj") iut.insert_form(base.forms, "lemma", {form = base.lemma}) -- Add linked version of lemma for use in head=. We write this in a general fashion in case -- there are multiple lemma forms (which isn't possible currently at this level, although it's -- possible overall using the ((...,...)) notation). iut.insert_forms(base.forms, "lemma_linked", iut.map_forms(base.forms.lemma, function(form) if form == base.lemma and base.linked_lemma:find("%[%[") then return base.linked_lemma else return form end end)) end local inflect_props = { slot_table = all_verb_slots, inflect_word_spec = conjugate_verb, } iut.inflect_multiword_or_alternant_multiword_spec(alternant_multiword_spec, inflect_props) -- (4) Fetch the forms and put the conjugated lemmas in data.heads if not explicitly given. local function fetch_termobjs(slot) local forms = alternant_multiword_spec.forms[slot] -- See above. This should only occur if the user explicitly used - for a spec. if not forms or not forms[1] then return {{term = "-"}} end local termobjs = {} for _, formobj in ipairs(forms) do insert(termobjs, m_headword_utilities.convert_formobj_to_termobj(formobj)) end return termobjs end pres_3sgs = fetch_termobjs("s_form") pres_ptcs = fetch_termobjs("ing_form") pasts = fetch_termobjs("ed_form") past_ptcs = fetch_termobjs("en_form") -- Use the "linked" form of the lemma as the head if no head= explicitly given and the user specified -- brackets in one of the lemmas. Otherwise we use the default headword-linking algorithm. if not data.user_specified_heads[1] and alternant_multiword_spec.saw_bracket then data.heads = {} for _, lemma_obj in ipairs(alternant_multiword_spec.forms.lemma_linked) do insert(data.heads, m_headword_utilities.convert_formobj_to_termobj(lemma_obj)) end end else -------------------------- SEPARATE-PARAM FORMAT -------------------------- set_lemma_and_default_forms(pagename) par1s = par1s or parse_inflection(args, {1, "pres_3sg"}) pres_3sgs = {} pres_ptcs = {} pasts = {} past_ptcs = {} if not par1s[1] then par1s = {{term = "+"}} end if not par2s[1] then par2s = {{term = "+"}} end if not par3s[1] then par3s = {{term = "+"}} end if not par4s[1] then par4s = {{term = "+"}} end local function process_argument(args, dest, canon_func, default_values, default_already_canonicalized) local function canonicalize_and_insert(arg) local canon_arg = canon_func(arg) if type(canon_arg) == "string" then arg.term = canon_arg m_headword_utilities.insert_termobj_combining_duplicates(dest, arg) else for _, canon in ipairs(canon_arg) do m_headword_utilities.combine_termobj_decorations(canon, arg) m_headword_utilities.insert_termobj_combining_duplicates(dest, canon) end end end for _, arg in ipairs(args) do if arg.term == "+" then for _, val in ipairs(default_values) do val = shallowCopy(val) m_headword_utilities.combine_termobj_decorations(val, arg) if default_already_canonicalized then m_headword_utilities.insert_termobj_combining_duplicates(dest, val) else canonicalize_and_insert(val) end end else canonicalize_and_insert(arg) end end end local all_part_default_specs = {} local function process_and_canonicalize_s_form(arg) local form = arg.term if form == "+" then error("Internal error: '+' should have been converted to '^' by now") end if form == "^" or form == "++" or form == "+l" or form == "+!" or form == "+'" or form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then insert(all_part_default_specs, shallowCopy(arg)) end return canonicalize_s_form(form) end process_argument(par1s, pres_3sgs, process_and_canonicalize_s_form, {{term = "^"}}) if not all_part_default_specs[1] then all_part_default_specs[1] = {term = "^"} end process_argument(par2s, pres_ptcs, function(arg) return canonicalize_ing_form(arg.term) end, all_part_default_specs) process_argument(par3s, pasts, function(arg) return canonicalize_ed_form(arg.term) end, all_part_default_specs) process_argument(par4s, past_ptcs, function(arg) return canonicalize_en_form(arg.term) end, pasts, "default already canonicalized") end ------------------------------------------- INSERT INFLECTIONS ------------------------------------------ insert_inflection(data, pres_3sgs, "third-person singular simple present", "s-verb-form") insert_inflection(data, pres_ptcs, "present participle", "ing-form") if deepEquals(pasts, past_ptcs) then insert_inflection(data, pasts, "simple past and past participle", "ed-form", "no simple past or past participle") else insert_inflection(data, pasts, "simple past", "spast") insert_inflection(data, past_ptcs, "past participle", "past|part") end if pagename:find(" ") then -- Check for placeholder "it" local words = split(pagename, " ") for _, word in ipairs(words) do if word == "it" or word == "its" or word == "it's" then insert(data.categories, langname .. ' terms wiþ placeholder "it"') break end end -- Check for phrasal verbs local phrasal_adverbs = list_to_set{ -- NOTE: This should only contain common phrasal adverbs, not random words like [[low]], -- [[adrift]], etc. "aback", "about", "above", "across", "after", "against", "ahead", "along", "apart", "around", "as", "aside", "at", "away", "back", "before", "behind", "below", "between", "beyond", "by", "down", "for", "forth", "from", "in", "into", "of", "off", "on", "onto", "out", "over", "past", "round", "through", "to", "together", "towards", "under", "up", "upon", "with", "without", } local allowed_non_adverb_words = list_to_set{ "it", "one", "oneself", "someone", } local base = pagename local seen_adverbs = {} -- Only consider a verb to be phrasal if it consists of a single base verb followed exclusively by either -- adverbs from `phrasal_adverbs` or placeholder words from `allowed_non_adverb_words`, where at -- least one following word is from `phrasal_adverbs` (hence [[can it]] is not a phrasal verb). while true do local prev, word = base:match("^(.+) (.-)$") if not prev then break end if phrasal_adverbs[word] then insert(seen_adverbs, word) elseif allowed_non_adverb_words[word] then -- do nothing else break end base = prev end if not base:find(" ") and seen_adverbs[1] then insert(data.categories, langname .. " phrasal verbs") for i = #seen_adverbs, 1, -1 do insert(data.categories, langname .. ' phrasal verbs formed wiþ "' .. seen_adverbs[i] .. '"') end end end end, } ----------------------------------------------------------------------------------------- -- Suffix forms -- ----------------------------------------------------------------------------------------- pos_functions["suffix forms"] = { params = { [1] = {required = true, list = true, disallow_holes = true}, }, func = function(args, data, is_suffix) local suffix_type = {} for _, typ in ipairs(args[1]) do insert(suffix_type, typ .. "-forming suffix") end insert(data.inflections, {label = "non-lemma form of " .. m_table.serialCommaJoin(suffix_type, {conj = "or"})}) end, } return export 0pr0m96lw09bogexw1yeqsgg96stl2m 55006 55004 2026-09-30T18:25:43Z Deadend0914 7211 55006 Scribunto text/plain local export = {} local pos_functions = {} --[==[ Author from 2020 on: mostly Benwing2, with significant contributions from Theknightwho. Based on a prior version by Rua (by now mostly rewritten), with contributions from Erutuon and others (see history for full attribution). ]==] local force_cat = false -- for testing; if true, categories appear in non-mainspace pages local require = require local require_when_needed = require("Module:require when needed") local en_utilities_module = "Module:en-utilities" local headword_utilities_module = "Module:headword utilities" local headword_module = "Module:headword" local inflection_utilities_module = "Module:inflection utilities" local parse_utilities_module = "Module:parse utilities" local JSON_module = "Module:JSON" local labels_module = "Module:labels" local links_module = "Module:links" local parameters_module = "Module:parameters" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local utilities_module = "Module:utilities" local yesno_module = "Module:yesno" local iut = require_when_needed(inflection_utilities_module) local put = require_when_needed(parse_utilities_module) local m_headword_utilities = require_when_needed(headword_utilities_module) local add_links_to_multiword_term = require_when_needed(headword_utilities_module, "add_links_to_multiword_term") local add_suffix = require_when_needed(en_utilities_module, "add_suffix") local apply_link_modifiers = require_when_needed(headword_utilities_module, "apply_link_modifiers") local concat = table.concat local deepEquals = require_when_needed(table_module, "deepEquals") local dump = mw.dumpObject local format_categories = require_when_needed(utilities_module, "format_categories") local full_headword = require_when_needed(headword_module, "full_headword") local get_label_info = require_when_needed(labels_module, "get_label_info") local get_link_page = require_when_needed(links_module, "get_link_page") local glossary_link = require_when_needed(headword_utilities_module, "glossary_link") local insert = table.insert local insertIfNot = require_when_needed(table_module, "insertIfNot") local ipairs = ipairs local is_regular_plural = require_when_needed(en_utilities_module, "is_regular_plural") local list_to_set = require_when_needed(table_module, "listToSet") local pairs = pairs local process_params = require_when_needed(parameters_module, "process") local remove = table.remove local remove_links = require_when_needed(links_module, "remove_links") local replacement_escape = require_when_needed(string_utilities_module, "replacement_escape") local shallowCopy = require_when_needed(table_module, "shallowCopy") local singularize = require_when_needed(en_utilities_module, "singularize") local split = require_when_needed(string_utilities_module, "split") local toJSON = require_when_needed(JSON_module, "toJSON") local toNFD = mw.ustring.toNFD local type = type local ulen = require_when_needed(string_utilities_module, "len") local ulower = require_when_needed(string_utilities_module, "lower") local umatch = require_when_needed(string_utilities_module, "match") local u = require_when_needed(string_utilities_module, "char") local ugsub = require_when_needed(string_utilities_module, "gsub") local lang = require("Module:languages").getByCode("en") local langname = lang:getCanonicalName() local list_param = {list = true, disallow_holes = true} local list_allow_holes = {list = true, allow_holes = true} local boolean_param = {type = "boolean"} local function ine(val) if val == "" then return nil else return val end end local function track(page) require("Module:debug/track")("en-headword/" .. page) return true end ------------------------------------------- UTILITY FUNCTIONS ------------------------------------------ -- Parse and return an inflection not requiring additional processing. The raw arguments come from `args[field]`, which -- is parsed for inline modifiers. local function parse_inflection(args, field, is_head) local argfield = field if type(argfield) == "table" then argfield = argfield[1] end return m_headword_utilities.parse_term_list_with_modifiers { paramname = field, forms = args[argfield], splitchar = ",", is_head = is_head, } end -- Insert the parsed inflections in `terms` (as parsed by `parse_inflection`) into `data.inflections`, with label -- `label` and optional accelerator spec `accel`. local function insert_inflection(data, terms, label, accel, no_label) for _, termobj in ipairs(terms) do m_headword_utilities.remove_termobj_field_modifiers(termobj) end m_headword_utilities.insert_inflection { headdata = data, terms = terms, label = label, no_label = no_label, accel = accel and {form = accel} or nil, } end -- Insert a fixed label `label` into the inflections for `data`. If `originating_term` is supplied, copy the decorations -- from it into the fixed label. local function insert_fixed_inflection(data, label, originating_term) m_headword_utilities.insert_fixed_inflection { headdata = data, originating_term = originating_term, label = label, } 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 -- These functions are used directly in the <> format as well as in the utility functions #2 below. local function compute_double_last_cons_stem(term) local last_cons = term:match("([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])$") if not last_cons then error("Verb stem '" .. term .. "' must end in a consonant to use ++") end return term .. last_cons end local function compute_plusplus_s_form(term, default_s_form) if term:find("[szx]$") then -- regas -> regasses, derez -> derezzes return compute_double_last_cons_stem(term) .. "es" else return default_s_form end end -- The main entry point. -- This is the only function that can be invoked from a template. function export.show(frame) local iparams = { [1] = true, } local iargs = require("Module:parameters").process(frame.args, iparams) local parargs = frame:getParent().args local poscat = iargs[1] local pos_in_1 = not poscat if pos_in_1 then poscat = ine(parargs[1]) or mw.title.getCurrentTitle().fullText == "Template:en-head" and "interjection" or error("Part of speech must be specified in 1=") poscat = require(headword_module).canonicalize_pos(poscat) end local indexing_poscat = pos_in_1 and "head" or poscat local params = { ["head"] = list_param, ["id"] = true, ["json"] = boolean_param, ["sort"] = true, ["splithyph"] = boolean_param, ["nosplithyph"] = boolean_param, ["hyphspace"] = boolean_param, ["nolink"] = boolean_param, ["nolinkhead"] = {type = "boolean_param", alias_of = "nolink"}, ["suffix"] = boolean_param, ["nosuffix"] = boolean_param, ["nomultiwordcat"] = boolean_param, ["abbr"] = list_param, ["the"] = true, ["def"] = {alias_of = "the"}, ["pagename"] = true, -- for testing } if pos_in_1 then params[1] = {required = true} -- required but ignored as already processed above end local pos_data = pos_functions[indexing_poscat] local pos_func if pos_data then local pos_params = pos_data.params if pos_params then for key, val in pairs(pos_params) do params[key] = val end end pos_func = pos_data.func end local args = process_params(parargs, params) -- Account for unsupported titles, e.g. 'C|N>K' instead of 'Unsupported titles/C through N to K'. local pagename = args.pagename or mw.loadData("Module:headword/data").pagename local user_specified_heads = parse_inflection(args, "head", "is_head") local heads = user_specified_heads local autohead if args.nolink or not pagename:find("[ '%-]") then autohead = pagename else local en_no_split_apostrophe_words = list_to_set { "one's", "someone's", "he's", "she's", "it's", } local en_include_hyphen_prefixes = list_to_set { -- We don't include things that are also words even though they are often (perhaps mostly) prefixes, e.g. -- "be", "counter", "cross", "extra", "half", "mid", "over", "pan", "under". "acro", "acousto", "Afro", "agro", "anarcho", "angio", "Anglo", "ante", "anti", "arch", "auto", "bi", "bio", "cis", "co", "cryo", "crypto", "de", "demi", "eco", "electro", "Euro", "ex", "Greco", "hemi", "hydro", "hyper", "hypo", "infra", "Indo", "inter", "intra", "Judeo", "macro", "meta", "micro", "mini", "multi", "neo", "neuro", "non", "para", "peri", "post", "pre", "pro", "proto", "pseudo", "re", "semi", "sub", "super", "trans", "un", "vice", } local function is_english(term) local title = mw.title.new(term) if title and title.exists then local content = title:getContent() if content and content:find("==English==\n") then return true end end return false end local function en_split_hyphen_when_space(word) if not word:find("-", nil, true) then return nil end if args.hyphspace then return "[[" .. word:gsub("%-+", " ") .. "|" .. word .. "]]" end if args.nosplithyph then return "[[" .. word .. "]]" end if not args.splithyph then local space_word = word:gsub("%-+", " ") if is_english(space_word) then return "[[" .. space_word .. "|" .. word .. "]]" end if is_english(word) then return "[[" .. word .. "]]" end end return nil end local function en_split_apostrophe(word) local base = word:match("^(.*)'s$") if base then return "[[" .. base .. "]][[-'s|'s]]" end -- Only treat final apostrophe as possessive if preceded by something that looks like a plural ending in /z/. -- In particular we don't want to do it for words like [[truckin']]. base = word:match("^(.*[sxz])'$") if base then if base:find("s$") then local sg = singularize(base) if is_english(sg) then return "[[" .. sg .. "|" .. base .. "]][[-'|']]" end end return "[[" .. base .. "]][[-'|']]" end return "[[" .. word .. "]]" end autohead = add_links_to_multiword_term(pagename, { split_hyphen_when_space = en_split_hyphen_when_space, split_apostrophe = en_split_apostrophe, no_split_apostrophe_words = en_no_split_apostrophe_words, include_hyphen_prefixes = en_include_hyphen_prefixes, }) end if not heads[1] then heads = {{term = autohead}} else for _, headobj in ipairs(heads) do local head = headobj.term if head:find("^~") then head = apply_link_modifiers(autohead, head:sub(2), lang) headobj.term = head elseif head:find("^[!?]$") then -- If explicit head= just consists of ! or ?, add it to the end of the default head. headobj.term = autohead .. head end if head == autohead then track("redundant-head") end end end -- handle the=/def= if args.the == "~" then local newheads = {} for _, headobj in ipairs(heads) do local barehead = shallowCopy(headobj) insert(newheads, barehead) headobj.term = "the " .. headobj.term insert(newheads, headobj) end heads = newheads elseif args.the then local the = require(yesno_module)(args.the) if the then for _, headobj in ipairs(heads) do headobj.term = "the " .. headobj.term end end end local data = { lang = lang, pos_category = poscat, categories = {}, heads = heads, user_specified_heads = user_specified_heads, -- We use our own splitting algorithm so the redundant head cat will be inaccurate. no_redundant_head_cat = true, inflections = {}, nomultiwordcat = args.nomultiwordcat, sort_key = args.sort, pagename = pagename, id = args.id, force_cat_output = force_cat, } local function inscat(cat) insert(data.categories, langname .. " " .. cat) end local is_suffix = false if args.suffix or not args.nosuffix and pagename:find("^%-") and not pagename:find("^%-%-") and poscat ~= "suffix forms" then is_suffix = true data.pos_category = "suffixes" local singular_poscat = singularize(poscat) inscat(singular_poscat .. "-forming suffixes") insert(data.inflections, {label = singular_poscat .. "-forming suffix"}) end if pos_func then pos_func(args, data, is_suffix) end local extra_categories = {} if pagename:find("[Qq]") then -- Check for q not followed by u. We want to exclude things like [[13q deletion syndrome]] and [[BFOQ]] that -- don't have a lowercase letter on either side, as well as things like [[& seq.]] and [[acq.]] that are -- abbreviations for words containing a following u. -- -- Approximate range of combining diacritics; we want to remove them so the checks below for -- a lowercase letter next to the q aren't tripped up by diacritics on the letter. local u300 = u(0x0300) local u36F = u(0x036F) local pagename_no_diacritics = ugsub(toNFD(pagename), "[" .. u300 .. "-" .. u36F .. "]", "") if pagename_no_diacritics:find("[Qq][a-tv-z]") or pagename_no_diacritics:find("[a-z]q[^u.]") or pagename_no_diacritics:find("[a-z]q$") then inscat("words containing Q not followed by U") end end -- toNFD performs decomposition, so letters that decompose to an ASCII -- vowel and a diacritic, such as é, are counted as vowels and do not do not -- need to be included in the pattern. if not umatch(ulower(toNFD(pagename)), "[aeiouyæœøəªºαεηιουω]") then inscat("words spelled wiþūtan vowels") end if pagename:find("yre$") then inscat('words ending in "-yre"') end if not pagename:find(" ") and ulen(pagename) >= 25 then insert(extra_categories, "Long " .. langname .. " words") end if pagename:find("^[^aeiou ]*a[^aeiou ]*e[^aeiou ]*i[^aeiou ]*o[^aeiou ]*u[^aeiou ]*$") then inscat("words that use all vowels in alphabetical order") end parse_and_insert_inflection(data, args, "abbr", "abbreviation") if args.json then return toJSON(data) end return full_headword(data) .. (extra_categories[1] and format_categories(extra_categories, lang, args.sort) or "") end local function make_default_comparative(word) if word == "good" or word == "well" then return {"better"} elseif word == "bad" or word == "badly" then return {"worse"} elseif word == "far" then return {"further", "farther"} else return {add_suffix(word, "r")} end end local function make_default_superlative(word) if word == "good" or word == "well" then return {"best"} elseif word == "bad" or word == "badly" then return {"worst"} elseif word == "far" then return {"furthest", "farthest"} else return {add_suffix(word, "st.superlative")} end end -- This function does the common work between adjectives and adverbs. local function process_comparative_args(data, args, plpos) local pagename = data.pagename local comps = parse_inflection(args, 1) local sups = parse_inflection(args, "sup") local outcomps, outsups if args.componly then if comps[1] then error("Can't specify comparatives of comparative-only " .. plpos) end insert(data.inflections, {label = glossary_link("comparative") .. " form only"}) insert(data.categories, langname .. " comparative-only " .. plpos) -- Set to empty list so we don't get any comparatives output, but process superlatives if specified. outcomps = {} if not sups[1] then -- Set to empty list so we don't get any superlatives output unless explicitly given. outsups = {} end elseif args.suponly then if comps[1] or sups[1] then error("Can't specify comparatives or superlatives of or superlative-only " .. plpos) end insert(data.inflections, {label = glossary_link("superlative") .. " form only"}) insert(data.categories, langname .. " superlative-only " .. plpos) return end -- If the first parameter is ?, then don't show anything, just return. if comps[1] and comps[1].term == "?" then if comps[2] then error("Can't specify additional comparatives along wiþ '?'") end if sups[1] then error("Can't specify superlatives along wiþ '?' for the comparative") end return end if comps[1] and comps[1].term == "-" then local hyphencomp = remove(comps, 1) -- Remove the "-" but retain for decorations. -- Not (generally) comparable; may occasionally have a comparative if comps[1] then insert_fixed_inflection(data, "not generally <<comparable>>", hyphencomp) elseif not sups[1] then insert_fixed_inflection(data, "not <<comparable>>", hyphencomp) insert(data.categories, langname .. " uncomparable " .. plpos) return else -- No comparative, but a superlative. insert_inflection() will correctly generate 'no comparative' if we -- pass in "-" as the value. outcomps = {hyphencomp} end elseif not comps[1] then comps = {{term = "more"}} end if not outcomps then -- not if we set `outcomps` to "-" above or processed a comparative-only term outcomps = {} -- Go over each parameter given and create a comparative and superlative form. for _, compobj in ipairs(comps) do local comp = compobj.term if comp == "-" then error("Comparative of '-' only allowed as first comparative") end if comp == "+" then comp = "+more" elseif comp == "more" and pagename ~= "many" and pagename ~= "much" then comp = "+more" elseif comp == "further" and pagename ~= "far" then comp = "+further" elseif comp == "better" and pagename ~= "good" and pagename ~= "well" then comp = "+better" elseif comp:find("~") then comp = comp:gsub("~", replacement_escape(pagename)) end compobj.origterm = comp if comp == "+more" then comp = "more [[" .. pagename .. "]]" elseif comp == "+further" then comp = {"further [[" .. pagename .. "]]", "farther [[" .. pagename .. "]]"} elseif comp == "+better" then comp = "better [[" .. pagename .. "]]" elseif comp == "er" then -- Add -er. comp = add_suffix(pagename, "r") elseif comp == "ier" then if pagename:sub(-1) ~= "y" then error("Can't specify 'ier' comparative unless the term ends wiþ 'y': " .. pagename) end comp = pagename:gsub("e?y$", "ier") elseif comp:find("^%+") then local special = m_headword_utilities.get_special_indicator(comp, "noerror") if special then comp = m_headword_utilities.handle_multiword(pagename, special, make_default_comparative) end end if type(comp) == "table" and not comp[2] then comp = comp[1] end if type(comp) == "table" then for i = 1, #comp - 1 do local outobj = shallowCopy(compobj) outobj.term = comp[i] insert(outcomps, outobj) end compobj.term = comp[#comp] insert(outcomps, compobj) else compobj.term = comp insert(outcomps, compobj) end end end if sups[1] and sups[1].term == "-" then if sups[2] then error("Can't specify '-' as superlative followed by further values") end -- No superlative. insert_inflection() will correctly generate 'no superlative' if we pass in "-" as the value. outsups = sups else if not sups[1] then sups = {{term = "+"}} end end -- `outsups` will be set if we set `outsups` to "-" above or processed a comparative-only term without superlatives. if not outsups then outsups = {} local function process_sup(sup, special, supobj, compobj) if special then sup = m_headword_utilities.handle_multiword(pagename, special, make_default_superlative) elseif sup == "-" or sup == "+" then error(("Internal error: Superlative value of '%s' should have been handled earlier"):format(sup)) elseif sup == "+most" then sup = "most [[" .. pagename .. "]]" elseif sup == "+furthest" then sup = {"furthest [[" .. pagename .. "]]", "farthest [[" .. pagename .. "]]"} elseif sup == "+best" then sup = "best [[" .. pagename .. "]]" elseif sup == "est" then -- Add -est. sup = add_suffix(pagename, "st.superlative") elseif sup == "iest" then if pagename:sub(-1) ~= "y" then error("Can't specify 'iest' superlative unless the term ends wiþ 'y': " .. pagename) end sup = pagename:gsub("e?y$", "iest") end if type(sup) == "table" and not sup[2] then sup = sup[1] end if compobj then supobj = shallowCopy(supobj) supobj = m_headword_utilities.combine_termobj_decorations(supobj, compobj) end if type(sup) == "table" then for i = 1, #sup - 1 do local outobj = shallowCopy(supobj) outobj.term = sup[i] insert(outsups, outobj) end supobj.term = sup[#sup] insert(outsups, supobj) else supobj.term = sup insert(outsups, supobj) end end for _, supobj in ipairs(sups) do local sup = supobj.term if sup == "-" then error("Superlative of '-' only allowed as first superlative") end if sup == "+" then if not comps[1] then error("Superlative of '+' can't be specified when there are no comparatives") end for _, compobj in ipairs(comps) do local comp = compobj.origterm local special if comp == "+more" then sup = "+most" elseif comp == "+further" then sup = "+furthest" elseif comp == "+better" then sup = "+best" elseif comp == "er" then sup = "est" elseif comp == "ier" then sup = "iest" else if comp:find("^%+") then special = m_headword_utilities.get_special_indicator(comp, "noerror") end if not special then -- If the full comparative was given, then derive the superlative by replacing -er with -- -est. if comp:sub(-2) == "er" then sup = comp:sub(1, -3) .. "est" else error(("The superlative cannot be derived automatically from comparative '%s' because it doesn't end in -er"):format(comp)) end end end process_sup(sup, special, supobj, compobj) end else local special = m_headword_utilities.get_special_indicator(sup, "noerror") -- Do some work here rather than in process_sup() so we don't end up double-processing a term with a '~' -- in it or a term that happens to be 'most' or similar after substitution of ~ in the comparative. if not special then if sup == "most" and pagename ~= "many" and pagename ~= "much" then sup = "+most" elseif sup == "furthest" and pagename ~= "far" then sup = "+furthest" elseif sup == "best" and pagename ~= "good" and pagename ~= "well" then sup = "+best" elseif sup:find("~") then sup = sup:gsub("~", replacement_escape(pagename)) end end process_sup(sup, special, supobj) end end end insert_inflection(data, outcomps, "<<comparative>>", "comparative") insert_inflection(data, outsups, "<<superlative>>", "superlative") end pos_functions["adjectives"] = { params = { [1] = list_param, ["comp_qual"] = {list = "comp\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the comparative value", }, ["sup"] = list_param, ["sup_qual"] = {list = "sup\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the superlative value", }, ["componly"] = boolean_param, ["suponly"] = boolean_param, }, func = function(args, data) -- Process the comparatives and superlatives. process_comparative_args(data, args, "adjectives") end, } pos_functions["adverbs"] = { params = { [1] = list_param, ["comp_qual"] = {list = "comp\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the comparative value", }, ["sup"] = list_param, ["sup_qual"] = {list = "sup\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the superlative value", }, ["componly"] = boolean_param, ["suponly"] = boolean_param, }, func = function(args, data) -- Process the comparatives and superlatives. process_comparative_args(data, args, "adverbs") end, } local function escape(str) return (str:gsub("\\([:#])", "\\\\%1") :gsub("[:#]", "\\%0")) end local function canonicalize_plural(pl, pagename, pos) if pl == "+" then return escape(add_suffix(pagename, "s.plural", pos)) elseif pl == "++" then return escape(compute_plusplus_s_form(pagename, add_suffix(pagename, "s.plural", pos))) elseif pl == "*" then return escape(pagename) elseif pl == "ies" then if pagename:sub(-1) == "y" then return escape(pagename:gsub("e?y$", pl)) end error("Can't specify 'ies' plural unless the term ends wiþ 'y'.") elseif pl == "s" or pl == "es" or pl == "'s" then return escape(pagename .. pl) end end local function do_nouns(args, data, pos) local pagename = data.pagename pos = pos or "noun" local plurals = parse_inflection(args, 1) local function insert_plurale_tantum_inflections(is_plural_only, originating_label) if args.sg[1] then insert_fixed_inflection(data, "normally plural", originating_label) parse_and_insert_inflection(data, args, "anf.", "singular") elseif is_plural_only then insert_fixed_inflection(data, "plural only", originating_label) end if args.attr[1] then parse_and_insert_inflection(data, args, "attr", "attributive") end end local function first_pl_term() return plurals[1] and plurals[1].term or nil end if first_pl_term() == "p" then -- plurale tantum if plurals[2] then error("Wiþ plurale tantum nama, can't specify more than one plural") end data.genders = {"p"} -- this should auto-insert the correct 'pluralia tantum' category insert_plurale_tantum_inflections("plural only", plurals[1]) return end local function inscat(cat) insert(data.categories, langname .. " " .. cat) end local need_default_plural = pos == "noun" if first_pl_term() == "sp" then -- construed as singular or plural sp = remove(plurals, 1) -- Remove the "sp" but retain it for its decorations. inscat("naman construed as singular or plural") data.genders = {"s", "p"} -- this should auto-insert the correct 'pluralia tantum' category insert_plurale_tantum_inflections(nil, sp) need_default_plural = false elseif first_pl_term() == "-" then -- Untellendlīc noun; may occasionally have a plural local hyphpl = remove(plurals, 1) -- Remove the "-" but retain for decorations. inscat("uncountable nouns") -- If plural forms were given explicitly, then show "oft" if plurals[1] then insert_fixed_inflection(data, "oft <<untellendlīc>>", hyphpl) else insert_fixed_inflection(data, "<<untellendlīc>>", hyphpl) end need_default_plural = false elseif first_pl_term() == "#" then -- oft tellendlīc (e.g., "grilled cheese") local hashpl = remove(plurals, 1) -- Remove the "#" but retain for decorations. insert_fixed_inflection(data, "oft <<tellendlīc>>", hashpl) inscat("uncountable nouns") inscat("countable nouns") -- If no plural was given, add a default one now if not plurals[1] then plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end elseif first_pl_term() == "~" then -- Mixed tellendlīc/untellendlīc noun, always has a plural local tildepl = remove(plurals, 1) -- Remove the "~" but retain for decorations. insert_fixed_inflection(data, "<<tellendlīc>> and <<untellendlīc>>", tildepl) inscat("uncountable nouns") inscat("countable nouns") -- If no plural was given, add a default one now if not plurals[1] then plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end end -- Plural is unknown if first_pl_term() == "?" then local questionpl = remove(plurals, 1) -- Remove the "?" but retain for decorations. -- Not desired; see [[Wiktionary:Tea_room/2021/August#"Plural unknown or uncertain"]] -- insert_fixed_inflection(data, "manigfeald unġecnāwen oþþe ungewiss", questionpl) inscat("naman wiþ unġecnāwen oþþe ungewiss manigfealde") if plurals[1] then error("Ne cann āmearcian sweotole manigfealde andlang wiþ '?' of unġecnāwen/ungewiss manigfeald") end return end -- Plural is not attested if first_pl_term() == "!" then local exclampl = remove(plurals, 1) -- Remove the "!" but retain for decorations. insert_fixed_inflection(data, "plural not attested", exclampl) inscat("naman wiþ unattested manigfealde") if plurals[1] then error("Ne cann āmearcian sweotole manigfealde andlang wiþ '!' of gecȳþed manigfeald") end return end -- If no plural was given, maybe add a default one, otherwise (when "-" was given or proper noun) return. if not plurals[1] then if not need_default_plural then inscat("uncountable nouns") return end plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end -- There are plural forms to show, so show them. inscat("countable nouns") local irregular, indeclinable for i, pl in ipairs(plurals) do local canon_pl = canonicalize_plural(pl.term, pagename, pos) if canon_pl then pl.term = canon_pl end local pl_term = get_link_page(pl.term, lang) if not (pagename:find(" ") or is_regular_plural(pl_term, pagename)) then irregular = true if pl_term == pagename then indeclinable = true end end end if irregular then inscat("naman wiþ fremde manigfealde") end if indeclinable then inscat("indeclinable nouns") end insert_inflection(data, plurals, "mnf.", "p") end -- Return the parameters to be used for nouns and proper nouns. Currently the same. local noun_params = { [1] = list_param, ["pl\1qual"] = {list = true, allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the plural", }, -- The following four only used for pluralia tantum (1=p) ["sg"] = list_param, ["attr"] = list_param, } pos_functions["nouns"] = { params = noun_params, func = do_nouns, } pos_functions["proper nouns"] = { params = noun_params, func = function(args, data) return do_nouns(args, data, "proper nouns") end, } local function base_default_verb_forms(verb) return escape(add_suffix(verb, "s.verb")), escape(add_suffix(verb, "ing")), escape(add_suffix(verb, "d")) end local function default_verb_forms(verb) local full_s_form, full_ing_form, full_ed_form = base_default_verb_forms(verb) if verb:find(" ") then local first, rest = verb:match("^(.-)( .*)$") local first_s_form, first_ing_form, first_ed_form = base_default_verb_forms(first) return full_s_form, full_ing_form, full_ed_form, first_s_form .. rest, first_ing_form .. rest, first_ed_form .. rest, first, rest else return full_s_form, full_ing_form, full_ed_form, nil, nil, nil, nil, nil end end local function compute_double_last_cons_stem_of_split_verb(verb, ending) local first, rest = verb:match("^(.-)( .*)$") if not first then error("Word '" .. verb .. "' mōt hæfþ rum in sīde tō notienne **") end local last_cons = first:match("([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])$") if not last_cons then error("Fyrest word '" .. first .. "' mōt ende wiþ samod swēgend tō notienne **") end return first .. last_cons .. ending .. rest end local function check_non_nil_star_form(form, pagename) if form == nil then error("Word '" .. pagename .. "' mōt hæfþ rum in sīde tō notienne *, **, *l, *! or *'") end return form end local function sub_tilde(form, pagename) if not form then return nil end if form:find("~") then form = form:gsub("~", replacement_escape(pagename)) end return form end local deprecated_qual_replaced_by_inline_modifier = { list = true, allow_holes = true, replaced_by = false, instead = "use an inline modifier <q:...> or <l:...> on the value" } pos_functions["verbs"] = { params = { [1] = {list = "pres_3sg", disallow_holes = true}, ["pres_3sg\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [2] = {list = "pres_ptc", disallow_holes = true}, ["pres_ptc\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [3] = {list = "past", disallow_holes = true}, ["past\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [4] = {list = "past_ptc", allow_holes = true}, ["past_ptc\1_qual"] = deprecated_qual_replaced_by_inline_modifier, ["noautolinkverb"] = boolean_param, ["angle_bracket"] = boolean_param, }, func = function(args, data) -- Get parameters local par1s local par2s = parse_inflection(args, {2, "pres_ptc"}) local par3s = parse_inflection(args, {3, "past"}) local par4s = parse_inflection(args, {4, "past_ptc"}) local pres_3sgs, pres_ptcs, pasts, past_ptcs local pagename = data.pagename ------------------------------------------- UTILITY FUNCTIONS #2 ------------------------------------------ -- These functions are used in both in the separate-parameter format and in the override params such as past_ptc2=. local full_default_s, full_default_ing, full_default_ed, split_default_s, split_default_ing, split_default_ed local lemma local function set_lemma_and_default_forms(the_lemma) lemma = the_lemma full_default_s, full_default_ing, full_default_ed, split_default_s, split_default_ing, split_default_ed, lemma_first, lemma_rest = default_verb_forms(the_lemma) end local function canonicalize_s_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_s elseif form == "*" then return check_non_nil_star_form(split_default_s, lemma) elseif form == "++" then return compute_plusplus_s_form(lemma, full_default_s) elseif form == "**" then if lemma:find("^[^ ]*[szx] ") then return compute_double_last_cons_stem_of_split_verb(lemma, "es") else return check_non_nil_star_form(split_default_s, lemma) end elseif form == "+!" then return lemma .. "s" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "s" .. lemma_rest elseif form == "+'" then return lemma .. "'s" elseif form == "*'" then return check_non_nil_star_form(lemma_first) .. "'s" .. lemma_rest elseif form == "+l" then if lemma:find("[szx]$") then return {{term = full_default_s, l = {"US"}}, {term = compute_plusplus_s_form(lemma, full_default_s), l = {"UK"}}} else return compute_plusplus_s_form(lemma, full_default_s) end elseif form == "*l" then if lemma:find("^[^ ]*[szx] ") then return {{term = check_non_nil_star_form(split_default_s, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "es"), l = {"UK"}}} else return check_non_nil_star_form(split_default_s, lemma) end else return sub_tilde(form, lemma) end end local function canonicalize_ing_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_ing elseif form == "*" then return check_non_nil_star_form(split_default_ing, lemma) elseif form == "++" then return compute_double_last_cons_stem(lemma) .. "ing" elseif form == "**" then return compute_double_last_cons_stem_of_split_verb(lemma, "ing") elseif form == "+!" then return lemma .. "ing" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "ing" .. lemma_rest elseif form == "+'" then return lemma .. "'ing" elseif form == "*'" then return check_non_nil_star_form(lemma_first) .. "'ing" .. lemma_rest elseif form == "+l" then return {{term = full_default_ing, l = {"US"}}, {term = compute_double_last_cons_stem(lemma) .. "ing", l = {"UK"}}} elseif form == "*l" then return {{term = check_non_nil_star_form(split_default_ing, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "ing"), l = {"UK"}}} else return sub_tilde(form, lemma) end end local function canonicalize_ed_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_ed elseif form == "*" then return check_non_nil_star_form(split_default_ed, lemma) elseif form == "++" then return compute_double_last_cons_stem(lemma) .. "ed" elseif form == "+!" then return lemma .. "ed" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "ed" .. lemma_rest elseif form == "+'" then return {{term = lemma .. "'d"}, {term = lemma .. "'ed"}} elseif form == "*'" then return {{term = check_non_nil_star_form(lemma_first) .. "'d" .. lemma_rest}, {term = check_non_nil_star_form(lemma_first) .. "'ed" .. lemma_rest}} elseif form == "**" then return compute_double_last_cons_stem_of_split_verb(lemma, "ed") elseif form == "+l" then return {{term = full_default_ed, l = {"US"}}, {term = compute_double_last_cons_stem(lemma) .. "ed", l = {"UK"}}} elseif form == "*l" then return {{term = check_non_nil_star_form(split_default_ed, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "ed"), l = {"UK"}}} else return sub_tilde(form, lemma) end end -- FIXME: options should be "+", "*", "++", "**", "+n", "*n", "++n" and "**n", but not "n" local function canonicalize_en_form(form) if form == "n" then track("n4") return add_suffix(lemma, "n") end return canonicalize_ed_form(form) end --------------------------------- MAIN PARSING/CONJUGATING CODE -------------------------------- local is_angle_bracket = args.angle_bracket if is_angle_bracket then if par2s[1] or par3s[1] or par4s[1] then error("Can't specify explicit values for 2=, 3= or 4= along wiþ the angle-bracket format") end elseif is_angle_bracket == nil and not par2s[1] and not par3s[1] and not par4s[1] and not args[1][2] and args[1][1] and args[1][1]:find("<") then if put.term_contains_top_level_html(args[1][1]) then -- Often, term_contains_top_level_html() returns true on the angle-bracket format, which would -- make the pcall() below succeed but leave the angle brackets as-is. Check for this and only do the -- pcall() if term_contains_top_level_html() returns false. is_angle_bracket = true else -- If it's ambiguous whether it's an angle-bracket format or separate params with an inline modifier, -- try to parse as the latter. If an error occurs, treat as the former. local ok ok, par1s = pcall(parse_inflection, args, {1, "pres_3sg"}) if not ok then par1s = nil is_angle_bracket = true end end end if is_angle_bracket then -------------------------- ANGLE-BRACKET FORMAT -------------------------- -- (0) Expand multiword term with angle brackets just on the first word. local arg11 = args[1][1] if arg11:find("^<.*>$") and pagename:find(" ") then local first, rest = pagename:match("^(.-)( .*)$") arg11 = first .. arg11 .. rest end -- (1) Parse the indicator specs inside of angle brackets. local function parse_indicator_spec(angle_bracket_spec) local inside = angle_bracket_spec:match("^<(.*)>$") assert(inside) local segments = put.parse_balanced_segment_run(inside, "[", "]") local comma_separated_groups = put.split_alternating_runs(segments, ",") if #comma_separated_groups > 4 then error("Too many comma-separated parts in indicator spec, expected at most 4: " .. angle_bracket_spec) end local function fetch_footnotes(separated_group) local footnotes for j = 2, #separated_group - 1, 2 do if separated_group[j + 1] ~= "" then error("Extraneous text after bracketed footnotes: '" .. concat(separated_group) .. "'") end if not footnotes then footnotes = {} end insert(footnotes, separated_group[j]) end return footnotes end local function fetch_specs(comma_separated_group) if not comma_separated_group then return {{term = "+"}} end local specs = {} local colon_separated_groups = put.split_alternating_runs(comma_separated_group, ":") for _, colon_separated_group in ipairs(colon_separated_groups) do local form = colon_separated_group[1] if form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then error("*, **, *l, *! and *' not allowed inside of indicator specs: " .. angle_bracket_spec) end if form == "" then form = "+" end local termobj = { term = form } local footnotes = fetch_footnotes(colon_separated_group) if footnotes then for _, footnote in ipairs(footnotes) do m_headword_utilities.add_footnote_to_termobj(termobj, footnote) end end insert(specs, termobj) end return specs end local s_specs = fetch_specs(comma_separated_groups[1]) local ing_specs = fetch_specs(comma_separated_groups[2]) local ed_specs = fetch_specs(comma_separated_groups[3]) local en_specs = fetch_specs(comma_separated_groups[4]) return { forms = {}, s_specs = s_specs, ing_specs = ing_specs, ed_specs = ed_specs, en_specs = en_specs, } end local parse_props = { parse_indicator_spec = parse_indicator_spec, } local alternant_multiword_spec = iut.parse_inflected_text(arg11, parse_props) -- (2) Check for user-specified brackets; remove any links from the lemma, but remember the original -- form so we can use it below in the 'lemma_linked' form. -- Check to see if there are brackets in the pre-text or post-text. If so, use the linked lemma (with the -- verb autolinked unless noautolinkverb is given). Otherwise, use the default headword algorithm. local function check_bracket(val) if val:find("%[%[") then alternant_multiword_spec.saw_bracket = true end end for _, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do check_bracket(alternant_or_word_spec.before_text) if alternant_or_word_spec.alternants then for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do for _, word_spec in ipairs(multiword_spec.word_specs) do check_bracket(word_spec.before_text) end check_bracket(multiword_spec.post_text) end end end check_bracket(alternant_multiword_spec.post_text) iut.map_word_specs(alternant_multiword_spec, function(base) if base.lemma == "" then base.lemma = pagename end base.orig_lemma = base.lemma base.lemma = remove_links(base.lemma) if args.noautolinkverb or base.orig_lemma:find("%[%[") then base.linked_lemma = base.orig_lemma else base.linked_lemma = "[[" .. base.orig_lemma .. "]]" end end) -- (3) Conjugate the verbs according to the indicator specs parsed above. local all_verb_slots = { lemma = "infinitive", lemma_linked = "infinitive", s_form = "3|s|pres", ing_form = "pres|ptcp", ed_form = "past", en_form = "past|ptcp", } local function conjugate_verb(base) local function process_specs(slot, specs, canon_func, default_values, default_already_formobj) local function insert_termobj_into_slot(termobj) local formobj = m_headword_utilities.convert_termobj_to_formobj(termobj) -- If the form is -, don't insert any forms, which will result in there being no overall forms -- (in fact it will be nil). We check for that down below and substitute a single "-" as the -- form, which in turn gets turned into special labels like "no present participle". if formobj.form == "-" then if formobj.footnotes then error("Unable to preserve footnotes specified on missing form '-': FIXME: " .. dump(formobj.footnotes)) end else iut.insert_form(base.forms, slot, formobj) end end local function canonicalize_and_insert(arg) local canon_arg = canon_func(arg) if type(canon_arg) == "string" then arg.term = canon_arg insert_termobj_into_slot(arg) else for _, canon in ipairs(canon_arg) do m_headword_utilities.combine_termobj_decorations(canon, arg) insert_termobj_into_slot(canon) end end end for _, arg in ipairs(specs) do if arg.term == "+" then if default_values then -- will be nil if past tense specified as - and no past ptc given for _, val in ipairs(default_values) do val = shallowCopy(val) if default_already_formobj then local argformobj = m_headword_utilities.convert_termobj_to_formobj(arg) val.footnotes = iut.combine_footnotes(val.footnotes, argformobj.footnotes) iut.insert_form(base.forms, slot, val) else m_headword_utilities.combine_termobj_decorations(val, arg) canonicalize_and_insert(val) end end end else canonicalize_and_insert(arg) end end end set_lemma_and_default_forms(base.lemma) local all_part_default_specs = {} local function process_and_canonicalize_s_form(arg) local form = arg.term if form == "+" then error("Internal error: '+' should have been converted to '^' by now") end if form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then error(("Internal error: '%s' should have already thrown an error"):format(form)) end if form == "^" or form == "++" or form == "+l" or form == "+!" or form == "+'" then insert(all_part_default_specs, shallowCopy(arg)) end return canonicalize_s_form(form) end process_specs("s_form", base.s_specs, process_and_canonicalize_s_form, {{term = "^"}}) if not all_part_default_specs[1] then all_part_default_specs[1] = {term = "^"} end process_specs("ing_form", base.ing_specs, function(arg) return canonicalize_ing_form(arg.term) end, all_part_default_specs) process_specs("ed_form", base.ed_specs, function(arg) return canonicalize_ed_form(arg.term) end, all_part_default_specs) process_specs("en_form", base.en_specs, function(arg) return canonicalize_en_form(arg.term) end, base.forms.ed_form, "default already formobj") iut.insert_form(base.forms, "lemma", {form = base.lemma}) -- Add linked version of lemma for use in head=. We write this in a general fashion in case -- there are multiple lemma forms (which isn't possible currently at this level, although it's -- possible overall using the ((...,...)) notation). iut.insert_forms(base.forms, "lemma_linked", iut.map_forms(base.forms.lemma, function(form) if form == base.lemma and base.linked_lemma:find("%[%[") then return base.linked_lemma else return form end end)) end local inflect_props = { slot_table = all_verb_slots, inflect_word_spec = conjugate_verb, } iut.inflect_multiword_or_alternant_multiword_spec(alternant_multiword_spec, inflect_props) -- (4) Fetch the forms and put the conjugated lemmas in data.heads if not explicitly given. local function fetch_termobjs(slot) local forms = alternant_multiword_spec.forms[slot] -- See above. This should only occur if the user explicitly used - for a spec. if not forms or not forms[1] then return {{term = "-"}} end local termobjs = {} for _, formobj in ipairs(forms) do insert(termobjs, m_headword_utilities.convert_formobj_to_termobj(formobj)) end return termobjs end pres_3sgs = fetch_termobjs("s_form") pres_ptcs = fetch_termobjs("ing_form") pasts = fetch_termobjs("ed_form") past_ptcs = fetch_termobjs("en_form") -- Use the "linked" form of the lemma as the head if no head= explicitly given and the user specified -- brackets in one of the lemmas. Otherwise we use the default headword-linking algorithm. if not data.user_specified_heads[1] and alternant_multiword_spec.saw_bracket then data.heads = {} for _, lemma_obj in ipairs(alternant_multiword_spec.forms.lemma_linked) do insert(data.heads, m_headword_utilities.convert_formobj_to_termobj(lemma_obj)) end end else -------------------------- SEPARATE-PARAM FORMAT -------------------------- set_lemma_and_default_forms(pagename) par1s = par1s or parse_inflection(args, {1, "pres_3sg"}) pres_3sgs = {} pres_ptcs = {} pasts = {} past_ptcs = {} if not par1s[1] then par1s = {{term = "+"}} end if not par2s[1] then par2s = {{term = "+"}} end if not par3s[1] then par3s = {{term = "+"}} end if not par4s[1] then par4s = {{term = "+"}} end local function process_argument(args, dest, canon_func, default_values, default_already_canonicalized) local function canonicalize_and_insert(arg) local canon_arg = canon_func(arg) if type(canon_arg) == "string" then arg.term = canon_arg m_headword_utilities.insert_termobj_combining_duplicates(dest, arg) else for _, canon in ipairs(canon_arg) do m_headword_utilities.combine_termobj_decorations(canon, arg) m_headword_utilities.insert_termobj_combining_duplicates(dest, canon) end end end for _, arg in ipairs(args) do if arg.term == "+" then for _, val in ipairs(default_values) do val = shallowCopy(val) m_headword_utilities.combine_termobj_decorations(val, arg) if default_already_canonicalized then m_headword_utilities.insert_termobj_combining_duplicates(dest, val) else canonicalize_and_insert(val) end end else canonicalize_and_insert(arg) end end end local all_part_default_specs = {} local function process_and_canonicalize_s_form(arg) local form = arg.term if form == "+" then error("Internal error: '+' should have been converted to '^' by now") end if form == "^" or form == "++" or form == "+l" or form == "+!" or form == "+'" or form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then insert(all_part_default_specs, shallowCopy(arg)) end return canonicalize_s_form(form) end process_argument(par1s, pres_3sgs, process_and_canonicalize_s_form, {{term = "^"}}) if not all_part_default_specs[1] then all_part_default_specs[1] = {term = "^"} end process_argument(par2s, pres_ptcs, function(arg) return canonicalize_ing_form(arg.term) end, all_part_default_specs) process_argument(par3s, pasts, function(arg) return canonicalize_ed_form(arg.term) end, all_part_default_specs) process_argument(par4s, past_ptcs, function(arg) return canonicalize_en_form(arg.term) end, pasts, "default already canonicalized") end ------------------------------------------- INSERT INFLECTIONS ------------------------------------------ insert_inflection(data, pres_3sgs, "third-person singular simple present", "s-verb-form") insert_inflection(data, pres_ptcs, "present participle", "ing-form") if deepEquals(pasts, past_ptcs) then insert_inflection(data, pasts, "simple past and past participle", "ed-form", "no simple past or past participle") else insert_inflection(data, pasts, "simple past", "spast") insert_inflection(data, past_ptcs, "past participle", "past|part") end if pagename:find(" ") then -- Check for placeholder "it" local words = split(pagename, " ") for _, word in ipairs(words) do if word == "it" or word == "its" or word == "it's" then insert(data.categories, langname .. ' terms wiþ placeholder "it"') break end end -- Check for phrasal verbs local phrasal_adverbs = list_to_set{ -- NOTE: This should only contain common phrasal adverbs, not random words like [[low]], -- [[adrift]], etc. "aback", "about", "above", "across", "after", "against", "ahead", "along", "apart", "around", "as", "aside", "at", "away", "back", "before", "behind", "below", "between", "beyond", "by", "down", "for", "forth", "from", "in", "into", "of", "off", "on", "onto", "out", "over", "past", "round", "through", "to", "together", "towards", "under", "up", "upon", "with", "without", } local allowed_non_adverb_words = list_to_set{ "it", "one", "oneself", "someone", } local base = pagename local seen_adverbs = {} -- Only consider a verb to be phrasal if it consists of a single base verb followed exclusively by either -- adverbs from `phrasal_adverbs` or placeholder words from `allowed_non_adverb_words`, where at -- least one following word is from `phrasal_adverbs` (hence [[can it]] is not a phrasal verb). while true do local prev, word = base:match("^(.+) (.-)$") if not prev then break end if phrasal_adverbs[word] then insert(seen_adverbs, word) elseif allowed_non_adverb_words[word] then -- do nothing else break end base = prev end if not base:find(" ") and seen_adverbs[1] then insert(data.categories, langname .. " phrasal verbs") for i = #seen_adverbs, 1, -1 do insert(data.categories, langname .. ' phrasal verbs formed wiþ "' .. seen_adverbs[i] .. '"') end end end end, } ----------------------------------------------------------------------------------------- -- Suffix forms -- ----------------------------------------------------------------------------------------- pos_functions["suffix forms"] = { params = { [1] = {required = true, list = true, disallow_holes = true}, }, func = function(args, data, is_suffix) local suffix_type = {} for _, typ in ipairs(args[1]) do insert(suffix_type, typ .. "-forming suffix") end insert(data.inflections, {label = "non-lemma form of " .. m_table.serialCommaJoin(suffix_type, {conj = "or"})}) end, } return export k7b60n7hioqcf71urtf8o7t1b1oekdz 55018 55006 2026-09-30T19:05:03Z Deadend0914 7211 55018 Scribunto text/plain local export = {} local pos_functions = {} --[==[ Author from 2020 on: mostly Benwing2, with significant contributions from Theknightwho. Based on a prior version by Rua (by now mostly rewritten), with contributions from Erutuon and others (see history for full attribution). ]==] local force_cat = false -- for testing; if true, categories appear in non-mainspace pages local require = require local require_when_needed = require("Module:require when needed") local en_utilities_module = "Module:en-utilities" local headword_utilities_module = "Module:headword utilities" local headword_module = "Module:headword" local inflection_utilities_module = "Module:inflection utilities" local parse_utilities_module = "Module:parse utilities" local JSON_module = "Module:JSON" local labels_module = "Module:labels" local links_module = "Module:links" local parameters_module = "Module:parameters" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local utilities_module = "Module:utilities" local yesno_module = "Module:yesno" local iut = require_when_needed(inflection_utilities_module) local put = require_when_needed(parse_utilities_module) local m_headword_utilities = require_when_needed(headword_utilities_module) local add_links_to_multiword_term = require_when_needed(headword_utilities_module, "add_links_to_multiword_term") local add_suffix = require_when_needed(en_utilities_module, "add_suffix") local apply_link_modifiers = require_when_needed(headword_utilities_module, "apply_link_modifiers") local concat = table.concat local deepEquals = require_when_needed(table_module, "deepEquals") local dump = mw.dumpObject local format_categories = require_when_needed(utilities_module, "format_categories") local full_headword = require_when_needed(headword_module, "full_headword") local get_label_info = require_when_needed(labels_module, "get_label_info") local get_link_page = require_when_needed(links_module, "get_link_page") local glossary_link = require_when_needed(headword_utilities_module, "glossary_link") local insert = table.insert local insertIfNot = require_when_needed(table_module, "insertIfNot") local ipairs = ipairs local is_regular_plural = require_when_needed(en_utilities_module, "is_regular_plural") local list_to_set = require_when_needed(table_module, "listToSet") local pairs = pairs local process_params = require_when_needed(parameters_module, "process") local remove = table.remove local remove_links = require_when_needed(links_module, "remove_links") local replacement_escape = require_when_needed(string_utilities_module, "replacement_escape") local shallowCopy = require_when_needed(table_module, "shallowCopy") local singularize = require_when_needed(en_utilities_module, "singularize") local split = require_when_needed(string_utilities_module, "split") local toJSON = require_when_needed(JSON_module, "toJSON") local toNFD = mw.ustring.toNFD local type = type local ulen = require_when_needed(string_utilities_module, "len") local ulower = require_when_needed(string_utilities_module, "lower") local umatch = require_when_needed(string_utilities_module, "match") local u = require_when_needed(string_utilities_module, "char") local ugsub = require_when_needed(string_utilities_module, "gsub") local lang = require("Module:languages").getByCode("en") local langname = lang:getCanonicalName() local list_param = {list = true, disallow_holes = true} local list_allow_holes = {list = true, allow_holes = true} local boolean_param = {type = "boolean"} local function ine(val) if val == "" then return nil else return val end end local function track(page) require("Module:debug/track")("en-headword/" .. page) return true end ------------------------------------------- UTILITY FUNCTIONS ------------------------------------------ -- Parse and return an inflection not requiring additional processing. The raw arguments come from `args[field]`, which -- is parsed for inline modifiers. local function parse_inflection(args, field, is_head) local argfield = field if type(argfield) == "table" then argfield = argfield[1] end return m_headword_utilities.parse_term_list_with_modifiers { paramname = field, forms = args[argfield], splitchar = ",", is_head = is_head, } end -- Insert the parsed inflections in `terms` (as parsed by `parse_inflection`) into `data.inflections`, with label -- `label` and optional accelerator spec `accel`. local function insert_inflection(data, terms, label, accel, no_label) for _, termobj in ipairs(terms) do m_headword_utilities.remove_termobj_field_modifiers(termobj) end m_headword_utilities.insert_inflection { headdata = data, terms = terms, label = label, no_label = no_label, accel = accel and {form = accel} or nil, } end -- Insert a fixed label `label` into the inflections for `data`. If `originating_term` is supplied, copy the decorations -- from it into the fixed label. local function insert_fixed_inflection(data, label, originating_term) m_headword_utilities.insert_fixed_inflection { headdata = data, originating_term = originating_term, label = label, } 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 -- These functions are used directly in the <> format as well as in the utility functions #2 below. local function compute_double_last_cons_stem(term) local last_cons = term:match("([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])$") if not last_cons then error("Verb stem '" .. term .. "' must end in a consonant to use ++") end return term .. last_cons end local function compute_plusplus_s_form(term, default_s_form) if term:find("[szx]$") then -- regas -> regasses, derez -> derezzes return compute_double_last_cons_stem(term) .. "es" else return default_s_form end end -- The main entry point. -- This is the only function that can be invoked from a template. function export.show(frame) local iparams = { [1] = true, } local iargs = require("Module:parameters").process(frame.args, iparams) local parargs = frame:getParent().args local poscat = iargs[1] local pos_in_1 = not poscat if pos_in_1 then poscat = ine(parargs[1]) or mw.title.getCurrentTitle().fullText == "Template:en-head" and "interjection" or error("Part of speech must be specified in 1=") poscat = require(headword_module).canonicalize_pos(poscat) end local indexing_poscat = pos_in_1 and "head" or poscat local params = { ["head"] = list_param, ["id"] = true, ["json"] = boolean_param, ["sort"] = true, ["splithyph"] = boolean_param, ["nosplithyph"] = boolean_param, ["hyphspace"] = boolean_param, ["nolink"] = boolean_param, ["nolinkhead"] = {type = "boolean_param", alias_of = "nolink"}, ["suffix"] = boolean_param, ["nosuffix"] = boolean_param, ["nomultiwordcat"] = boolean_param, ["abbr"] = list_param, ["the"] = true, ["def"] = {alias_of = "the"}, ["pagename"] = true, -- for testing } if pos_in_1 then params[1] = {required = true} -- required but ignored as already processed above end local pos_data = pos_functions[indexing_poscat] local pos_func if pos_data then local pos_params = pos_data.params if pos_params then for key, val in pairs(pos_params) do params[key] = val end end pos_func = pos_data.func end local args = process_params(parargs, params) -- Account for unsupported titles, e.g. 'C|N>K' instead of 'Unsupported titles/C through N to K'. local pagename = args.pagename or mw.loadData("Module:headword/data").pagename local user_specified_heads = parse_inflection(args, "head", "is_head") local heads = user_specified_heads local autohead if args.nolink or not pagename:find("[ '%-]") then autohead = pagename else local en_no_split_apostrophe_words = list_to_set { "one's", "someone's", "he's", "she's", "it's", } local en_include_hyphen_prefixes = list_to_set { -- We don't include things that are also words even though they are often (perhaps mostly) prefixes, e.g. -- "be", "counter", "cross", "extra", "half", "mid", "over", "pan", "under". "acro", "acousto", "Afro", "agro", "anarcho", "angio", "Anglo", "ante", "anti", "arch", "auto", "bi", "bio", "cis", "co", "cryo", "crypto", "de", "demi", "eco", "electro", "Euro", "ex", "Greco", "hemi", "hydro", "hyper", "hypo", "infra", "Indo", "inter", "intra", "Judeo", "macro", "meta", "micro", "mini", "multi", "neo", "neuro", "non", "para", "peri", "post", "pre", "pro", "proto", "pseudo", "re", "semi", "sub", "super", "trans", "un", "vice", } local function is_english(term) local title = mw.title.new(term) if title and title.exists then local content = title:getContent() if content and content:find("==English==\n") then return true end end return false end local function en_split_hyphen_when_space(word) if not word:find("-", nil, true) then return nil end if args.hyphspace then return "[[" .. word:gsub("%-+", " ") .. "|" .. word .. "]]" end if args.nosplithyph then return "[[" .. word .. "]]" end if not args.splithyph then local space_word = word:gsub("%-+", " ") if is_english(space_word) then return "[[" .. space_word .. "|" .. word .. "]]" end if is_english(word) then return "[[" .. word .. "]]" end end return nil end local function en_split_apostrophe(word) local base = word:match("^(.*)'s$") if base then return "[[" .. base .. "]][[-'s|'s]]" end -- Only treat final apostrophe as possessive if preceded by something that looks like a plural ending in /z/. -- In particular we don't want to do it for words like [[truckin']]. base = word:match("^(.*[sxz])'$") if base then if base:find("s$") then local sg = singularize(base) if is_english(sg) then return "[[" .. sg .. "|" .. base .. "]][[-'|']]" end end return "[[" .. base .. "]][[-'|']]" end return "[[" .. word .. "]]" end autohead = add_links_to_multiword_term(pagename, { split_hyphen_when_space = en_split_hyphen_when_space, split_apostrophe = en_split_apostrophe, no_split_apostrophe_words = en_no_split_apostrophe_words, include_hyphen_prefixes = en_include_hyphen_prefixes, }) end if not heads[1] then heads = {{term = autohead}} else for _, headobj in ipairs(heads) do local head = headobj.term if head:find("^~") then head = apply_link_modifiers(autohead, head:sub(2), lang) headobj.term = head elseif head:find("^[!?]$") then -- If explicit head= just consists of ! or ?, add it to the end of the default head. headobj.term = autohead .. head end if head == autohead then track("redundant-head") end end end -- handle the=/def= if args.the == "~" then local newheads = {} for _, headobj in ipairs(heads) do local barehead = shallowCopy(headobj) insert(newheads, barehead) headobj.term = "the " .. headobj.term insert(newheads, headobj) end heads = newheads elseif args.the then local the = require(yesno_module)(args.the) if the then for _, headobj in ipairs(heads) do headobj.term = "the " .. headobj.term end end end local data = { lang = lang, pos_category = poscat, categories = {}, heads = heads, user_specified_heads = user_specified_heads, -- We use our own splitting algorithm so the redundant head cat will be inaccurate. no_redundant_head_cat = true, inflections = {}, nomultiwordcat = args.nomultiwordcat, sort_key = args.sort, pagename = pagename, id = args.id, force_cat_output = force_cat, } local function inscat(cat) insert(data.categories, langname .. " " .. cat) end local is_suffix = false if args.suffix or not args.nosuffix and pagename:find("^%-") and not pagename:find("^%-%-") and poscat ~= "suffix forms" then is_suffix = true data.pos_category = "suffixes" local singular_poscat = singularize(poscat) inscat(singular_poscat .. "-forming suffixes") insert(data.inflections, {label = singular_poscat .. "-forming suffix"}) end if pos_func then pos_func(args, data, is_suffix) end local extra_categories = {} if pagename:find("[Qq]") then -- Check for q not followed by u. We want to exclude things like [[13q deletion syndrome]] and [[BFOQ]] that -- don't have a lowercase letter on either side, as well as things like [[& seq.]] and [[acq.]] that are -- abbreviations for words containing a following u. -- -- Approximate range of combining diacritics; we want to remove them so the checks below for -- a lowercase letter next to the q aren't tripped up by diacritics on the letter. local u300 = u(0x0300) local u36F = u(0x036F) local pagename_no_diacritics = ugsub(toNFD(pagename), "[" .. u300 .. "-" .. u36F .. "]", "") if pagename_no_diacritics:find("[Qq][a-tv-z]") or pagename_no_diacritics:find("[a-z]q[^u.]") or pagename_no_diacritics:find("[a-z]q$") then inscat("words containing Q not followed by U") end end -- toNFD performs decomposition, so letters that decompose to an ASCII -- vowel and a diacritic, such as é, are counted as vowels and do not do not -- need to be included in the pattern. if not umatch(ulower(toNFD(pagename)), "[aeiouyæœøəªºαεηιουω]") then inscat("words spelled wiþūtan vowels") end if pagename:find("yre$") then inscat('words ending in "-yre"') end if not pagename:find(" ") and ulen(pagename) >= 25 then insert(extra_categories, "Long " .. langname .. " words") end if pagename:find("^[^aeiou ]*a[^aeiou ]*e[^aeiou ]*i[^aeiou ]*o[^aeiou ]*u[^aeiou ]*$") then inscat("words that use all vowels in alphabetical order") end parse_and_insert_inflection(data, args, "abbr", "abbreviation") if args.json then return toJSON(data) end return full_headword(data) .. (extra_categories[1] and format_categories(extra_categories, lang, args.sort) or "") end local function make_default_comparative(word) if word == "good" or word == "well" then return {"better"} elseif word == "bad" or word == "badly" then return {"worse"} elseif word == "far" then return {"further", "farther"} else return {add_suffix(word, "r")} end end local function make_default_superlative(word) if word == "good" or word == "well" then return {"best"} elseif word == "bad" or word == "badly" then return {"worst"} elseif word == "far" then return {"furthest", "farthest"} else return {add_suffix(word, "st.superlative")} end end -- This function does the common work between adjectives and adverbs. local function process_comparative_args(data, args, plpos) local pagename = data.pagename local comps = parse_inflection(args, 1) local sups = parse_inflection(args, "sup") local outcomps, outsups if args.componly then if comps[1] then error("Can't specify comparatives of comparative-only " .. plpos) end insert(data.inflections, {label = glossary_link("comparative") .. " form only"}) insert(data.categories, langname .. " comparative-only " .. plpos) -- Set to empty list so we don't get any comparatives output, but process superlatives if specified. outcomps = {} if not sups[1] then -- Set to empty list so we don't get any superlatives output unless explicitly given. outsups = {} end elseif args.suponly then if comps[1] or sups[1] then error("Can't specify comparatives or superlatives of or superlative-only " .. plpos) end insert(data.inflections, {label = glossary_link("superlative") .. " form only"}) insert(data.categories, langname .. " superlative-only " .. plpos) return end -- If the first parameter is ?, then don't show anything, just return. if comps[1] and comps[1].term == "?" then if comps[2] then error("Can't specify additional comparatives along wiþ '?'") end if sups[1] then error("Can't specify superlatives along wiþ '?' for the comparative") end return end if comps[1] and comps[1].term == "-" then local hyphencomp = remove(comps, 1) -- Remove the "-" but retain for decorations. -- Not (generally) comparable; may occasionally have a comparative if comps[1] then insert_fixed_inflection(data, "not generally <<comparable>>", hyphencomp) elseif not sups[1] then insert_fixed_inflection(data, "not <<comparable>>", hyphencomp) insert(data.categories, langname .. " uncomparable " .. plpos) return else -- No comparative, but a superlative. insert_inflection() will correctly generate 'no comparative' if we -- pass in "-" as the value. outcomps = {hyphencomp} end elseif not comps[1] then comps = {{term = "more"}} end if not outcomps then -- not if we set `outcomps` to "-" above or processed a comparative-only term outcomps = {} -- Go over each parameter given and create a comparative and superlative form. for _, compobj in ipairs(comps) do local comp = compobj.term if comp == "-" then error("Comparative of '-' only allowed as first comparative") end if comp == "+" then comp = "+more" elseif comp == "more" and pagename ~= "many" and pagename ~= "much" then comp = "+more" elseif comp == "further" and pagename ~= "far" then comp = "+further" elseif comp == "better" and pagename ~= "good" and pagename ~= "well" then comp = "+better" elseif comp:find("~") then comp = comp:gsub("~", replacement_escape(pagename)) end compobj.origterm = comp if comp == "+more" then comp = "more [[" .. pagename .. "]]" elseif comp == "+further" then comp = {"further [[" .. pagename .. "]]", "farther [[" .. pagename .. "]]"} elseif comp == "+better" then comp = "better [[" .. pagename .. "]]" elseif comp == "er" then -- Add -er. comp = add_suffix(pagename, "r") elseif comp == "ier" then if pagename:sub(-1) ~= "y" then error("Can't specify 'ier' comparative unless the term ends wiþ 'y': " .. pagename) end comp = pagename:gsub("e?y$", "ier") elseif comp:find("^%+") then local special = m_headword_utilities.get_special_indicator(comp, "noerror") if special then comp = m_headword_utilities.handle_multiword(pagename, special, make_default_comparative) end end if type(comp) == "table" and not comp[2] then comp = comp[1] end if type(comp) == "table" then for i = 1, #comp - 1 do local outobj = shallowCopy(compobj) outobj.term = comp[i] insert(outcomps, outobj) end compobj.term = comp[#comp] insert(outcomps, compobj) else compobj.term = comp insert(outcomps, compobj) end end end if sups[1] and sups[1].term == "-" then if sups[2] then error("Can't specify '-' as superlative followed by further values") end -- No superlative. insert_inflection() will correctly generate 'no superlative' if we pass in "-" as the value. outsups = sups else if not sups[1] then sups = {{term = "+"}} end end -- `outsups` will be set if we set `outsups` to "-" above or processed a comparative-only term without superlatives. if not outsups then outsups = {} local function process_sup(sup, special, supobj, compobj) if special then sup = m_headword_utilities.handle_multiword(pagename, special, make_default_superlative) elseif sup == "-" or sup == "+" then error(("Internal error: Superlative value of '%s' should have been handled earlier"):format(sup)) elseif sup == "+most" then sup = "most [[" .. pagename .. "]]" elseif sup == "+furthest" then sup = {"furthest [[" .. pagename .. "]]", "farthest [[" .. pagename .. "]]"} elseif sup == "+best" then sup = "best [[" .. pagename .. "]]" elseif sup == "est" then -- Add -est. sup = add_suffix(pagename, "st.superlative") elseif sup == "iest" then if pagename:sub(-1) ~= "y" then error("Can't specify 'iest' superlative unless the term ends wiþ 'y': " .. pagename) end sup = pagename:gsub("e?y$", "iest") end if type(sup) == "table" and not sup[2] then sup = sup[1] end if compobj then supobj = shallowCopy(supobj) supobj = m_headword_utilities.combine_termobj_decorations(supobj, compobj) end if type(sup) == "table" then for i = 1, #sup - 1 do local outobj = shallowCopy(supobj) outobj.term = sup[i] insert(outsups, outobj) end supobj.term = sup[#sup] insert(outsups, supobj) else supobj.term = sup insert(outsups, supobj) end end for _, supobj in ipairs(sups) do local sup = supobj.term if sup == "-" then error("Superlative of '-' only allowed as first superlative") end if sup == "+" then if not comps[1] then error("Superlative of '+' can't be specified when there are no comparatives") end for _, compobj in ipairs(comps) do local comp = compobj.origterm local special if comp == "+more" then sup = "+most" elseif comp == "+further" then sup = "+furthest" elseif comp == "+better" then sup = "+best" elseif comp == "er" then sup = "est" elseif comp == "ier" then sup = "iest" else if comp:find("^%+") then special = m_headword_utilities.get_special_indicator(comp, "noerror") end if not special then -- If the full comparative was given, then derive the superlative by replacing -er with -- -est. if comp:sub(-2) == "er" then sup = comp:sub(1, -3) .. "est" else error(("The superlative cannot be derived automatically from comparative '%s' because it doesn't end in -er"):format(comp)) end end end process_sup(sup, special, supobj, compobj) end else local special = m_headword_utilities.get_special_indicator(sup, "noerror") -- Do some work here rather than in process_sup() so we don't end up double-processing a term with a '~' -- in it or a term that happens to be 'most' or similar after substitution of ~ in the comparative. if not special then if sup == "most" and pagename ~= "many" and pagename ~= "much" then sup = "+most" elseif sup == "furthest" and pagename ~= "far" then sup = "+furthest" elseif sup == "best" and pagename ~= "good" and pagename ~= "well" then sup = "+best" elseif sup:find("~") then sup = sup:gsub("~", replacement_escape(pagename)) end end process_sup(sup, special, supobj) end end end insert_inflection(data, outcomps, "<<comparative>>", "comparative") insert_inflection(data, outsups, "<<superlative>>", "superlative") end pos_functions["adjectives"] = { params = { [1] = list_param, ["comp_qual"] = {list = "comp\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the comparative value", }, ["sup"] = list_param, ["sup_qual"] = {list = "sup\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the superlative value", }, ["componly"] = boolean_param, ["suponly"] = boolean_param, }, func = function(args, data) -- Process the comparatives and superlatives. process_comparative_args(data, args, "adjectives") end, } pos_functions["adverbs"] = { params = { [1] = list_param, ["comp_qual"] = {list = "comp\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the comparative value", }, ["sup"] = list_param, ["sup_qual"] = {list = "sup\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the superlative value", }, ["componly"] = boolean_param, ["suponly"] = boolean_param, }, func = function(args, data) -- Process the comparatives and superlatives. process_comparative_args(data, args, "adverbs") end, } local function escape(str) return (str:gsub("\\([:#])", "\\\\%1") :gsub("[:#]", "\\%0")) end local function canonicalize_plural(pl, pagename, pos) if pl == "+" then return escape(add_suffix(pagename, "s.plural", pos)) elseif pl == "++" then return escape(compute_plusplus_s_form(pagename, add_suffix(pagename, "s.plural", pos))) elseif pl == "*" then return escape(pagename) elseif pl == "ies" then if pagename:sub(-1) == "y" then return escape(pagename:gsub("e?y$", pl)) end error("Can't specify 'ies' plural unless the term ends wiþ 'y'.") elseif pl == "s" or pl == "es" or pl == "'s" then return escape(pagename .. pl) end end local function do_nouns(args, data, pos) local pagename = data.pagename pos = pos or "noun" local plurals = parse_inflection(args, 1) local function insert_plurale_tantum_inflections(is_plural_only, originating_label) if args.sg[1] then insert_fixed_inflection(data, "normally plural", originating_label) parse_and_insert_inflection(data, args, "anf.", "singular") elseif is_plural_only then insert_fixed_inflection(data, "plural only", originating_label) end if args.attr[1] then parse_and_insert_inflection(data, args, "attr", "attributive") end end local function first_pl_term() return plurals[1] and plurals[1].term or nil end if first_pl_term() == "p" then -- plurale tantum if plurals[2] then error("Wiþ plurale tantum nama, can't specify more than one plural") end data.genders = {"p"} -- this should auto-insert the correct 'pluralia tantum' category insert_plurale_tantum_inflections("plural only", plurals[1]) return end local function inscat(cat) insert(data.categories, langname .. " " .. cat) end local need_default_plural = pos == "noun" if first_pl_term() == "sp" then -- construed as singular or plural sp = remove(plurals, 1) -- Remove the "sp" but retain it for its decorations. inscat("naman construed as singular or plural") data.genders = {"s", "p"} -- this should auto-insert the correct 'pluralia tantum' category insert_plurale_tantum_inflections(nil, sp) need_default_plural = false elseif first_pl_term() == "-" then -- Untellendlīc noun; may occasionally have a plural local hyphpl = remove(plurals, 1) -- Remove the "-" but retain for decorations. inscat("untellendlīc naman") -- If plural forms were given explicitly, then show "oft" if plurals[1] then insert_fixed_inflection(data, "oft <<untellendlīc>>", hyphpl) else insert_fixed_inflection(data, "<<untellendlīc>>", hyphpl) end need_default_plural = false elseif first_pl_term() == "#" then -- oft tellendlīc (e.g., "grilled cheese") local hashpl = remove(plurals, 1) -- Remove the "#" but retain for decorations. insert_fixed_inflection(data, "oft <<tellendlīc>>", hashpl) inscat("untellendlīc naman") inscat("tellendlīc naman") -- If no plural was given, add a default one now if not plurals[1] then plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end elseif first_pl_term() == "~" then -- Mixed tellendlīc/untellendlīc noun, always has a plural local tildepl = remove(plurals, 1) -- Remove the "~" but retain for decorations. insert_fixed_inflection(data, "<<tellendlīc>> and <<untellendlīc>>", tildepl) inscat("untellendlīc naman") inscat("tellendlīc naman") -- If no plural was given, add a default one now if not plurals[1] then plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end end -- Plural is unknown if first_pl_term() == "?" then local questionpl = remove(plurals, 1) -- Remove the "?" but retain for decorations. -- Not desired; see [[Wiktionary:Tea_room/2021/August#"Plural unknown or uncertain"]] -- insert_fixed_inflection(data, "manigfeald unġecnāwen oþþe ungewiss", questionpl) inscat("naman wiþ unġecnāwen oþþe ungewiss manigfealde") if plurals[1] then error("Ne cann āmearcian sweotole manigfealde andlang wiþ '?' of unġecnāwen/ungewiss manigfeald") end return end -- Plural is not attested if first_pl_term() == "!" then local exclampl = remove(plurals, 1) -- Remove the "!" but retain for decorations. insert_fixed_inflection(data, "plural not attested", exclampl) inscat("naman wiþ unattested manigfealde") if plurals[1] then error("Ne cann āmearcian sweotole manigfealde andlang wiþ '!' of gecȳþed manigfeald") end return end -- If no plural was given, maybe add a default one, otherwise (when "-" was given or proper noun) return. if not plurals[1] then if not need_default_plural then inscat("untellendlīc naman") return end plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end -- There are plural forms to show, so show them. inscat("tellendlīc naman") local irregular, indeclinable for i, pl in ipairs(plurals) do local canon_pl = canonicalize_plural(pl.term, pagename, pos) if canon_pl then pl.term = canon_pl end local pl_term = get_link_page(pl.term, lang) if not (pagename:find(" ") or is_regular_plural(pl_term, pagename)) then irregular = true if pl_term == pagename then indeclinable = true end end end if irregular then inscat("naman wiþ fremde manigfealde") end if indeclinable then inscat("undeclīnigendlīc naman") end insert_inflection(data, plurals, "mnf.", "p") end -- Return the parameters to be used for nouns and proper nouns. Currently the same. local noun_params = { [1] = list_param, ["pl\1qual"] = {list = true, allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the plural", }, -- The following four only used for pluralia tantum (1=p) ["sg"] = list_param, ["attr"] = list_param, } pos_functions["nouns"] = { params = noun_params, func = do_nouns, } pos_functions["proper nouns"] = { params = noun_params, func = function(args, data) return do_nouns(args, data, "proper nouns") end, } local function base_default_verb_forms(verb) return escape(add_suffix(verb, "s.verb")), escape(add_suffix(verb, "ing")), escape(add_suffix(verb, "d")) end local function default_verb_forms(verb) local full_s_form, full_ing_form, full_ed_form = base_default_verb_forms(verb) if verb:find(" ") then local first, rest = verb:match("^(.-)( .*)$") local first_s_form, first_ing_form, first_ed_form = base_default_verb_forms(first) return full_s_form, full_ing_form, full_ed_form, first_s_form .. rest, first_ing_form .. rest, first_ed_form .. rest, first, rest else return full_s_form, full_ing_form, full_ed_form, nil, nil, nil, nil, nil end end local function compute_double_last_cons_stem_of_split_verb(verb, ending) local first, rest = verb:match("^(.-)( .*)$") if not first then error("Word '" .. verb .. "' mōt hæfþ rum in sīde tō notienne **") end local last_cons = first:match("([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])$") if not last_cons then error("Fyrest word '" .. first .. "' mōt ende wiþ samod swēgend tō notienne **") end return first .. last_cons .. ending .. rest end local function check_non_nil_star_form(form, pagename) if form == nil then error("Word '" .. pagename .. "' mōt hæfþ rum in sīde tō notienne *, **, *l, *! or *'") end return form end local function sub_tilde(form, pagename) if not form then return nil end if form:find("~") then form = form:gsub("~", replacement_escape(pagename)) end return form end local deprecated_qual_replaced_by_inline_modifier = { list = true, allow_holes = true, replaced_by = false, instead = "use an inline modifier <q:...> or <l:...> on the value" } pos_functions["verbs"] = { params = { [1] = {list = "pres_3sg", disallow_holes = true}, ["pres_3sg\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [2] = {list = "pres_ptc", disallow_holes = true}, ["pres_ptc\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [3] = {list = "past", disallow_holes = true}, ["past\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [4] = {list = "past_ptc", allow_holes = true}, ["past_ptc\1_qual"] = deprecated_qual_replaced_by_inline_modifier, ["noautolinkverb"] = boolean_param, ["angle_bracket"] = boolean_param, }, func = function(args, data) -- Get parameters local par1s local par2s = parse_inflection(args, {2, "pres_ptc"}) local par3s = parse_inflection(args, {3, "past"}) local par4s = parse_inflection(args, {4, "past_ptc"}) local pres_3sgs, pres_ptcs, pasts, past_ptcs local pagename = data.pagename ------------------------------------------- UTILITY FUNCTIONS #2 ------------------------------------------ -- These functions are used in both in the separate-parameter format and in the override params such as past_ptc2=. local full_default_s, full_default_ing, full_default_ed, split_default_s, split_default_ing, split_default_ed local lemma local function set_lemma_and_default_forms(the_lemma) lemma = the_lemma full_default_s, full_default_ing, full_default_ed, split_default_s, split_default_ing, split_default_ed, lemma_first, lemma_rest = default_verb_forms(the_lemma) end local function canonicalize_s_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_s elseif form == "*" then return check_non_nil_star_form(split_default_s, lemma) elseif form == "++" then return compute_plusplus_s_form(lemma, full_default_s) elseif form == "**" then if lemma:find("^[^ ]*[szx] ") then return compute_double_last_cons_stem_of_split_verb(lemma, "es") else return check_non_nil_star_form(split_default_s, lemma) end elseif form == "+!" then return lemma .. "s" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "s" .. lemma_rest elseif form == "+'" then return lemma .. "'s" elseif form == "*'" then return check_non_nil_star_form(lemma_first) .. "'s" .. lemma_rest elseif form == "+l" then if lemma:find("[szx]$") then return {{term = full_default_s, l = {"US"}}, {term = compute_plusplus_s_form(lemma, full_default_s), l = {"UK"}}} else return compute_plusplus_s_form(lemma, full_default_s) end elseif form == "*l" then if lemma:find("^[^ ]*[szx] ") then return {{term = check_non_nil_star_form(split_default_s, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "es"), l = {"UK"}}} else return check_non_nil_star_form(split_default_s, lemma) end else return sub_tilde(form, lemma) end end local function canonicalize_ing_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_ing elseif form == "*" then return check_non_nil_star_form(split_default_ing, lemma) elseif form == "++" then return compute_double_last_cons_stem(lemma) .. "ing" elseif form == "**" then return compute_double_last_cons_stem_of_split_verb(lemma, "ing") elseif form == "+!" then return lemma .. "ing" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "ing" .. lemma_rest elseif form == "+'" then return lemma .. "'ing" elseif form == "*'" then return check_non_nil_star_form(lemma_first) .. "'ing" .. lemma_rest elseif form == "+l" then return {{term = full_default_ing, l = {"US"}}, {term = compute_double_last_cons_stem(lemma) .. "ing", l = {"UK"}}} elseif form == "*l" then return {{term = check_non_nil_star_form(split_default_ing, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "ing"), l = {"UK"}}} else return sub_tilde(form, lemma) end end local function canonicalize_ed_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_ed elseif form == "*" then return check_non_nil_star_form(split_default_ed, lemma) elseif form == "++" then return compute_double_last_cons_stem(lemma) .. "ed" elseif form == "+!" then return lemma .. "ed" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "ed" .. lemma_rest elseif form == "+'" then return {{term = lemma .. "'d"}, {term = lemma .. "'ed"}} elseif form == "*'" then return {{term = check_non_nil_star_form(lemma_first) .. "'d" .. lemma_rest}, {term = check_non_nil_star_form(lemma_first) .. "'ed" .. lemma_rest}} elseif form == "**" then return compute_double_last_cons_stem_of_split_verb(lemma, "ed") elseif form == "+l" then return {{term = full_default_ed, l = {"US"}}, {term = compute_double_last_cons_stem(lemma) .. "ed", l = {"UK"}}} elseif form == "*l" then return {{term = check_non_nil_star_form(split_default_ed, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "ed"), l = {"UK"}}} else return sub_tilde(form, lemma) end end -- FIXME: options should be "+", "*", "++", "**", "+n", "*n", "++n" and "**n", but not "n" local function canonicalize_en_form(form) if form == "n" then track("n4") return add_suffix(lemma, "n") end return canonicalize_ed_form(form) end --------------------------------- MAIN PARSING/CONJUGATING CODE -------------------------------- local is_angle_bracket = args.angle_bracket if is_angle_bracket then if par2s[1] or par3s[1] or par4s[1] then error("Can't specify explicit values for 2=, 3= or 4= along wiþ the angle-bracket format") end elseif is_angle_bracket == nil and not par2s[1] and not par3s[1] and not par4s[1] and not args[1][2] and args[1][1] and args[1][1]:find("<") then if put.term_contains_top_level_html(args[1][1]) then -- Often, term_contains_top_level_html() returns true on the angle-bracket format, which would -- make the pcall() below succeed but leave the angle brackets as-is. Check for this and only do the -- pcall() if term_contains_top_level_html() returns false. is_angle_bracket = true else -- If it's ambiguous whether it's an angle-bracket format or separate params with an inline modifier, -- try to parse as the latter. If an error occurs, treat as the former. local ok ok, par1s = pcall(parse_inflection, args, {1, "pres_3sg"}) if not ok then par1s = nil is_angle_bracket = true end end end if is_angle_bracket then -------------------------- ANGLE-BRACKET FORMAT -------------------------- -- (0) Expand multiword term with angle brackets just on the first word. local arg11 = args[1][1] if arg11:find("^<.*>$") and pagename:find(" ") then local first, rest = pagename:match("^(.-)( .*)$") arg11 = first .. arg11 .. rest end -- (1) Parse the indicator specs inside of angle brackets. local function parse_indicator_spec(angle_bracket_spec) local inside = angle_bracket_spec:match("^<(.*)>$") assert(inside) local segments = put.parse_balanced_segment_run(inside, "[", "]") local comma_separated_groups = put.split_alternating_runs(segments, ",") if #comma_separated_groups > 4 then error("Too many comma-separated parts in indicator spec, expected at most 4: " .. angle_bracket_spec) end local function fetch_footnotes(separated_group) local footnotes for j = 2, #separated_group - 1, 2 do if separated_group[j + 1] ~= "" then error("Extraneous text after bracketed footnotes: '" .. concat(separated_group) .. "'") end if not footnotes then footnotes = {} end insert(footnotes, separated_group[j]) end return footnotes end local function fetch_specs(comma_separated_group) if not comma_separated_group then return {{term = "+"}} end local specs = {} local colon_separated_groups = put.split_alternating_runs(comma_separated_group, ":") for _, colon_separated_group in ipairs(colon_separated_groups) do local form = colon_separated_group[1] if form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then error("*, **, *l, *! and *' not allowed inside of indicator specs: " .. angle_bracket_spec) end if form == "" then form = "+" end local termobj = { term = form } local footnotes = fetch_footnotes(colon_separated_group) if footnotes then for _, footnote in ipairs(footnotes) do m_headword_utilities.add_footnote_to_termobj(termobj, footnote) end end insert(specs, termobj) end return specs end local s_specs = fetch_specs(comma_separated_groups[1]) local ing_specs = fetch_specs(comma_separated_groups[2]) local ed_specs = fetch_specs(comma_separated_groups[3]) local en_specs = fetch_specs(comma_separated_groups[4]) return { forms = {}, s_specs = s_specs, ing_specs = ing_specs, ed_specs = ed_specs, en_specs = en_specs, } end local parse_props = { parse_indicator_spec = parse_indicator_spec, } local alternant_multiword_spec = iut.parse_inflected_text(arg11, parse_props) -- (2) Check for user-specified brackets; remove any links from the lemma, but remember the original -- form so we can use it below in the 'lemma_linked' form. -- Check to see if there are brackets in the pre-text or post-text. If so, use the linked lemma (with the -- verb autolinked unless noautolinkverb is given). Otherwise, use the default headword algorithm. local function check_bracket(val) if val:find("%[%[") then alternant_multiword_spec.saw_bracket = true end end for _, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do check_bracket(alternant_or_word_spec.before_text) if alternant_or_word_spec.alternants then for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do for _, word_spec in ipairs(multiword_spec.word_specs) do check_bracket(word_spec.before_text) end check_bracket(multiword_spec.post_text) end end end check_bracket(alternant_multiword_spec.post_text) iut.map_word_specs(alternant_multiword_spec, function(base) if base.lemma == "" then base.lemma = pagename end base.orig_lemma = base.lemma base.lemma = remove_links(base.lemma) if args.noautolinkverb or base.orig_lemma:find("%[%[") then base.linked_lemma = base.orig_lemma else base.linked_lemma = "[[" .. base.orig_lemma .. "]]" end end) -- (3) Conjugate the verbs according to the indicator specs parsed above. local all_verb_slots = { lemma = "infinitive", lemma_linked = "infinitive", s_form = "3|s|pres", ing_form = "pres|ptcp", ed_form = "past", en_form = "past|ptcp", } local function conjugate_verb(base) local function process_specs(slot, specs, canon_func, default_values, default_already_formobj) local function insert_termobj_into_slot(termobj) local formobj = m_headword_utilities.convert_termobj_to_formobj(termobj) -- If the form is -, don't insert any forms, which will result in there being no overall forms -- (in fact it will be nil). We check for that down below and substitute a single "-" as the -- form, which in turn gets turned into special labels like "no present participle". if formobj.form == "-" then if formobj.footnotes then error("Unable to preserve footnotes specified on missing form '-': FIXME: " .. dump(formobj.footnotes)) end else iut.insert_form(base.forms, slot, formobj) end end local function canonicalize_and_insert(arg) local canon_arg = canon_func(arg) if type(canon_arg) == "string" then arg.term = canon_arg insert_termobj_into_slot(arg) else for _, canon in ipairs(canon_arg) do m_headword_utilities.combine_termobj_decorations(canon, arg) insert_termobj_into_slot(canon) end end end for _, arg in ipairs(specs) do if arg.term == "+" then if default_values then -- will be nil if past tense specified as - and no past ptc given for _, val in ipairs(default_values) do val = shallowCopy(val) if default_already_formobj then local argformobj = m_headword_utilities.convert_termobj_to_formobj(arg) val.footnotes = iut.combine_footnotes(val.footnotes, argformobj.footnotes) iut.insert_form(base.forms, slot, val) else m_headword_utilities.combine_termobj_decorations(val, arg) canonicalize_and_insert(val) end end end else canonicalize_and_insert(arg) end end end set_lemma_and_default_forms(base.lemma) local all_part_default_specs = {} local function process_and_canonicalize_s_form(arg) local form = arg.term if form == "+" then error("Internal error: '+' should have been converted to '^' by now") end if form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then error(("Internal error: '%s' should have already thrown an error"):format(form)) end if form == "^" or form == "++" or form == "+l" or form == "+!" or form == "+'" then insert(all_part_default_specs, shallowCopy(arg)) end return canonicalize_s_form(form) end process_specs("s_form", base.s_specs, process_and_canonicalize_s_form, {{term = "^"}}) if not all_part_default_specs[1] then all_part_default_specs[1] = {term = "^"} end process_specs("ing_form", base.ing_specs, function(arg) return canonicalize_ing_form(arg.term) end, all_part_default_specs) process_specs("ed_form", base.ed_specs, function(arg) return canonicalize_ed_form(arg.term) end, all_part_default_specs) process_specs("en_form", base.en_specs, function(arg) return canonicalize_en_form(arg.term) end, base.forms.ed_form, "default already formobj") iut.insert_form(base.forms, "lemma", {form = base.lemma}) -- Add linked version of lemma for use in head=. We write this in a general fashion in case -- there are multiple lemma forms (which isn't possible currently at this level, although it's -- possible overall using the ((...,...)) notation). iut.insert_forms(base.forms, "lemma_linked", iut.map_forms(base.forms.lemma, function(form) if form == base.lemma and base.linked_lemma:find("%[%[") then return base.linked_lemma else return form end end)) end local inflect_props = { slot_table = all_verb_slots, inflect_word_spec = conjugate_verb, } iut.inflect_multiword_or_alternant_multiword_spec(alternant_multiword_spec, inflect_props) -- (4) Fetch the forms and put the conjugated lemmas in data.heads if not explicitly given. local function fetch_termobjs(slot) local forms = alternant_multiword_spec.forms[slot] -- See above. This should only occur if the user explicitly used - for a spec. if not forms or not forms[1] then return {{term = "-"}} end local termobjs = {} for _, formobj in ipairs(forms) do insert(termobjs, m_headword_utilities.convert_formobj_to_termobj(formobj)) end return termobjs end pres_3sgs = fetch_termobjs("s_form") pres_ptcs = fetch_termobjs("ing_form") pasts = fetch_termobjs("ed_form") past_ptcs = fetch_termobjs("en_form") -- Use the "linked" form of the lemma as the head if no head= explicitly given and the user specified -- brackets in one of the lemmas. Otherwise we use the default headword-linking algorithm. if not data.user_specified_heads[1] and alternant_multiword_spec.saw_bracket then data.heads = {} for _, lemma_obj in ipairs(alternant_multiword_spec.forms.lemma_linked) do insert(data.heads, m_headword_utilities.convert_formobj_to_termobj(lemma_obj)) end end else -------------------------- SEPARATE-PARAM FORMAT -------------------------- set_lemma_and_default_forms(pagename) par1s = par1s or parse_inflection(args, {1, "pres_3sg"}) pres_3sgs = {} pres_ptcs = {} pasts = {} past_ptcs = {} if not par1s[1] then par1s = {{term = "+"}} end if not par2s[1] then par2s = {{term = "+"}} end if not par3s[1] then par3s = {{term = "+"}} end if not par4s[1] then par4s = {{term = "+"}} end local function process_argument(args, dest, canon_func, default_values, default_already_canonicalized) local function canonicalize_and_insert(arg) local canon_arg = canon_func(arg) if type(canon_arg) == "string" then arg.term = canon_arg m_headword_utilities.insert_termobj_combining_duplicates(dest, arg) else for _, canon in ipairs(canon_arg) do m_headword_utilities.combine_termobj_decorations(canon, arg) m_headword_utilities.insert_termobj_combining_duplicates(dest, canon) end end end for _, arg in ipairs(args) do if arg.term == "+" then for _, val in ipairs(default_values) do val = shallowCopy(val) m_headword_utilities.combine_termobj_decorations(val, arg) if default_already_canonicalized then m_headword_utilities.insert_termobj_combining_duplicates(dest, val) else canonicalize_and_insert(val) end end else canonicalize_and_insert(arg) end end end local all_part_default_specs = {} local function process_and_canonicalize_s_form(arg) local form = arg.term if form == "+" then error("Internal error: '+' should have been converted to '^' by now") end if form == "^" or form == "++" or form == "+l" or form == "+!" or form == "+'" or form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then insert(all_part_default_specs, shallowCopy(arg)) end return canonicalize_s_form(form) end process_argument(par1s, pres_3sgs, process_and_canonicalize_s_form, {{term = "^"}}) if not all_part_default_specs[1] then all_part_default_specs[1] = {term = "^"} end process_argument(par2s, pres_ptcs, function(arg) return canonicalize_ing_form(arg.term) end, all_part_default_specs) process_argument(par3s, pasts, function(arg) return canonicalize_ed_form(arg.term) end, all_part_default_specs) process_argument(par4s, past_ptcs, function(arg) return canonicalize_en_form(arg.term) end, pasts, "default already canonicalized") end ------------------------------------------- INSERT INFLECTIONS ------------------------------------------ insert_inflection(data, pres_3sgs, "third-person singular simple present", "s-verb-form") insert_inflection(data, pres_ptcs, "present participle", "ing-form") if deepEquals(pasts, past_ptcs) then insert_inflection(data, pasts, "simple past and past participle", "ed-form", "no simple past or past participle") else insert_inflection(data, pasts, "simple past", "spast") insert_inflection(data, past_ptcs, "past participle", "past|part") end if pagename:find(" ") then -- Check for placeholder "it" local words = split(pagename, " ") for _, word in ipairs(words) do if word == "it" or word == "its" or word == "it's" then insert(data.categories, langname .. ' terms wiþ placeholder "it"') break end end -- Check for phrasal verbs local phrasal_adverbs = list_to_set{ -- NOTE: This should only contain common phrasal adverbs, not random words like [[low]], -- [[adrift]], etc. "aback", "about", "above", "across", "after", "against", "ahead", "along", "apart", "around", "as", "aside", "at", "away", "back", "before", "behind", "below", "between", "beyond", "by", "down", "for", "forth", "from", "in", "into", "of", "off", "on", "onto", "out", "over", "past", "round", "through", "to", "together", "towards", "under", "up", "upon", "with", "without", } local allowed_non_adverb_words = list_to_set{ "it", "one", "oneself", "someone", } local base = pagename local seen_adverbs = {} -- Only consider a verb to be phrasal if it consists of a single base verb followed exclusively by either -- adverbs from `phrasal_adverbs` or placeholder words from `allowed_non_adverb_words`, where at -- least one following word is from `phrasal_adverbs` (hence [[can it]] is not a phrasal verb). while true do local prev, word = base:match("^(.+) (.-)$") if not prev then break end if phrasal_adverbs[word] then insert(seen_adverbs, word) elseif allowed_non_adverb_words[word] then -- do nothing else break end base = prev end if not base:find(" ") and seen_adverbs[1] then insert(data.categories, langname .. " phrasal verbs") for i = #seen_adverbs, 1, -1 do insert(data.categories, langname .. ' phrasal verbs formed wiþ "' .. seen_adverbs[i] .. '"') end end end end, } ----------------------------------------------------------------------------------------- -- Suffix forms -- ----------------------------------------------------------------------------------------- pos_functions["suffix forms"] = { params = { [1] = {required = true, list = true, disallow_holes = true}, }, func = function(args, data, is_suffix) local suffix_type = {} for _, typ in ipairs(args[1]) do insert(suffix_type, typ .. "-forming suffix") end insert(data.inflections, {label = "non-lemma form of " .. m_table.serialCommaJoin(suffix_type, {conj = "or"})}) end, } return export osb4kfl21hdj0yyrx7i63pqxluek83h 55047 55018 2026-09-30T19:53:13Z Deadend0914 7211 55047 Scribunto text/plain local export = {} local pos_functions = {} --[==[ Author from 2020 on: mostly Benwing2, with significant contributions from Theknightwho. Based on a prior version by Rua (by now mostly rewritten), with contributions from Erutuon and others (see history for full attribution). ]==] local force_cat = false -- for testing; if true, categories appear in non-mainspace pages local require = require local require_when_needed = require("Module:require when needed") local en_utilities_module = "Module:en-utilities" local headword_utilities_module = "Module:headword utilities" local headword_module = "Module:headword" local inflection_utilities_module = "Module:inflection utilities" local parse_utilities_module = "Module:parse utilities" local JSON_module = "Module:JSON" local labels_module = "Module:labels" local links_module = "Module:links" local parameters_module = "Module:parameters" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local utilities_module = "Module:utilities" local yesno_module = "Module:yesno" local iut = require_when_needed(inflection_utilities_module) local put = require_when_needed(parse_utilities_module) local m_headword_utilities = require_when_needed(headword_utilities_module) local add_links_to_multiword_term = require_when_needed(headword_utilities_module, "add_links_to_multiword_term") local add_suffix = require_when_needed(en_utilities_module, "add_suffix") local apply_link_modifiers = require_when_needed(headword_utilities_module, "apply_link_modifiers") local concat = table.concat local deepEquals = require_when_needed(table_module, "deepEquals") local dump = mw.dumpObject local format_categories = require_when_needed(utilities_module, "format_categories") local full_headword = require_when_needed(headword_module, "full_headword") local get_label_info = require_when_needed(labels_module, "get_label_info") local get_link_page = require_when_needed(links_module, "get_link_page") local glossary_link = require_when_needed(headword_utilities_module, "glossary_link") local insert = table.insert local insertIfNot = require_when_needed(table_module, "insertIfNot") local ipairs = ipairs local is_regular_plural = require_when_needed(en_utilities_module, "is_regular_plural") local list_to_set = require_when_needed(table_module, "listToSet") local pairs = pairs local process_params = require_when_needed(parameters_module, "process") local remove = table.remove local remove_links = require_when_needed(links_module, "remove_links") local replacement_escape = require_when_needed(string_utilities_module, "replacement_escape") local shallowCopy = require_when_needed(table_module, "shallowCopy") local singularize = require_when_needed(en_utilities_module, "singularize") local split = require_when_needed(string_utilities_module, "split") local toJSON = require_when_needed(JSON_module, "toJSON") local toNFD = mw.ustring.toNFD local type = type local ulen = require_when_needed(string_utilities_module, "len") local ulower = require_when_needed(string_utilities_module, "lower") local umatch = require_when_needed(string_utilities_module, "match") local u = require_when_needed(string_utilities_module, "char") local ugsub = require_when_needed(string_utilities_module, "gsub") local lang = require("Module:languages").getByCode("en") local langname = lang:getCanonicalName() local list_param = {list = true, disallow_holes = true} local list_allow_holes = {list = true, allow_holes = true} local boolean_param = {type = "boolean"} local function ine(val) if val == "" then return nil else return val end end local function track(page) require("Module:debug/track")("en-headword/" .. page) return true end ------------------------------------------- UTILITY FUNCTIONS ------------------------------------------ -- Parse and return an inflection not requiring additional processing. The raw arguments come from `args[field]`, which -- is parsed for inline modifiers. local function parse_inflection(args, field, is_head) local argfield = field if type(argfield) == "table" then argfield = argfield[1] end return m_headword_utilities.parse_term_list_with_modifiers { paramname = field, forms = args[argfield], splitchar = ",", is_head = is_head, } end -- Insert the parsed inflections in `terms` (as parsed by `parse_inflection`) into `data.inflections`, with label -- `label` and optional accelerator spec `accel`. local function insert_inflection(data, terms, label, accel, no_label) for _, termobj in ipairs(terms) do m_headword_utilities.remove_termobj_field_modifiers(termobj) end m_headword_utilities.insert_inflection { headdata = data, terms = terms, label = label, no_label = no_label, accel = accel and {form = accel} or nil, } end -- Insert a fixed label `label` into the inflections for `data`. If `originating_term` is supplied, copy the decorations -- from it into the fixed label. local function insert_fixed_inflection(data, label, originating_term) m_headword_utilities.insert_fixed_inflection { headdata = data, originating_term = originating_term, label = label, } 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 -- These functions are used directly in the <> format as well as in the utility functions #2 below. local function compute_double_last_cons_stem(term) local last_cons = term:match("([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])$") if not last_cons then error("Verb stem '" .. term .. "' must end in a consonant to use ++") end return term .. last_cons end local function compute_plusplus_s_form(term, default_s_form) if term:find("[szx]$") then -- regas -> regasses, derez -> derezzes return compute_double_last_cons_stem(term) .. "es" else return default_s_form end end -- The main entry point. -- This is the only function that can be invoked from a template. function export.show(frame) local iparams = { [1] = true, } local iargs = require("Module:parameters").process(frame.args, iparams) local parargs = frame:getParent().args local poscat = iargs[1] local pos_in_1 = not poscat if pos_in_1 then poscat = ine(parargs[1]) or mw.title.getCurrentTitle().fullText == "Template:en-head" and "interjection" or error("Part of speech must be specified in 1=") poscat = require(headword_module).canonicalize_pos(poscat) end local indexing_poscat = pos_in_1 and "head" or poscat local params = { ["head"] = list_param, ["id"] = true, ["json"] = boolean_param, ["sort"] = true, ["splithyph"] = boolean_param, ["nosplithyph"] = boolean_param, ["hyphspace"] = boolean_param, ["nolink"] = boolean_param, ["nolinkhead"] = {type = "boolean_param", alias_of = "nolink"}, ["suffix"] = boolean_param, ["nosuffix"] = boolean_param, ["nomultiwordcat"] = boolean_param, ["abbr"] = list_param, ["the"] = true, ["def"] = {alias_of = "the"}, ["pagename"] = true, -- for testing } if pos_in_1 then params[1] = {required = true} -- required but ignored as already processed above end local pos_data = pos_functions[indexing_poscat] local pos_func if pos_data then local pos_params = pos_data.params if pos_params then for key, val in pairs(pos_params) do params[key] = val end end pos_func = pos_data.func end local args = process_params(parargs, params) -- Account for unsupported titles, e.g. 'C|N>K' instead of 'Unsupported titles/C through N to K'. local pagename = args.pagename or mw.loadData("Module:headword/data").pagename local user_specified_heads = parse_inflection(args, "head", "is_head") local heads = user_specified_heads local autohead if args.nolink or not pagename:find("[ '%-]") then autohead = pagename else local en_no_split_apostrophe_words = list_to_set { "one's", "someone's", "he's", "she's", "it's", } local en_include_hyphen_prefixes = list_to_set { -- We don't include things that are also words even though they are often (perhaps mostly) prefixes, e.g. -- "be", "counter", "cross", "extra", "half", "mid", "over", "pan", "under". "acro", "acousto", "Afro", "agro", "anarcho", "angio", "Anglo", "ante", "anti", "arch", "auto", "bi", "bio", "cis", "co", "cryo", "crypto", "de", "demi", "eco", "electro", "Euro", "ex", "Greco", "hemi", "hydro", "hyper", "hypo", "infra", "Indo", "inter", "intra", "Judeo", "macro", "meta", "micro", "mini", "multi", "neo", "neuro", "non", "para", "peri", "post", "pre", "pro", "proto", "pseudo", "re", "semi", "sub", "super", "trans", "un", "vice", } local function is_english(term) local title = mw.title.new(term) if title and title.exists then local content = title:getContent() if content and content:find("==English==\n") then return true end end return false end local function en_split_hyphen_when_space(word) if not word:find("-", nil, true) then return nil end if args.hyphspace then return "[[" .. word:gsub("%-+", " ") .. "|" .. word .. "]]" end if args.nosplithyph then return "[[" .. word .. "]]" end if not args.splithyph then local space_word = word:gsub("%-+", " ") if is_english(space_word) then return "[[" .. space_word .. "|" .. word .. "]]" end if is_english(word) then return "[[" .. word .. "]]" end end return nil end local function en_split_apostrophe(word) local base = word:match("^(.*)'s$") if base then return "[[" .. base .. "]][[-'s|'s]]" end -- Only treat final apostrophe as possessive if preceded by something that looks like a plural ending in /z/. -- In particular we don't want to do it for words like [[truckin']]. base = word:match("^(.*[sxz])'$") if base then if base:find("s$") then local sg = singularize(base) if is_english(sg) then return "[[" .. sg .. "|" .. base .. "]][[-'|']]" end end return "[[" .. base .. "]][[-'|']]" end return "[[" .. word .. "]]" end autohead = add_links_to_multiword_term(pagename, { split_hyphen_when_space = en_split_hyphen_when_space, split_apostrophe = en_split_apostrophe, no_split_apostrophe_words = en_no_split_apostrophe_words, include_hyphen_prefixes = en_include_hyphen_prefixes, }) end if not heads[1] then heads = {{term = autohead}} else for _, headobj in ipairs(heads) do local head = headobj.term if head:find("^~") then head = apply_link_modifiers(autohead, head:sub(2), lang) headobj.term = head elseif head:find("^[!?]$") then -- If explicit head= just consists of ! or ?, add it to the end of the default head. headobj.term = autohead .. head end if head == autohead then track("redundant-head") end end end -- handle the=/def= if args.the == "~" then local newheads = {} for _, headobj in ipairs(heads) do local barehead = shallowCopy(headobj) insert(newheads, barehead) headobj.term = "the " .. headobj.term insert(newheads, headobj) end heads = newheads elseif args.the then local the = require(yesno_module)(args.the) if the then for _, headobj in ipairs(heads) do headobj.term = "the " .. headobj.term end end end local data = { lang = lang, pos_category = poscat, categories = {}, heads = heads, user_specified_heads = user_specified_heads, -- We use our own splitting algorithm so the redundant head cat will be inaccurate. no_redundant_head_cat = true, inflections = {}, nomultiwordcat = args.nomultiwordcat, sort_key = args.sort, pagename = pagename, id = args.id, force_cat_output = force_cat, } local function inscat(cat) insert(data.categories, langname .. " " .. cat) end local is_suffix = false if args.suffix or not args.nosuffix and pagename:find("^%-") and not pagename:find("^%-%-") and poscat ~= "suffix forms" then is_suffix = true data.pos_category = "suffixes" local singular_poscat = singularize(poscat) inscat(singular_poscat .. "-forming suffixes") insert(data.inflections, {label = singular_poscat .. "-forming suffix"}) end if pos_func then pos_func(args, data, is_suffix) end local extra_categories = {} if pagename:find("[Qq]") then -- Check for q not followed by u. We want to exclude things like [[13q deletion syndrome]] and [[BFOQ]] that -- don't have a lowercase letter on either side, as well as things like [[& seq.]] and [[acq.]] that are -- abbreviations for words containing a following u. -- -- Approximate range of combining diacritics; we want to remove them so the checks below for -- a lowercase letter next to the q aren't tripped up by diacritics on the letter. local u300 = u(0x0300) local u36F = u(0x036F) local pagename_no_diacritics = ugsub(toNFD(pagename), "[" .. u300 .. "-" .. u36F .. "]", "") if pagename_no_diacritics:find("[Qq][a-tv-z]") or pagename_no_diacritics:find("[a-z]q[^u.]") or pagename_no_diacritics:find("[a-z]q$") then inscat("words containing Q not followed by U") end end -- toNFD performs decomposition, so letters that decompose to an ASCII -- vowel and a diacritic, such as é, are counted as vowels and do not do not -- need to be included in the pattern. if not umatch(ulower(toNFD(pagename)), "[aeiouyæœøəªºαεηιουω]") then inscat("words spelled wiþūtan vowels") end if pagename:find("yre$") then inscat('words ending in "-yre"') end if not pagename:find(" ") and ulen(pagename) >= 25 then insert(extra_categories, "Long " .. langname .. " words") end if pagename:find("^[^aeiou ]*a[^aeiou ]*e[^aeiou ]*i[^aeiou ]*o[^aeiou ]*u[^aeiou ]*$") then inscat("words that use all vowels in alphabetical order") end parse_and_insert_inflection(data, args, "abbr", "abbreviation") if args.json then return toJSON(data) end return full_headword(data) .. (extra_categories[1] and format_categories(extra_categories, lang, args.sort) or "") end local function make_default_comparative(word) if word == "good" or word == "well" then return {"better"} elseif word == "bad" or word == "badly" then return {"worse"} elseif word == "far" then return {"further", "farther"} else return {add_suffix(word, "r")} end end local function make_default_superlative(word) if word == "good" or word == "well" then return {"best"} elseif word == "bad" or word == "badly" then return {"worst"} elseif word == "far" then return {"furthest", "farthest"} else return {add_suffix(word, "st.superlative")} end end -- This function does the common work between adjectives and adverbs. local function process_comparative_args(data, args, plpos) local pagename = data.pagename local comps = parse_inflection(args, 1) local sups = parse_inflection(args, "sup") local outcomps, outsups if args.componly then if comps[1] then error("Can't specify comparatives of comparative-only " .. plpos) end insert(data.inflections, {label = glossary_link("comparative") .. " form only"}) insert(data.categories, langname .. " comparative-only " .. plpos) -- Set to empty list so we don't get any comparatives output, but process superlatives if specified. outcomps = {} if not sups[1] then -- Set to empty list so we don't get any superlatives output unless explicitly given. outsups = {} end elseif args.suponly then if comps[1] or sups[1] then error("Can't specify comparatives or superlatives of or superlative-only " .. plpos) end insert(data.inflections, {label = glossary_link("superlative") .. " form only"}) insert(data.categories, langname .. " superlative-only " .. plpos) return end -- If the first parameter is ?, then don't show anything, just return. if comps[1] and comps[1].term == "?" then if comps[2] then error("Can't specify additional comparatives along wiþ '?'") end if sups[1] then error("Can't specify superlatives along wiþ '?' for the comparative") end return end if comps[1] and comps[1].term == "-" then local hyphencomp = remove(comps, 1) -- Remove the "-" but retain for decorations. -- Not (generally) comparable; may occasionally have a comparative if comps[1] then insert_fixed_inflection(data, "not generally <<comparable>>", hyphencomp) elseif not sups[1] then insert_fixed_inflection(data, "not <<comparable>>", hyphencomp) insert(data.categories, langname .. " uncomparable " .. plpos) return else -- No comparative, but a superlative. insert_inflection() will correctly generate 'no comparative' if we -- pass in "-" as the value. outcomps = {hyphencomp} end elseif not comps[1] then comps = {{term = "more"}} end if not outcomps then -- not if we set `outcomps` to "-" above or processed a comparative-only term outcomps = {} -- Go over each parameter given and create a comparative and superlative form. for _, compobj in ipairs(comps) do local comp = compobj.term if comp == "-" then error("Comparative of '-' only allowed as first comparative") end if comp == "+" then comp = "+more" elseif comp == "more" and pagename ~= "many" and pagename ~= "much" then comp = "+more" elseif comp == "further" and pagename ~= "far" then comp = "+further" elseif comp == "better" and pagename ~= "good" and pagename ~= "well" then comp = "+better" elseif comp:find("~") then comp = comp:gsub("~", replacement_escape(pagename)) end compobj.origterm = comp if comp == "+more" then comp = "more [[" .. pagename .. "]]" elseif comp == "+further" then comp = {"further [[" .. pagename .. "]]", "farther [[" .. pagename .. "]]"} elseif comp == "+better" then comp = "better [[" .. pagename .. "]]" elseif comp == "er" then -- Add -er. comp = add_suffix(pagename, "r") elseif comp == "ier" then if pagename:sub(-1) ~= "y" then error("Can't specify 'ier' comparative unless the term ends wiþ 'y': " .. pagename) end comp = pagename:gsub("e?y$", "ier") elseif comp:find("^%+") then local special = m_headword_utilities.get_special_indicator(comp, "noerror") if special then comp = m_headword_utilities.handle_multiword(pagename, special, make_default_comparative) end end if type(comp) == "table" and not comp[2] then comp = comp[1] end if type(comp) == "table" then for i = 1, #comp - 1 do local outobj = shallowCopy(compobj) outobj.term = comp[i] insert(outcomps, outobj) end compobj.term = comp[#comp] insert(outcomps, compobj) else compobj.term = comp insert(outcomps, compobj) end end end if sups[1] and sups[1].term == "-" then if sups[2] then error("Can't specify '-' as superlative followed by further values") end -- No superlative. insert_inflection() will correctly generate 'no superlative' if we pass in "-" as the value. outsups = sups else if not sups[1] then sups = {{term = "+"}} end end -- `outsups` will be set if we set `outsups` to "-" above or processed a comparative-only term without superlatives. if not outsups then outsups = {} local function process_sup(sup, special, supobj, compobj) if special then sup = m_headword_utilities.handle_multiword(pagename, special, make_default_superlative) elseif sup == "-" or sup == "+" then error(("Internal error: Superlative value of '%s' should have been handled earlier"):format(sup)) elseif sup == "+most" then sup = "most [[" .. pagename .. "]]" elseif sup == "+furthest" then sup = {"furthest [[" .. pagename .. "]]", "farthest [[" .. pagename .. "]]"} elseif sup == "+best" then sup = "best [[" .. pagename .. "]]" elseif sup == "est" then -- Add -est. sup = add_suffix(pagename, "st.superlative") elseif sup == "iest" then if pagename:sub(-1) ~= "y" then error("Can't specify 'iest' superlative unless the term ends wiþ 'y': " .. pagename) end sup = pagename:gsub("e?y$", "iest") end if type(sup) == "table" and not sup[2] then sup = sup[1] end if compobj then supobj = shallowCopy(supobj) supobj = m_headword_utilities.combine_termobj_decorations(supobj, compobj) end if type(sup) == "table" then for i = 1, #sup - 1 do local outobj = shallowCopy(supobj) outobj.term = sup[i] insert(outsups, outobj) end supobj.term = sup[#sup] insert(outsups, supobj) else supobj.term = sup insert(outsups, supobj) end end for _, supobj in ipairs(sups) do local sup = supobj.term if sup == "-" then error("Superlative of '-' only allowed as first superlative") end if sup == "+" then if not comps[1] then error("Superlative of '+' can't be specified when there are no comparatives") end for _, compobj in ipairs(comps) do local comp = compobj.origterm local special if comp == "+more" then sup = "+most" elseif comp == "+further" then sup = "+furthest" elseif comp == "+better" then sup = "+best" elseif comp == "er" then sup = "est" elseif comp == "ier" then sup = "iest" else if comp:find("^%+") then special = m_headword_utilities.get_special_indicator(comp, "noerror") end if not special then -- If the full comparative was given, then derive the superlative by replacing -er with -- -est. if comp:sub(-2) == "er" then sup = comp:sub(1, -3) .. "est" else error(("The superlative cannot be derived automatically from comparative '%s' because it doesn't end in -er"):format(comp)) end end end process_sup(sup, special, supobj, compobj) end else local special = m_headword_utilities.get_special_indicator(sup, "noerror") -- Do some work here rather than in process_sup() so we don't end up double-processing a term with a '~' -- in it or a term that happens to be 'most' or similar after substitution of ~ in the comparative. if not special then if sup == "most" and pagename ~= "many" and pagename ~= "much" then sup = "+most" elseif sup == "furthest" and pagename ~= "far" then sup = "+furthest" elseif sup == "best" and pagename ~= "good" and pagename ~= "well" then sup = "+best" elseif sup:find("~") then sup = sup:gsub("~", replacement_escape(pagename)) end end process_sup(sup, special, supobj) end end end insert_inflection(data, outcomps, "<<comparative>>", "comparative") insert_inflection(data, outsups, "<<superlative>>", "superlative") end pos_functions["adjectives"] = { params = { [1] = list_param, ["comp_qual"] = {list = "comp\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the comparative value", }, ["sup"] = list_param, ["sup_qual"] = {list = "sup\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the superlative value", }, ["componly"] = boolean_param, ["suponly"] = boolean_param, }, func = function(args, data) -- Process the comparatives and superlatives. process_comparative_args(data, args, "adjectives") end, } pos_functions["adverbs"] = { params = { [1] = list_param, ["comp_qual"] = {list = "comp\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the comparative value", }, ["sup"] = list_param, ["sup_qual"] = {list = "sup\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the superlative value", }, ["componly"] = boolean_param, ["suponly"] = boolean_param, }, func = function(args, data) -- Process the comparatives and superlatives. process_comparative_args(data, args, "adverbs") end, } local function escape(str) return (str:gsub("\\([:#])", "\\\\%1") :gsub("[:#]", "\\%0")) end local function canonicalize_plural(pl, pagename, pos) if pl == "+" then return escape(add_suffix(pagename, "s.plural", pos)) elseif pl == "++" then return escape(compute_plusplus_s_form(pagename, add_suffix(pagename, "s.plural", pos))) elseif pl == "*" then return escape(pagename) elseif pl == "ies" then if pagename:sub(-1) == "y" then return escape(pagename:gsub("e?y$", pl)) end error("Can't specify 'ies' plural unless the term ends wiþ 'y'.") elseif pl == "s" or pl == "es" or pl == "'s" then return escape(pagename .. pl) end end local function do_nouns(args, data, pos) local pagename = data.pagename pos = pos or "noun" local plurals = parse_inflection(args, 1) local function insert_plurale_tantum_inflections(is_plural_only, originating_label) if args.sg[1] then insert_fixed_inflection(data, "normally plural", originating_label) parse_and_insert_inflection(data, args, "anf.", "singular") elseif is_plural_only then insert_fixed_inflection(data, "plural only", originating_label) end if args.attr[1] then parse_and_insert_inflection(data, args, "attr", "attributive") end end local function first_pl_term() return plurals[1] and plurals[1].term or nil end if first_pl_term() == "p" then -- plurale tantum if plurals[2] then error("Wiþ plurale tantum nama, can't specify more than one plural") end data.genders = {"p"} -- this should auto-insert the correct 'pluralia tantum' category insert_plurale_tantum_inflections("plural only", plurals[1]) return end local function inscat(cat) insert(data.categories, langname .. " " .. cat) end local need_default_plural = pos == "noun" if first_pl_term() == "sp" then -- construed as singular or plural sp = remove(plurals, 1) -- Remove the "sp" but retain it for its decorations. inscat("naman construed as singular or plural") data.genders = {"s", "p"} -- this should auto-insert the correct 'pluralia tantum' category insert_plurale_tantum_inflections(nil, sp) need_default_plural = false elseif first_pl_term() == "-" then -- Untellendlīc noun; may occasionally have a plural local hyphpl = remove(plurals, 1) -- Remove the "-" but retain for decorations. inscat("untellendlīc naman") -- If plural forms were given explicitly, then show "oft" if plurals[1] then insert_fixed_inflection(data, "oft <<untellendlīc>>", hyphpl) else insert_fixed_inflection(data, "<<untellendlīc>>", hyphpl) end need_default_plural = false elseif first_pl_term() == "#" then -- oft tellendlīc (e.g., "grilled cheese") local hashpl = remove(plurals, 1) -- Remove the "#" but retain for decorations. insert_fixed_inflection(data, "oft <<tellendlīc>>", hashpl) inscat("untellendlīc naman") inscat("tellendlīc naman") -- If no plural was given, add a default one now if not plurals[1] then plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end elseif first_pl_term() == "~" then -- Mixed tellendlīc/untellendlīc noun, always has a plural local tildepl = remove(plurals, 1) -- Remove the "~" but retain for decorations. insert_fixed_inflection(data, "<<tellendlīc>> and <<untellendlīc>>", tildepl) inscat("untellendlīc naman") inscat("tellendlīc naman") -- If no plural was given, add a default one now if not plurals[1] then plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end end -- Plural is unknown if first_pl_term() == "?" then local questionpl = remove(plurals, 1) -- Remove the "?" but retain for decorations. -- Not desired; see [[Wiktionary:Tea_room/2021/August#"Plural unknown or uncertain"]] -- insert_fixed_inflection(data, "manigfeald unġecnāwen oþþe ungewiss", questionpl) inscat("naman wiþ unġecnāwen oþþe ungewiss manigfealde") if plurals[1] then error("Ne cann āmearcian sweotole manigfealde andlang wiþ '?' of unġecnāwen/ungewiss manigfeald") end return end -- Plural is not attested if first_pl_term() == "!" then local exclampl = remove(plurals, 1) -- Remove the "!" but retain for decorations. insert_fixed_inflection(data, "plural not attested", exclampl) inscat("naman wiþ unattested manigfealde") if plurals[1] then error("Ne cann āmearcian sweotole manigfealde andlang wiþ '!' of gecȳþed manigfeald") end return end -- If no plural was given, maybe add a default one, otherwise (when "-" was given or proper noun) return. if not plurals[1] then if not need_default_plural then inscat("untellendlīc naman") return end plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end -- There are plural forms to show, so show them. inscat("tellendlīc naman") local irregular, indeclinable for i, pl in ipairs(plurals) do local canon_pl = canonicalize_plural(pl.term, pagename, pos) if canon_pl then pl.term = canon_pl end local pl_term = get_link_page(pl.term, lang) if not (pagename:find(" ") or is_regular_plural(pl_term, pagename)) then irregular = true if pl_term == pagename then indeclinable = true end end end if irregular then inscat("naman wiþ fremde manigfealde") end if indeclinable then inscat("undeclīnigendlīc naman") end insert_inflection(data, plurals, "mnf.", "p") end -- Return the parameters to be used for nouns and proper nouns. Currently the same. local noun_params = { [1] = list_param, ["pl\1qual"] = {list = true, allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the plural", }, -- The following four only used for pluralia tantum (1=p) ["sg"] = list_param, ["attr"] = list_param, } pos_functions["nouns"] = { params = noun_params, func = do_nouns, } pos_functions["proper nouns"] = { params = noun_params, func = function(args, data) return do_nouns(args, data, "proper nouns") end, } local function base_default_verb_forms(verb) return escape(add_suffix(verb, "s.verb")), escape(add_suffix(verb, "ing")), escape(add_suffix(verb, "d")) end local function default_verb_forms(verb) local full_s_form, full_ing_form, full_ed_form = base_default_verb_forms(verb) if verb:find(" ") then local first, rest = verb:match("^(.-)( .*)$") local first_s_form, first_ing_form, first_ed_form = base_default_verb_forms(first) return full_s_form, full_ing_form, full_ed_form, first_s_form .. rest, first_ing_form .. rest, first_ed_form .. rest, first, rest else return full_s_form, full_ing_form, full_ed_form, nil, nil, nil, nil, nil end end local function compute_double_last_cons_stem_of_split_verb(verb, ending) local first, rest = verb:match("^(.-)( .*)$") if not first then error("Word '" .. verb .. "' mōt hæfþ rum in sīde tō notienne **") end local last_cons = first:match("([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])$") if not last_cons then error("Fyrest word '" .. first .. "' mōt ende wiþ samod swēgend tō notienne **") end return first .. last_cons .. ending .. rest end local function check_non_nil_star_form(form, pagename) if form == nil then error("Word '" .. pagename .. "' mōt hæfþ rum in sīde tō notienne *, **, *l, *! or *'") end return form end local function sub_tilde(form, pagename) if not form then return nil end if form:find("~") then form = form:gsub("~", replacement_escape(pagename)) end return form end local deprecated_qual_replaced_by_inline_modifier = { list = true, allow_holes = true, replaced_by = false, instead = "use an inline modifier <q:...> or <l:...> on the value" } pos_functions["verbs"] = { params = { [1] = {list = "pres_3sg", disallow_holes = true}, ["pres_3sg\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [2] = {list = "pres_ptc", disallow_holes = true}, ["pres_ptc\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [3] = {list = "past", disallow_holes = true}, ["past\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [4] = {list = "past_ptc", allow_holes = true}, ["past_ptc\1_qual"] = deprecated_qual_replaced_by_inline_modifier, ["noautolinkverb"] = boolean_param, ["angle_bracket"] = boolean_param, }, func = function(args, data) -- Get parameters local par1s local par2s = parse_inflection(args, {2, "pres_ptc"}) local par3s = parse_inflection(args, {3, "past"}) local par4s = parse_inflection(args, {4, "past_ptc"}) local pres_3sgs, pres_ptcs, pasts, past_ptcs local pagename = data.pagename ------------------------------------------- UTILITY FUNCTIONS #2 ------------------------------------------ -- These functions are used in both in the separate-parameter format and in the override params such as past_ptc2=. local full_default_s, full_default_ing, full_default_ed, split_default_s, split_default_ing, split_default_ed local lemma local function set_lemma_and_default_forms(the_lemma) lemma = the_lemma full_default_s, full_default_ing, full_default_ed, split_default_s, split_default_ing, split_default_ed, lemma_first, lemma_rest = default_verb_forms(the_lemma) end local function canonicalize_s_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_s elseif form == "*" then return check_non_nil_star_form(split_default_s, lemma) elseif form == "++" then return compute_plusplus_s_form(lemma, full_default_s) elseif form == "**" then if lemma:find("^[^ ]*[szx] ") then return compute_double_last_cons_stem_of_split_verb(lemma, "es") else return check_non_nil_star_form(split_default_s, lemma) end elseif form == "+!" then return lemma .. "s" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "s" .. lemma_rest elseif form == "+'" then return lemma .. "'s" elseif form == "*'" then return check_non_nil_star_form(lemma_first) .. "'s" .. lemma_rest elseif form == "+l" then if lemma:find("[szx]$") then return {{term = full_default_s, l = {"US"}}, {term = compute_plusplus_s_form(lemma, full_default_s), l = {"UK"}}} else return compute_plusplus_s_form(lemma, full_default_s) end elseif form == "*l" then if lemma:find("^[^ ]*[szx] ") then return {{term = check_non_nil_star_form(split_default_s, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "es"), l = {"UK"}}} else return check_non_nil_star_form(split_default_s, lemma) end else return sub_tilde(form, lemma) end end local function canonicalize_ing_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_ing elseif form == "*" then return check_non_nil_star_form(split_default_ing, lemma) elseif form == "++" then return compute_double_last_cons_stem(lemma) .. "ing" elseif form == "**" then return compute_double_last_cons_stem_of_split_verb(lemma, "ing") elseif form == "+!" then return lemma .. "ing" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "ing" .. lemma_rest elseif form == "+'" then return lemma .. "'ing" elseif form == "*'" then return check_non_nil_star_form(lemma_first) .. "'ing" .. lemma_rest elseif form == "+l" then return {{term = full_default_ing, l = {"US"}}, {term = compute_double_last_cons_stem(lemma) .. "ing", l = {"UK"}}} elseif form == "*l" then return {{term = check_non_nil_star_form(split_default_ing, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "ing"), l = {"UK"}}} else return sub_tilde(form, lemma) end end local function canonicalize_ed_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_ed elseif form == "*" then return check_non_nil_star_form(split_default_ed, lemma) elseif form == "++" then return compute_double_last_cons_stem(lemma) .. "ed" elseif form == "+!" then return lemma .. "ed" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "ed" .. lemma_rest elseif form == "+'" then return {{term = lemma .. "'d"}, {term = lemma .. "'ed"}} elseif form == "*'" then return {{term = check_non_nil_star_form(lemma_first) .. "'d" .. lemma_rest}, {term = check_non_nil_star_form(lemma_first) .. "'ed" .. lemma_rest}} elseif form == "**" then return compute_double_last_cons_stem_of_split_verb(lemma, "ed") elseif form == "+l" then return {{term = full_default_ed, l = {"US"}}, {term = compute_double_last_cons_stem(lemma) .. "ed", l = {"UK"}}} elseif form == "*l" then return {{term = check_non_nil_star_form(split_default_ed, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "ed"), l = {"UK"}}} else return sub_tilde(form, lemma) end end -- FIXME: options should be "+", "*", "++", "**", "+n", "*n", "++n" and "**n", but not "n" local function canonicalize_en_form(form) if form == "n" then track("n4") return add_suffix(lemma, "n") end return canonicalize_ed_form(form) end --------------------------------- MAIN PARSING/CONJUGATING CODE -------------------------------- local is_angle_bracket = args.angle_bracket if is_angle_bracket then if par2s[1] or par3s[1] or par4s[1] then error("Can't specify explicit values for 2=, 3= or 4= along wiþ the angle-bracket format") end elseif is_angle_bracket == nil and not par2s[1] and not par3s[1] and not par4s[1] and not args[1][2] and args[1][1] and args[1][1]:find("<") then if put.term_contains_top_level_html(args[1][1]) then -- Often, term_contains_top_level_html() returns true on the angle-bracket format, which would -- make the pcall() below succeed but leave the angle brackets as-is. Check for this and only do the -- pcall() if term_contains_top_level_html() returns false. is_angle_bracket = true else -- If it's ambiguous whether it's an angle-bracket format or separate params with an inline modifier, -- try to parse as the latter. If an error occurs, treat as the former. local ok ok, par1s = pcall(parse_inflection, args, {1, "pres_3sg"}) if not ok then par1s = nil is_angle_bracket = true end end end if is_angle_bracket then -------------------------- ANGLE-BRACKET FORMAT -------------------------- -- (0) Expand multiword term with angle brackets just on the first word. local arg11 = args[1][1] if arg11:find("^<.*>$") and pagename:find(" ") then local first, rest = pagename:match("^(.-)( .*)$") arg11 = first .. arg11 .. rest end -- (1) Parse the indicator specs inside of angle brackets. local function parse_indicator_spec(angle_bracket_spec) local inside = angle_bracket_spec:match("^<(.*)>$") assert(inside) local segments = put.parse_balanced_segment_run(inside, "[", "]") local comma_separated_groups = put.split_alternating_runs(segments, ",") if #comma_separated_groups > 4 then error("Too many comma-separated parts in indicator spec, expected at most 4: " .. angle_bracket_spec) end local function fetch_footnotes(separated_group) local footnotes for j = 2, #separated_group - 1, 2 do if separated_group[j + 1] ~= "" then error("Extraneous text after bracketed footnotes: '" .. concat(separated_group) .. "'") end if not footnotes then footnotes = {} end insert(footnotes, separated_group[j]) end return footnotes end local function fetch_specs(comma_separated_group) if not comma_separated_group then return {{term = "+"}} end local specs = {} local colon_separated_groups = put.split_alternating_runs(comma_separated_group, ":") for _, colon_separated_group in ipairs(colon_separated_groups) do local form = colon_separated_group[1] if form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then error("*, **, *l, *! and *' not allowed inside of indicator specs: " .. angle_bracket_spec) end if form == "" then form = "+" end local termobj = { term = form } local footnotes = fetch_footnotes(colon_separated_group) if footnotes then for _, footnote in ipairs(footnotes) do m_headword_utilities.add_footnote_to_termobj(termobj, footnote) end end insert(specs, termobj) end return specs end local s_specs = fetch_specs(comma_separated_groups[1]) local ing_specs = fetch_specs(comma_separated_groups[2]) local ed_specs = fetch_specs(comma_separated_groups[3]) local en_specs = fetch_specs(comma_separated_groups[4]) return { forms = {}, s_specs = s_specs, ing_specs = ing_specs, ed_specs = ed_specs, en_specs = en_specs, } end local parse_props = { parse_indicator_spec = parse_indicator_spec, } local alternant_multiword_spec = iut.parse_inflected_text(arg11, parse_props) -- (2) Check for user-specified brackets; remove any links from the lemma, but remember the original -- form so we can use it below in the 'lemma_linked' form. -- Check to see if there are brackets in the pre-text or post-text. If so, use the linked lemma (with the -- verb autolinked unless noautolinkverb is given). Otherwise, use the default headword algorithm. local function check_bracket(val) if val:find("%[%[") then alternant_multiword_spec.saw_bracket = true end end for _, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do check_bracket(alternant_or_word_spec.before_text) if alternant_or_word_spec.alternants then for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do for _, word_spec in ipairs(multiword_spec.word_specs) do check_bracket(word_spec.before_text) end check_bracket(multiword_spec.post_text) end end end check_bracket(alternant_multiword_spec.post_text) iut.map_word_specs(alternant_multiword_spec, function(base) if base.lemma == "" then base.lemma = pagename end base.orig_lemma = base.lemma base.lemma = remove_links(base.lemma) if args.noautolinkverb or base.orig_lemma:find("%[%[") then base.linked_lemma = base.orig_lemma else base.linked_lemma = "[[" .. base.orig_lemma .. "]]" end end) -- (3) Conjugate the verbs according to the indicator specs parsed above. local all_verb_slots = { lemma = "infinitive", lemma_linked = "infinitive", s_form = "3|s|pres", ing_form = "pres|ptcp", ed_form = "past", en_form = "past|ptcp", } local function conjugate_verb(base) local function process_specs(slot, specs, canon_func, default_values, default_already_formobj) local function insert_termobj_into_slot(termobj) local formobj = m_headword_utilities.convert_termobj_to_formobj(termobj) -- If the form is -, don't insert any forms, which will result in there being no overall forms -- (in fact it will be nil). We check for that down below and substitute a single "-" as the -- form, which in turn gets turned into special labels like "no present participle". if formobj.form == "-" then if formobj.footnotes then error("Unable to preserve footnotes specified on missing form '-': FIXME: " .. dump(formobj.footnotes)) end else iut.insert_form(base.forms, slot, formobj) end end local function canonicalize_and_insert(arg) local canon_arg = canon_func(arg) if type(canon_arg) == "string" then arg.term = canon_arg insert_termobj_into_slot(arg) else for _, canon in ipairs(canon_arg) do m_headword_utilities.combine_termobj_decorations(canon, arg) insert_termobj_into_slot(canon) end end end for _, arg in ipairs(specs) do if arg.term == "+" then if default_values then -- will be nil if past tense specified as - and no past ptc given for _, val in ipairs(default_values) do val = shallowCopy(val) if default_already_formobj then local argformobj = m_headword_utilities.convert_termobj_to_formobj(arg) val.footnotes = iut.combine_footnotes(val.footnotes, argformobj.footnotes) iut.insert_form(base.forms, slot, val) else m_headword_utilities.combine_termobj_decorations(val, arg) canonicalize_and_insert(val) end end end else canonicalize_and_insert(arg) end end end set_lemma_and_default_forms(base.lemma) local all_part_default_specs = {} local function process_and_canonicalize_s_form(arg) local form = arg.term if form == "+" then error("Internal error: '+' should have been converted to '^' by now") end if form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then error(("Internal error: '%s' should have already thrown an error"):format(form)) end if form == "^" or form == "++" or form == "+l" or form == "+!" or form == "+'" then insert(all_part_default_specs, shallowCopy(arg)) end return canonicalize_s_form(form) end process_specs("s_form", base.s_specs, process_and_canonicalize_s_form, {{term = "^"}}) if not all_part_default_specs[1] then all_part_default_specs[1] = {term = "^"} end process_specs("ing_form", base.ing_specs, function(arg) return canonicalize_ing_form(arg.term) end, all_part_default_specs) process_specs("ed_form", base.ed_specs, function(arg) return canonicalize_ed_form(arg.term) end, all_part_default_specs) process_specs("en_form", base.en_specs, function(arg) return canonicalize_en_form(arg.term) end, base.forms.ed_form, "default already formobj") iut.insert_form(base.forms, "lemma", {form = base.lemma}) -- Add linked version of lemma for use in head=. We write this in a general fashion in case -- there are multiple lemma forms (which isn't possible currently at this level, although it's -- possible overall using the ((...,...)) notation). iut.insert_forms(base.forms, "lemma_linked", iut.map_forms(base.forms.lemma, function(form) if form == base.lemma and base.linked_lemma:find("%[%[") then return base.linked_lemma else return form end end)) end local inflect_props = { slot_table = all_verb_slots, inflect_word_spec = conjugate_verb, } iut.inflect_multiword_or_alternant_multiword_spec(alternant_multiword_spec, inflect_props) -- (4) Fetch the forms and put the conjugated lemmas in data.heads if not explicitly given. local function fetch_termobjs(slot) local forms = alternant_multiword_spec.forms[slot] -- See above. This should only occur if the user explicitly used - for a spec. if not forms or not forms[1] then return {{term = "-"}} end local termobjs = {} for _, formobj in ipairs(forms) do insert(termobjs, m_headword_utilities.convert_formobj_to_termobj(formobj)) end return termobjs end pres_3sgs = fetch_termobjs("s_form") pres_ptcs = fetch_termobjs("ing_form") pasts = fetch_termobjs("ed_form") past_ptcs = fetch_termobjs("en_form") -- Use the "linked" form of the lemma as the head if no head= explicitly given and the user specified -- brackets in one of the lemmas. Otherwise we use the default headword-linking algorithm. if not data.user_specified_heads[1] and alternant_multiword_spec.saw_bracket then data.heads = {} for _, lemma_obj in ipairs(alternant_multiword_spec.forms.lemma_linked) do insert(data.heads, m_headword_utilities.convert_formobj_to_termobj(lemma_obj)) end end else -------------------------- SEPARATE-PARAM FORMAT -------------------------- set_lemma_and_default_forms(pagename) par1s = par1s or parse_inflection(args, {1, "pres_3sg"}) pres_3sgs = {} pres_ptcs = {} pasts = {} past_ptcs = {} if not par1s[1] then par1s = {{term = "+"}} end if not par2s[1] then par2s = {{term = "+"}} end if not par3s[1] then par3s = {{term = "+"}} end if not par4s[1] then par4s = {{term = "+"}} end local function process_argument(args, dest, canon_func, default_values, default_already_canonicalized) local function canonicalize_and_insert(arg) local canon_arg = canon_func(arg) if type(canon_arg) == "string" then arg.term = canon_arg m_headword_utilities.insert_termobj_combining_duplicates(dest, arg) else for _, canon in ipairs(canon_arg) do m_headword_utilities.combine_termobj_decorations(canon, arg) m_headword_utilities.insert_termobj_combining_duplicates(dest, canon) end end end for _, arg in ipairs(args) do if arg.term == "+" then for _, val in ipairs(default_values) do val = shallowCopy(val) m_headword_utilities.combine_termobj_decorations(val, arg) if default_already_canonicalized then m_headword_utilities.insert_termobj_combining_duplicates(dest, val) else canonicalize_and_insert(val) end end else canonicalize_and_insert(arg) end end end local all_part_default_specs = {} local function process_and_canonicalize_s_form(arg) local form = arg.term if form == "+" then error("Internal error: '+' should have been converted to '^' by now") end if form == "^" or form == "++" or form == "+l" or form == "+!" or form == "+'" or form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then insert(all_part_default_specs, shallowCopy(arg)) end return canonicalize_s_form(form) end process_argument(par1s, pres_3sgs, process_and_canonicalize_s_form, {{term = "^"}}) if not all_part_default_specs[1] then all_part_default_specs[1] = {term = "^"} end process_argument(par2s, pres_ptcs, function(arg) return canonicalize_ing_form(arg.term) end, all_part_default_specs) process_argument(par3s, pasts, function(arg) return canonicalize_ed_form(arg.term) end, all_part_default_specs) process_argument(par4s, past_ptcs, function(arg) return canonicalize_en_form(arg.term) end, pasts, "default already canonicalized") end ------------------------------------------- INSERT INFLECTIONS ------------------------------------------ insert_inflection(data, pres_3sgs, "þridda had anfeald ānfeald andweard", "s-verb-form") insert_inflection(data, pres_ptcs, "andweard dǣlnimend", "ing-form") if deepEquals(pasts, past_ptcs) then insert_inflection(data, pasts, "ānfeald forþgewiten and forþgewiten dǣlnimend", "ed-form", "na ānfeald forþgewiten oþþe forþgewiten dǣlnimend") else insert_inflection(data, pasts, "ānfeald forþgewiten", "spast") insert_inflection(data, past_ptcs, "forþgewiten dǣlnimend", "past|part") end if pagename:find(" ") then -- Check for placeholder "it" local words = split(pagename, " ") for _, word in ipairs(words) do if word == "it" or word == "its" or word == "it's" then insert(data.categories, langname .. ' terms wiþ placeholder "it"') break end end -- Check for phrasal verbs local phrasal_adverbs = list_to_set{ -- NOTE: This should only contain common phrasal adverbs, not random words like [[low]], -- [[adrift]], etc. "aback", "about", "above", "across", "after", "against", "ahead", "along", "apart", "around", "as", "aside", "at", "away", "back", "before", "behind", "below", "between", "beyond", "by", "down", "for", "forth", "from", "in", "into", "of", "off", "on", "onto", "out", "over", "past", "round", "through", "to", "together", "towards", "under", "up", "upon", "with", "without", } local allowed_non_adverb_words = list_to_set{ "it", "one", "oneself", "someone", } local base = pagename local seen_adverbs = {} -- Only consider a verb to be phrasal if it consists of a single base verb followed exclusively by either -- adverbs from `phrasal_adverbs` or placeholder words from `allowed_non_adverb_words`, where at -- least one following word is from `phrasal_adverbs` (hence [[can it]] is not a phrasal verb). while true do local prev, word = base:match("^(.+) (.-)$") if not prev then break end if phrasal_adverbs[word] then insert(seen_adverbs, word) elseif allowed_non_adverb_words[word] then -- do nothing else break end base = prev end if not base:find(" ") and seen_adverbs[1] then insert(data.categories, langname .. " phrasal verbs") for i = #seen_adverbs, 1, -1 do insert(data.categories, langname .. ' phrasal verbs formed wiþ "' .. seen_adverbs[i] .. '"') end end end end, } ----------------------------------------------------------------------------------------- -- Suffix forms -- ----------------------------------------------------------------------------------------- pos_functions["suffix forms"] = { params = { [1] = {required = true, list = true, disallow_holes = true}, }, func = function(args, data, is_suffix) local suffix_type = {} for _, typ in ipairs(args[1]) do insert(suffix_type, typ .. "-forming suffix") end insert(data.inflections, {label = "non-lemma form of " .. m_table.serialCommaJoin(suffix_type, {conj = "or"})}) end, } return export gqdd0x9n0oyiwg0iohbahujvi9xxwpm 55050 55047 2026-09-30T20:09:25Z Deadend0914 7211 55050 Scribunto text/plain local export = {} local pos_functions = {} --[==[ Author from 2020 on: mostly Benwing2, with significant contributions from Theknightwho. Based on a prior version by Rua (by now mostly rewritten), with contributions from Erutuon and others (see history for full attribution). ]==] local force_cat = false -- for testing; if true, categories appear in non-mainspace pages local require = require local require_when_needed = require("Module:require when needed") local en_utilities_module = "Module:en-utilities" local headword_utilities_module = "Module:headword utilities" local headword_module = "Module:headword" local inflection_utilities_module = "Module:inflection utilities" local parse_utilities_module = "Module:parse utilities" local JSON_module = "Module:JSON" local labels_module = "Module:labels" local links_module = "Module:links" local parameters_module = "Module:parameters" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local utilities_module = "Module:utilities" local yesno_module = "Module:yesno" local iut = require_when_needed(inflection_utilities_module) local put = require_when_needed(parse_utilities_module) local m_headword_utilities = require_when_needed(headword_utilities_module) local add_links_to_multiword_term = require_when_needed(headword_utilities_module, "add_links_to_multiword_term") local add_suffix = require_when_needed(en_utilities_module, "add_suffix") local apply_link_modifiers = require_when_needed(headword_utilities_module, "apply_link_modifiers") local concat = table.concat local deepEquals = require_when_needed(table_module, "deepEquals") local dump = mw.dumpObject local format_categories = require_when_needed(utilities_module, "format_categories") local full_headword = require_when_needed(headword_module, "full_headword") local get_label_info = require_when_needed(labels_module, "get_label_info") local get_link_page = require_when_needed(links_module, "get_link_page") local glossary_link = require_when_needed(headword_utilities_module, "glossary_link") local insert = table.insert local insertIfNot = require_when_needed(table_module, "insertIfNot") local ipairs = ipairs local is_regular_plural = require_when_needed(en_utilities_module, "is_regular_plural") local list_to_set = require_when_needed(table_module, "listToSet") local pairs = pairs local process_params = require_when_needed(parameters_module, "process") local remove = table.remove local remove_links = require_when_needed(links_module, "remove_links") local replacement_escape = require_when_needed(string_utilities_module, "replacement_escape") local shallowCopy = require_when_needed(table_module, "shallowCopy") local singularize = require_when_needed(en_utilities_module, "singularize") local split = require_when_needed(string_utilities_module, "split") local toJSON = require_when_needed(JSON_module, "toJSON") local toNFD = mw.ustring.toNFD local type = type local ulen = require_when_needed(string_utilities_module, "len") local ulower = require_when_needed(string_utilities_module, "lower") local umatch = require_when_needed(string_utilities_module, "match") local u = require_when_needed(string_utilities_module, "char") local ugsub = require_when_needed(string_utilities_module, "gsub") local lang = require("Module:languages").getByCode("en") local langname = lang:getCanonicalName() -- Old English localization: category wordings come from [[Module:floccnaman]] -- (see that module); the English wording is kept when a translation is missing. -- Loaded lazily and optionally, so a missing module can never break a page. local floccnaman_module = "Module:floccnaman" local floccnaman local function get_floccnaman() if floccnaman == nil then local ok, mod = pcall(require, floccnaman_module) floccnaman = ok and mod or false end return floccnaman or nil end -- Old English wording for one category word, or the English wording unchanged. local function cat_word(l, text) local mod = get_floccnaman() return mod and mod.get(l, text) or text end local list_param = {list = true, disallow_holes = true} local list_allow_holes = {list = true, allow_holes = true} local boolean_param = {type = "boolean"} local function ine(val) if val == "" then return nil else return val end end local function track(page) require("Module:debug/track")("en-headword/" .. page) return true end ------------------------------------------- UTILITY FUNCTIONS ------------------------------------------ -- Parse and return an inflection not requiring additional processing. The raw arguments come from `args[field]`, which -- is parsed for inline modifiers. local function parse_inflection(args, field, is_head) local argfield = field if type(argfield) == "table" then argfield = argfield[1] end return m_headword_utilities.parse_term_list_with_modifiers { paramname = field, forms = args[argfield], splitchar = ",", is_head = is_head, } end -- Insert the parsed inflections in `terms` (as parsed by `parse_inflection`) into `data.inflections`, with label -- `label` and optional accelerator spec `accel`. local function insert_inflection(data, terms, label, accel, no_label) for _, termobj in ipairs(terms) do m_headword_utilities.remove_termobj_field_modifiers(termobj) end m_headword_utilities.insert_inflection { headdata = data, terms = terms, label = label, no_label = no_label, accel = accel and {form = accel} or nil, } end -- Insert a fixed label `label` into the inflections for `data`. If `originating_term` is supplied, copy the decorations -- from it into the fixed label. local function insert_fixed_inflection(data, label, originating_term) m_headword_utilities.insert_fixed_inflection { headdata = data, originating_term = originating_term, label = label, } 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 -- These functions are used directly in the <> format as well as in the utility functions #2 below. local function compute_double_last_cons_stem(term) local last_cons = term:match("([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])$") if not last_cons then error("Verb stem '" .. term .. "' must end in a consonant to use ++") end return term .. last_cons end local function compute_plusplus_s_form(term, default_s_form) if term:find("[szx]$") then -- regas -> regasses, derez -> derezzes return compute_double_last_cons_stem(term) .. "es" else return default_s_form end end -- On English Wiktionary, {{tl|en-head}} is the template that means "interjection"; it invokes this module without -- a part of speech. This wiki's template namespace is named "Bysen", not "Template", so the namespace id and the -- base page title are compared instead of the full title text with its namespace prefix. local function is_en_head_template() local title = mw.title.getCurrentTitle() return title:getNamespace() == 10 and title:getBaseText() == "en-head" end -- The main entry point. -- This is the only function that can be invoked from a template. function export.show(frame) local iparams = { [1] = true, } local iargs = require("Module:parameters").process(frame.args, iparams) local parargs = frame:getParent().args local poscat = iargs[1] local pos_in_1 = not poscat if pos_in_1 then poscat = ine(parargs[1]) or is_en_head_template() and "interjection" or error("Part of speech must be specified in 1=") poscat = require(headword_module).canonicalize_pos(poscat) end local indexing_poscat = pos_in_1 and "head" or poscat local params = { ["head"] = list_param, ["id"] = true, ["json"] = boolean_param, ["sort"] = true, ["splithyph"] = boolean_param, ["nosplithyph"] = boolean_param, ["hyphspace"] = boolean_param, ["nolink"] = boolean_param, ["nolinkhead"] = {type = "boolean_param", alias_of = "nolink"}, ["suffix"] = boolean_param, ["nosuffix"] = boolean_param, ["nomultiwordcat"] = boolean_param, ["abbr"] = list_param, ["the"] = true, ["def"] = {alias_of = "the"}, ["pagename"] = true, -- for testing } if pos_in_1 then params[1] = {required = true} -- required but ignored as already processed above end local pos_data = pos_functions[indexing_poscat] local pos_func if pos_data then local pos_params = pos_data.params if pos_params then for key, val in pairs(pos_params) do params[key] = val end end pos_func = pos_data.func end local args = process_params(parargs, params) -- Account for unsupported titles, e.g. 'C|N>K' instead of 'Unsupported titles/C through N to K'. local pagename = args.pagename or mw.loadData("Module:headword/data").pagename local user_specified_heads = parse_inflection(args, "head", "is_head") local heads = user_specified_heads local autohead if args.nolink or not pagename:find("[ '%-]") then autohead = pagename else local en_no_split_apostrophe_words = list_to_set { "one's", "someone's", "he's", "she's", "it's", } local en_include_hyphen_prefixes = list_to_set { -- We don't include things that are also words even though they are often (perhaps mostly) prefixes, e.g. -- "be", "counter", "cross", "extra", "half", "mid", "over", "pan", "under". "acro", "acousto", "Afro", "agro", "anarcho", "angio", "Anglo", "ante", "anti", "arch", "auto", "bi", "bio", "cis", "co", "cryo", "crypto", "de", "demi", "eco", "electro", "Euro", "ex", "Greco", "hemi", "hydro", "hyper", "hypo", "infra", "Indo", "inter", "intra", "Judeo", "macro", "meta", "micro", "mini", "multi", "neo", "neuro", "non", "para", "peri", "post", "pre", "pro", "proto", "pseudo", "re", "semi", "sub", "super", "trans", "un", "vice", } local function is_english(term) local title = mw.title.new(term) if title and title.exists then local content = title:getContent() if content and content:find("==English==\n") then return true end end return false end local function en_split_hyphen_when_space(word) if not word:find("-", nil, true) then return nil end if args.hyphspace then return "[[" .. word:gsub("%-+", " ") .. "|" .. word .. "]]" end if args.nosplithyph then return "[[" .. word .. "]]" end if not args.splithyph then local space_word = word:gsub("%-+", " ") if is_english(space_word) then return "[[" .. space_word .. "|" .. word .. "]]" end if is_english(word) then return "[[" .. word .. "]]" end end return nil end local function en_split_apostrophe(word) local base = word:match("^(.*)'s$") if base then return "[[" .. base .. "]][[-'s|'s]]" end -- Only treat final apostrophe as possessive if preceded by something that looks like a plural ending in /z/. -- In particular we don't want to do it for words like [[truckin']]. base = word:match("^(.*[sxz])'$") if base then if base:find("s$") then local sg = singularize(base) if is_english(sg) then return "[[" .. sg .. "|" .. base .. "]][[-'|']]" end end return "[[" .. base .. "]][[-'|']]" end return "[[" .. word .. "]]" end autohead = add_links_to_multiword_term(pagename, { split_hyphen_when_space = en_split_hyphen_when_space, split_apostrophe = en_split_apostrophe, no_split_apostrophe_words = en_no_split_apostrophe_words, include_hyphen_prefixes = en_include_hyphen_prefixes, }) end if not heads[1] then heads = {{term = autohead}} else for _, headobj in ipairs(heads) do local head = headobj.term if head:find("^~") then head = apply_link_modifiers(autohead, head:sub(2), lang) headobj.term = head elseif head:find("^[!?]$") then -- If explicit head= just consists of ! or ?, add it to the end of the default head. headobj.term = autohead .. head end if head == autohead then track("redundant-head") end end end -- handle the=/def= if args.the == "~" then local newheads = {} for _, headobj in ipairs(heads) do local barehead = shallowCopy(headobj) insert(newheads, barehead) headobj.term = "the " .. headobj.term insert(newheads, headobj) end heads = newheads elseif args.the then local the = require(yesno_module)(args.the) if the then for _, headobj in ipairs(heads) do headobj.term = "the " .. headobj.term end end end local data = { lang = lang, pos_category = poscat, categories = {}, heads = heads, user_specified_heads = user_specified_heads, -- We use our own splitting algorithm so the redundant head cat will be inaccurate. no_redundant_head_cat = true, inflections = {}, nomultiwordcat = args.nomultiwordcat, sort_key = args.sort, pagename = pagename, id = args.id, force_cat_output = force_cat, } local function inscat(cat) insert(data.categories, langname .. " " .. cat_word(lang, cat)) end local is_suffix = false if args.suffix or not args.nosuffix and pagename:find("^%-") and not pagename:find("^%-%-") and poscat ~= "suffix forms" then is_suffix = true data.pos_category = "suffixes" local singular_poscat = singularize(poscat) inscat(singular_poscat .. "-forming suffixes") insert(data.inflections, {label = singular_poscat .. "-forming suffix"}) end if pos_func then pos_func(args, data, is_suffix) end local extra_categories = {} if pagename:find("[Qq]") then -- Check for q not followed by u. We want to exclude things like [[13q deletion syndrome]] and [[BFOQ]] that -- don't have a lowercase letter on either side, as well as things like [[& seq.]] and [[acq.]] that are -- abbreviations for words containing a following u. -- -- Approximate range of combining diacritics; we want to remove them so the checks below for -- a lowercase letter next to the q aren't tripped up by diacritics on the letter. local u300 = u(0x0300) local u36F = u(0x036F) local pagename_no_diacritics = ugsub(toNFD(pagename), "[" .. u300 .. "-" .. u36F .. "]", "") if pagename_no_diacritics:find("[Qq][a-tv-z]") or pagename_no_diacritics:find("[a-z]q[^u.]") or pagename_no_diacritics:find("[a-z]q$") then inscat("words containing Q not followed by U") end end -- toNFD performs decomposition, so letters that decompose to an ASCII -- vowel and a diacritic, such as é, are counted as vowels and do not do not -- need to be included in the pattern. if not umatch(ulower(toNFD(pagename)), "[aeiouyæœøəªºαεηιουω]") then inscat("words spelled wiþūtan vowels") end if pagename:find("yre$") then inscat('words ending in "-yre"') end if not pagename:find(" ") and ulen(pagename) >= 25 then insert(extra_categories, langname .. " " .. cat_word(lang, "Long words")) end if pagename:find("^[^aeiou ]*a[^aeiou ]*e[^aeiou ]*i[^aeiou ]*o[^aeiou ]*u[^aeiou ]*$") then inscat("words that use all vowels in alphabetical order") end parse_and_insert_inflection(data, args, "abbr", "abbreviation") if args.json then return toJSON(data) end return full_headword(data) .. (extra_categories[1] and format_categories(extra_categories, lang, args.sort) or "") end local function make_default_comparative(word) if word == "good" or word == "well" then return {"better"} elseif word == "bad" or word == "badly" then return {"worse"} elseif word == "far" then return {"further", "farther"} else return {add_suffix(word, "r")} end end local function make_default_superlative(word) if word == "good" or word == "well" then return {"best"} elseif word == "bad" or word == "badly" then return {"worst"} elseif word == "far" then return {"furthest", "farthest"} else return {add_suffix(word, "st.superlative")} end end -- This function does the common work between adjectives and adverbs. local function process_comparative_args(data, args, plpos) local pagename = data.pagename local comps = parse_inflection(args, 1) local sups = parse_inflection(args, "sup") local outcomps, outsups if args.componly then if comps[1] then error("Can't specify comparatives of comparative-only " .. plpos) end insert(data.inflections, {label = glossary_link("comparative") .. " form only"}) insert(data.categories, langname .. " " .. cat_word(lang, "comparative-only " .. plpos)) -- Set to empty list so we don't get any comparatives output, but process superlatives if specified. outcomps = {} if not sups[1] then -- Set to empty list so we don't get any superlatives output unless explicitly given. outsups = {} end elseif args.suponly then if comps[1] or sups[1] then error("Can't specify comparatives or superlatives of or superlative-only " .. plpos) end insert(data.inflections, {label = glossary_link("superlative") .. " form only"}) insert(data.categories, langname .. " " .. cat_word(lang, "superlative-only " .. plpos)) return end -- If the first parameter is ?, then don't show anything, just return. if comps[1] and comps[1].term == "?" then if comps[2] then error("Can't specify additional comparatives along wiþ '?'") end if sups[1] then error("Can't specify superlatives along wiþ '?' for the comparative") end return end if comps[1] and comps[1].term == "-" then local hyphencomp = remove(comps, 1) -- Remove the "-" but retain for decorations. -- Not (generally) comparable; may occasionally have a comparative if comps[1] then insert_fixed_inflection(data, "not generally <<comparable>>", hyphencomp) elseif not sups[1] then insert_fixed_inflection(data, "not <<comparable>>", hyphencomp) insert(data.categories, langname .. " " .. cat_word(lang, "uncomparable " .. plpos)) return else -- No comparative, but a superlative. insert_inflection() will correctly generate 'no comparative' if we -- pass in "-" as the value. outcomps = {hyphencomp} end elseif not comps[1] then comps = {{term = "more"}} end if not outcomps then -- not if we set `outcomps` to "-" above or processed a comparative-only term outcomps = {} -- Go over each parameter given and create a comparative and superlative form. for _, compobj in ipairs(comps) do local comp = compobj.term if comp == "-" then error("Comparative of '-' only allowed as first comparative") end if comp == "+" then comp = "+more" elseif comp == "more" and pagename ~= "many" and pagename ~= "much" then comp = "+more" elseif comp == "further" and pagename ~= "far" then comp = "+further" elseif comp == "better" and pagename ~= "good" and pagename ~= "well" then comp = "+better" elseif comp:find("~") then comp = comp:gsub("~", replacement_escape(pagename)) end compobj.origterm = comp if comp == "+more" then comp = "more [[" .. pagename .. "]]" elseif comp == "+further" then comp = {"further [[" .. pagename .. "]]", "farther [[" .. pagename .. "]]"} elseif comp == "+better" then comp = "better [[" .. pagename .. "]]" elseif comp == "er" then -- Add -er. comp = add_suffix(pagename, "r") elseif comp == "ier" then if pagename:sub(-1) ~= "y" then error("Can't specify 'ier' comparative unless the term ends wiþ 'y': " .. pagename) end comp = pagename:gsub("e?y$", "ier") elseif comp:find("^%+") then local special = m_headword_utilities.get_special_indicator(comp, "noerror") if special then comp = m_headword_utilities.handle_multiword(pagename, special, make_default_comparative) end end if type(comp) == "table" and not comp[2] then comp = comp[1] end if type(comp) == "table" then for i = 1, #comp - 1 do local outobj = shallowCopy(compobj) outobj.term = comp[i] insert(outcomps, outobj) end compobj.term = comp[#comp] insert(outcomps, compobj) else compobj.term = comp insert(outcomps, compobj) end end end if sups[1] and sups[1].term == "-" then if sups[2] then error("Can't specify '-' as superlative followed by further values") end -- No superlative. insert_inflection() will correctly generate 'no superlative' if we pass in "-" as the value. outsups = sups else if not sups[1] then sups = {{term = "+"}} end end -- `outsups` will be set if we set `outsups` to "-" above or processed a comparative-only term without superlatives. if not outsups then outsups = {} local function process_sup(sup, special, supobj, compobj) if special then sup = m_headword_utilities.handle_multiword(pagename, special, make_default_superlative) elseif sup == "-" or sup == "+" then error(("Internal error: Superlative value of '%s' should have been handled earlier"):format(sup)) elseif sup == "+most" then sup = "most [[" .. pagename .. "]]" elseif sup == "+furthest" then sup = {"furthest [[" .. pagename .. "]]", "farthest [[" .. pagename .. "]]"} elseif sup == "+best" then sup = "best [[" .. pagename .. "]]" elseif sup == "est" then -- Add -est. sup = add_suffix(pagename, "st.superlative") elseif sup == "iest" then if pagename:sub(-1) ~= "y" then error("Can't specify 'iest' superlative unless the term ends wiþ 'y': " .. pagename) end sup = pagename:gsub("e?y$", "iest") end if type(sup) == "table" and not sup[2] then sup = sup[1] end if compobj then supobj = shallowCopy(supobj) supobj = m_headword_utilities.combine_termobj_decorations(supobj, compobj) end if type(sup) == "table" then for i = 1, #sup - 1 do local outobj = shallowCopy(supobj) outobj.term = sup[i] insert(outsups, outobj) end supobj.term = sup[#sup] insert(outsups, supobj) else supobj.term = sup insert(outsups, supobj) end end for _, supobj in ipairs(sups) do local sup = supobj.term if sup == "-" then error("Superlative of '-' only allowed as first superlative") end if sup == "+" then if not comps[1] then error("Superlative of '+' can't be specified when there are no comparatives") end for _, compobj in ipairs(comps) do local comp = compobj.origterm local special if comp == "+more" then sup = "+most" elseif comp == "+further" then sup = "+furthest" elseif comp == "+better" then sup = "+best" elseif comp == "er" then sup = "est" elseif comp == "ier" then sup = "iest" else if comp:find("^%+") then special = m_headword_utilities.get_special_indicator(comp, "noerror") end if not special then -- If the full comparative was given, then derive the superlative by replacing -er with -- -est. if comp:sub(-2) == "er" then sup = comp:sub(1, -3) .. "est" else error(("The superlative cannot be derived automatically from comparative '%s' because it doesn't end in -er"):format(comp)) end end end process_sup(sup, special, supobj, compobj) end else local special = m_headword_utilities.get_special_indicator(sup, "noerror") -- Do some work here rather than in process_sup() so we don't end up double-processing a term with a '~' -- in it or a term that happens to be 'most' or similar after substitution of ~ in the comparative. if not special then if sup == "most" and pagename ~= "many" and pagename ~= "much" then sup = "+most" elseif sup == "furthest" and pagename ~= "far" then sup = "+furthest" elseif sup == "best" and pagename ~= "good" and pagename ~= "well" then sup = "+best" elseif sup:find("~") then sup = sup:gsub("~", replacement_escape(pagename)) end end process_sup(sup, special, supobj) end end end insert_inflection(data, outcomps, "<<comparative>>", "comparative") insert_inflection(data, outsups, "<<superlative>>", "superlative") end pos_functions["adjectives"] = { params = { [1] = list_param, ["comp_qual"] = {list = "comp\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the comparative value", }, ["sup"] = list_param, ["sup_qual"] = {list = "sup\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the superlative value", }, ["componly"] = boolean_param, ["suponly"] = boolean_param, }, func = function(args, data) -- Process the comparatives and superlatives. process_comparative_args(data, args, "adjectives") end, } pos_functions["adverbs"] = { params = { [1] = list_param, ["comp_qual"] = {list = "comp\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the comparative value", }, ["sup"] = list_param, ["sup_qual"] = {list = "sup\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the superlative value", }, ["componly"] = boolean_param, ["suponly"] = boolean_param, }, func = function(args, data) -- Process the comparatives and superlatives. process_comparative_args(data, args, "adverbs") end, } local function escape(str) return (str:gsub("\\([:#])", "\\\\%1") :gsub("[:#]", "\\%0")) end local function canonicalize_plural(pl, pagename, pos) if pl == "+" then return escape(add_suffix(pagename, "s.plural", pos)) elseif pl == "++" then return escape(compute_plusplus_s_form(pagename, add_suffix(pagename, "s.plural", pos))) elseif pl == "*" then return escape(pagename) elseif pl == "ies" then if pagename:sub(-1) == "y" then return escape(pagename:gsub("e?y$", pl)) end error("Can't specify 'ies' plural unless the term ends wiþ 'y'.") elseif pl == "s" or pl == "es" or pl == "'s" then return escape(pagename .. pl) end end local function do_nouns(args, data, pos) local pagename = data.pagename pos = pos or "noun" local plurals = parse_inflection(args, 1) local function insert_plurale_tantum_inflections(is_plural_only, originating_label) if args.sg[1] then insert_fixed_inflection(data, "normally plural", originating_label) parse_and_insert_inflection(data, args, "sg", "singular") elseif is_plural_only then insert_fixed_inflection(data, "plural only", originating_label) end if args.attr[1] then parse_and_insert_inflection(data, args, "attr", "attributive") end end local function first_pl_term() return plurals[1] and plurals[1].term or nil end if first_pl_term() == "p" then -- plurale tantum if plurals[2] then error("Wiþ plurale tantum nama, can't specify more than one plural") end data.genders = {"p"} -- this should auto-insert the correct 'pluralia tantum' category insert_plurale_tantum_inflections("plural only", plurals[1]) return end local function inscat(cat) insert(data.categories, langname .. " " .. cat_word(lang, cat)) end local need_default_plural = pos == "noun" if first_pl_term() == "sp" then -- construed as singular or plural sp = remove(plurals, 1) -- Remove the "sp" but retain it for its decorations. inscat("naman construed as singular or plural") data.genders = {"s", "p"} -- this should auto-insert the correct 'pluralia tantum' category insert_plurale_tantum_inflections(nil, sp) need_default_plural = false elseif first_pl_term() == "-" then -- Untellendlīc noun; may occasionally have a plural local hyphpl = remove(plurals, 1) -- Remove the "-" but retain for decorations. inscat("untellendlīc naman") -- If plural forms were given explicitly, then show "oft" if plurals[1] then insert_fixed_inflection(data, "oft <<untellendlīc>>", hyphpl) else insert_fixed_inflection(data, "<<untellendlīc>>", hyphpl) end need_default_plural = false elseif first_pl_term() == "#" then -- oft tellendlīc (e.g., "grilled cheese") local hashpl = remove(plurals, 1) -- Remove the "#" but retain for decorations. insert_fixed_inflection(data, "oft <<tellendlīc>>", hashpl) inscat("untellendlīc naman") inscat("tellendlīc naman") -- If no plural was given, add a default one now if not plurals[1] then plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end elseif first_pl_term() == "~" then -- Mixed tellendlīc/untellendlīc noun, always has a plural local tildepl = remove(plurals, 1) -- Remove the "~" but retain for decorations. insert_fixed_inflection(data, "<<tellendlīc>> and <<untellendlīc>>", tildepl) inscat("untellendlīc naman") inscat("tellendlīc naman") -- If no plural was given, add a default one now if not plurals[1] then plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end end -- Plural is unknown if first_pl_term() == "?" then local questionpl = remove(plurals, 1) -- Remove the "?" but retain for decorations. -- Not desired; see [[Wiktionary:Tea_room/2021/August#"Plural unknown or uncertain"]] -- insert_fixed_inflection(data, "manigfeald unġecnāwen oþþe ungewiss", questionpl) inscat("naman wiþ unġecnāwen oþþe ungewiss manigfealde") if plurals[1] then error("Ne cann āmearcian sweotole manigfealde andlang wiþ '?' of unġecnāwen/ungewiss manigfeald") end return end -- Plural is not attested if first_pl_term() == "!" then local exclampl = remove(plurals, 1) -- Remove the "!" but retain for decorations. insert_fixed_inflection(data, "plural not attested", exclampl) inscat("naman wiþ unattested manigfealde") if plurals[1] then error("Ne cann āmearcian sweotole manigfealde andlang wiþ '!' of gecȳþed manigfeald") end return end -- If no plural was given, maybe add a default one, otherwise (when "-" was given or proper noun) return. if not plurals[1] then if not need_default_plural then inscat("untellendlīc naman") return end plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end -- There are plural forms to show, so show them. inscat("tellendlīc naman") local irregular, indeclinable for i, pl in ipairs(plurals) do local canon_pl = canonicalize_plural(pl.term, pagename, pos) if canon_pl then pl.term = canon_pl end local pl_term = get_link_page(pl.term, lang) if not (pagename:find(" ") or is_regular_plural(pl_term, pagename)) then irregular = true if pl_term == pagename then indeclinable = true end end end if irregular then inscat("naman wiþ fremde manigfealde") end if indeclinable then inscat("undeclīnigendlīce naman") end insert_inflection(data, plurals, "plural", "p") end -- Return the parameters to be used for nouns and proper nouns. Currently the same. local noun_params = { [1] = list_param, ["pl\1qual"] = {list = true, allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the plural", }, -- The following four only used for pluralia tantum (1=p) ["sg"] = list_param, ["attr"] = list_param, } pos_functions["naman"] = { params = noun_params, func = do_naman, } pos_functions["agennaman"] = { params = noun_params, func = function(args, data) return do_nouns(args, data, "agennama") end, } local function base_default_verb_forms(verb) return escape(add_suffix(verb, "s.verb")), escape(add_suffix(verb, "ing")), escape(add_suffix(verb, "d")) end local function default_verb_forms(verb) local full_s_form, full_ing_form, full_ed_form = base_default_verb_forms(verb) if verb:find(" ") then local first, rest = verb:match("^(.-)( .*)$") local first_s_form, first_ing_form, first_ed_form = base_default_verb_forms(first) return full_s_form, full_ing_form, full_ed_form, first_s_form .. rest, first_ing_form .. rest, first_ed_form .. rest, first, rest else return full_s_form, full_ing_form, full_ed_form, nil, nil, nil, nil, nil end end local function compute_double_last_cons_stem_of_split_verb(verb, ending) local first, rest = verb:match("^(.-)( .*)$") if not first then error("Word '" .. verb .. "' mōt hæfþ rum in sīde tō notienne **") end local last_cons = first:match("([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])$") if not last_cons then error("Fyrest word '" .. first .. "' mōt ende wiþ samod swēgend tō notienne **") end return first .. last_cons .. ending .. rest end local function check_non_nil_star_form(form, pagename) if form == nil then error("Word '" .. pagename .. "' mōt hæfþ rum in sīde tō notienne *, **, *l, *! or *'") end return form end local function sub_tilde(form, pagename) if not form then return nil end if form:find("~") then form = form:gsub("~", replacement_escape(pagename)) end return form end local deprecated_qual_replaced_by_inline_modifier = { list = true, allow_holes = true, replaced_by = false, instead = "use an inline modifier <q:...> or <l:...> on the value" } pos_functions["verbs"] = { params = { [1] = {list = "pres_3sg", disallow_holes = true}, ["pres_3sg\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [2] = {list = "pres_ptc", disallow_holes = true}, ["pres_ptc\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [3] = {list = "past", disallow_holes = true}, ["past\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [4] = {list = "past_ptc", allow_holes = true}, ["past_ptc\1_qual"] = deprecated_qual_replaced_by_inline_modifier, ["noautolinkverb"] = boolean_param, ["angle_bracket"] = boolean_param, }, func = function(args, data) -- Get parameters local par1s local par2s = parse_inflection(args, {2, "pres_ptc"}) local par3s = parse_inflection(args, {3, "past"}) local par4s = parse_inflection(args, {4, "past_ptc"}) local pres_3sgs, pres_ptcs, pasts, past_ptcs local pagename = data.pagename ------------------------------------------- UTILITY FUNCTIONS #2 ------------------------------------------ -- These functions are used in both in the separate-parameter format and in the override params such as past_ptc2=. local full_default_s, full_default_ing, full_default_ed, split_default_s, split_default_ing, split_default_ed local lemma local function set_lemma_and_default_forms(the_lemma) lemma = the_lemma full_default_s, full_default_ing, full_default_ed, split_default_s, split_default_ing, split_default_ed, lemma_first, lemma_rest = default_verb_forms(the_lemma) end local function canonicalize_s_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_s elseif form == "*" then return check_non_nil_star_form(split_default_s, lemma) elseif form == "++" then return compute_plusplus_s_form(lemma, full_default_s) elseif form == "**" then if lemma:find("^[^ ]*[szx] ") then return compute_double_last_cons_stem_of_split_verb(lemma, "es") else return check_non_nil_star_form(split_default_s, lemma) end elseif form == "+!" then return lemma .. "s" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "s" .. lemma_rest elseif form == "+'" then return lemma .. "'s" elseif form == "*'" then return check_non_nil_star_form(lemma_first) .. "'s" .. lemma_rest elseif form == "+l" then if lemma:find("[szx]$") then return {{term = full_default_s, l = {"US"}}, {term = compute_plusplus_s_form(lemma, full_default_s), l = {"UK"}}} else return compute_plusplus_s_form(lemma, full_default_s) end elseif form == "*l" then if lemma:find("^[^ ]*[szx] ") then return {{term = check_non_nil_star_form(split_default_s, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "es"), l = {"UK"}}} else return check_non_nil_star_form(split_default_s, lemma) end else return sub_tilde(form, lemma) end end local function canonicalize_ing_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_ing elseif form == "*" then return check_non_nil_star_form(split_default_ing, lemma) elseif form == "++" then return compute_double_last_cons_stem(lemma) .. "ing" elseif form == "**" then return compute_double_last_cons_stem_of_split_verb(lemma, "ing") elseif form == "+!" then return lemma .. "ing" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "ing" .. lemma_rest elseif form == "+'" then return lemma .. "'ing" elseif form == "*'" then return check_non_nil_star_form(lemma_first) .. "'ing" .. lemma_rest elseif form == "+l" then return {{term = full_default_ing, l = {"US"}}, {term = compute_double_last_cons_stem(lemma) .. "ing", l = {"UK"}}} elseif form == "*l" then return {{term = check_non_nil_star_form(split_default_ing, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "ing"), l = {"UK"}}} else return sub_tilde(form, lemma) end end local function canonicalize_ed_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_ed elseif form == "*" then return check_non_nil_star_form(split_default_ed, lemma) elseif form == "++" then return compute_double_last_cons_stem(lemma) .. "ed" elseif form == "+!" then return lemma .. "ed" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "ed" .. lemma_rest elseif form == "+'" then return {{term = lemma .. "'d"}, {term = lemma .. "'ed"}} elseif form == "*'" then return {{term = check_non_nil_star_form(lemma_first) .. "'d" .. lemma_rest}, {term = check_non_nil_star_form(lemma_first) .. "'ed" .. lemma_rest}} elseif form == "**" then return compute_double_last_cons_stem_of_split_verb(lemma, "ed") elseif form == "+l" then return {{term = full_default_ed, l = {"US"}}, {term = compute_double_last_cons_stem(lemma) .. "ed", l = {"UK"}}} elseif form == "*l" then return {{term = check_non_nil_star_form(split_default_ed, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "ed"), l = {"UK"}}} else return sub_tilde(form, lemma) end end -- FIXME: options should be "+", "*", "++", "**", "+n", "*n", "++n" and "**n", but not "n" local function canonicalize_en_form(form) if form == "n" then track("n4") return add_suffix(lemma, "n") end return canonicalize_ed_form(form) end --------------------------------- MAIN PARSING/CONJUGATING CODE -------------------------------- local is_angle_bracket = args.angle_bracket if is_angle_bracket then if par2s[1] or par3s[1] or par4s[1] then error("Can't specify explicit values for 2=, 3= or 4= along wiþ the angle-bracket format") end elseif is_angle_bracket == nil and not par2s[1] and not par3s[1] and not par4s[1] and not args[1][2] and args[1][1] and args[1][1]:find("<") then if put.term_contains_top_level_html(args[1][1]) then -- Often, term_contains_top_level_html() returns true on the angle-bracket format, which would -- make the pcall() below succeed but leave the angle brackets as-is. Check for this and only do the -- pcall() if term_contains_top_level_html() returns false. is_angle_bracket = true else -- If it's ambiguous whether it's an angle-bracket format or separate params with an inline modifier, -- try to parse as the latter. If an error occurs, treat as the former. local ok ok, par1s = pcall(parse_inflection, args, {1, "pres_3sg"}) if not ok then par1s = nil is_angle_bracket = true end end end if is_angle_bracket then -------------------------- ANGLE-BRACKET FORMAT -------------------------- -- (0) Expand multiword term with angle brackets just on the first word. local arg11 = args[1][1] if arg11:find("^<.*>$") and pagename:find(" ") then local first, rest = pagename:match("^(.-)( .*)$") arg11 = first .. arg11 .. rest end -- (1) Parse the indicator specs inside of angle brackets. local function parse_indicator_spec(angle_bracket_spec) local inside = angle_bracket_spec:match("^<(.*)>$") assert(inside) local segments = put.parse_balanced_segment_run(inside, "[", "]") local comma_separated_groups = put.split_alternating_runs(segments, ",") if #comma_separated_groups > 4 then error("Too many comma-separated parts in indicator spec, expected at most 4: " .. angle_bracket_spec) end local function fetch_footnotes(separated_group) local footnotes for j = 2, #separated_group - 1, 2 do if separated_group[j + 1] ~= "" then error("Extraneous text after bracketed footnotes: '" .. concat(separated_group) .. "'") end if not footnotes then footnotes = {} end insert(footnotes, separated_group[j]) end return footnotes end local function fetch_specs(comma_separated_group) if not comma_separated_group then return {{term = "+"}} end local specs = {} local colon_separated_groups = put.split_alternating_runs(comma_separated_group, ":") for _, colon_separated_group in ipairs(colon_separated_groups) do local form = colon_separated_group[1] if form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then error("*, **, *l, *! and *' not allowed inside of indicator specs: " .. angle_bracket_spec) end if form == "" then form = "+" end local termobj = { term = form } local footnotes = fetch_footnotes(colon_separated_group) if footnotes then for _, footnote in ipairs(footnotes) do m_headword_utilities.add_footnote_to_termobj(termobj, footnote) end end insert(specs, termobj) end return specs end local s_specs = fetch_specs(comma_separated_groups[1]) local ing_specs = fetch_specs(comma_separated_groups[2]) local ed_specs = fetch_specs(comma_separated_groups[3]) local en_specs = fetch_specs(comma_separated_groups[4]) return { forms = {}, s_specs = s_specs, ing_specs = ing_specs, ed_specs = ed_specs, en_specs = en_specs, } end local parse_props = { parse_indicator_spec = parse_indicator_spec, } local alternant_multiword_spec = iut.parse_inflected_text(arg11, parse_props) -- (2) Check for user-specified brackets; remove any links from the lemma, but remember the original -- form so we can use it below in the 'lemma_linked' form. -- Check to see if there are brackets in the pre-text or post-text. If so, use the linked lemma (with the -- verb autolinked unless noautolinkverb is given). Otherwise, use the default headword algorithm. local function check_bracket(val) if val:find("%[%[") then alternant_multiword_spec.saw_bracket = true end end for _, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do check_bracket(alternant_or_word_spec.before_text) if alternant_or_word_spec.alternants then for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do for _, word_spec in ipairs(multiword_spec.word_specs) do check_bracket(word_spec.before_text) end check_bracket(multiword_spec.post_text) end end end check_bracket(alternant_multiword_spec.post_text) iut.map_word_specs(alternant_multiword_spec, function(base) if base.lemma == "" then base.lemma = pagename end base.orig_lemma = base.lemma base.lemma = remove_links(base.lemma) if args.noautolinkverb or base.orig_lemma:find("%[%[") then base.linked_lemma = base.orig_lemma else base.linked_lemma = "[[" .. base.orig_lemma .. "]]" end end) -- (3) Conjugate the verbs according to the indicator specs parsed above. local all_verb_slots = { lemma = "infinitive", lemma_linked = "infinitive", s_form = "3|s|pres", ing_form = "pres|ptcp", ed_form = "past", en_form = "past|ptcp", } local function conjugate_verb(base) local function process_specs(slot, specs, canon_func, default_values, default_already_formobj) local function insert_termobj_into_slot(termobj) local formobj = m_headword_utilities.convert_termobj_to_formobj(termobj) -- If the form is -, don't insert any forms, which will result in there being no overall forms -- (in fact it will be nil). We check for that down below and substitute a single "-" as the -- form, which in turn gets turned into special labels like "no present participle". if formobj.form == "-" then if formobj.footnotes then error("Unable to preserve footnotes specified on missing form '-': FIXME: " .. dump(formobj.footnotes)) end else iut.insert_form(base.forms, slot, formobj) end end local function canonicalize_and_insert(arg) local canon_arg = canon_func(arg) if type(canon_arg) == "string" then arg.term = canon_arg insert_termobj_into_slot(arg) else for _, canon in ipairs(canon_arg) do m_headword_utilities.combine_termobj_decorations(canon, arg) insert_termobj_into_slot(canon) end end end for _, arg in ipairs(specs) do if arg.term == "+" then if default_values then -- will be nil if past tense specified as - and no past ptc given for _, val in ipairs(default_values) do val = shallowCopy(val) if default_already_formobj then local argformobj = m_headword_utilities.convert_termobj_to_formobj(arg) val.footnotes = iut.combine_footnotes(val.footnotes, argformobj.footnotes) iut.insert_form(base.forms, slot, val) else m_headword_utilities.combine_termobj_decorations(val, arg) canonicalize_and_insert(val) end end end else canonicalize_and_insert(arg) end end end set_lemma_and_default_forms(base.lemma) local all_part_default_specs = {} local function process_and_canonicalize_s_form(arg) local form = arg.term if form == "+" then error("Internal error: '+' should have been converted to '^' by now") end if form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then error(("Internal error: '%s' should have already thrown an error"):format(form)) end if form == "^" or form == "++" or form == "+l" or form == "+!" or form == "+'" then insert(all_part_default_specs, shallowCopy(arg)) end return canonicalize_s_form(form) end process_specs("s_form", base.s_specs, process_and_canonicalize_s_form, {{term = "^"}}) if not all_part_default_specs[1] then all_part_default_specs[1] = {term = "^"} end process_specs("ing_form", base.ing_specs, function(arg) return canonicalize_ing_form(arg.term) end, all_part_default_specs) process_specs("ed_form", base.ed_specs, function(arg) return canonicalize_ed_form(arg.term) end, all_part_default_specs) process_specs("en_form", base.en_specs, function(arg) return canonicalize_en_form(arg.term) end, base.forms.ed_form, "default already formobj") iut.insert_form(base.forms, "lemma", {form = base.lemma}) -- Add linked version of lemma for use in head=. We write this in a general fashion in case -- there are multiple lemma forms (which isn't possible currently at this level, although it's -- possible overall using the ((...,...)) notation). iut.insert_forms(base.forms, "lemma_linked", iut.map_forms(base.forms.lemma, function(form) if form == base.lemma and base.linked_lemma:find("%[%[") then return base.linked_lemma else return form end end)) end local inflect_props = { slot_table = all_verb_slots, inflect_word_spec = conjugate_verb, } iut.inflect_multiword_or_alternant_multiword_spec(alternant_multiword_spec, inflect_props) -- (4) Fetch the forms and put the conjugated lemmas in data.heads if not explicitly given. local function fetch_termobjs(slot) local forms = alternant_multiword_spec.forms[slot] -- See above. This should only occur if the user explicitly used - for a spec. if not forms or not forms[1] then return {{term = "-"}} end local termobjs = {} for _, formobj in ipairs(forms) do insert(termobjs, m_headword_utilities.convert_formobj_to_termobj(formobj)) end return termobjs end pres_3sgs = fetch_termobjs("s_form") pres_ptcs = fetch_termobjs("ing_form") pasts = fetch_termobjs("ed_form") past_ptcs = fetch_termobjs("en_form") -- Use the "linked" form of the lemma as the head if no head= explicitly given and the user specified -- brackets in one of the lemmas. Otherwise we use the default headword-linking algorithm. if not data.user_specified_heads[1] and alternant_multiword_spec.saw_bracket then data.heads = {} for _, lemma_obj in ipairs(alternant_multiword_spec.forms.lemma_linked) do insert(data.heads, m_headword_utilities.convert_formobj_to_termobj(lemma_obj)) end end else -------------------------- SEPARATE-PARAM FORMAT -------------------------- set_lemma_and_default_forms(pagename) par1s = par1s or parse_inflection(args, {1, "pres_3sg"}) pres_3sgs = {} pres_ptcs = {} pasts = {} past_ptcs = {} if not par1s[1] then par1s = {{term = "+"}} end if not par2s[1] then par2s = {{term = "+"}} end if not par3s[1] then par3s = {{term = "+"}} end if not par4s[1] then par4s = {{term = "+"}} end local function process_argument(args, dest, canon_func, default_values, default_already_canonicalized) local function canonicalize_and_insert(arg) local canon_arg = canon_func(arg) if type(canon_arg) == "string" then arg.term = canon_arg m_headword_utilities.insert_termobj_combining_duplicates(dest, arg) else for _, canon in ipairs(canon_arg) do m_headword_utilities.combine_termobj_decorations(canon, arg) m_headword_utilities.insert_termobj_combining_duplicates(dest, canon) end end end for _, arg in ipairs(args) do if arg.term == "+" then for _, val in ipairs(default_values) do val = shallowCopy(val) m_headword_utilities.combine_termobj_decorations(val, arg) if default_already_canonicalized then m_headword_utilities.insert_termobj_combining_duplicates(dest, val) else canonicalize_and_insert(val) end end else canonicalize_and_insert(arg) end end end local all_part_default_specs = {} local function process_and_canonicalize_s_form(arg) local form = arg.term if form == "+" then error("Internal error: '+' should have been converted to '^' by now") end if form == "^" or form == "++" or form == "+l" or form == "+!" or form == "+'" or form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then insert(all_part_default_specs, shallowCopy(arg)) end return canonicalize_s_form(form) end process_argument(par1s, pres_3sgs, process_and_canonicalize_s_form, {{term = "^"}}) if not all_part_default_specs[1] then all_part_default_specs[1] = {term = "^"} end process_argument(par2s, pres_ptcs, function(arg) return canonicalize_ing_form(arg.term) end, all_part_default_specs) process_argument(par3s, pasts, function(arg) return canonicalize_ed_form(arg.term) end, all_part_default_specs) process_argument(par4s, past_ptcs, function(arg) return canonicalize_en_form(arg.term) end, pasts, "default already canonicalized") end ------------------------------------------- INSERT INFLECTIONS ------------------------------------------ insert_inflection(data, pres_3sgs, "third-person singular simple present", "s-verb-form") insert_inflection(data, pres_ptcs, "present participle", "ing-form") if deepEquals(pasts, past_ptcs) then insert_inflection(data, pasts, "simple past and past participle", "ed-form", "no simple past or past participle") else insert_inflection(data, pasts, "simple past", "spast") insert_inflection(data, past_ptcs, "past participle", "past|part") end if pagename:find(" ") then -- Check for placeholder "it" local words = split(pagename, " ") for _, word in ipairs(words) do if word == "it" or word == "its" or word == "it's" then insert(data.categories, langname .. ' ' .. cat_word(lang, 'terms with placeholder "it"')) break end end -- Check for phrasal verbs local phrasal_adverbs = list_to_set{ -- NOTE: This should only contain common phrasal adverbs, not random words like [[low]], -- [[adrift]], etc. "aback", "about", "above", "across", "after", "against", "ahead", "along", "apart", "around", "as", "aside", "at", "away", "back", "before", "behind", "below", "between", "beyond", "by", "down", "for", "forth", "from", "in", "into", "of", "off", "on", "onto", "out", "over", "past", "round", "through", "to", "together", "towards", "under", "up", "upon", "with", "without", } local allowed_non_adverb_words = list_to_set{ "it", "one", "oneself", "someone", } local base = pagename local seen_adverbs = {} -- Only consider a verb to be phrasal if it consists of a single base verb followed exclusively by either -- adverbs from `phrasal_adverbs` or placeholder words from `allowed_non_adverb_words`, where at -- least one following word is from `phrasal_adverbs` (hence [[can it]] is not a phrasal verb). while true do local prev, word = base:match("^(.+) (.-)$") if not prev then break end if phrasal_adverbs[word] then insert(seen_adverbs, word) elseif allowed_non_adverb_words[word] then -- do nothing else break end base = prev end if not base:find(" ") and seen_adverbs[1] then insert(data.categories, langname .. " " .. cat_word(lang, "phrasal verbs")) for i = #seen_adverbs, 1, -1 do insert(data.categories, langname .. ' ' .. cat_word(lang, "phrasal verbs formed with") .. ' "' .. seen_adverbs[i] .. '\"') end end end end, } ----------------------------------------------------------------------------------------- -- Suffix forms -- ----------------------------------------------------------------------------------------- pos_functions["suffix forms"] = { params = { [1] = {required = true, list = true, disallow_holes = true}, }, func = function(args, data, is_suffix) local suffix_type = {} for _, typ in ipairs(args[1]) do insert(suffix_type, typ .. "-forming suffix") end insert(data.inflections, {label = "non-lemma form of " .. m_table.serialCommaJoin(suffix_type, {conj = "or"})}) end, } return export flfnyrsa9a9yvant9ksx7owv6yhgvye 55051 55050 2026-09-30T20:15:04Z Deadend0914 7211 55051 Scribunto text/plain local export = {} local pos_functions = {} --[==[ Author from 2020 on: mostly Benwing2, with significant contributions from Theknightwho. Based on a prior version by Rua (by now mostly rewritten), with contributions from Erutuon and others (see history for full attribution). ]==] local force_cat = false -- for testing; if true, categories appear in non-mainspace pages local require = require local require_when_needed = require("Module:require when needed") local en_utilities_module = "Module:en-utilities" local headword_utilities_module = "Module:headword utilities" local headword_module = "Module:headword" local inflection_utilities_module = "Module:inflection utilities" local parse_utilities_module = "Module:parse utilities" local JSON_module = "Module:JSON" local labels_module = "Module:labels" local links_module = "Module:links" local parameters_module = "Module:parameters" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local utilities_module = "Module:utilities" local yesno_module = "Module:yesno" local iut = require_when_needed(inflection_utilities_module) local put = require_when_needed(parse_utilities_module) local m_headword_utilities = require_when_needed(headword_utilities_module) local add_links_to_multiword_term = require_when_needed(headword_utilities_module, "add_links_to_multiword_term") local add_suffix = require_when_needed(en_utilities_module, "add_suffix") local apply_link_modifiers = require_when_needed(headword_utilities_module, "apply_link_modifiers") local concat = table.concat local deepEquals = require_when_needed(table_module, "deepEquals") local dump = mw.dumpObject local format_categories = require_when_needed(utilities_module, "format_categories") local full_headword = require_when_needed(headword_module, "full_headword") local get_label_info = require_when_needed(labels_module, "get_label_info") local get_link_page = require_when_needed(links_module, "get_link_page") local glossary_link = require_when_needed(headword_utilities_module, "glossary_link") local insert = table.insert local insertIfNot = require_when_needed(table_module, "insertIfNot") local ipairs = ipairs local is_regular_plural = require_when_needed(en_utilities_module, "is_regular_plural") local list_to_set = require_when_needed(table_module, "listToSet") local pairs = pairs local process_params = require_when_needed(parameters_module, "process") local remove = table.remove local remove_links = require_when_needed(links_module, "remove_links") local replacement_escape = require_when_needed(string_utilities_module, "replacement_escape") local shallowCopy = require_when_needed(table_module, "shallowCopy") local singularize = require_when_needed(en_utilities_module, "singularize") local split = require_when_needed(string_utilities_module, "split") local toJSON = require_when_needed(JSON_module, "toJSON") local toNFD = mw.ustring.toNFD local type = type local ulen = require_when_needed(string_utilities_module, "len") local ulower = require_when_needed(string_utilities_module, "lower") local umatch = require_when_needed(string_utilities_module, "match") local u = require_when_needed(string_utilities_module, "char") local ugsub = require_when_needed(string_utilities_module, "gsub") local lang = require("Module:languages").getByCode("en") local langname = lang:getCanonicalName() local list_param = {list = true, disallow_holes = true} local list_allow_holes = {list = true, allow_holes = true} local boolean_param = {type = "boolean"} local function ine(val) if val == "" then return nil else return val end end local function track(page) require("Module:debug/track")("en-headword/" .. page) return true end ------------------------------------------- UTILITY FUNCTIONS ------------------------------------------ -- Parse and return an inflection not requiring additional processing. The raw arguments come from `args[field]`, which -- is parsed for inline modifiers. local function parse_inflection(args, field, is_head) local argfield = field if type(argfield) == "table" then argfield = argfield[1] end return m_headword_utilities.parse_term_list_with_modifiers { paramname = field, forms = args[argfield], splitchar = ",", is_head = is_head, } end -- Insert the parsed inflections in `terms` (as parsed by `parse_inflection`) into `data.inflections`, with label -- `label` and optional accelerator spec `accel`. local function insert_inflection(data, terms, label, accel, no_label) for _, termobj in ipairs(terms) do m_headword_utilities.remove_termobj_field_modifiers(termobj) end m_headword_utilities.insert_inflection { headdata = data, terms = terms, label = label, no_label = no_label, accel = accel and {form = accel} or nil, } end -- Insert a fixed label `label` into the inflections for `data`. If `originating_term` is supplied, copy the decorations -- from it into the fixed label. local function insert_fixed_inflection(data, label, originating_term) m_headword_utilities.insert_fixed_inflection { headdata = data, originating_term = originating_term, label = label, } 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 -- These functions are used directly in the <> format as well as in the utility functions #2 below. local function compute_double_last_cons_stem(term) local last_cons = term:match("([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])$") if not last_cons then error("Verb stem '" .. term .. "' must end in a consonant to use ++") end return term .. last_cons end local function compute_plusplus_s_form(term, default_s_form) if term:find("[szx]$") then -- regas -> regasses, derez -> derezzes return compute_double_last_cons_stem(term) .. "es" else return default_s_form end end -- The main entry point. -- This is the only function that can be invoked from a template. function export.show(frame) local iparams = { [1] = true, } local iargs = require("Module:parameters").process(frame.args, iparams) local parargs = frame:getParent().args local poscat = iargs[1] local pos_in_1 = not poscat if pos_in_1 then poscat = ine(parargs[1]) or mw.title.getCurrentTitle().fullText == "Template:en-head" and "interjection" or error("Part of speech must be specified in 1=") poscat = require(headword_module).canonicalize_pos(poscat) end local indexing_poscat = pos_in_1 and "head" or poscat local params = { ["head"] = list_param, ["id"] = true, ["json"] = boolean_param, ["sort"] = true, ["splithyph"] = boolean_param, ["nosplithyph"] = boolean_param, ["hyphspace"] = boolean_param, ["nolink"] = boolean_param, ["nolinkhead"] = {type = "boolean_param", alias_of = "nolink"}, ["suffix"] = boolean_param, ["nosuffix"] = boolean_param, ["nomultiwordcat"] = boolean_param, ["abbr"] = list_param, ["the"] = true, ["def"] = {alias_of = "the"}, ["pagename"] = true, -- for testing } if pos_in_1 then params[1] = {required = true} -- required but ignored as already processed above end local pos_data = pos_functions[indexing_poscat] local pos_func if pos_data then local pos_params = pos_data.params if pos_params then for key, val in pairs(pos_params) do params[key] = val end end pos_func = pos_data.func end local args = process_params(parargs, params) -- Account for unsupported titles, e.g. 'C|N>K' instead of 'Unsupported titles/C through N to K'. local pagename = args.pagename or mw.loadData("Module:headword/data").pagename local user_specified_heads = parse_inflection(args, "head", "is_head") local heads = user_specified_heads local autohead if args.nolink or not pagename:find("[ '%-]") then autohead = pagename else local en_no_split_apostrophe_words = list_to_set { "one's", "someone's", "he's", "she's", "it's", } local en_include_hyphen_prefixes = list_to_set { -- We don't include things that are also words even though they are often (perhaps mostly) prefixes, e.g. -- "be", "counter", "cross", "extra", "half", "mid", "over", "pan", "under". "acro", "acousto", "Afro", "agro", "anarcho", "angio", "Anglo", "ante", "anti", "arch", "auto", "bi", "bio", "cis", "co", "cryo", "crypto", "de", "demi", "eco", "electro", "Euro", "ex", "Greco", "hemi", "hydro", "hyper", "hypo", "infra", "Indo", "inter", "intra", "Judeo", "macro", "meta", "micro", "mini", "multi", "neo", "neuro", "non", "para", "peri", "post", "pre", "pro", "proto", "pseudo", "re", "semi", "sub", "super", "trans", "un", "vice", } local function is_english(term) local title = mw.title.new(term) if title and title.exists then local content = title:getContent() if content and content:find("==English==\n") then return true end end return false end local function en_split_hyphen_when_space(word) if not word:find("-", nil, true) then return nil end if args.hyphspace then return "[[" .. word:gsub("%-+", " ") .. "|" .. word .. "]]" end if args.nosplithyph then return "[[" .. word .. "]]" end if not args.splithyph then local space_word = word:gsub("%-+", " ") if is_english(space_word) then return "[[" .. space_word .. "|" .. word .. "]]" end if is_english(word) then return "[[" .. word .. "]]" end end return nil end local function en_split_apostrophe(word) local base = word:match("^(.*)'s$") if base then return "[[" .. base .. "]][[-'s|'s]]" end -- Only treat final apostrophe as possessive if preceded by something that looks like a plural ending in /z/. -- In particular we don't want to do it for words like [[truckin']]. base = word:match("^(.*[sxz])'$") if base then if base:find("s$") then local sg = singularize(base) if is_english(sg) then return "[[" .. sg .. "|" .. base .. "]][[-'|']]" end end return "[[" .. base .. "]][[-'|']]" end return "[[" .. word .. "]]" end autohead = add_links_to_multiword_term(pagename, { split_hyphen_when_space = en_split_hyphen_when_space, split_apostrophe = en_split_apostrophe, no_split_apostrophe_words = en_no_split_apostrophe_words, include_hyphen_prefixes = en_include_hyphen_prefixes, }) end if not heads[1] then heads = {{term = autohead}} else for _, headobj in ipairs(heads) do local head = headobj.term if head:find("^~") then head = apply_link_modifiers(autohead, head:sub(2), lang) headobj.term = head elseif head:find("^[!?]$") then -- If explicit head= just consists of ! or ?, add it to the end of the default head. headobj.term = autohead .. head end if head == autohead then track("redundant-head") end end end -- handle the=/def= if args.the == "~" then local newheads = {} for _, headobj in ipairs(heads) do local barehead = shallowCopy(headobj) insert(newheads, barehead) headobj.term = "the " .. headobj.term insert(newheads, headobj) end heads = newheads elseif args.the then local the = require(yesno_module)(args.the) if the then for _, headobj in ipairs(heads) do headobj.term = "the " .. headobj.term end end end local data = { lang = lang, pos_category = poscat, categories = {}, heads = heads, user_specified_heads = user_specified_heads, -- We use our own splitting algorithm so the redundant head cat will be inaccurate. no_redundant_head_cat = true, inflections = {}, nomultiwordcat = args.nomultiwordcat, sort_key = args.sort, pagename = pagename, id = args.id, force_cat_output = force_cat, } local function inscat(cat) insert(data.categories, langname .. " " .. cat) end local is_suffix = false if args.suffix or not args.nosuffix and pagename:find("^%-") and not pagename:find("^%-%-") and poscat ~= "suffix forms" then is_suffix = true data.pos_category = "suffixes" local singular_poscat = singularize(poscat) inscat(singular_poscat .. "-forming suffixes") insert(data.inflections, {label = singular_poscat .. "-forming suffix"}) end if pos_func then pos_func(args, data, is_suffix) end local extra_categories = {} if pagename:find("[Qq]") then -- Check for q not followed by u. We want to exclude things like [[13q deletion syndrome]] and [[BFOQ]] that -- don't have a lowercase letter on either side, as well as things like [[& seq.]] and [[acq.]] that are -- abbreviations for words containing a following u. -- -- Approximate range of combining diacritics; we want to remove them so the checks below for -- a lowercase letter next to the q aren't tripped up by diacritics on the letter. local u300 = u(0x0300) local u36F = u(0x036F) local pagename_no_diacritics = ugsub(toNFD(pagename), "[" .. u300 .. "-" .. u36F .. "]", "") if pagename_no_diacritics:find("[Qq][a-tv-z]") or pagename_no_diacritics:find("[a-z]q[^u.]") or pagename_no_diacritics:find("[a-z]q$") then inscat("words containing Q not followed by U") end end -- toNFD performs decomposition, so letters that decompose to an ASCII -- vowel and a diacritic, such as é, are counted as vowels and do not do not -- need to be included in the pattern. if not umatch(ulower(toNFD(pagename)), "[aeiouyæœøəªºαεηιουω]") then inscat("words spelled wiþūtan vowels") end if pagename:find("yre$") then inscat('words ending in "-yre"') end if not pagename:find(" ") and ulen(pagename) >= 25 then insert(extra_categories, "Long " .. langname .. " words") end if pagename:find("^[^aeiou ]*a[^aeiou ]*e[^aeiou ]*i[^aeiou ]*o[^aeiou ]*u[^aeiou ]*$") then inscat("words that use all vowels in alphabetical order") end parse_and_insert_inflection(data, args, "abbr", "abbreviation") if args.json then return toJSON(data) end return full_headword(data) .. (extra_categories[1] and format_categories(extra_categories, lang, args.sort) or "") end local function make_default_comparative(word) if word == "good" or word == "well" then return {"better"} elseif word == "bad" or word == "badly" then return {"worse"} elseif word == "far" then return {"further", "farther"} else return {add_suffix(word, "r")} end end local function make_default_superlative(word) if word == "good" or word == "well" then return {"best"} elseif word == "bad" or word == "badly" then return {"worst"} elseif word == "far" then return {"furthest", "farthest"} else return {add_suffix(word, "st.superlative")} end end -- This function does the common work between adjectives and adverbs. local function process_comparative_args(data, args, plpos) local pagename = data.pagename local comps = parse_inflection(args, 1) local sups = parse_inflection(args, "sup") local outcomps, outsups if args.componly then if comps[1] then error("Can't specify comparatives of comparative-only " .. plpos) end insert(data.inflections, {label = glossary_link("comparative") .. " form only"}) insert(data.categories, langname .. " comparative-only " .. plpos) -- Set to empty list so we don't get any comparatives output, but process superlatives if specified. outcomps = {} if not sups[1] then -- Set to empty list so we don't get any superlatives output unless explicitly given. outsups = {} end elseif args.suponly then if comps[1] or sups[1] then error("Can't specify comparatives or superlatives of or superlative-only " .. plpos) end insert(data.inflections, {label = glossary_link("superlative") .. " form only"}) insert(data.categories, langname .. " superlative-only " .. plpos) return end -- If the first parameter is ?, then don't show anything, just return. if comps[1] and comps[1].term == "?" then if comps[2] then error("Can't specify additional comparatives along wiþ '?'") end if sups[1] then error("Can't specify superlatives along wiþ '?' for the comparative") end return end if comps[1] and comps[1].term == "-" then local hyphencomp = remove(comps, 1) -- Remove the "-" but retain for decorations. -- Not (generally) comparable; may occasionally have a comparative if comps[1] then insert_fixed_inflection(data, "not generally <<comparable>>", hyphencomp) elseif not sups[1] then insert_fixed_inflection(data, "not <<comparable>>", hyphencomp) insert(data.categories, langname .. " uncomparable " .. plpos) return else -- No comparative, but a superlative. insert_inflection() will correctly generate 'no comparative' if we -- pass in "-" as the value. outcomps = {hyphencomp} end elseif not comps[1] then comps = {{term = "more"}} end if not outcomps then -- not if we set `outcomps` to "-" above or processed a comparative-only term outcomps = {} -- Go over each parameter given and create a comparative and superlative form. for _, compobj in ipairs(comps) do local comp = compobj.term if comp == "-" then error("Comparative of '-' only allowed as first comparative") end if comp == "+" then comp = "+more" elseif comp == "more" and pagename ~= "many" and pagename ~= "much" then comp = "+more" elseif comp == "further" and pagename ~= "far" then comp = "+further" elseif comp == "better" and pagename ~= "good" and pagename ~= "well" then comp = "+better" elseif comp:find("~") then comp = comp:gsub("~", replacement_escape(pagename)) end compobj.origterm = comp if comp == "+more" then comp = "more [[" .. pagename .. "]]" elseif comp == "+further" then comp = {"further [[" .. pagename .. "]]", "farther [[" .. pagename .. "]]"} elseif comp == "+better" then comp = "better [[" .. pagename .. "]]" elseif comp == "er" then -- Add -er. comp = add_suffix(pagename, "r") elseif comp == "ier" then if pagename:sub(-1) ~= "y" then error("Can't specify 'ier' comparative unless the term ends wiþ 'y': " .. pagename) end comp = pagename:gsub("e?y$", "ier") elseif comp:find("^%+") then local special = m_headword_utilities.get_special_indicator(comp, "noerror") if special then comp = m_headword_utilities.handle_multiword(pagename, special, make_default_comparative) end end if type(comp) == "table" and not comp[2] then comp = comp[1] end if type(comp) == "table" then for i = 1, #comp - 1 do local outobj = shallowCopy(compobj) outobj.term = comp[i] insert(outcomps, outobj) end compobj.term = comp[#comp] insert(outcomps, compobj) else compobj.term = comp insert(outcomps, compobj) end end end if sups[1] and sups[1].term == "-" then if sups[2] then error("Can't specify '-' as superlative followed by further values") end -- No superlative. insert_inflection() will correctly generate 'no superlative' if we pass in "-" as the value. outsups = sups else if not sups[1] then sups = {{term = "+"}} end end -- `outsups` will be set if we set `outsups` to "-" above or processed a comparative-only term without superlatives. if not outsups then outsups = {} local function process_sup(sup, special, supobj, compobj) if special then sup = m_headword_utilities.handle_multiword(pagename, special, make_default_superlative) elseif sup == "-" or sup == "+" then error(("Internal error: Superlative value of '%s' should have been handled earlier"):format(sup)) elseif sup == "+most" then sup = "most [[" .. pagename .. "]]" elseif sup == "+furthest" then sup = {"furthest [[" .. pagename .. "]]", "farthest [[" .. pagename .. "]]"} elseif sup == "+best" then sup = "best [[" .. pagename .. "]]" elseif sup == "est" then -- Add -est. sup = add_suffix(pagename, "st.superlative") elseif sup == "iest" then if pagename:sub(-1) ~= "y" then error("Can't specify 'iest' superlative unless the term ends wiþ 'y': " .. pagename) end sup = pagename:gsub("e?y$", "iest") end if type(sup) == "table" and not sup[2] then sup = sup[1] end if compobj then supobj = shallowCopy(supobj) supobj = m_headword_utilities.combine_termobj_decorations(supobj, compobj) end if type(sup) == "table" then for i = 1, #sup - 1 do local outobj = shallowCopy(supobj) outobj.term = sup[i] insert(outsups, outobj) end supobj.term = sup[#sup] insert(outsups, supobj) else supobj.term = sup insert(outsups, supobj) end end for _, supobj in ipairs(sups) do local sup = supobj.term if sup == "-" then error("Superlative of '-' only allowed as first superlative") end if sup == "+" then if not comps[1] then error("Superlative of '+' can't be specified when there are no comparatives") end for _, compobj in ipairs(comps) do local comp = compobj.origterm local special if comp == "+more" then sup = "+most" elseif comp == "+further" then sup = "+furthest" elseif comp == "+better" then sup = "+best" elseif comp == "er" then sup = "est" elseif comp == "ier" then sup = "iest" else if comp:find("^%+") then special = m_headword_utilities.get_special_indicator(comp, "noerror") end if not special then -- If the full comparative was given, then derive the superlative by replacing -er with -- -est. if comp:sub(-2) == "er" then sup = comp:sub(1, -3) .. "est" else error(("The superlative cannot be derived automatically from comparative '%s' because it doesn't end in -er"):format(comp)) end end end process_sup(sup, special, supobj, compobj) end else local special = m_headword_utilities.get_special_indicator(sup, "noerror") -- Do some work here rather than in process_sup() so we don't end up double-processing a term with a '~' -- in it or a term that happens to be 'most' or similar after substitution of ~ in the comparative. if not special then if sup == "most" and pagename ~= "many" and pagename ~= "much" then sup = "+most" elseif sup == "furthest" and pagename ~= "far" then sup = "+furthest" elseif sup == "best" and pagename ~= "good" and pagename ~= "well" then sup = "+best" elseif sup:find("~") then sup = sup:gsub("~", replacement_escape(pagename)) end end process_sup(sup, special, supobj) end end end insert_inflection(data, outcomps, "<<comparative>>", "comparative") insert_inflection(data, outsups, "<<superlative>>", "superlative") end pos_functions["adjectives"] = { params = { [1] = list_param, ["comp_qual"] = {list = "comp\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the comparative value", }, ["sup"] = list_param, ["sup_qual"] = {list = "sup\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the superlative value", }, ["componly"] = boolean_param, ["suponly"] = boolean_param, }, func = function(args, data) -- Process the comparatives and superlatives. process_comparative_args(data, args, "adjectives") end, } pos_functions["adverbs"] = { params = { [1] = list_param, ["comp_qual"] = {list = "comp\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the comparative value", }, ["sup"] = list_param, ["sup_qual"] = {list = "sup\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the superlative value", }, ["componly"] = boolean_param, ["suponly"] = boolean_param, }, func = function(args, data) -- Process the comparatives and superlatives. process_comparative_args(data, args, "adverbs") end, } local function escape(str) return (str:gsub("\\([:#])", "\\\\%1") :gsub("[:#]", "\\%0")) end local function canonicalize_plural(pl, pagename, pos) if pl == "+" then return escape(add_suffix(pagename, "s.plural", pos)) elseif pl == "++" then return escape(compute_plusplus_s_form(pagename, add_suffix(pagename, "s.plural", pos))) elseif pl == "*" then return escape(pagename) elseif pl == "ies" then if pagename:sub(-1) == "y" then return escape(pagename:gsub("e?y$", pl)) end error("Can't specify 'ies' plural unless the term ends wiþ 'y'.") elseif pl == "s" or pl == "es" or pl == "'s" then return escape(pagename .. pl) end end local function do_nouns(args, data, pos) local pagename = data.pagename pos = pos or "noun" local plurals = parse_inflection(args, 1) local function insert_plurale_tantum_inflections(is_plural_only, originating_label) if args.sg[1] then insert_fixed_inflection(data, "normally plural", originating_label) parse_and_insert_inflection(data, args, "anf.", "singular") elseif is_plural_only then insert_fixed_inflection(data, "plural only", originating_label) end if args.attr[1] then parse_and_insert_inflection(data, args, "attr", "attributive") end end local function first_pl_term() return plurals[1] and plurals[1].term or nil end if first_pl_term() == "p" then -- plurale tantum if plurals[2] then error("Wiþ plurale tantum nama, can't specify more than one plural") end data.genders = {"p"} -- this should auto-insert the correct 'pluralia tantum' category insert_plurale_tantum_inflections("plural only", plurals[1]) return end local function inscat(cat) insert(data.categories, langname .. " " .. cat) end local need_default_plural = pos == "noun" if first_pl_term() == "sp" then -- construed as singular or plural sp = remove(plurals, 1) -- Remove the "sp" but retain it for its decorations. inscat("naman construed as singular or plural") data.genders = {"s", "p"} -- this should auto-insert the correct 'pluralia tantum' category insert_plurale_tantum_inflections(nil, sp) need_default_plural = false elseif first_pl_term() == "-" then -- Untellendlīc noun; may occasionally have a plural local hyphpl = remove(plurals, 1) -- Remove the "-" but retain for decorations. inscat("untellendlīc naman") -- If plural forms were given explicitly, then show "oft" if plurals[1] then insert_fixed_inflection(data, "oft <<untellendlīc>>", hyphpl) else insert_fixed_inflection(data, "<<untellendlīc>>", hyphpl) end need_default_plural = false elseif first_pl_term() == "#" then -- oft tellendlīc (e.g., "grilled cheese") local hashpl = remove(plurals, 1) -- Remove the "#" but retain for decorations. insert_fixed_inflection(data, "oft <<tellendlīc>>", hashpl) inscat("untellendlīc naman") inscat("tellendlīc naman") -- If no plural was given, add a default one now if not plurals[1] then plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end elseif first_pl_term() == "~" then -- Mixed tellendlīc/untellendlīc noun, always has a plural local tildepl = remove(plurals, 1) -- Remove the "~" but retain for decorations. insert_fixed_inflection(data, "<<tellendlīc>> and <<untellendlīc>>", tildepl) inscat("untellendlīc naman") inscat("tellendlīc naman") -- If no plural was given, add a default one now if not plurals[1] then plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end end -- Plural is unknown if first_pl_term() == "?" then local questionpl = remove(plurals, 1) -- Remove the "?" but retain for decorations. -- Not desired; see [[Wiktionary:Tea_room/2021/August#"Plural unknown or uncertain"]] -- insert_fixed_inflection(data, "manigfeald unġecnāwen oþþe ungewiss", questionpl) inscat("naman wiþ unġecnāwen oþþe ungewiss manigfealde") if plurals[1] then error("Ne cann āmearcian sweotole manigfealde andlang wiþ '?' of unġecnāwen/ungewiss manigfeald") end return end -- Plural is not attested if first_pl_term() == "!" then local exclampl = remove(plurals, 1) -- Remove the "!" but retain for decorations. insert_fixed_inflection(data, "plural not attested", exclampl) inscat("naman wiþ unattested manigfealde") if plurals[1] then error("Ne cann āmearcian sweotole manigfealde andlang wiþ '!' of gecȳþed manigfeald") end return end -- If no plural was given, maybe add a default one, otherwise (when "-" was given or proper noun) return. if not plurals[1] then if not need_default_plural then inscat("untellendlīc naman") return end plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end -- There are plural forms to show, so show them. inscat("tellendlīc naman") local irregular, indeclinable for i, pl in ipairs(plurals) do local canon_pl = canonicalize_plural(pl.term, pagename, pos) if canon_pl then pl.term = canon_pl end local pl_term = get_link_page(pl.term, lang) if not (pagename:find(" ") or is_regular_plural(pl_term, pagename)) then irregular = true if pl_term == pagename then indeclinable = true end end end if irregular then inscat("naman wiþ fremde manigfealde") end if indeclinable then inscat("undeclīnigendlīc naman") end insert_inflection(data, plurals, "mnf.", "p") end -- Return the parameters to be used for nouns and proper nouns. Currently the same. local noun_params = { [1] = list_param, ["pl\1qual"] = {list = true, allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the plural", }, -- The following four only used for pluralia tantum (1=p) ["sg"] = list_param, ["attr"] = list_param, } pos_functions["nouns"] = { params = noun_params, func = do_nouns, } pos_functions["proper nouns"] = { params = noun_params, func = function(args, data) return do_nouns(args, data, "proper nouns") end, } local function base_default_verb_forms(verb) return escape(add_suffix(verb, "s.verb")), escape(add_suffix(verb, "ing")), escape(add_suffix(verb, "d")) end local function default_verb_forms(verb) local full_s_form, full_ing_form, full_ed_form = base_default_verb_forms(verb) if verb:find(" ") then local first, rest = verb:match("^(.-)( .*)$") local first_s_form, first_ing_form, first_ed_form = base_default_verb_forms(first) return full_s_form, full_ing_form, full_ed_form, first_s_form .. rest, first_ing_form .. rest, first_ed_form .. rest, first, rest else return full_s_form, full_ing_form, full_ed_form, nil, nil, nil, nil, nil end end local function compute_double_last_cons_stem_of_split_verb(verb, ending) local first, rest = verb:match("^(.-)( .*)$") if not first then error("Word '" .. verb .. "' mōt hæfþ rum in sīde tō notienne **") end local last_cons = first:match("([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])$") if not last_cons then error("Fyrest word '" .. first .. "' mōt ende wiþ samod swēgend tō notienne **") end return first .. last_cons .. ending .. rest end local function check_non_nil_star_form(form, pagename) if form == nil then error("Word '" .. pagename .. "' mōt hæfþ rum in sīde tō notienne *, **, *l, *! or *'") end return form end local function sub_tilde(form, pagename) if not form then return nil end if form:find("~") then form = form:gsub("~", replacement_escape(pagename)) end return form end local deprecated_qual_replaced_by_inline_modifier = { list = true, allow_holes = true, replaced_by = false, instead = "use an inline modifier <q:...> or <l:...> on the value" } pos_functions["verbs"] = { params = { [1] = {list = "pres_3sg", disallow_holes = true}, ["pres_3sg\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [2] = {list = "pres_ptc", disallow_holes = true}, ["pres_ptc\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [3] = {list = "past", disallow_holes = true}, ["past\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [4] = {list = "past_ptc", allow_holes = true}, ["past_ptc\1_qual"] = deprecated_qual_replaced_by_inline_modifier, ["noautolinkverb"] = boolean_param, ["angle_bracket"] = boolean_param, }, func = function(args, data) -- Get parameters local par1s local par2s = parse_inflection(args, {2, "pres_ptc"}) local par3s = parse_inflection(args, {3, "past"}) local par4s = parse_inflection(args, {4, "past_ptc"}) local pres_3sgs, pres_ptcs, pasts, past_ptcs local pagename = data.pagename ------------------------------------------- UTILITY FUNCTIONS #2 ------------------------------------------ -- These functions are used in both in the separate-parameter format and in the override params such as past_ptc2=. local full_default_s, full_default_ing, full_default_ed, split_default_s, split_default_ing, split_default_ed local lemma local function set_lemma_and_default_forms(the_lemma) lemma = the_lemma full_default_s, full_default_ing, full_default_ed, split_default_s, split_default_ing, split_default_ed, lemma_first, lemma_rest = default_verb_forms(the_lemma) end local function canonicalize_s_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_s elseif form == "*" then return check_non_nil_star_form(split_default_s, lemma) elseif form == "++" then return compute_plusplus_s_form(lemma, full_default_s) elseif form == "**" then if lemma:find("^[^ ]*[szx] ") then return compute_double_last_cons_stem_of_split_verb(lemma, "es") else return check_non_nil_star_form(split_default_s, lemma) end elseif form == "+!" then return lemma .. "s" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "s" .. lemma_rest elseif form == "+'" then return lemma .. "'s" elseif form == "*'" then return check_non_nil_star_form(lemma_first) .. "'s" .. lemma_rest elseif form == "+l" then if lemma:find("[szx]$") then return {{term = full_default_s, l = {"US"}}, {term = compute_plusplus_s_form(lemma, full_default_s), l = {"UK"}}} else return compute_plusplus_s_form(lemma, full_default_s) end elseif form == "*l" then if lemma:find("^[^ ]*[szx] ") then return {{term = check_non_nil_star_form(split_default_s, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "es"), l = {"UK"}}} else return check_non_nil_star_form(split_default_s, lemma) end else return sub_tilde(form, lemma) end end local function canonicalize_ing_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_ing elseif form == "*" then return check_non_nil_star_form(split_default_ing, lemma) elseif form == "++" then return compute_double_last_cons_stem(lemma) .. "ing" elseif form == "**" then return compute_double_last_cons_stem_of_split_verb(lemma, "ing") elseif form == "+!" then return lemma .. "ing" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "ing" .. lemma_rest elseif form == "+'" then return lemma .. "'ing" elseif form == "*'" then return check_non_nil_star_form(lemma_first) .. "'ing" .. lemma_rest elseif form == "+l" then return {{term = full_default_ing, l = {"US"}}, {term = compute_double_last_cons_stem(lemma) .. "ing", l = {"UK"}}} elseif form == "*l" then return {{term = check_non_nil_star_form(split_default_ing, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "ing"), l = {"UK"}}} else return sub_tilde(form, lemma) end end local function canonicalize_ed_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_ed elseif form == "*" then return check_non_nil_star_form(split_default_ed, lemma) elseif form == "++" then return compute_double_last_cons_stem(lemma) .. "ed" elseif form == "+!" then return lemma .. "ed" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "ed" .. lemma_rest elseif form == "+'" then return {{term = lemma .. "'d"}, {term = lemma .. "'ed"}} elseif form == "*'" then return {{term = check_non_nil_star_form(lemma_first) .. "'d" .. lemma_rest}, {term = check_non_nil_star_form(lemma_first) .. "'ed" .. lemma_rest}} elseif form == "**" then return compute_double_last_cons_stem_of_split_verb(lemma, "ed") elseif form == "+l" then return {{term = full_default_ed, l = {"US"}}, {term = compute_double_last_cons_stem(lemma) .. "ed", l = {"UK"}}} elseif form == "*l" then return {{term = check_non_nil_star_form(split_default_ed, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "ed"), l = {"UK"}}} else return sub_tilde(form, lemma) end end -- FIXME: options should be "+", "*", "++", "**", "+n", "*n", "++n" and "**n", but not "n" local function canonicalize_en_form(form) if form == "n" then track("n4") return add_suffix(lemma, "n") end return canonicalize_ed_form(form) end --------------------------------- MAIN PARSING/CONJUGATING CODE -------------------------------- local is_angle_bracket = args.angle_bracket if is_angle_bracket then if par2s[1] or par3s[1] or par4s[1] then error("Can't specify explicit values for 2=, 3= or 4= along wiþ the angle-bracket format") end elseif is_angle_bracket == nil and not par2s[1] and not par3s[1] and not par4s[1] and not args[1][2] and args[1][1] and args[1][1]:find("<") then if put.term_contains_top_level_html(args[1][1]) then -- Often, term_contains_top_level_html() returns true on the angle-bracket format, which would -- make the pcall() below succeed but leave the angle brackets as-is. Check for this and only do the -- pcall() if term_contains_top_level_html() returns false. is_angle_bracket = true else -- If it's ambiguous whether it's an angle-bracket format or separate params with an inline modifier, -- try to parse as the latter. If an error occurs, treat as the former. local ok ok, par1s = pcall(parse_inflection, args, {1, "pres_3sg"}) if not ok then par1s = nil is_angle_bracket = true end end end if is_angle_bracket then -------------------------- ANGLE-BRACKET FORMAT -------------------------- -- (0) Expand multiword term with angle brackets just on the first word. local arg11 = args[1][1] if arg11:find("^<.*>$") and pagename:find(" ") then local first, rest = pagename:match("^(.-)( .*)$") arg11 = first .. arg11 .. rest end -- (1) Parse the indicator specs inside of angle brackets. local function parse_indicator_spec(angle_bracket_spec) local inside = angle_bracket_spec:match("^<(.*)>$") assert(inside) local segments = put.parse_balanced_segment_run(inside, "[", "]") local comma_separated_groups = put.split_alternating_runs(segments, ",") if #comma_separated_groups > 4 then error("Too many comma-separated parts in indicator spec, expected at most 4: " .. angle_bracket_spec) end local function fetch_footnotes(separated_group) local footnotes for j = 2, #separated_group - 1, 2 do if separated_group[j + 1] ~= "" then error("Extraneous text after bracketed footnotes: '" .. concat(separated_group) .. "'") end if not footnotes then footnotes = {} end insert(footnotes, separated_group[j]) end return footnotes end local function fetch_specs(comma_separated_group) if not comma_separated_group then return {{term = "+"}} end local specs = {} local colon_separated_groups = put.split_alternating_runs(comma_separated_group, ":") for _, colon_separated_group in ipairs(colon_separated_groups) do local form = colon_separated_group[1] if form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then error("*, **, *l, *! and *' not allowed inside of indicator specs: " .. angle_bracket_spec) end if form == "" then form = "+" end local termobj = { term = form } local footnotes = fetch_footnotes(colon_separated_group) if footnotes then for _, footnote in ipairs(footnotes) do m_headword_utilities.add_footnote_to_termobj(termobj, footnote) end end insert(specs, termobj) end return specs end local s_specs = fetch_specs(comma_separated_groups[1]) local ing_specs = fetch_specs(comma_separated_groups[2]) local ed_specs = fetch_specs(comma_separated_groups[3]) local en_specs = fetch_specs(comma_separated_groups[4]) return { forms = {}, s_specs = s_specs, ing_specs = ing_specs, ed_specs = ed_specs, en_specs = en_specs, } end local parse_props = { parse_indicator_spec = parse_indicator_spec, } local alternant_multiword_spec = iut.parse_inflected_text(arg11, parse_props) -- (2) Check for user-specified brackets; remove any links from the lemma, but remember the original -- form so we can use it below in the 'lemma_linked' form. -- Check to see if there are brackets in the pre-text or post-text. If so, use the linked lemma (with the -- verb autolinked unless noautolinkverb is given). Otherwise, use the default headword algorithm. local function check_bracket(val) if val:find("%[%[") then alternant_multiword_spec.saw_bracket = true end end for _, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do check_bracket(alternant_or_word_spec.before_text) if alternant_or_word_spec.alternants then for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do for _, word_spec in ipairs(multiword_spec.word_specs) do check_bracket(word_spec.before_text) end check_bracket(multiword_spec.post_text) end end end check_bracket(alternant_multiword_spec.post_text) iut.map_word_specs(alternant_multiword_spec, function(base) if base.lemma == "" then base.lemma = pagename end base.orig_lemma = base.lemma base.lemma = remove_links(base.lemma) if args.noautolinkverb or base.orig_lemma:find("%[%[") then base.linked_lemma = base.orig_lemma else base.linked_lemma = "[[" .. base.orig_lemma .. "]]" end end) -- (3) Conjugate the verbs according to the indicator specs parsed above. local all_verb_slots = { lemma = "infinitive", lemma_linked = "infinitive", s_form = "3|s|pres", ing_form = "pres|ptcp", ed_form = "past", en_form = "past|ptcp", } local function conjugate_verb(base) local function process_specs(slot, specs, canon_func, default_values, default_already_formobj) local function insert_termobj_into_slot(termobj) local formobj = m_headword_utilities.convert_termobj_to_formobj(termobj) -- If the form is -, don't insert any forms, which will result in there being no overall forms -- (in fact it will be nil). We check for that down below and substitute a single "-" as the -- form, which in turn gets turned into special labels like "no present participle". if formobj.form == "-" then if formobj.footnotes then error("Unable to preserve footnotes specified on missing form '-': FIXME: " .. dump(formobj.footnotes)) end else iut.insert_form(base.forms, slot, formobj) end end local function canonicalize_and_insert(arg) local canon_arg = canon_func(arg) if type(canon_arg) == "string" then arg.term = canon_arg insert_termobj_into_slot(arg) else for _, canon in ipairs(canon_arg) do m_headword_utilities.combine_termobj_decorations(canon, arg) insert_termobj_into_slot(canon) end end end for _, arg in ipairs(specs) do if arg.term == "+" then if default_values then -- will be nil if past tense specified as - and no past ptc given for _, val in ipairs(default_values) do val = shallowCopy(val) if default_already_formobj then local argformobj = m_headword_utilities.convert_termobj_to_formobj(arg) val.footnotes = iut.combine_footnotes(val.footnotes, argformobj.footnotes) iut.insert_form(base.forms, slot, val) else m_headword_utilities.combine_termobj_decorations(val, arg) canonicalize_and_insert(val) end end end else canonicalize_and_insert(arg) end end end set_lemma_and_default_forms(base.lemma) local all_part_default_specs = {} local function process_and_canonicalize_s_form(arg) local form = arg.term if form == "+" then error("Internal error: '+' should have been converted to '^' by now") end if form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then error(("Internal error: '%s' should have already thrown an error"):format(form)) end if form == "^" or form == "++" or form == "+l" or form == "+!" or form == "+'" then insert(all_part_default_specs, shallowCopy(arg)) end return canonicalize_s_form(form) end process_specs("s_form", base.s_specs, process_and_canonicalize_s_form, {{term = "^"}}) if not all_part_default_specs[1] then all_part_default_specs[1] = {term = "^"} end process_specs("ing_form", base.ing_specs, function(arg) return canonicalize_ing_form(arg.term) end, all_part_default_specs) process_specs("ed_form", base.ed_specs, function(arg) return canonicalize_ed_form(arg.term) end, all_part_default_specs) process_specs("en_form", base.en_specs, function(arg) return canonicalize_en_form(arg.term) end, base.forms.ed_form, "default already formobj") iut.insert_form(base.forms, "lemma", {form = base.lemma}) -- Add linked version of lemma for use in head=. We write this in a general fashion in case -- there are multiple lemma forms (which isn't possible currently at this level, although it's -- possible overall using the ((...,...)) notation). iut.insert_forms(base.forms, "lemma_linked", iut.map_forms(base.forms.lemma, function(form) if form == base.lemma and base.linked_lemma:find("%[%[") then return base.linked_lemma else return form end end)) end local inflect_props = { slot_table = all_verb_slots, inflect_word_spec = conjugate_verb, } iut.inflect_multiword_or_alternant_multiword_spec(alternant_multiword_spec, inflect_props) -- (4) Fetch the forms and put the conjugated lemmas in data.heads if not explicitly given. local function fetch_termobjs(slot) local forms = alternant_multiword_spec.forms[slot] -- See above. This should only occur if the user explicitly used - for a spec. if not forms or not forms[1] then return {{term = "-"}} end local termobjs = {} for _, formobj in ipairs(forms) do insert(termobjs, m_headword_utilities.convert_formobj_to_termobj(formobj)) end return termobjs end pres_3sgs = fetch_termobjs("s_form") pres_ptcs = fetch_termobjs("ing_form") pasts = fetch_termobjs("ed_form") past_ptcs = fetch_termobjs("en_form") -- Use the "linked" form of the lemma as the head if no head= explicitly given and the user specified -- brackets in one of the lemmas. Otherwise we use the default headword-linking algorithm. if not data.user_specified_heads[1] and alternant_multiword_spec.saw_bracket then data.heads = {} for _, lemma_obj in ipairs(alternant_multiword_spec.forms.lemma_linked) do insert(data.heads, m_headword_utilities.convert_formobj_to_termobj(lemma_obj)) end end else -------------------------- SEPARATE-PARAM FORMAT -------------------------- set_lemma_and_default_forms(pagename) par1s = par1s or parse_inflection(args, {1, "pres_3sg"}) pres_3sgs = {} pres_ptcs = {} pasts = {} past_ptcs = {} if not par1s[1] then par1s = {{term = "+"}} end if not par2s[1] then par2s = {{term = "+"}} end if not par3s[1] then par3s = {{term = "+"}} end if not par4s[1] then par4s = {{term = "+"}} end local function process_argument(args, dest, canon_func, default_values, default_already_canonicalized) local function canonicalize_and_insert(arg) local canon_arg = canon_func(arg) if type(canon_arg) == "string" then arg.term = canon_arg m_headword_utilities.insert_termobj_combining_duplicates(dest, arg) else for _, canon in ipairs(canon_arg) do m_headword_utilities.combine_termobj_decorations(canon, arg) m_headword_utilities.insert_termobj_combining_duplicates(dest, canon) end end end for _, arg in ipairs(args) do if arg.term == "+" then for _, val in ipairs(default_values) do val = shallowCopy(val) m_headword_utilities.combine_termobj_decorations(val, arg) if default_already_canonicalized then m_headword_utilities.insert_termobj_combining_duplicates(dest, val) else canonicalize_and_insert(val) end end else canonicalize_and_insert(arg) end end end local all_part_default_specs = {} local function process_and_canonicalize_s_form(arg) local form = arg.term if form == "+" then error("Internal error: '+' should have been converted to '^' by now") end if form == "^" or form == "++" or form == "+l" or form == "+!" or form == "+'" or form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then insert(all_part_default_specs, shallowCopy(arg)) end return canonicalize_s_form(form) end process_argument(par1s, pres_3sgs, process_and_canonicalize_s_form, {{term = "^"}}) if not all_part_default_specs[1] then all_part_default_specs[1] = {term = "^"} end process_argument(par2s, pres_ptcs, function(arg) return canonicalize_ing_form(arg.term) end, all_part_default_specs) process_argument(par3s, pasts, function(arg) return canonicalize_ed_form(arg.term) end, all_part_default_specs) process_argument(par4s, past_ptcs, function(arg) return canonicalize_en_form(arg.term) end, pasts, "default already canonicalized") end ------------------------------------------- INSERT INFLECTIONS ------------------------------------------ insert_inflection(data, pres_3sgs, "þridda had anfeald ānfeald andweard", "s-verb-form") insert_inflection(data, pres_ptcs, "andweard dǣlnimend", "ing-form") if deepEquals(pasts, past_ptcs) then insert_inflection(data, pasts, "ānfeald forþgewiten and forþgewiten dǣlnimend", "ed-form", "na ānfeald forþgewiten oþþe forþgewiten dǣlnimend") else insert_inflection(data, pasts, "ānfeald forþgewiten", "spast") insert_inflection(data, past_ptcs, "forþgewiten dǣlnimend", "past|part") end if pagename:find(" ") then -- Check for placeholder "it" local words = split(pagename, " ") for _, word in ipairs(words) do if word == "it" or word == "its" or word == "it's" then insert(data.categories, langname .. ' terms wiþ placeholder "it"') break end end -- Check for phrasal verbs local phrasal_adverbs = list_to_set{ -- NOTE: This should only contain common phrasal adverbs, not random words like [[low]], -- [[adrift]], etc. "aback", "about", "above", "across", "after", "against", "ahead", "along", "apart", "around", "as", "aside", "at", "away", "back", "before", "behind", "below", "between", "beyond", "by", "down", "for", "forth", "from", "in", "into", "of", "off", "on", "onto", "out", "over", "past", "round", "through", "to", "together", "towards", "under", "up", "upon", "with", "without", } local allowed_non_adverb_words = list_to_set{ "it", "one", "oneself", "someone", } local base = pagename local seen_adverbs = {} -- Only consider a verb to be phrasal if it consists of a single base verb followed exclusively by either -- adverbs from `phrasal_adverbs` or placeholder words from `allowed_non_adverb_words`, where at -- least one following word is from `phrasal_adverbs` (hence [[can it]] is not a phrasal verb). while true do local prev, word = base:match("^(.+) (.-)$") if not prev then break end if phrasal_adverbs[word] then insert(seen_adverbs, word) elseif allowed_non_adverb_words[word] then -- do nothing else break end base = prev end if not base:find(" ") and seen_adverbs[1] then insert(data.categories, langname .. " phrasal verbs") for i = #seen_adverbs, 1, -1 do insert(data.categories, langname .. ' phrasal verbs formed wiþ "' .. seen_adverbs[i] .. '"') end end end end, } ----------------------------------------------------------------------------------------- -- Suffix forms -- ----------------------------------------------------------------------------------------- pos_functions["suffix forms"] = { params = { [1] = {required = true, list = true, disallow_holes = true}, }, func = function(args, data, is_suffix) local suffix_type = {} for _, typ in ipairs(args[1]) do insert(suffix_type, typ .. "-forming suffix") end insert(data.inflections, {label = "non-lemma form of " .. m_table.serialCommaJoin(suffix_type, {conj = "or"})}) end, } return export gqdd0x9n0oyiwg0iohbahujvi9xxwpm 55052 55051 2026-09-30T20:19:39Z Deadend0914 7211 55052 Scribunto text/plain local export = {} local pos_functions = {} --[==[ Author from 2020 on: mostly Benwing2, with significant contributions from Theknightwho. Based on a prior version by Rua (by now mostly rewritten), with contributions from Erutuon and others (see history for full attribution). ]==] local force_cat = false -- for testing; if true, categories appear in non-mainspace pages local require = require local require_when_needed = require("Module:require when needed") local en_utilities_module = "Module:en-utilities" local headword_utilities_module = "Module:headword utilities" local headword_module = "Module:headword" local inflection_utilities_module = "Module:inflection utilities" local parse_utilities_module = "Module:parse utilities" local JSON_module = "Module:JSON" local labels_module = "Module:labels" local links_module = "Module:links" local parameters_module = "Module:parameters" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local utilities_module = "Module:utilities" local yesno_module = "Module:yesno" local iut = require_when_needed(inflection_utilities_module) local put = require_when_needed(parse_utilities_module) local m_headword_utilities = require_when_needed(headword_utilities_module) local add_links_to_multiword_term = require_when_needed(headword_utilities_module, "add_links_to_multiword_term") local add_suffix = require_when_needed(en_utilities_module, "add_suffix") local apply_link_modifiers = require_when_needed(headword_utilities_module, "apply_link_modifiers") local concat = table.concat local deepEquals = require_when_needed(table_module, "deepEquals") local dump = mw.dumpObject local format_categories = require_when_needed(utilities_module, "format_categories") local full_headword = require_when_needed(headword_module, "full_headword") local get_label_info = require_when_needed(labels_module, "get_label_info") local get_link_page = require_when_needed(links_module, "get_link_page") local glossary_link = require_when_needed(headword_utilities_module, "glossary_link") local insert = table.insert local insertIfNot = require_when_needed(table_module, "insertIfNot") local ipairs = ipairs local is_regular_plural = require_when_needed(en_utilities_module, "is_regular_plural") local list_to_set = require_when_needed(table_module, "listToSet") local pairs = pairs local process_params = require_when_needed(parameters_module, "process") local remove = table.remove local remove_links = require_when_needed(links_module, "remove_links") local replacement_escape = require_when_needed(string_utilities_module, "replacement_escape") local shallowCopy = require_when_needed(table_module, "shallowCopy") local singularize = require_when_needed(en_utilities_module, "singularize") local split = require_when_needed(string_utilities_module, "split") local toJSON = require_when_needed(JSON_module, "toJSON") local toNFD = mw.ustring.toNFD local type = type local ulen = require_when_needed(string_utilities_module, "len") local ulower = require_when_needed(string_utilities_module, "lower") local umatch = require_when_needed(string_utilities_module, "match") local u = require_when_needed(string_utilities_module, "char") local ugsub = require_when_needed(string_utilities_module, "gsub") local lang = require("Module:languages").getByCode("en") local langname = lang:getCanonicalName() -- Old English localization: category wordings come from [[Module:floccnaman]] -- (see that module); the English wording is kept when a translation is missing. -- Loaded lazily and optionally, so a missing module can never break a page. local floccnaman_module = "Module:floccnaman" local floccnaman local function get_floccnaman() if floccnaman == nil then local ok, mod = pcall(require, floccnaman_module) floccnaman = ok and mod or false end return floccnaman or nil end -- Old English wording for one category word, or the English wording unchanged. local function cat_word(l, text) local mod = get_floccnaman() return mod and mod.get(l, text) or text end local list_param = {list = true, disallow_holes = true} local list_allow_holes = {list = true, allow_holes = true} local boolean_param = {type = "boolean"} local function ine(val) if val == "" then return nil else return val end end local function track(page) require("Module:debug/track")("en-headword/" .. page) return true end ------------------------------------------- UTILITY FUNCTIONS ------------------------------------------ -- Parse and return an inflection not requiring additional processing. The raw arguments come from `args[field]`, which -- is parsed for inline modifiers. local function parse_inflection(args, field, is_head) local argfield = field if type(argfield) == "table" then argfield = argfield[1] end return m_headword_utilities.parse_term_list_with_modifiers { paramname = field, forms = args[argfield], splitchar = ",", is_head = is_head, } end -- Insert the parsed inflections in `terms` (as parsed by `parse_inflection`) into `data.inflections`, with label -- `label` and optional accelerator spec `accel`. local function insert_inflection(data, terms, label, accel, no_label) for _, termobj in ipairs(terms) do m_headword_utilities.remove_termobj_field_modifiers(termobj) end m_headword_utilities.insert_inflection { headdata = data, terms = terms, label = label, no_label = no_label, accel = accel and {form = accel} or nil, } end -- Insert a fixed label `label` into the inflections for `data`. If `originating_term` is supplied, copy the decorations -- from it into the fixed label. local function insert_fixed_inflection(data, label, originating_term) m_headword_utilities.insert_fixed_inflection { headdata = data, originating_term = originating_term, label = label, } 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 -- These functions are used directly in the <> format as well as in the utility functions #2 below. local function compute_double_last_cons_stem(term) local last_cons = term:match("([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])$") if not last_cons then error("Verb stem '" .. term .. "' must end in a consonant to use ++") end return term .. last_cons end local function compute_plusplus_s_form(term, default_s_form) if term:find("[szx]$") then -- regas -> regasses, derez -> derezzes return compute_double_last_cons_stem(term) .. "es" else return default_s_form end end -- On English Wiktionary, {{tl|en-head}} is the template that means "interjection"; it invokes this module without -- a part of speech. This wiki's template namespace is named "Bysen", not "Template", so the namespace id and the -- title without its namespace prefix are compared instead of a hard-coded "Template:en-head". -- Scribunto's title object exposes these as the fields `namespace`, `nsText` and `fullText`; it has no -- getNamespace()/getBaseText() methods, so those must not be used here. local function is_en_head_template() local title = mw.title.getCurrentTitle() return title.namespace == 10 and title.fullText == title.nsText .. ":en-head" end -- The main entry point. -- This is the only function that can be invoked from a template. function export.show(frame) local iparams = { [1] = true, } local iargs = require("Module:parameters").process(frame.args, iparams) local parargs = frame:getParent().args local poscat = iargs[1] local pos_in_1 = not poscat if pos_in_1 then poscat = ine(parargs[1]) or is_en_head_template() and "interjection" or error("Part of speech must be specified in 1=") poscat = require(headword_module).canonicalize_pos(poscat) end local indexing_poscat = pos_in_1 and "head" or poscat local params = { ["head"] = list_param, ["id"] = true, ["json"] = boolean_param, ["sort"] = true, ["splithyph"] = boolean_param, ["nosplithyph"] = boolean_param, ["hyphspace"] = boolean_param, ["nolink"] = boolean_param, ["nolinkhead"] = {type = "boolean_param", alias_of = "nolink"}, ["suffix"] = boolean_param, ["nosuffix"] = boolean_param, ["nomultiwordcat"] = boolean_param, ["abbr"] = list_param, ["the"] = true, ["def"] = {alias_of = "the"}, ["pagename"] = true, -- for testing } if pos_in_1 then params[1] = {required = true} -- required but ignored as already processed above end local pos_data = pos_functions[indexing_poscat] local pos_func if pos_data then local pos_params = pos_data.params if pos_params then for key, val in pairs(pos_params) do params[key] = val end end pos_func = pos_data.func end local args = process_params(parargs, params) -- Account for unsupported titles, e.g. 'C|N>K' instead of 'Unsupported titles/C through N to K'. local pagename = args.pagename or mw.loadData("Module:headword/data").pagename local user_specified_heads = parse_inflection(args, "head", "is_head") local heads = user_specified_heads local autohead if args.nolink or not pagename:find("[ '%-]") then autohead = pagename else local en_no_split_apostrophe_words = list_to_set { "one's", "someone's", "he's", "she's", "it's", } local en_include_hyphen_prefixes = list_to_set { -- We don't include things that are also words even though they are often (perhaps mostly) prefixes, e.g. -- "be", "counter", "cross", "extra", "half", "mid", "over", "pan", "under". "acro", "acousto", "Afro", "agro", "anarcho", "angio", "Anglo", "ante", "anti", "arch", "auto", "bi", "bio", "cis", "co", "cryo", "crypto", "de", "demi", "eco", "electro", "Euro", "ex", "Greco", "hemi", "hydro", "hyper", "hypo", "infra", "Indo", "inter", "intra", "Judeo", "macro", "meta", "micro", "mini", "multi", "neo", "neuro", "non", "para", "peri", "post", "pre", "pro", "proto", "pseudo", "re", "semi", "sub", "super", "trans", "un", "vice", } local function is_english(term) local title = mw.title.new(term) if title and title.exists then local content = title:getContent() if content and content:find("==English==\n") then return true end end return false end local function en_split_hyphen_when_space(word) if not word:find("-", nil, true) then return nil end if args.hyphspace then return "[[" .. word:gsub("%-+", " ") .. "|" .. word .. "]]" end if args.nosplithyph then return "[[" .. word .. "]]" end if not args.splithyph then local space_word = word:gsub("%-+", " ") if is_english(space_word) then return "[[" .. space_word .. "|" .. word .. "]]" end if is_english(word) then return "[[" .. word .. "]]" end end return nil end local function en_split_apostrophe(word) local base = word:match("^(.*)'s$") if base then return "[[" .. base .. "]][[-'s|'s]]" end -- Only treat final apostrophe as possessive if preceded by something that looks like a plural ending in /z/. -- In particular we don't want to do it for words like [[truckin']]. base = word:match("^(.*[sxz])'$") if base then if base:find("s$") then local sg = singularize(base) if is_english(sg) then return "[[" .. sg .. "|" .. base .. "]][[-'|']]" end end return "[[" .. base .. "]][[-'|']]" end return "[[" .. word .. "]]" end autohead = add_links_to_multiword_term(pagename, { split_hyphen_when_space = en_split_hyphen_when_space, split_apostrophe = en_split_apostrophe, no_split_apostrophe_words = en_no_split_apostrophe_words, include_hyphen_prefixes = en_include_hyphen_prefixes, }) end if not heads[1] then heads = {{term = autohead}} else for _, headobj in ipairs(heads) do local head = headobj.term if head:find("^~") then head = apply_link_modifiers(autohead, head:sub(2), lang) headobj.term = head elseif head:find("^[!?]$") then -- If explicit head= just consists of ! or ?, add it to the end of the default head. headobj.term = autohead .. head end if head == autohead then track("redundant-head") end end end -- handle the=/def= if args.the == "~" then local newheads = {} for _, headobj in ipairs(heads) do local barehead = shallowCopy(headobj) insert(newheads, barehead) headobj.term = "the " .. headobj.term insert(newheads, headobj) end heads = newheads elseif args.the then local the = require(yesno_module)(args.the) if the then for _, headobj in ipairs(heads) do headobj.term = "the " .. headobj.term end end end local data = { lang = lang, pos_category = poscat, categories = {}, heads = heads, user_specified_heads = user_specified_heads, -- We use our own splitting algorithm so the redundant head cat will be inaccurate. no_redundant_head_cat = true, inflections = {}, nomultiwordcat = args.nomultiwordcat, sort_key = args.sort, pagename = pagename, id = args.id, force_cat_output = force_cat, } local function inscat(cat) insert(data.categories, langname .. " " .. cat_word(lang, cat)) end local is_suffix = false if args.suffix or not args.nosuffix and pagename:find("^%-") and not pagename:find("^%-%-") and poscat ~= "suffix forms" then is_suffix = true data.pos_category = "suffixes" local singular_poscat = singularize(poscat) inscat(singular_poscat .. "-forming suffixes") insert(data.inflections, {label = singular_poscat .. "-forming suffix"}) end if pos_func then pos_func(args, data, is_suffix) end local extra_categories = {} if pagename:find("[Qq]") then -- Check for q not followed by u. We want to exclude things like [[13q deletion syndrome]] and [[BFOQ]] that -- don't have a lowercase letter on either side, as well as things like [[& seq.]] and [[acq.]] that are -- abbreviations for words containing a following u. -- -- Approximate range of combining diacritics; we want to remove them so the checks below for -- a lowercase letter next to the q aren't tripped up by diacritics on the letter. local u300 = u(0x0300) local u36F = u(0x036F) local pagename_no_diacritics = ugsub(toNFD(pagename), "[" .. u300 .. "-" .. u36F .. "]", "") if pagename_no_diacritics:find("[Qq][a-tv-z]") or pagename_no_diacritics:find("[a-z]q[^u.]") or pagename_no_diacritics:find("[a-z]q$") then inscat("words containing Q not followed by U") end end -- toNFD performs decomposition, so letters that decompose to an ASCII -- vowel and a diacritic, such as é, are counted as vowels and do not do not -- need to be included in the pattern. if not umatch(ulower(toNFD(pagename)), "[aeiouyæœøəªºαεηιουω]") then inscat("words spelled wiþūtan vowels") end if pagename:find("yre$") then inscat('words ending in "-yre"') end if not pagename:find(" ") and ulen(pagename) >= 25 then insert(extra_categories, langname .. " " .. cat_word(lang, "Long words")) end if pagename:find("^[^aeiou ]*a[^aeiou ]*e[^aeiou ]*i[^aeiou ]*o[^aeiou ]*u[^aeiou ]*$") then inscat("words that use all vowels in alphabetical order") end parse_and_insert_inflection(data, args, "abbr", "abbreviation") if args.json then return toJSON(data) end return full_headword(data) .. (extra_categories[1] and format_categories(extra_categories, lang, args.sort) or "") end local function make_default_comparative(word) if word == "good" or word == "well" then return {"better"} elseif word == "bad" or word == "badly" then return {"worse"} elseif word == "far" then return {"further", "farther"} else return {add_suffix(word, "r")} end end local function make_default_superlative(word) if word == "good" or word == "well" then return {"best"} elseif word == "bad" or word == "badly" then return {"worst"} elseif word == "far" then return {"furthest", "farthest"} else return {add_suffix(word, "st.superlative")} end end -- This function does the common work between adjectives and adverbs. local function process_comparative_args(data, args, plpos) local pagename = data.pagename local comps = parse_inflection(args, 1) local sups = parse_inflection(args, "sup") local outcomps, outsups if args.componly then if comps[1] then error("Can't specify comparatives of comparative-only " .. plpos) end insert(data.inflections, {label = glossary_link("comparative") .. " form only"}) insert(data.categories, langname .. " " .. cat_word(lang, "comparative-only " .. plpos)) -- Set to empty list so we don't get any comparatives output, but process superlatives if specified. outcomps = {} if not sups[1] then -- Set to empty list so we don't get any superlatives output unless explicitly given. outsups = {} end elseif args.suponly then if comps[1] or sups[1] then error("Can't specify comparatives or superlatives of or superlative-only " .. plpos) end insert(data.inflections, {label = glossary_link("superlative") .. " form only"}) insert(data.categories, langname .. " " .. cat_word(lang, "superlative-only " .. plpos)) return end -- If the first parameter is ?, then don't show anything, just return. if comps[1] and comps[1].term == "?" then if comps[2] then error("Can't specify additional comparatives along wiþ '?'") end if sups[1] then error("Can't specify superlatives along wiþ '?' for the comparative") end return end if comps[1] and comps[1].term == "-" then local hyphencomp = remove(comps, 1) -- Remove the "-" but retain for decorations. -- Not (generally) comparable; may occasionally have a comparative if comps[1] then insert_fixed_inflection(data, "not generally <<comparable>>", hyphencomp) elseif not sups[1] then insert_fixed_inflection(data, "not <<comparable>>", hyphencomp) insert(data.categories, langname .. " " .. cat_word(lang, "uncomparable " .. plpos)) return else -- No comparative, but a superlative. insert_inflection() will correctly generate 'no comparative' if we -- pass in "-" as the value. outcomps = {hyphencomp} end elseif not comps[1] then comps = {{term = "more"}} end if not outcomps then -- not if we set `outcomps` to "-" above or processed a comparative-only term outcomps = {} -- Go over each parameter given and create a comparative and superlative form. for _, compobj in ipairs(comps) do local comp = compobj.term if comp == "-" then error("Comparative of '-' only allowed as first comparative") end if comp == "+" then comp = "+more" elseif comp == "more" and pagename ~= "many" and pagename ~= "much" then comp = "+more" elseif comp == "further" and pagename ~= "far" then comp = "+further" elseif comp == "better" and pagename ~= "good" and pagename ~= "well" then comp = "+better" elseif comp:find("~") then comp = comp:gsub("~", replacement_escape(pagename)) end compobj.origterm = comp if comp == "+more" then comp = "more [[" .. pagename .. "]]" elseif comp == "+further" then comp = {"further [[" .. pagename .. "]]", "farther [[" .. pagename .. "]]"} elseif comp == "+better" then comp = "better [[" .. pagename .. "]]" elseif comp == "er" then -- Add -er. comp = add_suffix(pagename, "r") elseif comp == "ier" then if pagename:sub(-1) ~= "y" then error("Can't specify 'ier' comparative unless the term ends wiþ 'y': " .. pagename) end comp = pagename:gsub("e?y$", "ier") elseif comp:find("^%+") then local special = m_headword_utilities.get_special_indicator(comp, "noerror") if special then comp = m_headword_utilities.handle_multiword(pagename, special, make_default_comparative) end end if type(comp) == "table" and not comp[2] then comp = comp[1] end if type(comp) == "table" then for i = 1, #comp - 1 do local outobj = shallowCopy(compobj) outobj.term = comp[i] insert(outcomps, outobj) end compobj.term = comp[#comp] insert(outcomps, compobj) else compobj.term = comp insert(outcomps, compobj) end end end if sups[1] and sups[1].term == "-" then if sups[2] then error("Can't specify '-' as superlative followed by further values") end -- No superlative. insert_inflection() will correctly generate 'no superlative' if we pass in "-" as the value. outsups = sups else if not sups[1] then sups = {{term = "+"}} end end -- `outsups` will be set if we set `outsups` to "-" above or processed a comparative-only term without superlatives. if not outsups then outsups = {} local function process_sup(sup, special, supobj, compobj) if special then sup = m_headword_utilities.handle_multiword(pagename, special, make_default_superlative) elseif sup == "-" or sup == "+" then error(("Internal error: Superlative value of '%s' should have been handled earlier"):format(sup)) elseif sup == "+most" then sup = "most [[" .. pagename .. "]]" elseif sup == "+furthest" then sup = {"furthest [[" .. pagename .. "]]", "farthest [[" .. pagename .. "]]"} elseif sup == "+best" then sup = "best [[" .. pagename .. "]]" elseif sup == "est" then -- Add -est. sup = add_suffix(pagename, "st.superlative") elseif sup == "iest" then if pagename:sub(-1) ~= "y" then error("Can't specify 'iest' superlative unless the term ends wiþ 'y': " .. pagename) end sup = pagename:gsub("e?y$", "iest") end if type(sup) == "table" and not sup[2] then sup = sup[1] end if compobj then supobj = shallowCopy(supobj) supobj = m_headword_utilities.combine_termobj_decorations(supobj, compobj) end if type(sup) == "table" then for i = 1, #sup - 1 do local outobj = shallowCopy(supobj) outobj.term = sup[i] insert(outsups, outobj) end supobj.term = sup[#sup] insert(outsups, supobj) else supobj.term = sup insert(outsups, supobj) end end for _, supobj in ipairs(sups) do local sup = supobj.term if sup == "-" then error("Superlative of '-' only allowed as first superlative") end if sup == "+" then if not comps[1] then error("Superlative of '+' can't be specified when there are no comparatives") end for _, compobj in ipairs(comps) do local comp = compobj.origterm local special if comp == "+more" then sup = "+most" elseif comp == "+further" then sup = "+furthest" elseif comp == "+better" then sup = "+best" elseif comp == "er" then sup = "est" elseif comp == "ier" then sup = "iest" else if comp:find("^%+") then special = m_headword_utilities.get_special_indicator(comp, "noerror") end if not special then -- If the full comparative was given, then derive the superlative by replacing -er with -- -est. if comp:sub(-2) == "er" then sup = comp:sub(1, -3) .. "est" else error(("The superlative cannot be derived automatically from comparative '%s' because it doesn't end in -er"):format(comp)) end end end process_sup(sup, special, supobj, compobj) end else local special = m_headword_utilities.get_special_indicator(sup, "noerror") -- Do some work here rather than in process_sup() so we don't end up double-processing a term with a '~' -- in it or a term that happens to be 'most' or similar after substitution of ~ in the comparative. if not special then if sup == "most" and pagename ~= "many" and pagename ~= "much" then sup = "+most" elseif sup == "furthest" and pagename ~= "far" then sup = "+furthest" elseif sup == "best" and pagename ~= "good" and pagename ~= "well" then sup = "+best" elseif sup:find("~") then sup = sup:gsub("~", replacement_escape(pagename)) end end process_sup(sup, special, supobj) end end end insert_inflection(data, outcomps, "<<comparative>>", "comparative") insert_inflection(data, outsups, "<<superlative>>", "superlative") end pos_functions["adjectives"] = { params = { [1] = list_param, ["comp_qual"] = {list = "comp\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the comparative value", }, ["sup"] = list_param, ["sup_qual"] = {list = "sup\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the superlative value", }, ["componly"] = boolean_param, ["suponly"] = boolean_param, }, func = function(args, data) -- Process the comparatives and superlatives. process_comparative_args(data, args, "adjectives") end, } pos_functions["adverbs"] = { params = { [1] = list_param, ["comp_qual"] = {list = "comp\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the comparative value", }, ["sup"] = list_param, ["sup_qual"] = {list = "sup\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the superlative value", }, ["componly"] = boolean_param, ["suponly"] = boolean_param, }, func = function(args, data) -- Process the comparatives and superlatives. process_comparative_args(data, args, "adverbs") end, } local function escape(str) return (str:gsub("\\([:#])", "\\\\%1") :gsub("[:#]", "\\%0")) end local function canonicalize_plural(pl, pagename, pos) if pl == "+" then return escape(add_suffix(pagename, "s.plural", pos)) elseif pl == "++" then return escape(compute_plusplus_s_form(pagename, add_suffix(pagename, "s.plural", pos))) elseif pl == "*" then return escape(pagename) elseif pl == "ies" then if pagename:sub(-1) == "y" then return escape(pagename:gsub("e?y$", pl)) end error("Can't specify 'ies' plural unless the term ends wiþ 'y'.") elseif pl == "s" or pl == "es" or pl == "'s" then return escape(pagename .. pl) end end local function do_nouns(args, data, pos) local pagename = data.pagename pos = pos or "noun" local plurals = parse_inflection(args, 1) local function insert_plurale_tantum_inflections(is_plural_only, originating_label) if args.sg[1] then insert_fixed_inflection(data, "normally plural", originating_label) parse_and_insert_inflection(data, args, "sg", "singular") elseif is_plural_only then insert_fixed_inflection(data, "plural only", originating_label) end if args.attr[1] then parse_and_insert_inflection(data, args, "attr", "attributive") end end local function first_pl_term() return plurals[1] and plurals[1].term or nil end if first_pl_term() == "p" then -- plurale tantum if plurals[2] then error("Wiþ plurale tantum nama, can't specify more than one plural") end data.genders = {"p"} -- this should auto-insert the correct 'pluralia tantum' category insert_plurale_tantum_inflections("plural only", plurals[1]) return end local function inscat(cat) insert(data.categories, langname .. " " .. cat_word(lang, cat)) end local need_default_plural = pos == "noun" if first_pl_term() == "sp" then -- construed as singular or plural sp = remove(plurals, 1) -- Remove the "sp" but retain it for its decorations. inscat("naman construed as singular or plural") data.genders = {"s", "p"} -- this should auto-insert the correct 'pluralia tantum' category insert_plurale_tantum_inflections(nil, sp) need_default_plural = false elseif first_pl_term() == "-" then -- Untellendlīc noun; may occasionally have a plural local hyphpl = remove(plurals, 1) -- Remove the "-" but retain for decorations. inscat("untellendlīc naman") -- If plural forms were given explicitly, then show "oft" if plurals[1] then insert_fixed_inflection(data, "oft <<untellendlīc>>", hyphpl) else insert_fixed_inflection(data, "<<untellendlīc>>", hyphpl) end need_default_plural = false elseif first_pl_term() == "#" then -- oft tellendlīc (e.g., "grilled cheese") local hashpl = remove(plurals, 1) -- Remove the "#" but retain for decorations. insert_fixed_inflection(data, "oft <<tellendlīc>>", hashpl) inscat("untellendlīc naman") inscat("tellendlīc naman") -- If no plural was given, add a default one now if not plurals[1] then plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end elseif first_pl_term() == "~" then -- Mixed tellendlīc/untellendlīc noun, always has a plural local tildepl = remove(plurals, 1) -- Remove the "~" but retain for decorations. insert_fixed_inflection(data, "<<tellendlīc>> and <<untellendlīc>>", tildepl) inscat("untellendlīc naman") inscat("tellendlīc naman") -- If no plural was given, add a default one now if not plurals[1] then plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end end -- Plural is unknown if first_pl_term() == "?" then local questionpl = remove(plurals, 1) -- Remove the "?" but retain for decorations. -- Not desired; see [[Wiktionary:Tea_room/2021/August#"Plural unknown or uncertain"]] -- insert_fixed_inflection(data, "manigfeald unġecnāwen oþþe ungewiss", questionpl) inscat("naman wiþ unġecnāwen oþþe ungewiss manigfealde") if plurals[1] then error("Ne cann āmearcian sweotole manigfealde andlang wiþ '?' of unġecnāwen/ungewiss manigfeald") end return end -- Plural is not attested if first_pl_term() == "!" then local exclampl = remove(plurals, 1) -- Remove the "!" but retain for decorations. insert_fixed_inflection(data, "plural not attested", exclampl) inscat("naman wiþ unattested manigfealde") if plurals[1] then error("Ne cann āmearcian sweotole manigfealde andlang wiþ '!' of gecȳþed manigfeald") end return end -- If no plural was given, maybe add a default one, otherwise (when "-" was given or proper noun) return. if not plurals[1] then if not need_default_plural then inscat("untellendlīc naman") return end plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end -- There are plural forms to show, so show them. inscat("tellendlīc naman") local irregular, indeclinable for i, pl in ipairs(plurals) do local canon_pl = canonicalize_plural(pl.term, pagename, pos) if canon_pl then pl.term = canon_pl end local pl_term = get_link_page(pl.term, lang) if not (pagename:find(" ") or is_regular_plural(pl_term, pagename)) then irregular = true if pl_term == pagename then indeclinable = true end end end if irregular then inscat("naman wiþ fremde manigfealde") end if indeclinable then inscat("undeclīnigendlīce naman") end insert_inflection(data, plurals, "plural", "p") end -- Return the parameters to be used for nouns and proper nouns. Currently the same. local noun_params = { [1] = list_param, ["pl\1qual"] = {list = true, allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the plural", }, -- The following four only used for pluralia tantum (1=p) ["sg"] = list_param, ["attr"] = list_param, } pos_functions["naman"] = { params = noun_params, func = do_naman, } pos_functions["agennaman"] = { params = noun_params, func = function(args, data) return do_nouns(args, data, "agennama") end, } local function base_default_verb_forms(verb) return escape(add_suffix(verb, "s.verb")), escape(add_suffix(verb, "ing")), escape(add_suffix(verb, "d")) end local function default_verb_forms(verb) local full_s_form, full_ing_form, full_ed_form = base_default_verb_forms(verb) if verb:find(" ") then local first, rest = verb:match("^(.-)( .*)$") local first_s_form, first_ing_form, first_ed_form = base_default_verb_forms(first) return full_s_form, full_ing_form, full_ed_form, first_s_form .. rest, first_ing_form .. rest, first_ed_form .. rest, first, rest else return full_s_form, full_ing_form, full_ed_form, nil, nil, nil, nil, nil end end local function compute_double_last_cons_stem_of_split_verb(verb, ending) local first, rest = verb:match("^(.-)( .*)$") if not first then error("Word '" .. verb .. "' mōt hæfþ rum in sīde tō notienne **") end local last_cons = first:match("([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])$") if not last_cons then error("Fyrest word '" .. first .. "' mōt ende wiþ samod swēgend tō notienne **") end return first .. last_cons .. ending .. rest end local function check_non_nil_star_form(form, pagename) if form == nil then error("Word '" .. pagename .. "' mōt hæfþ rum in sīde tō notienne *, **, *l, *! or *'") end return form end local function sub_tilde(form, pagename) if not form then return nil end if form:find("~") then form = form:gsub("~", replacement_escape(pagename)) end return form end local deprecated_qual_replaced_by_inline_modifier = { list = true, allow_holes = true, replaced_by = false, instead = "use an inline modifier <q:...> or <l:...> on the value" } pos_functions["verbs"] = { params = { [1] = {list = "pres_3sg", disallow_holes = true}, ["pres_3sg\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [2] = {list = "pres_ptc", disallow_holes = true}, ["pres_ptc\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [3] = {list = "past", disallow_holes = true}, ["past\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [4] = {list = "past_ptc", allow_holes = true}, ["past_ptc\1_qual"] = deprecated_qual_replaced_by_inline_modifier, ["noautolinkverb"] = boolean_param, ["angle_bracket"] = boolean_param, }, func = function(args, data) -- Get parameters local par1s local par2s = parse_inflection(args, {2, "pres_ptc"}) local par3s = parse_inflection(args, {3, "past"}) local par4s = parse_inflection(args, {4, "past_ptc"}) local pres_3sgs, pres_ptcs, pasts, past_ptcs local pagename = data.pagename ------------------------------------------- UTILITY FUNCTIONS #2 ------------------------------------------ -- These functions are used in both in the separate-parameter format and in the override params such as past_ptc2=. local full_default_s, full_default_ing, full_default_ed, split_default_s, split_default_ing, split_default_ed local lemma local function set_lemma_and_default_forms(the_lemma) lemma = the_lemma full_default_s, full_default_ing, full_default_ed, split_default_s, split_default_ing, split_default_ed, lemma_first, lemma_rest = default_verb_forms(the_lemma) end local function canonicalize_s_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_s elseif form == "*" then return check_non_nil_star_form(split_default_s, lemma) elseif form == "++" then return compute_plusplus_s_form(lemma, full_default_s) elseif form == "**" then if lemma:find("^[^ ]*[szx] ") then return compute_double_last_cons_stem_of_split_verb(lemma, "es") else return check_non_nil_star_form(split_default_s, lemma) end elseif form == "+!" then return lemma .. "s" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "s" .. lemma_rest elseif form == "+'" then return lemma .. "'s" elseif form == "*'" then return check_non_nil_star_form(lemma_first) .. "'s" .. lemma_rest elseif form == "+l" then if lemma:find("[szx]$") then return {{term = full_default_s, l = {"US"}}, {term = compute_plusplus_s_form(lemma, full_default_s), l = {"UK"}}} else return compute_plusplus_s_form(lemma, full_default_s) end elseif form == "*l" then if lemma:find("^[^ ]*[szx] ") then return {{term = check_non_nil_star_form(split_default_s, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "es"), l = {"UK"}}} else return check_non_nil_star_form(split_default_s, lemma) end else return sub_tilde(form, lemma) end end local function canonicalize_ing_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_ing elseif form == "*" then return check_non_nil_star_form(split_default_ing, lemma) elseif form == "++" then return compute_double_last_cons_stem(lemma) .. "ing" elseif form == "**" then return compute_double_last_cons_stem_of_split_verb(lemma, "ing") elseif form == "+!" then return lemma .. "ing" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "ing" .. lemma_rest elseif form == "+'" then return lemma .. "'ing" elseif form == "*'" then return check_non_nil_star_form(lemma_first) .. "'ing" .. lemma_rest elseif form == "+l" then return {{term = full_default_ing, l = {"US"}}, {term = compute_double_last_cons_stem(lemma) .. "ing", l = {"UK"}}} elseif form == "*l" then return {{term = check_non_nil_star_form(split_default_ing, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "ing"), l = {"UK"}}} else return sub_tilde(form, lemma) end end local function canonicalize_ed_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_ed elseif form == "*" then return check_non_nil_star_form(split_default_ed, lemma) elseif form == "++" then return compute_double_last_cons_stem(lemma) .. "ed" elseif form == "+!" then return lemma .. "ed" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "ed" .. lemma_rest elseif form == "+'" then return {{term = lemma .. "'d"}, {term = lemma .. "'ed"}} elseif form == "*'" then return {{term = check_non_nil_star_form(lemma_first) .. "'d" .. lemma_rest}, {term = check_non_nil_star_form(lemma_first) .. "'ed" .. lemma_rest}} elseif form == "**" then return compute_double_last_cons_stem_of_split_verb(lemma, "ed") elseif form == "+l" then return {{term = full_default_ed, l = {"US"}}, {term = compute_double_last_cons_stem(lemma) .. "ed", l = {"UK"}}} elseif form == "*l" then return {{term = check_non_nil_star_form(split_default_ed, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "ed"), l = {"UK"}}} else return sub_tilde(form, lemma) end end -- FIXME: options should be "+", "*", "++", "**", "+n", "*n", "++n" and "**n", but not "n" local function canonicalize_en_form(form) if form == "n" then track("n4") return add_suffix(lemma, "n") end return canonicalize_ed_form(form) end --------------------------------- MAIN PARSING/CONJUGATING CODE -------------------------------- local is_angle_bracket = args.angle_bracket if is_angle_bracket then if par2s[1] or par3s[1] or par4s[1] then error("Can't specify explicit values for 2=, 3= or 4= along wiþ the angle-bracket format") end elseif is_angle_bracket == nil and not par2s[1] and not par3s[1] and not par4s[1] and not args[1][2] and args[1][1] and args[1][1]:find("<") then if put.term_contains_top_level_html(args[1][1]) then -- Often, term_contains_top_level_html() returns true on the angle-bracket format, which would -- make the pcall() below succeed but leave the angle brackets as-is. Check for this and only do the -- pcall() if term_contains_top_level_html() returns false. is_angle_bracket = true else -- If it's ambiguous whether it's an angle-bracket format or separate params with an inline modifier, -- try to parse as the latter. If an error occurs, treat as the former. local ok ok, par1s = pcall(parse_inflection, args, {1, "pres_3sg"}) if not ok then par1s = nil is_angle_bracket = true end end end if is_angle_bracket then -------------------------- ANGLE-BRACKET FORMAT -------------------------- -- (0) Expand multiword term with angle brackets just on the first word. local arg11 = args[1][1] if arg11:find("^<.*>$") and pagename:find(" ") then local first, rest = pagename:match("^(.-)( .*)$") arg11 = first .. arg11 .. rest end -- (1) Parse the indicator specs inside of angle brackets. local function parse_indicator_spec(angle_bracket_spec) local inside = angle_bracket_spec:match("^<(.*)>$") assert(inside) local segments = put.parse_balanced_segment_run(inside, "[", "]") local comma_separated_groups = put.split_alternating_runs(segments, ",") if #comma_separated_groups > 4 then error("Too many comma-separated parts in indicator spec, expected at most 4: " .. angle_bracket_spec) end local function fetch_footnotes(separated_group) local footnotes for j = 2, #separated_group - 1, 2 do if separated_group[j + 1] ~= "" then error("Extraneous text after bracketed footnotes: '" .. concat(separated_group) .. "'") end if not footnotes then footnotes = {} end insert(footnotes, separated_group[j]) end return footnotes end local function fetch_specs(comma_separated_group) if not comma_separated_group then return {{term = "+"}} end local specs = {} local colon_separated_groups = put.split_alternating_runs(comma_separated_group, ":") for _, colon_separated_group in ipairs(colon_separated_groups) do local form = colon_separated_group[1] if form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then error("*, **, *l, *! and *' not allowed inside of indicator specs: " .. angle_bracket_spec) end if form == "" then form = "+" end local termobj = { term = form } local footnotes = fetch_footnotes(colon_separated_group) if footnotes then for _, footnote in ipairs(footnotes) do m_headword_utilities.add_footnote_to_termobj(termobj, footnote) end end insert(specs, termobj) end return specs end local s_specs = fetch_specs(comma_separated_groups[1]) local ing_specs = fetch_specs(comma_separated_groups[2]) local ed_specs = fetch_specs(comma_separated_groups[3]) local en_specs = fetch_specs(comma_separated_groups[4]) return { forms = {}, s_specs = s_specs, ing_specs = ing_specs, ed_specs = ed_specs, en_specs = en_specs, } end local parse_props = { parse_indicator_spec = parse_indicator_spec, } local alternant_multiword_spec = iut.parse_inflected_text(arg11, parse_props) -- (2) Check for user-specified brackets; remove any links from the lemma, but remember the original -- form so we can use it below in the 'lemma_linked' form. -- Check to see if there are brackets in the pre-text or post-text. If so, use the linked lemma (with the -- verb autolinked unless noautolinkverb is given). Otherwise, use the default headword algorithm. local function check_bracket(val) if val:find("%[%[") then alternant_multiword_spec.saw_bracket = true end end for _, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do check_bracket(alternant_or_word_spec.before_text) if alternant_or_word_spec.alternants then for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do for _, word_spec in ipairs(multiword_spec.word_specs) do check_bracket(word_spec.before_text) end check_bracket(multiword_spec.post_text) end end end check_bracket(alternant_multiword_spec.post_text) iut.map_word_specs(alternant_multiword_spec, function(base) if base.lemma == "" then base.lemma = pagename end base.orig_lemma = base.lemma base.lemma = remove_links(base.lemma) if args.noautolinkverb or base.orig_lemma:find("%[%[") then base.linked_lemma = base.orig_lemma else base.linked_lemma = "[[" .. base.orig_lemma .. "]]" end end) -- (3) Conjugate the verbs according to the indicator specs parsed above. local all_verb_slots = { lemma = "infinitive", lemma_linked = "infinitive", s_form = "3|s|pres", ing_form = "pres|ptcp", ed_form = "past", en_form = "past|ptcp", } local function conjugate_verb(base) local function process_specs(slot, specs, canon_func, default_values, default_already_formobj) local function insert_termobj_into_slot(termobj) local formobj = m_headword_utilities.convert_termobj_to_formobj(termobj) -- If the form is -, don't insert any forms, which will result in there being no overall forms -- (in fact it will be nil). We check for that down below and substitute a single "-" as the -- form, which in turn gets turned into special labels like "no present participle". if formobj.form == "-" then if formobj.footnotes then error("Unable to preserve footnotes specified on missing form '-': FIXME: " .. dump(formobj.footnotes)) end else iut.insert_form(base.forms, slot, formobj) end end local function canonicalize_and_insert(arg) local canon_arg = canon_func(arg) if type(canon_arg) == "string" then arg.term = canon_arg insert_termobj_into_slot(arg) else for _, canon in ipairs(canon_arg) do m_headword_utilities.combine_termobj_decorations(canon, arg) insert_termobj_into_slot(canon) end end end for _, arg in ipairs(specs) do if arg.term == "+" then if default_values then -- will be nil if past tense specified as - and no past ptc given for _, val in ipairs(default_values) do val = shallowCopy(val) if default_already_formobj then local argformobj = m_headword_utilities.convert_termobj_to_formobj(arg) val.footnotes = iut.combine_footnotes(val.footnotes, argformobj.footnotes) iut.insert_form(base.forms, slot, val) else m_headword_utilities.combine_termobj_decorations(val, arg) canonicalize_and_insert(val) end end end else canonicalize_and_insert(arg) end end end set_lemma_and_default_forms(base.lemma) local all_part_default_specs = {} local function process_and_canonicalize_s_form(arg) local form = arg.term if form == "+" then error("Internal error: '+' should have been converted to '^' by now") end if form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then error(("Internal error: '%s' should have already thrown an error"):format(form)) end if form == "^" or form == "++" or form == "+l" or form == "+!" or form == "+'" then insert(all_part_default_specs, shallowCopy(arg)) end return canonicalize_s_form(form) end process_specs("s_form", base.s_specs, process_and_canonicalize_s_form, {{term = "^"}}) if not all_part_default_specs[1] then all_part_default_specs[1] = {term = "^"} end process_specs("ing_form", base.ing_specs, function(arg) return canonicalize_ing_form(arg.term) end, all_part_default_specs) process_specs("ed_form", base.ed_specs, function(arg) return canonicalize_ed_form(arg.term) end, all_part_default_specs) process_specs("en_form", base.en_specs, function(arg) return canonicalize_en_form(arg.term) end, base.forms.ed_form, "default already formobj") iut.insert_form(base.forms, "lemma", {form = base.lemma}) -- Add linked version of lemma for use in head=. We write this in a general fashion in case -- there are multiple lemma forms (which isn't possible currently at this level, although it's -- possible overall using the ((...,...)) notation). iut.insert_forms(base.forms, "lemma_linked", iut.map_forms(base.forms.lemma, function(form) if form == base.lemma and base.linked_lemma:find("%[%[") then return base.linked_lemma else return form end end)) end local inflect_props = { slot_table = all_verb_slots, inflect_word_spec = conjugate_verb, } iut.inflect_multiword_or_alternant_multiword_spec(alternant_multiword_spec, inflect_props) -- (4) Fetch the forms and put the conjugated lemmas in data.heads if not explicitly given. local function fetch_termobjs(slot) local forms = alternant_multiword_spec.forms[slot] -- See above. This should only occur if the user explicitly used - for a spec. if not forms or not forms[1] then return {{term = "-"}} end local termobjs = {} for _, formobj in ipairs(forms) do insert(termobjs, m_headword_utilities.convert_formobj_to_termobj(formobj)) end return termobjs end pres_3sgs = fetch_termobjs("s_form") pres_ptcs = fetch_termobjs("ing_form") pasts = fetch_termobjs("ed_form") past_ptcs = fetch_termobjs("en_form") -- Use the "linked" form of the lemma as the head if no head= explicitly given and the user specified -- brackets in one of the lemmas. Otherwise we use the default headword-linking algorithm. if not data.user_specified_heads[1] and alternant_multiword_spec.saw_bracket then data.heads = {} for _, lemma_obj in ipairs(alternant_multiword_spec.forms.lemma_linked) do insert(data.heads, m_headword_utilities.convert_formobj_to_termobj(lemma_obj)) end end else -------------------------- SEPARATE-PARAM FORMAT -------------------------- set_lemma_and_default_forms(pagename) par1s = par1s or parse_inflection(args, {1, "pres_3sg"}) pres_3sgs = {} pres_ptcs = {} pasts = {} past_ptcs = {} if not par1s[1] then par1s = {{term = "+"}} end if not par2s[1] then par2s = {{term = "+"}} end if not par3s[1] then par3s = {{term = "+"}} end if not par4s[1] then par4s = {{term = "+"}} end local function process_argument(args, dest, canon_func, default_values, default_already_canonicalized) local function canonicalize_and_insert(arg) local canon_arg = canon_func(arg) if type(canon_arg) == "string" then arg.term = canon_arg m_headword_utilities.insert_termobj_combining_duplicates(dest, arg) else for _, canon in ipairs(canon_arg) do m_headword_utilities.combine_termobj_decorations(canon, arg) m_headword_utilities.insert_termobj_combining_duplicates(dest, canon) end end end for _, arg in ipairs(args) do if arg.term == "+" then for _, val in ipairs(default_values) do val = shallowCopy(val) m_headword_utilities.combine_termobj_decorations(val, arg) if default_already_canonicalized then m_headword_utilities.insert_termobj_combining_duplicates(dest, val) else canonicalize_and_insert(val) end end else canonicalize_and_insert(arg) end end end local all_part_default_specs = {} local function process_and_canonicalize_s_form(arg) local form = arg.term if form == "+" then error("Internal error: '+' should have been converted to '^' by now") end if form == "^" or form == "++" or form == "+l" or form == "+!" or form == "+'" or form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then insert(all_part_default_specs, shallowCopy(arg)) end return canonicalize_s_form(form) end process_argument(par1s, pres_3sgs, process_and_canonicalize_s_form, {{term = "^"}}) if not all_part_default_specs[1] then all_part_default_specs[1] = {term = "^"} end process_argument(par2s, pres_ptcs, function(arg) return canonicalize_ing_form(arg.term) end, all_part_default_specs) process_argument(par3s, pasts, function(arg) return canonicalize_ed_form(arg.term) end, all_part_default_specs) process_argument(par4s, past_ptcs, function(arg) return canonicalize_en_form(arg.term) end, pasts, "default already canonicalized") end ------------------------------------------- INSERT INFLECTIONS ------------------------------------------ insert_inflection(data, pres_3sgs, "third-person singular simple present", "s-verb-form") insert_inflection(data, pres_ptcs, "present participle", "ing-form") if deepEquals(pasts, past_ptcs) then insert_inflection(data, pasts, "simple past and past participle", "ed-form", "no simple past or past participle") else insert_inflection(data, pasts, "simple past", "spast") insert_inflection(data, past_ptcs, "past participle", "past|part") end if pagename:find(" ") then -- Check for placeholder "it" local words = split(pagename, " ") for _, word in ipairs(words) do if word == "it" or word == "its" or word == "it's" then insert(data.categories, langname .. ' ' .. cat_word(lang, 'terms with placeholder "it"')) break end end -- Check for phrasal verbs local phrasal_adverbs = list_to_set{ -- NOTE: This should only contain common phrasal adverbs, not random words like [[low]], -- [[adrift]], etc. "aback", "about", "above", "across", "after", "against", "ahead", "along", "apart", "around", "as", "aside", "at", "away", "back", "before", "behind", "below", "between", "beyond", "by", "down", "for", "forth", "from", "in", "into", "of", "off", "on", "onto", "out", "over", "past", "round", "through", "to", "together", "towards", "under", "up", "upon", "with", "without", } local allowed_non_adverb_words = list_to_set{ "it", "one", "oneself", "someone", } local base = pagename local seen_adverbs = {} -- Only consider a verb to be phrasal if it consists of a single base verb followed exclusively by either -- adverbs from `phrasal_adverbs` or placeholder words from `allowed_non_adverb_words`, where at -- least one following word is from `phrasal_adverbs` (hence [[can it]] is not a phrasal verb). while true do local prev, word = base:match("^(.+) (.-)$") if not prev then break end if phrasal_adverbs[word] then insert(seen_adverbs, word) elseif allowed_non_adverb_words[word] then -- do nothing else break end base = prev end if not base:find(" ") and seen_adverbs[1] then insert(data.categories, langname .. " " .. cat_word(lang, "phrasal verbs")) for i = #seen_adverbs, 1, -1 do insert(data.categories, langname .. ' ' .. cat_word(lang, "phrasal verbs formed with") .. ' "' .. seen_adverbs[i] .. '\"') end end end end, } ----------------------------------------------------------------------------------------- -- Suffix forms -- ----------------------------------------------------------------------------------------- pos_functions["suffix forms"] = { params = { [1] = {required = true, list = true, disallow_holes = true}, }, func = function(args, data, is_suffix) local suffix_type = {} for _, typ in ipairs(args[1]) do insert(suffix_type, typ .. "-forming suffix") end insert(data.inflections, {label = "non-lemma form of " .. m_table.serialCommaJoin(suffix_type, {conj = "or"})}) end, } return export 5duit8o5s5658vqhagczvdy4r9pg8bv 55053 55052 2026-09-30T21:58:33Z Deadend0914 7211 55053 Scribunto text/plain local export = {} local pos_functions = {} --[==[ Author from 2020 on: mostly Benwing2, with significant contributions from Theknightwho. Based on a prior version by Rua (by now mostly rewritten), with contributions from Erutuon and others (see history for full attribution). ]==] local force_cat = false -- for testing; if true, categories appear in non-mainspace pages local require = require local require_when_needed = require("Module:require when needed") local en_utilities_module = "Module:en-utilities" local headword_utilities_module = "Module:headword utilities" local headword_module = "Module:headword" local inflection_utilities_module = "Module:inflection utilities" local parse_utilities_module = "Module:parse utilities" local JSON_module = "Module:JSON" local labels_module = "Module:labels" local links_module = "Module:links" local parameters_module = "Module:parameters" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local utilities_module = "Module:utilities" local yesno_module = "Module:yesno" local iut = require_when_needed(inflection_utilities_module) local put = require_when_needed(parse_utilities_module) local m_headword_utilities = require_when_needed(headword_utilities_module) local add_links_to_multiword_term = require_when_needed(headword_utilities_module, "add_links_to_multiword_term") local add_suffix = require_when_needed(en_utilities_module, "add_suffix") local apply_link_modifiers = require_when_needed(headword_utilities_module, "apply_link_modifiers") local concat = table.concat local deepEquals = require_when_needed(table_module, "deepEquals") local dump = mw.dumpObject local format_categories = require_when_needed(utilities_module, "format_categories") local full_headword = require_when_needed(headword_module, "full_headword") local get_label_info = require_when_needed(labels_module, "get_label_info") local get_link_page = require_when_needed(links_module, "get_link_page") local glossary_link = require_when_needed(headword_utilities_module, "glossary_link") local insert = table.insert local insertIfNot = require_when_needed(table_module, "insertIfNot") local ipairs = ipairs local is_regular_plural = require_when_needed(en_utilities_module, "is_regular_plural") local list_to_set = require_when_needed(table_module, "listToSet") local pairs = pairs local process_params = require_when_needed(parameters_module, "process") local remove = table.remove local remove_links = require_when_needed(links_module, "remove_links") local replacement_escape = require_when_needed(string_utilities_module, "replacement_escape") local shallowCopy = require_when_needed(table_module, "shallowCopy") local singularize = require_when_needed(en_utilities_module, "singularize") local split = require_when_needed(string_utilities_module, "split") local toJSON = require_when_needed(JSON_module, "toJSON") local toNFD = mw.ustring.toNFD local type = type local ulen = require_when_needed(string_utilities_module, "len") local ulower = require_when_needed(string_utilities_module, "lower") local umatch = require_when_needed(string_utilities_module, "match") local u = require_when_needed(string_utilities_module, "char") local ugsub = require_when_needed(string_utilities_module, "gsub") local lang = require("Module:languages").getByCode("en") local langname = lang:getCanonicalName() -- Old English localization: category wordings come from [[Module:floccnaman]] -- (see that module); the English wording is kept when a translation is missing. -- Loaded lazily and optionally, so a missing module can never break a page. local floccnaman_module = "Module:floccnaman" local floccnaman local function get_floccnaman() if floccnaman == nil then local ok, mod = pcall(require, floccnaman_module) floccnaman = ok and mod or false end return floccnaman or nil end -- Old English wording for one category word, or the English wording unchanged. local function cat_word(l, text) local mod = get_floccnaman() return mod and mod.get(l, text) or text end local list_param = {list = true, disallow_holes = true} local list_allow_holes = {list = true, allow_holes = true} local boolean_param = {type = "boolean"} local function ine(val) if val == "" then return nil else return val end end local function track(page) require("Module:debug/track")("en-headword/" .. page) return true end ------------------------------------------- UTILITY FUNCTIONS ------------------------------------------ -- Parse and return an inflection not requiring additional processing. The raw arguments come from `args[field]`, which -- is parsed for inline modifiers. local function parse_inflection(args, field, is_head) local argfield = field if type(argfield) == "table" then argfield = argfield[1] end return m_headword_utilities.parse_term_list_with_modifiers { paramname = field, forms = args[argfield], splitchar = ",", is_head = is_head, } end -- Insert the parsed inflections in `terms` (as parsed by `parse_inflection`) into `data.inflections`, with label -- `label` and optional accelerator spec `accel`. local function insert_inflection(data, terms, label, accel, no_label) for _, termobj in ipairs(terms) do m_headword_utilities.remove_termobj_field_modifiers(termobj) end m_headword_utilities.insert_inflection { headdata = data, terms = terms, label = label, no_label = no_label, accel = accel and {form = accel} or nil, } end -- Insert a fixed label `label` into the inflections for `data`. If `originating_term` is supplied, copy the decorations -- from it into the fixed label. local function insert_fixed_inflection(data, label, originating_term) m_headword_utilities.insert_fixed_inflection { headdata = data, originating_term = originating_term, label = label, } 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 -- These functions are used directly in the <> format as well as in the utility functions #2 below. local function compute_double_last_cons_stem(term) local last_cons = term:match("([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])$") if not last_cons then error("Verb stem '" .. term .. "' must end in a consonant to use ++") end return term .. last_cons end local function compute_plusplus_s_form(term, default_s_form) if term:find("[szx]$") then -- regas -> regasses, derez -> derezzes return compute_double_last_cons_stem(term) .. "es" else return default_s_form end end -- On English Wiktionary, {{tl|en-head}} is the template that means "interjection"; it invokes this module without -- a part of speech. This wiki's template namespace is named "Bysen", not "Template", so the namespace id and the -- title without its namespace prefix are compared instead of a hard-coded "Template:en-head". -- Scribunto's title object exposes these as the fields `namespace`, `nsText` and `fullText`; it has no -- getNamespace()/getBaseText() methods, so those must not be used here. local function is_en_head_template() local title = mw.title.getCurrentTitle() return title.namespace == 10 and title.fullText == title.nsText .. ":en-head" end -- The main entry point. -- This is the only function that can be invoked from a template. function export.show(frame) local iparams = { [1] = true, } local iargs = require("Module:parameters").process(frame.args, iparams) local parargs = frame:getParent().args local poscat = iargs[1] local pos_in_1 = not poscat if pos_in_1 then poscat = ine(parargs[1]) or is_en_head_template() and "interjection" or error("Part of speech must be specified in 1=") poscat = require(headword_module).canonicalize_pos(poscat) end local indexing_poscat = pos_in_1 and "head" or poscat local params = { ["head"] = list_param, ["id"] = true, ["json"] = boolean_param, ["sort"] = true, ["splithyph"] = boolean_param, ["nosplithyph"] = boolean_param, ["hyphspace"] = boolean_param, ["nolink"] = boolean_param, ["nolinkhead"] = {type = "boolean_param", alias_of = "nolink"}, ["suffix"] = boolean_param, ["nosuffix"] = boolean_param, ["nomultiwordcat"] = boolean_param, ["abbr"] = list_param, ["the"] = true, ["def"] = {alias_of = "the"}, ["pagename"] = true, -- for testing } if not pos_in_1 then params[1] = {required = true} -- required but ignored as already processed above end local pos_data = pos_functions[indexing_poscat] local pos_func if pos_data then local pos_params = pos_data.params if pos_params then for key, val in pairs(pos_params) do params[key] = val end end pos_func = pos_data.func end local args = process_params(parargs, params) -- Account for unsupported titles, e.g. 'C|N>K' instead of 'Unsupported titles/C through N to K'. local pagename = args.pagename or mw.loadData("Module:headword/data").pagename local user_specified_heads = parse_inflection(args, "head", "is_head") local heads = user_specified_heads local autohead if args.nolink or not pagename:find("[ '%-]") then autohead = pagename else local en_no_split_apostrophe_words = list_to_set { "one's", "someone's", "he's", "she's", "it's", } local en_include_hyphen_prefixes = list_to_set { -- We don't include things that are also words even though they are often (perhaps mostly) prefixes, e.g. -- "be", "counter", "cross", "extra", "half", "mid", "over", "pan", "under". "acro", "acousto", "Afro", "agro", "anarcho", "angio", "Anglo", "ante", "anti", "arch", "auto", "bi", "bio", "cis", "co", "cryo", "crypto", "de", "demi", "eco", "electro", "Euro", "ex", "Greco", "hemi", "hydro", "hyper", "hypo", "infra", "Indo", "inter", "intra", "Judeo", "macro", "meta", "micro", "mini", "multi", "neo", "neuro", "non", "para", "peri", "post", "pre", "pro", "proto", "pseudo", "re", "semi", "sub", "super", "trans", "un", "vice", } local function is_english(term) local title = mw.title.new(term) if title and title.exists then local content = title:getContent() if content and content:find("==English==\n") then return true end end return false end local function en_split_hyphen_when_space(word) if not word:find("-", nil, true) then return nil end if args.hyphspace then return "[[" .. word:gsub("%-+", " ") .. "|" .. word .. "]]" end if args.nosplithyph then return "[[" .. word .. "]]" end if not args.splithyph then local space_word = word:gsub("%-+", " ") if is_english(space_word) then return "[[" .. space_word .. "|" .. word .. "]]" end if is_english(word) then return "[[" .. word .. "]]" end end return nil end local function en_split_apostrophe(word) local base = word:match("^(.*)'s$") if base then return "[[" .. base .. "]][[-'s|'s]]" end -- Only treat final apostrophe as possessive if preceded by something that looks like a plural ending in /z/. -- In particular we don't want to do it for words like [[truckin']]. base = word:match("^(.*[sxz])'$") if base then if base:find("s$") then local sg = singularize(base) if is_english(sg) then return "[[" .. sg .. "|" .. base .. "]][[-'|']]" end end return "[[" .. base .. "]][[-'|']]" end return "[[" .. word .. "]]" end autohead = add_links_to_multiword_term(pagename, { split_hyphen_when_space = en_split_hyphen_when_space, split_apostrophe = en_split_apostrophe, no_split_apostrophe_words = en_no_split_apostrophe_words, include_hyphen_prefixes = en_include_hyphen_prefixes, }) end if not heads[1] then heads = {{term = autohead}} else for _, headobj in ipairs(heads) do local head = headobj.term if head:find("^~") then head = apply_link_modifiers(autohead, head:sub(2), lang) headobj.term = head elseif head:find("^[!?]$") then -- If explicit head= just consists of ! or ?, add it to the end of the default head. headobj.term = autohead .. head end if head == autohead then track("redundant-head") end end end -- handle the=/def= if args.the == "~" then local newheads = {} for _, headobj in ipairs(heads) do local barehead = shallowCopy(headobj) insert(newheads, barehead) headobj.term = "the " .. headobj.term insert(newheads, headobj) end heads = newheads elseif args.the then local the = require(yesno_module)(args.the) if the then for _, headobj in ipairs(heads) do headobj.term = "the " .. headobj.term end end end local data = { lang = lang, pos_category = poscat, categories = {}, heads = heads, user_specified_heads = user_specified_heads, -- We use our own splitting algorithm so the redundant head cat will be inaccurate. no_redundant_head_cat = true, inflections = {}, nomultiwordcat = args.nomultiwordcat, sort_key = args.sort, pagename = pagename, id = args.id, force_cat_output = force_cat, } local function inscat(cat) insert(data.categories, langname .. " " .. cat_word(lang, cat)) end local is_suffix = false if args.suffix or not args.nosuffix and pagename:find("^%-") and not pagename:find("^%-%-") and poscat ~= "suffix forms" then is_suffix = true data.pos_category = "suffixes" local singular_poscat = singularize(poscat) inscat(singular_poscat .. "-forming suffixes") insert(data.inflections, {label = singular_poscat .. "-forming suffix"}) end if pos_func then pos_func(args, data, is_suffix) end local extra_categories = {} if pagename:find("[Qq]") then -- Check for q not followed by u. We want to exclude things like [[13q deletion syndrome]] and [[BFOQ]] that -- don't have a lowercase letter on either side, as well as things like [[& seq.]] and [[acq.]] that are -- abbreviations for words containing a following u. -- -- Approximate range of combining diacritics; we want to remove them so the checks below for -- a lowercase letter next to the q aren't tripped up by diacritics on the letter. local u300 = u(0x0300) local u36F = u(0x036F) local pagename_no_diacritics = ugsub(toNFD(pagename), "[" .. u300 .. "-" .. u36F .. "]", "") if pagename_no_diacritics:find("[Qq][a-tv-z]") or pagename_no_diacritics:find("[a-z]q[^u.]") or pagename_no_diacritics:find("[a-z]q$") then inscat("words containing Q not followed by U") end end -- toNFD performs decomposition, so letters that decompose to an ASCII -- vowel and a diacritic, such as é, are counted as vowels and do not do not -- need to be included in the pattern. if not umatch(ulower(toNFD(pagename)), "[aeiouyæœøəªºαεηιουω]") then inscat("words spelled wiþūtan vowels") end if pagename:find("yre$") then inscat('words ending in "-yre"') end if not pagename:find(" ") and ulen(pagename) >= 25 then insert(extra_categories, langname .. " " .. cat_word(lang, "Long words")) end if pagename:find("^[^aeiou ]*a[^aeiou ]*e[^aeiou ]*i[^aeiou ]*o[^aeiou ]*u[^aeiou ]*$") then inscat("words that use all vowels in alphabetical order") end parse_and_insert_inflection(data, args, "abbr", "abbreviation") if args.json then return toJSON(data) end return full_headword(data) .. (extra_categories[1] and format_categories(extra_categories, lang, args.sort) or "") end local function make_default_comparative(word) if word == "good" or word == "well" then return {"better"} elseif word == "bad" or word == "badly" then return {"worse"} elseif word == "far" then return {"further", "farther"} else return {add_suffix(word, "r")} end end local function make_default_superlative(word) if word == "good" or word == "well" then return {"best"} elseif word == "bad" or word == "badly" then return {"worst"} elseif word == "far" then return {"furthest", "farthest"} else return {add_suffix(word, "st.superlative")} end end -- This function does the common work between adjectives and adverbs. local function process_comparative_args(data, args, plpos) local pagename = data.pagename local comps = parse_inflection(args, 1) local sups = parse_inflection(args, "sup") local outcomps, outsups if args.componly then if comps[1] then error("Can't specify comparatives of comparative-only " .. plpos) end insert(data.inflections, {label = glossary_link("comparative") .. " form only"}) insert(data.categories, langname .. " " .. cat_word(lang, "comparative-only " .. plpos)) -- Set to empty list so we don't get any comparatives output, but process superlatives if specified. outcomps = {} if not sups[1] then -- Set to empty list so we don't get any superlatives output unless explicitly given. outsups = {} end elseif args.suponly then if comps[1] or sups[1] then error("Can't specify comparatives or superlatives of or superlative-only " .. plpos) end insert(data.inflections, {label = glossary_link("superlative") .. " form only"}) insert(data.categories, langname .. " " .. cat_word(lang, "superlative-only " .. plpos)) return end -- If the first parameter is ?, then don't show anything, just return. if comps[1] and comps[1].term == "?" then if comps[2] then error("Can't specify additional comparatives along wiþ '?'") end if sups[1] then error("Can't specify superlatives along wiþ '?' for the comparative") end return end if comps[1] and comps[1].term == "-" then local hyphencomp = remove(comps, 1) -- Remove the "-" but retain for decorations. -- Not (generally) comparable; may occasionally have a comparative if comps[1] then insert_fixed_inflection(data, "not generally <<comparable>>", hyphencomp) elseif not sups[1] then insert_fixed_inflection(data, "not <<comparable>>", hyphencomp) insert(data.categories, langname .. " " .. cat_word(lang, "uncomparable " .. plpos)) return else -- No comparative, but a superlative. insert_inflection() will correctly generate 'no comparative' if we -- pass in "-" as the value. outcomps = {hyphencomp} end elseif not comps[1] then comps = {{term = "more"}} end if not outcomps then -- not if we set `outcomps` to "-" above or processed a comparative-only term outcomps = {} -- Go over each parameter given and create a comparative and superlative form. for _, compobj in ipairs(comps) do local comp = compobj.term if comp == "-" then error("Comparative of '-' only allowed as first comparative") end if comp == "+" then comp = "+more" elseif comp == "more" and pagename ~= "many" and pagename ~= "much" then comp = "+more" elseif comp == "further" and pagename ~= "far" then comp = "+further" elseif comp == "better" and pagename ~= "good" and pagename ~= "well" then comp = "+better" elseif comp:find("~") then comp = comp:gsub("~", replacement_escape(pagename)) end compobj.origterm = comp if comp == "+more" then comp = "more [[" .. pagename .. "]]" elseif comp == "+further" then comp = {"further [[" .. pagename .. "]]", "farther [[" .. pagename .. "]]"} elseif comp == "+better" then comp = "better [[" .. pagename .. "]]" elseif comp == "er" then -- Add -er. comp = add_suffix(pagename, "r") elseif comp == "ier" then if pagename:sub(-1) ~= "y" then error("Can't specify 'ier' comparative unless the term ends wiþ 'y': " .. pagename) end comp = pagename:gsub("e?y$", "ier") elseif comp:find("^%+") then local special = m_headword_utilities.get_special_indicator(comp, "noerror") if special then comp = m_headword_utilities.handle_multiword(pagename, special, make_default_comparative) end end if type(comp) == "table" and not comp[2] then comp = comp[1] end if type(comp) == "table" then for i = 1, #comp - 1 do local outobj = shallowCopy(compobj) outobj.term = comp[i] insert(outcomps, outobj) end compobj.term = comp[#comp] insert(outcomps, compobj) else compobj.term = comp insert(outcomps, compobj) end end end if sups[1] and sups[1].term == "-" then if sups[2] then error("Can't specify '-' as superlative followed by further values") end -- No superlative. insert_inflection() will correctly generate 'no superlative' if we pass in "-" as the value. outsups = sups else if not sups[1] then sups = {{term = "+"}} end end -- `outsups` will be set if we set `outsups` to "-" above or processed a comparative-only term without superlatives. if not outsups then outsups = {} local function process_sup(sup, special, supobj, compobj) if special then sup = m_headword_utilities.handle_multiword(pagename, special, make_default_superlative) elseif sup == "-" or sup == "+" then error(("Internal error: Superlative value of '%s' should have been handled earlier"):format(sup)) elseif sup == "+most" then sup = "most [[" .. pagename .. "]]" elseif sup == "+furthest" then sup = {"furthest [[" .. pagename .. "]]", "farthest [[" .. pagename .. "]]"} elseif sup == "+best" then sup = "best [[" .. pagename .. "]]" elseif sup == "est" then -- Add -est. sup = add_suffix(pagename, "st.superlative") elseif sup == "iest" then if pagename:sub(-1) ~= "y" then error("Can't specify 'iest' superlative unless the term ends wiþ 'y': " .. pagename) end sup = pagename:gsub("e?y$", "iest") end if type(sup) == "table" and not sup[2] then sup = sup[1] end if compobj then supobj = shallowCopy(supobj) supobj = m_headword_utilities.combine_termobj_decorations(supobj, compobj) end if type(sup) == "table" then for i = 1, #sup - 1 do local outobj = shallowCopy(supobj) outobj.term = sup[i] insert(outsups, outobj) end supobj.term = sup[#sup] insert(outsups, supobj) else supobj.term = sup insert(outsups, supobj) end end for _, supobj in ipairs(sups) do local sup = supobj.term if sup == "-" then error("Superlative of '-' only allowed as first superlative") end if sup == "+" then if not comps[1] then error("Superlative of '+' can't be specified when there are no comparatives") end for _, compobj in ipairs(comps) do local comp = compobj.origterm local special if comp == "+more" then sup = "+most" elseif comp == "+further" then sup = "+furthest" elseif comp == "+better" then sup = "+best" elseif comp == "er" then sup = "est" elseif comp == "ier" then sup = "iest" else if comp:find("^%+") then special = m_headword_utilities.get_special_indicator(comp, "noerror") end if not special then -- If the full comparative was given, then derive the superlative by replacing -er with -- -est. if comp:sub(-2) == "er" then sup = comp:sub(1, -3) .. "est" else error(("The superlative cannot be derived automatically from comparative '%s' because it doesn't end in -er"):format(comp)) end end end process_sup(sup, special, supobj, compobj) end else local special = m_headword_utilities.get_special_indicator(sup, "noerror") -- Do some work here rather than in process_sup() so we don't end up double-processing a term with a '~' -- in it or a term that happens to be 'most' or similar after substitution of ~ in the comparative. if not special then if sup == "most" and pagename ~= "many" and pagename ~= "much" then sup = "+most" elseif sup == "furthest" and pagename ~= "far" then sup = "+furthest" elseif sup == "best" and pagename ~= "good" and pagename ~= "well" then sup = "+best" elseif sup:find("~") then sup = sup:gsub("~", replacement_escape(pagename)) end end process_sup(sup, special, supobj) end end end insert_inflection(data, outcomps, "<<comparative>>", "comparative") insert_inflection(data, outsups, "<<superlative>>", "superlative") end pos_functions["adjectives"] = { params = { [1] = list_param, ["comp_qual"] = {list = "comp\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the comparative value", }, ["sup"] = list_param, ["sup_qual"] = {list = "sup\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the superlative value", }, ["componly"] = boolean_param, ["suponly"] = boolean_param, }, func = function(args, data) -- Process the comparatives and superlatives. process_comparative_args(data, args, "adjectives") end, } pos_functions["adverbs"] = { params = { [1] = list_param, ["comp_qual"] = {list = "comp\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the comparative value", }, ["sup"] = list_param, ["sup_qual"] = {list = "sup\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the superlative value", }, ["componly"] = boolean_param, ["suponly"] = boolean_param, }, func = function(args, data) -- Process the comparatives and superlatives. process_comparative_args(data, args, "adverbs") end, } local function escape(str) return (str:gsub("\\([:#])", "\\\\%1") :gsub("[:#]", "\\%0")) end local function canonicalize_plural(pl, pagename, pos) if pl == "+" then return escape(add_suffix(pagename, "s.plural", pos)) elseif pl == "++" then return escape(compute_plusplus_s_form(pagename, add_suffix(pagename, "s.plural", pos))) elseif pl == "*" then return escape(pagename) elseif pl == "ies" then if pagename:sub(-1) == "y" then return escape(pagename:gsub("e?y$", pl)) end error("Can't specify 'ies' plural unless the term ends wiþ 'y'.") elseif pl == "s" or pl == "es" or pl == "'s" then return escape(pagename .. pl) end end local function do_nouns(args, data, pos) local pagename = data.pagename pos = pos or "noun" local plurals = parse_inflection(args, 1) local function insert_plurale_tantum_inflections(is_plural_only, originating_label) if args.sg[1] then insert_fixed_inflection(data, "normally plural", originating_label) parse_and_insert_inflection(data, args, "sg", "singular") elseif is_plural_only then insert_fixed_inflection(data, "plural only", originating_label) end if args.attr[1] then parse_and_insert_inflection(data, args, "attr", "attributive") end end local function first_pl_term() return plurals[1] and plurals[1].term or nil end if first_pl_term() == "p" then -- plurale tantum if plurals[2] then error("Wiþ plurale tantum nama, can't specify more than one plural") end data.genders = {"p"} -- this should auto-insert the correct 'pluralia tantum' category insert_plurale_tantum_inflections("plural only", plurals[1]) return end local function inscat(cat) insert(data.categories, langname .. " " .. cat_word(lang, cat)) end local need_default_plural = pos == "noun" if first_pl_term() == "sp" then -- construed as singular or plural sp = remove(plurals, 1) -- Remove the "sp" but retain it for its decorations. inscat("naman construed as singular or plural") data.genders = {"s", "p"} -- this should auto-insert the correct 'pluralia tantum' category insert_plurale_tantum_inflections(nil, sp) need_default_plural = false elseif first_pl_term() == "-" then -- Untellendlīc noun; may occasionally have a plural local hyphpl = remove(plurals, 1) -- Remove the "-" but retain for decorations. inscat("untellendlīc naman") -- If plural forms were given explicitly, then show "oft" if plurals[1] then insert_fixed_inflection(data, "oft <<untellendlīc>>", hyphpl) else insert_fixed_inflection(data, "<<untellendlīc>>", hyphpl) end need_default_plural = false elseif first_pl_term() == "#" then -- oft tellendlīc (e.g., "grilled cheese") local hashpl = remove(plurals, 1) -- Remove the "#" but retain for decorations. insert_fixed_inflection(data, "oft <<tellendlīc>>", hashpl) inscat("untellendlīc naman") inscat("tellendlīc naman") -- If no plural was given, add a default one now if not plurals[1] then plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end elseif first_pl_term() == "~" then -- Mixed tellendlīc/untellendlīc noun, always has a plural local tildepl = remove(plurals, 1) -- Remove the "~" but retain for decorations. insert_fixed_inflection(data, "<<tellendlīc>> and <<untellendlīc>>", tildepl) inscat("untellendlīc naman") inscat("tellendlīc naman") -- If no plural was given, add a default one now if not plurals[1] then plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end end -- Plural is unknown if first_pl_term() == "?" then local questionpl = remove(plurals, 1) -- Remove the "?" but retain for decorations. -- Not desired; see [[Wiktionary:Tea_room/2021/August#"Plural unknown or uncertain"]] -- insert_fixed_inflection(data, "manigfeald unġecnāwen oþþe ungewiss", questionpl) inscat("naman wiþ unġecnāwen oþþe ungewiss manigfealde") if plurals[1] then error("Ne cann āmearcian sweotole manigfealde andlang wiþ '?' of unġecnāwen/ungewiss manigfeald") end return end -- Plural is not attested if first_pl_term() == "!" then local exclampl = remove(plurals, 1) -- Remove the "!" but retain for decorations. insert_fixed_inflection(data, "plural not attested", exclampl) inscat("naman wiþ unattested manigfealde") if plurals[1] then error("Ne cann āmearcian sweotole manigfealde andlang wiþ '!' of gecȳþed manigfeald") end return end -- If no plural was given, maybe add a default one, otherwise (when "-" was given or proper noun) return. if not plurals[1] then if not need_default_plural then inscat("untellendlīc naman") return end plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end -- There are plural forms to show, so show them. inscat("tellendlīc naman") local irregular, indeclinable for i, pl in ipairs(plurals) do local canon_pl = canonicalize_plural(pl.term, pagename, pos) if canon_pl then pl.term = canon_pl end local pl_term = get_link_page(pl.term, lang) if not (pagename:find(" ") or is_regular_plural(pl_term, pagename)) then irregular = true if pl_term == pagename then indeclinable = true end end end if irregular then inscat("naman wiþ fremde manigfealde") end if indeclinable then inscat("undeclīnigendlīce naman") end insert_inflection(data, plurals, "plural", "p") end -- Return the parameters to be used for nouns and proper nouns. Currently the same. local noun_params = { [1] = list_param, ["pl\1qual"] = {list = true, allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the plural", }, -- The following four only used for pluralia tantum (1=p) ["sg"] = list_param, ["attr"] = list_param, } pos_functions["naman"] = { params = noun_params, func = do_naman, } pos_functions["agennaman"] = { params = noun_params, func = function(args, data) return do_nouns(args, data, "agennama") end, } local function base_default_verb_forms(verb) return escape(add_suffix(verb, "s.verb")), escape(add_suffix(verb, "ing")), escape(add_suffix(verb, "d")) end local function default_verb_forms(verb) local full_s_form, full_ing_form, full_ed_form = base_default_verb_forms(verb) if verb:find(" ") then local first, rest = verb:match("^(.-)( .*)$") local first_s_form, first_ing_form, first_ed_form = base_default_verb_forms(first) return full_s_form, full_ing_form, full_ed_form, first_s_form .. rest, first_ing_form .. rest, first_ed_form .. rest, first, rest else return full_s_form, full_ing_form, full_ed_form, nil, nil, nil, nil, nil end end local function compute_double_last_cons_stem_of_split_verb(verb, ending) local first, rest = verb:match("^(.-)( .*)$") if not first then error("Word '" .. verb .. "' mōt hæfþ rum in sīde tō notienne **") end local last_cons = first:match("([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])$") if not last_cons then error("Fyrest word '" .. first .. "' mōt ende wiþ samod swēgend tō notienne **") end return first .. last_cons .. ending .. rest end local function check_non_nil_star_form(form, pagename) if form == nil then error("Word '" .. pagename .. "' mōt hæfþ rum in sīde tō notienne *, **, *l, *! or *'") end return form end local function sub_tilde(form, pagename) if not form then return nil end if form:find("~") then form = form:gsub("~", replacement_escape(pagename)) end return form end local deprecated_qual_replaced_by_inline_modifier = { list = true, allow_holes = true, replaced_by = false, instead = "use an inline modifier <q:...> or <l:...> on the value" } pos_functions["verbs"] = { params = { [1] = {list = "pres_3sg", disallow_holes = true}, ["pres_3sg\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [2] = {list = "pres_ptc", disallow_holes = true}, ["pres_ptc\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [3] = {list = "past", disallow_holes = true}, ["past\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [4] = {list = "past_ptc", allow_holes = true}, ["past_ptc\1_qual"] = deprecated_qual_replaced_by_inline_modifier, ["noautolinkverb"] = boolean_param, ["angle_bracket"] = boolean_param, }, func = function(args, data) -- Get parameters local par1s local par2s = parse_inflection(args, {2, "pres_ptc"}) local par3s = parse_inflection(args, {3, "past"}) local par4s = parse_inflection(args, {4, "past_ptc"}) local pres_3sgs, pres_ptcs, pasts, past_ptcs local pagename = data.pagename ------------------------------------------- UTILITY FUNCTIONS #2 ------------------------------------------ -- These functions are used in both in the separate-parameter format and in the override params such as past_ptc2=. local full_default_s, full_default_ing, full_default_ed, split_default_s, split_default_ing, split_default_ed local lemma local function set_lemma_and_default_forms(the_lemma) lemma = the_lemma full_default_s, full_default_ing, full_default_ed, split_default_s, split_default_ing, split_default_ed, lemma_first, lemma_rest = default_verb_forms(the_lemma) end local function canonicalize_s_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_s elseif form == "*" then return check_non_nil_star_form(split_default_s, lemma) elseif form == "++" then return compute_plusplus_s_form(lemma, full_default_s) elseif form == "**" then if lemma:find("^[^ ]*[szx] ") then return compute_double_last_cons_stem_of_split_verb(lemma, "es") else return check_non_nil_star_form(split_default_s, lemma) end elseif form == "+!" then return lemma .. "s" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "s" .. lemma_rest elseif form == "+'" then return lemma .. "'s" elseif form == "*'" then return check_non_nil_star_form(lemma_first) .. "'s" .. lemma_rest elseif form == "+l" then if lemma:find("[szx]$") then return {{term = full_default_s, l = {"US"}}, {term = compute_plusplus_s_form(lemma, full_default_s), l = {"UK"}}} else return compute_plusplus_s_form(lemma, full_default_s) end elseif form == "*l" then if lemma:find("^[^ ]*[szx] ") then return {{term = check_non_nil_star_form(split_default_s, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "es"), l = {"UK"}}} else return check_non_nil_star_form(split_default_s, lemma) end else return sub_tilde(form, lemma) end end local function canonicalize_ing_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_ing elseif form == "*" then return check_non_nil_star_form(split_default_ing, lemma) elseif form == "++" then return compute_double_last_cons_stem(lemma) .. "ing" elseif form == "**" then return compute_double_last_cons_stem_of_split_verb(lemma, "ing") elseif form == "+!" then return lemma .. "ing" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "ing" .. lemma_rest elseif form == "+'" then return lemma .. "'ing" elseif form == "*'" then return check_non_nil_star_form(lemma_first) .. "'ing" .. lemma_rest elseif form == "+l" then return {{term = full_default_ing, l = {"US"}}, {term = compute_double_last_cons_stem(lemma) .. "ing", l = {"UK"}}} elseif form == "*l" then return {{term = check_non_nil_star_form(split_default_ing, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "ing"), l = {"UK"}}} else return sub_tilde(form, lemma) end end local function canonicalize_ed_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_ed elseif form == "*" then return check_non_nil_star_form(split_default_ed, lemma) elseif form == "++" then return compute_double_last_cons_stem(lemma) .. "ed" elseif form == "+!" then return lemma .. "ed" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "ed" .. lemma_rest elseif form == "+'" then return {{term = lemma .. "'d"}, {term = lemma .. "'ed"}} elseif form == "*'" then return {{term = check_non_nil_star_form(lemma_first) .. "'d" .. lemma_rest}, {term = check_non_nil_star_form(lemma_first) .. "'ed" .. lemma_rest}} elseif form == "**" then return compute_double_last_cons_stem_of_split_verb(lemma, "ed") elseif form == "+l" then return {{term = full_default_ed, l = {"US"}}, {term = compute_double_last_cons_stem(lemma) .. "ed", l = {"UK"}}} elseif form == "*l" then return {{term = check_non_nil_star_form(split_default_ed, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "ed"), l = {"UK"}}} else return sub_tilde(form, lemma) end end -- FIXME: options should be "+", "*", "++", "**", "+n", "*n", "++n" and "**n", but not "n" local function canonicalize_en_form(form) if form == "n" then track("n4") return add_suffix(lemma, "n") end return canonicalize_ed_form(form) end --------------------------------- MAIN PARSING/CONJUGATING CODE -------------------------------- local is_angle_bracket = args.angle_bracket if is_angle_bracket then if par2s[1] or par3s[1] or par4s[1] then error("Can't specify explicit values for 2=, 3= or 4= along wiþ the angle-bracket format") end elseif is_angle_bracket == nil and not par2s[1] and not par3s[1] and not par4s[1] and not args[1][2] and args[1][1] and args[1][1]:find("<") then if put.term_contains_top_level_html(args[1][1]) then -- Often, term_contains_top_level_html() returns true on the angle-bracket format, which would -- make the pcall() below succeed but leave the angle brackets as-is. Check for this and only do the -- pcall() if term_contains_top_level_html() returns false. is_angle_bracket = true else -- If it's ambiguous whether it's an angle-bracket format or separate params with an inline modifier, -- try to parse as the latter. If an error occurs, treat as the former. local ok ok, par1s = pcall(parse_inflection, args, {1, "pres_3sg"}) if not ok then par1s = nil is_angle_bracket = true end end end if is_angle_bracket then -------------------------- ANGLE-BRACKET FORMAT -------------------------- -- (0) Expand multiword term with angle brackets just on the first word. local arg11 = args[1][1] if arg11:find("^<.*>$") and pagename:find(" ") then local first, rest = pagename:match("^(.-)( .*)$") arg11 = first .. arg11 .. rest end -- (1) Parse the indicator specs inside of angle brackets. local function parse_indicator_spec(angle_bracket_spec) local inside = angle_bracket_spec:match("^<(.*)>$") assert(inside) local segments = put.parse_balanced_segment_run(inside, "[", "]") local comma_separated_groups = put.split_alternating_runs(segments, ",") if #comma_separated_groups > 4 then error("Too many comma-separated parts in indicator spec, expected at most 4: " .. angle_bracket_spec) end local function fetch_footnotes(separated_group) local footnotes for j = 2, #separated_group - 1, 2 do if separated_group[j + 1] ~= "" then error("Extraneous text after bracketed footnotes: '" .. concat(separated_group) .. "'") end if not footnotes then footnotes = {} end insert(footnotes, separated_group[j]) end return footnotes end local function fetch_specs(comma_separated_group) if not comma_separated_group then return {{term = "+"}} end local specs = {} local colon_separated_groups = put.split_alternating_runs(comma_separated_group, ":") for _, colon_separated_group in ipairs(colon_separated_groups) do local form = colon_separated_group[1] if form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then error("*, **, *l, *! and *' not allowed inside of indicator specs: " .. angle_bracket_spec) end if form == "" then form = "+" end local termobj = { term = form } local footnotes = fetch_footnotes(colon_separated_group) if footnotes then for _, footnote in ipairs(footnotes) do m_headword_utilities.add_footnote_to_termobj(termobj, footnote) end end insert(specs, termobj) end return specs end local s_specs = fetch_specs(comma_separated_groups[1]) local ing_specs = fetch_specs(comma_separated_groups[2]) local ed_specs = fetch_specs(comma_separated_groups[3]) local en_specs = fetch_specs(comma_separated_groups[4]) return { forms = {}, s_specs = s_specs, ing_specs = ing_specs, ed_specs = ed_specs, en_specs = en_specs, } end local parse_props = { parse_indicator_spec = parse_indicator_spec, } local alternant_multiword_spec = iut.parse_inflected_text(arg11, parse_props) -- (2) Check for user-specified brackets; remove any links from the lemma, but remember the original -- form so we can use it below in the 'lemma_linked' form. -- Check to see if there are brackets in the pre-text or post-text. If so, use the linked lemma (with the -- verb autolinked unless noautolinkverb is given). Otherwise, use the default headword algorithm. local function check_bracket(val) if val:find("%[%[") then alternant_multiword_spec.saw_bracket = true end end for _, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do check_bracket(alternant_or_word_spec.before_text) if alternant_or_word_spec.alternants then for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do for _, word_spec in ipairs(multiword_spec.word_specs) do check_bracket(word_spec.before_text) end check_bracket(multiword_spec.post_text) end end end check_bracket(alternant_multiword_spec.post_text) iut.map_word_specs(alternant_multiword_spec, function(base) if base.lemma == "" then base.lemma = pagename end base.orig_lemma = base.lemma base.lemma = remove_links(base.lemma) if args.noautolinkverb or base.orig_lemma:find("%[%[") then base.linked_lemma = base.orig_lemma else base.linked_lemma = "[[" .. base.orig_lemma .. "]]" end end) -- (3) Conjugate the verbs according to the indicator specs parsed above. local all_verb_slots = { lemma = "infinitive", lemma_linked = "infinitive", s_form = "3|s|pres", ing_form = "pres|ptcp", ed_form = "past", en_form = "past|ptcp", } local function conjugate_verb(base) local function process_specs(slot, specs, canon_func, default_values, default_already_formobj) local function insert_termobj_into_slot(termobj) local formobj = m_headword_utilities.convert_termobj_to_formobj(termobj) -- If the form is -, don't insert any forms, which will result in there being no overall forms -- (in fact it will be nil). We check for that down below and substitute a single "-" as the -- form, which in turn gets turned into special labels like "no present participle". if formobj.form == "-" then if formobj.footnotes then error("Unable to preserve footnotes specified on missing form '-': FIXME: " .. dump(formobj.footnotes)) end else iut.insert_form(base.forms, slot, formobj) end end local function canonicalize_and_insert(arg) local canon_arg = canon_func(arg) if type(canon_arg) == "string" then arg.term = canon_arg insert_termobj_into_slot(arg) else for _, canon in ipairs(canon_arg) do m_headword_utilities.combine_termobj_decorations(canon, arg) insert_termobj_into_slot(canon) end end end for _, arg in ipairs(specs) do if arg.term == "+" then if default_values then -- will be nil if past tense specified as - and no past ptc given for _, val in ipairs(default_values) do val = shallowCopy(val) if default_already_formobj then local argformobj = m_headword_utilities.convert_termobj_to_formobj(arg) val.footnotes = iut.combine_footnotes(val.footnotes, argformobj.footnotes) iut.insert_form(base.forms, slot, val) else m_headword_utilities.combine_termobj_decorations(val, arg) canonicalize_and_insert(val) end end end else canonicalize_and_insert(arg) end end end set_lemma_and_default_forms(base.lemma) local all_part_default_specs = {} local function process_and_canonicalize_s_form(arg) local form = arg.term if form == "+" then error("Internal error: '+' should have been converted to '^' by now") end if form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then error(("Internal error: '%s' should have already thrown an error"):format(form)) end if form == "^" or form == "++" or form == "+l" or form == "+!" or form == "+'" then insert(all_part_default_specs, shallowCopy(arg)) end return canonicalize_s_form(form) end process_specs("s_form", base.s_specs, process_and_canonicalize_s_form, {{term = "^"}}) if not all_part_default_specs[1] then all_part_default_specs[1] = {term = "^"} end process_specs("ing_form", base.ing_specs, function(arg) return canonicalize_ing_form(arg.term) end, all_part_default_specs) process_specs("ed_form", base.ed_specs, function(arg) return canonicalize_ed_form(arg.term) end, all_part_default_specs) process_specs("en_form", base.en_specs, function(arg) return canonicalize_en_form(arg.term) end, base.forms.ed_form, "default already formobj") iut.insert_form(base.forms, "lemma", {form = base.lemma}) -- Add linked version of lemma for use in head=. We write this in a general fashion in case -- there are multiple lemma forms (which isn't possible currently at this level, although it's -- possible overall using the ((...,...)) notation). iut.insert_forms(base.forms, "lemma_linked", iut.map_forms(base.forms.lemma, function(form) if form == base.lemma and base.linked_lemma:find("%[%[") then return base.linked_lemma else return form end end)) end local inflect_props = { slot_table = all_verb_slots, inflect_word_spec = conjugate_verb, } iut.inflect_multiword_or_alternant_multiword_spec(alternant_multiword_spec, inflect_props) -- (4) Fetch the forms and put the conjugated lemmas in data.heads if not explicitly given. local function fetch_termobjs(slot) local forms = alternant_multiword_spec.forms[slot] -- See above. This should only occur if the user explicitly used - for a spec. if not forms or not forms[1] then return {{term = "-"}} end local termobjs = {} for _, formobj in ipairs(forms) do insert(termobjs, m_headword_utilities.convert_formobj_to_termobj(formobj)) end return termobjs end pres_3sgs = fetch_termobjs("s_form") pres_ptcs = fetch_termobjs("ing_form") pasts = fetch_termobjs("ed_form") past_ptcs = fetch_termobjs("en_form") -- Use the "linked" form of the lemma as the head if no head= explicitly given and the user specified -- brackets in one of the lemmas. Otherwise we use the default headword-linking algorithm. if not data.user_specified_heads[1] and alternant_multiword_spec.saw_bracket then data.heads = {} for _, lemma_obj in ipairs(alternant_multiword_spec.forms.lemma_linked) do insert(data.heads, m_headword_utilities.convert_formobj_to_termobj(lemma_obj)) end end else -------------------------- SEPARATE-PARAM FORMAT -------------------------- set_lemma_and_default_forms(pagename) par1s = par1s or parse_inflection(args, {1, "pres_3sg"}) pres_3sgs = {} pres_ptcs = {} pasts = {} past_ptcs = {} if not par1s[1] then par1s = {{term = "+"}} end if not par2s[1] then par2s = {{term = "+"}} end if not par3s[1] then par3s = {{term = "+"}} end if not par4s[1] then par4s = {{term = "+"}} end local function process_argument(args, dest, canon_func, default_values, default_already_canonicalized) local function canonicalize_and_insert(arg) local canon_arg = canon_func(arg) if type(canon_arg) == "string" then arg.term = canon_arg m_headword_utilities.insert_termobj_combining_duplicates(dest, arg) else for _, canon in ipairs(canon_arg) do m_headword_utilities.combine_termobj_decorations(canon, arg) m_headword_utilities.insert_termobj_combining_duplicates(dest, canon) end end end for _, arg in ipairs(args) do if arg.term == "+" then for _, val in ipairs(default_values) do val = shallowCopy(val) m_headword_utilities.combine_termobj_decorations(val, arg) if default_already_canonicalized then m_headword_utilities.insert_termobj_combining_duplicates(dest, val) else canonicalize_and_insert(val) end end else canonicalize_and_insert(arg) end end end local all_part_default_specs = {} local function process_and_canonicalize_s_form(arg) local form = arg.term if form == "+" then error("Internal error: '+' should have been converted to '^' by now") end if form == "^" or form == "++" or form == "+l" or form == "+!" or form == "+'" or form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then insert(all_part_default_specs, shallowCopy(arg)) end return canonicalize_s_form(form) end process_argument(par1s, pres_3sgs, process_and_canonicalize_s_form, {{term = "^"}}) if not all_part_default_specs[1] then all_part_default_specs[1] = {term = "^"} end process_argument(par2s, pres_ptcs, function(arg) return canonicalize_ing_form(arg.term) end, all_part_default_specs) process_argument(par3s, pasts, function(arg) return canonicalize_ed_form(arg.term) end, all_part_default_specs) process_argument(par4s, past_ptcs, function(arg) return canonicalize_en_form(arg.term) end, pasts, "default already canonicalized") end ------------------------------------------- INSERT INFLECTIONS ------------------------------------------ insert_inflection(data, pres_3sgs, "third-person singular simple present", "s-verb-form") insert_inflection(data, pres_ptcs, "present participle", "ing-form") if deepEquals(pasts, past_ptcs) then insert_inflection(data, pasts, "simple past and past participle", "ed-form", "no simple past or past participle") else insert_inflection(data, pasts, "simple past", "spast") insert_inflection(data, past_ptcs, "past participle", "past|part") end if pagename:find(" ") then -- Check for placeholder "it" local words = split(pagename, " ") for _, word in ipairs(words) do if word == "it" or word == "its" or word == "it's" then insert(data.categories, langname .. ' ' .. cat_word(lang, 'terms with placeholder "it"')) break end end -- Check for phrasal verbs local phrasal_adverbs = list_to_set{ -- NOTE: This should only contain common phrasal adverbs, not random words like [[low]], -- [[adrift]], etc. "aback", "about", "above", "across", "after", "against", "ahead", "along", "apart", "around", "as", "aside", "at", "away", "back", "before", "behind", "below", "between", "beyond", "by", "down", "for", "forth", "from", "in", "into", "of", "off", "on", "onto", "out", "over", "past", "round", "through", "to", "together", "towards", "under", "up", "upon", "with", "without", } local allowed_non_adverb_words = list_to_set{ "it", "one", "oneself", "someone", } local base = pagename local seen_adverbs = {} -- Only consider a verb to be phrasal if it consists of a single base verb followed exclusively by either -- adverbs from `phrasal_adverbs` or placeholder words from `allowed_non_adverb_words`, where at -- least one following word is from `phrasal_adverbs` (hence [[can it]] is not a phrasal verb). while true do local prev, word = base:match("^(.+) (.-)$") if not prev then break end if phrasal_adverbs[word] then insert(seen_adverbs, word) elseif allowed_non_adverb_words[word] then -- do nothing else break end base = prev end if not base:find(" ") and seen_adverbs[1] then insert(data.categories, langname .. " " .. cat_word(lang, "phrasal verbs")) for i = #seen_adverbs, 1, -1 do insert(data.categories, langname .. ' ' .. cat_word(lang, "phrasal verbs formed with") .. ' "' .. seen_adverbs[i] .. '\"') end end end end, } ----------------------------------------------------------------------------------------- -- Suffix forms -- ----------------------------------------------------------------------------------------- pos_functions["suffix forms"] = { params = { [1] = {required = true, list = true, disallow_holes = true}, }, func = function(args, data, is_suffix) local suffix_type = {} for _, typ in ipairs(args[1]) do insert(suffix_type, typ .. "-forming suffix") end insert(data.inflections, {label = "non-lemma form of " .. m_table.serialCommaJoin(suffix_type, {conj = "or"})}) end, } return export aczogv58cwqd8atwgt0t7mi5n61gzdv 55059 55053 2026-09-30T22:19:21Z Deadend0914 7211 55059 Scribunto text/plain local export = {} local pos_functions = {} --[==[ Author from 2020 on: mostly Benwing2, with significant contributions from Theknightwho. Based on a prior version by Rua (by now mostly rewritten), with contributions from Erutuon and others (see history for full attribution). ]==] local force_cat = false -- for testing; if true, categories appear in non-mainspace pages local require = require local require_when_needed = require("Module:require when needed") local en_utilities_module = "Module:en-utilities" local headword_utilities_module = "Module:headword utilities" local headword_module = "Module:headword" local inflection_utilities_module = "Module:inflection utilities" local parse_utilities_module = "Module:parse utilities" local JSON_module = "Module:JSON" local labels_module = "Module:labels" local links_module = "Module:links" local parameters_module = "Module:parameters" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local utilities_module = "Module:utilities" local yesno_module = "Module:yesno" local iut = require_when_needed(inflection_utilities_module) local put = require_when_needed(parse_utilities_module) local m_headword_utilities = require_when_needed(headword_utilities_module) local add_links_to_multiword_term = require_when_needed(headword_utilities_module, "add_links_to_multiword_term") local add_suffix = require_when_needed(en_utilities_module, "add_suffix") local apply_link_modifiers = require_when_needed(headword_utilities_module, "apply_link_modifiers") local concat = table.concat local deepEquals = require_when_needed(table_module, "deepEquals") local dump = mw.dumpObject local format_categories = require_when_needed(utilities_module, "format_categories") local full_headword = require_when_needed(headword_module, "full_headword") local get_label_info = require_when_needed(labels_module, "get_label_info") local get_link_page = require_when_needed(links_module, "get_link_page") local glossary_link = require_when_needed(headword_utilities_module, "glossary_link") local insert = table.insert local insertIfNot = require_when_needed(table_module, "insertIfNot") local ipairs = ipairs local is_regular_plural = require_when_needed(en_utilities_module, "is_regular_plural") local list_to_set = require_when_needed(table_module, "listToSet") local pairs = pairs local process_params = require_when_needed(parameters_module, "process") local remove = table.remove local remove_links = require_when_needed(links_module, "remove_links") local replacement_escape = require_when_needed(string_utilities_module, "replacement_escape") local shallowCopy = require_when_needed(table_module, "shallowCopy") local singularize = require_when_needed(en_utilities_module, "singularize") local split = require_when_needed(string_utilities_module, "split") local toJSON = require_when_needed(JSON_module, "toJSON") local toNFD = mw.ustring.toNFD local type = type local ulen = require_when_needed(string_utilities_module, "len") local ulower = require_when_needed(string_utilities_module, "lower") local umatch = require_when_needed(string_utilities_module, "match") local u = require_when_needed(string_utilities_module, "char") local ugsub = require_when_needed(string_utilities_module, "gsub") local lang = require("Module:languages").getByCode("en") local langname = lang:getCanonicalName() -- Old English localization: category wordings come from [[Module:floccnaman]] -- (see that module); the English wording is kept when a translation is missing. -- Loaded lazily and optionally, so a missing module can never break a page. local floccnaman_module = "Module:floccnaman" local floccnaman local function get_floccnaman() if floccnaman == nil then local ok, mod = pcall(require, floccnaman_module) floccnaman = ok and mod or false end return floccnaman or nil end -- Old English wording for one category word, or the English wording unchanged. local function cat_word(l, text) local mod = get_floccnaman() return mod and mod.get(l, text) or text end local list_param = {list = true, disallow_holes = true} local list_allow_holes = {list = true, allow_holes = true} local boolean_param = {type = "boolean"} local function ine(val) if val == "" then return nil else return val end end local function track(page) require("Module:debug/track")("en-headword/" .. page) return true end ------------------------------------------- UTILITY FUNCTIONS ------------------------------------------ -- Parse and return an inflection not requiring additional processing. The raw arguments come from `args[field]`, which -- is parsed for inline modifiers. local function parse_inflection(args, field, is_head) local argfield = field if type(argfield) == "table" then argfield = argfield[1] end return m_headword_utilities.parse_term_list_with_modifiers { paramname = field, forms = args[argfield], splitchar = ",", is_head = is_head, } end -- Insert the parsed inflections in `terms` (as parsed by `parse_inflection`) into `data.inflections`, with label -- `label` and optional accelerator spec `accel`. local function insert_inflection(data, terms, label, accel, no_label) for _, termobj in ipairs(terms) do m_headword_utilities.remove_termobj_field_modifiers(termobj) end m_headword_utilities.insert_inflection { headdata = data, terms = terms, label = label, no_label = no_label, accel = accel and {form = accel} or nil, } end -- Insert a fixed label `label` into the inflections for `data`. If `originating_term` is supplied, copy the decorations -- from it into the fixed label. local function insert_fixed_inflection(data, label, originating_term) m_headword_utilities.insert_fixed_inflection { headdata = data, originating_term = originating_term, label = label, } 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 -- These functions are used directly in the <> format as well as in the utility functions #2 below. local function compute_double_last_cons_stem(term) local last_cons = term:match("([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])$") if not last_cons then error("Verb stem '" .. term .. "' must end in a consonant to use ++") end return term .. last_cons end local function compute_plusplus_s_form(term, default_s_form) if term:find("[szx]$") then -- regas -> regasses, derez -> derezzes return compute_double_last_cons_stem(term) .. "es" else return default_s_form end end -- On English Wiktionary, {{tl|en-head}} is the template that means "interjection"; it invokes this module without -- a part of speech. This wiki's template namespace is named "Bysen", not "Template", so the namespace id and the -- title without its namespace prefix are compared instead of a hard-coded "Template:en-head". -- Scribunto's title object exposes these as the fields `namespace`, `nsText` and `fullText`; it has no -- getNamespace()/getBaseText() methods, so those must not be used here. local function is_en_head_template() local title = mw.title.getCurrentTitle() return title.namespace == 10 and title.fullText == title.nsText .. ":en-head" end -- The main entry point. -- This is the only function that can be invoked from a template. function export.show(frame) local iparams = { [1] = true, } local iargs = require("Module:parameters").process(frame.args, iparams) local parargs = frame:getParent().args local poscat = iargs[1] local pos_in_1 = not poscat if pos_in_1 then poscat = ine(parargs[1]) or is_en_head_template() and "interjection" or error("Part of speech must be specified in 1=") poscat = require(headword_module).canonicalize_pos(poscat) end local indexing_poscat = pos_in_1 and "head" or poscat local params = { ["head"] = list_param, ["id"] = true, ["json"] = boolean_param, ["sort"] = true, ["splithyph"] = boolean_param, ["nosplithyph"] = boolean_param, ["hyphspace"] = boolean_param, ["nolink"] = boolean_param, ["nolinkhead"] = {type = "boolean_param", alias_of = "nolink"}, ["suffix"] = boolean_param, ["nosuffix"] = boolean_param, ["nomultiwordcat"] = boolean_param, ["abbr"] = list_param, ["the"] = true, ["def"] = {alias_of = "the"}, ["pagename"] = true, -- for testing } if not pos_in_1 then params[1] = {required = true} -- required but ignored as already processed above end local pos_data = pos_functions[indexing_poscat] local pos_func if pos_data then local pos_params = pos_data.params if pos_params then for key, val in pairs(pos_params) do params[key] = val end end pos_func = pos_data.func end local args = process_params(parargs, params) -- Account for unsupported titles, e.g. 'C|N>K' instead of 'Unsupported titles/C through N to K'. local pagename = args.pagename or mw.loadData("Module:headword/data").pagename local user_specified_heads = parse_inflection(args, "head", "is_head") local heads = user_specified_heads local autohead if args.nolink or not pagename:find("[ '%-]") then autohead = pagename else local en_no_split_apostrophe_words = list_to_set { "one's", "someone's", "he's", "she's", "it's", } local en_include_hyphen_prefixes = list_to_set { -- We don't include things that are also words even though they are often (perhaps mostly) prefixes, e.g. -- "be", "counter", "cross", "extra", "half", "mid", "over", "pan", "under". "acro", "acousto", "Afro", "agro", "anarcho", "angio", "Anglo", "ante", "anti", "arch", "auto", "bi", "bio", "cis", "co", "cryo", "crypto", "de", "demi", "eco", "electro", "Euro", "ex", "Greco", "hemi", "hydro", "hyper", "hypo", "infra", "Indo", "inter", "intra", "Judeo", "macro", "meta", "micro", "mini", "multi", "neo", "neuro", "non", "para", "peri", "post", "pre", "pro", "proto", "pseudo", "re", "semi", "sub", "super", "trans", "un", "vice", } local function is_english(term) local title = mw.title.new(term) if title and title.exists then local content = title:getContent() if content and content:find("==English==\n") then return true end end return false end local function en_split_hyphen_when_space(word) if not word:find("-", nil, true) then return nil end if args.hyphspace then return "[[" .. word:gsub("%-+", " ") .. "|" .. word .. "]]" end if args.nosplithyph then return "[[" .. word .. "]]" end if not args.splithyph then local space_word = word:gsub("%-+", " ") if is_english(space_word) then return "[[" .. space_word .. "|" .. word .. "]]" end if is_english(word) then return "[[" .. word .. "]]" end end return nil end local function en_split_apostrophe(word) local base = word:match("^(.*)'s$") if base then return "[[" .. base .. "]][[-'s|'s]]" end -- Only treat final apostrophe as possessive if preceded by something that looks like a plural ending in /z/. -- In particular we don't want to do it for words like [[truckin']]. base = word:match("^(.*[sxz])'$") if base then if base:find("s$") then local sg = singularize(base) if is_english(sg) then return "[[" .. sg .. "|" .. base .. "]][[-'|']]" end end return "[[" .. base .. "]][[-'|']]" end return "[[" .. word .. "]]" end autohead = add_links_to_multiword_term(pagename, { split_hyphen_when_space = en_split_hyphen_when_space, split_apostrophe = en_split_apostrophe, no_split_apostrophe_words = en_no_split_apostrophe_words, include_hyphen_prefixes = en_include_hyphen_prefixes, }) end if not heads[1] then heads = {{term = autohead}} else for _, headobj in ipairs(heads) do local head = headobj.term if head:find("^~") then head = apply_link_modifiers(autohead, head:sub(2), lang) headobj.term = head elseif head:find("^[!?]$") then -- If explicit head= just consists of ! or ?, add it to the end of the default head. headobj.term = autohead .. head end if head == autohead then track("redundant-head") end end end -- handle the=/def= if args.the == "~" then local newheads = {} for _, headobj in ipairs(heads) do local barehead = shallowCopy(headobj) insert(newheads, barehead) headobj.term = "the " .. headobj.term insert(newheads, headobj) end heads = newheads elseif args.the then local the = require(yesno_module)(args.the) if the then for _, headobj in ipairs(heads) do headobj.term = "the " .. headobj.term end end end local data = { lang = lang, pos_category = poscat, categories = {}, heads = heads, user_specified_heads = user_specified_heads, -- We use our own splitting algorithm so the redundant head cat will be inaccurate. no_redundant_head_cat = true, inflections = {}, nomultiwordcat = args.nomultiwordcat, sort_key = args.sort, pagename = pagename, id = args.id, force_cat_output = force_cat, } local function inscat(cat) insert(data.categories, langname .. " " .. cat_word(lang, cat)) end local is_suffix = false if args.suffix or not args.nosuffix and pagename:find("^%-") and not pagename:find("^%-%-") and poscat ~= "suffix forms" then is_suffix = true data.pos_category = "suffixes" local singular_poscat = singularize(poscat) inscat(singular_poscat .. "-forming suffixes") insert(data.inflections, {label = singular_poscat .. "-forming suffix"}) end if pos_func then pos_func(args, data, is_suffix) end local extra_categories = {} if pagename:find("[Qq]") then -- Check for q not followed by u. We want to exclude things like [[13q deletion syndrome]] and [[BFOQ]] that -- don't have a lowercase letter on either side, as well as things like [[& seq.]] and [[acq.]] that are -- abbreviations for words containing a following u. -- -- Approximate range of combining diacritics; we want to remove them so the checks below for -- a lowercase letter next to the q aren't tripped up by diacritics on the letter. local u300 = u(0x0300) local u36F = u(0x036F) local pagename_no_diacritics = ugsub(toNFD(pagename), "[" .. u300 .. "-" .. u36F .. "]", "") if pagename_no_diacritics:find("[Qq][a-tv-z]") or pagename_no_diacritics:find("[a-z]q[^u.]") or pagename_no_diacritics:find("[a-z]q$") then inscat("words containing Q not followed by U") end end -- toNFD performs decomposition, so letters that decompose to an ASCII -- vowel and a diacritic, such as é, are counted as vowels and do not do not -- need to be included in the pattern. if not umatch(ulower(toNFD(pagename)), "[aeiouyæœøəªºαεηιουω]") then inscat("words spelled wiþūtan vowels") end if pagename:find("yre$") then inscat('words ending in "-yre"') end if not pagename:find(" ") and ulen(pagename) >= 25 then insert(extra_categories, langname .. " " .. cat_word(lang, "Long words")) end if pagename:find("^[^aeiou ]*a[^aeiou ]*e[^aeiou ]*i[^aeiou ]*o[^aeiou ]*u[^aeiou ]*$") then inscat("words that use all vowels in alphabetical order") end parse_and_insert_inflection(data, args, "abbr", "abbreviation") if args.json then return toJSON(data) end return full_headword(data) .. (extra_categories[1] and format_categories(extra_categories, lang, args.sort) or "") end local function make_default_comparative(word) if word == "good" or word == "well" then return {"better"} elseif word == "bad" or word == "badly" then return {"worse"} elseif word == "far" then return {"further", "farther"} else return {add_suffix(word, "r")} end end local function make_default_superlative(word) if word == "good" or word == "well" then return {"best"} elseif word == "bad" or word == "badly" then return {"worst"} elseif word == "far" then return {"furthest", "farthest"} else return {add_suffix(word, "st.superlative")} end end -- This function does the common work between adjectives and adverbs. local function process_comparative_args(data, args, plpos) local pagename = data.pagename local comps = parse_inflection(args, 1) local sups = parse_inflection(args, "sup") local outcomps, outsups if args.componly then if comps[1] then error("Can't specify comparatives of comparative-only " .. plpos) end insert(data.inflections, {label = glossary_link("comparative") .. " form only"}) insert(data.categories, langname .. " " .. cat_word(lang, "comparative-only " .. plpos)) -- Set to empty list so we don't get any comparatives output, but process superlatives if specified. outcomps = {} if not sups[1] then -- Set to empty list so we don't get any superlatives output unless explicitly given. outsups = {} end elseif args.suponly then if comps[1] or sups[1] then error("Can't specify comparatives or superlatives of or superlative-only " .. plpos) end insert(data.inflections, {label = glossary_link("superlative") .. " form only"}) insert(data.categories, langname .. " " .. cat_word(lang, "superlative-only " .. plpos)) return end -- If the first parameter is ?, then don't show anything, just return. if comps[1] and comps[1].term == "?" then if comps[2] then error("Can't specify additional comparatives along wiþ '?'") end if sups[1] then error("Can't specify superlatives along wiþ '?' for the comparative") end return end if comps[1] and comps[1].term == "-" then local hyphencomp = remove(comps, 1) -- Remove the "-" but retain for decorations. -- Not (generally) comparable; may occasionally have a comparative if comps[1] then insert_fixed_inflection(data, "not generally <<comparable>>", hyphencomp) elseif not sups[1] then insert_fixed_inflection(data, "not <<comparable>>", hyphencomp) insert(data.categories, langname .. " " .. cat_word(lang, "uncomparable " .. plpos)) return else -- No comparative, but a superlative. insert_inflection() will correctly generate 'no comparative' if we -- pass in "-" as the value. outcomps = {hyphencomp} end elseif not comps[1] then comps = {{term = "more"}} end if not outcomps then -- not if we set `outcomps` to "-" above or processed a comparative-only term outcomps = {} -- Go over each parameter given and create a comparative and superlative form. for _, compobj in ipairs(comps) do local comp = compobj.term if comp == "-" then error("Comparative of '-' only allowed as first comparative") end if comp == "+" then comp = "+more" elseif comp == "more" and pagename ~= "many" and pagename ~= "much" then comp = "+more" elseif comp == "further" and pagename ~= "far" then comp = "+further" elseif comp == "better" and pagename ~= "good" and pagename ~= "well" then comp = "+better" elseif comp:find("~") then comp = comp:gsub("~", replacement_escape(pagename)) end compobj.origterm = comp if comp == "+more" then comp = "more [[" .. pagename .. "]]" elseif comp == "+further" then comp = {"further [[" .. pagename .. "]]", "farther [[" .. pagename .. "]]"} elseif comp == "+better" then comp = "better [[" .. pagename .. "]]" elseif comp == "er" then -- Add -er. comp = add_suffix(pagename, "r") elseif comp == "ier" then if pagename:sub(-1) ~= "y" then error("Can't specify 'ier' comparative unless the term ends wiþ 'y': " .. pagename) end comp = pagename:gsub("e?y$", "ier") elseif comp:find("^%+") then local special = m_headword_utilities.get_special_indicator(comp, "noerror") if special then comp = m_headword_utilities.handle_multiword(pagename, special, make_default_comparative) end end if type(comp) == "table" and not comp[2] then comp = comp[1] end if type(comp) == "table" then for i = 1, #comp - 1 do local outobj = shallowCopy(compobj) outobj.term = comp[i] insert(outcomps, outobj) end compobj.term = comp[#comp] insert(outcomps, compobj) else compobj.term = comp insert(outcomps, compobj) end end end if sups[1] and sups[1].term == "-" then if sups[2] then error("Can't specify '-' as superlative followed by further values") end -- No superlative. insert_inflection() will correctly generate 'no superlative' if we pass in "-" as the value. outsups = sups else if not sups[1] then sups = {{term = "+"}} end end -- `outsups` will be set if we set `outsups` to "-" above or processed a comparative-only term without superlatives. if not outsups then outsups = {} local function process_sup(sup, special, supobj, compobj) if special then sup = m_headword_utilities.handle_multiword(pagename, special, make_default_superlative) elseif sup == "-" or sup == "+" then error(("Internal error: Superlative value of '%s' should have been handled earlier"):format(sup)) elseif sup == "+most" then sup = "most [[" .. pagename .. "]]" elseif sup == "+furthest" then sup = {"furthest [[" .. pagename .. "]]", "farthest [[" .. pagename .. "]]"} elseif sup == "+best" then sup = "best [[" .. pagename .. "]]" elseif sup == "est" then -- Add -est. sup = add_suffix(pagename, "st.superlative") elseif sup == "iest" then if pagename:sub(-1) ~= "y" then error("Can't specify 'iest' superlative unless the term ends wiþ 'y': " .. pagename) end sup = pagename:gsub("e?y$", "iest") end if type(sup) == "table" and not sup[2] then sup = sup[1] end if compobj then supobj = shallowCopy(supobj) supobj = m_headword_utilities.combine_termobj_decorations(supobj, compobj) end if type(sup) == "table" then for i = 1, #sup - 1 do local outobj = shallowCopy(supobj) outobj.term = sup[i] insert(outsups, outobj) end supobj.term = sup[#sup] insert(outsups, supobj) else supobj.term = sup insert(outsups, supobj) end end for _, supobj in ipairs(sups) do local sup = supobj.term if sup == "-" then error("Superlative of '-' only allowed as first superlative") end if sup == "+" then if not comps[1] then error("Superlative of '+' can't be specified when there are no comparatives") end for _, compobj in ipairs(comps) do local comp = compobj.origterm local special if comp == "+more" then sup = "+most" elseif comp == "+further" then sup = "+furthest" elseif comp == "+better" then sup = "+best" elseif comp == "er" then sup = "est" elseif comp == "ier" then sup = "iest" else if comp:find("^%+") then special = m_headword_utilities.get_special_indicator(comp, "noerror") end if not special then -- If the full comparative was given, then derive the superlative by replacing -er with -- -est. if comp:sub(-2) == "er" then sup = comp:sub(1, -3) .. "est" else error(("The superlative cannot be derived automatically from comparative '%s' because it doesn't end in -er"):format(comp)) end end end process_sup(sup, special, supobj, compobj) end else local special = m_headword_utilities.get_special_indicator(sup, "noerror") -- Do some work here rather than in process_sup() so we don't end up double-processing a term with a '~' -- in it or a term that happens to be 'most' or similar after substitution of ~ in the comparative. if not special then if sup == "most" and pagename ~= "many" and pagename ~= "much" then sup = "+most" elseif sup == "furthest" and pagename ~= "far" then sup = "+furthest" elseif sup == "best" and pagename ~= "good" and pagename ~= "well" then sup = "+best" elseif sup:find("~") then sup = sup:gsub("~", replacement_escape(pagename)) end end process_sup(sup, special, supobj) end end end insert_inflection(data, outcomps, "<<comparative>>", "comparative") insert_inflection(data, outsups, "<<superlative>>", "superlative") end pos_functions["adjectives"] = { params = { [1] = list_param, ["comp_qual"] = {list = "comp\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the comparative value", }, ["sup"] = list_param, ["sup_qual"] = {list = "sup\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the superlative value", }, ["componly"] = boolean_param, ["suponly"] = boolean_param, }, func = function(args, data) -- Process the comparatives and superlatives. process_comparative_args(data, args, "adjectives") end, } pos_functions["adverbs"] = { params = { [1] = list_param, ["comp_qual"] = {list = "comp\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the comparative value", }, ["sup"] = list_param, ["sup_qual"] = {list = "sup\1_qual", allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the superlative value", }, ["componly"] = boolean_param, ["suponly"] = boolean_param, }, func = function(args, data) -- Process the comparatives and superlatives. process_comparative_args(data, args, "adverbs") end, } local function escape(str) return (str:gsub("\\([:#])", "\\\\%1") :gsub("[:#]", "\\%0")) end local function canonicalize_plural(pl, pagename, pos) if pl == "+" then return escape(add_suffix(pagename, "s.plural", pos)) elseif pl == "++" then return escape(compute_plusplus_s_form(pagename, add_suffix(pagename, "s.plural", pos))) elseif pl == "*" then return escape(pagename) elseif pl == "ies" then if pagename:sub(-1) == "y" then return escape(pagename:gsub("e?y$", pl)) end error("Can't specify 'ies' plural unless the term ends wiþ 'y'.") elseif pl == "s" or pl == "es" or pl == "'s" then return escape(pagename .. pl) end end local function do_nouns(args, data, pos) local pagename = data.pagename pos = pos or "noun" local plurals = parse_inflection(args, 1) local function insert_plurale_tantum_inflections(is_plural_only, originating_label) if args.sg[1] then insert_fixed_inflection(data, "normally plural", originating_label) parse_and_insert_inflection(data, args, "sg", "singular") elseif is_plural_only then insert_fixed_inflection(data, "plural only", originating_label) end if args.attr[1] then parse_and_insert_inflection(data, args, "attr", "attributive") end end local function first_pl_term() return plurals[1] and plurals[1].term or nil end if first_pl_term() == "p" then -- plurale tantum if plurals[2] then error("Wiþ plurale tantum nama, can't specify more than one plural") end data.genders = {"p"} -- this should auto-insert the correct 'pluralia tantum' category insert_plurale_tantum_inflections("plural only", plurals[1]) return end local function inscat(cat) insert(data.categories, langname .. " " .. cat_word(lang, cat)) end local need_default_plural = pos == "noun" if first_pl_term() == "sp" then -- construed as singular or plural sp = remove(plurals, 1) -- Remove the "sp" but retain it for its decorations. inscat("naman construed as singular or plural") data.genders = {"s", "p"} -- this should auto-insert the correct 'pluralia tantum' category insert_plurale_tantum_inflections(nil, sp) need_default_plural = false elseif first_pl_term() == "-" then -- Untellendlīc noun; may occasionally have a plural local hyphpl = remove(plurals, 1) -- Remove the "-" but retain for decorations. inscat("untellendlīc naman") -- If plural forms were given explicitly, then show "oft" if plurals[1] then insert_fixed_inflection(data, "oft <<untellendlīc>>", hyphpl) else insert_fixed_inflection(data, "<<untellendlīc>>", hyphpl) end need_default_plural = false elseif first_pl_term() == "#" then -- oft tellendlīc (e.g., "grilled cheese") local hashpl = remove(plurals, 1) -- Remove the "#" but retain for decorations. insert_fixed_inflection(data, "oft <<tellendlīc>>", hashpl) inscat("untellendlīc naman") inscat("tellendlīc naman") -- If no plural was given, add a default one now if not plurals[1] then plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end elseif first_pl_term() == "~" then -- Mixed tellendlīc/untellendlīc noun, always has a plural local tildepl = remove(plurals, 1) -- Remove the "~" but retain for decorations. insert_fixed_inflection(data, "<<tellendlīc>> and <<untellendlīc>>", tildepl) inscat("untellendlīc naman") inscat("tellendlīc naman") -- If no plural was given, add a default one now if not plurals[1] then plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end end -- Plural is unknown if first_pl_term() == "?" then local questionpl = remove(plurals, 1) -- Remove the "?" but retain for decorations. -- Not desired; see [[Wiktionary:Tea_room/2021/August#"Plural unknown or uncertain"]] -- insert_fixed_inflection(data, "manigfeald unġecnāwen oþþe ungewiss", questionpl) inscat("naman wiþ unġecnāwen oþþe ungewiss manigfealde") if plurals[1] then error("Ne cann āmearcian sweotole manigfealde andlang wiþ '?' of unġecnāwen/ungewiss manigfeald") end return end -- Plural is not attested if first_pl_term() == "!" then local exclampl = remove(plurals, 1) -- Remove the "!" but retain for decorations. insert_fixed_inflection(data, "plural not attested", exclampl) inscat("naman wiþ unattested manigfealde") if plurals[1] then error("Ne cann āmearcian sweotole manigfealde andlang wiþ '!' of gecȳþed manigfeald") end return end -- If no plural was given, maybe add a default one, otherwise (when "-" was given or proper noun) return. if not plurals[1] then if not need_default_plural then inscat("untellendlīc naman") return end plurals[1] = {term = escape(add_suffix(pagename, "s.plural", pos))} end -- There are plural forms to show, so show them. inscat("tellendlīc naman") local irregular, indeclinable for i, pl in ipairs(plurals) do local canon_pl = canonicalize_plural(pl.term, pagename, pos) if canon_pl then pl.term = canon_pl end local pl_term = get_link_page(pl.term, lang) if not (pagename:find(" ") or is_regular_plural(pl_term, pagename)) then irregular = true if pl_term == pagename then indeclinable = true end end end if irregular then inscat("naman wiþ fremde manigfealde") end if indeclinable then inscat("undeclīnigendlīce naman") end insert_inflection(data, plurals, "plural", "p") end -- Return the parameters to be used for nouns and proper nouns. Currently the same. local noun_params = { [1] = list_param, ["pl\1qual"] = {list = true, allow_holes = true, replaced_by = false, instead = "use <l:...> or <q:...> inline modifier on the plural", }, -- The following four only used for pluralia tantum (1=p) ["sg"] = list_param, ["attr"] = list_param, } pos_functions["naman"] = { params = noun_params, func = do_naman, } pos_functions["agennaman"] = { params = noun_params, func = function(args, data) return do_nouns(args, data, "agennama") end, } local function base_default_verb_forms(verb) return escape(add_suffix(verb, "s.verb")), escape(add_suffix(verb, "ing")), escape(add_suffix(verb, "d")) end local function default_verb_forms(verb) local full_s_form, full_ing_form, full_ed_form = base_default_verb_forms(verb) if verb:find(" ") then local first, rest = verb:match("^(.-)( .*)$") local first_s_form, first_ing_form, first_ed_form = base_default_verb_forms(first) return full_s_form, full_ing_form, full_ed_form, first_s_form .. rest, first_ing_form .. rest, first_ed_form .. rest, first, rest else return full_s_form, full_ing_form, full_ed_form, nil, nil, nil, nil, nil end end local function compute_double_last_cons_stem_of_split_verb(verb, ending) local first, rest = verb:match("^(.-)( .*)$") if not first then error("Word '" .. verb .. "' mōt hæfþ rum in sīde tō notienne **") end local last_cons = first:match("([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])$") if not last_cons then error("Fyrest word '" .. first .. "' mōt ende wiþ samod swēgend tō notienne **") end return first .. last_cons .. ending .. rest end local function check_non_nil_star_form(form, pagename) if form == nil then error("Word '" .. pagename .. "' mōt hæfþ rum in sīde tō notienne *, **, *l, *! or *'") end return form end local function sub_tilde(form, pagename) if not form then return nil end if form:find("~") then form = form:gsub("~", replacement_escape(pagename)) end return form end local deprecated_qual_replaced_by_inline_modifier = { list = true, allow_holes = true, replaced_by = false, instead = "use an inline modifier <q:...> or <l:...> on the value" } pos_functions["verbs"] = { params = { [1] = {list = "pres_3sg", disallow_holes = true}, ["pres_3sg\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [2] = {list = "pres_ptc", disallow_holes = true}, ["pres_ptc\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [3] = {list = "past", disallow_holes = true}, ["past\1_qual"] = deprecated_qual_replaced_by_inline_modifier, [4] = {list = "past_ptc", allow_holes = true}, ["past_ptc\1_qual"] = deprecated_qual_replaced_by_inline_modifier, ["noautolinkverb"] = boolean_param, ["angle_bracket"] = boolean_param, }, func = function(args, data) -- Get parameters local par1s local par2s = parse_inflection(args, {2, "pres_ptc"}) local par3s = parse_inflection(args, {3, "past"}) local par4s = parse_inflection(args, {4, "past_ptc"}) local pres_3sgs, pres_ptcs, pasts, past_ptcs local pagename = data.pagename ------------------------------------------- UTILITY FUNCTIONS #2 ------------------------------------------ -- These functions are used in both in the separate-parameter format and in the override params such as past_ptc2=. local full_default_s, full_default_ing, full_default_ed, split_default_s, split_default_ing, split_default_ed local lemma local function set_lemma_and_default_forms(the_lemma) lemma = the_lemma full_default_s, full_default_ing, full_default_ed, split_default_s, split_default_ing, split_default_ed, lemma_first, lemma_rest = default_verb_forms(the_lemma) end local function canonicalize_s_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_s elseif form == "*" then return check_non_nil_star_form(split_default_s, lemma) elseif form == "++" then return compute_plusplus_s_form(lemma, full_default_s) elseif form == "**" then if lemma:find("^[^ ]*[szx] ") then return compute_double_last_cons_stem_of_split_verb(lemma, "es") else return check_non_nil_star_form(split_default_s, lemma) end elseif form == "+!" then return lemma .. "s" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "s" .. lemma_rest elseif form == "+'" then return lemma .. "'s" elseif form == "*'" then return check_non_nil_star_form(lemma_first) .. "'s" .. lemma_rest elseif form == "+l" then if lemma:find("[szx]$") then return {{term = full_default_s, l = {"US"}}, {term = compute_plusplus_s_form(lemma, full_default_s), l = {"UK"}}} else return compute_plusplus_s_form(lemma, full_default_s) end elseif form == "*l" then if lemma:find("^[^ ]*[szx] ") then return {{term = check_non_nil_star_form(split_default_s, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "es"), l = {"UK"}}} else return check_non_nil_star_form(split_default_s, lemma) end else return sub_tilde(form, lemma) end end local function canonicalize_ing_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_ing elseif form == "*" then return check_non_nil_star_form(split_default_ing, lemma) elseif form == "++" then return compute_double_last_cons_stem(lemma) .. "ing" elseif form == "**" then return compute_double_last_cons_stem_of_split_verb(lemma, "ing") elseif form == "+!" then return lemma .. "ing" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "ing" .. lemma_rest elseif form == "+'" then return lemma .. "'ing" elseif form == "*'" then return check_non_nil_star_form(lemma_first) .. "'ing" .. lemma_rest elseif form == "+l" then return {{term = full_default_ing, l = {"US"}}, {term = compute_double_last_cons_stem(lemma) .. "ing", l = {"UK"}}} elseif form == "*l" then return {{term = check_non_nil_star_form(split_default_ing, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "ing"), l = {"UK"}}} else return sub_tilde(form, lemma) end end local function canonicalize_ed_form(form) if form == "+" then error("Internal error: Should not see '+' here") elseif form == "^" then return full_default_ed elseif form == "*" then return check_non_nil_star_form(split_default_ed, lemma) elseif form == "++" then return compute_double_last_cons_stem(lemma) .. "ed" elseif form == "+!" then return lemma .. "ed" elseif form == "*!" then return check_non_nil_star_form(lemma_first) .. "ed" .. lemma_rest elseif form == "+'" then return {{term = lemma .. "'d"}, {term = lemma .. "'ed"}} elseif form == "*'" then return {{term = check_non_nil_star_form(lemma_first) .. "'d" .. lemma_rest}, {term = check_non_nil_star_form(lemma_first) .. "'ed" .. lemma_rest}} elseif form == "**" then return compute_double_last_cons_stem_of_split_verb(lemma, "ed") elseif form == "+l" then return {{term = full_default_ed, l = {"US"}}, {term = compute_double_last_cons_stem(lemma) .. "ed", l = {"UK"}}} elseif form == "*l" then return {{term = check_non_nil_star_form(split_default_ed, lemma), l = {"US"}}, {term = compute_double_last_cons_stem_of_split_verb(lemma, "ed"), l = {"UK"}}} else return sub_tilde(form, lemma) end end -- FIXME: options should be "+", "*", "++", "**", "+n", "*n", "++n" and "**n", but not "n" local function canonicalize_en_form(form) if form == "n" then track("n4") return add_suffix(lemma, "n") end return canonicalize_ed_form(form) end --------------------------------- MAIN PARSING/CONJUGATING CODE -------------------------------- local is_angle_bracket = args.angle_bracket if is_angle_bracket then if par2s[1] or par3s[1] or par4s[1] then error("Can't specify explicit values for 2=, 3= or 4= along wiþ the angle-bracket format") end elseif is_angle_bracket == nil and not par2s[1] and not par3s[1] and not par4s[1] and not args[1][2] and args[1][1] and args[1][1]:find("<") then if put.term_contains_top_level_html(args[1][1]) then -- Often, term_contains_top_level_html() returns true on the angle-bracket format, which would -- make the pcall() below succeed but leave the angle brackets as-is. Check for this and only do the -- pcall() if term_contains_top_level_html() returns false. is_angle_bracket = true else -- If it's ambiguous whether it's an angle-bracket format or separate params with an inline modifier, -- try to parse as the latter. If an error occurs, treat as the former. local ok ok, par1s = pcall(parse_inflection, args, {1, "pres_3sg"}) if not ok then par1s = nil is_angle_bracket = true end end end if is_angle_bracket then -------------------------- ANGLE-BRACKET FORMAT -------------------------- -- (0) Expand multiword term with angle brackets just on the first word. local arg11 = args[1][1] if arg11:find("^<.*>$") and pagename:find(" ") then local first, rest = pagename:match("^(.-)( .*)$") arg11 = first .. arg11 .. rest end -- (1) Parse the indicator specs inside of angle brackets. local function parse_indicator_spec(angle_bracket_spec) local inside = angle_bracket_spec:match("^<(.*)>$") assert(inside) local segments = put.parse_balanced_segment_run(inside, "[", "]") local comma_separated_groups = put.split_alternating_runs(segments, ",") if #comma_separated_groups > 4 then error("Too many comma-separated parts in indicator spec, expected at most 4: " .. angle_bracket_spec) end local function fetch_footnotes(separated_group) local footnotes for j = 2, #separated_group - 1, 2 do if separated_group[j + 1] ~= "" then error("Extraneous text after bracketed footnotes: '" .. concat(separated_group) .. "'") end if not footnotes then footnotes = {} end insert(footnotes, separated_group[j]) end return footnotes end local function fetch_specs(comma_separated_group) if not comma_separated_group then return {{term = "+"}} end local specs = {} local colon_separated_groups = put.split_alternating_runs(comma_separated_group, ":") for _, colon_separated_group in ipairs(colon_separated_groups) do local form = colon_separated_group[1] if form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then error("*, **, *l, *! and *' not allowed inside of indicator specs: " .. angle_bracket_spec) end if form == "" then form = "+" end local termobj = { term = form } local footnotes = fetch_footnotes(colon_separated_group) if footnotes then for _, footnote in ipairs(footnotes) do m_headword_utilities.add_footnote_to_termobj(termobj, footnote) end end insert(specs, termobj) end return specs end local s_specs = fetch_specs(comma_separated_groups[1]) local ing_specs = fetch_specs(comma_separated_groups[2]) local ed_specs = fetch_specs(comma_separated_groups[3]) local en_specs = fetch_specs(comma_separated_groups[4]) return { forms = {}, s_specs = s_specs, ing_specs = ing_specs, ed_specs = ed_specs, en_specs = en_specs, } end local parse_props = { parse_indicator_spec = parse_indicator_spec, } local alternant_multiword_spec = iut.parse_inflected_text(arg11, parse_props) -- (2) Check for user-specified brackets; remove any links from the lemma, but remember the original -- form so we can use it below in the 'lemma_linked' form. -- Check to see if there are brackets in the pre-text or post-text. If so, use the linked lemma (with the -- verb autolinked unless noautolinkverb is given). Otherwise, use the default headword algorithm. local function check_bracket(val) if val:find("%[%[") then alternant_multiword_spec.saw_bracket = true end end for _, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do check_bracket(alternant_or_word_spec.before_text) if alternant_or_word_spec.alternants then for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do for _, word_spec in ipairs(multiword_spec.word_specs) do check_bracket(word_spec.before_text) end check_bracket(multiword_spec.post_text) end end end check_bracket(alternant_multiword_spec.post_text) iut.map_word_specs(alternant_multiword_spec, function(base) if base.lemma == "" then base.lemma = pagename end base.orig_lemma = base.lemma base.lemma = remove_links(base.lemma) if args.noautolinkverb or base.orig_lemma:find("%[%[") then base.linked_lemma = base.orig_lemma else base.linked_lemma = "[[" .. base.orig_lemma .. "]]" end end) -- (3) Conjugate the verbs according to the indicator specs parsed above. local all_verb_slots = { lemma = "infinitive", lemma_linked = "infinitive", s_form = "3|s|pres", ing_form = "pres|ptcp", ed_form = "past", en_form = "past|ptcp", } local function conjugate_verb(base) local function process_specs(slot, specs, canon_func, default_values, default_already_formobj) local function insert_termobj_into_slot(termobj) local formobj = m_headword_utilities.convert_termobj_to_formobj(termobj) -- If the form is -, don't insert any forms, which will result in there being no overall forms -- (in fact it will be nil). We check for that down below and substitute a single "-" as the -- form, which in turn gets turned into special labels like "no present participle". if formobj.form == "-" then if formobj.footnotes then error("Unable to preserve footnotes specified on missing form '-': FIXME: " .. dump(formobj.footnotes)) end else iut.insert_form(base.forms, slot, formobj) end end local function canonicalize_and_insert(arg) local canon_arg = canon_func(arg) if type(canon_arg) == "string" then arg.term = canon_arg insert_termobj_into_slot(arg) else for _, canon in ipairs(canon_arg) do m_headword_utilities.combine_termobj_decorations(canon, arg) insert_termobj_into_slot(canon) end end end for _, arg in ipairs(specs) do if arg.term == "+" then if default_values then -- will be nil if past tense specified as - and no past ptc given for _, val in ipairs(default_values) do val = shallowCopy(val) if default_already_formobj then local argformobj = m_headword_utilities.convert_termobj_to_formobj(arg) val.footnotes = iut.combine_footnotes(val.footnotes, argformobj.footnotes) iut.insert_form(base.forms, slot, val) else m_headword_utilities.combine_termobj_decorations(val, arg) canonicalize_and_insert(val) end end end else canonicalize_and_insert(arg) end end end set_lemma_and_default_forms(base.lemma) local all_part_default_specs = {} local function process_and_canonicalize_s_form(arg) local form = arg.term if form == "+" then error("Internal error: '+' should have been converted to '^' by now") end if form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then error(("Internal error: '%s' should have already thrown an error"):format(form)) end if form == "^" or form == "++" or form == "+l" or form == "+!" or form == "+'" then insert(all_part_default_specs, shallowCopy(arg)) end return canonicalize_s_form(form) end process_specs("s_form", base.s_specs, process_and_canonicalize_s_form, {{term = "^"}}) if not all_part_default_specs[1] then all_part_default_specs[1] = {term = "^"} end process_specs("ing_form", base.ing_specs, function(arg) return canonicalize_ing_form(arg.term) end, all_part_default_specs) process_specs("ed_form", base.ed_specs, function(arg) return canonicalize_ed_form(arg.term) end, all_part_default_specs) process_specs("en_form", base.en_specs, function(arg) return canonicalize_en_form(arg.term) end, base.forms.ed_form, "default already formobj") iut.insert_form(base.forms, "lemma", {form = base.lemma}) -- Add linked version of lemma for use in head=. We write this in a general fashion in case -- there are multiple lemma forms (which isn't possible currently at this level, although it's -- possible overall using the ((...,...)) notation). iut.insert_forms(base.forms, "lemma_linked", iut.map_forms(base.forms.lemma, function(form) if form == base.lemma and base.linked_lemma:find("%[%[") then return base.linked_lemma else return form end end)) end local inflect_props = { slot_table = all_verb_slots, inflect_word_spec = conjugate_verb, } iut.inflect_multiword_or_alternant_multiword_spec(alternant_multiword_spec, inflect_props) -- (4) Fetch the forms and put the conjugated lemmas in data.heads if not explicitly given. local function fetch_termobjs(slot) local forms = alternant_multiword_spec.forms[slot] -- See above. This should only occur if the user explicitly used - for a spec. if not forms or not forms[1] then return {{term = "-"}} end local termobjs = {} for _, formobj in ipairs(forms) do insert(termobjs, m_headword_utilities.convert_formobj_to_termobj(formobj)) end return termobjs end pres_3sgs = fetch_termobjs("s_form") pres_ptcs = fetch_termobjs("ing_form") pasts = fetch_termobjs("ed_form") past_ptcs = fetch_termobjs("en_form") -- Use the "linked" form of the lemma as the head if no head= explicitly given and the user specified -- brackets in one of the lemmas. Otherwise we use the default headword-linking algorithm. if not data.user_specified_heads[1] and alternant_multiword_spec.saw_bracket then data.heads = {} for _, lemma_obj in ipairs(alternant_multiword_spec.forms.lemma_linked) do insert(data.heads, m_headword_utilities.convert_formobj_to_termobj(lemma_obj)) end end else -------------------------- SEPARATE-PARAM FORMAT -------------------------- set_lemma_and_default_forms(pagename) par1s = par1s or parse_inflection(args, {1, "pres_3sg"}) pres_3sgs = {} pres_ptcs = {} pasts = {} past_ptcs = {} if not par1s[1] then par1s = {{term = "+"}} end if not par2s[1] then par2s = {{term = "+"}} end if not par3s[1] then par3s = {{term = "+"}} end if not par4s[1] then par4s = {{term = "+"}} end local function process_argument(args, dest, canon_func, default_values, default_already_canonicalized) local function canonicalize_and_insert(arg) local canon_arg = canon_func(arg) if type(canon_arg) == "string" then arg.term = canon_arg m_headword_utilities.insert_termobj_combining_duplicates(dest, arg) else for _, canon in ipairs(canon_arg) do m_headword_utilities.combine_termobj_decorations(canon, arg) m_headword_utilities.insert_termobj_combining_duplicates(dest, canon) end end end for _, arg in ipairs(args) do if arg.term == "+" then for _, val in ipairs(default_values) do val = shallowCopy(val) m_headword_utilities.combine_termobj_decorations(val, arg) if default_already_canonicalized then m_headword_utilities.insert_termobj_combining_duplicates(dest, val) else canonicalize_and_insert(val) end end else canonicalize_and_insert(arg) end end end local all_part_default_specs = {} local function process_and_canonicalize_s_form(arg) local form = arg.term if form == "+" then error("Internal error: '+' should have been converted to '^' by now") end if form == "^" or form == "++" or form == "+l" or form == "+!" or form == "+'" or form == "*" or form == "**" or form == "*l" or form == "*!" or form == "*'" then insert(all_part_default_specs, shallowCopy(arg)) end return canonicalize_s_form(form) end process_argument(par1s, pres_3sgs, process_and_canonicalize_s_form, {{term = "^"}}) if not all_part_default_specs[1] then all_part_default_specs[1] = {term = "^"} end process_argument(par2s, pres_ptcs, function(arg) return canonicalize_ing_form(arg.term) end, all_part_default_specs) process_argument(par3s, pasts, function(arg) return canonicalize_ed_form(arg.term) end, all_part_default_specs) process_argument(par4s, past_ptcs, function(arg) return canonicalize_en_form(arg.term) end, pasts, "default already canonicalized") end ------------------------------------------- INSERT INFLECTIONS ------------------------------------------ insert_inflection(data, pres_3sgs, "þridda had anfeald ānfeald andweard", "s-verb-form") insert_inflection(data, pres_ptcs, "andweard dǣlnimend", "ing-form") if deepEquals(pasts, past_ptcs) then insert_inflection(data, pasts, "ānfeald forþgewiten and forþgewiten dǣlnimend", "ed-form", "na ānfeald forþgewiten oþþe forþgewiten dǣlnimend") else insert_inflection(data, pasts, "ānfeald forþgewiten", "spast") insert_inflection(data, past_ptcs, "forþgewiten dǣlnimend", "past|part") end if pagename:find(" ") then -- Check for placeholder "it" local words = split(pagename, " ") for _, word in ipairs(words) do if word == "it" or word == "its" or word == "it's" then insert(data.categories, langname .. ' ' .. cat_word(lang, 'terms with placeholder "it"')) break end end -- Check for phrasal verbs local phrasal_adverbs = list_to_set{ -- NOTE: This should only contain common phrasal adverbs, not random words like [[low]], -- [[adrift]], etc. "aback", "about", "above", "across", "after", "against", "ahead", "along", "apart", "around", "as", "aside", "at", "away", "back", "before", "behind", "below", "between", "beyond", "by", "down", "for", "forth", "from", "in", "into", "of", "off", "on", "onto", "out", "over", "past", "round", "through", "to", "together", "towards", "under", "up", "upon", "with", "without", } local allowed_non_adverb_words = list_to_set{ "it", "one", "oneself", "someone", } local base = pagename local seen_adverbs = {} -- Only consider a verb to be phrasal if it consists of a single base verb followed exclusively by either -- adverbs from `phrasal_adverbs` or placeholder words from `allowed_non_adverb_words`, where at -- least one following word is from `phrasal_adverbs` (hence [[can it]] is not a phrasal verb). while true do local prev, word = base:match("^(.+) (.-)$") if not prev then break end if phrasal_adverbs[word] then insert(seen_adverbs, word) elseif allowed_non_adverb_words[word] then -- do nothing else break end base = prev end if not base:find(" ") and seen_adverbs[1] then insert(data.categories, langname .. " " .. cat_word(lang, "phrasal verbs")) for i = #seen_adverbs, 1, -1 do insert(data.categories, langname .. ' ' .. cat_word(lang, "phrasal verbs formed with") .. ' "' .. seen_adverbs[i] .. '\"') end end end end, } ----------------------------------------------------------------------------------------- -- Suffix forms -- ----------------------------------------------------------------------------------------- pos_functions["suffix forms"] = { params = { [1] = {required = true, list = true, disallow_holes = true}, }, func = function(args, data, is_suffix) local suffix_type = {} for _, typ in ipairs(args[1]) do insert(suffix_type, typ .. "-forming suffix") end insert(data.inflections, {label = "non-lemma form of " .. m_table.serialCommaJoin(suffix_type, {conj = "or"})}) end, } return export 7z87v7bded7ojeuopcxgwkrmozglh1b Bysen:ang-decl-noun-a-n 10 8057 55011 54867 2026-09-30T18:37:36Z Deadend0914 7211 55011 wikitext text/x-wiki {{#invoke:checkparams|error}}<!-- Validate template parameters -->{{ang-decl-noun<!-- -->|type=strang ''a''-stefn<!-- -->|1={{{nomsg|{{{1}}}}}}<!-- -->|3={{{nomsg|{{{1}}}}}}<!-- -->|5={{{1}}}{{#if:{{{vowel|}}}||e}}s<!-- -->|7={{{datsg|{{{1}}}{{#if:{{{vowel|}}}||e}}}}}<!-- -->|2={{#if:{{{short|}}}|{{{2|{{{1}}}}}}{{#if:{{{vowel|}}}||u}}|{{{nomsg|{{{2|{{{1}}}}}}}}}}}<!-- -->|4={{#if:{{{short|}}}|{{{2|{{{1}}}}}}{{#if:{{{vowel|}}}||u}}|{{{nomsg|{{{2|{{{1}}}}}}}}}}}<!-- -->|6={{{2|{{{1}}}}}}{{#if:{{{vowel|}}}|n}}a<!-- -->|8={{{2|{{{1}}}}}}{{#if:{{{vowel|}}}||u}}m{{#if:{{{vowel|}}}|,{{{2|{{{1}}}}}}um}}<!-- -->|num={{{num|}}}<!-- -->|title={{{title|}}}<!-- -->}}<!-- -->{{cln|ang|neuter a-stem nouns}}<!-- --><noinclude>{{documentation}}</noinclude> 9tq2txa2h1alrtegp0dr2erc87sz4ef food 0 8071 55035 54888 2026-09-30T19:34:08Z Deadend0914 7211 /* Nama */ 55035 wikitext text/x-wiki =={{sprǣc|en}}== ===Rihtstefn=== * IPA: /fuːd/ ===Wordstǣr=== * Of Middelenglisce ''[[fode]]'', of Englisce ''[[fōda]]'' ===Nama=== {{en-noun|-|s}} # [[fōda]] [[Flocc: Nīwenglisc nama]] [[Flocc: Nīwenglisc nama ofgangen of Middelenglisce]] nnbsymtd8eg38fyizuybucu41wi3o6s 55038 55035 2026-09-30T19:36:35Z Deadend0914 7211 /* Wordstǣr */ 55038 wikitext text/x-wiki =={{sprǣc|en}}== ===Rihtstefn=== * IPA: /fuːd/ ===Wordstǣr=== Of {{inh|en|enm|fode}}, of {{inh|en|ang|fōda}}, of {{inh|en|gmw-pro|*fōdō}}, of {{inh|en|gem-pro|*fōdô}}, of {{der|en|ine-pro|*peh₂-|}}. ===Nama=== {{en-noun|-|s}} # [[fōda]] [[Flocc: Nīwenglisc nama]] [[Flocc: Nīwenglisc nama ofgangen of Middelenglisce]] b0i8shnrrscgtiflpv9xju1ofvhov0n 55039 55038 2026-09-30T19:36:53Z Deadend0914 7211 55039 wikitext text/x-wiki =={{sprǣc|en}}== ===Rihtstefn=== * IPA: /fuːd/ ===Wordstǣr=== Of {{inh|en|enm|fode}}, of {{inh|en|ang|fōda}}, of {{inh|en|gmw-pro|*fōdō}}, of {{inh|en|gem-pro|*fōdô}}, of {{der|en|ine-pro|*peh₂-|}}. ===Nama=== {{en-noun|-|s}} # [[fōda]] 9675arrxjcklda8hmimhvj00oir336m Module:sprǣcnaman 828 8074 54958 54957 2026-09-30T17:14:19Z Deadend0914 7211 54958 Scribunto text/plain --[=[ Rēgister of sprǣcnaman (language names) for the Old English Wiktionary. This is the single place where the Old English name of a language is entered. Everything that displays a language name on this wiki reads it from here: * [[Bysen:sprǣc]] (the ==L2== heading template) is a thin wrapper around sprǣc() below. * [[Module:etymology]] and [[Module:etymology/templates]] use get_name() for headings and category names, and get_of() for the "of"-form, because Old English uses "of" + genitive when an ancestor or source language is mentioned, e.g. "Of Englisce and". Names are entered without leading or trailing spaces. get_of() applies the following rules in order: 1. an explicit entry in `of_names` (for names that follow no rule); 2. `-isc` + "e" (Englisc -> Englisce, Nīwenglisc -> Nīwenglisce, Þēodisc -> Þēodisce); 3. otherwise the nominative name is returned unchanged and the miss is tracked in [[Wiktionary:Tracking/sprǣcnaman/no of-form]] so that an entry can be supplied. Codes with no name at all display "?" (as [[Bysen:sprǣc]] always has) and are tracked in [[Wiktionary:Tracking/sprǣc/unknown code]], see [[Special:WhatLinksHere/Wiktionary:Tracking/sprǣc/unknown code]]. ]=] local export = {} local names = { aa = "Cafarisc", ab = "Apscwisc", af = "Affricanisc", ak = "Ækanisc", ang = "Englisc", bn = "Bengalisc", ca = "Catalānisc", cs = "Bæmisc", da = "Denisc", de = "Þēodisc", el = "Crēacisc", en = "Nīwenglisc", eo = "Esperantisc", es = "Spēonisc", et = "Eotolisc", eu = "Wascisc", fa = "Persisc", fi = "Finnisc", fo = "Faerisc", fr = "Frencisc", fra = "Frencisc", frk = "Frencisc", frp = "Profencalisc", ga = "Īrisc", gem = "Germanisc", ["gem-pro"] = "Ealdoric Germanisc", gl = "Galizisc", gn = "Awaisc", ["gmq-pro"] = "Ealdoric Noren", gmw = "Westerngermanisc", ["gmw-pro"] = "Ealdoric Westerngermanisc", goh = "Ealdhēahþēodisc", got = "Gotisc", gv = "Manisc", haw = "Hawaiisc", hi = "Hindisc", hu = "Ungrisc", id = "Indonesisc", io = "Idoisc", is = "Īslendisc", it = "Italisc", iu = "Inuctitutisc", la = "Lǣden", li = "Limburgisc", lt = "Litwisc", lv = "Lettisc", mul = "Þēodeorþe", nap = "Neapolitisc", nl = "Niðerlendisc", no = "Norþwegisc", non = "Eald Noren", ovd = "Ælfdælisc", pl = "Polisc", pt = "Portugisc", qu = "Kescwisc", ro = "Rumǣnisc", ru = "Rusisc", sa = "Sanscritisc", sco = "Niderscyttisc", sq = "Albānisc", sv = "Swēonisc", tpi = "Tok Pisin", vo = "Volapük", xpi = "Pihtisc" } --[=[ Codes that are needed in etymology lines (ancestors and source languages) but that have no name yet. They currently display their '''English''' name (via [[Module:languages]]) and should be given an Old English name here. The forms below are suggestions only and are deliberately commented out until they have been checked against a source. -- gem-pro = "?", -- Proto-Germanic -- gmw-pro = "?", -- Proto-West Germanic -- ine-pro = "?", -- Proto-Indo-European -- enm = "Middelenglisc", -- Middle English -- gmh = "Middelhēahþēodisc", -- Middle High German -- odt = "Ealdniðerlendisc", -- Old Dutch -- dum = "Middelniðerlendisc", -- Middle Dutch -- ofs = "?", -- Old Frisian -- osx = "Ealdseaxisc", -- Old Saxon -- gml = "Middelniðersaxisc", -- Middle Low German -- cel-pro = "?", -- Proto-Celtic -- gmq-pro = "?", -- Proto-North Germanic ]=] --[=[ Explicit "of"-forms, for names that the `-isc` rule cannot handle. Add an entry whenever [[Wiktionary:Tracking/sprǣcnaman/no of-form]] lists a code that needs one. Suggestions (commented out until checked against a source): -- la = "Lǣdenes", -- Latin -- non = "Eald Norðrena", -- Old Norse -- mul = "Þēodeorðan", -- Translingual -- vo = "Volapük", -- Volapük (no ending to inflect) -- tpi = "Tok Pisin", -- Tok Pisin (no ending to inflect) ]=] local of_names = {} -- Spellings that may be passed to {{sprǣc}} or found in a ==L2== heading, mapped to their code. local aliases = { ["Ænglisc"] = "ang", } -- Codes with no name display "?" and are tracked, so that the gaps are visible. local function track(key) local ok, track_module = pcall(require, "Module:debug/track") if ok then track_module(key) end end function export.track_missing_name(code) code = tostring(code) if code == "" then code = "(empty)" end track("sprǣc/unknown code/" .. code) end local function track_no_of_form(code) track("sprǣcnaman/no of-form/" .. tostring(code)) end -- Accept either a language code or a language object (from [[Module:languages]]). local function code_candidates(x) if type(x) ~= "table" then return { x } end local codes = {} local code = x.getCode and x:getCode() if code then codes[#codes + 1] = code end local full_code = x.getFullCode and x:getFullCode() if full_code and full_code ~= code then codes[#codes + 1] = full_code end return codes end --[==[ Return the Old English name of the given language (code or language object), or nil if the language has no name yet. Used for ==L2== headings and for category names. ]==] function export.get_name(x) for _, code in ipairs(code_candidates(x)) do if names[code] then return names[code] end end return nil end --[==[ Return the "of"-form of the given language (code or language object), or nil if the language has no name yet. Used wherever Old English needs "of" + genitive, e.g. "Of Englisce and". Names that follow no rule fall back to the nominative and are tracked. ]==] function export.get_of(x) for _, code in ipairs(code_candidates(x)) do if names[code] then return export.get_of_for_code(code) end end return nil end function export.get_of_for_code(code) local name = names[code] if not name then return nil end if of_names[code] then return of_names[code] end if name:sub(-3) == "isc" then return name .. "e" end track_no_of_form(code) return name end local code_of_name, lower_code_of_name local function make_reverse_maps() if code_of_name then return end code_of_name, lower_code_of_name = {}, {} for alias, code in pairs(aliases) do code_of_name[alias] = code lower_code_of_name[mw.ustring.lower(alias)] = code end for code, name in pairs(names) do code_of_name[name] = code_of_name[name] or code local lower = mw.ustring.lower(name) lower_code_of_name[lower] = lower_code_of_name[lower] or code end end --[==[ Return the code of the language with the given Old English name, or nil. Used by [[Module:etymology/templates]] to match an ==L2== heading back to its language. ]==] function export.get_code(name) if type(name) ~= "string" then return nil end name = mw.text.trim(name) if name == "" then return nil end make_reverse_maps() return code_of_name[name] or lower_code_of_name[mw.ustring.lower(name)] end --[==[ Entry point for [[Bysen:sprǣc]]: {{sprǣc|CODE}} gives the name, {{sprǣc|CODE|of}} the "of"-form. A code with no name gives "?" and is tracked, exactly as the template always has. ]==] export["sprǣc"] = function(frame) local args = frame and frame.args or {} local code = mw.text.trim(args[1] or "") local mode = mw.text.trim(args[2] or "") if code == "" then export.track_missing_name("") return "?" end local name if mode == "of" or mode == "gen" or mode == "genitive" then name = export.get_of(code) else name = export.get_name(code) if not name then -- The argument may be a name rather than a code (e.g. "Englisc"). local matched_code = export.get_code(code) name = matched_code and names[matched_code] or nil end end if not name then export.track_missing_name(code) return "?" end return name end --[==[ Entry point for templates that need the "of"-form only: {{#invoke:sprǣcnaman|of|CODE}}. ]==] function export.of(frame) local args = frame and frame.args or {} local code = mw.text.trim(args[1] or "") if code == "" then export.track_missing_name("") return "?" end local name = export.get_of(code) if not name then export.track_missing_name(code) return "?" end return name end -- The tables are exposed for tests and maintenance scripts; use the getters elsewhere. export.names = names export.of_names = of_names return export 1h5b29x7uq35jufstfrux1s78efp7zz 54959 54958 2026-09-30T17:17:37Z Deadend0914 7211 54959 Scribunto text/plain --[=[ Rēgister of sprǣcnaman (language names) for the Old English Wiktionary. This is the single place where the Old English name of a language is entered. Everything that displays a language name on this wiki reads it from here: * [[Bysen:sprǣc]] (the ==L2== heading template) is a thin wrapper around sprǣc() below. * [[Module:etymology]] and [[Module:etymology/templates]] use get_name() for headings and category names, and get_of() for the "of"-form, because Old English uses "of" + genitive when an ancestor or source language is mentioned, e.g. "Of Englisce and". Names are entered without leading or trailing spaces. get_of() applies the following rules in order: 1. an explicit entry in `of_names` (for names that follow no rule); 2. `-isc` + "e" (Englisc -> Englisce, Nīwenglisc -> Nīwenglisce, Þēodisc -> Þēodisce); 3. otherwise the nominative name is returned unchanged and the miss is tracked in [[Wiktionary:Tracking/sprǣcnaman/no of-form]] so that an entry can be supplied. Codes with no name at all display "?" (as [[Bysen:sprǣc]] always has) and are tracked in [[Wiktionary:Tracking/sprǣc/unknown code]], see [[Special:WhatLinksHere/Wiktionary:Tracking/sprǣc/unknown code]]. ]=] local export = {} local names = { aa = "Cafarisc", ab = "Apscwisc", af = "Affricanisc", ak = "Ækanisc", ang = "Englisc", bn = "Bengalisc", ca = "Catalānisc", cs = "Bæmisc", da = "Denisc", de = "Þēodisc", el = "Crēacisc", en = "Nīwenglisc", eo = "Esperantisc", es = "Spēonisc", et = "Eotolisc", eu = "Wascisc", fa = "Persisc", fi = "Finnisc", fo = "Faerisc", fr = "Frencisc", fra = "Frencisc", frk = "Frencisc", frp = "Profencalisc", ga = "Īrisc", gem = "Germanisc", ["gem-pro"] = "Ealdoric Germanisc", gl = "Galizisc", gn = "Awaisc", ["gmq-pro"] = "Ealdoric Noren", gmw = "Westerngermanisc", ["gmw-pro"] = "Ealdoric Westerngermanisc", goh = "Ealdhēahþēodisc", got = "Gotisc", gv = "Manisc", haw = "Hawaiisc", hi = "Hindisc", hu = "Ungrisc", id = "Indonesisc", io = "Idoisc", ine = "Indoeuropisc", ["ine-pro"] = "Ealdoric Indoeuropisc", is = "Īslendisc", it = "Italisc", iu = "Inuctitutisc", la = "Lǣden", li = "Limburgisc", lt = "Litwisc", lv = "Lettisc", mul = "Þēodeorþe", nap = "Neapolitisc", nl = "Niðerlendisc", no = "Norþwegisc", non = "Eald Noren", ovd = "Ælfdælisc", pl = "Polisc", pt = "Portugisc", qu = "Kescwisc", ro = "Rumǣnisc", ru = "Rusisc", sa = "Sanscritisc", sco = "Niderscyttisc", sq = "Albānisc", sv = "Swēonisc", tpi = "Tok Pisin", vo = "Volapük", xpi = "Pihtisc" } --[=[ Codes that are needed in etymology lines (ancestors and source languages) but that have no name yet. They currently display their '''English''' name (via [[Module:languages]]) and should be given an Old English name here. The forms below are suggestions only and are deliberately commented out until they have been checked against a source. -- gem-pro = "?", -- Proto-Germanic -- gmw-pro = "?", -- Proto-West Germanic -- ine-pro = "?", -- Proto-Indo-European -- enm = "Middelenglisc", -- Middle English -- gmh = "Middelhēahþēodisc", -- Middle High German -- odt = "Ealdniðerlendisc", -- Old Dutch -- dum = "Middelniðerlendisc", -- Middle Dutch -- ofs = "?", -- Old Frisian -- osx = "Ealdseaxisc", -- Old Saxon -- gml = "Middelniðersaxisc", -- Middle Low German -- cel-pro = "?", -- Proto-Celtic -- gmq-pro = "?", -- Proto-North Germanic ]=] --[=[ Explicit "of"-forms, for names that the `-isc` rule cannot handle. Add an entry whenever [[Wiktionary:Tracking/sprǣcnaman/no of-form]] lists a code that needs one. Suggestions (commented out until checked against a source): -- la = "Lǣdenes", -- Latin -- non = "Eald Norðrena", -- Old Norse -- mul = "Þēodeorðan", -- Translingual -- vo = "Volapük", -- Volapük (no ending to inflect) -- tpi = "Tok Pisin", -- Tok Pisin (no ending to inflect) ]=] local of_names = {} -- Spellings that may be passed to {{sprǣc}} or found in a ==L2== heading, mapped to their code. local aliases = { ["Ænglisc"] = "ang", } -- Codes with no name display "?" and are tracked, so that the gaps are visible. local function track(key) local ok, track_module = pcall(require, "Module:debug/track") if ok then track_module(key) end end function export.track_missing_name(code) code = tostring(code) if code == "" then code = "(empty)" end track("sprǣc/unknown code/" .. code) end local function track_no_of_form(code) track("sprǣcnaman/no of-form/" .. tostring(code)) end -- Accept either a language code or a language object (from [[Module:languages]]). local function code_candidates(x) if type(x) ~= "table" then return { x } end local codes = {} local code = x.getCode and x:getCode() if code then codes[#codes + 1] = code end local full_code = x.getFullCode and x:getFullCode() if full_code and full_code ~= code then codes[#codes + 1] = full_code end return codes end --[==[ Return the Old English name of the given language (code or language object), or nil if the language has no name yet. Used for ==L2== headings and for category names. ]==] function export.get_name(x) for _, code in ipairs(code_candidates(x)) do if names[code] then return names[code] end end return nil end --[==[ Return the "of"-form of the given language (code or language object), or nil if the language has no name yet. Used wherever Old English needs "of" + genitive, e.g. "Of Englisce and". Names that follow no rule fall back to the nominative and are tracked. ]==] function export.get_of(x) for _, code in ipairs(code_candidates(x)) do if names[code] then return export.get_of_for_code(code) end end return nil end function export.get_of_for_code(code) local name = names[code] if not name then return nil end if of_names[code] then return of_names[code] end if name:sub(-3) == "isc" then return name .. "e" end track_no_of_form(code) return name end local code_of_name, lower_code_of_name local function make_reverse_maps() if code_of_name then return end code_of_name, lower_code_of_name = {}, {} for alias, code in pairs(aliases) do code_of_name[alias] = code lower_code_of_name[mw.ustring.lower(alias)] = code end for code, name in pairs(names) do code_of_name[name] = code_of_name[name] or code local lower = mw.ustring.lower(name) lower_code_of_name[lower] = lower_code_of_name[lower] or code end end --[==[ Return the code of the language with the given Old English name, or nil. Used by [[Module:etymology/templates]] to match an ==L2== heading back to its language. ]==] function export.get_code(name) if type(name) ~= "string" then return nil end name = mw.text.trim(name) if name == "" then return nil end make_reverse_maps() return code_of_name[name] or lower_code_of_name[mw.ustring.lower(name)] end --[==[ Entry point for [[Bysen:sprǣc]]: {{sprǣc|CODE}} gives the name, {{sprǣc|CODE|of}} the "of"-form. A code with no name gives "?" and is tracked, exactly as the template always has. ]==] export["sprǣc"] = function(frame) local args = frame and frame.args or {} local code = mw.text.trim(args[1] or "") local mode = mw.text.trim(args[2] or "") if code == "" then export.track_missing_name("") return "?" end local name if mode == "of" or mode == "gen" or mode == "genitive" then name = export.get_of(code) else name = export.get_name(code) if not name then -- The argument may be a name rather than a code (e.g. "Englisc"). local matched_code = export.get_code(code) name = matched_code and names[matched_code] or nil end end if not name then export.track_missing_name(code) return "?" end return name end --[==[ Entry point for templates that need the "of"-form only: {{#invoke:sprǣcnaman|of|CODE}}. ]==] function export.of(frame) local args = frame and frame.args or {} local code = mw.text.trim(args[1] or "") if code == "" then export.track_missing_name("") return "?" end local name = export.get_of(code) if not name then export.track_missing_name(code) return "?" end return name end -- The tables are exposed for tests and maintenance scripts; use the getters elsewhere. export.names = names export.of_names = of_names return export chm0sq5y4oq13xd0n7z7ah449j2t2qs 55040 54959 2026-09-30T19:37:16Z Deadend0914 7211 55040 Scribunto text/plain --[=[ Rēgister of sprǣcnaman (language names) for the Old English Wiktionary. This is the single place where the Old English name of a language is entered. Everything that displays a language name on this wiki reads it from here: * [[Bysen:sprǣc]] (the ==L2== heading template) is a thin wrapper around sprǣc() below. * [[Module:etymology]] and [[Module:etymology/templates]] use get_name() for headings and category names, and get_of() for the "of"-form, because Old English uses "of" + genitive when an ancestor or source language is mentioned, e.g. "Of Englisce and". Names are entered without leading or trailing spaces. get_of() applies the following rules in order: 1. an explicit entry in `of_names` (for names that follow no rule); 2. `-isc` + "e" (Englisc -> Englisce, Nīwenglisc -> Nīwenglisce, Þēodisc -> Þēodisce); 3. otherwise the nominative name is returned unchanged and the miss is tracked in [[Wiktionary:Tracking/sprǣcnaman/no of-form]] so that an entry can be supplied. Codes with no name at all display "?" (as [[Bysen:sprǣc]] always has) and are tracked in [[Wiktionary:Tracking/sprǣc/unknown code]], see [[Special:WhatLinksHere/Wiktionary:Tracking/sprǣc/unknown code]]. ]=] local export = {} local names = { aa = "Cafarisc", ab = "Apscwisc", af = "Affricanisc", ak = "Ækanisc", ang = "Englisc", bn = "Bengalisc", ca = "Catalānisc", cs = "Bæmisc", da = "Denisc", de = "Þēodisc", el = "Crēacisc", en = "Nīwenglisc", enm = "Middelenglisc", eo = "Esperantisc", es = "Spēonisc", et = "Eotolisc", eu = "Wascisc", fa = "Persisc", fi = "Finnisc", fo = "Faerisc", fr = "Frencisc", fra = "Frencisc", frk = "Frencisc", frp = "Profencalisc", ga = "Īrisc", gem = "Germanisc", ["gem-pro"] = "Ealdoric Germanisc", gl = "Galizisc", gn = "Awaisc", ["gmq-pro"] = "Ealdoric Noren", gmw = "Westerngermanisc", ["gmw-pro"] = "Ealdoric Westerngermanisc", goh = "Ealdhēahþēodisc", got = "Gotisc", gv = "Manisc", haw = "Hawaiisc", hi = "Hindisc", hu = "Ungrisc", id = "Indonesisc", io = "Idoisc", ine = "Indoeuropisc", ["ine-pro"] = "Ealdoric Indoeuropisc", is = "Īslendisc", it = "Italisc", iu = "Inuctitutisc", la = "Lǣden", li = "Limburgisc", lt = "Litwisc", lv = "Lettisc", mul = "Þēodeorþe", nap = "Neapolitisc", nl = "Niðerlendisc", no = "Norþwegisc", non = "Eald Noren", ovd = "Ælfdælisc", pl = "Polisc", pt = "Portugisc", qu = "Kescwisc", ro = "Rumǣnisc", ru = "Rusisc", sa = "Sanscritisc", sco = "Niderscyttisc", sq = "Albānisc", sv = "Swēonisc", tpi = "Tok Pisin", vo = "Volapük", xpi = "Pihtisc" } --[=[ Codes that are needed in etymology lines (ancestors and source languages) but that have no name yet. They currently display their '''English''' name (via [[Module:languages]]) and should be given an Old English name here. The forms below are suggestions only and are deliberately commented out until they have been checked against a source. -- gem-pro = "?", -- Proto-Germanic -- gmw-pro = "?", -- Proto-West Germanic -- ine-pro = "?", -- Proto-Indo-European -- enm = "Middelenglisc", -- Middle English -- gmh = "Middelhēahþēodisc", -- Middle High German -- odt = "Ealdniðerlendisc", -- Old Dutch -- dum = "Middelniðerlendisc", -- Middle Dutch -- ofs = "?", -- Old Frisian -- osx = "Ealdseaxisc", -- Old Saxon -- gml = "Middelniðersaxisc", -- Middle Low German -- cel-pro = "?", -- Proto-Celtic -- gmq-pro = "?", -- Proto-North Germanic ]=] --[=[ Explicit "of"-forms, for names that the `-isc` rule cannot handle. Add an entry whenever [[Wiktionary:Tracking/sprǣcnaman/no of-form]] lists a code that needs one. Suggestions (commented out until checked against a source): -- la = "Lǣdenes", -- Latin -- non = "Eald Norðrena", -- Old Norse -- mul = "Þēodeorðan", -- Translingual -- vo = "Volapük", -- Volapük (no ending to inflect) -- tpi = "Tok Pisin", -- Tok Pisin (no ending to inflect) ]=] local of_names = {} -- Spellings that may be passed to {{sprǣc}} or found in a ==L2== heading, mapped to their code. local aliases = { ["Ænglisc"] = "ang", } -- Codes with no name display "?" and are tracked, so that the gaps are visible. local function track(key) local ok, track_module = pcall(require, "Module:debug/track") if ok then track_module(key) end end function export.track_missing_name(code) code = tostring(code) if code == "" then code = "(empty)" end track("sprǣc/unknown code/" .. code) end local function track_no_of_form(code) track("sprǣcnaman/no of-form/" .. tostring(code)) end -- Accept either a language code or a language object (from [[Module:languages]]). local function code_candidates(x) if type(x) ~= "table" then return { x } end local codes = {} local code = x.getCode and x:getCode() if code then codes[#codes + 1] = code end local full_code = x.getFullCode and x:getFullCode() if full_code and full_code ~= code then codes[#codes + 1] = full_code end return codes end --[==[ Return the Old English name of the given language (code or language object), or nil if the language has no name yet. Used for ==L2== headings and for category names. ]==] function export.get_name(x) for _, code in ipairs(code_candidates(x)) do if names[code] then return names[code] end end return nil end --[==[ Return the "of"-form of the given language (code or language object), or nil if the language has no name yet. Used wherever Old English needs "of" + genitive, e.g. "Of Englisce and". Names that follow no rule fall back to the nominative and are tracked. ]==] function export.get_of(x) for _, code in ipairs(code_candidates(x)) do if names[code] then return export.get_of_for_code(code) end end return nil end function export.get_of_for_code(code) local name = names[code] if not name then return nil end if of_names[code] then return of_names[code] end if name:sub(-3) == "isc" then return name .. "e" end track_no_of_form(code) return name end local code_of_name, lower_code_of_name local function make_reverse_maps() if code_of_name then return end code_of_name, lower_code_of_name = {}, {} for alias, code in pairs(aliases) do code_of_name[alias] = code lower_code_of_name[mw.ustring.lower(alias)] = code end for code, name in pairs(names) do code_of_name[name] = code_of_name[name] or code local lower = mw.ustring.lower(name) lower_code_of_name[lower] = lower_code_of_name[lower] or code end end --[==[ Return the code of the language with the given Old English name, or nil. Used by [[Module:etymology/templates]] to match an ==L2== heading back to its language. ]==] function export.get_code(name) if type(name) ~= "string" then return nil end name = mw.text.trim(name) if name == "" then return nil end make_reverse_maps() return code_of_name[name] or lower_code_of_name[mw.ustring.lower(name)] end --[==[ Entry point for [[Bysen:sprǣc]]: {{sprǣc|CODE}} gives the name, {{sprǣc|CODE|of}} the "of"-form. A code with no name gives "?" and is tracked, exactly as the template always has. ]==] export["sprǣc"] = function(frame) local args = frame and frame.args or {} local code = mw.text.trim(args[1] or "") local mode = mw.text.trim(args[2] or "") if code == "" then export.track_missing_name("") return "?" end local name if mode == "of" or mode == "gen" or mode == "genitive" then name = export.get_of(code) else name = export.get_name(code) if not name then -- The argument may be a name rather than a code (e.g. "Englisc"). local matched_code = export.get_code(code) name = matched_code and names[matched_code] or nil end end if not name then export.track_missing_name(code) return "?" end return name end --[==[ Entry point for templates that need the "of"-form only: {{#invoke:sprǣcnaman|of|CODE}}. ]==] function export.of(frame) local args = frame and frame.args or {} local code = mw.text.trim(args[1] or "") if code == "" then export.track_missing_name("") return "?" end local name = export.get_of(code) if not name then export.track_missing_name(code) return "?" end return name end -- The tables are exposed for tests and maintenance scripts; use the getters elsewhere. export.names = names export.of_names = of_names return export g4edz5mk1jj7gemx5f3pv3aglvc4taq Bysen:inh+ 10 8076 55031 54904 2026-09-30T19:29:44Z Deadend0914 7211 55031 wikitext text/x-wiki <includeonly>{{#invoke:Wordstǣr|inh_plus|of=Of}}</includeonly><noinclude>{{Documentation}}</noinclude> 94fiuxe03agab16ol9gqbfvwhez5zh7 Module:floccnaman 828 8082 55005 54944 2026-09-30T18:16:50Z Deadend0914 7211 55005 Scribunto text/plain --[=[ Floccnaman (category names) for the Old English Wiktionary. Every category that a reader sees on an entry page is built as "<sprǣc> <word>" or "<sprǣc> <word> <word>", exactly like the categories that already exist by hand on this wiki ("Englisc nama", "Nīwenglisc fégung", "Eald Noren werlic nama"), so only the words need translating, never the word order. The modules that build such categories ([[Module:headword]], [[Module:headword utilities]], [[Module:form of]], [[Module:gender and number]], [[Module:affix]], [[Module:etymology]], [[Module:etymology/specialized]], [[Module:ang-headword]]) ask this module for the Old English wording first and keep the English wording if it is not here yet. Every missing wording is tracked in [[Wiktionary:Tracking/flocc/untranslated/<wording>]], so the list of what still needs to be added is always up to date: [[Special:WhatLinksHere/Wiktionary:Tracking/flocc/untranslated]] Words are entered without leading or trailing spaces, in the singular for parts of speech ("nama", not "naman"), because that is what the pre-existing categories use. The plural forms used for the lemma categories are entered separately ("lemmas", "non-lemma forms", ...). Only categories a reader sees on an entry page are translated here. Maintenance and tracking categories (sortkeys, script codes, requests, pages with ...), the categories of the template namespace and the ones produced by MediaWiki itself stay English on purpose. ]=] local export = {} --[=[ English wording -> Old English wording. The words in this first group are already used by categories and template names that exist on this wiki, so their spelling is established ("Flocc:Nama", "Flocc:Bīnaman", "Flocc:Nīwenglisc fégung", "Bysen:Tōgeīecendlic-Tabule", "Cynn 1 strang forþgewiten dǣlnimend"), and the gender words match the existing "Eald Noren werlic nama" style. ]=] local words = { -- Parts of speech. ["nouns"] = "naman", ["verbs"] = "word", ["adjectives"] = "tōgeīecendlice word", ["adverbs"] = "bīword", ["conjunctions"] = "fégunga", ["pronouns"] = "bīnaman", ["prepositions"] = "foresetnessa", ["participles"] = "dǣlnimend", ["past participles"] = "forþgewiten dǣlnimend", ["present participles"] = "andweard dǣlnimend", ["masculine"] = "werlic", ["masculine nouns"] = "werlic naman", ["masculine a-stem nouns"] = "werlic a-stefn naman", ["feminine"] = "wīflic", ["feminine nouns"] = "wīflic naman", ["neuter"] = "nāhwæðer", ["neuter POS"] = "nāhwæðer", ["lemmas"] = "frumcennede word", ["non-lemma forms"] = "gebīegede word", ["variant lemmas"] = "ōþre frumcennede word", ["variant non-lemma forms"] = "ōþre gebīegede word", ["proper nouns"] = "āgennama", ["numerals"] = "getælu", ["letters"] = "stafas", ["proverbs"] = "gecyndesprǣce", ["other languages"] = "ōþre sprǣce", ["terms borrowed from"] = "word borgodon of", ["terms borrowed back into"] = "word borgodon tō", ["terms inherited from"] = "word ierfde of", ["terms derived from"] = "word ofgangende fram", ["learned borrowings"] = "leornode borgiende", ["compound words"] = "manigfeald word", ["abbreviations"] = "scortnessa", ["plural"] = "manigfeald", ["phrases"] = "cwidas", ["determiners"] = "bīnama", ["symbols"] = "rūna", ["countable nouns"] = "tellendlīc naman", ["uncountable nouns"] = "untellendlīc naman", ["initialisms"] = "styrtende stæfgefēg", ["particles"] = "grot", ["pages with entries"] = "trāmetas mid word" } --[=[ Wording still to be translated. Anything listed here (or used in a module but not listed at all) falls back to its English form and shows up in [[Wiktionary:Tracking/flocc/untranslated]], so nothing breaks while the table is filled in. Suggestions, commented out until checked against a source: -- ["suffixes"] = "?", -- "Englisc ?" -- ["prefixes"] = "?", -- ["interjections"] = "?", -- ["Translingual"] = "?", -- ["taxonomic names"] = "?", ]=] --[=[ Wording that carries a variable part, as a leading phrase. For example the affix templates build categories such as "terms suffixed with -kas", where only the beginning is fixed; the language name is prepended by the caller. Longest phrase first, so the most specific rule wins. ]=] local prefixes = { {"terms spelled with ", "word areahton mid "} -- {"terms suffixed with ", "?"}, -- {"terms prefixed with ", "?"}, -- {"terms circumfixed with ", "?"}, -- {"terms infixed with ", "?"}, -- {"terms interfixed with ", "?"}, } local function track(text) local ok, track_module = pcall(require, "Module:debug/track") if ok then track_module("flocc/untranslated/" .. tostring(text)) end end --[==[ Return the Old English wording for one category name, or nil if it is not known yet. `lang` is accepted so that a wording can later be made language-specific if that turns out to be needed. ]==] function export.translate(lang, text) if type(text) ~= "string" or text == "" then return nil end if words[text] then return words[text] end for _, rule in ipairs(prefixes) do if text:sub(1, #rule[1]) == rule[1] then return rule[2] .. text:sub(#rule[1] + 1) end end track(text) return nil end --[==[ Return the Old English wording for one category name, or the English wording unchanged if it is not known yet. Use this in the modules that build categories: the language name is prepended by the caller, so only the wording itself is passed in. ]==] function export.get(lang, text) return export.translate(lang, text) or text end -- The table is exposed for tests and maintenance scripts; use get() elsewhere. export.words = words return export i7uyto2oc6fnpjv2yfhinma72oh1og3 55010 55005 2026-09-30T18:36:36Z Deadend0914 7211 55010 Scribunto text/plain --[=[ Floccnaman (category names) for the Old English Wiktionary. Every category that a reader sees on an entry page is built as "<sprǣc> <word>" or "<sprǣc> <word> <word>", exactly like the categories that already exist by hand on this wiki ("Englisc nama", "Nīwenglisc fégung", "Eald Noren werlic nama"), so only the words need translating, never the word order. The modules that build such categories ([[Module:headword]], [[Module:headword utilities]], [[Module:form of]], [[Module:gender and number]], [[Module:affix]], [[Module:etymology]], [[Module:etymology/specialized]], [[Module:ang-headword]]) ask this module for the Old English wording first and keep the English wording if it is not here yet. Every missing wording is tracked in [[Wiktionary:Tracking/flocc/untranslated/<wording>]], so the list of what still needs to be added is always up to date: [[Special:WhatLinksHere/Wiktionary:Tracking/flocc/untranslated]] Words are entered without leading or trailing spaces, in the singular for parts of speech ("nama", not "naman"), because that is what the pre-existing categories use. The plural forms used for the lemma categories are entered separately ("lemmas", "non-lemma forms", ...). Only categories a reader sees on an entry page are translated here. Maintenance and tracking categories (sortkeys, script codes, requests, pages with ...), the categories of the template namespace and the ones produced by MediaWiki itself stay English on purpose. ]=] local export = {} --[=[ English wording -> Old English wording. The words in this first group are already used by categories and template names that exist on this wiki, so their spelling is established ("Flocc:Nama", "Flocc:Bīnaman", "Flocc:Nīwenglisc fégung", "Bysen:Tōgeīecendlic-Tabule", "Cynn 1 strang forþgewiten dǣlnimend"), and the gender words match the existing "Eald Noren werlic nama" style. ]=] local words = { -- Parts of speech. ["nouns"] = "naman", ["verbs"] = "word", ["adjectives"] = "tōgeīecendlice word", ["adverbs"] = "bīword", ["conjunctions"] = "fégunga", ["pronouns"] = "bīnaman", ["prepositions"] = "foresetnessa", ["participles"] = "dǣlnimend", ["past participles"] = "forþgewiten dǣlnimend", ["present participles"] = "andweard dǣlnimend", ["masculine"] = "werlic", ["masculine nouns"] = "werlic naman", ["masculine a-stem nouns"] = "werlic a-stefn naman", ["feminine"] = "wīflic", ["feminine nouns"] = "wīflic naman", ["neuter"] = "nāhwæðer", ["neuter nouns"] = "nāhwæðer naman", ["lemmas"] = "frumcennede word", ["non-lemma forms"] = "gebīegede word", ["variant lemmas"] = "ōþre frumcennede word", ["variant non-lemma forms"] = "ōþre gebīegede word", ["proper nouns"] = "āgennama", ["numerals"] = "getælu", ["letters"] = "stafas", ["proverbs"] = "gecyndesprǣce", ["other languages"] = "ōþre sprǣce", ["terms borrowed from"] = "word borgodon of", ["terms borrowed back into"] = "word borgodon tō", ["terms inherited from"] = "word ierfde of", ["terms derived from"] = "word ofgangende fram", ["learned borrowings"] = "leornode borgiende", ["compound words"] = "manigfeald word", ["abbreviations"] = "scortnessa", ["plural"] = "manigfeald", ["phrases"] = "cwidas", ["determiners"] = "bīnama", ["symbols"] = "rūna", ["countable nouns"] = "tellendlīc naman", ["uncountable nouns"] = "untellendlīc naman", ["initialisms"] = "styrtende stæfgefēg", ["particles"] = "grot", ["pages with entries"] = "trāmetas mid word" } --[=[ Wording still to be translated. Anything listed here (or used in a module but not listed at all) falls back to its English form and shows up in [[Wiktionary:Tracking/flocc/untranslated]], so nothing breaks while the table is filled in. Suggestions, commented out until checked against a source: -- ["suffixes"] = "?", -- "Englisc ?" -- ["prefixes"] = "?", -- ["interjections"] = "?", -- ["Translingual"] = "?", -- ["taxonomic names"] = "?", ]=] --[=[ Wording that carries a variable part, as a leading phrase. For example the affix templates build categories such as "terms suffixed with -kas", where only the beginning is fixed; the language name is prepended by the caller. Longest phrase first, so the most specific rule wins. ]=] local prefixes = { {"terms spelled with ", "word areahton mid "} -- {"terms suffixed with ", "?"}, -- {"terms prefixed with ", "?"}, -- {"terms circumfixed with ", "?"}, -- {"terms infixed with ", "?"}, -- {"terms interfixed with ", "?"}, } local function track(text) local ok, track_module = pcall(require, "Module:debug/track") if ok then track_module("flocc/untranslated/" .. tostring(text)) end end --[==[ Return the Old English wording for one category name, or nil if it is not known yet. `lang` is accepted so that a wording can later be made language-specific if that turns out to be needed. ]==] function export.translate(lang, text) if type(text) ~= "string" or text == "" then return nil end if words[text] then return words[text] end for _, rule in ipairs(prefixes) do if text:sub(1, #rule[1]) == rule[1] then return rule[2] .. text:sub(#rule[1] + 1) end end track(text) return nil end --[==[ Return the Old English wording for one category name, or the English wording unchanged if it is not known yet. Use this in the modules that build categories: the language name is prepended by the caller, so only the wording itself is passed in. ]==] function export.get(lang, text) return export.translate(lang, text) or text end -- The table is exposed for tests and maintenance scripts; use get() elsewhere. export.words = words return export 4p71zjjux08e5nutegi5120j652v3xn 55012 55010 2026-09-30T18:39:14Z Deadend0914 7211 55012 Scribunto text/plain --[=[ Floccnaman (category names) for the Old English Wiktionary. Every category that a reader sees on an entry page is built as "<sprǣc> <word>" or "<sprǣc> <word> <word>", exactly like the categories that already exist by hand on this wiki ("Englisc nama", "Nīwenglisc fégung", "Eald Noren werlic nama"), so only the words need translating, never the word order. The modules that build such categories ([[Module:headword]], [[Module:headword utilities]], [[Module:form of]], [[Module:gender and number]], [[Module:affix]], [[Module:etymology]], [[Module:etymology/specialized]], [[Module:ang-headword]]) ask this module for the Old English wording first and keep the English wording if it is not here yet. Every missing wording is tracked in [[Wiktionary:Tracking/flocc/untranslated/<wording>]], so the list of what still needs to be added is always up to date: [[Special:WhatLinksHere/Wiktionary:Tracking/flocc/untranslated]] Words are entered without leading or trailing spaces, in the singular for parts of speech ("nama", not "naman"), because that is what the pre-existing categories use. The plural forms used for the lemma categories are entered separately ("lemmas", "non-lemma forms", ...). Only categories a reader sees on an entry page are translated here. Maintenance and tracking categories (sortkeys, script codes, requests, pages with ...), the categories of the template namespace and the ones produced by MediaWiki itself stay English on purpose. ]=] local export = {} --[=[ English wording -> Old English wording. The words in this first group are already used by categories and template names that exist on this wiki, so their spelling is established ("Flocc:Nama", "Flocc:Bīnaman", "Flocc:Nīwenglisc fégung", "Bysen:Tōgeīecendlic-Tabule", "Cynn 1 strang forþgewiten dǣlnimend"), and the gender words match the existing "Eald Noren werlic nama" style. ]=] local words = { -- Parts of speech. ["nouns"] = "naman", ["verbs"] = "word", ["adjectives"] = "tōgeīecendlice word", ["adverbs"] = "bīword", ["conjunctions"] = "fégunga", ["pronouns"] = "bīnaman", ["prepositions"] = "foresetnessa", ["participles"] = "dǣlnimend", ["past participles"] = "forþgewiten dǣlnimend", ["present participles"] = "andweard dǣlnimend", ["masculine"] = "werlic", ["masculine nouns"] = "werlic naman", ["masculine a-stem nouns"] = "werlic a-stefn naman", ["feminine"] = "wīflic", ["feminine nouns"] = "wīflic naman", ["neuter"] = "nāhwæðer", ["neuter nouns"] = "nāhwæðer naman", ["neuter a-stem nouns"] = "nāhwæðer a-stefn naman", ["lemmas"] = "frumcennede word", ["non-lemma forms"] = "gebīegede word", ["variant lemmas"] = "ōþre frumcennede word", ["variant non-lemma forms"] = "ōþre gebīegede word", ["proper nouns"] = "āgennama", ["numerals"] = "getælu", ["letters"] = "stafas", ["proverbs"] = "gecyndesprǣce", ["other languages"] = "ōþre sprǣce", ["terms borrowed from"] = "word borgodon of", ["terms borrowed back into"] = "word borgodon tō", ["terms inherited from"] = "word ierfde of", ["terms derived from"] = "word ofgangende fram", ["learned borrowings"] = "leornode borgiende", ["compound words"] = "manigfeald word", ["abbreviations"] = "scortnessa", ["plural"] = "manigfeald", ["phrases"] = "cwidas", ["determiners"] = "bīnama", ["symbols"] = "rūna", ["countable nouns"] = "tellendlīc naman", ["uncountable nouns"] = "untellendlīc naman", ["initialisms"] = "styrtende stæfgefēg", ["particles"] = "grot", ["pages with entries"] = "trāmetas mid word" } --[=[ Wording still to be translated. Anything listed here (or used in a module but not listed at all) falls back to its English form and shows up in [[Wiktionary:Tracking/flocc/untranslated]], so nothing breaks while the table is filled in. Suggestions, commented out until checked against a source: -- ["suffixes"] = "?", -- "Englisc ?" -- ["prefixes"] = "?", -- ["interjections"] = "?", -- ["Translingual"] = "?", -- ["taxonomic names"] = "?", ]=] --[=[ Wording that carries a variable part, as a leading phrase. For example the affix templates build categories such as "terms suffixed with -kas", where only the beginning is fixed; the language name is prepended by the caller. Longest phrase first, so the most specific rule wins. ]=] local prefixes = { {"terms spelled with ", "word areahton mid "} -- {"terms suffixed with ", "?"}, -- {"terms prefixed with ", "?"}, -- {"terms circumfixed with ", "?"}, -- {"terms infixed with ", "?"}, -- {"terms interfixed with ", "?"}, } local function track(text) local ok, track_module = pcall(require, "Module:debug/track") if ok then track_module("flocc/untranslated/" .. tostring(text)) end end --[==[ Return the Old English wording for one category name, or nil if it is not known yet. `lang` is accepted so that a wording can later be made language-specific if that turns out to be needed. ]==] function export.translate(lang, text) if type(text) ~= "string" or text == "" then return nil end if words[text] then return words[text] end for _, rule in ipairs(prefixes) do if text:sub(1, #rule[1]) == rule[1] then return rule[2] .. text:sub(#rule[1] + 1) end end track(text) return nil end --[==[ Return the Old English wording for one category name, or the English wording unchanged if it is not known yet. Use this in the modules that build categories: the language name is prepended by the caller, so only the wording itself is passed in. ]==] function export.get(lang, text) return export.translate(lang, text) or text end -- The table is exposed for tests and maintenance scripts; use get() elsewhere. export.words = words return export qcyz8amhcl3qb4wdkdtl2j8o1i55mdk Module:table/sortedPairs 828 8086 54961 2026-09-30T17:32:41Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local table_keys_to_list_module = "Module:table/keysToList" local function keys_to_list(...) keys_to_list = require(table_keys_to_list_module) return keys_to_list(...) end --[==[ Iterates through a table, with the keys sorted using the keysToList function. If there are only numerical keys, `export.sparseIpairs` is probably faster.]==] return function(t, key_sort) local list, i = keys_to_list(t, key_sort), 0 return function() i = i + 1 local k = list[i]...' 54961 Scribunto text/plain local table_keys_to_list_module = "Module:table/keysToList" local function keys_to_list(...) keys_to_list = require(table_keys_to_list_module) return keys_to_list(...) end --[==[ Iterates through a table, with the keys sorted using the keysToList function. If there are only numerical keys, `export.sparseIpairs` is probably faster.]==] return function(t, key_sort) local list, i = keys_to_list(t, key_sort), 0 return function() i = i + 1 local k = list[i] if k ~= nil then return k, t[k] end end end k9kspqkam96jj5np6nfk0sg15yvnnd3 Module:table/keysToList 828 8087 54962 2026-09-30T17:33:04Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local compare_module = "Module:compare" local pairs = pairs local sort = table.sort local compare local function get_compare() compare, get_compare = require(compare_module), nil return compare end --[==[ Returns a list of the keys in a table, sorted using either the default `table.sort` function or a custom `key_sort` function. If there are only numerical keys, [[Module:table/numKeys]] is probably faster.]==] return function(t, key_sort) local list, i = {...' 54962 Scribunto text/plain local compare_module = "Module:compare" local pairs = pairs local sort = table.sort local compare local function get_compare() compare, get_compare = require(compare_module), nil return compare end --[==[ Returns a list of the keys in a table, sorted using either the default `table.sort` function or a custom `key_sort` function. If there are only numerical keys, [[Module:table/numKeys]] is probably faster.]==] return function(t, key_sort) local list, i = {}, 0 for key in pairs(t) do i = i + 1 list[i] = key end -- Use specified sort function, or otherwise `compare`. sort(list, key_sort == nil and (compare or get_compare()) or key_sort) return list end db7h2mui4n7duv8tkb39qolcbe3t8rm Module:compare 828 8088 54963 2026-09-30T17:33:28Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local math_compare_module = "Module:math/compare" local string_compare_module = "Module:string/compare" local table_compare_module = "Module:table/compare" local require = require local type = type local types local function get_types() types, get_types = { ["nil"] = "\1", ["boolean"] = "\2", ["number"] = "\3", ["string"] = "\4", }, nil return types end local compare_funcs local function get_compare_funcs() compare_funcs = {} function compare_fu...' 54963 Scribunto text/plain local math_compare_module = "Module:math/compare" local string_compare_module = "Module:string/compare" local table_compare_module = "Module:table/compare" local require = require local type = type local types local function get_types() types, get_types = { ["nil"] = "\1", ["boolean"] = "\2", ["number"] = "\3", ["string"] = "\4", }, nil return types end local compare_funcs local function get_compare_funcs() compare_funcs = {} function compare_funcs.boolean(a, b) return a == true and b == false end function compare_funcs.number(...) local math_compare = require(math_compare_module) compare_funcs.number = math_compare return math_compare(...) end function compare_funcs.string(...) local string_compare = require(string_compare_module) compare_funcs.string = string_compare return string_compare(...) end function compare_funcs.table(...) local table_compare = require(table_compare_module) compare_funcs.table = table_compare return table_compare(...) end get_compare_funcs = nil return compare_funcs end --[==[ A general comparison function, which returns {true} if {a} sorts before {b}, or otherwise {false}; it can be used as the sort function with {table.sort}. This function is roughly equivalent to the {<} operator, but is capable of comparing a mix of types, using the following rules: * When items of different types are compared, the type-order is {"nil"}, {"boolean"}, {"number"}, {"string"}, then alphabetical order by type name for all other types. * Numbers are compared with [[Module:math/compare]]. * Strings are compared with [[Module:string/compare]]. * Tables are compared with [[Module:table/compare]]. * Boolean {true} sorts before {false}. * All other types always return {false}.]==] return function(a, b) local type_a, type_b = type(a), type(b) if type_a == type_b then local func = (compare_funcs or get_compare_funcs())[type_a] return func and func(a, b) or false end return ((types or get_types())[type_a] or type_a) < (types[type_b] or type_b) end fs82yur52kblfpa4j6uwsb5dbcxr7em Module:math/compare 828 8089 54964 2026-09-30T17:33:54Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local math_module = "Module:math" local function is_NaN(...) is_NaN = require(math_module).is_NaN return is_NaN(...) end local function sign(...) sign = require(math_module).sign return sign(...) end --[==[ A comparison function for numbers, which returns {true} if {a} sorts before {b}, or otherwise {false}; it can be used as the sort function with {table.sort}. This function is roughly equivalent to the {<} operator, but contains the following special co...' 54964 Scribunto text/plain local math_module = "Module:math" local function is_NaN(...) is_NaN = require(math_module).is_NaN return is_NaN(...) end local function sign(...) sign = require(math_module).sign return sign(...) end --[==[ A comparison function for numbers, which returns {true} if {a} sorts before {b}, or otherwise {false}; it can be used as the sort function with {table.sort}. This function is roughly equivalent to the {<} operator, but contains the following special considerations in accordance with the {{w|IEEE 754}} standard: * {{w|NaN}} is sorted as though it has a larger absolute value than infinity ({-NaN < -Inf}; {+Inf < +NaN}). * {{w|Signed zero}} is acknowledged, with {-0 < +0}.]==] return function(a, b) -- <, > and == canot return true if either `a` or `b` are NaN. if a < b then return true -- Use > then == instead of >=, so that the ±0 check is only done when `a` -- and `b` are equal. elseif a > b then return false elseif a == b then -- 1/(+0) is Inf; 1/(-0) is -Inf. return a == 0 and b == 0 and 1 / a < 1 / b or false -- One or both must be NaN, and NaN is the only number that returns false -- to a self-equality check, so if `a` == `a` then `b` is NaN (and vice- -- versa). -NaN sorts before everything and +NaN after everything, so the -- sign determines the result. elseif not is_NaN(a) then -- `b` is NaN return sign(b) == 1 elseif not is_NaN(b) then -- `a` is NaN return sign(a) == -1 end -- If both are NaN, only return true if `a` is -NaN and `b` is +NaN. return sign(a) < sign(b) end 632obodjr2zjfbz9zpr3erx1io9vwm4 Module:table/compare 828 8090 54965 2026-09-30T17:34:28Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local string_linesub_module = "Module:string/linesub" local table_get_metamethod_module = "Module:table/getMetamethod" local error = error local rawequal = rawequal local require = require local sub = string.sub local traceback = debug.traceback local xpcall = xpcall local function get_metamethod(...) get_metamethod = require(table_get_metamethod_module) return get_metamethod(...) end local function linesub(...) linesub = require(string_linesub_module) ret...' 54965 Scribunto text/plain local string_linesub_module = "Module:string/linesub" local table_get_metamethod_module = "Module:table/getMetamethod" local error = error local rawequal = rawequal local require = require local sub = string.sub local traceback = debug.traceback local xpcall = xpcall local function get_metamethod(...) get_metamethod = require(table_get_metamethod_module) return get_metamethod(...) end local function linesub(...) linesub = require(string_linesub_module) return linesub(...) end local _a, _b -- used to pass arguments to compare_for_xpcall() local NOT_COMPARABLE = {} -- sentinel local function compare_for_xpcall() local a, b = _a, _b _a, _b = nil return a < b end local function err_handler(err) -- Error message relate to comparing two table values, but the trace at the -- start of the message needs to be trimmed. Traceback will only refer to -- xpcall() on line 5 if the error occured in compare_for_xpcall(); if it -- originated further up the stack (i.e. in a metamethod), xpcall() will -- only be mentioned on a later line. if ( sub(err, -35) == "attempt to compare two table values" and linesub(traceback(), 5, 5) == "\t[C]: in function 'xpcall'" ) then return NOT_COMPARABLE end return err end --[==[ A comparison function for tables, which returns {true} if {a} sorts before {b}, or otherwise {false}; it can be used as the sort function with {table.sort}. By default, attempting to compare two tables will result in an error, unless both tables have the same {__lt} metamethod. This function will also use the {__lt} metamethod under the same circumstances, but will return {false} instead of throwing an error if the two tables are not comparable.]==] return function(a, b) -- If `a` and `b` are tables, they are only comparable if they both have the -- same __lt metamethod. get_unprotected_metatable() returns nil if there is -- no metatable, and false if the metatable is protected. local success_a, __lt_a = get_metamethod(a, "__lt") -- Not comparable if `a` has no __lt metamethod. if success_a and __lt_a == nil then return false end local success_b, __lt_b = get_metamethod(b, "__lt") if success_b then -- Not comparable if `b` has no __lt metamethod. if __lt_b == nil then return false -- If `a` and `b` have known __lt metamethods, they are only comparable -- if the two metamethods are primitively equal. elseif __lt_a ~= nil then return rawequal(__lt_a, __lt_b) and a < b end end -- Not possible to know if `a` and `b` are comparable if either or both have -- protected metatables, and neither are confirmed to have no __lt -- metamethod. Use xpcall() when comparing them, with an error handler that -- determines if the error was due to `a` and `b` not being comparable. -- Unlike pcall(), xpcall() can't pass on arguments to the called function, -- so use upvalues in the top-level of the module to circumvent this. _a, _b = a, b local success, result = xpcall(compare_for_xpcall, err_handler) if success then return result elseif result == NOT_COMPARABLE then return false end -- If the error wasn't due to the comparison, rethrow it. error(result) end 8mcw42u3tihzaq6279e3zlbjws3nhdr Module:string/linesub 828 8091 54966 2026-09-30T17:34:52Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local index_module = "Module:index" local string_linelen_module = "Module:string/linelen" local find = string.find local require = require local sub = string.sub local function index_range(...) index_range = require(index_module).range return index_range(...) end local function linelen(...) linelen = require(string_linelen_module) return linelen(...) end return function(str, i, j) i, j = index_range(str, i, j, linelen) if j and j < i then return "" e...' 54966 Scribunto text/plain local index_module = "Module:index" local string_linelen_module = "Module:string/linelen" local find = string.find local require = require local sub = string.sub local function index_range(...) index_range = require(index_module).range return index_range(...) end local function linelen(...) linelen = require(string_linelen_module) return linelen(...) end return function(str, i, j) i, j = index_range(str, i, j, linelen) if j and j < i then return "" end local first, pos1, pos2, newline, pattern, plain = 1 if not find(str, "\r", nil, true) then pattern, plain = "\n", true elseif not find(str, "\n", nil, true) then pattern, plain = "\r", true else pattern = "([\n\r]\n?)" end for _ = 1, i - 1 do pos1, pos2, newline = find(str, pattern, first, plain) if pos1 == nil then return "" end first = newline == "\n\n" and pos2 or pos2 + 1 end if j == nil then return sub(str, first) end local last = first for _ = i, j do pos1, pos2, newline = find(str, pattern, last, plain) if pos1 == nil then return sub(str, first) end last = newline == "\n\n" and pos2 or pos2 + 1 end last = pos1 - 1 return sub(str, first, last) end qzydmib4uoy2d53y6s4vvt1a6pynnwv Module:index 828 8092 54967 2026-09-30T17:35:22Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local math_module = "Module:math" local error = error local format = string.format local tostring = tostring local type = type local function to_integer(...) to_integer = require(math_module).to_integer return to_integer(...) end local function input_err(i, funcname, param) local idx_type = type(i) error(format( "bad argument #%d to 'index.%s' (integer expected, got %s)", param, funcname, idx_type == "number" and tostring(i) or idx_t...' 54967 Scribunto text/plain local export = {} local math_module = "Module:math" local error = error local format = string.format local tostring = tostring local type = type local function to_integer(...) to_integer = require(math_module).to_integer return to_integer(...) end local function input_err(i, funcname, param) local idx_type = type(i) error(format( "bad argument #%d to 'index.%s' (integer expected, got %s)", param, funcname, idx_type == "number" and tostring(i) or idx_type ), 4) end local function get_index(i, obj, func, neg1_nil, funcname, param) i = to_integer(i) or input_err(i, funcname, param) -- A relative index of -1 refers to the final index. For many functions, -- it makes no difference if the final index is nil or -1, so if the -- `neg1_nil` flag is set and the index is -1, return nil to avoid having -- to call `func`. if neg1_nil and i == -1 then return nil, obj, func elseif i < 0 then if func ~= nil then obj, func = func(obj), nil end i = obj + i + 1 end return i, obj, func end function export.absolute(obj, i, func, i_min, i_max) i, obj, func = get_index(i, obj, func, false, "absolute", 2) if i_min ~= nil then i_min, obj, func = get_index(i_min, obj, func, false, "absolute", 4) if i < i_min then i = i_min end end if i_max ~= nil then i_max, obj, func = get_index(i_max, obj, func, false, "absolute", 5) if i_min and i_min > i_max then error("bad arguments #4 and #5 to 'index.absolute' (`i_min` cannot be greater than `i_max`)", 2) elseif i > i_max then i = i_max end end return i end function export.range(obj, i, j, func, always_infer_upper, i_min, j_max) -- Canonicalize `i`, or otherwise default to 1. if i == nil then i = 1 else i, obj, func = get_index(i, obj, func, false, "range", 2) end -- Get the minimum value for `i`. This is 1 by default, because a start -- index of 0 is always equivalent to 1 for most functions (e.g. string.byte -- and string.sub). However, many module functions have checks like -- "while i <= j" or "if i > j" that won't, therefore, handle (0, 0) like -- (1, 0), so converting 0 to 1 avoids the need to special-case it. If -- `i_min_allow_0` is set, the minimum defaults to 0 instead (including when -- the input is nil), which may be needed if 0 represents the position -- before the first index. if i_min == nil then i_min = 1 else i_min, obj, func = get_index(i_min, obj, func, false, "range", 5) end -- Set `i` as the minimum value if it hasn't been specified. if i < i_min then i = i_min end -- Canonicalize `j`, which will default to the end if not given. By default, -- this may be implied by returning nil for `j` (even if the input is -1), -- to avoid calling `func` unnecessarily. if j ~= nil then j, obj, func = get_index(j, obj, func, not always_infer_upper, "range", 3) end -- Get the maximum value for `j` (if any). if j_max ~= nil then j_max, obj, func = get_index(j_max, obj, func, not always_infer_upper, "range", 6) end -- If `j` is not set but `j_max` has a value, use that. Otherwise, if -- `always_infer_upper` is set then give the length of `obj`. if not j then j = j_max or always_infer_upper and (not func and obj or func(obj)) or nil -- Ensure `j` does not exceed `j_max`. elseif j_max and j > j_max then j = j_max end -- The minimum value for `j` is i - 1, which is equivalent to a range of -- 0 length (e.g. string.find("", "") returns (1, 0)). local j_min = i - 1 if j and j < j_min then j = j_min end return i, j end return export oo8syi7zilb2cjg7t58khlglmo5bzyt Module:string/linelen 828 8093 54968 2026-09-30T17:35:44Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local gmatch = string.gmatch --[==[ Counts the number of lines in the input string, treating {"\n"}, {"\r"} and {"\r\n"} as new lines.]==] return function(str) local n = 1 for newline in gmatch(str, "([\n\r]\n?)[^\n\r]*") do n = n + (newline == "\n\n" and 2 or 1) end return n end' 54968 Scribunto text/plain local gmatch = string.gmatch --[==[ Counts the number of lines in the input string, treating {"\n"}, {"\r"} and {"\r\n"} as new lines.]==] return function(str) local n = 1 for newline in gmatch(str, "([\n\r]\n?)[^\n\r]*") do n = n + (newline == "\n\n" and 2 or 1) end return n end jpgjm9kkpx83bv03ujdphtzhtxxjsg2 Module:labels 828 8094 54969 2026-09-30T17:37:22Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} export.lang_specific_data_list_module = "Module:labels/data/lang" export.lang_specific_data_modules_prefix = "Module:labels/data/lang/" local load_module = "Module:load" local parse_utilities_module = "Module:parse utilities" local string_utilities_module = "Module:string utilities" local utilities_module = "Module:utilities" local insert = table.insert local concat = table.concat local require_when_needed = require("Module:require when neede...' 54969 Scribunto text/plain local export = {} export.lang_specific_data_list_module = "Module:labels/data/lang" export.lang_specific_data_modules_prefix = "Module:labels/data/lang/" local load_module = "Module:load" local parse_utilities_module = "Module:parse utilities" local string_utilities_module = "Module:string utilities" local utilities_module = "Module:utilities" local insert = table.insert local concat = table.concat local require_when_needed = require("Module:require when needed") local unpack = unpack or table.unpack -- Lua 5.2 compatibility local dump = mw.dumpObject local m_lang_specific_data = mw.loadData(export.lang_specific_data_list_module) local m_table = require_when_needed("Module:table") --[==[ intro: Labels go through several stages of processing to get from the original (raw) label specified in the Wikicode to the final (formatted) label displayed to the user. The following terminology will help keep things straight: * The "raw label" is the label specified in the Wikicode. * The "non-canonical label" is the label extracted from the raw label, used for looking up in the label modules in order to fetch the associated label data structure and determine the canonical form of the label. Normally this is the same as the raw label, but it will be different if the raw label is of the form `!<var>label</var>` (e.g. `!Australian`) `<var>label</var>!<var>display</var>` (e.g. `Southern US!Southern`). The former syntax indicates that the label should display as-is instead of in its canonical form (which in the example given is `Australia`), and the latter syntax indicates that the label should display in the form specified after the exclamation point. * The "canonical label" is the result of applying alias resolution to the non-canonical label. Normally, the canonical label rather than the non-canonical label is what is shown to the user. * The "display form of the label" is what is shown to the user, not considering links and HTML that may wrap the display form to get the formatted form of the label. The display form comes from the `.display` field of the module label data for the label; if no such field exists in the label data, it is normally the canonical label. However, if the display override exists (see below), it takes precedence over the `.display` field or canonical label when determining the display form of the label. * The "display override", if specified, overrides all other means of determining the display form of the label. It is specified in two circumstances, i.e. in the `!<var>label</var>` and `<var>label</var>!<var>display</var>` raw label formats (i.e. in the same cirumstances where the raw label and non-canonical label are different). * The "formatted form of the label" is the final form of the label shown directly to the user. It generally appears to the user as the display form of the label, but in the Wikicode, the formatted form may wrap the display form with a link to Wikipedia, the Wiktionary glossary or another Wiktionary entry, and that link in turn may be wrapped in an HTML span with a "deprecated" CSS class attached, causing the label to display differently (to indicate that it is deprecated). ]==] -- for testing local force_cat = false local m_headword_data = mw.loadData("Module:headword/data") local SUBPAGENAME = m_headword_data.pagename -- Disable tracking on heavy pages to save time. local pages_where_tracking_is_disabled = m_headword_data.large_pages -- Add tracking category for PAGE. The tracking category linked to is [[Wiktionary:Tracking/labels/PAGE]]. -- We also add to [[Wiktionary:Tracking/labels/PAGE/LANGCODE]] and [[Wiktionary:Tracking/labels/PAGE/MODE]] if -- LANGCODE and/or MODE given. local function track(page, langcode, mode) if pages_where_tracking_is_disabled[SUBPAGENAME] then return true end -- avoid including links in pages (may cause error) page = page:gsub("%[", "("):gsub("%]", ")"):gsub("|", "!") require("Module:debug/track")("labels/" .. page) if langcode then require("Module:debug/track")("labels/" .. page .. "/" .. langcode) end if mode then require("Module:debug/track")("labels/" .. page .. "/" .. mode) end -- We don't currently add a tracking label for both langcode and mode to reduce the total number of labels, to -- save some memory. return true end local function ucfirst(txt) return mw.getContentLanguage():ucfirst(txt) end local mode_to_outer_class = { ["label"] = "usage-label-sense", ["term-label"] = "usage-label-term", ["accent"] = "usage-label-accent", ["form-of"] = "usage-label-form-of", } local mode_to_property_prefix = { ["label"] = false, ["term-label"] = false, -- handled specially ["accent"] = "accent_", ["form-of"] = "form_of_", } local function validate_mode(mode) mode = mode or "label" if not mode_to_outer_class[mode] then local allowed_values = {} for key, _ in pairs(mode_to_outer_class) do insert(allowed_values, "'" .. key .. "'") end table.sort(allowed_values) error(("Invalid value '%s' for `mode`; should be one of %s"):format(mode, concat(allowed_values, ", "))) end return mode end local function getprop(labdata, mode, prop) local mode_prefix = mode_to_property_prefix[mode] return mode_prefix and labdata[mode_prefix .. prop] or labdata[prop] end local function check_type(label, lang, prop, value, expected_types) if value == nil or expected_types == nil then return value end if type(expected_types) ~= "table" then expected_types = {expected_types} end local valtype = type(value) local matches = false for _, expected_type in ipairs(expected_types) do if type(expected_type) == "string" then if valtype == expected_type then matches = true break end elseif value == expected_type then matches = true break end end if not matches then local function join_untagged_or(elements) return m_table.serialCommaJoin(elements, {conj = "or", dontTag = true}) end local quoted_types = {} local quoted_values = {} for _, expected_type in ipairs(expected_types) do if type(expected_type) == "string" then insert(quoted_types, "'" .. expected_type .. "'") else insert(quoted_values, "'" .. dump(expected_type) .. "'") end end local possible_matches = {} if quoted_types[1] then insert(possible_matches, ("be of type%s %s"):format( quoted_types[2] and "s" or "", join_untagged_or(quoted_types))) end if quoted_values[1] then insert(possible_matches, ("have the value%s %s"):format( quoted_values[2] and "s" or "", join_untagged_or(quoted_values))) end error(("Internal error: For label '%s', langcode '%s', property '%s' should %s but is of type '%s' with value %s"):format( label, lang and lang:getCode() or "UNKNOWN", prop, join_untagged_or(possible_matches), valtype, dump(value))) end end -- HACK! For languages in any of the given families, check the specified-language Wikipedia for appropriate -- Wikipedia articles for the language in question (esp. useful for obscure etymology-only languages that may not -- have English articles for them, like many Chinese lects). local families_to_wikipedia_languages = { {"zhx", "zh"}, {"sem-arb", "ar"}, } --[==[ Given language `lang` (a full language, etymology-language or family), fetch a list of Wikimedia languages to check when converting a Wikidata item to a Wikipedia article. English is always first, followed by the Wikimedia language code(s) of `lang` if `lang` is a language (which may or may not be the same as `lang`'s Wiktionary code), followed by the macrolanguage of `lang` for certain languages and families (currently, only languages and families in the Chinese and Arabic families). If `lang` is nil, only return English. Note that the same code may occur more than once in the list. This is exported because it's also used by [[Module:category tree/lects]]. ]==] function export.get_langs_to_extract_wikipedia_articles_from_wikidata(lang) local wikipedia_langs = {} insert(wikipedia_langs, "en") if lang then local article_lang = lang while article_lang do if article_lang:hasType("language") then local wmcodes = article_lang:getWikimediaLanguageCodes() for _, wmcode in ipairs(wmcodes) do insert(wikipedia_langs, wmcode) end end article_lang = article_lang:getParent() end for _, family_to_wp_lang in ipairs(families_to_wikipedia_languages) do local family, wp_lang = unpack(family_to_wp_lang) if lang:inFamily(family) then insert(wikipedia_langs, wp_lang) end end end return wikipedia_langs end --[==[ Fetch the categories to add to a page, given that the label whose canonical form is `canon_label` with language `lang` has been seen. `labdata` is the label data structure for `label`, fetched from the appropriate submodule. `mode` specifies how the label was invoked (see {get_label_info()} for more information). The return value is a list of the actual categories, unless `for_doc` is specified, in which case the categories returned are marked up for display on a documentation page. If `for_doc` is given, `lang` may be nil to format the categories in a language-independent fashion; otherwise, it must be specified. If `category_types` is specified, it should be a set object (i.e. with category types as keys and {true} as values), and only categories of the specified types will be returned. ]==] function export.fetch_categories(canon_label, labdata, lang, mode, for_doc, category_types) local categories = {} mode = validate_mode(mode) local langcode, canonical_name if lang then langcode = lang:getFullCode() canonical_name = lang:getFullName() elseif for_doc then langcode = "<var>[langcode]</var>" canonical_name = "<var>[language name]</var>" else error("Internal error: Must specify `lang` unless `for_doc` is given") end local function labprop(prop, expected_types) local retval = getprop(labdata, mode, prop) check_type(canon_label, lang, prop, retval, expected_types) return retval end local empty_list = {} local function get_cats(cat_type) if category_types and not category_types[cat_type] then return empty_list end local cats = labprop(cat_type) if not cats then return empty_list end if type(cats) ~= "table" then return {cats} end return cats end local topical_categories = get_cats("topical_categories") local sense_categories = get_cats("sense_categories") local pos_categories = get_cats("pos_categories") local regional_categories = get_cats("regional_categories") local plain_categories = get_cats("plain_categories") local function insert_cat(cat, sense_cat) if for_doc then cat = "<code>" .. cat .. "</code>" if sense_cat then if mode == "term-label" then cat = cat .. " (using {{tl|tlb}})" else cat = cat .. " (using {{tl|lb}} or form-of template)" end cat = mw.getCurrentFrame():preprocess(cat) end end insert(categories, cat) end for _, cat in ipairs(topical_categories) do insert_cat(langcode .. ":" .. (cat == true and ucfirst(canon_label) or cat)) end for _, cat in ipairs(sense_categories) do if cat == true then cat = canon_label end cat = mode == "term-label" and cat .. " terms" or "terms with " .. cat .. " senses" insert_cat(canonical_name .. " " .. cat, true) end for _, cat in ipairs(pos_categories) do insert_cat(canonical_name .. " " .. (cat == true and canon_label or cat)) end for _, cat in ipairs(regional_categories) do insert_cat((cat == true and ucfirst(canon_label) or cat) .. " " .. canonical_name) end for _, cat in ipairs(plain_categories) do insert_cat(cat == true and ucfirst(canon_label) or cat) end return categories end --[==[ Return the list of all labels data modules for a label whose language is `lang`. The return value is a list of module names, with overriding modules earlier in the list (that is, if a label occurs in two modules in the list, the earlier-listed module takes precedence). If `lang` is nil, only return non-language-specific submodules. ]==] function export.get_submodules(lang) local submodules = { "Module:labels/data", "Module:labels/data/qualifiers", "Module:labels/data/regional", "Module:labels/data/topical", } if not lang then return submodules end -- get language-specific labels from data module local langcode = lang:getFullCode() if m_lang_specific_data.langs_with_lang_specific_modules[langcode] then -- prefer per-language label in order to pick subvariety labels over regional ones insert(submodules, 1, export.lang_specific_data_modules_prefix .. langcode) end return submodules end --[==[ Return the formatted form of a label `label` (which should be the canonical form of the label; see comment at top), given (a) the label data structure `labdata` from one of the data modules; (b) the language object `lang` of the language being processed, or nil for no language; (c) `deprecated` (true if the label is deprecated, otherwise the deprecation information is taken from `labdata`); (d) `override_display` (if specified, override the display form of the label with the specified string, instead of any value in `labdata.display` or `labdata.special_display` or the canonical label in `label` itself); (e) `mode` (same as `data.mode` passed to {get_label_info()}). Returns two values: the formatted label form and a boolean indicating whether the label is deprecated. '''NOTE: Under normal circumstances, do not use this.''' Instead, use {get_label_info()}, which searches all the data modules for a given label and handles other complications. ]==] function export.format_label(label, labdata, lang, deprecated, override_display, mode) local formatted_label mode = validate_mode(mode) local function labprop(prop, expected_types) local retval = getprop(labdata, mode, prop) check_type(label, lang, prop, retval, expected_types) return retval end deprecated = deprecated or labprop("deprecated") if not override_display and labprop("special_display") then local function add_language_name(str) if str == "canonical_name" then if lang then return lang:getFullName() else return "<code><var>[language name]</var></code>" end else return "" end end formatted_label = labprop("special_display", "string"):gsub("<(.-)>", add_language_name) else --[=[ We proceed as follows: 1. The display form comes from either (a) the `override_display` variable if set (this happens when the user uses a label like '!British'); (b) the `display` property, if set; or (c) the label iself. 2. If the display form contains a link, use it directly and ignore the other display-related settings. (NOTE: Settings `Wikipedia` and `Wikidata` may still be used on the category page itself, by the category tree code.) 3. Otherwise, use one of the other display-related settings, in the following order: `glossary` > `Wiktionary` > `Wikipedia` > `Wikidata`. Specifically: a. If any of the values is equal to `true`, that is equivalent to specifying a string consisting of the canonical label. b. If `glossary` is set, it specifies the anchor in [[Appendix:Glossary]]. c. If `Wiktionary` is set, it specifies an arbitrary Wiktionary page or page + anchor (e.g. a separate Appendix entry). d. If `Wikipedia` is set, it specifies an arbitrary Wikipedia article, or a list of such items (in this case, we select the first one, but the category tree uses all of them). e. If `Wikidata` is set, it specifies an arbitrary Wikidata item to retrieve a Wikipedia article from, or a list of such items (in this case, we select the first one, but the category tree uses all of them). If the item is of the form `wmcode:id`, the Wikipedia article corresponding to `id` in the `wmcode`-language Wikipedia is fetched if available. Otherwise, the English-language Wikipedia article corresponding to `id` is retrieved if available, falling back to the Wikimedia language(s) corresponding to `lang` and then (in certain cases) to the macrolanguage that `lang` is part of. Note that if `mode` is specified, prefixed properties (e.g. `accent_display` for `mode` == "accent", `form_display` for `mode` == "form") are checked before the bare equivalent (e.g. `display`). ]=] local display = override_display or labprop("display", "string") or label -- There are several 'Foo spelling' labels specially designed for use in the |from= param in -- {{alternative form of}}, {{standard spelling of}} and the like. Often the display includes the word -- "spelling" at the end (e.g. if it's defaulted), which is useful when the label is used with {{tl|lb}} or -- {{tl|tlb}}; but it causes redundancy when used with the form-of templates, which add the word "form", -- "spelling", "standard spelling", etc. after the label. if mode == "form-of" then display = display:gsub(" spelling$", "") end if display:find("%[%[") then formatted_label = display else local glossary = labprop("glossary", {"string", true}) local Wiktionary = labprop("Wiktionary", {"string", true}) local Wikipedia = labprop("Wikipedia", {"string", true, "table"}) local Wikidata = labprop("Wikidata", {"string", true, "table"}) if glossary then local glossary_entry = glossary == true and label or glossary formatted_label = "[[Appendix:Glossary#" .. glossary_entry .. "|" .. display .. "]]" elseif Wiktionary then local Wiktionary_entry = Wiktionary == true and label or Wiktionary if Wiktionary == display then formatted_label = "[[" .. display .. "]]" else formatted_label = "[[" .. Wiktionary_entry .. "|" .. display .. "]]" end elseif Wikipedia then if type(Wikipedia) == "table" then Wikipedia = Wikipedia[1] end local Wikipedia_entry = Wikipedia == true and label or Wikipedia formatted_label = "[[w:" .. Wikipedia_entry .. "|" .. display .. "]]" elseif Wikidata then if not mw.wikibase then error(("Unable to retrieve data from Wikidata ID for label '%s'; `mw.wikibase` not defined" ):format(label)) end local function make_formatted_label(wmcode, id) local article = mw.wikibase.sitelink(id, wmcode .. "wiki") if article then local link = wmcode == "en" and "w:" .. article or "w:" .. wmcode .. ":" .. article return ("[[%s|%s]]"):format(link, display) else return nil end end if type(Wikidata) == "table" then Wikidata = Wikidata[1] end local wmcode, id = Wikidata:match("^(.*):(.*)$") if wmcode then formatted_label = make_formatted_label(wmcode, id) else local langs_to_check = export.get_langs_to_extract_wikipedia_articles_from_wikidata(lang) for _, wmcode in ipairs(langs_to_check) do formatted_label = make_formatted_label(wmcode, Wikidata) if formatted_label then break end end end formatted_label = formatted_label or display else formatted_label = display end end end if deprecated then formatted_label = '<span class="deprecated-label">' .. formatted_label .. '</span>' end return formatted_label, deprecated end --[==[ Return information on a label. On input `data` is an object with the following fields: * `label`: The raw label to return information on. * `lang`: The language of the label. Must be specified unless `for_doc` is given. * `mode`: How the label was invoked. One of the following: ** {nil} or {"label"}: invoked through {{tl|lb}} or another template whose labels in the same fashion, e.g. {{tl|alt}}, {{tl|quote}} or {{tl|syn}}; ** {"term-label"}: invoked through {{tl|tlb}}; ** {"accent"}: invoked through {{tl|a}} or the {{para|a}} or {{para|aa}} parameters of other pronunciation templates, such as {{tl|IPA}}, {{tl|rhymes}} or {{tl|homophones}}; ** {"form-of"}: invoked through {{tl|alt form}}, {{tl|standard spelling of}} or other form-of template. This changes the display and/or categorization of a minority of labels. (The majority work the same for all modes.) * `for_doc`: Data is being fetched for documentation purposes. This causes the raw categories returned in `categories` to be formatted for documentation display. * `nocat`: If true, don't add the label to any categories. * `force_cat`: Force adding categories even in namespaces that normally exclude them (e.g. userspace and discussion pages). * `notrack`: Disable all tracking for this label. * `sort`: Sort key for categorization. * `already_seen`: An object used to track labels already seen, so they aren't displayed twice. Tracking is according to the display form of the label, so if two labels have the same display form, the second one won't be displayed (but its categories will still be added). If `already_seen` is {nil}, this tracking doesn't happen. The return value is an object with the following fields: * `raw_text`: If specified, the object does not describe a label but simply raw text surrounding labels. This occurs when double angle bracket (<<...>>) notation is used. {get_label_info()} does not currently return objects with this field set, but {process_raw_labels()} does. The value is {"begin"} (this is the first raw text portion derived from a double angle bracket spec, provided there are at least two raw text portions); {"end"} (this is the last raw text portion derived from a double angle bracket spec, provided there are at least two portions); {"middle"} (this is neither the first nor the last raw text portion); or {"only"} (this is a raw text portion standing by itself). The particular value determines the handling of commas and spaces on one or both sides of the raw text. If this field is specified, only the `label` field (containing the actual raw text) and the `category` field (containing an empty list) are set; all other fields are {nil}. * `raw_label`: The raw label that was passed in. * `non_canonical`: The label prior to canonicalization (i.e. alias resolution). Usually this is the same as `raw_label`, but if the raw label was preceded by an exclamation point (meaning "display the raw label as-is"), this field will contain the label stripped of the exclamation point, and if the raw label is of the form `<var>label</var>!<var>display</var>` (meaning "display the label in the specified form"), this field will contain the label before the exclamation point. * `canonical`: If the label in `non_canonical` is an alias, this contains the canonical name of the label; otherwise it will be {nil}. * `override_display`: If specified, this contains a string that overrides the normal display form of the label. The display form of a label is the `.display` field of the label data if present, and otherwise is normally the canonical form of the label (i.e. after alias resolution). (This is not the same as the formatted form of the label, found in `label`, which is the final form shown to the user and includes links to Wikipedia, the glossary, etc. as well as an HTML wrapper if the label is deprecated.) If `override_display` is specified, however, this is used in place of the normal display form of the label. This currently happens in two circumstances: (1) the label was preceded by ! to indicate that the raw label should be displayed rather than the canonical form; (2) the label was given in the form `<var>label</var>!<var>display</var>` (meaning "display the label in the specified `<var>display</var>` form"). * `label`: The formatted form of the label. This is what is actually shown to the user. If the label is recognized (found in some module), this will typically be in the form of a link. * `categories`: A list of the categories to add the label to; an empty list if `nocat` was specified. * `formatted_categories`: A string containing the formatted categories; {nil} if `nocat` or `for_doc` was specified, or if `categories` is empty. Currently will be an empty string if there are categories to format but the namespace is one that normally excludes categories (e.g. userspace and discussion pages), and `force_cat` isn't specified. * `deprecated`: True if the label is deprecated. * `recognized`: If true, the label was found in some module. * `data`: The data structure for the label, as fetched from the label modules. For unrecognized labels, this will be an empty object. ]==] function export.get_label_info(data) if not data.label then error("`data` must now be an object containing the params") end local mode = validate_mode(data.mode) local ret = {categories = {}} local label = data.label local raw_label = label ret.raw_label = raw_label local override_display if label:find("^!") then label = label:gsub("^!", "") override_display = label elseif label:find("![^%s]") then label, override_display = label:match("^(.-)!([^%s].*)$") if not label then error(("Internal error: This Lua pattern should never fail to match for label '%s'"):format(raw_label)) end end local non_canonical = label ret.non_canonical = non_canonical local deprecated = false local labdata local submodule local data_langcode = data.lang and data.lang:getCode() or nil local submodules_to_check = export.get_submodules(data.lang) for _, submodule_to_check in ipairs(submodules_to_check) do submodule = mw.loadData(submodule_to_check) local this_labdata = submodule[label] local resolved_label if type(this_labdata) == "string" then resolved_label = this_labdata this_labdata = submodule[this_labdata] if not this_labdata then error(("Internal error: Label alias '%s' points to '%s', which is undefined in module [[%s]]"):format( label, resolved_label, submodule_to_check)) end if type(this_labdata) == "string" then error(("Internal error: Label alias '%s' points to '%s', which is also an alias (of '%s') in module [[%s]]"):format( label, resolved_label, this_labdata, submodule_to_check)) end end if this_labdata then -- Make sure either there's no lang restriction, or we're processing lang-independent, or our language -- is among the listed languages. Otherwise, continue processing (which could conceivably pick up a -- lang-appropriate version of the label in another label data module). local lablangs = getprop(this_labdata, mode, "langs") if not lablangs or not data_langcode then labdata = this_labdata label = resolved_label or label break end local lang_in_list = false for _, langcode in ipairs(lablangs) do if langcode == data_langcode then lang_in_list = true break end end if lang_in_list then labdata = this_labdata label = resolved_label or label break elseif not data.notrack then -- Track use of a label that fails the lang restriction. -- [[Special:WhatLinksHere/Wiktionary:Tracking/labels/wrong-lang-label]] -- [[Special:WhatLinksHere/Wiktionary:Tracking/labels/wrong-lang-label/LANGCODE]] -- [[Special:WhatLinksHere/Wiktionary:Tracking/labels/wrong-lang-label/LABEL]] -- [[Special:WhatLinksHere/Wiktionary:Tracking/labels/wrong-lang-label/LABEL/LANGCODE]] track("wrong-lang-label", data_langcode) track("wrong-lang-label/" .. label, data_langcode) if resolved_label then track("wrong-lang-label/" .. resolved_label, data_langcode) end end end end if labdata then ret.recognized = true else labdata = {} ret.recognized = false end local function labprop(prop) return getprop(labdata, mode, prop) end if labprop("deprecated") then deprecated = true end if label ~= non_canonical then -- Note that this is an alias and store the canonical version. ret.canonical = label end if not data.notrack then -- labprop("track") then -- track all labels now -- Track label (after converting aliases to canonical form; but also track raw label (alias) if different -- from canonical label). -- [[Special:WhatLinksHere/Wiktionary:Tracking/labels/label/LABEL]] -- [[Special:WhatLinksHere/Wiktionary:Tracking/labels/label/LABEL/LANGCODE]] -- [[Special:WhatLinksHere/Wiktionary:Tracking/labels/label/LABEL/MODE]] track("label/" .. label, data_langcode, mode) if label ~= non_canonical then track("label/" .. non_canonical, data_langcode, mode) end end local formatted_label formatted_label, deprecated = export.format_label(label, labdata, data.lang, deprecated, override_display, mode) ret.deprecated = deprecated if deprecated then if not data.nocat then local depcat = "Entries with deprecated labels" if data.for_doc then depcat = "<code>" .. depcat .. "</code>" end insert(ret.categories, depcat) end end local label_for_already_seen = (labprop("topical_categories") or labprop("regional_categories") or labprop("plain_categories") or labprop("pos_categories") or labprop("sense_categories")) and formatted_label or nil -- Track label text. If label text was previously used, don't show it, but include the categories. -- For an example, see [[hypocretin]]. if data.already_seen and data.already_seen[label_for_already_seen] then ret.label = "" else if formatted_label:find("{") then formatted_label = mw.getCurrentFrame():preprocess(formatted_label) end ret.label = formatted_label end if data.nocat then -- do nothing else local cats = export.fetch_categories(label, labdata, data.lang, mode, data.for_doc) for _, cat in ipairs(cats) do insert(ret.categories, cat) end if not ret.categories[1] or data.for_doc then -- Don't try to format categories if we're doing this for documentation ({{label/doc}}), because there -- will be HTML in the categories. -- do nothing else ret.formatted_categories = require(utilities_module).format_categories(ret.categories, data.lang, data.sort, nil, force_cat or data.force_cat) end end ret.data = labdata if label_for_already_seen and data.already_seen then data.already_seen[label_for_already_seen] = true end return ret end --[==[ Split a string containing comma-separated raw labels into the individual labels. This will not split on a comma followed by whitespace, and it will not split inside of matched <...> or [...]. The code is written to be efficient, so that it does not load modules (e.g. [[Module:parse utilities]]) unnecessarily. ]==] function export.split_labels_on_comma(term) if term:find("[%[<]") then -- Do it the "hard way". We don't want to split anything inside of <...> or <<...>> even if there are commas -- inside of the angle brackets. For good measure we do the same for [...] and [[...]]. We first parse balanced -- segment runs involving either [...] or <...>. Then we split alternating runs on comma (but not on -- comma+whitespace). Then we rejoin the split runs. For example, given the following: -- "regional,older <<non-rhotic,and,non-hoarse-horse>> speakers", the first call to -- parse_multi_delimiter_balanced_segment_run() produces -- -- {"regional,older ", "<<non-rhotic,and,non-hoarse-horse>>", " speakers"} -- -- After calling split_alternating_runs_on_comma(), we get the following: -- -- {{"regional"}, {"older ", "<<non-rhotic,and,non-hoarse-horse>>", " speakers"}} -- -- After rejoining each group, we get: -- -- {"regional", "older <<non-rhotic,and,non-hoarse-horse>> speakers"} -- -- which is the desired output. When processing the second "label" string, the code in process_raw_labels() -- will do a similar process to this to pull out the labels inside of the <<...>> notation. local put = require(parse_utilities_module) local segments = put.parse_multi_delimiter_balanced_segment_run(term, {{"<", ">"}, {"[", "]"}}) -- This won't split on comma+whitespace. local comma_separated_groups = put.split_alternating_runs_on_comma(segments) for i, group in ipairs(comma_separated_groups) do comma_separated_groups[i] = concat(group) end return comma_separated_groups elseif term:find(",%s") then -- This won't split on comma+whitespace. return require(parse_utilities_module).split_on_comma(term) elseif term:find(",") then return require(string_utilities_module).split(term, ",") else return {term} end end --[==[ Return a list of objects corresponding to a set of raw labels. Each object returned is of the format returned by {get_label_info()}. This is similar to looping over the labels and calling {get_label_info()} on each one, but it also correctly handles embedded double angle bracket specs <<...>> found in the labels. (In such a case, there will be more objects returned than raw labels passed in.) On input, `data` is an object with the following fields: * `labels`: The list of labels to process. * `lang`: The language of the labels. Must be specified. * `mode`: How the label was invoked; see {get_label_info()} for more information. * `nocat`: If true, don't add the label to any categories. * `force_cat`: Force adding categories even in namespaces that normally exclude them (e.g. userspace and discussion pages). * `notrack`: Disable all tracking for this label. * `sort`: Sort key for categorization. * `already_seen`: An object used to track labels already seen, so they aren't displayed twice. Tracking is according to the display form of the label, so if two labels have the same display form, the second one won't be displayed (but its categories will still be added). If `already_seen` is {nil}, this tracking doesn't happen. * `ok_to_destructively_modify`: If set, the `data` structure will be destructively modified in the process of this function running. ]==] function export.process_raw_labels(data) local label_infos = {} if not data.ok_to_destructively_modify then data = m_table.shallowCopy(data) data.ok_to_destructively_modify = true end local function get_info_and_insert(label) -- Reuse this structure to save memory. data.label = label insert(label_infos, export.get_label_info(data)) end for _, label in ipairs(data.labels) do if label:find("<<") then local segments = require(string_utilities_module).split(label, "<<(.-)>>") for i, segment in ipairs(segments) do if i % 2 == 1 then local raw_text_type = i == 1 and "begin" or i == #segments and "end" or "middle" insert(label_infos, {raw_text = raw_text_type, label = segment, categories = {}}) else local segment_labels = export.split_labels_on_comma(segment) for _, segment_label in ipairs(segment_labels) do get_info_and_insert(segment_label) end end end else get_info_and_insert(label) end end return label_infos end --[==[ Split a comma-separated string of raw labels and process each label to get a list of objects suitable for passing to {format_processed_labels()}. Each object returned is of the format returned by {get_label_info()}. This is equivalent to calling {split_labels_on_comma()} followed by {process_raw_labels()}. On input, `data` is an object with the following fields: * `labels`: The string containing the raw comma-separated labels. * `lang`: The language of the labels. Must be specified. * `mode`: How the label was invoked; see {get_label_info()} for more information. * `nocat`: If true, don't add the label to any categories. * `force_cat`: Force adding categories even in namespaces that normally exclude them (e.g. userspace and discussion pages). * `notrack`: Disable all tracking for this label. * `sort`: Sort key for categorization. * `already_seen`: An object used to track labels already seen, so they aren't displayed twice. Tracking is according to the display form of the label, so if two labels have the same display form, the second one won't be displayed (but its categories will still be added). If `already_seen` is {nil}, this tracking doesn't happen. * `ok_to_destructively_modify`: If set, the `data` structure will be destructively modified in the process of this function running. ]==] function export.split_and_process_raw_labels(data) if not data.ok_to_destructively_modify then data = m_table.shallowCopy(data) data.ok_to_destructively_modify = true end data.labels = export.split_labels_on_comma(data.labels) return export.process_raw_labels(data) end --[==[ Format one or more already-processed labels for display and categorization. "Already-processed" means that {get_label_info()} or {process_raw_labels()} has been called on the raw labels to convert them into objects containing information on how to display and categorize the labels. This is a lower-level alternative to {show_labels()} and is meant for modules such as [[Module:alternative forms]], [[Module:quote]] and [[Module:etymology/templates/descendant]] that support displaying labels along with some other information. On input `data` is an object with the following fields: * `labels`: List of the label objects to format, in the format returned by {get_label_info()}. * `lang`: The language of the labels. * `open`: Open bracket or parenthesis to display before the concatenated labels. If specified, it is wrapped in the {"ib-brac"} and {"label-brac"} CSS classes. If {nil} or {false}, no open bracket is displayed. * `close`: Close bracket or parenthesis to display after the concatenated labels. If specified, it is wrapped in the {"ib-brac"} and {"label-brac"} CSS classes. If {nil} or {false}, no close bracket is displayed. * `no_ib_content`: By default, the concatenated formatted labels inside of the open/close brackets are wrapped in the {"ib-content"} and {"label-content"} CSS classes. Specify this to suppress this wrapping. * `raw`: Suppress all CSS wrapping of content, including open/close parentheses, content and comma delimiters (which are normally wrapped in {"ib-comma"} and {"label-comma"} CSS classes). * `ok_to_destructively_modify`: If set, the `data` structure, and the `data.labels` table inside of it, will be destructively modified in the process of this function running. * `split_output`: If not given, the return value is a concatenation of the formatted concatenated labels and formatted categories. Otherwise, two values are returned: the formatted pronunciation and the categories. If `split_output` is the value {"raw"}, the categories are returned in list form, where the list elements are strings f the form suitable for passing to {format_categories()} in [[Module:utilities]]. If `split_output` is any other value besides {nil}, the categories are returned as a pre-formatted concatenated string. The return value (or the first return value, if `split_output` is given) is a string containing the contenated labels, optionally surrounded by open/close brackets or parentheses. Normally, labels are separated by comma-space sequences, but this may be suppressed for certain labels. If `nocat` wasn't given to {get_label_info()} or {process_raw_labels()}, and `split_output` wasn't given, the label objects will contain formatted categories in them, which will be inserted into the returned text. (Use `split_output` if you need the categories returned separately.) The concatenated text inside of the open/close brackets is normally wrapped in the {"ib-content"} CSS class, but this can be suppressed, as mentioned above. ]==] function export.format_processed_labels(data) if not data.labels then error("`data` must now be an object containing the params") end if not data.ok_to_destructively_modify then data = m_table.shallowCopy(data) data.labels = m_table.deepCopy(data.labels) data.ok_to_destructively_modify = true end local labels = data.labels if not labels[1] then error("You must specify at least one label.") end local split_output = data.split_output -- Show the labels local omit_preComma local omit_preSpace local omit_postComma = true local omit_postSpace = true for _, label in ipairs(labels) do omit_preComma = omit_postComma omit_preSpace = omit_postSpace local raw_text_omit_before = label.raw_text == "middle" or label.raw_text == "end" local raw_text_omit_after = label.raw_text == "middle" or label.raw_text == "begin" label.omit_comma = omit_preComma or (label.data and label.data.omit_preComma) or raw_text_omit_before omit_postComma = (label.data and label.data.omit_postComma) or raw_text_omit_after label.omit_space = omit_preSpace or (label.data and label.data.omit_preSpace) or raw_text_omit_before omit_postSpace = (label.data and label.data.omit_postSpace) or raw_text_omit_after end if data.lang then local lang_functions_module = export.lang_specific_data_modules_prefix .. data.lang:getCode() .. "/functions" local m_lang_functions = require(load_module).safe_require(lang_functions_module) if m_lang_functions and m_lang_functions.postprocess_handlers then for _, handler in ipairs(m_lang_functions.postprocess_handlers) do handler(data) end end end local function wrap_css(txt, suffix) if data.raw then return txt end return ("<span class=\"ib-%s label-%s\">%s</span>"):format(suffix, suffix, txt) end local categories = nil local formatted_categories = split_output and split_output ~= "raw" and {} or nil for i, labelinfo in ipairs(labels) do local label -- Need to check for 'not raw_text' here because blank labels may legitimately occur as raw text if a double -- angle bracket spec occurs at the beginning of a label. In this case we've already taken into account the -- context and don't want to leave out a preceding comma and space e.g. in a case like -- {{lb|en|rare|<<dialect>> or <<eye dialect>>}}. FIXME: We should reconsider whether we need this special case -- at all. if labelinfo.label == "" and not labelinfo.raw_text then label = "" else label = (labelinfo.omit_comma and "" or wrap_css(",", "comma")) .. (labelinfo.omit_space and "" or "&#32;") .. labelinfo.label end if split_output then labels[i] = label if split_output == "raw" then if labelinfo.categories and labelinfo.categories[1] then if categories then m_table.extend(categories, labelinfo.categories) else categories = labelinfo.categories end end elseif labelinfo.formatted_categories then insert(formatted_categories, labelinfo.formatted_categories) end else labels[i] = label .. (labelinfo.formatted_categories or "") end end local function wrap_open_close(val) if val then return wrap_css(val, "brac") else return "" end end local concatenated_labels = concat(labels, "") if not data.no_ib_content then concatenated_labels = wrap_css(concatenated_labels, "content") end local ret_labels = wrap_open_close(data.open) .. concatenated_labels .. wrap_open_close(data.close) if split_output == "raw" then return ret_labels, categories elseif split_output then return ret_labels, concat(formatted_categories) else return ret_labels end end --[==[ Format one or more labels for display and categorization. This provides the implementation of the {{tl|label}}/{{tl|lb}}, {{tl|term label}}/{{tl|tlb}} and {{tl|accent}}/{{tl|a}} templates, and can also be called from a module. The return value is a string to be inserted into the generated page, including the display and categories. On input `data` is an object with the following fields: * `labels`: List of the labels to format. * `lang`: The language of the labels. * `mode`: How the label was invoked; see {get_label_info()} for more information. * `nocat`: If true, don't add the labels to any categories. * `force_cat`: Force adding categories even in namespaces that normally exclude them (e.g. userspace and discussion pages). * `notrack`: Disable all tracking for these labels. * `sort`: Sort key for categorization. * `no_track_already_seen`: Don't track already-seen labels. If not specified, already-seen labels are not displayed again, but still categorize. See the documentation of {get_label_info()}. * `open`: Open bracket or parenthesis to display before the concatenated labels. If {nil}, defaults to an open parenthesis. Set to {false} to disable. * `close`: Close bracket or parenthesis to display after the concatenated labels. If {nil}, defaults to a close parenthesis. Set to {false} to disable. * `no_ib_content`: As in `format_processed_labels()`. * `raw`: As in `format_processed_labels()`. Also suppress wrapping the entire formatted result in a usage label CSS class (see below). * `ok_to_destructively_modify`: If set, the `data` structure will be destructively modified in the process of this function running. Compared with {format_processed_labels()}, this function has the following differences: # The labels specified in `labels` are raw labels (i.e. strings) rather than formatted objects. # The open and close brackets default to parentheses ("round brackets") rather than not being displayed by default. # Tracking of already-seen labels is enabled unless explicitly turned off using `no_track_already_seen`. # The entire formatted result is wrapped in a {"usage-label-<var>type</var>"} CSS class (depending on the value of `mode`), unless `raw` is given. ]==] function export.show_labels(data) if not data.labels then error("`data` must now be an object containing the params") end if not data.ok_to_destructively_modify then data = m_table.shallowCopy(data) data.ok_to_destructively_modify = true end local labels = data.labels if not labels[1] then error("You must specify at least one label.") end local mode = validate_mode(data.mode) if not data.no_track_already_seen then data.already_seen = {} end data.labels = export.process_raw_labels(data) if data.open == nil then data.open = "(" end if data.close == nil then data.close = ")" end local formatted = export.format_processed_labels(data) if data.raw then return formatted else return "<span class=\"" .. mode_to_outer_class[mode] .. "\">" .. formatted .. "</span>" end end --[==[Helper function for the data modules.]==] function export.alias(labels, key, aliases) m_table.alias(labels, key, aliases) end --[==[ Split the display form of a label. Returns two values: `link` and `display`. If the display form consists of a two-part link, `link` is the first part and `display` is the second part. If the display form consists of a single-part link, `link` and `display` are the same. Otherwise (the display form is not a link or contains an embedded link), `link` is the same as the passed-in `label` and `display` is nil. ]==] function export.split_display_form(label) if not label:find("%[%[") then return label, nil end local link, display = label:match("^%[%[([^%[%]|]+)|([^%[%]|]+)%]%]$") if link then return link, display end link = label:match("^%[%[([^%[%]|])+%]%]$") if link then return link, link end return label, nil end --[==[ Combine the `link` and `display` parts of the display form of a label as returned by {split_display_form()}. If `display` is nil, `link` is returned directly. Otherwise, a one-part or two-part link is constructed depending on whether `link` and `display` are the same. (As a special case, if both consist of a blank string, the return value is a blank string rather than a malformed link.) ]==] function export.combine_display_form_parts(link, display) if not display then return link end if link == display then if link == "" then return "" else return ("[[%s]]"):format(link) end end return ("[[%s|%s]]"):format(link, display) end --[==[Used to finalize the data into the form that is actually returned.]==] function export.finalize_data(labels) local shallow_copy = m_table.shallowCopy local aliases = {} for label, data in pairs(labels) do if type(data) == "table" then if data.aliases then for _, alias in ipairs(data.aliases) do aliases[alias] = label end data.aliases = nil end if data.deprecated_aliases then local data2 = shallow_copy(data) data2.deprecated = true data2.canonical = label for _, alias in ipairs(data2.deprecated_aliases) do aliases[alias] = data2 end data.deprecated_aliases = nil data2.deprecated_aliases = nil end end end for label, data in pairs(aliases) do labels[label] = data end return labels end return export b5g3rb91z6k1folvf0qlc1gqgh8low4 Module:labels/data/lang 828 8095 54970 2026-09-30T17:37:50Z Deadend0914 7211 Gesceop tramet þe hafaþ '-- Table listing all of the languages with lang-specific labels modules. local langs_with_lang_specific_modules = { ["ab"] = true, ["acm"] = true, ["ady"] = true, ["ae"] = true, ["af"] = true, ["afb"] = true, ["aht"] = true, ["aii"] = true, ["ain"] = true, ["ajp"] = true, ["ak"] = true, ["akk"] = true, ["amf"] = true, ["an"] = true, ["ang"] = true, ["apc"] = true, ["ar"] = true, ["arc"] = true, ["arq"] = true, ["arz"] = true, ["as"] = true,...' 54970 Scribunto text/plain -- Table listing all of the languages with lang-specific labels modules. local langs_with_lang_specific_modules = { ["ab"] = true, ["acm"] = true, ["ady"] = true, ["ae"] = true, ["af"] = true, ["afb"] = true, ["aht"] = true, ["aii"] = true, ["ain"] = true, ["ajp"] = true, ["ak"] = true, ["akk"] = true, ["amf"] = true, ["an"] = true, ["ang"] = true, ["apc"] = true, ["ar"] = true, ["arc"] = true, ["arq"] = true, ["arz"] = true, ["as"] = true, ["ast"] = true, ["av"] = true, ["ayl"] = true, ["az"] = true, ["bar"] = true, ["bcl"] = true, ["be"] = true, ["bew"] = true, ["bg"] = true, ["bho"] = true, ["bn"] = true, ["bo"] = true, ["br"] = true, ["bsh"] = true, ["bua"] = true, ["byk"] = true, ["ca"] = true, ["car"] = true, ["cbk"] = true, ["ceb"] = true, ["cel-pro"] = true, ["ch"] = true, ["cho"] = true, ["chr"] = true, ["cim"] = true, ["ckb"] = true, ["cop"] = true, ["cpg"] = true, ["cpi"] = true, ["crh"] = true, ["crp-cpr"] = true, ["cs"] = true, ["csb"] = true, ["cu"] = true, ["cy"] = true, ["da"] = true, ["dcc"] = true, ["de"] = true, ["dlm"] = true, ["dng"] = true, ["dnj"] = true, ["dum"] = true, ["dv"] = true, ["egl"] = true, ["egy"] = true, ["el"] = true, ["en"] = true, ["enm"] = true, ["es"] = true, ["et"] = true, ["eu"] = true, ["evn"] = true, ["fa"] = true, ["fax"] = true, ["ff"] = true, ["fi"] = true, ["fo"] = true, ["fr"] = true, ["fro"] = true, ["frp"] = true, ["frr"] = true, ["fur"] = true, ["fy"] = true, ["ga"] = true, ["gcf"] = true, ["gd"] = true, ["gem-pro"] = true, ["gl"] = true, ["gmq-oda"] = true, ["gmq-pro"] = true, ["gmw-bgh"] = true, ["gmw-cfr"] = true, ["gmw-ecg"] = true, ["gmw-pro"] = true, ["gmw-rfr"] = true, ["gmy"] = true, ["goh"] = true, ["grc"] = true, ["grk-ita"] = true, ["gsw"] = true, ["gu"] = true, ["gug"] = true, ["guw"] = true, ["ha"] = true, ["haa"] = true, ["haw"] = true, ["he"] = true, ["hi"] = true, ["hit"] = true, ["hrx"] = true, ["hsb"] = true, ["ht"] = true, ["hu"] = true, ["hy"] = true, ["id"] = true, ["idb"] = true, ["ilo"] = true, ["inc-apa"] = true, ["inc-ash"] = true, ["inc-ohi"] = true, ["is"] = true, ["it"] = true, ["iu"] = true, ["izh"] = true, ["ja"] = true, ["jbo"] = true, ["jje"] = true, ["jut"] = true, ["jv"] = true, ["ka"] = true, ["kbd"] = true, ["kca-eas"] = true, ["kca-nor"] = true, ["kca-sou"] = true, ["kea"] = true, ["kho"] = true, ["kix"] = true, ["klj"] = true, ["kls"] = true, ["kmr"] = true, ["kn"] = true, ["kne"] = true, ["ko"] = true, ["kok"] = true, ["kpt"] = true, ["kpv"] = true, ["krc"] = true, ["krl"] = true, ["kry"] = true, ["kw"] = true, ["kzw"] = true, ["la"] = true, ["lad"] = true, ["li"] = true, ["lij"] = true, ["lis"] = true, ["liv"] = true, ["lld"] = true, ["lmo"] = true, ["lrl"] = true, ["lt"] = true, ["lv"] = true, ["lzz"] = true, ["mak"] = true, ["mch"] = true, ["mco"] = true, ["mh"] = true, ["mhd"] = true, ["mic"] = true, ["mk"] = true, ["ml"] = true, ["mlm"] = true, ["mn"] = true, ["mns-cen"] = true, ["mns-nor"] = true, ["mns-sou"] = true, ["moh"] = true, ["mr"] = true, ["ms"] = true, ["mt"] = true, ["mui"] = true, ["mul"] = true, ["mus"] = true, ["mvi"] = true, ["mwl"] = true, ["my"] = true, ["nap"] = true, ["nb"] = true, ["nds"] = true, ["ne"] = true, ["new"] = true, ["nhn"] = true, ["nhx"] = true, ["niv"] = true, ["nl"] = true, ["nn"] = true, ["non"] = true, ["nrf"] = true, ["nrn"] = true, ["nv"] = true, ["oc"] = true, ["oj"] = true, ["oko"] = true, ["okz"] = true, ["onb"] = true, ["os"] = true, ["osc"] = true, ["ota"] = true, ["otk"] = true, ["pa"] = true, ["pam"] = true, ["paw"] = true, ["peh"] = true, ["phl"] = true, ["pl"] = true, ["pms"] = true, ["pnt"] = true, ["poz-pro"] = true, ["ppl"] = true, ["pra"] = true, ["ps"] = true, ["pt"] = true, ["qu"] = true, ["qwc"] = true, ["qwm"] = true, ["rcf"] = true, ["rgn"] = true, ["rm"] = true, ["rmc"] = true, ["rml"] = true, ["rmn"] = true, ["rmy"] = true, ["ro"] = true, ["roa-bbn"] = true, ["roa-leo"] = true, ["roa-ona"] = true, ["roa-opt"] = true, ["rom"] = true, ["rsk"] = true, ["ru"] = true, ["rue"] = true, ["rup"] = true, ["rw"] = true, ["rys"] = true, ["ryu"] = true, ["sa"] = true, ["saj"] = true, ["sc"] = true, ["scl"] = true, ["scn"] = true, ["sco"] = true, ["sd"] = true, ["se"] = true, ["sel-sou"] = true, ["sgh"] = true, ["sh"] = true, ["shi"] = true, ["sjd"] = true, ["sjs"] = true, ["sk"] = true, ["skr"] = true, ["sl"] = true, ["sla-pro"] = true, ["smi-pro"] = true, ["sn"] = true, ["sq"] = true, ["srn"] = true, ["su"] = true, ["sux"] = true, ["sv"] = true, ["sw"] = true, ["szl"] = true, ["ta"] = true, ["taa"] = true, ["tay"] = true, ["te"] = true, ["tet"] = true, ["tfn"] = true, ["tg"] = true, ["th"] = true, ["tkr"] = true, ["tl"] = true, ["tmh"] = true, ["tpw"] = true, ["tr"] = true, ["trk-pro"] = true, ["tsg"] = true, ["tt"] = true, ["tup-kaw"] = true, ["tuw-sol"] = true, ["tyv"] = true, ["udi"] = true, ["udm"] = true, ["uk"] = true, ["ur"] = true, ["urj-fin-pro"] = true, ["uz"] = true, ["vot"] = true, ["war"] = true, ["vec"] = true, ["vi"] = true, ["vo"] = true, ["xcl"] = true, ["xh"] = true, ["xme-ker"] = true, ["xmf"] = true, ["xpg"] = true, ["xqa"] = true, ["xum"] = true, ["ycr"] = true, ["yi"] = true, ["yo"] = true, ["yok-bvy"] = true, ["yok-dly"] = true, ["yok-kry"] = true, ["yok-nvy"] = true, ["yok-svy"] = true, ["yok-tky"] = true, ["yrk-tun"] = true, ["yrl"] = true, ["za"] = true, ["xbo"] = true, ["zh"] = true, ["zle-ono"] = true, ["zle-ort"] = true, ["zls-chs"] = true, ["zlw-ocs"] = true, ["zlw-opl"] = true, ["zlw-osk"] = true, ["zlw-slv"] = true, ["zu"] = true, } return { langs_with_lang_specific_modules = langs_with_lang_specific_modules, } 021olkkfh2bsnz6mw7ra0p6nehnwb1s Module:labels/data 828 8096 54971 2026-09-30T17:38:52Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local labels = {} -- Grammatical labels labels["abbreviation"] = { aliases = {"abbreviations", "abbreviated"}, glossary = true, pos_categories = "abbreviations", } labels["abstract noun"] = { display = "abstract", glossary = true, pos_categories = "abstract nouns", } labels["abstract verb"] = { display = "abstract", glossary = true, pos_categories = "abstract verbs", } labels["acronym"] = { glossary = true, pos_categories = "acronyms", } labels["...' 54971 Scribunto text/plain local labels = {} -- Grammatical labels labels["abbreviation"] = { aliases = {"abbreviations", "abbreviated"}, glossary = true, pos_categories = "abbreviations", } labels["abstract noun"] = { display = "abstract", glossary = true, pos_categories = "abstract nouns", } labels["abstract verb"] = { display = "abstract", glossary = true, pos_categories = "abstract verbs", } labels["acronym"] = { glossary = true, pos_categories = "acronyms", } labels["active voice"] = { aliases = {"active", "in active", "in the active", "in active voice", "in the active voice"}, glossary = true, } labels["ambitransitive"] = { aliases = {"ambitransitively"}, glossary = true, pos_categories = {"transitive verbs", "intransitive verbs"}, } labels["angry register"] = { aliases = {"angry", "anger", "said in anger"}, glossary = true, pos_categories = "angry register terms", } labels["animate"] = { glossary = true, } labels["indicative"] = { aliases = {"in the indicative", "indicative mood"}, glossary = "indicative mood", } labels["subjunctive"] = { aliases = {"in the subjunctive", "subjunctive mood"}, glossary = "subjunctive mood", } labels["imperative"] = { aliases = {"in the imperative", "imperative mood"}, glossary = "imperative mood", } labels["jussive"] = { aliases = {"in the jussive", "jussive mood"}, glossary = "jussive mood", } labels["atelic"] = { glossary = true, } labels["attenuative"] = { pos_categories = "attenuative verbs", } labels["attributive"] = { glossary = true, } labels["attributively"] = { glossary = "attributive", } labels["auxiliary"] = { glossary = true, pos_categories = "auxiliary verbs", } labels["cardinal"] = { aliases = {"cardinal number", "cardinal numeral"}, display = "[[cardinal number]]", pos_categories = "cardinal numbers", } labels["catenative"] = { glossary = "catenative verb", } labels["causative"] = { glossary = true, } labels["causative verb"] = { display = "causative", glossary = true, pos_categories = "causative verbs", } labels["cognate object"] = { aliases = {"with cognate object"}, display = "with [[w:Cognate object|cognate object]]", pos_categories = "verbs used with cognate objects", } labels["collective"] = { glossary = true, display = "collective", pos_categories = "collective nouns", } labels["collectively"] = { glossary = "collective", display = "collectively", pos_categories = "collective nouns", } labels["collective number"] = { aliases = {"collective numeral"}, display = "[[collective number]]", pos_categories = "collective numbers", } labels["common"] = { glossary = true, } labels["comparable"] = { glossary = true, } labels["completive"] = { pos_categories = "completive verbs", } labels["concrete verb"] = { display = "concrete", Wiktionary = true, pos_categories = "concrete verbs", } labels["contraction"] = { aliases = {"contractions", "contracted"}, glossary = true, pos_categories = "contractions", } labels["control verb"] = { aliases = {"control"}, Wikipedia = true, pos_categories = "control verbs", } labels["copulative"] = { aliases = {"copular"}, glossary = true, pos_categories = "copulative verbs", } labels["countable"] = { glossary = true, pos_categories = "countable nouns", } labels["cumulative"] = { pos_categories = "cumulative verbs", } labels["defective adjective"] = { aliases = {"defective a", "defective adj", "defective adjectives"}, display = "defective", glossary = true, pos_categories = "defective adjectives", } labels["defective noun"] = { aliases = {"defective n", "defective nouns"}, display = "defective", glossary = true, pos_categories = "defective nouns", } labels["defective verb"] = { aliases = {"defective v", "defective vb", "defective verbs"}, display = "defective", glossary = true, pos_categories = "defective verbs", } labels["definite"] = { aliases = {"def"}, glossary = true, } labels["deliberate misspelling"] = { deprecated_aliases = {"deliberate mispelling"}, display = "deliberate [[misspelling]]", } labels["delimitative"] = { pos_categories = "delimitative verbs", } labels["deponent"] = { glossary = true, pos_categories = "deponent verbs", } labels["distributive"] = { pos_categories = "distributive verbs", } labels["distributive number"] = { aliases = {"distributive numeral"}, display = "[[distributive number]]", pos_categories = "distributive numbers", } labels["ditransitive"] = { aliases = {"ditransitively"}, glossary = true, pos_categories = "ditransitive verbs", } labels["dual only"] = { aliases = {"dual-only", "dualonly", "duale tantum"}, display = "[[dual]] only", pos_categories = "dualia tantum", } labels["dysphemistic"] = { aliases = {"dysphemism"}, glossary = "dysphemism", pos_categories = "dysphemisms", } labels["by ellipsis"] = { aliases = {"ellipsis"}, glossary = "ellipsis", pos_categories = "ellipses", } labels["elongated"] = { aliases = {"elongation"}, glossary = true, pos_categories = "elongated forms", } labels["emphatic"] = { glossary = true, } labels["ergative"] = { glossary = true, pos_categories = "ergative verbs", } labels["expressive"] = { glossary = true, pos_categories = "expressive terms", } labels["by extension"] = { aliases = {"hence"}, } labels["feminine"] = { glossary = true, } labels["focus"] = { glossary = true, pos_categories = "focus adverbs", } labels["fractional"] = { aliases = {"fractional number", "fractional numeral"}, display = "[[fractional number]]", pos_categories = "fractional numbers", } labels["frequentative"] = { glossary = true, pos_categories = "frequentative verbs", } labels["hedge"] = { aliases = {"hedges"}, glossary = true, pos_categories = "hedges", } labels["ideophonic"] = { aliases = {"ideophone"}, glossary = true, } labels["idiomatic"] = { aliases = {"idiom", "idiomatically"}, glossary = true, pos_categories = "idioms", } labels["imperfect"] = { glossary = true, } labels["imperfective"] = { glossary = true, pos_categories = "imperfective verbs", } labels["impersonal"] = { glossary = "impersonal verb", pos_categories = "impersonal verbs", } labels["in the singular"] = { aliases = {"in singular"}, display = "in the [[singular]]", } labels["in the dual"] = { aliases = {"in dual"}, display = "in the [[dual]]", } labels["in the plural"] = { aliases = {"in plural"}, display = "in the [[Appendix:Glossary#plural|plural]]", } labels["inanimate"] = { aliases = {"not animate"}, glossary = true, } labels["inchoative"] = { glossary = true, pos_categories = "inchoative verbs", } labels["indefinite"] = { aliases = {"indef", "not definite"}, glossary = true, } labels["initialism"] = { glossary = true, pos_categories = "initialisms", } labels["intensive verb"] = { display = "intensive", pos_categories = "intensive verbs", } labels["intransitive"] = { aliases = {"not transitive", "intransitively", "not transitively"}, glossary = true, pos_categories = "intransitive verbs", } labels["IPA"] = { aliases = {"International Phonetic Alphabet"}, Wikipedia = "International Phonetic Alphabet", plain_categories = "IPA symbols", } labels["iterative"] = { glossary = true, pos_categories = "iterative verbs", } labels["litotes"] = { aliases = {"litote", "litotic", "litotical"}, glossary = true, pos_categories = true, } labels["masculine"] = { glossary = true, } labels["mediopassive voice"] = { aliases = {"mediopassive", "middle passive", "middle-passive", "in mediopassive", "in middle passive", "in middle-passive", "in the mediopassive", "in the middle passive", "in the middle-passive", "in mediopassive voice", "in middle passive voice", "in middle-passive voice", "in the mediopassive voice", "in the middle passive voice", "in the middle-passive voice"}, glossary = true, } labels["meiosis"] = { aliases = {"meioses", "meiotic"}, glossary = true, pos_categories = "meioses", } labels["middle voice"] = { aliases = {"middle", "in middle", "in the middle", "in middle voice", "in the middle voice"}, glossary = true, } labels["misspelling"] = { deprecated_aliases = {"mispelling"}, display = "[[misspelling]]", } labels["mnemonic"] = { display = "[[mnemonic]]", pos_categories = "mnemonics", } labels["modal"] = { Wikipedia = "Modality (linguistics)", } labels["modal adverb"] = { aliases = {"modal adverbs"}, display = "modal", Wikipedia = "Modality (linguistics)", pos_categories = "modal adverbs", } labels["modal verb"] = { aliases = {"modal verbs"}, display = "modal", Wikipedia = "Modality (linguistics)", pos_categories = "modal verbs", } labels["NAPA"] = { aliases = {"Americanist_phonetic_notation"}, Wikipedia = "Americanist_phonetic_notation", plain_categories = "NAPA symbols", } labels["always in the negative"] = { display = "always in the [[Appendix:Glossary#negative polarity item|negative]]", pos_categories = "negative polarity items", } labels["chiefly in the negative"] = { aliases = {"chiefly used in the negative", "negative polarity", "negative polarity item", "usually in the negative", "usually used in the negative"}, display = "chiefly in the [[Appendix:Glossary#negative polarity item|negative]]", pos_categories = "negative polarity items", } labels["chiefly in the negative plural"] = { aliases = {"chiefly used in the negative plural", "negative polarity plural", "negative polarity plural item", "usually in the negative plural", "usually used in the negative plural"}, display = "chiefly in the [[Appendix:Glossary#negative polarity item|negative]] [[Appendix:Glossary#plural|plural]]", pos_categories = "negative polarity items", } labels["always in the positive"] = { display = "always in the [[Appendix:Glossary#positive polarity item|positive]]", -- pos_categories = {"positive polarity items"}, } labels["chiefly in the positive"] = { aliases = {"chiefly used in the positive", "positive polarity", "positive polarity item", "usually in the positive", "usually used in the positive"}, display = "chiefly in the [[Appendix:Glossary#positive polarity item|positive]]", -- pos_categories = {"positive polarity items"}, } labels["chiefly in the positive plural"] = { aliases = {"chiefly used in the positive plural", "positive polarity plural", "positive polarity plural item", "usually in the positive plural", "usually used in the positive plural"}, display = "chiefly in the [[Appendix:Glossary#positive polarity item|positive]] [[Appendix:Glossary#plural|plural]]", -- pos_categories = "positive polarity items", } labels["neuter"] = { glossary = true, } -- British English ("ise") labels["nominalised"] = { aliases = {"nominalisation", "substantivised", "substantivisation"}, glossary = "nominalization", pos_categories = "nominalized adjectives", } -- American English ("ize") labels["nominalized"] = { aliases = {"nominalization", "substantivized", "substantivization"}, glossary = "nominalization", pos_categories = "nominalized adjectives", } labels["not comparable"] = { aliases = {"notcomp", "incomparable", "uncomparable"}, glossary = "uncomparable", } labels["onomatopoeia"] = { glossary = true, pos_categories = "onomatopoeias", } labels["ordinal"] = { aliases = {"ordinal number", "ordinal numeral"}, display = "[[ordinal number]]", pos_categories = "ordinal numbers", } labels["partitive verb"] = { display = "[[Appendix:Glossary#transitive|transitive]], usually [[Appendix:Finnic telic and atelic verbs|atelic]]", pos_categories = "transitive verbs", -- = "partitive verbs", } labels["perfect"] = { glossary = true, } labels["participle"] = { glossary = true, } labels["passive voice"] = { aliases = {"passive", "in passive", "in the passive", "in passive voice", "in the passive voice"}, glossary = true, } labels["perfect"] = { glossary = true, } labels["perfective"] = { glossary = true, pos_categories = "perfective verbs", } labels["plural only"] = { aliases = {"plural-only", "pluralonly", "plurale tantum"}, display = "[[Appendix:Glossary#plural|plural]] only", pos_categories = "pluralia tantum", } labels["possessional adjective"] = { aliases = {"possessional", "possessional adjectives"}, display = "possessional", glossary = true, pos_categories = "possessional adjectives", } labels["possessive pronoun"] = { aliases = {"possessive determiner"}, display = "possessive", glossary = "possessive determiner", pos_categories = "possessive pronouns", } labels["postpositive"] = { glossary = true, } labels["predicative"] = { glossary = true, } labels["predicatively"] = { glossary = "predicative", } labels["prepositive"] = { glossary = true, } labels["prescriptive"] = { aliases = {"normative", "prescribed"}, glossary = true, } labels["privative"] = { pos_categories = "privative verbs", } labels["procedure word"] = { display = "[[procedure word]]", } labels["productive"] = { glossary = true, } -- TODO: This label is probably inappropriate for many languages labels["pronominal"] = { glossary = "pronominal verb", } labels["pronunciation spelling"] = { glossary = true, } labels["pro-verb"] = { Wikipedia = true, } labels["reciprocal"] = { glossary = true, pos_categories = "reciprocal verbs", } labels["reflexive"] = { glossary = true, pos_categories = "reflexive verbs", } labels["reflexive pronoun"] = { glossary = "reflexive", pos_categories = "reflexive pronouns", } labels["relational"] = { glossary = true, pos_categories = "relational adjectives", } labels["repetitive"] = { pos_categories = "repetitive verbs", } labels["respelling"] = { glossary = true, } labels["reversative"] = { pos_categories = "reversative verbs", } labels["rhetorical question"] = { glossary = true, pos_categories = "rhetorical questions", } labels["rhotic"] = { glossary = true, } labels["saturative"] = { aliases = {"sative"}, pos_categories = "saturative verbs", } labels["semelfactive"] = { glossary = true, pos_categories = "semelfactive verbs", } labels["sentence adverb"] = { display = "[[sentence adverb]]", pos_categories = "sentence adverbs", } labels["set phrase"] = { display = "[[set phrase]]", } labels["simile"] = { glossary = true, pos_categories = "similes", } labels["singular only"] = { aliases = {"singular-only", "singulare tantum", "no plural"}, display = "singular only", pos_categories = "singularia tantum", } labels["snowclone"] = { glossary = true, pos_categories = "snowclones", } labels["stative"] = { aliases = {"stative verb"}, glossary = true, pos_categories = "stative verbs", } labels["strictly"] = { aliases = {"strict", "narrowly", "narrow"}, glossary = true, } labels["substantive"] = { glossary = true, track = true, } labels["terminative"] = { pos_categories = "terminative verbs", } labels["transitive"] = { aliases = {"transitively"}, glossary = true, pos_categories = "transitive verbs", } labels["transmission error"] = { Wiktionary = true, } labels["tritransitive"] = { aliases = {"tritransitively"}, glossary = true, pos_categories = "tritransitive verbs", } labels["unaccusative"] = { aliases = {"not accusative"}, Wikipedia = "Unaccusative verb", } labels["uncountable"] = { aliases = {"not countable"}, glossary = true, pos_categories = "uncountable nouns", } labels["unergative"] = { aliases = {"not ergative"}, Wikipedia = "Unergative verb", } labels["UPA"] = { aliases = {"Uralic Phonetic Alphabet"}, Wikipedia = "Uralic Phonetic Alphabet", plain_categories = "UPA symbols", } labels["usually plural"] = { aliases = {"usually in the plural", "usually in plural"}, display = "usually in the [[Appendix:Glossary#plural|plural]]", deprecated = true, } -- Usage labels labels["4chan"] = { aliases = {"4chan slang"}, display = "[[w:4chan|4chan]] {{glossary|slang}}", pos_categories = "4chan slang", } labels["4chan lgbt"] = { aliases = {"tttt"}, display = "[[w:4chan|4chan]] /lgbt/ {{glossary|slang}}", pos_categories = "4chan /lgbt/ slang", } labels["ACG"] = { display = "[[ACG]]", -- see also "fandom slang" pos_categories = "fandom slang", } labels["endearing"] = { aliases = {"affectionate"}, display = "[[endearing]]", -- should be "terms with X senses", leaving "X terms" to the term-context temp pos_categories = "endearing terms", } labels["endearing form"] = { aliases = {"affectionate form"}, display = "[[endearing]]", pos_categories = "endearing forms", } labels["pre-classical"] = { aliases = {"Pre-classical", "pre-Classical", "Pre-Classical", "Preclassical", "preclassical", "ante-classical", "Ante-classical", "ante-Classical", "Ante-Classical", "Anteclassical", "anteclassical"}, display = "pre-Classical", regional_categories = true, } labels["anti-LGBTQ slur"] = { -- don't add aliases "homophobia" or "transphobia" because these could be topical categories aliases = {"homophobic", "transphobic"}, display = "anti-[[LGBTQ]] [[slur]]", pos_categories = "anti-LGBTQ slurs", } labels["archaic"] = { aliases = {"antiquated"}, glossary = true, sense_categories = true, } labels["archaic form"] = { glossary = "archaic", display = "archaic", pos_categories = "archaic forms", } labels["Australian slang"] = { display = "[[Australian]] {{glossary|slang}}", regional_categories = "Australian", plain_categories = true, } labels["avoidance"] = { glossary = true, } labels["back slang"] = { aliases = {"backslang", "back-slang"}, glossary = "backslang", pos_categories = true, } labels["Bargoens"] = { Wikipedia = true, plain_categories = true, } labels["Braille"] = { Wikipedia = true, } labels["British slang"] = { aliases = {"UK slang"}, display = "[[British]] {{glossary|slang}}", plain_categories = true, } labels["Cambridge University slang"] = { aliases = {"University of Cambridge slang", "Cantab slang"}, display = "[[w:University of Cambridge|Cambridge University]] {{glossary|slang}}", topical_categories = "Universities", plain_categories = true, } labels["cant"] = { aliases = {"argot", "cryptolect"}, display = "[[cant]]", pos_categories = true, } labels["capitalized"] = { aliases = {"capitalised"}, display = "[[capitalisation|capitalized]]", } labels["Castilianism"] = { aliases = {"Hispanicism"}, display = "[[Castilianism]]", } labels["childish"] = { aliases = {"baby talk", "child language", "infantile", "puerile"}, display = "[[childish]]", -- should be "terms with X senses", leaving "X terms" to the term-context temp? pos_categories = "childish terms", } labels["chu Nom"] = { display = "[[Vietnamese]] [[chữ Nôm]]", plain_categories = "Vietnamese Han tu", } labels["Cockney rhyming slang"] = { display = "[[Cockney rhyming slang]]", plain_categories = true, } labels["colloquial"] = { aliases = {"colloquially"}, glossary = true, pos_categories = "colloquialisms", } -- FIXME! The following two are apparently for Persian but probably don't belong in this file. labels["colloquial-um"] = { glossary = "colloquial", pos_categories = "colloquialisms containing sequence um", } labels["colloquial-un"] = { glossary = "colloquial", pos_categories = "colloquialisms containing sequence un", } labels["corporate jargon"] = { aliases = {"business jargon", "corporatese", "businessese", "corporate speak", "business speak"}, display = "[[corporate]] [[jargon]]", pos_categories = true, } labels["costermongers"] = { aliases = {"coster", "costers", "costermonger", "costermongers back slang", "costermongers' back slang"}, display = "[[Appendix:Costermongers' back slang|costermongers]]", plain_categories = "Costermongers' back slang", } labels["criminal slang"] = { aliases = {"thieves' cant", "Thieves' Cant", "thieves cant", "thieves'", "thieves", "thieves' cant"}, -- Thieves' Cant is English-only, so defined in the English submodule; if other languages try to use it, it's just criminal slang display = "[[criminal]] {{glossary|slang}}", topical_categories = "Crime", pos_categories = true, } labels["dated"] = { aliases = {"old-fashioned"}, glossary = true, -- should be "terms with X senses", leaving "X terms" to the term-context temp pos_categories = "dated terms", } labels["dated form"] = { aliases = {"old-fashioned form"}, glossary = "dated", display = "dated", pos_categories = "dated forms", } -- combine with previous? labels["dated sense"] = { glossary = "dated", sense_categories = "dated", } labels["derogatory"] = { aliases = {"pejorative", "derogative", "disparaging"}, display = "[[derogatory]]", -- should be "terms with X senses", leaving "X terms" to the term-context temp pos_categories = "derogatory terms", } labels["derogatory form"] = { aliases = {"pejorative form", "derogative form", "disparaging form"}, display = "[[derogatory]]", pos_categories = "derogatory forms", } labels["dialect"] = {-- separated from "dialectal" so e.g. "obsolete|outside|the|_|dialect|of..." displays right glossary = "dialectal", pos_categories = "dialectal terms", } labels["dialectal"] = { glossary = true, -- should be "terms with X senses", leaving "X terms" to the term-context temp pos_categories = "dialectal terms", } labels["dialectal form"] = { glossary = "dialectal", display = "dialectal", pos_categories = "dialectal forms", } labels["dialects"] = {-- separated from "dialectal" so e.g. "obsolete|outside|dialects" displays right glossary = "dialectal", pos_categories = "dialectal terms", } labels["dis legomenon"] = { display = "[[dis legomenon]]", pos_categories = "dis legomena", } labels["dismissal"] = { display = "[[dismissal]]", pos_categories = "dismissals", } labels["drag slang"] = { aliases = {"Drag Race slang"}, display = "[[drag]] {{glossary|slang}}", pos_categories = "drag slang", } labels["ecclesiastical"] = { display = "[[ecclesiastical#Adjective|ecclesiastical]]", pos_categories = "ecclesiastical terms", } labels["ethnic slur"] = { aliases = {"racial slur"}, display = "[[ethnic]] [[slur]]", pos_categories = "ethnic slurs", } labels["euphemistic"] = { aliases = {"euphemism"}, glossary = "euphemism", pos_categories = "euphemisms", } labels["eye dialect"] = { display = "[[eye dialect]]", pos_categories = true, } labels["familiar"] = { glossary = true, -- should be "terms with X senses", leaving "X terms" to the term-context temp? pos_categories = "familiar terms", } labels["fandom slang"] = { aliases = {"fandom"}, display = "[[fandom]] {{glossary|slang}}", pos_categories = true, } labels["figurative"] = { aliases = {"metaphorical", "metaphoric", "metaphor"}, glossary = "figurative", } labels["figuratively"] = { aliases = {"metaphorically"}, glossary = "figurative", } labels["folk songs"] = { aliases = {"folksongs", "used in folk songs", "used in folksongs"}, pos_categories = "folk-poetic terms", display = "used in [[folk song]]s", } labels["folk tales"] = { aliases = {"folktales", "used in folk tales", "used in folktales"}, pos_categories = "folk-poetic terms", display = "used in [[folk tale]]s", } labels["folk-poetic"] = { -- should be "terms with X senses", leaving "X terms" to the term-context temp aliases = {"folk poetic"}, Wikipedia = "Folk poetry", pos_categories = "folk-poetic terms", } labels["formal"] = { glossary = true, -- should be "terms with X senses", leaving "X terms" to the term-context temp? pos_categories = "formal terms", } labels["formal form"] = { glossary = "formal", display = "formal", pos_categories = "formal forms", } labels["gay slang"] = { display = "[[gay]] {{glossary|slang}}", pos_categories = true, } labels["gender critical slang"] = { aliases = {"gender-critical slang", "GC slang", "TERF slang"}, display = "[[gender-critical]] {{glossary|slang}}", pos_categories = "gender-critical slang", } labels["gender-neutral"] = { glossary = "gender-neutral", pos_categories = "gender-neutral terms", } labels["graffiti slang"] = { display = "[[graffiti#Noun|graffiti]] {{glossary|slang}}", pos_categories = true, } labels["genericized trademark"] = { aliases = {"genericised trademark", "generic trademark", "proprietary eponym", "gentrade"}, display = "[[genericized trademark]]", pos_categories = "genericized trademarks", } labels["ghost word"] = { aliases = {"ghost"}, display = "ghost word", glossary = true, pos_categories = "ghost words", } labels["hapax legomenon"] = { aliases = {"hapax"}, display = "hapax legomenon", glossary = true, pos_categories = "hapax legomena", } labels["higher register"] = { aliases = {"high register", "elevated register", "elevated"}, glossary = "higher register", pos_categories = "higher register terms", } labels["historical"] = { aliases = {"historic"}, glossary = true, sense_categories = true, } labels["non-native speakers"] = {-- language-agnostic version aliases = {"NNS"}, display = "[[non-native speaker]]s", -- so preceded by "used by", "error by children and", etc? or reword? regional_categories = {"Non-native speakers'"}, } -- used exclusively by languages that use the "Jpan" script code labels["historical hiragana"] = { pos_categories = true, } -- used exclusively by languages that use the "Jpan" script code labels["historical katakana"] = { pos_categories = true, } -- applies to Japanese and Korean, etc., please do not confuse with "polite" labels["honorific"] = { Wikipedia = "Honorifics (linguistics)", -- should be "terms with X senses", leaving "X terms" to the term-context temp? pos_categories = "honorific terms", } -- for Ancient Greek labels["Homeric epithet"] = { display = "[[Homeric Greek|Homeric]] [[w:Epithets in Homer|epithet]]", omit_postComma = true, plain_categories = "Epic Greek", } -- applies to Japanese and Korean, etc. labels["humble"] = { -- should be "terms with X senses", leaving "X terms" to the term-context temp? display = "[[humble]]", pos_categories = "humble terms", } -- for Akkadian labels["in hendiadys"] = { aliases = {"hendiadys"}, display = "in {{w|hendiadys}}", pos_categories = "terms used in hendiadys", } labels["humorous"] = { -- should be "terms with X senses", leaving "X terms" to the term-context temp; NB and cf a similar "jocular" label further up on this page aliases = {"humorously", "jocular"}, glossary = true, pos_categories = "humorous terms", } labels["hyperbolic"] = { aliases = {"hyperbole"}, glossary = true, pos_categories = "hyperboles", } labels["hypercorrect"] = { glossary = true, pos_categories = "hypercorrections", } labels["hyperforeign"] = { glossary = true, pos_categories = "hyperforeign terms", } labels["imperial"] = { aliases = {"emperor", "empress"}, pos_categories = "royal terms", } labels["incel slang"] = { display = "[[incel]] {{glossary|slang}}", pos_categories = true, } labels["informal"] = { aliases = {"informally", "not formal"}, glossary = true, -- should be "terms with X senses", leaving "X terms" to the term-context temp pos_categories = "informal terms", } labels["informal form"] = { glossary = "informal", display = "informal", pos_categories = "informal forms", } labels["Internet slang"] = { aliases = {"internet slang"}, display = "[[Internet]] {{glossary|slang}}", pos_categories = "internet slang", } labels["IRC"] = { display = "[[IRC]]", pos_categories = "internet slang", } labels["ironic"] = { display = "[[irony|ironic]]", } -- Not the same as "journalism", which maps to a topical category (e.g. [[:Category:en:Journalism]], instead of [[:Category:English journalistic terms]]). labels["journalistic"] = { aliases = {"journalese"}, display = "[[journalistic]]", pos_categories = "journalistic terms", } labels["leet"] = { aliases = {"leetspeak"}, display = "[[leetspeak]]", pos_categories = "leetspeak", } labels["LGBTQ slang"] = { aliases = {"LGBT slang"}, display = "[[LGBTQ]] {{glossary|slang}}", pos_categories = true, } labels["literal"] = { glossary = "literally", } labels["literally"] = { glossary = "literally", } labels["literary"] = { -- should be "terms with X senses", leaving "X terms" to the term-context temp aliases = {"bookish"}, glossary = true, pos_categories = "literary terms", } labels["literary form"] = { aliases = {"bookish form"}, glossary = "literary", display = "literary", pos_categories = "literary forms", } --see also "short scale" labels["long scale"] = { display = "[[w:Long and short scales|long scale]]" } labels["loosely"] = { aliases = {"loose", "broadly", "broad"}, glossary = true, } labels["Lubunyaca"] = { display = "[[Lubunyaca]]", pos_categories = true, } labels["medical slang"] = { display = "[[medical]] {{glossary|slang}}", pos_categories = true, } -- for Awetí, Karajá, etc., where men and women use different words labels["men's speech"] = { aliases = {"male speech"}, glossary = "men's speech", pos_categories = "men's speech terms", } labels["metonymic"] = { aliases = {"metonymically", "metonymy", "metonym"}, glossary = true, pos_categories = "metonyms", } labels["military slang"] = { display = "[[military]] {{glossary|slang}}", pos_categories = true, } labels["minced oath"] = { display = "[[minced oath]]", pos_categories = "minced oaths", } labels["multiplicative"] = { aliases = {"multiplicative number", "multiplicative numeral"}, display = "[[multiplicative number]]", pos_categories = "multiplicative numbers", } labels["multiplicity slang"] = { display = "{{l|en|multiplicity|id=multiple personalities}} {{glossary|slang}}", pos_categories = true, } labels["naval slang"] = { aliases = {"navy slang"}, display = "[[naval]] {{glossary|slang}}", pos_categories = true, } labels["neologism"] = { aliases = {"neologistic"}, glossary = true, pos_categories = "neologisms", } labels["neopronoun"] = { display = "[[neopronoun]]", -- pos_categories = {"neopronouns"}, } labels["no longer productive"] = { aliases = {"non-productive"}, display = "no longer [[Appendix:Glossary#productive|productive]]", } labels["nonce word"] = { -- should be "terms with X senses", leaving "X terms" to the term-context temp? aliases = {"nonce"}, glossary = true, pos_categories = "nonce terms", } labels["nonstandard"] = { aliases = {"non-standard", "substandard", "sub-standard"}, glossary = true, -- should be "terms with X senses", leaving "X terms" to the term-context temp pos_categories = "nonstandard terms", } labels["nonstandard form"] = { aliases = {"non-standard form", "substandard form", "sub-standard form"}, glossary = "nonstandard", display = "nonstandard", pos_categories = "nonstandard forms", } labels["numismatic slang"] = { display = "[[numismatic]] {{glossary|slang}}", pos_categories = true, } labels["obsolete"] = { glossary = true, sense_categories = true, } labels["obsolete form"] = { glossary = "obsolete", display = "obsolete", pos_categories = "obsolete forms", } labels["obsolete term"] = { glossary = "obsolete", -- combine with previous two, q.v. pos_categories = "obsolete terms", } labels["offensive"] = { glossary = true, -- should be "terms with X senses", leaving "X terms" to the term-context temp pos_categories = "offensive terms", } labels["officialese"] = { aliases = {"bureaucratic"}, display = "[[officialese]]", pos_categories = "officialese terms", } labels["Oxbridge slang"] = { display = "[[w:Oxbridge|Oxbridge]] {{glossary|slang}}", topical_categories = "Universities", plain_categories = {"Cambridge University slang", "Oxford University slang"}, } labels["Oxford University slang"] = { aliases = {"University of Oxford slang", "Oxon slang"}, display = "[[w:University of Oxford|Oxford University]] {{glossary|slang}}", topical_categories = "Universities", plain_categories = true, } labels["poetic"] = { aliases = {"poi"}, -- Only used in Ancient Greek as a holdover from [[Module:grc:Dialects]]. -- should be "terms with X senses", leaving "X terms" to the term-context temp glossary = true, pos_categories = "poetic terms", } labels["poetic form"] = { glossary = "poetic", display = "poetic", pos_categories = "poetic forms", } labels["polite"] = { glossary = true, pos_categories = "polite terms", } labels["post-classical"] = { aliases = {"Post-classical", "post-Classical", "Post-Classical", "Postclassical", "postclassical"}, display = "post-Classical", regional_categories = true, } labels["prison slang"] = { display = "[[prison]] {{glossary|slang}}", pos_categories = true, } labels["proscribed"] = { glossary = true, pos_categories = "proscribed terms", } labels["puristic"] = { aliases = {"purism"}, Wikipedia = "Linguistic purism", pos_categories = "puristic terms", } labels["radio slang"] = { display = "[[radio]] {{glossary|slang}}", pos_categories = true, } labels["Reddit slang"] = { display = "[[Reddit]] {{glossary|slang}}", pos_categories = true, } labels["rare"] = { aliases = {"rare sense"}, glossary = true, sense_categories = true, } labels["rare form"] = { glossary = "rare", display = "rare", pos_categories = "rare forms", } labels["rare term"] = { display = "rare", -- see comments about "obsolete" pos_categories = "rare terms", } -- cf Cockney rhyming slang labels["rhyming slang"] = { display = "[[rhyming slang]]", pos_categories = true, } labels["religious slur"] = { aliases = {"sectarian slur"}, display = "[[religious]] [[slur]]", pos_categories = "religious slurs", } labels["retronym"] = { glossary = true, pos_categories = "retronyms", } labels["reverential"] = { -- should be "terms with X senses", leaving "X terms" to the term-context temp? display = "[[reverential]]", pos_categories = "reverential terms", } labels["royal"] = { aliases = {"regal"}, pos_categories = "royal terms", } labels["rustic"] = { glossary = true, -- should be "terms with X senses", leaving "X terms" to the term-context temp? aliases = {"rural"}, pos_categories = "rustic terms", } labels["sarcastic"] = { display = "[[sarcastic]]", pos_categories = "sarcastic terms", } labels["school slang"] = { aliases = {"public school slang"}, display = "[[school]] {{glossary|slang}}", pos_categories = true, } labels["self-deprecatory"] = { aliases = {"self-deprecating"}, display = "[[self-deprecatory]]", -- should be "terms with X senses", leaving "X terms" to the term-context temp? pos_categories = "self-deprecatory terms", } -- Swahili Sheng cant / argot -- should this be in a language-specific module? labels["Sheng"] = { Wikipedia = "Sheng slang", plain_categories = true, } labels["siglum"] = { aliases = {"sigla"}, glossary = true, pos_categories = "sigla", } --see also "long scale" labels["short scale"] = { display = "[[w:Long and short scales|short scale]]" } labels["slang"] = { glossary = true, pos_categories = true, } labels["solemn"] = { glossary = true, pos_categories = "solemn terms", } labels["Stenoscript"] = { aliases = {"stenoscript"}, display = "[[Stenoscript]]", pos_categories = "Stenoscript abbreviations", } labels["superseded"] = { glossary = true } labels["swear word"] = { aliases = {"profanity", "expletive"}, pos_categories = "swear words", } labels["syncopated"] = { aliases = {"syncope", "syncopic", "syncopation"}, glossary = true, pos_categories = "syncopic forms", } labels["synecdochic"] = { aliases = {"synecdochically", "synecdochical", "synecdoche"}, glossary = true, pos_categories = "synecdoches", } labels["technical"] = { display = "[[technical]]", pos_categories = "technical terms", } labels["telic"] = { glossary = true, } labels["text messaging"] = { aliases = {"texting"}, display = "[[text messaging]]", pos_categories = "text messaging slang", } labels["tone indicator"] = { display = "[[tone indicator]]", pos_categories = "tone indicators", } labels["trademark"] = { display = "[[trademark]]", pos_categories = "trademarks", } labels["transferred sense"] = { glossary = true, pos_categories = "terms with transferred senses", } labels["transferred senses"] = { display = "[[transferred sense#English|transferred senses]]", pos_categories = "terms with transferred senses", } labels["transgender slang"] = { aliases = {"trans slang"}, display = "[[transgender]] {{glossary|slang}}", pos_categories = true, } labels["Twitch-speak"] = { display = "[[Twitch-speak]]", pos_categories = true, } labels["uds."] = { display = "[[Appendix:Spanish pronouns#Ustedes and vosotros|used formally in Spain]]", } labels["uncommon"] = { glossary = true, sense_categories = true, } labels["uncommon form"] = { glossary = "uncommon", display = "uncommon", pos_categories = "uncommon forms", } labels["university slang"] = { aliases = {"college slang", "student slang"}, display = "[[university]] {{glossary|slang}}", topical_categories = "Universities", pos_categories = "student slang", } labels["verlan"] = { glossary = true, plain_categories = true, } labels["very rare"] = { display = "very [[Appendix:Glossary#rare|rare]]", sense_categories = "rare", } labels["vulgar"] = { aliases = {"coarse", "obscene", "profane"}, glossary = true, pos_categories = "vulgarities", } labels["vesre"] = { Wikipedia = true, plain_categories = true, } labels["youth slang"] = { display = "[[youth]] {{glossary|slang}}", pos_categories = "slang", } labels["2channel slang"] = { aliases = {"2channel", "2ch slang"}, display ="[[w:2channel|2channel]] {{glossary|slang}}", pos_categories = {"internet slang" , "2channel slang"}, } -- for Awetí, Karajá, etc., where men & women use different words labels["women's speech"] = { aliases = {"female speech"}, glossary = "women's speech", pos_categories = "women's speech terms", } -- terms applying to Old Norse skaldic poetry labels["kenning"] = { aliases = {"Kenning"}, Wikipedia = "Kenning", pos_categories = "kennings", } labels["heiti"] = { aliases = {"Heiti"}, Wikipedia = "Heiti", pos_categories = true, } return require("Module:labels").finalize_data(labels) n8m44ayqmg3vl4yjuowxa0q1qq4mflm Module:inflection utilities 828 8097 54972 2026-09-30T17:39:33Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local m_links = require("Module:links") local m_str_utils = require("Module:string utilities") local m_table = require("Module:table") local put = require("Module:parse utilities") local headword_data_module = "Module:headword/data" local script_utilities_module = "Module:script utilities" local pages_module = "Module:pages" local template_parser_module = "Module:template parser" local is_callable = require("Module:fun").is_callable local spli...' 54972 Scribunto text/plain local export = {} local m_links = require("Module:links") local m_str_utils = require("Module:string utilities") local m_table = require("Module:table") local put = require("Module:parse utilities") local headword_data_module = "Module:headword/data" local script_utilities_module = "Module:script utilities" local pages_module = "Module:pages" local template_parser_module = "Module:template parser" local is_callable = require("Module:fun").is_callable local split = m_str_utils.split local ufind = m_str_utils.find local usub = m_str_utils.sub local ucfirst = m_str_utils.ucfirst local unpack = unpack or table.unpack -- Lua 5.2 compatibility local dump = mw.dumpObject local new_title = mw.title.new local insert = table.insert local concat = table.concat local insert_if_not = m_table.insertIfNot local shallow_copy = m_table.shallowCopy local function track(page) require("Module:debug/track")("inflection utilities/" .. page) return true end local footnote_abbrevs = { ["a"] = "archaic", ["c"] = "colloquial", ["d"] = "dialectal", ["fp"] = "folk-poetic", ["l"] = "literary", ["lc"] = "low colloquial", ["p"] = "poetic", ["pej"] = "pejorative", ["r"] = "rare", } --[==[ intro: The following code is used in building up the inflection of terms in inflected languages, where a term can potentially consist of several inflected words, each surrounded by fixed text, and a given slot (e.g. accusative singular) of a given word can potentially consist of multiple possible inflected forms. In addition, each form may be associated with a manual transliteration and/or a list of footnotes (or qualifiers, in the case of headword lines). The following terminology is helpful to understand: * A '''term''' is a word or multiword expression that can be inflected. A multiword term may in turn consist of several single-word inflected terms with surrounding fixed text. A term belongs to a particular '''part of speech''' (e.g. noun, verb, adjective, etc.). * An '''inflection dimension''' is a particular dimension over which a term may be inflected, such as case, number, gender, person, tense, mood, voice, aspect, etc. * The '''lemma''' is the particular form of a term under which the term is entered into a dictionary. For example, for verbs, it is most commonly the infinitive, but this differs for some languages: e.g. Latin, Greek and Bulgarian use the first-person singular present indicative (active voice in the case of Latin and Greek); Sanskrit and Macedonian use the third-person singular present indicative (active voice in the case of Sanskrit); Hebrew and Arabic use the third-person singular masculine past (aka "perfect"); etc. For nouns, the lemma form is most commonly the nominative singular, but e.g. for Old French it is the objective singular and for Sanskrit it is the root. * A '''slot''' is a particular combination of inflection dimensions. An example might be "accusative plural" for a noun, or "first-person singular present indicative" for a verb. Slots are named in a language-specific fashion. For example, the slot "accusative plural" might have a name `accpl`, while "first-person singular present indicative" might be variously named `pres1s`, `pres_ind_1_sg`, etc. Each slot is filled with zero or more '''forms'''. * A '''form''' is a particular inflection of a slot for a particular term. Note that a given slot may (and often does) have more than one associated form; these different forms are termed '''variants'''. An example is {{m+|de|Bug||bow (of a ship)}}, which has two genitive singular forms ''Buges'' and ''Bugs''; two plural forms in all cases, e.g. nominative plural ''Buge'' and ''Büge''; and two dative singular forms ''Bug'' and rare/archaic ''Buge''. The form variants for a given slot are ordered, and generally should have the more common and/or preferred variants first, along with rare, archaic or obsolete variants last (if they are included at all). * Forms are described using '''form objects''', which are Lua objects taking the form `{form="``form_value``", translit="``manual_translit``", footnotes={"``footnote``", "``footnote``", ...}}`. (Additional '''metadata''' may be present in a form object, although the support for preserving such metadata when transformations are applied to form objects isn't yet complete.) ``form_value`` is a '''form value''' specifying the value of the form itself in the term's script. ``manual_translit`` specifies optional manual transliteration for the form, in case (a) the form value is in a different script; and (b) either the form's automatic transliteration is incorrect and needs to be overridden, or the language of the term has no automatic transliteration (e.g. in the case of Persian and Hebrew). ``footnote`` is a footnote to be attached to the form in question, and should be e.g. {"[archaic]"} or {"[only in the meaning 'to succeed (an officeholder)']"}, i.e. the string must be surrounded by brackets and should begin with a lowercase letter and not end in a period/full stop. When such footnotes are converted to actual footnotes in a table of inflected forms, the brackets will be removed, the first letter will be capitalized and a period/full stop will be added to the end. (However, when such footnotes are used as qualifiers in headword lines, only the brackets will be removed, with no capitalization or final period.) Note that only ``form_value`` is mandatory. * A list of zero or more form objects is termed a '''form object list''', or usually just a '''form list'''. Such lists are ordered and go into form tables (see below). * A '''form table''' is a Lua table (i.e. a dictionary) describing all the possible inflections of a given term. The keys in such a table are slots (strings) and the values are form lists. '''NOTE:''' All inflection code assumes and maintains the invariant that no two slots, and no two forms in a single slot, share the same form object (by reference, i.e. the Lua object describing a form object should never be shared in two places). This allows for safely side-effecting form objects in certain sorts of operations. This same invariant necessarily applies to the Lua list objects containing the form objects, but does '''NOT''' apply to metadata inside of form objects. In particular, a list of footnotes may well be shared among different form objects. This means it is '''NOT''' safe to side-effect such lists, and in fact no code in this module that manipulates footnote lists will ever side-effect such lists; they are treated as immutable. * Some functions, to save memory, accept and work with abbreviated forms of form objects and/or form lists. Specifically, an '''abbreviated form object''' is either a form object or a string, the latter corresponding to a form object whose form value is the string and all other properties are nil. Similarly, an '''abbreviated form list''' is either a single abbreviated form object or a list of such objects, i.e. any of a string, form object or list of strings and/or form objects. Functions that do not accept such abbreviated structures may be said to insist on being passed form objects in '''full form''', or form lists in '''full list form'''. * Each slot is associated with an '''accelerator tag set''', which is a list of inflection tags that are used when generating an accelerator entry for the forms in the slot (see [[WT:ACCEL]]). For example, the first singular present indicative of a verb might have slot name `pres_1sg` and corresponding accelerator tag set `1|s|pres|ind`. As shown, the accelerator tag set is a string consisting of inflection tags (as used in {{tl|inflection of}}) separated by `|`. Despite the terminology ''tag set'', the tags in a tag set are ordered, although the same tag should never occur twice. * Some inflected terms are '''multiword''', i.e. they consist of multiple '''words''', where each word is generally separated by spaces or sometimes hyphens. In such a term, some of the words inflect, while others remain fixed. Words that inflect are termed '''inflecting words''' (or more correctly '''inflecting parts''', since in some circumstances, parts of a word can inflect). The '''fixed text''' is all the parts of a multiword term that do not inflect. * The descriptor that describes how a given term inflects is called an '''inflection spec''', and consists of the lemma form of the term itself, annotated with an '''angle bracket spec''' after each inflecting word. As the name implies, an angle bracket spec is surrounded by angle brackets (`<...>`). A simple example is {{m+|de|Feder||feather}}, whose inflection spec looks like `Feder<f>`, where `f` specifies the feminine gender. In this case, although there are several properties that could be specified between angle brackets, all except the gender are optional and have been left out, indicating that defaults should be used. Another example is {{m+|de|Baske|Basque person}}, whose inflection spec looks like `Baske<m.weak>`, where `m` specifies the masculine gender and `weak` specifies the weak inflection. Note that individual components of an angle bracket spec like `m` and `weak` are termed '''indicators''' and are separated by periods/full stops. A slightly more complex example is {{m+|de|Zeitgeist||zeitgeist}}, whose inflection spec looks like `Zeitgeist<m,es:s,er>` and which specifies three things in a single '''compound indicator''': `m` (the masculine gender); `es:s` (the genitive singular, which can end in either ''-es'' or ''-s''); and `er` (the nomininative plural, which ends in ''-er''). * If there are several inflecting words in a term, each one will be followed by its own angle bracket spec. An example is {{m+|de|schwarzes Loch||black hole}}, whose inflection spec looks like `schwarzes<+> Loch<n,es:s,^er>`. Here, the adjective ''schwarzes'' (the nominative neuter singular of {{m|de|schwarz||black}}) is followed by the angle bracket spec `<+>` specifying that it inflects as an adjective, and the noun ''Loch'' has the angle bracket spec `<n,es:s,^er>`, indicating (similarly to the above example) that it is neuter, has a genitive singular in either ''-es'' or ''-s'', and has a nominative plural in ''-er'' with umlaut, hence ''Löcher'' (the `^` specifies that the form requires umlaut). * Sometimes a given term has multiple ways of inflecting that differ in ways that can't be specified using a single angle bracket spec. This is supported using '''alternants''', which are specified using double parentheses. (This is so that terms that themselves contain parentheses can be specified without interference.) An example is {{m+|uk|русин||Rusyn}}, which can be stressed either as ''ру́син'' (stress on the first syllable and following accent paradigm ''a'', hence genitive singular ''ру́сина'') or ''руси́н'' (stress on the second syllable and following accent paradigm ''b'', hence genitive singular ''русина́''; note how the stress moves onto the ending, in accordance with the accent paradigm). This is specified using `((ру́син<pr>,руси́н<b.pr>))`, i.e. each separate the alternants with a comma and surround them with double parentheses. (Here, `pr` means that the terms belong to the personal animacy class, and `b` specifies the accent paradigm; paradigm ''a'' is the default and hence is omitted.) * Note that occasionally, parts of a single space-delimited word can inflect separately. An example is {{m+|la|rōsmarīnus||rosemary}}, which is a compound of {{m+|la|rōs||dew}} and {{m|la|marīnus||marine, of the sea}}. In this compound, both parts of the compound can inflect separately; hence genitive singular ''rōrismarīnī'', accusative singular ''rōremmarīnum'', etc. Alternatively, only the second part inflects; hence genitive singular ''rōsmarīnī'', accusative singular ''rōsmarīnum'', etc. This is specified as `((rōs/rōr<3.M>marīnus<2>,rōsmarīnus<2>))`. Here, the term {{m|la|rōs}} by itself would have inflection spec `rōs/rōr<3.M>` (indicating that it is third declension masculine with a non-nominative-singular stem ''rōr-'') and the term {{m|la|marīnus}} would have inflection spec `<2>` (indicating that it is second declension; the masculine gender is inferred from the ''-us'' ending). When combined in a single inflection spec, the doubly-inflecting alternant is written `rōs/rōr<3.M>marīnus<2>`, with each inflecting part followed by its corresponding angle bracket spec, and the singly-inflecting alternant is written `rōsmarīnus<2>`. As this example shows, the two alternants need not correspond in how many inflecting parts there are. It should also be noted that fixed text can surround an alternant and it is even possible to supply multiple alternants in a single inflection spec (e.g. if the term has two words in it and each word requires an alternant to inflect). * The result of parsing a single angle bracket spec is stored into a '''word spec'''. The structure of a word spec is fairly arbitrary and is determined by the user-written `parse_indicator_spec` function, but always contains a form table under the `forms` key that is populated during inflection (see below). A parameter or local variable that holds a word spec is conventionally named `base` for historical reasons. Word specs are grouped together into a structure termed a '''multiword spec''', which describes one or more word specs along with the fixed text in between and around the inflected words. Multiword specs are in turn grouped into structures termed '''alternant specs''', indicating the distinct alternants and the words in each alternant. Finally, multiword specs and alternant specs are grouped into an '''alternant multiword spec''', which is the top-level object describing an inflection spec. Each of these different specs has a form table in it stored in the `forms` key that is populated during the inflection process and contains the form objects that specify the inflections of this part of the full multiword term. (It should be noted that the term '''spec''' is overloaded to mean two different things: the user-specified descriptor that specifies the lemma form of the term and associated inflection, and the associated internal Lua object that encapsulates all information derived from the descriptor, along with later-generated information on how to inflect the term(s) being described.) * Among these various "spec" structures, the two most important are the top-level alternant multiword spec and the bottom-level word spec or "base". You will rarely find it necessary to manipulate the intermediate structures or concern yourself with the details of their formation. * The term ''form'' is unfortunately overloaded in various modules to mean several things. In particular, for historical reasons, the form value inside of a form object is stored using the key `form`; the form table inside of an alternant multiword spec, a word spec (or "base") and the intermediate structures is stored using the key `forms`; and the accelerator tag set is internally referred to in [[WT:ACCEL]] as a "form". To avoid confusion, the following conventions are followed in code in this module, and should be followed for code in invoking modules as well: *# Functions that accept form objects often name the relevant parameter `form` (if a single form object is required) or `forms` (if a list of form objects, aka form list, is required). *# Functions that accept abbreviated form objects should (but don't always) indicate this by naming the parameter `abform` (for a single abbreviated form object) or `abforms` (for an abbreviated form list). *# Functions that accept a form value (the native-script string portion of a form object, stored for historical reasons in the `.form` property) should '''not''' call such a parameter `form`, but instead use something that makes clear that a form value is required, such as `formval` or sometimes just `val`. *# Similarly, functions that accept a form table should '''not''' call such a parameter `forms` (although for historical reasons the form table in an alternant multiword spec is stored in the field `forms`). Instead, use `formtable` or `formtab`, or similar name that makes clear that the value is a form table (i.e. a map from slot to form list). ====Footnote handling==== Each form can have one or more attached footnotes. The form of a footnote as specified by the user and stored in form values is e.g. {"[archaic]"} or {"[only in the meaning 'to succeed (an officeholder)']"}, i.e. the string must be surrounded by brackets and should begin with a lowercase letter and not end in a period/full stop. When such footnotes are converted to actual footnotes in a table of inflected forms, the brackets will be removed, the first letter will be capitalized and a period/full stop will be added to the end. (However, when such footnotes are used as qualifiers in headword lines, only the brackets will be removed, with no capitalization or final period.) When merging two forms into one, such as when concatenating the form objects of two inflected words in a multiword term or deduplicating form objects sharing the same form value during `show_forms()`, the footnotes are generally combined as well. This means that if one form object has footnotes and the other doesn't, the resulting form object inherits the footnotes of the object that has them, and if both form objects have footnotes, the resulting form object gets all footnotes from both source form objects, with duplicates removed. However, when inserting a form into a form table slot that already has a form whose form value and translit are identical to the new form, the behavior is different. In under normal circumstances the footnotes of the new form are ''not'' incorporated into those of the existing form (if any), but are simply dropped. To understand why this makes sense, consider a term that has two possible forms of its lemma (e.g. two forms differing in stress or in vowel length), where the second form is archaic, rare, colloquial or the like, and has an attached footnote indicating this. An example of this is {{m+|ru|кожух||sheepskin coat; bullet shell}}, where the form ''кожу́х'' with accent pattern ''b'' is more common overall but the form ''ко́жух'' with accent pattern ''c(1)'' is more common among professionals. On first glance, this could be indicated using `((кожу́х&lt;b>,ко́жух<c(1).[professional usage only]>))`. But some forms of these two declensions are the same (in particular, the genitive, dative, instrumental and prepositional plural). If for these slots, the footnotes of the duplicate forms were combined (i.e. the footnotes of the second declension pattern were added to the already-existing form taken from the first declension pattern), these forms would wrongly be labeled as ''professional usage only''. For this reason, it makes more sense to drop the footnotes of the second form when deduplicating. The same sort of behavior makes sense when a single lemma can have two different declensions, the second of which requires a footnote and where some forms in the two declensions are shared. An example of this is {{m+|uk|окови́та||strong, high-quality liquor}}, which can be inflected adjectivally or (rarely) nominally. This would be indicated as `((окови́та<sg.+>,окови́та<sg.[rare]>))` where the `+` indicates adjectival declension and the `sg` indicates that this term only exists in the singular. Here, the two declensions differ in the genitive, dative/locative and vocative (respectively, adjectival ''окови́тої'', ''окови́тій'', ''окови́та'' vs. nominal ''окови́ти'', ''окови́ті'', ''окови́то'') but are the same in the accusative (''окови́ту'') and instrumental (''окови́тою''). Again, dropping the footnotes of the second form when deduplicating is correct and including them would be wrong. This behavior can be changed by attaching a '''footnote modifier''' to the footnote associated the second form. A footnote modifier is a symbol attached to the beginning of a footnote, directly following the opening bracket. The following modifiers are currently recognized: * `!` or `+`: If placed on a footnote of the second form, combine that footnote with those of the first form (if any) rather than dropping it. * `*`: If placed on a footnote of the first form, drop that footnote when merging a second form with any footnotes. An example where the `*` modifier makes sense is a modification of the above example with {{m+|ru|кожух}}. If we notated it as `((кожу́х<b.[more common among laymen]>,ко́жух<c(1).[more common among professionals]>))`, the shared forms would wrongly have the footnote ''more common among laymen'' when in fact they are the only possible forms. If instead we used `((кожу́х<b.[*more common among laymen]>,ко́жух<c(1).[more common among professionals]>))`, the shared forms would correctly have no footnote. ]==] local function extract_footnote_modifiers(footnote) local footnote_mods, footnote_without_mods = footnote:match("^%[([!*+]?)(.*)%]$") if not footnote_mods then error("Saw footnote '" .. footnote .. "' not surrounded by brackets") end return footnote_mods, footnote_without_mods end function export.deduplicate_footnotes(existing, new) if not new then return existing end -- Check to see if there are existing footnotes with *; if so, remove them. if existing then local any_footnotes_with_asterisk = false for _, footnote in ipairs(existing) do local footnote_mods, _ = extract_footnote_modifiers(footnote) if footnote_mods:find("%*") then any_footnotes_with_asterisk = true break end end if any_footnotes_with_asterisk then local filtered_footnotes = {} for _, footnote in ipairs(existing) do local footnote_mods, _ = extract_footnote_modifiers(footnote) if not footnote_mods:find("%*") then insert(filtered_footnotes, footnote) end end if #filtered_footnotes > 0 then existing = filtered_footnotes else existing = nil end end end -- The behavior here has changed; track cases where the old behavior might -- be needed by adding ! to the footnote. track("combining-footnotes") local any_footnotes_with_bang = false for _, footnote in ipairs(new) do local footnote_mods, _ = extract_footnote_modifiers(footnote) if footnote_mods:find("[!+]") then any_footnotes_with_bang = true break end end if any_footnotes_with_bang then if not existing then existing = {} else existing = shallow_copy(existing) end for _, footnote in ipairs(new) do local already_seen = false local footnote_mods, footnote_without_mods = extract_footnote_modifiers(footnote) if footnote_mods:find("[!+]") then for _, existing_footnote in ipairs(existing) do local _, existing_footnote_without_mods = extract_footnote_modifiers(existing_footnote) if existing_footnote_without_mods == footnote_without_mods then already_seen = true break end end if not already_seen then insert(existing, footnote) end end end end return existing end --[==[ Combine two sets of footnotes. If either is {nil}, just return the other, and if both are {nil}, return {nil}. ]==] function export.combine_footnotes(notes1, notes2) if not notes1 and not notes2 then return nil end if not notes1 then return notes2 end if not notes2 then return notes1 end local combined = shallow_copy(notes1) for _, note in ipairs(notes2) do insert_if_not(combined, note) end return combined end function export.combine_formobj_lists(list1, list2, metadata) if not list1 and not list2 then return nil end if not list1 then return list2 end if not list2 then return list1 end return export.insert_formobjs_into_list { existing_list = list1, formobjs = list2, metadata = metadata, } end function export.looks_like_formobj_list(list) if list == nil then return true end if type(list) ~= "table" then return false end for k, v in pairs(list) do if type(k) ~= "number" then return false end if type(v) ~= "table" then return false end if type(v.form) ~= "string" then return false end end return true end function export.looks_like_boolean(object) return object == nil or type(object) == "boolean" end function export.default_fallback_deduplicate_handler(_prop, existing, _new) return existing, true end function export.default_fallback_combine_handler(_prop, existing, new) if export.looks_like_formobj_list(existing) and export.looks_like_formobj_list(new) then return export.combine_formobj_lists(existing, new), true end if export.looks_like_boolean(existing) and export.looks_like_boolean(new) then return existing or new, true end if existing ~= nil then return existing, true end return new, true end --[=[ Default handler functions for working with form object metadata. The user-specific metadata structure has the same format. The key is a specific metadata field and the value is an object with fields specifying handlers and other properties of the metadata. The recognized per-metadata fields are: * `deduplicate`: Handler to deduplicate form objects when inserting a form object into an existing list (e.g. in a form table) and two form objects have the same form value and translit. It is passed two values, respectively the metadata property from the existing list object and the new object being inserted into the list. The default and most common behavior here is to preserve the property from the existing object, even if {nil}, and discard the value from the new object. This is what is done in footnotes, for example, unless the existing footnote is preceded by a * (meaning to discard both values) or the new one is preceded by a + (meaning to preserve and combine both values, which is not currrently used anywhere and may not make sense). This ensures, for example, that if a noun can be declined according to two paradigms and the second one is rare, the {"rare"} footnote on the second one gets discarded whenever the two paradigms coincide. * `combine`: Handler to combine form objects when deriving a new object from an existing one. This is conceptually very different from deduplication. In this situation, the existing and new objects may or may not match in form value and translit, and since the new form object is a derivative of the existing one, it should combine the metadata properties of both, preserving as much information as possible and not discarding anything. Combining footnotes, for example, means taking the footnotes from either one, and if both exising and new objects have footnotes, merging the two lists and removing duplicates. (The only exception is if a new footnote is preceded by !, which causes existing footnotes to be discarded. This is termed an ''override'', and allows e.g. for one form of a paradigm to override the footnote associated with the paradigm as a whole, as for example if a paradigm is archaic except for one of the forms.) The default `combine` strategy is as follows: *# If both the existing and new objects are {nil} or "look like" form object lists (i.e. are tables where all keys are numeric and all values are tables with a `form` key whose value is a string), combine them using `insert_formobjs_into_list`. *# If both the existing and new objects are {nil} or boolean, combine them using `A or B`. *# Otherwise, take the existing value if it is non-{nil}, and if not, take the new value. Note that this means that the existing value will be kept even if it is {false} (which would not happen if the new value were {true}); this allows for metadata where {false} is a special signal and keys are otherwise strings or objects. However, in general if the metadata objects are not boolean and not form object lists, they will need a custom combine handler. Some keys in the metadata handler object are special: * `*`: This provides "fallback" handlers. These handlers are called for all metadata fields without dedicated handlers. These handlers work similar to normal `deduplicate` and `combine` handlers except (a) they are passed three values, first the metadata property name and then the existing and new values; (b) they should return two values, first the appropriate merged value and second a boolean indicating whether the property was handled. (If {false} is returned, the first value is ignored and should be {nil}.) Dedicated and fallback handlers are called in the following order: (1) dedicated user-specified handlers; (2) dedicated default handlers; (3) fallback user-specified handlers; (4) fallback default handlers. This ensures that standard dedicated default handlers (particularly for footnotes) get run prior to the user-specified fallback handlers; otherwise, practically all such fallback handlers will need to check for and ignore footnotes. * `_side_effect_formobjs`: If true, existing form objects can be side-effected during deduplication or combining. This requires that two logically distinct form objects in a given form table never have the same reference. (Generally two such objects should be in different slots. If two references to the same object occur in different positions in the list of form objects under a single slot, it indicates an internal error in the deduplication mechanism.) This means that user code that generates form objects for insertion into a table must take care never to reuse existing form objects (e.g. when setting one slot equal to another slot), but must always shallow-copy the form objects. This is not the default for this reason, and in fact it's not clear that implementing this results in any efficiency gain (it may actually be the reverse, since form objects have to be copied that could otherwise be shared). ]=] local default_metadata_handlers = { footnotes = { deduplicate = export.deduplicate_footnotes, combine = export.combine_footnotes, }, ["*"] = { deduplicate = export.default_fallback_deduplicate_handler, combine = export.default_fallback_combine_handler, } } local function is_non_metadata_property(k) return k == "form" or k == "translit" end -- Call the appropriate handler for merging the metadata for property `prop` with values `existing_val` and `new_val`, -- using method `method` (either "deduplicate" or "combine"). `metadata` is the structure describing all properties of -- all metadata in a given form object. This looks for, in order: -- 1. A handler for the specific property in `metadata`. -- 2. A default handler for the specific property. -- 3. A fallback handler in `metadata`. -- 4. A default fallback handler. -- Return the new value. local function call_merge_metadata_handler(metadata, prop, method, existing_val, new_val) local function try_specific(handler_struct, prop) if handler_struct and handler_struct[prop] and handler_struct[prop][method] then return (handler_struct[prop][method](existing_val, new_val)), true end return nil, false end local function try_fallback(handler_struct, prop) if handler_struct and handler_struct["*"] and handler_struct["*"][method] then -- Two values are returned from the handler, the new value and whether the property was handled. return handler_struct["*"][method](prop, existing_val, new_val) end return nil, false end local retval, matched = try_specific(metadata, prop) if matched then return retval end retval, matched = try_specific(default_metadata_handlers, prop) if matched then return retval end retval, matched = try_fallback(metadata, prop) if matched then return retval end retval, matched = try_fallback(default_metadata_handlers, prop) return retval end -- Merge the metadata of two form objects using `method` (either "deduplicate" or "combine"). `metadata` is the -- structure describing all properties of all metadata in a given form object. Metadata is merged between -- `existing_formobj` (on the left) and `new_formobj` (on the right), and is normally stored into `existing_formobj`, -- but will be stored into `new_formobj` if `merge_into_new` is given. If `metadata` allows for side-effecting, the -- changes will be made by side-effecting the object to be changed; otherwise, a shallow copy of the object will be -- made if necessary (only if the new value is not equal to the existing value). Returns two values, the changed form -- object (which may be the same as one of the passed-in objects) and a flag which is true if a copy was made of a form -- object. local function merge_metadata(metadata, method, existing_formobj, new_formobj, merge_into_new) local props_of_either = nil for k, _ in pairs(existing_formobj) do if not is_non_metadata_property(k) then props_of_either = props_of_either or {} props_of_either[k] = true end end for k, _ in pairs(new_formobj) do if not is_non_metadata_property(k) then props_of_either = props_of_either or {} props_of_either[k] = true end end local store_object = merge_into_new and new_formobj or existing_formobj if props_of_either then if metadata._side_effect_formobjs then for k, _ in pairs(props_of_either) do store_object[k] = call_merge_metadata_handler(metadata, k, method, existing_formobj[k], new_formobj[k]) end else local need_to_copy = false local new_values = {} for k, _ in pairs(props_of_either) do local newval = call_merge_metadata_handler(metadata, k, method, existing_formobj[k], new_formobj[k]) if newval ~= store_object[k] and not m_table.deepEquals(newval, store_object[k]) then need_to_copy = true end new_values[k] = newval end if need_to_copy then store_object = shallow_copy(store_object) for k, _ in pairs(props_of_either) do store_object[k] = new_values[k] end end return store_object, need_to_copy end else return store_object, false end end --[==[ Insert a form object (see above) into a list of such objects. If the form is already present (i.e. both the form value and translit, if any, match), the footnotes of the existing and new form might be combined (specifically, footnotes in the new form beginning with `+` will be combined). ]==] function export.insert_formobj_into_list(data) local existing_list = data.existing_list local formobj = data.formobj -- Don't do anything if the form object is nil. if not formobj then return existing_list end for i, existing_formobj in ipairs(existing_list) do if existing_formobj.form == formobj.form and existing_formobj.translit == formobj.translit then local new_existing, copied = merge_metadata(data.metadata, "deduplicate", existing_formobj, formobj) if copied then local list_copy = shallow_copy(existing_list) list_copy[i] = new_existing return list_copy end return existing_list end end -- Form not found. insert(existing_list, formobj) return existing_list end --[==[ Insert a list of form objects (see above) into an existing list of such objects. ``forms`` can be {nil}, in which case nothing happens. This is equivalent to calling `insert_form_into_list` for each form object. ]==] function export.insert_formobjs_into_list(data) local existing_list = data.existing_list local formobjs = data.formobjs if not formobjs then return existing_list end for _, formobj in ipairs(formobjs) do existing_list = export.insert_formobj_into_list { existing_list = existing_list, formobj = formobj, metadata = data.metadata, } end return existing_list end --[==[ Insert a form object (see above) into the given slot in the given form table. On input, `data` is an object with the following fields: * `formtable`: A form table (see above). * `slot`: A slot in the form table, i.e. a key whose value is a form object list. * `formobj`: A form object to be inserted into the slot. Can be {nil}, in which case nothing happens. * `metadata`: The metadata structure describing how to combine user-specified metadata. Can be {nil} if there is no user-specified metadata or the default fallback metadata handlers are sufficient (which is usually the case if the all user-specified metadata is boolean). ]==] function export.insert_formobj_into_form_table(data) local formtable, slot, formobj, metadata = data.formtable, data.slot, data.formobj, data.metadata -- Don't do anything if the form object is nil. if not formobj then return end if not formtable[slot] then formtable[slot] = {} end formtable[slot] = export.insert_formobj_into_list { existing_list = formtable[slot], formobj = formobj, metadata = metadata, } end --[==[ Insert a list of form objects (see above) into the given slot in the given form table. On input, `data` is an object with the following fields: * `formtable`: A form table (see above). * `slot`: A slot in the form table, i.e. a key whose value is a form object list. * `formobjs`: A list of form objects (in "full list" form) to be inserted into the slot. Can be {nil} or an empty list, in which case nothing happens. * `metadata`: The metadata structure describing how to combine user-specified metadata. Can be {nil} if there is no user-specified metadata or the default fallback metadata handlers are sufficient (which is usually the case if the all user-specified metadata is boolean). ]==] function export.insert_formobjs_into_form_table(data) local formtable, slot, formobjs, metadata = data.formtable, data.slot, data.formobjs, data.metadata if not formobjs or not formobjs[1] then return end if not formtable[slot] then formtable[slot] = {} end formtable[slot] = export.insert_formobjs_into_list { existing_list = formtable[slot], formobjs = formobjs, metadata = metadata, } end --[==[ Flatmap a function over a list of form objects and return the (potentially flattened) results. Flatmapping is like a normal mapping operation except that the function being called may return a list, and all values returned are concatenated into a single flattened list. On input, `data` is an object with the following fields: * `formobjs`: A list of form objects to map over, in "full list form" (see above). * `fn`: The function to map over each form object. If a form object's value is {"?"}, the form object is preserved on output and the function is not called. Otherwise, the function is called with one argument, the form object, and should {nil} (equivalent to returning an empty list) or an abbreviated form list (i.e. anything that is convertible into a full list form, such as a single form value, a list of form values, a form object or a list of form objects). For each form object in the return value, the metadata of the object mapped over is combined with the metadata of the returned object, unless `no_combine_metadata` is given. The resulting form objects from all function calls are flattened into a list and deduplicated in the same fashion as `insert_formobj_into_list()` (in case two different form objects map to the same form value). * `metadata`: The metadata structure describing how to combine user-specified metadata. Can be {nil} if there is no user-specified metadata or the default fallback metadata handlers are sufficient (which is usually the case if the all user-specified metadata is boolean). * `no_combine_metadata`: See `fn` above. If given, metadata from objects mapped over will not be combined into the returned objects. NOTE: There is no `map_formobjs` function provided because `flatmap_formobjs` will correctly handle functions that return single form objects. ]==] function export.flatmap_formobjs(data) local formobjs, fn, metadata, no_combine_metadata = data.formobjs, data.fn, data.metadata, data.no_combine_metadata if not formobjs then return nil end local retval = {} for _, formobj in ipairs(formobjs) do local funret if formobj.form == "?" then if metadata and metadata._side_effect_formobjs then funret = shallow_copy(formobj) else funret = formobj end else funret = fn(formobj) end if funret then funret = export.convert_to_full_list_form(funret) for _, fr in ipairs(funret) do if not no_combine_metadata then fr = merge_metadata(metadata, "combine", formobj, fr, "merge_into_new") end retval = export.insert_formobj_into_list { existing_list = retval, formobj = fr, metadata = metadata, } end end end return retval end --[==[ Insert a form object (see above) into a list of such objects. If the form is already present (i.e. both the form value and translit, if any, match), the footnotes of the existing and new form might be combined (specifically, footnotes in the new form beginning with `!` will be combined). ]==] function export.insert_form_into_list(list, form) -- Don't do anything if the form object or the form inside it is nil. This simplifies -- form insertion in the presence of inflection generating functions that may return nil, -- such as generate_noun_vocative() and generate_noun_count_form(). if not form or not form.form then return end for _, listform in ipairs(list) do if listform.form == form.form and listform.translit == form.translit then listform.footnotes = export.deduplicate_footnotes(listform.footnotes, form.footnotes) return end end -- Form not found. insert(list, form) end --[==[ Insert a list of form objects (see above) into an existing list of such objects. ``forms`` can be {nil}, in which case nothing happens. This is equivalent to calling `insert_form_into_list` for each form object. ]==] function export.insert_forms_into_list(list, forms) if not forms then return end for _, form in ipairs(forms) do export.insert_form_into_list(list, form) end end --[==[ Insert a form object (see above) into the given slot in the given form table. ``form`` can be {nil}, in which case nothing happens. ]==] function export.insert_form(formtable, slot, form) -- Don't do anything if the form object or the form inside it is nil. This simplifies -- form insertion in the presence of inflection generating functions that may return nil, -- such as generate_noun_vocative() and generate_noun_count_form(). if not form or not form.form then return end if not formtable[slot] then formtable[slot] = {} end export.insert_form_into_list(formtable[slot], form) end --[==[ Insert a list of form objects (see above) into the given slot in the given form table. ``forms`` can be {nil}, in which case nothing happens. ]==] function export.insert_forms(formtable, slot, forms) if not forms then return end for _, form in ipairs(forms) do export.insert_form(formtable, slot, form) end end --[==[ Identity mapping function. ]==] function export.identity(formval, translit) return formval, translit end local function form_value_transliterable(formval) return formval ~= "?" and formval ~= "—" end local function call_map_function_str(str, fun) if str == "?" then return "?" end local newformval, newtranslit = fun(str) if newtranslit then return {form=newformval, translit=newtranslit} else return newformval end end -- FIXME: This doesn't correctly handle metadata. local function call_map_function_obj(form, fun) if form.form == "?" then return {form = "?", footnotes = form.footnotes} end local newformval, newtranslit = fun(form.form, form.translit) return {form = newformval, translit = newtranslit, footnotes = form.footnotes} end --[==[ Map a function over the form values in ``forms`` (a list of form objects in "full list form"; see above). If an input form value is {"?"}, it is preserved on output and the function is not called. Otherwise, the function is called with two arguments, the original form and manual translit; if manual translit isn't relevant, it's fine to declare the function with only one argument. The return value is either a single value (the new form) or two values (the new form and new manual translit). The footnotes (if any) from the input form objects are preserved on output. Uses `insert_form_into_list()` to insert the resulting form objects into the returned list in case two different forms map to the same thing. FIXME: Expand this to correctly handle metadata, or create a variant that correctly handles metadata. ]==] function export.map_forms(forms, fun) if not forms then return nil end local retval = {} for _, form in ipairs(forms) do export.insert_form_into_list(retval, call_map_function_obj(form, fun)) end return retval end --[==[ Map a list-returning function over the form values in ``forms`` (a list of form objects in "full list form"; see above). If an input form value is {"?"}, it is preserved on output and the function is not called. Otherwise, the function is called with two arguments, the original form and manual translit; if manual translit isn't relevant, it's fine to declare the function with only one argument. The return value of the function can be {nil} or an abbreviated form list (i.e. anything that is convertible into a full list form, such as a single form value, a list of form values, a form object or a list of form objects). For each form object in the return value, the footnotes of that form object (if any) are combined with any footnotes from the input form object, and the result inserted into the returned list using `insert_form_into_list()` in case two different forms map to the same thing. ]==] function export.flatmap_forms(forms, fun) if not forms then return nil end local retval = {} for _, form in ipairs(forms) do local funret = form.form == "?" and form or fun(form.form, form.translit) if funret then funret = export.convert_to_general_list_form(funret) for _, fr in ipairs(funret) do local newform = { form = fr.form, translit = fr.translit, footnotes = export.combine_footnotes(form.footnotes, fr.footnotes) } export.insert_form_into_list(retval, newform) end end end return retval end --[==[ Map a function over the form values in ``abforms`` (an abbreviated form list). If the input form value is {"?"}, it is preserved on output and the function is not called. If ``first_only`` is given and ``abforms`` is a list, only map over the first element. Return value is of the same form as ``abforms``, unless ``abforms`` is a string and the function returns both form value and manual translit (in which case the return value is a form object). The function is called with two arguments, the original form value and manual translit; if manual translit isn't relevant, it's fine to declare the function with only one argument. The return value is either a single value (the new form value) or two values (the new form value and new manual translit). The footnotes (if any) from the input form objects are preserved on output. FIXME: This function is used only in [[Module:bg-verb]] and should be moved into that module. ]==] function export.map_form_or_forms(abforms, fun, first_only) if not abforms then return nil elseif type(abforms) == "string" then return call_map_function_str(abforms, fun) elseif abforms.form then return call_map_function_obj(abforms, fun) else local retval = {} for i, abform in ipairs(abforms) do if first_only then return export.map_form_or_forms(abform, fun) end insert(retval, export.map_form_or_forms(abform, fun)) end return retval end end --[==[ Expand a given footnote (as specified by the user, including the surrounding brackets) into the form to be inserted into the final generated table. If ``no_parse_refs`` is not given and the footnote is a reference (of the form {"[ref:...]"}), parse and return the specified reference(s). Two values are returned, `footnote_string` (the expanded footnote, or nil if the second value is present) and `references` (a list of objects of the form `{text = ``text``, name = ``name``, group = ``group``}` if the footnote is a reference and ``no_parse_refs`` is not given, otherwise {nil}). Unless ``return_raw`` is given, the returned footnote string is capitalized and has a final period added. ]==] function export.expand_footnote_or_references(note, return_raw, no_parse_refs) local _, notetext = extract_footnote_modifiers(note) if not no_parse_refs and notetext:find("^ref:") then -- a reference notetext = notetext:gsub("^ref:", "") local parsed_refs = require("Module:references").parse_references(notetext) for i, ref in ipairs(parsed_refs) do if type(ref) == "string" then parsed_refs[i] = {text = ref} end end return nil, parsed_refs end if footnote_abbrevs[notetext] then notetext = footnote_abbrevs[notetext] track("footnote-whole-abbrev") else local split_notes = split(notetext, "<(.-)>") for i, split_note in ipairs(split_notes) do if i % 2 == 0 then split_notes[i] = footnote_abbrevs[split_note] track("footnote-angle-bracket-abbrev") if not split_notes[i] then -- Don't error for now, because HTML might be in the footnote. -- Instead we should switch the syntax here to e.g. <<a>> to avoid -- conflicting with HTML. split_notes[i] = "<" .. split_note .. ">" track("footnote-unrecognized-angle-bracket-abbrev") --error("Unrecognized footnote abbrev: <" .. split_note .. ">") else track("footnote-recognized-angle-bracket-abbrev") end end end notetext = concat(split_notes) end return return_raw and notetext or ucfirst(notetext) .. "." end --[==[ Convert a list of foonotes to qualifiers and references for use in [[Module:headword]] or similar. Returns two values, a list of qualifiers (possibly {nil}) and a list of reference structures (possibly {nil}), following the structure defined in [[Module:references]]). ]==] function export.convert_footnotes_to_qualifiers_and_references(footnotes) if not footnotes then return nil end local quals, refs for _, qualifier in ipairs(footnotes) do local this_footnote, this_refs = export.expand_footnote_or_references(qualifier, "return raw") if this_refs then if not refs then refs = this_refs else for _, ref in ipairs(this_refs) do insert(refs, ref) end end else if not quals then quals = {this_footnote} else insert(quals, this_footnote) end end end return quals, refs end --[==[ Combine an abbreviated form object (either a string or a table) with additional footnotes, possibly replacing the form value and/or translit in the process. Normally called in one of two ways: (1) `combine_form_and_footnotes(``form_obj``, ``addl_footnotes``, ``new_form``, ``new_translit``)` where ``form_obj`` is an existing abbreviated form object; ``addl_footnotes`` is either {nil}, a single string (a footnote) or a list of footnotes; ``new_formval`` is either {nil} or the new form value to substitute; and ``new_translit`` is either {nil} or the new translit string to substitute. (2) `combine_form_and_footnotes(``form_value``, ``footnotes``)`, where ``form_value`` is a form value (a string) and ``footnotes`` is either {nil}, a single string (a footnote) or a list of footnotes. In either case, a form object is returned, preserving as many properties as possible from any existing form object in ``abform``. Do the minimal amount of work; e.g. if ``abform`` is a form object and ``addl_footnotes``, ``new_formval`` and ``new_translit`` are all {nil}, the same object as passed in is returned. Under no circumstances is the existing form object side-effected. '''FIXME:''' This does not correctly preserve metadata. ]==] function export.combine_form_and_footnotes(abform, addl_footnotes, new_formval, new_translit) if type(addl_footnotes) == "string" then addl_footnotes = {addl_footnotes} end if not addl_footnotes and not new_formval and not new_translit then return abform end if type(abform) == "string" then new_formval = new_formval or abform return {form = new_formval, translit = new_translit, footnotes = addl_footnotes} end abform = shallow_copy(abform) if new_formval then abform.form = new_formval end if new_translit then abform.translit = new_translit end if addl_footnotes then abform.footnotes = export.combine_footnotes(abform.footnotes, addl_footnotes) end return abform end --[==[ Convert an abbreviated form list (either a string, form object, or list of either) into full list form. This function does not side-effect any of the objects passed into ``abforms``, but will return ``abforms`` unchanged if already in full list form. ]==] function export.convert_to_full_list_form(abforms) if type(abforms) == "string" then return {{form = abforms}} elseif abforms.form then return {abforms} else -- Check if already in full list form and return directly if so. local must_convert = false for _, form in ipairs(abforms) do if type(form) == "string" then must_convert = true break end end if not must_convert then return abforms end end local retval = {} for _, form in ipairs(abforms) do if type(form) == "string" then insert(retval, {form = form}) else insert(retval, form) end end return retval end --[==[ Convert an abbreviated form list (either a string, form object, or list of either) into full list form. If ``footnotes`` is supplied, then for each form in the form list, combine the form's footnotes with ``footnotes``. This function does not side-effect any of the objects passed into ``abforms``, but will return ``abforms`` unchanged if already in full list form and ``footnotes`` is {nil}. '''FIXME:''' This does not correctly preserve metadata. ]==] function export.convert_to_general_list_form(abforms, footnotes) if type(footnotes) == "string" then footnotes = {footnotes} end if type(abforms) == "string" then return {{form = abforms, footnotes = footnotes}} elseif abforms.form then return {export.combine_form_and_footnotes(abforms, footnotes)} elseif not footnotes then -- Check if already in full list form and return directly if so. local must_convert = false for _, form in ipairs(abforms) do if type(form) == "string" then must_convert = true break end end if not must_convert then return abforms end end local retval = {} for _, form in ipairs(abforms) do if type(form) == "string" then insert(retval, {form = form, footnotes = footnotes}) else insert(retval, export.combine_form_and_footnotes(form, footnotes)) end end return retval end local function is_table_of_strings(forms) for k, v in pairs(forms) do if type(k) ~= "number" or type(v) ~= "string" then return false end end return true end local function lang_or_func_transliterate(func, lang, text) local retval if func then retval = func(text) else retval = (lang:transliterate(text)) end -- FIXME! Hack to work around bug in ...:transliterate(). Remove me as soon as this bug is fixed. if not retval and (text == " " or text == "-" or text == "-́" or text == "?" or text == "-∅") then retval = text end if not retval then error(("Unable to transliterate text '%s'"):format(text)) end return retval end --[==[ Combine ``stems`` and ``endings`` and store into slot ``slot`` of form table ``formtable``. Either of ``stems`` and ``endings`` can be {nil} or an abbreviated form list. The combination of a given stem and ending happens using ``combine_stem_ending``, which takes two parameters (stem and ending, each a string) and returns one value (a string). If manual transliteration is present in either ``stems`` or ``endings``, ``lang`` (a language object or a function of one argument to transliterate a string) along with ``combine_stem_ending_tr`` (a function for combining manual transliterations that works much like ``combine_stem_ending``) must be given. ``footnotes``, if specified, is a list of additional footnotes to attach to the resulting inflections (stem+ending combinations). The resulting inflections are inserted into the form table using `insert_form()`, in case of duplication. ]==] function export.add_forms(formtable, slot, stems, endings, combine_stem_ending, lang, combine_stem_ending_tr, footnotes) if stems == nil or endings == nil then return end local function combine(stem, ending) if stem == "?" or ending == "?" then return "?" end return combine_stem_ending(stem, ending) end local function transliterate(text) return lang_or_func_transliterate(is_callable(lang) and lang or nil, lang, text) end if type(stems) == "string" and type(endings) == "string" then export.insert_form(formtable, slot, {form = combine(stems, endings), footnotes = footnotes}) elseif type(stems) == "string" and is_table_of_strings(endings) then for _, ending in ipairs(endings) do export.insert_form(formtable, slot, {form = combine(stems, ending), footnotes = footnotes}) end else stems = export.convert_to_general_list_form(stems) endings = export.convert_to_general_list_form(endings, footnotes) for _, stem in ipairs(stems) do for _, ending in ipairs(endings) do local new_footnotes = nil if stem.footnotes and ending.footnotes then new_footnotes = shallow_copy(stem.footnotes) for _, footnote in ipairs(ending.footnotes) do insert_if_not(new_footnotes, footnote) end elseif stem.footnotes then new_footnotes = stem.footnotes elseif ending.footnotes then new_footnotes = ending.footnotes end local new_form = combine(stem.form, ending.form) local new_translit if new_form ~= "?" and (stem.translit or ending.translit) then if not lang or not combine_stem_ending_tr then error("Internal error: With manual translit, 'lang' and 'combine_stem_ending_tr' must be passed to 'add_forms'") end local stem_tr = stem.translit or transliterate(m_links.remove_links(stem.form)) local ending_tr = ending.translit or transliterate(m_links.remove_links(ending.form)) new_translit = combine_stem_ending_tr(stem_tr, ending_tr) end export.insert_form(formtable, slot, {form = new_form, translit = new_translit, footnotes = new_footnotes}) end end end end --[==[ Combine any number of form components and store into slot ``slot`` of form table ``formtable``. ``components`` is a list of abbreviated form lists which should be concatenated similarly to how `add_forms()` does it, and stored in ``slot`` along with any footnotes in ``footnotes``. More specifically: # If there are no components, nothing happens. # If there is one component, it is converted to full list form and `insert_forms()` called. # If there are two components, they are treated as stems and endings respectively and `add_forms()` is called. # If there are three or more components, they are concatenated left-to-right in the manner of a `reduce()` operation: the first two components are combined using `add_forms()` and stored into a temporary table, then the next component is combined with the result of the previous operation, etc. In the last combination, footnotes in `footnotes` are combined in, and the result stored into `formtable`. This should generally be used when you are likely to have three or more components, as in [[Module:ar-verb]] (prefixes, stems and endings) and [[Module:de-verb]] (which in some situations has five components combined together). ``combine_stem_ending``, ``lang``, ``combine_stem_ending_tr`` and ``footnotes`` are as in `add_forms()`. ]==] function export.add_multiple_forms(formtable, slot, components, combine_stem_ending, lang, combine_stem_ending_tr, footnotes) if #components == 0 then return elseif #components == 1 then local forms = export.convert_to_general_list_form(components[1], footnotes) export.insert_forms(formtable, slot, forms) elseif #components == 2 then local stems = components[1] local endings = components[2] export.add_forms(formtable, slot, stems, endings, combine_stem_ending, lang, combine_stem_ending_tr, footnotes) else local prev = components[1] for i=2, #components do local temptable = {} export.add_forms(temptable, slot, prev, components[i], combine_stem_ending, lang, combine_stem_ending_tr, i == #components and footnotes or nil) prev = temptable[slot] end export.insert_forms(formtable, slot, prev) end end local function iterate_slot_list_or_table(props, do_slot) if props.slot_list then for _, slot_and_accel_tag_set in ipairs(props.slot_list) do local slot, accel_tag_set = unpack(slot_and_accel_tag_set) do_slot(slot, accel_tag_set) end else for slot, accel_tag_set in pairs(props.slot_table) do do_slot(slot, accel_tag_set) end end end function export.default_split_bracketed_runs_into_words(bracketed_runs, data) -- If the text begins with a hyphen, include the hyphen in the set of allowed characters -- for an inflected segment. This way, e.g. conjugating "-ir" is treated as a regular -- -ir verb rather than a hyphen + irregular [[ir]]. local is_suffix = (not data or data.text_index == 1) and bracketed_runs[1]:find("^%-") local split_pattern = is_suffix and " " or "[ %-]" return put.split_alternating_runs(bracketed_runs, split_pattern, "preserve splitchar") end local function props_transliterate(props, text) return lang_or_func_transliterate(props.transliterate, props.lang, text) end local function parse_before_or_post_text(data) -- NOTE: data.text_index is also used (in `default_split_bracketed_runs_into_words`). local props, text, segments, lemma_is_last = data.props, data.text, data.segments, data.lemma_is_last -- Call parse_balanced_segment_run() to keep multiword links together. local bracketed_runs = put.parse_balanced_segment_run(text, "[", "]") -- Split normally on space or hyphen (but customizable). Use preserve_splitchar so we know whether the separator was -- a space or hyphen. local space_separated_groups if props.split_bracketed_runs_into_words then space_separated_groups = props.split_bracketed_runs_into_words(bracketed_runs) end if not space_separated_groups then space_separated_groups = export.default_split_bracketed_runs_into_words(bracketed_runs, data) end local parsed_components = {} local parsed_components_translit = {} local saw_manual_translit = false local lemma for j, space_separated_group in ipairs(space_separated_groups) do local component = concat(space_separated_group) if lemma_is_last and j == #space_separated_groups then lemma = component if lemma == "" and not props.allow_blank_lemma then error("Word is blank: '" .. concat(segments) .. "'") end elseif component:find("//") then -- Manual translit or respelling specified. if not props.lang then error("Manual translit not allowed for this language; if this is incorrect, 'props.lang' must be set internally") end saw_manual_translit = true local split_components = split(component, "//", "plain") if #split_components ~= 2 then error("Term with translit or respelling should have only one // in it: " .. component) end local translit component, translit = unpack(split_components) if props.transliterate_respelling then translit = props.transliterate_respelling(translit) end insert(parsed_components, component) insert(parsed_components_translit, translit) else insert(parsed_components, component) insert(parsed_components_translit, false) -- signal that it may need later transliteration end end if saw_manual_translit then for j, parsed_component in ipairs(parsed_components) do if not parsed_components_translit[j] then parsed_components_translit[j] = props_transliterate(props, m_links.remove_links(parsed_component)) end end end text = concat(parsed_components) local translit if saw_manual_translit then translit = concat(parsed_components_translit) end return text, translit, lemma end --[=[ Parse a segmented multiword spec such as "[[медичний|меди́чна]]<+> [[сестра́]]<*,*#.pr>" (in Ukrainian). "Segmented" here means it is broken up on <...> segments using parse_balanced_segment_run(text, "<", ">"), e.g. the above text would be passed in as {"[[медичний|меди́чна]]", "<+>", " [[сестра́]]", "<*,*#.pr>", ""}. The return value is a table of the form { word_specs = {``word_spec``, ``word_spec``, ...}, post_text = "``text-at-end``", post_text_no_links = "``text-at-end-no-links``", post_text_translit = "``manual-translit-of-text-at-end``" or nil (if no manual translit or respelling was specified in the post-text) } where ``word_spec`` describes an individual inflected word and "``text-at-end``" is any raw text that may occur after all inflected words. Individual words or linked text (including multiword text) may be given manual transliteration or respelling in languages that support this using ``text``//``translit`` or ``text``//``respelling``. Each ``word_spec`` is of the form returned by parse_indicator_spec(): { lemma = "``lemma``", before_text = "``text-before-word``", before_text_no_links = "``text-before-word-no-links``", before_text_translit = "``manual-translit-of-text-before-word``" or nil (if no manual translit or respelling was specified in the before-text) -- Fields as described in parse_indicator_spec() ... } For example, the return value for "[[медичний|меди́чна]]<+> [[сестра́]]<*,*#.pr>" is { word_specs = { { lemma = "[[медичний|меди́чна]]", overrides = {}, adj = true, before_text = "", before_text_no_links = "", forms = {}, }, { lemma = "[[сестра́]]", overrides = {}, stresses = { { reducible = true, genpl_reversed = false, }, { reducible = true, genpl_reversed = true, }, }, animacy = "pr", before_text = " ", before_text_no_links = " ", forms = {}, }, }, post_text = "", post_text_no_links = "", } ]=] local function parse_multiword_spec(segments, props, disable_allow_default_indicator) local multiword_spec = { word_specs = {} } if not disable_allow_default_indicator then if #segments == 1 then if props.allow_default_indicator then insert(segments, "<>") insert(segments, "") elseif props.angle_brackets_omittable then segments[1] = "<" .. segments[1] .. ">" insert(segments, 1, "") insert(segments, "") end end end -- Loop over every other segment. The even-numbered segments are angle-bracket specs while -- the odd-numbered segments are the text between them. for i = 2, #segments - 1, 2 do local before_text, before_text_translit, lemma = parse_before_or_post_text { props = props, text = segments[i - 1], text_index = i - 1, segments = segments, lemma_is_last = true } local base = props.parse_indicator_spec(segments[i], lemma) base.before_text = before_text base.before_text_no_links = m_links.remove_links(base.before_text) base.before_text_translit = before_text_translit base.lemma = base.lemma or lemma insert(multiword_spec.word_specs, base) end multiword_spec.post_text, multiword_spec.post_text_translit = parse_before_or_post_text { props = props, text = segments[#segments], text_index = #segments, segments = segments, lemma_is_last = false } multiword_spec.post_text_no_links = m_links.remove_links(multiword_spec.post_text) return multiword_spec end --[=[ Parse an alternant, e.g. "((родо́вий,родови́й))" or "((ру́син<pr>,руси́н<b.pr>))" (both in Ukrainian). The return value is a table of the form { alternants = {``multiword_spec``, ``multiword_spec``, ...} } where ``multiword_spec`` describes a given alternant and is as returned by parse_multiword_spec(). ]=] local function parse_alternant(alternant, props) local alternant_text = alternant:match("^%(%((.*)%)%)$") local segments = put.parse_balanced_segment_run(alternant_text, "<", ">") local comma_separated_groups = put.split_alternating_runs(segments, "%s*,%s*") local alternant_spec = {alternants = {}} for _, comma_separated_group in ipairs(comma_separated_groups) do insert(alternant_spec.alternants, parse_multiword_spec(comma_separated_group, props)) end return alternant_spec end --[==[ Top-level parsing function. Parse text describing one or more inflected words. `text` is the inflected text to parse, which generally has `<...>` specs following words to be inflected, and may have alternants indicated using double parens. Examples: * {"[[медичний|меди́чна]]<+> [[сестра́]]<*,*#.pr>"} (Ukrainian, for {{m|uk|меди́чна сестра́||nurse|lit=medical sister}}); * {"((ру́син<pr>,руси́н<b.pr>))"} (Ukrainian, for {{m|uk|русин||Rusyn}}, with two possible stress patterns); * {"पंचायती//पंचाय*ती राज<M>"} (Hindi, for {{m|hi|पंचायती राज||village council}}, with phonetic respelling in the before-text component); * {"((<M>,<M.plstem:फ़तूह.dirpl:फ़तूह>))"} (Hindi, for {{m|hi|फ़तह||win, victory}} when used on that page, where the lemma is omitted and taken from the pagename); * {""} (for any number of Hindi adjectives, where the lemma is omitted and taken from the pagename, and the angle bracket spec <> is assumed); * {"काला<+>धन<M>"} (Hindi, for {{m|hi|कालाधन||black money}}, showing that closed compounds where each part is declined can be correctly handled). `props` is an object specifying properties used during parsing, as follows: ```{ parse_indicator_spec = __function__(``angle_bracket_spec``, ``lemma``) `''(required)''`, lang = __lang object__, transliterate_respelling = __function__(``respelling_or_translit``) `''(optional)''`, split_bracketed_runs_into_words = __function__(``bracket_split_runs``) `''(optional)''`, allow_default_indicator = __boolean__, angle_brackets_omittable = __boolean__, allow_blank_lemma = __boolean__, }``` `parse_indicator_spec` is a required function that takes two arguments, a string surrounded by angle brackets and the lemma, and should return an arbitrary object containing properties describing the indicators inside of the angle brackets). This object is often called a '''base''' and given the argument name `base` in inflection code. `lang` is the language object for the language in question; only needed if manual translit or respelling may be present using `//`. `transliterate_respelling` is a function that is only needed if respelling is allowed in place of manual translit after `//`. It takes one argument, the respelling or translit, and should return the transliteration of any respelling but return any translit unchanged. `split_bracketed_runs_into_words` is an optional function to split the passed-in text into words. It is used, for example, to determine what text constitutes a word when followed by an angle-bracket spec, i.e. what the lemma to be inflected is vs. surrounding fixed text. It takes one argument, the result of splitting the original text on brackets, and should return alternating runs of words and split characters, or nil to apply the default algorithm. Specifically, the value passed in is the result of calling `parse_balanced_segment_run(``text``, "[", "]")` from [[Module:parse utilities]] on the original text, and the default version of this function calls `split_alternating_runs(``bracketed_runs``, ``pattern``, "preserve splitchar")`, where ``bracketed_runs`` is the value passed in and ``pattern`` splits on either spaces or hyphens (unless the text begins with a hyphen, in which case splitting is only on spaces, so that suffixes can be inflected). `allow_default_indicator` should be {true} if an empty indicator in angle brackets `<>` can be omitted and should be automatically added at the end of the multiword text (if no alternants) or at the end of each alternant (if alternants present). `angle_brackets_omittable` should be {true} if angle brackets can be omitted around a non-empty indicator in the presence of a blank lemma. In this case, if the combined indicator spec has no angle brackets, they will be added around the indicator (or around all indicators, if alternants are present). This only makes sense when `allow_blank_lemma` is specified. `allow_blank_lemma` should be {true} of if a blank lemma is allowed; in such a case, the calling function should substitute a default lemma, typically taken from the pagename. The return value is a table referred to as an '''alternant multiword spec''', and is of the form ```{ alternant_or_word_specs = {``alternant_or_word_spec``, ``alternant_or_word_spec``, ...}, post_text = "``text_at_end``", post_text_no_links = "``text_at_end_no_links``", post_text_translit = "``translit_of_text_at_end``" `(or nil)`, }``` where `alternant_or_word_spec` is either an '''alternant spec''' as returned by `parse_alternant()` or a '''multiword spec''' as described in the comment above `parse_multiword_spec()`. An alternant spec looks as follows: ```{ alternants = {``multiword_spec``, ``multiword_spec``, ...}, before_text = "``text_before_alternant``", before_text_no_links = "``text_before_alternant``", before_text_translit = "``translit_of_text_before_alternant``" `(or nil)`, }``` i.e. it is like what is returned by `parse_alternant()` but has extra `before_text` and `before_text_no_links` fields. ]==] function export.parse_inflected_text(text, props) if props.angle_brackets_omittable and not props.allow_blank_lemma then error("If 'angle_brackets_omittable' is specified, so should 'allow_blank_lemma'") end local alternant_multiword_spec = {alternant_or_word_specs = {}} local alternant_segments = split(text, "(%(%(.-%)%))") local last_post_text, last_post_text_no_links, last_post_text_translit for i = 1, #alternant_segments do if i % 2 == 1 then local segments = put.parse_balanced_segment_run(alternant_segments[i], "<", ">") -- Disable allow_default_indicator if alternants are present and we're processing -- the non-alternant text. Otherwise we will try to treat the non-alternant text -- surrounding the alternants as an inflected word rather than as raw text. local multiword_spec = parse_multiword_spec(segments, props, #alternant_segments ~= 1) for _, word_spec in ipairs(multiword_spec.word_specs) do insert(alternant_multiword_spec.alternant_or_word_specs, word_spec) end last_post_text = multiword_spec.post_text last_post_text_no_links = multiword_spec.post_text_no_links last_post_text_translit = multiword_spec.post_text_translit else local alternant_spec = parse_alternant(alternant_segments[i], props) alternant_spec.before_text = last_post_text alternant_spec.before_text_no_links = last_post_text_no_links alternant_spec.before_text_translit = last_post_text_translit insert(alternant_multiword_spec.alternant_or_word_specs, alternant_spec) end end alternant_multiword_spec.post_text = last_post_text alternant_multiword_spec.post_text_no_links = last_post_text_no_links alternant_multiword_spec.post_text_translit = last_post_text_translit -- Save boolean properties from `props`. We need at least `allow_default_indicator` when implementing -- `reconstruct_original_spec()`. alternant_multiword_spec.allow_default_indicator = props.allow_default_indicator alternant_multiword_spec.angle_brackets_omittable = props.angle_brackets_omittable alternant_multiword_spec.allow_blank_lemma = props.allow_blank_lemma return alternant_multiword_spec end -- Inflect alternants in ``alternant_spec`` (an object as returned by parse_alternant()). -- This sets the form values in ```alternant_spec``.forms` for all slots. -- (If a given slot has no values, it will not be present in ```alternant_spec``.forms`). local function inflect_alternants(alternant_spec, props) alternant_spec.forms = {} for _, multiword_spec in ipairs(alternant_spec.alternants) do export.inflect_multiword_or_alternant_multiword_spec(multiword_spec, props) iterate_slot_list_or_table(props, function(slot) if not props.skip_slot or not props.skip_slot(slot) then export.insert_forms(alternant_spec.forms, slot, multiword_spec.forms[slot]) end end) end end --[=[ Subfunction of `inflect_multiword_or_alternant_multiword_spec()`. This is used in building up the inflections of multiword expressions. The basic purpose of this function is to append a set of forms representing the inflections of a given inflected term in a given slot onto the existing forms for that slot. Given a multiword expression potentially consisting of several inflected terms along with fixed text in between, we work iteratively from left to right, adding the new forms onto the existing ones. Normally, all combinations of new and existing forms are created, meaning if there are M existing forms and N new ones, we will end up with M*N forms. However, some of these combinations can be rejected using the variant mechanism (see the description of get_variants below). Specifically, `formtable` is a table of per-slot forms, where the key is a slot and the value is a list of form objects (objects of the form {form=``form``, translit=``manual_translit``, footnotes=``footnotes``}). `slot` is the slot in question. `forms` specifies the forms to be appended onto the existing forms, and is likewise a list of form objects. `props` is the same as in `inflect_multiword_or_alternant_multiword_spec()`. `before_text` is the fixed text that goes before the forms to be added. `before_text_no_links` is the same as `before_text` but with any links (i.e. hyperlinks of the form [[``term``]] or [[``term``|``display``]]) converted into raw terms using remove_links() in [[Module:links]], and `before_text_translit` is optional manual translit of `before_text_no_links`. Note that the value "?" in a form is "infectious" in that if either the existing or new form has the value "?", the resulting combination will also be "?". This allows "?" to be used to mean "unknown". ]=] local function append_forms(props, formtable, slot, forms, before_text, before_text_no_links, before_text_translit) if not forms then return end local old_forms = formtable[slot] or {{form = ""}} local ret_forms = {} for _, old_form in ipairs(old_forms) do for _, form in ipairs(forms) do local old_form_vars = props.get_variants and props.get_variants(old_form.form) or "" local form_vars = props.get_variants and props.get_variants(form.form) or "" if old_form_vars ~= "" and form_vars ~= "" and old_form_vars ~= form_vars then -- Reject combination due to non-matching variant codes. else local new_formval local new_translit if old_form.form == "?" or form.from == "?" then new_formval = "?" else new_formval = old_form.form .. before_text .. form.form if old_form.translit or before_text_translit or form.translit then if not props.lang then error("Internal error: If manual translit is given, 'props.lang' must be set") end if not before_text_translit then before_text_translit = props_transliterate(props, before_text_no_links) or "" end local old_translit = old_form.translit or props_transliterate(props, m_links.remove_links(old_form.form)) or "" local translit = form.translit or props_transliterate(props, m_links.remove_links(form.form)) or "" new_translit = old_translit .. before_text_translit .. translit end end local new_formobj local new_footnotes = export.combine_footnotes(old_form.footnotes, form.footnotes) if new_formval == form.form and new_translit == form.translit then -- Automatically preserve metadata when possible. new_formobj = shallow_copy(form) new_formobj.footnotes = new_footnotes else new_formobj = {form=new_formval, translit=new_translit, footnotes=new_footnotes} if props.combine_metadata then props.combine_metadata { slot = slot, dest_form = new_formobj, form1 = old_form, form2 = form, between_text = before_text, between_text_no_links = before_text_no_links, between_text_translit = before_text_translit, } end end insert(ret_forms, new_formobj) end end end formtable[slot] = ret_forms end --[==[ Top-level inflection function. Create the inflections of a noun, verb, adjective or similar. `alternant_multiword_spec` is as returned by `parse_inflected_text` and describes the properties of the term to be inflected, including all the user-provided inflection specifications (e.g. the number, gender, conjugation/declension/etc. of each word) and the surrounding text. `props` indicates how to do the actual inflection (see below). The resulting inflected forms are stored into the `.forms` property of `multiword_spec`. This property holds a table whose keys are slots (i.e. ID's of individual inflected forms, such as "pres_1sg" for the first-person singular present indicative tense of a verb) and whose values are lists of the form `{ form = ``form``, translit = ``manual_translit_or_nil``, footnotes = ``footnote_list_or_nil``}`, where ``form`` is a string specifying the value of the form (e.g. "ouço" for the first-person singular present indicative of the Portuguese verb [[ouvir]]); ``manual_translit_or_nil`` is the corresponding manual transliteration if needed (i.e. if the form is in a non-Latin script and the automatic transliteration is incorrect or unavailable), otherwise nil; and ``footnote_list_or_nil`` is a list of footnotes to be attached to the form, or nil for no footnotes. Note that currently footnotes must be surrounded by brackets, e.g "[archaic]", and should not begin with a capital letter or end with a period. (Conversion from "[archaic]" to "Archaic." happens automatically.) This function has no return value, but modifies `multiword_spec` in-place, adding the `forms` table as described above. After calling this function, call show_forms() on the `forms` table to convert the forms and footnotes given in this table to strings suitable for display. `props` is an object specifying properties used during inflection, as follows: ```{ slot_list = {{"``slot``", "``accel``"}, {"``slot``", "``accel``"}, ...}, slot_table = {``slot`` = "``accel``", ``slot`` = "``accel``", ...}, skip_slot = nil `or` __function__(slot), lang = nil `or` __lang_object__, inflect_word_spec = __function__(base), get_variants = nil 'or` __function__(formval), combine_metadata = nil `or` __function__(data), include_user_specified_links = __boolean__, }``` `slot_list` is a list of two-element lists of slots and associated accelerator tags. ``slot`` is arbitrary but should correspond with slot names as generated by `inflect_word_spec`. ``accel`` is the corresponding accelerator tags; e.g. if ``slot`` is "pres_1sg", ``accel`` might be "1|s|pres|ind". ``accel`` is actually unused during inflection, but is used during `show_forms()`, which takes the same `slot_list` as a property upon input. `slot_table` is a table mapping slots to associated accelerator tags and serves the same function as `slot_list`. Only one of `slot_list` or `slot_table` must be given. For new code it is preferable to use `slot_list` because this allows you to control the order of processing slots, which may occasionally be important. `skip_slot` is a function of one argument, a slot name, and should return a boolean indicating whether to skip the given slot during inflection. It can be used, for example, to skip singular slots if the overall term being inflected is plural-only, and vice-versa. `lang` is a language object. This is only used to generate manual transliteration. If the language is written in the Latin script or manual transliteration cannot be specified in the input to parse_inflected_text(), this can be omitted. (Manual transliteration is allowed if the `lang` object is set in the `props` passed to parse_inflected_text().) `inflect_word_spec` is the function to do the actual inflection. It is passed a single argument, which is a ``word_spec`` object describing the word to be inflected and the user-provided inflection specifications. It is exactly the same as was returned by the `parse_indicator_spec` function provided in the `props` sent on input to `parse_inflected_text`, but has additional fields describing the word to be inflected and the surrounding text, as follows: ```{ lemma = "``lemma``", before_text = "``text-before-word``", before_text_no_links = "``text-before-word-no-links``", before_text_translit = "``manual-translit-of-text-before-word``" or nil (if no manual translit or respelling was specified in the before-text) -- Fields as described in parse_indicator_spec() ... }``` Here ``lemma`` is the word to be inflected as specified by the user (including any links if so given), and the `before_text*` fields describe the raw text preceding the word to be inflected. Any other fields in this object are as set by `parse_inflected_text`, and describe things like the gender, number, conjugation/declension, etc. as specified by the user in the <...> spec following the word to be inflected. `inflect_word_spec` should initialize the `.forms` property of the passed-in ``word_spec`` object to the inflected forms of the word in question. The value of this property is a table of the same format as the `.forms` property that is ultimately generated by inflect_multiword_or_alternant_multiword_spec() and described above near the top of this documentation: i.e. a table whose keys are slots and whose values are lists of the form `{ form = ``form``, translit = ``manual_translit_or_nil``, footnotes = ``footnote_list_or_nil``}`. `get_variants` is either {nil} or a function of one argument (a string, a form value). The purpose of this function is to ensure that in a multiword term where a given slot has more than one possible variant, the final output has only parallel variants in it. For example, feminine nouns and adjectives in Russian have two possible endings, one typically in -ой (-oj) and the other in -ою (-oju). If we have a feminine adjective-noun combination (or a hyphenated feminine noun-noun combination, or similar), and we don't specify `get_variants`, we'll end up with four values for the instrumental singular: one where both adjective and noun end in -ой, one where both end in -ою, and two where one of the words ends in -ой and the other in -ою. In general if we have N words each with K variants, we'll end up with an explosion of N^K possibilities. `get_variants` avoids this by returning a variant code (an arbitary string) for each variant. If two words each have a non-empty variant code, and the variant codes disagree, the combination will be rejected. If `get_variants` is not provided, or either variant code is an empty string, or the variant codes agree, the combination is allowed. The recommended way to use `get_variants` is as follows: 1. During inflection in `inflect_word_spec`, add a special character or string to each of the variants generated for a given slot when there is more than one. (As an optimization, do this only when there is more than one word being inflected.) Special Unicode characters can be used for this purpose, e.g. U+FFF0, U+FFF1, ..., U+FFFD, which have no meaning in Unicode. 2. Specify `get_variants` as a function that pulls out and returns the special character(s) or string included in the variant forms. 3. When calling show_forms(), specify a `canonicalize` function that removes the variant code character(s) or string from each form before converting to the display form. See [[Module:hi-verb]] and [[Module:hi-common]] for an example of doing this in a generalized fashion. (Look for add_variant_codes(), get_variants() and remove_variant_codes().) `combine_metadata` is a function that is invoked when combining two form objects along along with in-between text and storing into a destination form object. When this happens, if the the form value and translit in the first form object is empty and the in-between text is likewise empty (which regularly happens when appending the form object describing the first word in a multiword expression to empty base text), the second form object is simply shallow-copied along with all of its metadata, and any footnotes are combined appropriately (normally the first form object is such a case won't have footnotes). Otherwise, a new form object is constructed by combining the form values, translit and footnotes from the two objects and in-between text, and calling `combine_metadata` to combine any other metadata. Leave this unspecified if there is no additional metadata or if you don't want any metadata carried over. (Examples of metadata that should generally not be carried over are glosses of individual words, sense ID's and similar word-level properties that can't easily be combined to generate a multiword equivalent. Examples of metadata that should be carried over and combined are qualifiers, labels and certain boolean properties such as an uncertainty flag indicating that a given form is uncertain. For some metadata, it is more complex; for example, if both source words have the same gender or part of speech, the destination should keep that value, but if they differ, it may be safest to leave the field blank.) This function, if specified, is called with a single argument as follows: ```{ slot = "__string__", dest_form = __formobj__, form1 = __formobj__, form2 = __formobj__, between_text = "__string__", between_text_no_links = "__string__", between_text_translit = "__string__" `or` nil }``` Here, `slot` is the slot whose forms are being constructed. `dest_form` is the destination form object into which the combined metadata should be written, and is pre-populated with appropriate `form`, `translit` and `footnotes` fields. `form1` and `form2` are the two source forms being combined, and `between_text` is the text to be inserted between the two source forms. `between_text_no_links` is the same as `between_text` but with double-bracket links removed, and `between_text_translit` is the manual transliteration of `between_text_no_links`, if specified. The function should return nothing, but should side-effect `dest_form` as appropriate. `include_user_specified_links`, if given, ensures that user-specified links in the raw text surrounding a given word are preserved in the output. If omitted or set to false, such links will be removed and the whole multiword expression will be linked. ]==] function export.inflect_multiword_or_alternant_multiword_spec(multiword_spec, props) multiword_spec.forms = {} local is_alternant_multiword = not not multiword_spec.alternant_or_word_specs for _, word_spec in ipairs(is_alternant_multiword and multiword_spec.alternant_or_word_specs or multiword_spec.word_specs) do if word_spec.alternants then inflect_alternants(word_spec, props) else props.inflect_word_spec(word_spec) end iterate_slot_list_or_table(props, function(slot) if not props.skip_slot or not props.skip_slot(slot) then append_forms(props, multiword_spec.forms, slot, word_spec.forms[slot], (slot:find("linked") or props.include_user_specified_links) and word_spec.before_text or word_spec.before_text_no_links, word_spec.before_text_no_links, word_spec.before_text_translit ) end end) end if multiword_spec.post_text ~= "" then local pseudoform = {{form=""}} iterate_slot_list_or_table(props, function(slot) -- If slot is empty or should be skipped, don't try to append post-text. if (not props.skip_slot or not props.skip_slot(slot)) and multiword_spec.forms[slot] then append_forms(props, multiword_spec.forms, slot, pseudoform, (slot:find("linked") or props.include_user_specified_links) and multiword_spec.post_text or multiword_spec.post_text_no_links, multiword_spec.post_text_no_links, multiword_spec.post_text_translit ) end end) end end function export.map_word_specs(alternant_multiword_spec, fun) for _, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do if alternant_or_word_spec.alternants then for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do for _, word_spec in ipairs(multiword_spec.word_specs) do fun(word_spec) end end else fun(alternant_or_word_spec) end end end function export.create_footnote_obj() return { notes = {}, seen_notes = {}, noteindex = 1, seen_refs = {}, } end function export.get_footnote_text(footnotes, footnote_obj) if not footnotes then return "" end local link_indices = {} local all_refs = {} for _, footnote in ipairs(footnotes) do local refs footnote, refs = export.expand_footnote_or_references(footnote) if footnote then local this_noteindex = footnote_obj.seen_notes[footnote] if not this_noteindex then -- Generate a footnote index. this_noteindex = footnote_obj.noteindex footnote_obj.noteindex = footnote_obj.noteindex + 1 insert(footnote_obj.notes, '<sup style="color: var(--wikt-palette-red, red)">' .. this_noteindex .. '</sup>' .. footnote) footnote_obj.seen_notes[footnote] = this_noteindex end insert_if_not(link_indices, this_noteindex) end if refs then for _, ref in ipairs(refs) do if not ref.name then local this_refhash = footnote_obj.seen_refs[ref.text] if not this_refhash then -- Different text needs to have different auto-generated names, globally across the entire page, -- including across different invocations of {{it-verb}} or {{it-conj}}. The easiest way to accomplish -- this is to use a message-digest hashing function. It does not have to be cryptographically secure -- (MD5 is insecure); it just needs to have low probability of collisions. this_refhash = mw.hash.hashValue("md5", ref.text) footnote_obj.seen_refs[ref.text] = this_refhash end ref.autoname = this_refhash end -- I considered using "n" as the default group rather than nothing, to more clearly distinguish regular -- footnotes from references, but this requires referencing group "n" as <references group="n"> below, -- which is non-obvious. insert_if_not(all_refs, ref) end end end table.sort(link_indices) local function sort_refs(r1, r2) -- FIXME, we are now sorting on an arbitrary hash. Should we keep track of the order we -- saw the autonamed references and sort on that? if r1.autoname and r2.name then return true elseif r1.name and r2.autoname then return false elseif r1.name and r2.name then return r1.name < r2.name else return r1.autoname < r2.autoname end end table.sort(all_refs, sort_refs) for i, ref in ipairs(all_refs) do local refargs = {name = ref.name or ref.autoname, group = ref.group} all_refs[i] = mw.getCurrentFrame():extensionTag("ref", ref.text, refargs) end local link_text if #link_indices > 0 then link_text = '<sup style="color: var(--wikt-palette-red, red)">' .. concat(link_indices, ",") .. '</sup>' else link_text = "" end local ref_text = concat(all_refs) if link_text ~= "" and ref_text ~= "" then return link_text .. "<sup>,</sup>" .. ref_text else return link_text .. ref_text end end --[==[ Add links around words in a term. If multiword_only, do it only in multiword terms. ]==] function export.add_links(form, multiword_only) if form == "" or form == " " then return form end if not form:find("%[%[") then if ufind(form, "[%s%p]") then --optimization to avoid loading [[Module:headword]] on single-word forms local m_headword = require("Module:headword") if m_headword.head_is_multiword(form) then form = m_headword.add_multiword_links(form) end end if not multiword_only and not form:find("%[%[") then form = "[[" .. form .. "]]" end end return form end --[==[ Remove redundant link surrounding entire term. ]==] function export.remove_redundant_links(term) return (term:gsub("^%[%[([^%[%]|]*)%]%]$", "%1")) end --[==[ Add links to all before and after text; for use in inflection modules that preserve links in multiword lemmas and include links in non-lemma forms rather than allowing the entire form to be a link. If `remember_original`, remember the original user-specified before/after text so we can reconstruct the original spec later. `add_links` is a function of one argument to add links to a given piece of text; if unspecified, it defaults to `export.add_links`. ]==] function export.add_links_to_before_and_after_text(alternant_multiword_spec, remember_original, add_links) add_links = add_links or export.add_links local function add_links_remember_original(object, field) if remember_original then object["user_specified_" .. field] = object[field] end object[field] = add_links(object[field]) end for _, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do add_links_remember_original(alternant_or_word_spec, "before_text") if alternant_or_word_spec.alternants then for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do for _, word_spec in ipairs(multiword_spec.word_specs) do add_links_remember_original(word_spec, "before_text") end add_links_remember_original(multiword_spec, "post_text") end end end add_links_remember_original(alternant_multiword_spec, "post_text") end --[==[ Reconstruct the original overall spec from the output of parse_inflected_text(), so we can use it in the language-specific acceleration module in the implementation of {{tl|pt-verb form of}} and the like. `props` is an optional table of properties. Currently only `preprocess_angle_bracket_spec` is recognized, and is an optional function of one argument that is called to process an angle-bracket spec before inserting into the reconstructed spec. ]==] function export.reconstruct_original_spec(alternant_multiword_spec, props) local parts = {} props = props or {} local function ins(txt) insert(parts, txt) end local function insert_angle_bracket_spec(spec) if props.preprocess_angle_bracket_spec then spec = props.preprocess_angle_bracket_spec(spec) end ins(spec) end for _, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do ins(alternant_or_word_spec.user_specified_before_text) if alternant_or_word_spec.alternants then ins("((") for i, multiword_spec in ipairs(alternant_or_word_spec.alternants) do if i > 1 then ins(",") end for _, word_spec in ipairs(multiword_spec.word_specs) do ins(word_spec.user_specified_before_text) ins(word_spec.user_specified_lemma) insert_angle_bracket_spec(word_spec.angle_bracket_spec) end ins(multiword_spec.user_specified_post_text) end ins("))") else ins(alternant_or_word_spec.user_specified_lemma) insert_angle_bracket_spec(alternant_or_word_spec.angle_bracket_spec) end end ins(alternant_multiword_spec.user_specified_post_text) local retval = concat(parts) if alternant_multiword_spec.allow_default_indicator then -- As a special case, if we see e.g. "amar<>", remove the <>. Don't do this if there are spaces or alternants. if not retval:find(" ") and not retval:find("%(%(") then local retval_no_angle_brackets = retval:match("^(.*)<>$") if retval_no_angle_brackets then return retval_no_angle_brackets end end end return retval end --[==[ Convert the forms in ``formtable`` (a form table, whose keys are slots and whose values are lists of form objects, each of which is a table of the form `form = ``form``, translit = ``manual_translit_or_nil``, footnotes = ``footnote_list_or_nil``, no_accel = ``true_to_suppress_accelerators``, ... `) into strings. The form table is side-effected. Each form list turns into a string consisting of a comma-separated list of linked forms, with accelerators (unless `no_accel` is set in a given form object). If `include_translit` is specified, each string consists of a comma-separated list of form values (each formatted as a link), an HTML `&lt;br/>`, and a comma-separated list of transliterations. `props` is a table used in generating the strings, as follows: ```{ lang = __lang_object__, lemmas = {"``lemma``", "``lemma``", ...}, slot_list = {{"``slot``", "``accel``"}, {"``slot``", "``accel``"}, ...}, slot_table = {``slot`` = "``accel``", ``slot`` = "``accel``", ...}, include_translit = __boolean__, create_footnote_obj = nil `or` __function__(), canonicalize = nil or __function__(formval), preprocess_forms = nil `or` __function__(data), no_deduplicate_forms = __boolean__, combine_metadata_during_dedup = nil `or` __function__(data), transform_accel_obj = nil `or` __function__(slot, form, accel_obj), format_forms = nil `or` __function__(data), generate_link = nil `or` __function__(data), format_tr = nil `or` __function__(data), join_spans = nil `or` __function__(data), footnotes = nil or {"``extra_footnote``", "``extra_footnote``", ...}, }``` `lemmas` is the list of lemmas, used in the accelerators. `slot_list` is a list of two-element lists of slots and associated accelerator tag sets. ``slot`` should correspond to slots generated during `inflect_multiword_or_alternant_multiword_spec()`. ``accel`` is the corresponding accelerator tag set; e.g. if ``slot`` is "pres_1sg", ``accel`` might be "1|s|pres|ind". ``accel`` is used in generating entries for accelerator support (see [[WT:ACCEL]]). `slot_table` is a table mapping slots to associated accelerator tag sets and serves the same function as `slot_list`. Only one of `slot_list` or `slot_table` must be given. For new code it is preferable to use `slot_list` because this allows you to control the order of processing slots, which may occasionally be important. `include_translit`, if given, causes transliteration to be included in the generated strings. The function works as follows: # Create an object to hold footnotes (customizable using `create_footnote_obj`). # Generate the comma-separated lemma form values and store in `.lemma` in the form table. # Loop over the slots specified using `slot_list` or `slot_table`. For each slot: ## Canonicalize the form values (customizable using `canonicalize`; by default does nothing). ## Preprocess the forms (customizable using `preprocess_forms`; by default does nothing). ## Unless `no_deduplicate_forms` is set, deduplicate forms in a slot sharing the same form value but possibly different transliteration. (This happens e.g. in Russian, where it is relatively common for a given form to have two possible transliterations, one reflecting a more nativized pronunciation where Cyrillic е triggers palatalization of the preceding consonant, and one reflecting a more "foreign" pronunciation where this palatalization does not happen. In such a case, the automatic transliteration would normally suffice for the more nativized pronunciation but the more "foreign" pronunciation will need manual transliteration.) As part of deduplication, footnotes will be combined using `combine_footnotes`; distinct manual transliterations will be combined into a list (meaning the `translit` field of form objects in some subsequent `props` functions may hold a list; this will be noted when possible); and any remaining metadata will be combined using the `combine_metadata_during_dedup` method, if provided. ## Add acceleration to all forms. The acceleration tag set associated with a given form comes from `slot_list` or `slot_table`, i.e. all forms in a given slot have the same tag set. However, different forms will have different associated transliterations stored into the accelerator object associated with the form, as well as possibly different lemmas. In particular, when there are multiple lemma forms, this is often due to alternative ways to pronounce the lemma (e.g. alternative stress positions or vowel lengths), and there are often associated non-lemma forms that match each lemma. An example given in the introduction is {{m+|uk|русин||Rusyn}}, stressed in the lemma as ''ру́син'' or ''руси́н'' with associated genitive singulars ''ру́сина'' and ''русина́''. We would like the auto-generated accelerator entry for {{m|uk|русина}} to show the variant ''ру́сина'' as having lemma ''ру́син'' and the variant ''русина́'' as having the lemma ''руси́н'', rather than showing both variants as having both lemmas, which is less accurate. As a result, the code that generates acceleration objects for forms matches up forms and lemmas one-to-one if possible. If this is not possible, the matching is usually one lemma to many forms, as in {{m+|uk|міст||bridge}} with genitive singular ''мо́сту'' or ''моста́'' (in which case all forms get the same lemma), or many lemmas to one form, as in {{m+|uk|черга||turn, queue}} stressed either ''че́рга'' or ''черга́'' with nominative singular only ''че́рги'' (in which case the single form gets assicated all lemmas). If there are multiple lemmas and multiple forms, the algorithm attempts to align them as evenly as possible (e.g. two lemma variants to four forms means the first two forms get assigned the first lemma variant and the last two forms get assigned the second lemma variant); this is often going to be incorrect, but (a) there's unlikely to be a single algorithm that works in all such circumstances, and (b) these cases are very rare. Finally, note the following: ##* No acceleration is assigned to a form if any of the following apply: (a) there are no lemmas given in `props.lemmas`; (b) the `no_accel` key in the form object has a non-falsy value; (c) the form value of the form is {"?"} or an em-dash ({"—"}); (d) the accelerator tag set is given as a hyphen {"-"}); or (e) the form value contains an internal link. ##* The accelerator code sets the `formval_for_link` key in each form object to the version of the form value that should be passed to `full_link()` in [[Module:links]]. This is usually the same as the passed-in form value, but differs when the entire form value is surrounded with a redundant internal link (which is removed). ##* The resulting accelerator object can be modified (or replaced entirely) by the `transform_accel_obj` function. This is used, for example, in [[Module:es-verb]], [[Module:pt-verb]] and other Romance-language verb conjugation modules (likewise [[Module:ar-verb]]) to replace the tag set with the original verb spec used to generate the verb, so that the accelerator code can generate the appropriate call to {{tl|es-verb form of}}, {{tl|pt-verb form of}} or the like, which computes the inflections, instead of directly listing the inflections. ## Format the forms into strings. The entire default process can be replaced using `format_forms`; otherwise the default algorithm works as follows: ### Generate the '''form value spans''', with one entry (a linked HTML-ized version of the form value) per form. This can be customized using `generate_link`. (Various modules do this. For example, the Arabic verb module includes qualifiers, labels, ID's and the like that can be specified by the user; the Portuguese and reintegrated Galician verb modules italicize certain superseded or otherwise less-desirable forms instead of linking them normally; the German verb module adds {{m|de|dass}} to subjunctive forms and optional pronouns to imperative forms; and the German adjective module adds articles to adjective forms normally accompanied by articles and the equivalent of "he/she is" etc. to predicate forms.) The default uses `full_link()` in [[Module:links]] (with transliteration generation disabled) concatenated with the appropriate footnote symbol(s) (if any). ### Generate the '''transliteration spans''', with one entry per distinct translit, auto-generated if manual translit isn't available. Note that, due to the earlier form value deduplication step, there may be multiple translits per form object. These translits are themselves deduplicated to get the list of spans. (Such duplication can happen, for example, in Arabic with terms containing a glottal stop in them; there may be multiple ways of spelling the glottal stop or ''hamza'' in Arabic, but only one way of transliterating it.) Each span consists of an object specifying the translit and the list of footnotes. These objects are then converted to formatted strings, either using `format_tr` if supplied or else calling `tag_translit()` in [[Module:script utilities]] and concatenating the appropriate footnote symbol(s) (if any). ### Combine the form value and transliteration spans. If `join_spans` is supplied, use it; otherwise, concatenate the form value spans (comma-separated) and (if available) transliteration spans (comma-separated), and (if appropriate) combine them using {<br />}. `create_footnote_obj` is an optional function of no arguments to create the footnote object used to track footnotes; see `create_footnote_obj()`. Customizing it is useful to prepopulate the footnote table using `get_footnote_text()`. `canonicalize` is an optional function of one argument (a form value) to canonicalize each form before processing; it can return nil for no change. The most common purpose of this function is to remove variant codes from the form value. See the documentation for `inflect_multiword_or_alternant_multiword_spec()` for a description of variant codes and their purpose. `preprocess_forms` is an optional function of one argument (a table of properties) to preprocess the form objects as a whole. It runs after `canonicalize` (meaning that the form values passed in are canonicalized) and before deduplication and the addition of acceleration info. The property table passed in has the following properties: * `slot`: The slot being processed. * `forms`: The list of form objects for this slot. * `accel_tag_set`: The accelerator tag set for this slot, taken from `slot_list` or `slot_table`. * `footnote_obj`: The footnote object returned by the `create_footnote_obj` property or the default `create_footnote_obj()` function. `preprocess_forms` should return a list of preprocessed form objects, or {nil} to use the passed-in `forms`. If this function does deduplication, you should set `no_deduplicate_forms` to disable the default deduplication process. `no_deduplicate_forms`, if set, disables the deduplication step (see above). `combine_metadata_during_dedup` is an optional function of one argument (a table of properties) to combine the metadata of deduplicated form objects. The property table passed in has the following properties: * `slot`: The slot being processed. * `existing_form`: The existing form object into which a duplicated form is being combined. * `dup_form`: The duplicated form being combined into `existing_form`. * `existing_form_pos`: The one-based position of the existing form in the deduplicated form list (not necessarily its original position). * `dup_form_pos`: The one-based position of the duplicated form in its original list. The following should be noted about the form objects passed in: # The form values in `.form` have been canonicalized using `.canonicalize`, if provided. # The form values in `existing_form` and `dup_form` are always the same. # The footnotes in `existing_form` have already been combined with those in `dup_form`. # If there was manual translit either in `existing_form` (prior to deduplication) or in `dup_form`, there will be manual translit in `existing_form.translit` that is a list and combines any previous accumulated translits in `existing_form` as well as the translit in `dup_form` (even if one of them was specified as {nil} indicating an automatic translit). This means that the translit in `existing_form.translit` is always either {nil} or a list of strings (and the same applies to `dup_form.translit`). `transform_accel_obj` is an optional function of three arguments (``slot``, ``formobj``, ``accel_obj``) to transform the default constructed accelerator object in ``accel_obj`` into an object that should be passed to `full_link()` in [[Module:links]]. It should return the new accelerator object, or {nil} for no acceleration. (If {nil} is returned, the corresponding form has no acceleration; this is unlike most customization functions, where returning {nil} causes the default algorithm to be invoked.) The function can destructively modify the accelerator object passed in. '''NOTE''': This is called even when the passed-in ``accel_obj`` is {nil} (see the (a) through (e) reasons above why no acceleration may be assigned to a form). Thus, your code needs to do something sensible in this case. The description above of how `show_forms()` works inclues various examples of modules that supply a `transform_accel_obj` function and the reasons for doing so. `format_forms`, if supplied, is a function that entirely replaces the formatting portion of `show_forms()`. An example of why you might want to do this is to get a different layout than the default, e.g. one where translit is displayed next to each form value instead of the form values and translits grouped and displayed on separate lines. Under normal circumstances, you should not do this, but instead customize the functions that replace specific parts of the default formatting algorithm (see below). This function is passed one argument (a table of properties) and should return a string (the formatted forms, ready to store into the slot in the form table) or {nil} to proceed with the default algorithm (see above). The property table passed in has the following properties: * `slot`: The slot being processed. * `forms`: The list of form objects, deduplicated and with accelerator info added. * `footnote_obj`: The footnote object returned by the `create_footnote_obj` property or the default `create_footnote_obj()` function. The following should be noted about the form objects in `forms`: # There are extra fields `formval_for_link` and `accel_obj`. The first one is as described above under the paragraph beginning "Add acceleration to all forms" under "The function works as follows". The second one is the accelerator object in the format expected by [[Module:links]]. # The `translit` field, if non-{nil}, is a list of transliterations rather than a single transliteration; this is due to the form value deduplication step. `generate_link` is an optional function to generate the link text for a given form value. It is passed a single argument (a table of properties) and should return a string, the formatted link. If it returns {nil}, the default algorithm (see above) is invoked. The property table passed in has the following properties: * `slot`: The slot being processed. * `form`: The form to be converted to a formatted link. As with the `format_forms` function described above, the form objects passed in contain extra fields `formval_for_link` and `accel_obj` (both of which will normally be used), and the `translit` field, if non-{nil}, is a list. * `pos`: The one-based position of the form being processed, in the list of form value spans. Rarely used. * `footnote_obj`: The footnote object returned by the `create_footnote_obj` property or the default `create_footnote_obj()` function. Normally used in order to get the footnote symbol associated with any footnotes in `footnotes`. The description above of how `show_forms()` works inclues various examples of modules that supply a `generate_link` function and the reasons for doing so. `format_tr` is an optional function to generate the formatted text for a given transliteration. It is passed a single argument (a table of properties) and should return a string, the formatted transliteration text. If it returns {nil}, the default algorithm (see above) is invoked. The property table passed in has the following properties: * `slot`: The slot being processed. * `tr_for_tag`: The transliteration to process. * `pos`: The one-based position of the transliteration being processed, in the list of transliteration spans. Rarely used. * `footnotes`: The list of footnotes associated with all form objects with this transliteration. (If there were multiple form objects with the same transliteration, the list of footnotes will have been generated using `combine_footnotes()`.) * `footnote_obj`: The footnote object returned by the `create_footnote_obj` property or the default `create_footnote_obj()` function. Normally used in order to get the footnote symbol associated with any footnotes in `footnotes`. `join_spans` is an optional function to join the processed form value and transliteration spans into a formatted string. It is passed a single argument (a table of properties) and should return the final string to store into the form table slot. If it returns {nil}, the default algorithm (see above) is invoked. The property table passed in has the following properties: * `slot`: The slot being processed. * `formval_spans`: A list of strings, the formatted form value spans. * `tr_spans`: A list of strings, the formatted transliteration spans. If there is no transliteration, this will be an empty list. A custom `join_spans` is provided by [[Module:de-verb]], which concatenates the form value spans vertically (using {"<br />"}) instead of horizontally using a comma, as is normal; this is because there is no translit and the form values are often long, containing extra words attached during `generate_link()`. The only exception is the `aux` slot holding the auxiliaries, which is concatenated horizontally using {" or "}. [[Module:de-adjective]] similarly provides a custom `join_spans` function that concatenates the form value spans vertically. ]==] function export.show_forms(formtable, props) local footnote_obj = props.create_footnote_obj and props.create_footnote_obj() or export.create_footnote_obj() local function fetch_formval_and_translit(entry, remove_links) local formval, translit if type(entry) == "table" then formval, translit = entry.form, entry.translit else formval = entry end if remove_links then formval = m_links.remove_links(formval) end return formval, translit end local lemma_formvals = {} for _, lemma in ipairs(props.lemmas) do local lemma_formval, _ = fetch_formval_and_translit(lemma) insert_if_not(lemma_formvals, lemma_formval) end formtable.lemma = #lemma_formvals > 0 and concat(lemma_formvals, ", ") or mw.loadData(headword_data_module).pagename -- For safety, since we in-place modify `lemmas` usually before processing a given slot, make a copy. local props_lemmas = shallow_copy(props.lemmas) for i, lemma in ipairs(props_lemmas) do props_lemmas[i] = shallow_copy(lemma) end local function do_slot(slot, accel_tag_set) local formobjs = formtable[slot] if formobjs then if type(formobjs) ~= "table" then error("Internal error: For slot '" .. slot .. "', expected table but saw " .. dump(formobjs)) end -- Maybe canonicalize the form values (e.g. remove variant codes and monosyllabic accents). if props.canonicalize then for _, form in ipairs(formobjs) do form.form = props.canonicalize(form.form) or form.form end end -- Preprocess the forms as a whole if called for. if props.preprocess_forms then formobjs = props.preprocess_forms { slot = slot, forms = formobjs, accel_tag_set = accel_tag_set, footnote_obj = footnote_obj, } or formobjs end -- Maybe deduplicate form values (happens e.g. in Russian with two terms with the same Russian form but -- different translits). if not props.no_deduplicate_forms then local deduped_formobjs = {} for i, form in ipairs(formobjs) do local function combine_forms(existing_form, dup_form, pos) assert(existing_form.form == dup_form.form) -- Combine footnotes. existing_form.footnotes = export.combine_footnotes(existing_form.footnotes, dup_form.footnotes) -- If translit is being generated, and there's manual translit associated with either form, we -- need to generate any missing translits and combine them, taking into account the fact that a -- translit value may actually be a list of translits (particularly with the existing form if we -- already combined an item with manual translit into it). if props.include_translit and form_value_transliterable(existing_form.form) and ( existing_form.translit or dup_form.translit) then local combined_translit if not existing_form.translit then combined_translit = { props_transliterate(props, m_links.remove_links(existing_form.form)) } elseif type(existing_form.translit) == "string" then combined_translit = {existing_form.translit} else combined_translit = existing_form.translit end local dup_form_translit = dup_form.translit if not dup_form_translit then -- dup_form.form is the same as existing_form.form (see assert above), but this is -- defensive programming in case that changes dup_form_translit = {props_transliterate(props, m_links.remove_links(dup_form.form))} elseif type(dup_form_translit) == "string" then dup_form_translit = {dup_form_translit} end for _, translit in ipairs(dup_form_translit) do insert_if_not(combined_translit, translit) end existing_form.translit = combined_translit end if props.combine_metadata_during_dedup then props.combine_metadata_during_dedup { slot = slot, existing_form = existing_form, existing_form_pos = pos, dup_form = dup_form, dup_form_pos = i, } end end insert_if_not(deduped_formobjs, form, { key = function(formobj) return formobj.form end, combine = combine_forms, }) end formobjs = deduped_formobjs end -- Add acceleration info to form objects. for i, form in ipairs(formobjs) do local formval = form.form if not form_value_transliterable(formval) then form.formval_for_link = formval else local formval_for_link formval_for_link = formval -- remove redundant link surrounding entire form formval_for_link = export.remove_redundant_links(formval_for_link) form.formval_for_link = formval_for_link -------------------- Compute the accelerator object. ----------------- local accel_obj -- Check if form still has links; if so, don't add accelerators because the resulting entries will -- be wrong. if props_lemmas[1] and not form.no_accel and accel_tag_set ~= "-" and not formval_for_link:find("%[%[") then -- If there is more than one form or more than one lemma, things get tricky. Often, there are -- the same number of forms as lemmas, e.g. for Ukrainian [[зимовий]] "wintry; winter (rel.)", -- which can be stressed зимо́вий or зимови́й with corresponding masculine/neuter genitive -- singulars зимо́вого or зимово́го etc. In this case, usually the forms and lemmas match up so -- we do this. If there are different numbers of forms than lemmas, it's usually one lemma -- against several forms e.g. Ukrainian [[міст]] "bridge" with genitive singular мо́сту or моста́ -- (accent patterns b or c) or [[ложка|ло́жка]] "spoon" with nominative plural ло́жки or ложки́ -- (accent patterns a or c). Here, we should assign the same lemma to both forms. The opposite -- can happen, e.g. [[черга]] "turn, queue" stressed че́рга or черга́ with nominative plural only -- че́рги (accent patterns a or d). Here we should assign both lemmas to the same form. In more -- complicated cases, with more than one lemma and form and different numbers of each, we try -- to align them as much as possible, e.g. if there are somehow eight forms and three lemmas, -- we assign lemma 1 to forms 1-3, lemma 2 to forms 4-6 and lemma 3 to forms 7 and 8, and -- conversely if there are somehow three forms and eight lemmas. This is likely to be wrong, but -- (a) there's unlikely to be a single algorithm that works in all such circumstances, and (b) -- these cases are vanishingly rare or nonexistent. Properly we should try to remember which -- form was generated by which lemma, but that is significant extra work for little gain. local first_lemma, last_lemma if #formobjs >= #props_lemmas then -- More forms than lemmas. Try to even out the forms assigned per lemma. local forms_per_lemma = math.ceil(#formobjs / #props_lemmas) first_lemma = math.floor((i - 1) / forms_per_lemma) + 1 last_lemma = first_lemma else -- More lemmas than forms. Try to even out the lemmas assigned per form. local lemmas_per_form = math.ceil(#props_lemmas / #formobjs) first_lemma = (i - 1) * lemmas_per_form + 1 last_lemma = math.min(first_lemma + lemmas_per_form - 1, #props_lemmas) end local accel_lemma, accel_lemma_translit if first_lemma == last_lemma then accel_lemma, accel_lemma_translit = fetch_formval_and_translit(props_lemmas[first_lemma], "remove links") else accel_lemma = {} accel_lemma_translit = {} for j=first_lemma, last_lemma do local this_accel_lemma, this_accel_lemma_translit = fetch_formval_and_translit(props_lemmas[j], "remove links") -- Do not use insert() especially for the translit because it may be nil and in -- that case we want gaps in the array. accel_lemma[j - first_lemma + 1] = this_accel_lemma accel_lemma_translit[j - first_lemma + 1] = this_accel_lemma_translit end end local accel_translit if props.include_translit and form.translit then if type(form.translit) == "table" then accel_translit = concat(form.translit, ", ") elseif type(form.translit) == "string" then accel_translit = form.translit else error(("Internal error: For slot '%s', form translit is not a table or string: %s"): format(slot, dump(form.translit))) end end accel_obj = { form = accel_tag_set, translit = accel_translit, lemma = accel_lemma, lemma_translit = props.include_translit and accel_lemma_translit or nil, } end -- Postprocess if requested. if props.transform_accel_obj then accel_obj = props.transform_accel_obj(slot, form, accel_obj) end form.accel_obj = accel_obj end end -- Format the form objects into a string for insertion into the table. local formatted_forms if props.format_forms then formatted_forms = props.format_forms { slot = slot, forms = formobjs, footnote_obj = footnote_obj, } end if not formatted_forms then -- Default algorithm: Separate form values and translits and concatenate on separate lines. -- Form values have already been deduplicated but we may need to deduplicate translits (this happens -- e.g. in Arabic where there may be multiple ways of spelling a hamza in the Arabic script but only -- one way in transliteration). local formval_spans = {} local tr_spans = {} for i, form in ipairs(formobjs) do local link if props.generate_link then link = props.generate_link { slot = slot, pos = i, form = form, footnote_obj = footnote_obj, } end if not link then link = m_links.full_link { lang = props.lang, term = form.formval_for_link, tr = "-", accel = form.accel_obj } .. export.get_footnote_text(form.footnotes, footnote_obj) end formval_spans[i] = link if props.include_translit then local translits = form.translit or props_transliterate(props, m_links.remove_links(form.form)) if type(translits) == "string" then translits = {translits} end for _, tr in ipairs(translits) do local tr_for_tag = tr -- formerly may have differed due to old-style footnote symbols insert_if_not(tr_spans, { tr_for_tag = tr_for_tag, footnotes = form.footnotes, }, { key = function(trobj) return trobj.tr_for_tag end, combine = function(trobj, newtrobj) -- Combine footnotes. trobj.footnotes = export.combine_footnotes(trobj.footnotes, newtrobj.footnotes) end, }) end end end for i, tr_span in ipairs(tr_spans) do local formatted_tr if props.format_tr then formatted_tr = props.format_tr { slot = slot, pos = i, tr_for_tag = tr_span.tr_for_tag, footnotes = tr_span.footnotes, footnote_obj = footnote_obj, } end if not formatted_tr then formatted_tr = require(script_utilities_module).tag_translit(tr_span.tr_for_tag, props.lang, "default", " style=\"color: var(--wikt-palette-grey-8,#888);\"") .. export.get_footnote_text(tr_span.footnotes, footnote_obj) end tr_spans[i] = formatted_tr end if props.join_spans then formatted_forms = props.join_spans { slot = slot, formval_spans = formval_spans, tr_spans = tr_spans, } end if not formatted_forms then local formval_span = concat(formval_spans, ", ") local tr_span if #tr_spans > 0 then tr_span = concat(tr_spans, ", ") end if tr_span then formatted_forms = formval_span .. "<br />" .. tr_span else formatted_forms = formval_span end end end formtable[slot] = formatted_forms else formtable[slot] = "—" end end iterate_slot_list_or_table(props, do_slot) formtable.footnote = concat(footnote_obj.notes, "<br />") end --[==[ Find the inflection spec for a given lemma by scraping the contents of the section for the lemma's language, looking for the appropriate inflection template (which may in some cases be a headword template with inflections specified in the headword, as for {{tl|en-verb}}). An inflection ID can be given; in that case, the value of the appropriate ID param (usually `id=`) must match the specified ID; otherwise, any inflection template call of the right name will work. If anything goes wrong in the process, a string is returned describing the error message; otherwise an object is returned, where `template` is the template found (an object of the form returned by `find_templates` in [[Module:template parser]]). There is one param, an object with the following fields: * `lemma`: The page to scrape for inflection calls. * `langname`: The name of the language of the lemma (e.g. {"English"} or {"Icelandic"}). Only that section is scraped. * `infltemp`: A string specifying the name of the template containing the inflections, or a function of one argument, the template object (an object of the form returned by `find_templates` in [[Module:template parser]]), which should return true if the given object specifies an appropriate inflection template. * `idparam`: The name of the parameter holding the inflection ID used to disambiguate lemmas with multiple inflections. * `inflid`: The inflection ID that must match the value of the `idparam` parameter specified by the inflection template. If not specified, there must be exactly one matching inflection template present, and it must not have a value given for the `idparam` parameter. Otherwise, all matching inflection templates must have a value specified for the `idparam` parameter of the template, and there must be exactly one template whose `idparam` parameter value is the same as the value of `inflid`. Find the inflection spec by scraping the contents of the Icelandic section of `lemma`, looking for `infltemp` calls (where template is e.g. "is-ndecl", "is-adecl" or "is-conj") If `inflid` is given, it must match the value of the |id= param specified to the inflection template; otherwise, any inflection template call will work. If anything goes wrong in the process, a string is returned describing the error message; otherwise a table of inflections is returned, each containing a field `infl` with the inflection spec. The inflection spec comes from the |deriv=, |deriv2=, etc. params in the inflection template if specified and `is_deriv` is given; otherwise from 1=, 2=, etc. If `allow_empty_infl` is given, a missing inflection spec in 1= is allowed and converted to an empty string; otherwise, an error is signaled. ]==] function export.scrape_inflection(data) local langname, lemma, infltemp, inflid, idparam = data.langname, data.lemma, data.infltemp, data.inflid, data.idparam local lang_lemma = ("%s base lemma '[[%s]]'"):format(langname, lemma) local title = new_title(lemma) if title then local content = title:getContent() if content then local langtext = require(pages_module).get_section(content, langname) if langtext then local inflobjs_by_id = {} local inflobjs_without_id = {} local ordered_seen_ids = {} for template in require(template_parser_module).find_templates(langtext) do local matches if type(infltemp) == "string" then matches = template:get_name() == infltemp else matches = infltemp(template) end if matches then local inflobj = {template = template} local args = template:get_arguments() local tid = args[idparam] if tid then if inflobjs_by_id[tid] then return ("For %s base lemma '[[%s]]', saw id='%s' twice"):format( langname, lemma, tid ) end inflobjs_by_id[tid] = inflobj insert_if_not(ordered_seen_ids, tid) else insert(inflobjs_without_id, inflobj) end end end local function concat_ordered_seen_ids() local quoted_seen_ids = {} for _, seen_id in ipairs(ordered_seen_ids) do insert(quoted_seen_ids, "'" .. seen_id .. "'") end return m_table.serialCommaJoin(quoted_seen_ids, {dontTag = true}) end local formatted_template = type(infltemp) == "string" and ("[[Template:%s]]"):format(infltemp) or "inflection template" local formatted_template_sing_article = type(infltemp) == "string" and ("[[Template:%s]]"):format(infltemp) or "an inflection template" local formatted_template_plural = type(infltemp) == "string" and "'s" or "s" if ordered_seen_ids[1] and inflobjs_without_id[1] then return ("For %s, saw %s %s%s with id=%s as well as %s %s%s without ID; this is not allowed; " .. "with multiple %s calls, all must have id= params"):format(lang_lemma, #ordered_seen_ids, formatted_template, ordered_seen_ids[2] and formatted_template_plural or "", concat_ordered_seen_ids(), #inflobjs_without_id, formatted_template, inflobjs_without_id[2] and formatted_template_plural or "", formatted_template) elseif not ordered_seen_ids[1] and not inflobjs_without_id[1] then return ("For %s, found %s section but couldn't find any calls to %s"):format( lang_lemma, langname, formatted_template_sing_article) elseif #inflobjs_without_id > 1 then return ("For %s, found %s %s%s without ID's; this is not allowed; with multiple %s calls, " .. "all must have id= params"):format(lang_lemma, #inflobjs_without_id, formatted_template, formatted_template_plural, formatted_template) elseif inflid then if inflobjs_by_id[inflid] then return inflobjs_by_id[inflid] elseif ordered_seen_ids[1] then return ("For %s, found %s section but couldn't find any inflections matching ID '%s'; " .. "instead found ID's %s; you may have misspelled the ID"):format(lang_lemma, langname, inflid, concat_ordered_seen_ids()) else return ("For %s, found %s section with a single %s without ID, but ID requirement '%s' " .. "specified; consider removing the ID requirement"):format(lang_lemma, langname, formatted_template, inflid) end elseif inflobjs_without_id[1] then -- only one infl template, and it doesn't have an id=; return it return inflobjs_without_id[1] elseif ordered_seen_ids[2] then return ("For %s, saw %s %s%s with id=%s but with a request to return the inflection without " .. "ID; consider adding an ID restriction to the scrape request, e.g. '@@:%s' for " .. "self-scraping or '@%s:%s' for scraping from another page"):format(lang_lemma, #ordered_seen_ids, formatted_template, formatted_template_plural, concat_ordered_seen_ids(), ordered_seen_ids[1], usub(lemma, 1, 1), ordered_seen_ids[1]) else return ("For %s, found %s section with a single %s with id=%s, but the scrape request didn't " .. "specify an ID; consider removing the id= param from the %s call unless you expect to add " .. "more such calls to the page in the future (in which case add the ID to the scrape request, " .. "e.g. '@@:%s' for self-scraping or '@%s:%s' for scraping from another page"):format(lang_lemma, langname, formatted_template, concat_ordered_seen_ids(), formatted_template, ordered_seen_ids[1], usub(lemma, 1, 1), ordered_seen_ids[1]) end else return ("For %s, page exists but has no %s section"):format(lang_lemma, langname) end else return ("For %s, couldn't fetch contents for page; page may not exist"):format(lang_lemma) end else return ("Bad %s; couldn't create title object"):format(lang_lemma) end end return export nv8di3n6mnnnmvmo7fjstb2viyp4j22 Module:yesno 828 8098 54973 2026-09-30T17:40:07Z Deadend0914 7211 Gesceop tramet þe hafaþ '-- Function allowing for consistent treatment of boolean-like wikitext input. -- It works similarly to the template {{yesno}}. local lower = string.lower local tonumber = tonumber local type = type local yesno return function (val, default) if val == nil then return nil elseif not yesno then yesno = { [true] = true, [false] = false, ["true"] = true, ["false"] = false, ["t"] = true, ["f"] = false, [1] = true, [0] = false, ["1"] = true, [...' 54973 Scribunto text/plain -- Function allowing for consistent treatment of boolean-like wikitext input. -- It works similarly to the template {{yesno}}. local lower = string.lower local tonumber = tonumber local type = type local yesno return function (val, default) if val == nil then return nil elseif not yesno then yesno = { [true] = true, [false] = false, ["true"] = true, ["false"] = false, ["t"] = true, ["f"] = false, [1] = true, [0] = false, ["1"] = true, ["0"] = false, ["yes"] = true, ["no"] = false, ["y"] = true, ["n"] = false, ["on"] = true, ["off"] = false, } end local ret = yesno[val] if ret ~= nil then return ret elseif type(val) ~= "string" then return default end -- Catch inputs like "00". ret = yesno[tonumber(val)] if ret ~= nil then return ret end -- Case-insensitive. ret = yesno[lower(val)] if ret ~= nil then return ret end return default end 50fw3j6qj6cod4hrcb5leyd15zs34hg Module:JSON 828 8099 54974 2026-09-30T17:40:42Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local m_math = require("Module:math") local m_str_utils = require("Module:string utilities") local m_table = require("Module:table") local codepoint = m_str_utils.codepoint local concat = table.concat local converter -- forward declaration local find = string.find local format = string.format local gsub = string.gsub local insert = table.insert local ipairs = ipairs local is_array = m_table.isArray local is_finite_real_number = m_math.is_finit...' 54974 Scribunto text/plain local export = {} local m_math = require("Module:math") local m_str_utils = require("Module:string utilities") local m_table = require("Module:table") local codepoint = m_str_utils.codepoint local concat = table.concat local converter -- forward declaration local find = string.find local format = string.format local gsub = string.gsub local insert = table.insert local ipairs = ipairs local is_array = m_table.isArray local is_finite_real_number = m_math.is_finite_real_number local is_integer = m_math.is_integer local is_utf8 = mw.ustring.isutf8 local match = string.match local toNFC = mw.ustring.toNFC local tonumber = tonumber local pairs = pairs local sorted_pairs = m_table.sortedPairs local trycall = require("Module:fun/trycall") local type = type local function json_fromBoolean(b) return b and "true" or "false" end -- Given a finite real number x, returns a string containing its JSON -- representation, with enough precision that it should round-trip correctly -- (depending on the well-behavedness of the system on the other end). local function json_fromNumber(x, level) if not is_finite_real_number(x) then error(format("Cannot encode non-finite real number %g", x), level) end -- Give integers within the range RFC 7159 considers interoperable. if is_integer(x) and x < 0x1p53 and x > -0x1p53 then return format("%d", x) end -- Otherwise, give a (double) float with the %g specifier, which handles any -- leading/trailing 0s etc. Double floats have precision ranging from 15 to -- 17 digits, meaning rounding artefacts can appear when precision is set to -- 16 or 17 (e.g. 1.1 is converted to 1.1000000000000001). Avoid this by -- trying each in turn, returning the first one which converts back into the -- original number, which avoids implying that it has higher precision than -- it really does. for prec = 15, 17 do local poss = format(format("%%.%dg", prec), x) if prec == 17 or tonumber(poss) == x then x = poss break end end -- If there's an exponent, remove any + sign and leading 0s from it. if find(x, "e", nil, true) then return (gsub(x, "(e%-?)%+?0*", "%1")) end -- If it resembles an integer, convert it to scientific notation to avoid -- the other end interpreting it as one. local d, f = match(x, "^(%d)(%d-)0*$") return d and format("%s%s%se%d", d, f == "" and "" or ".", f, #x - 1) or x end local function escape_codepoint(cp) if cp < 0x10000 then return format("\\u%04X", cp) end cp = cp - 0x10000 return format("\\u%04X\\u%04X", 0xD800 + (cp / 1024), 0xDC00 + (cp % 1024)) end local escapes local function get_escapes() escapes, get_escapes = { [0x8] = [[\b]], [0x9] = [[\t]], [0xA] = [[\n]], [0xC] = [[\f]], [0xD] = [[\r]], [0x22] = [[\"]], [0x2F] = [[\/]], [0x5C] = [[\\]], }, nil local function _add(cp) if escapes[cp] == nil then escapes[cp] = escape_codepoint(cp) end end local function add(cp1, cp2) if cp2 == nil then return _add(cp1) end for cp = cp1, cp2 do _add(cp) end end add(0x0000, 0x001F) add(0x007F, 0x00A0) add(0x00AD) add(0x034F) add(0x0600, 0x0605) add(0x061C) add(0x06DD) add(0x070F) add(0x0890, 0x0891) add(0x08E2) add(0x115F, 0x1160) add(0x1680) add(0x17B4, 0x17B5) add(0x180B, 0x180F) add(0x2000, 0x200F) add(0x2028, 0x202F) add(0x205F, 0x206F) add(0x3000) add(0x3164) add(0xFDD0, 0xFDEF) add(0xFE00, 0xFE0F) add(0xFEFF) add(0xFFA0) add(0xFFF0, 0xFFFF) add(0x110BD) add(0x110CD) add(0x1107F) add(0x13430, 0x1343F) add(0x16FE4) add(0x1BC9D) add(0x1BCA0, 0x1BCA3) add(0x1D173, 0x1D17A) for i = 0x2, 0x11 do i = i * 0x10000 add(i - 2, i - 1) end return escapes end local function escape_char(ch) local cp = codepoint(ch) return (escapes or get_escapes())[cp] or escape_codepoint(cp) end local function maybe_escape_char(ch) local cp = codepoint(ch) if cp >= 0xE0000 and cp <= 0xE0FFF then return escape_char(ch) end return (escapes or get_escapes())[cp] or ch end -- Given a string, escapes any illegal characters and wraps it in double-quotes. -- Raises an error if the string is not valid UTF-8. local function json_fromString(s, ascii, level) if not is_utf8(s) then error(format("Cannot encode non-UTF-8 string '%s'", s), level) end local pattern = '[%c"/\\\128-\255][\128-\191]*' if not ascii then local escaped = gsub(s, pattern, maybe_escape_char) if escaped == toNFC(escaped) then return '"' .. escaped .. '"' end end return '"' .. gsub(s, pattern, escape_char) .. '"' end local function json_fromTable(t, opts, current, level) local ret, open, close = {} if is_array(t) then for key, value in ipairs(t) do ret[key] = converter(value, opts, current, level + 1) or "null" end open, close = "[", "]" else -- `seen_keys` memoizes keys already seen, to prevent collisions (e.g. 1 -- and "1"). local seen_keys, colon, ascii = {}, opts.compress and ":" or " : ", opts.ascii for key, value in (opts.sort_keys and sorted_pairs or pairs)(t) do local key_type = type(key) if key_type == "boolean" then key = json_fromBoolean(key) elseif key_type == "number" then key = json_fromNumber(key, level + 1) elseif key_type ~= "string" then error(format("Cannot use type '%s' as a table key", key_type), level) end key = json_fromString(key, ascii, level + 1) if seen_keys[key] then error(format("Collision for JSON key %s", key), level) end seen_keys[key] = true insert(ret, key .. colon .. (converter(value, opts, current, level + 1) or "null")) end open, close = "{", "}" end ret = open .. ( opts.compress and concat(ret, ",") .. close or " " .. concat(ret, ", ") .. ( #ret == 0 and "" or " " ) .. close ) current[t] = nil return ret end function converter(this, opts, current, level) -- local declared above local val_type = type(this) if val_type == "nil" then return "null" elseif val_type == "boolean" then return json_fromBoolean(this) elseif val_type == "number" then return json_fromNumber(this, level + 1) elseif val_type == "string" then return json_fromString(this, opts.ascii, level + 1) elseif val_type ~= "table" then error(format("Cannot encode type '%s'", val_type), level) elseif current[this] then error("Cannot use recursive tables", level) end -- Memoize the table to enable recursion checking. current[this] = true if opts.ignore_toJSON then return json_fromTable(this, opts, current, level + 1) end -- Check if a toJSON method can be used. Use the lua_table flag to get a Lua -- table, as any options need to be applied to the output. local to_json = this.toJSON if to_json == nil then return json_fromTable(this, opts, current, level + 1) end -- Try to call it. local success, new = trycall(to_json, this, {lua_table = true}) if success then -- If successful, use the returned value. local ret = converter(new, opts, current, level + 1) current[this] = nil return ret end -- Otherwise, treat as a conventional value. return json_fromTable(this, opts, current, level + 1) end -- This function makes an effort to convert an arbitrary Lua value to a string -- containing a JSON representation of it. function export.toJSON(this, opts) return converter(this, opts == nil and {} or opts, {}, 3) end return export 2ak4p5c82rula80h5a4l22jokudmyrq Module:references 828 8100 54975 2026-09-30T17:41:12Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local rsplit = mw.text.split --[==[ Parse a references spec as used in the {{para|ref}} param to {{tl|IPA}}, {{tl|IPAchar}}, {{tl|homophones}}, {{tl|rhymes}}, etc. and soon the {{para|f<var>N</var>ref}} param to {{tl|head}}. `parse_err` is a function of one argument to throw an error with the specified argument as the error message; defaults to `error`. Multiple references are separated by `!!!` (optionally with spaces around it), and the equ...' 54975 Scribunto text/plain local export = {} local rsplit = mw.text.split --[==[ Parse a references spec as used in the {{para|ref}} param to {{tl|IPA}}, {{tl|IPAchar}}, {{tl|homophones}}, {{tl|rhymes}}, etc. and soon the {{para|f<var>N</var>ref}} param to {{tl|head}}. `parse_err` is a function of one argument to throw an error with the specified argument as the error message; defaults to `error`. Multiple references are separated by `!!!` (optionally with spaces around it), and the equivalent of `<nowiki><ref name="bendo">{{R:it:DiPI|bendo}}</ref><ref>{{R:it:Olivetti}}</ref></nowiki>` can be specified using a syntax like the following: { {{IPA|it|ˈben.do|ˈbɛn.do|ref2={{R:it:DiPI|bendo}}<<name:bendo>> !!! {{R:it:Olivetti}}}}} To include a group as in `<nowiki><ref name="bendo" group="pron">...</ref></nowiki>` use: { {{IPA|it|ˈben.do|ˈbɛn.do|ref2={{R:it:DiPI|bendo}}<<name:bendo>><<group:pron>>}}} To reference a prior name, as in `<nowiki><ref name="bendo"/></nowiki>`, leave the reference text blank: { {{IPA|it|ˈben.do|ˈbɛn.do|ref2=<<name:bendo>>}}} Similarly, to reference a prior name in a particular group, as in `<nowiki><ref name="bendo" group="pron"/></nowiki>`, use: { {{IPA|it|ˈben.do|ˈbɛn.do|ref2=<<name:bendo>><<group:pron>>}}} The return value consists of a list of objects of the form { {text = TEXT, name = NAME, group = GROUP}}. This is the same format as is expected in the `part.refs` in [[Module:headword]] and `item.refs` in [[Module:IPA]]. ]==] function export.parse_references(text, parse_err) parse_err = parse_err or error local refs = {} local raw_notes = rsplit(text, "%s*!!!%s*") for _, raw_note in ipairs(raw_notes) do local note if raw_note:find("<<") then local splitvals = require("Module:string utilities").split(raw_note, "(<<[a-z]+:.->>)") note = {text = splitvals[1]} for i = 2, #splitvals, 2 do local key, value = splitvals[i]:match("^<<([a-z]+):(.*)>>$") if not key then parse_err("Internal error: Can't parse " .. splitvals[i]) end if key == "name" or key == "group" then note[key] = value else parse_err("Unrecognized key '" .. key .. "' in " .. splitvals[i]) end if splitvals[i + 1] ~= "" then parse_err("Extraneous text '" .. splitvals[i + 1] .. "' after " .. splitvals[i]) end end else note = raw_note end table.insert(refs, note) end return refs end --[==[ Format a list of reference specs, using a parser function. The return string contains a footnote number that hyperlinks to the actual reference, located in the `<nowiki><references /></nowiki>` section. The format an individual reference spec is either a string containing the reference text (typically a call to a citation template such as {{tl|cite-book}}, or a template wrapping such a call), or an object with fields `text` (the reference text), `name` (the name of the reference, as in `<nowiki><ref name="foo">...</ref></nowiki>` or `<nowiki><ref name="foo" /></nowiki>`) and/or `group` (the group of the reference, as in `<nowiki><ref name="foo" group="bar">...</ref></nowiki>` or `<nowiki><ref name="foo" group="bar"/></nowiki>`). ]==] function export.format_references(refspecs) local refs = {} for _, refspec in ipairs(refspecs) 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 table.insert(refs, mw.getCurrentFrame():extensionTag("ref", refspec.text, refargs)) end return table.concat(refs) end return export 49olnq52uhabuj9xvknmmj73r6ycrd7 Module:gender and number utilities 828 8101 54976 2026-09-30T17:41:50Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local parameter_utilities_module = "Module:parameter utilities" local parse_interface_module = "Module:parse interface" --[==[ Parse a gender and number spec, possibly with multiple comma-separated gender/number specs and potentially with inline modifiers. Used by [[Module:parameters]]. `data` is an object with the following fields: * `spec`: The unparsed gender/number spec. * `paramname`: The name of the parameter where the gender/number spec...' 54976 Scribunto text/plain local export = {} local parameter_utilities_module = "Module:parameter utilities" local parse_interface_module = "Module:parse interface" --[==[ Parse a gender and number spec, possibly with multiple comma-separated gender/number specs and potentially with inline modifiers. Used by [[Module:parameters]]. `data` is an object with the following fields: * `spec`: The unparsed gender/number spec. * `paramname`: The name of the parameter where the gender/number spec was retrieved from, for error messages. Optional. * `parse_err`: Optional function to display a parse error message, in the format required for `parse_inline_modifiers` in [[Module:parse utilities]]. * `allow_multiple`: If true, allow multiple comma-separated gender/number specs. If given, the return value will be a list of parsed gender/number specs instead of a single such object. * `allow_space_around_comma`: If true, spaces can be placed around the comma separating multiple gender/number specs. It's not generally recommended to set this. The return value is a gender/number spec of the sort accepted by [[Module:gender and number]]; or, if `allow_multiple` is given, a list of such specs. In each spec, the field `spec` contains the gender/number spec itself (e.g. `m-in-p`) and other fields contain parsed inline modifiers. The following modifiers are supported: * `q`: Left qualifier. * `qq`: Right qualifier. * `l`: Left label(s). * `ll`: Right label(s). * `ref`: Reference(s). ]==] function export.parse_gender_and_number_spec(data) local spec = data.spec if not spec:find("[,<]") then return data.allow_multiple and {{spec = spec}} or {spec = spec} end local param_mods = require(parameter_utilities_module).construct_param_mods { {group = {"q", "l", "ref"}}, } local function generate_obj(term, parse_err) return { spec = term } end return require(parse_interface_module).parse_inline_modifiers(spec, { paramname = data.paramname, param_mods = param_mods, generate_obj = generate_obj, parse_err = data.parse_err, escape_fun = false, unescape_fun = false, splitchar = data.allow_multiple and (data.allow_space_around_comma and "%s*,%s*" or ",") or nil, }) end return export 2ikdohznxczh7f3kgp9efot9mt9h6p8 Module:wikimedia languages 828 8102 54977 2026-09-30T17:42:19Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local languages_module = "Module:languages" local language_like_module = "Module:language-like" local load_module = "Module:load" local wm_languages_data_module = "Module:wikimedia languages/data" local get_by_code -- Defined below. local gmatch = string.gmatch local is_known_language_tag = mw.language.isKnownLanguageTag local make_object -- Defined below. local require = require local setmetatable = setmetatable local type = type --[==[ Load...' 54977 Scribunto text/plain local export = {} local languages_module = "Module:languages" local language_like_module = "Module:language-like" local load_module = "Module:load" local wm_languages_data_module = "Module:wikimedia languages/data" local get_by_code -- Defined below. local gmatch = string.gmatch local is_known_language_tag = mw.language.isKnownLanguageTag local make_object -- Defined below. 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 get_lang(...) get_lang = require(languages_module).getByCode return get_lang(...) end local function get_lang_data_module_name(...) get_lang_data_module_name = require(languages_module).getDataModuleName return get_lang_data_module_name(...) end local function load_data(...) load_data = require(load_module).load_data return load_data(...) 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 wm_languages_data local function get_wm_languages_data() wm_languages_data, get_wm_languages_data = load_data(wm_languages_data_module), nil return wm_languages_data end local WikimediaLanguage = {} WikimediaLanguage.__index = WikimediaLanguage function WikimediaLanguage:getCode() return self._code end function WikimediaLanguage:getCanonicalName() return self._data[1] end --function WikimediaLanguage:getAllNames() -- return self._data.names --end --[==[Returns a table of types as a lookup table (with the types as keys). Currently, the only possible type is {Wikimedia language}.]==] function WikimediaLanguage:getTypes() local types = self._types if types == nil then types = {["Wikimedia language"] = true} 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 Wikimedia language has all of them.]==] function WikimediaLanguage:hasType(...) WikimediaLanguage.hasType = require(language_like_module).hasType return self:hasType(...) end function WikimediaLanguage:getWiktionaryLanguage() local object = self._wiktionaryLanguageObject if object == nil then object = get_lang(self._data.wiktionary_code, nil, "allow etym") self._wiktionaryLanguageObject = object end return object end -- Do NOT use this method! -- All uses should be pre-approved on the talk page! function WikimediaLanguage:getData() return self._data end --[==[Returns the name of the module containing the Wikimedia language's data (if any). Currently, this is always [[Module:wikimedia languages/data]].]==] function WikimediaLanguage:getDataModuleName() return wm_languages_data_module 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}, WikimediaLanguage) end make_object = export.makeObject function export.getByCode(code) -- Only accept codes the software recognises. if not is_known_language_tag(code) then return nil end local data = (wm_languages_data or get_wm_languages_data())[code] -- If there is no specific Wikimedia code, then "borrow" the information -- from the general Wiktionary language code. local name, wiktionary_code if data ~= nil then name, wiktionary_code = data[1], data.wiktionary_code if not (name == nil or wiktionary_code == nil) then return make_object(code, data) end end -- Get the associated Wiktionary language, using the wiktionary_code key or -- else the input code. if wiktionary_code == nil then wiktionary_code = code end local lang = get_lang(wiktionary_code, nil, "allow etym", "allow family") if lang ~= nil then return make_object(code, { name == nil and lang:getCanonicalName() or name, wiktionary_code = wiktionary_code, }) end -- If there's no Wiktionary language for the relevant code, throw an error. -- This should never happen. local msg, arg3 if data == nil then msg = "code '%s' is a valid Wikimedia language code, but there is no corresponding data in [[%s]], [[%s]] or [[Module:families/data]]" elseif wiktionary_code ~= code then msg = "code '%s' is a valid Wikimedia language code and has data in [[%s]], but its 'wiktionary_code' key '%s' is not valid" arg3 = wiktionary_code else msg = "code '%s' is a valid Wikimedia language code and has data in [[%s]], but no corresponding data in [[%s]] or [[Module:families/data]]" end error(msg:format(code, wm_languages_data_module, arg3 or get_lang_data_module_name(code))) end get_by_code = export.getByCode function export.getByCodeWithFallback(code) local object = get_by_code(code) if object ~= nil then return object end local lang = get_lang(code, nil, "allow etym") return lang ~= nil and lang:getWikimediaLanguages()[1] or nil end return export ef45ys2do2au63pewsjhq7qzjxsy1nr Module:table/append 828 8103 54978 2026-09-30T17:43:20Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local select = select local function append(n, i, t, t_len, item, ...) local k = 0 while true do k = k + 1 local v = item[k] if v ~= nil then t_len = t_len + 1 t[t_len] = v elseif i == n then return t else return append(n, i + 1, t, t_len, ...) end end end --[==[ Appends any number of lists together as a new list.]==] return function(...) local n, t = select("#", ...), {} return n == 0 and t or append(n, 1, t, 0, ...) end' 54978 Scribunto text/plain local select = select local function append(n, i, t, t_len, item, ...) local k = 0 while true do k = k + 1 local v = item[k] if v ~= nil then t_len = t_len + 1 t[t_len] = v elseif i == n then return t else return append(n, i + 1, t, t_len, ...) end end end --[==[ Appends any number of lists together as a new list.]==] return function(...) local n, t = select("#", ...), {} return n == 0 and t or append(n, 1, t, 0, ...) end kr9qgu53vdzydjiqsbbxw5wnf2llyao Module:table/compressSparseArray 828 8104 54979 2026-09-30T17:43:51Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local table_num_keys_module = "Module:table/numKeys" local function num_keys(...) num_keys = require(table_num_keys_module) return num_keys(...) end --[==[ Given a list that may contain gaps (e.g. {1, 2, nil, 4}), returns a new gapless list in the same order.]==] return function(t) local list, keys, i = {}, num_keys(t), 0 while true do i = i + 1 local k = keys[i] if k == nil then return list end list[i] = t[k] end end' 54979 Scribunto text/plain local table_num_keys_module = "Module:table/numKeys" local function num_keys(...) num_keys = require(table_num_keys_module) return num_keys(...) end --[==[ Given a list that may contain gaps (e.g. {1, 2, nil, 4}), returns a new gapless list in the same order.]==] return function(t) local list, keys, i = {}, num_keys(t), 0 while true do i = i + 1 local k = keys[i] if k == nil then return list end list[i] = t[k] end end 23nyyesbtac5ffo4gwnkyx8uhxrigj0 Module:table/contains 828 8105 54980 2026-09-30T17:44:13Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local table_find_module = "Module:table/find" local function table_find(...) table_find = require(table_find_module) return table_find(...) end --[==[ Given a list and a value to be found, returns {true} if the value is in the array portion of the list, or {false} if not. `options` is an optional table of additional options to control the behavior of the operation. The following options are recognized: * `comparison`: Function of two arguments to compare whe...' 54980 Scribunto text/plain local table_find_module = "Module:table/find" local function table_find(...) table_find = require(table_find_module) return table_find(...) end --[==[ Given a list and a value to be found, returns {true} if the value is in the array portion of the list, or {false} if not. `options` is an optional table of additional options to control the behavior of the operation. The following options are recognized: * `comparison`: Function of two arguments to compare whether `item` is equal to an existing item in `list`. If unspecified, items are considered equal if either the standard equality operator {==} or {deepEquals} return {true}. As a special case, if the string value {"=="} is specified, then the standard equality operator alone will be used. * `key`: Function of one argument to return a comparison key, which will be used with the comparison function. The key function is applied to both `item` and the existing item in `list` to compare against, and the comparison is done against the results.]==] return function(...) return table_find(...) and true or false end avq83e5r7vp9ry6mn3nxu1gtka33xms Module:table/deepEquals 828 8106 54981 2026-09-30T17:44:45Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local getmetatable = getmetatable local next = next local pairs = pairs local type = type local function is_eq(a, b, seen, include_mt) -- If `a` and `b` have been compared before, return the memoized result. This will usually be true, since failures normally fail the whole check outright, but match failures are tolerated during the laborious check without this happening, since it compares key/value pairs until it finds a match, so it could be false. local memo...' 54981 Scribunto text/plain local getmetatable = getmetatable local next = next local pairs = pairs local type = type local function is_eq(a, b, seen, include_mt) -- If `a` and `b` have been compared before, return the memoized result. This will usually be true, since failures normally fail the whole check outright, but match failures are tolerated during the laborious check without this happening, since it compares key/value pairs until it finds a match, so it could be false. local memo_a = seen[a] if memo_a then local result = memo_a[b] if result ~= nil then return result end -- To avoid recursive references causing infinite loops, assume the tables currently being compared are equivalent by memoizing the comparison as true; this will be corrected to false if there's a match failure. memo_a[b] = true else memo_a = {[b] = true} seen[a] = memo_a end -- Don't bother checking `memo_b` for `a`, since if `a` and `b` had been compared before then `b` would be in `memo_a`, but it isn't. local memo_b = seen[b] if memo_b then memo_b[a] = true else memo_b = {[a] = true} seen[b] = memo_b end -- If `include_mt` is set, check the metatables are equivalent. if include_mt then local mt_a, mt_b = getmetatable(a), getmetatable(b) if not (mt_a == mt_b or type(mt_a) == "table" and type(mt_b) == "table" and is_eq(mt_a, mt_b, seen, true)) then memo_a[b], memo_b[a] = false, false return false end end -- Copy all key/values pairs in `b` to `remaining_b`, and count the size: this uses `pairs`, which will also be used to iterate over `a`, ensuring that `a` and `b` are iterated over using the same iterator. This is necessary to ensure that `deepEquals(a, b)` and `deepEquals(b, a)` always give the same result. Simply iterating over `a` while accessing keys in `b` for comparison would ignore any `__pairs` metamethod that `b` has, which could cause asymmetrical outputs if `__pairs` returns more or less than the complete set of key/value pairs accessible via `__index`, so using `pairs` for both `a` and `b` prevents this. -- TODO: handle exotic `__pairs` methods which return the same key multiple times with different values. local remaining_b, size_b = {}, 0 for k_b, v_b in pairs(b) do remaining_b[k_b], size_b = v_b, size_b + 1 end -- Fast check: iterate over the keys in `a`, checking if an equivalent value exists at the same key in `remaining_b`. As matches are found, key/value pairs are removed from `remaining_b`. If any keys in `a` or `remaining_b` are tables, the fast check will only work if the exact same object exists as a key in the other table. Any others from `a` that don't match anything in `remaining_b` are added to `remaining_a`, while those in `remaining_b` that weren't found will still remain once the loop ends. `remaining_a` and `remaining_b` are then compared at the end with the laborious check. local size_a, remaining_a = 0 for k, v_a in pairs(a) do local v_b = remaining_b[k] -- If `k` isn't in `remaining_b`, `a` and `b` can't be equivalent unless it's a table. if v_b == nil then if type(k) ~= "table" then memo_a[b], memo_b[a] = false, false return false -- Otherwise, add the `k`/`v_a` pair to `remaining_a` for the laborious check. elseif not remaining_a then remaining_a = {} end remaining_a[k], size_a = v_a, size_a + 1 -- Otherwise, if `k` exists in `a` and `remaining_b`, `v_a` and `v_b` must be equivalent for there to be a match. elseif v_a == v_b or type(v_a) == "table" and type(v_b) == "table" and is_eq(v_a, v_b, seen, include_mt) then remaining_b[k], size_b = nil, size_b - 1 else memo_a[b], memo_b[a] = false, false return false end end -- Must be the same number of remaining keys in each table. if size_a ~= size_b then memo_a[b], memo_b[a] = false, false return false -- If the size is 0, there's nothing left to check. elseif size_a == 0 then return true end -- Laborious check: since it's not possible to use table lookups to check if two keys are equivalent when they're tables, check each key/value pair in `remaining_a` against every key/value pair in `remaining_b` until a match is found, removing the matching key/value pair from `remaining_b` each time, to ensure one-to-one equivalence. for k_a, v_a in next, remaining_a do local success for k_b, v_b in next, remaining_b do -- Keys/value pairs must be equivalent in order to match. if ( -- More efficient to compare the values first, as they might not be tables. (v_a == v_b or type(v_a) == "table" and type(v_b) == "table" and is_eq(v_a, v_b, seen, include_mt)) and (k_a == k_b or type(k_a) == "table" and type(k_b) == "table" and is_eq(k_a, k_b, seen, include_mt)) ) then -- Remove matched key from `remaining_b`, and break the inner loop. success, remaining_b[k_b] = true, nil break end end -- Fail if `remaining_b` runs out of keys, as the `k_a`/`v_a` pair still hasn't matched. if not success then memo_a[b], memo_b[a] = false, false return false end end -- If every key/value pair in `remaining_a` matched with one in `remaining_b`, `a` and `b` must be equivalent. Note that `remaining_b` will now be empty, since the laborious check only starts if `remaining_a` and `remaining_b` are the same size. return true end --[==[ Recursively compare two values that may be tables, and returns true if all key-value pairs are structurally equivalent. Note that this handles arbitrary nesting of subtables (including recursive nesting) to any depth, for keys as well as values. If `include_mt` is true, then metatables are also compared.]==] return function(a, b, include_mt) -- Do simple checks before calling is_eq to avoid generating an unnecessary memo. -- Simple equality check and type check; if not a ~= b, a and b can only be equivalent if they're both tables. return a == b or type(a) == "table" and type(b) == "table" and is_eq(a, b, {}, include_mt) end 6rgswepb0gwhbiojbyrcr1at7m4h27n Module:table/empty 828 8107 54982 2026-09-30T17:45:11Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local table_get_metamethod = "Module:table/getMetamethod" local next = next local pairs = pairs local pairs_default_iter = pairs{} local function get_metamethod(...) get_metamethod = require(table_get_metamethod) return get_metamethod(...) end -- Not possible to simply iterate with a for-loop, since removing a key means -- it can't be given to next() on the subsequent iteration. Instead, repeatedly -- giving next() the initial state and key will either retur...' 54982 Scribunto text/plain local table_get_metamethod = "Module:table/getMetamethod" local next = next local pairs = pairs local pairs_default_iter = pairs{} local function get_metamethod(...) get_metamethod = require(table_get_metamethod) return get_metamethod(...) end -- Not possible to simply iterate with a for-loop, since removing a key means -- it can't be given to next() on the subsequent iteration. Instead, repeatedly -- giving next() the initial state and key will either return some valid key, -- which can then be removed, or nil, once the table is empty. local function empty_raw(t, iter, state, init) while true do local k = iter(state, init) if k == nil then return t end t[k] = nil end end --[==[ Given a table, sets all keys to {nil}. If `raw` is set, then metamethods will be ignored. Warning: this function is only recommended for tables with a small handful of keys, where reusing a table will avoid creating a large number of temporary objects. In the vast majority of situations, it is more efficient to simply create a new table.]==] return function(t, raw) if raw then return empty_raw(t, next, t) end -- If `raw` is not set, try to check for a __pairs metamethod. The check -- will fail if there's a protected metatable, but if it succeeds and -- there's no __pairs metamethod, use empty_raw() as an optimisation. local success, __pairs = get_metamethod(t, "__pairs") if success and __pairs == nil then return empty_raw(t, next, t) end -- If the iterator returned by the __pairs metamethod is just the default -- pairs iterator or next(), use empty_raw() with the returned state and -- key. This is most likely to happen if a protected metatable doesn't have -- a __pairs metamethod. local iter, state, k = pairs(t) if iter == pairs_default_iter or iter == next then return empty_raw(t, iter, state, k) end -- If the iterator returns nothing, `t` is already empty. k = iter(state, k) if k == nil then return t -- already empty end -- Otherwise, build a table of keys first, then iterate over it to remove -- keys from `t`, which ensures removals don't disrupt the loop. local keys = {[k] = true} for k in iter, state, k do keys[k] = true end for k in next, keys do t[k] = nil end return t end 5cjvu27spz7eb4k7wsc8euj5vvq30dg Module:table/extendIfNot 828 8108 54983 2026-09-30T17:45:42Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local table_insert_if_not_module = "Module:table/insertIfNot" local table_shallow_copy_module = "Module:table/shallowCopy" local require = require local type = type local function insert_if_not(...) insert_if_not = require(table_insert_if_not_module) return insert_if_not(...) end local function shallow_copy(...) shallow_copy = require(table_shallow_copy_module) return shallow_copy(...) end local function extend_with_pos(list, new_items, options) local i...' 54983 Scribunto text/plain local table_insert_if_not_module = "Module:table/insertIfNot" local table_shallow_copy_module = "Module:table/shallowCopy" local require = require local type = type local function insert_if_not(...) insert_if_not = require(table_insert_if_not_module) return insert_if_not(...) end local function shallow_copy(...) shallow_copy = require(table_shallow_copy_module) return shallow_copy(...) end local function extend_with_pos(list, new_items, options) local i = 1 while true do local item = new_items[i] if item == nil then return list end local success = insert_if_not(list, item, options) if success then options.pos = options.pos + 1 end i = i + 1 end end local function extend_with_options_as_pos(list, new_items, pos) local i = 1 while true do local item = new_items[i] if item == nil then return list end local success = insert_if_not(list, item, pos) if success then pos = pos + 1 end i = i + 1 end end local function extend_without_pos(list, new_items, options) local i = 1 while true do local item = new_items[i] if item == nil then return list end insert_if_not(list, item, options) i = i + 1 end end --[==[ Extend an existing list by a new list, using {insertIfNot()} for each item. `options` is an optional table of additional options to control the behavior of the operation. The following options are recognized: * `pos`: As in {insertIfNot()}. * `comparison`: As in {insertIfNot()}. * `key`: As in {insertIfNot()}. * `combine`: As in {insertIfNot()}. Unlike {insertIfNot()}, this function does not return a boolean indicating whether any items were inserted.]==] return function(list, new_items, options) if options == nil then return extend_without_pos(list, new_items) elseif type(options) == "number" then return extend_with_options_as_pos(list, new_items, options) end local pos = options.pos if pos == nil then return extend_without_pos(list, new_items, options) end return extend_with_pos(list, new_items, shallow_copy(options)) end crdbb3ps6pmk0j0qpkle192t06mpn8v Module:table/find 828 8109 54984 2026-09-30T17:46:03Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local table_deep_equals_module = "Module:table/deepEquals" local deep_equals local function get_deep_equals() deep_equals, get_deep_equals = require(table_deep_equals_module), nil return deep_equals end local function equality_op(a, b) return a == b end --[==[ Given a list and a value to be found, returns the value's index if the value is in the array portion of the list, or {nil} if not found. `options` is an optional table of additional options to contro...' 54984 Scribunto text/plain local table_deep_equals_module = "Module:table/deepEquals" local deep_equals local function get_deep_equals() deep_equals, get_deep_equals = require(table_deep_equals_module), nil return deep_equals end local function equality_op(a, b) return a == b end --[==[ Given a list and a value to be found, returns the value's index if the value is in the array portion of the list, or {nil} if not found. `options` is an optional table of additional options to control the behavior of the operation. The following options are recognized: * `comparison`: Function of two arguments to compare whether `item` will be deemed equal to an existing item in `list`. If unspecified, items are deemed equal if {deepEquals} returns {true} (which is always the case if the equality operator {==} would return true). As a special case, if the string value {"=="} is specified, then the equality operator alone will be used. * `key`: Function of one argument to return a comparison key, which will be used with the comparison function. The key function is applied to both `item` and the existing item in `list` to compare against, and the comparison is done against the results.]==] return function(list, x, options) local i, key_func, comp_func = 1 if options == nil then comp_func = deep_equals or get_deep_equals() else key_func, comp_func = options.key, options.comparison if key_func ~= nil then x = key_func(x) end if comp_func == nil then comp_func = deep_equals or get_deep_equals() elseif comp_func == "==" then comp_func = equality_op end end while true do local v = list[i] if v == nil then return nil elseif key_func ~= nil then v = key_func(v) end if comp_func(v, x) then return i end i = i + 1 end end rwdwjaudop2hyb464ozqxzbdhpka0td Module:table/flatten 828 8110 54985 2026-09-30T17:46:26Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local error = error local insert = table.insert local type = type local function flatten(t, list, n, current) current[t] = true local i = 0 while true do i = i + 1 local v = t[i] if v == nil then current[t] = nil return n elseif type(v) ~= "table" then n = n + 1 list[n] = v elseif current[v] then error("loop in input list") else n = flatten(v, list, n, current) end end end --[==[ Given a list, which may contain sublists, flat...' 54985 Scribunto text/plain local error = error local insert = table.insert local type = type local function flatten(t, list, n, current) current[t] = true local i = 0 while true do i = i + 1 local v = t[i] if v == nil then current[t] = nil return n elseif type(v) ~= "table" then n = n + 1 list[n] = v elseif current[v] then error("loop in input list") else n = flatten(v, list, n, current) end end end --[==[ Given a list, which may contain sublists, flatten it into a single list. For example, {flatten({ "a", { "b", "c" }, "d" })} -> { { "a", "b", "c", "d" }}]==] return function(t) local list = {} flatten(t, list, 0, {}) return list end 529c10ci266kmiv1iiqgwuyg3hgav1d Module:table/indexIpairs 828 8111 54986 2026-09-30T17:47:00Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local table_get_unprotected_metatable = "Module:table/getUnprotectedMetatable" local error = error local ipairs = ipairs local ipairs_iter = ipairs{} local pcall = pcall local rawget = rawget local type = type local function get_unprotected_metatable(...) get_unprotected_metatable = require(table_get_unprotected_metatable) return get_unprotected_metatable(...) end local current = {} -- Iterator used when there is no __ipairs metamethod to deal with. local f...' 54986 Scribunto text/plain local table_get_unprotected_metatable = "Module:table/getUnprotectedMetatable" local error = error local ipairs = ipairs local ipairs_iter = ipairs{} local pcall = pcall local rawget = rawget local type = type local function get_unprotected_metatable(...) get_unprotected_metatable = require(table_get_unprotected_metatable) return get_unprotected_metatable(...) end local current = {} -- Iterator used when there is no __ipairs metamethod to deal with. local function default_iter(t, i) i = i + 1 local v = t[i] if v ~= nil then return i, v end end local function index_ipairs(t) if current[t] then return ipairs_iter, t, 0 end local mt = get_unprotected_metatable(t) if mt == nil then return ipairs_iter, t, 0 elseif not mt then current[t] = true -- Use pcall(), so that `t` can always be unset from `current`. local success, iter, state, init = pcall(ipairs, t) current[t] = nil -- If there was an error, raise it. if not success then error(iter) end return iter, state, init end -- Iterate up through __index metamethods. local parent repeat -- If an __ipairs metamethod is found, use the custom iterator. local ipairs_mm = rawget(mt, "__ipairs") if not (ipairs_mm == nil or ipairs_mm == index_ipairs) then break end -- If __index isn't a table, use default_iter(). parent = rawget(mt, "__index") if not (parent and type(parent) == "table") then return default_iter, t, 0 end -- If there's no metatable, use default_iter(). mt = get_unprotected_metatable(parent) if mt == nil then return default_iter, t, 0 end -- get_unprotected_metatable() will return false if the metatable is -- protected, which means there could be an __ipairs metamethod, so use the -- custom iterator. until not mt local iter, state return function(t, i) local new_i = i + 1 local v = t[new_i] if v ~= nil then return new_i, v -- Use iter() as a fallback. elseif iter == nil then iter, state = ipairs(parent) end return iter(state, i) end, t, 0 end return index_ipairs 8r3dgh93aivb4vx7t0eff6tqdguttv8 Module:table/indexPairs 828 8112 54987 2026-09-30T17:47:32Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local table_get_unprotected_metatable = "Module:table/getUnprotectedMetatable" local error = error local next = next local pairs = pairs local pcall = pcall local rawget = rawget local type = type local function get_unprotected_metatable(...) get_unprotected_metatable = require(table_get_unprotected_metatable) return get_unprotected_metatable(...) end local current = {} local index_pairs -- Takes a table, and returns: -- * The value at __index in its metata...' 54987 Scribunto text/plain local table_get_unprotected_metatable = "Module:table/getUnprotectedMetatable" local error = error local next = next local pairs = pairs local pcall = pcall local rawget = rawget local type = type local function get_unprotected_metatable(...) get_unprotected_metatable = require(table_get_unprotected_metatable) return get_unprotected_metatable(...) end local current = {} local index_pairs -- Takes a table, and returns: -- * The value at __index in its metatable. -- * A boolean stating whether it has a __pairs metamethod. -- * The three iterator values returned by calling pairs() on it. local function get_iter(t) local mt = get_unprotected_metatable(t) if mt == nil then return nil, false, next, t, nil -- If the metatable is protected, it's not possible to get the __index or -- __pairs metamethods, so call pairs() on `t` and return the result. If -- __pairs is index_pairs, set current[t] so that it knows to return next() -- instead of getting into an infinite loop. elseif not mt then current[t] = true -- Use pcall(), so that `t` can always be unset from `current`. local success, iter, state, init = pcall(pairs, t) current[t] = nil -- If there was an error, raise it. if not success then error(iter) end return nil, true, iter, state, init end -- If there's a __pairs metamethod that isn't index_pairs(), use it. local pairs_mm = rawget(mt, "__pairs") if not (pairs_mm == nil or pairs_mm == index_pairs) then return nil, true, pairs_mm(t) end local parent = rawget(mt, "__index") if type(parent) == "function" then parent = nil end return parent, false, next, t, nil end function index_pairs(t) if current[t] then return next, t, nil end local parent, is_pairs_mm, iter, state, k = get_iter(t) -- If there is no parent, just return the iterator. if parent == nil then return iter, state, k end local v, seen_k, seen_t, catch_values return function() while true do if is_pairs_mm then return catch_values(iter(state, k)) end while true do k, v = iter(state, k) if k == nil then break -- If a repeated key is found, skip and iterate again. elseif not seen_k then seen_k = {[k] = true} return k, v elseif not seen_k[k] then seen_k[k] = true return k, v end end if parent == nil then return elseif seen_t == nil then seen_t = {} end seen_t[t] = true t, parent, is_pairs_mm, iter, state, k = parent, get_iter(parent) if seen_t[t] then error("loop in gettable") elseif is_pairs_mm then -- Skip keys which have already been seen, but the return is -- arbitrary, so allow repeats if the metamethod generates them. function catch_values(new_k, ...) if seen_k[new_k] then return catch_values(iter(state, new_k)) end k = new_k return new_k, ... end end end end end return index_pairs rm6y8vh41zlq8hc2bo1vk41ur43xx7v Module:table/insertIfNot 828 8113 54988 2026-09-30T17:48:01Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local table_find_module = "Module:table/find" local insert = table.insert local type = type local function table_find(...) table_find = require(table_find_module) return table_find(...) end --[==[ Given a `list` and a `new_item` to be inserted, append the value to the end of the list if not already present (or insert at an arbitrary position, if `options.pos` is given; see below). Comparison is by value, using {deepEquals}. `options` is an optional table of...' 54988 Scribunto text/plain local table_find_module = "Module:table/find" local insert = table.insert local type = type local function table_find(...) table_find = require(table_find_module) return table_find(...) end --[==[ Given a `list` and a `new_item` to be inserted, append the value to the end of the list if not already present (or insert at an arbitrary position, if `options.pos` is given; see below). Comparison is by value, using {deepEquals}. `options` is an optional table of additional options to control the behavior of the operation. The following options are recognized: * `pos`: Position at which insertion happens (i.e. before the existing item at position `pos`). * `comparison`: Function of two arguments to compare whether `item` is equal to an existing item in `list`. If unspecified, items are considered equal if either the standard equality operator {==} or {deepEquals} return {true}. As a special case, if the string value {"=="} is specified, then the standard equality operator alone will be used. * `key`: Function of one argument to return a comparison key, which will be used with the comparison function. The key function is applied to both `item` and the existing item in `list` to compare against, and the comparison is done against the results. This is useful when inserting a complex structure into an existing list while avoiding duplicates. * `combine`: Function of three arguments (the existing item, the new item and the position, respectively) to combine an existing item with `new_item`, when `new_item` is found in `list`. If unspecified, the existing item is left alone. Returns {false} if an entry is already found, or {true} if inserted. By default, {false} indicates that no change was made to the input table, but if the `combine` is used, {false} indicates that the pre-existing entry was modified. For compatibility, `pos` can be specified directly as the third argument in place of `options`, but this is not recommended for new code. NOTE: This function is O(N) in the size of the existing list. If you use this function in a loop to insert several items, you will get O(M*(M+N)) behavior, effectively O((M+N)^2). Thus it is not recommended to use this unless you are sure the total number of items will be small. (An alternative for large lists is to insert all the items without checking for duplicates, and use {removeDuplicates()} at the end.)]==] return function(list, new_item, options) local pos if options then if type(options) == "number" then pos, options = options, nil else pos = options.pos end end local i = table_find(list, new_item, options) if not i then if pos == nil then insert(list, new_item) else insert(list, pos, new_item) end return true elseif options ~= nil then local combine_func = options.combine if combine_func ~= nil then local newval = combine_func(list[i], new_item, i) if newval ~= nil then list[i] = newval end end end return false end tjy2hflim20bhcfnmmalkf8958c64ib Module:table/invert 828 8114 54989 2026-09-30T17:48:22Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local pairs = pairs --[==[ Invert a table. For example, {invert({ "a", "b", "c" })} -> { { a = 1, b = 2, c = 3 }}]==] return function(t) local map = {} for k, v in pairs(t) do map[v] = k end return map end' 54989 Scribunto text/plain local pairs = pairs --[==[ Invert a table. For example, {invert({ "a", "b", "c" })} -> { { a = 1, b = 2, c = 3 }}]==] return function(t) local map = {} for k, v in pairs(t) do map[v] = k end return map end 584zgb0j9tdibi3oq5xt0rnaq3wjeoe Module:table/isArray 828 8115 54990 2026-09-30T17:48:45Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local pairs = pairs --[==[ Returns true if all keys in the table are consecutive integers starting from 1.]==] return function(t) -- pairs() is unordered, but can be assumed to return every key in `t`, so -- if every integer key from 1 to n (the number of keys in `t`) is in use, -- then all the keys in `t` form an integer range from 1 to n, making `t` -- a contiguous array. local i = 0 for _ in pairs(t) do i = i + 1 if t[i] == nil then return false...' 54990 Scribunto text/plain local pairs = pairs --[==[ Returns true if all keys in the table are consecutive integers starting from 1.]==] return function(t) -- pairs() is unordered, but can be assumed to return every key in `t`, so -- if every integer key from 1 to n (the number of keys in `t`) is in use, -- then all the keys in `t` form an integer range from 1 to n, making `t` -- a contiguous array. local i = 0 for _ in pairs(t) do i = i + 1 if t[i] == nil then return false end end return true end g82iokgqr1uqfaagx0is49bas3cpq0c Module:table/isSubsetList 828 8116 54991 2026-09-30T17:49:13Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local table_list_to_set_module = "Module:table/listToSet" local pairs = pairs local function list_to_set(...) list_to_set = require(table_list_to_set_module) return list_to_set(...) end --[==[ Returns true if the first list, taken as a set, is a subset of the second list, taken as a set.]==] return function(t1, t2) t2 = list_to_set(t2) for _, v in pairs(t1) do if t2[v] == nil then return false end end return true end' 54991 Scribunto text/plain local table_list_to_set_module = "Module:table/listToSet" local pairs = pairs local function list_to_set(...) list_to_set = require(table_list_to_set_module) return list_to_set(...) end --[==[ Returns true if the first list, taken as a set, is a subset of the second list, taken as a set.]==] return function(t1, t2) t2 = list_to_set(t2) for _, v in pairs(t1) do if t2[v] == nil then return false end end return true end pcdp2epgqndvuy22amynwhyqbhyrhpl Module:table/isSubsetMap 828 8117 54992 2026-09-30T17:49:41Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local pairs = pairs --[==[ Returns true if the first map, taken as a set, is a subset of the second map, taken as a set.]==] return function(t1, t2) for k in pairs(t1) do if t2[k] == nil then return false end end return true end' 54992 Scribunto text/plain local pairs = pairs --[==[ Returns true if the first map, taken as a set, is a subset of the second map, taken as a set.]==] return function(t1, t2) for k in pairs(t1) do if t2[k] == nil then return false end end return true end oqh54e8vhwwpi0kv29wuqnbe8n0creu Module:table/maxIndex 828 8118 54993 2026-09-30T17:50:07Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local math_module = "Module:math" local maxn = table.maxn local pairs = pairs local function is_positive_integer(...) is_positive_integer = require(math_module).is_positive_integer return is_positive_integer(...) end --[==[ Returns the highest positive integer index of a table or array that possibly has holes in it, or otherwise 0 if no positive integer keys are found. Note that this differs from `table.maxn`, which returns the highest positive numerical ind...' 54993 Scribunto text/plain local math_module = "Module:math" local maxn = table.maxn local pairs = pairs local function is_positive_integer(...) is_positive_integer = require(math_module).is_positive_integer return is_positive_integer(...) end --[==[ Returns the highest positive integer index of a table or array that possibly has holes in it, or otherwise 0 if no positive integer keys are found. Note that this differs from `table.maxn`, which returns the highest positive numerical index, even if it is not an integer.]==] if maxn == nil then return function(t) local max_key = 0 for k in pairs(t) do if is_positive_integer(k) and k > max_key then max_key = k end end return max_key end else -- table.maxn() is deprecated, but use it if it's available return function(t) -- Quick check: if maxn() returns an integer >= 0, return it. local max_key = maxn(t) if is_positive_integer(max_key, "include_0") then return max_key end -- Slow check. max_key = 0 for k in pairs(t) do if is_positive_integer(k) and k > max_key then max_key = k end end return max_key end end e1j8nlmbua7wpbkz82e5y2727u2uoir Module:table/pack 828 8119 54994 2026-09-30T17:50:32Z Deadend0914 7211 Gesceop tramet þe hafaþ '--[==[ An implementation of the {table.pack} function found in Lua 5.2+, which returns a table containing a list of the arguments passed. The returned table also contains the key {n}, which is an integer giving the number of arguments passed. Note that the list in the returned table will contain gaps if any of the arguments passed were {nil}. This will also be reflected in the value of {n}, which always matches the number of arguments passed (e.g. {pack("foo")}...' 54994 Scribunto text/plain --[==[ An implementation of the {table.pack} function found in Lua 5.2+, which returns a table containing a list of the arguments passed. The returned table also contains the key {n}, which is an integer giving the number of arguments passed. Note that the list in the returned table will contain gaps if any of the arguments passed were {nil}. This will also be reflected in the value of {n}, which always matches the number of arguments passed (e.g. {pack("foo")} returns the table { {"foo", n = 1} }, whereas {pack("foo", nil, nil)} returns { {"foo", n = 3} }).]==] return table.pack or function(...) -- On Lua 5.1, use the hidden `arg` variable with `...`, which is the same -- as table.pack(...) on Lua 5.2+. Both are strictly equivalent to -- {n = select("#", ...), ...}, but more performant. return arg end ai5lyp0zhpjyp9n736jj7c60lm0cvm7 Module:table/removeDuplicates 828 8120 54995 2026-09-30T17:51:00Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local table_deep_equals_module = "Module:table/deepEquals" local table_get_metamethod_module = "Module:table/getMetamethod" local rawequal = rawequal local require = require local type = type local function get_metamethod(...) get_metamethod = require(table_get_metamethod_module) return get_metamethod(...) end local deep_equals local function get_deep_equals() deep_equals, get_deep_equals = require(table_deep_equals_module), nil return deep_equals end loc...' 54995 Scribunto text/plain local table_deep_equals_module = "Module:table/deepEquals" local table_get_metamethod_module = "Module:table/getMetamethod" local rawequal = rawequal local require = require local type = type local function get_metamethod(...) get_metamethod = require(table_get_metamethod_module) return get_metamethod(...) end local deep_equals local function get_deep_equals() deep_equals, get_deep_equals = require(table_deep_equals_module), nil return deep_equals end local function equal(a, b) return a == b end -- Sentinel for nil. local _nil = {} local __eq_types = { table = true, userdata = true, } local function seen_map_check(seen_map, v_comp, comp_func) if not comp_func(v_comp, v_comp) then return false, seen_map end local v_seen_map = v_comp == nil and _nil or v_comp if not seen_map then seen_map = {[v_seen_map] = true} return false, seen_map elseif seen_map[v_seen_map] then return true, seen_map end seen_map[v_seen_map] = true return false, seen_map end -- Differs from [[Module:table/contains]], as keys might be nil. local function seen_array_contains(v_comp, seen_array, seen_array_n, comp_func) for i = 1, seen_array_n do if comp_func(v_comp, seen_array[i]) then return true end end return false end local function default_seen_array_check(v_comp, seen_array, seen_array_n, comp_func) local v_comp_type = type(v_comp) if not __eq_types[v_comp_type] then return false, seen_array, seen_array_n elseif not seen_array then return false, {v_comp}, 1 -- If `comp_func` is `deep_equals` and `v_comp` is a table, `seen_array` -- must be checked. elseif comp_func == deep_equals and v_comp_type == "table" then if seen_array_contains(v_comp, seen_array, seen_array_n, comp_func) then return true, seen_array, seen_array_n end else -- If `v_comp` has an `__eq` metamethod (or could have one, if it's not -- possible to retrieve due to a protected metatable), then check with -- `seen_array_contains`. local success, __eq = get_metamethod(v_comp, "__eq") if ( not (success and __eq == nil) and seen_array_contains(v_comp, seen_array, seen_array_n, comp_func) ) then return true, seen_array, seen_array_n end end seen_array_n = seen_array_n + 1 seen_array[seen_array_n] = v_comp return false, seen_array, seen_array_n end local function other_seen_array_check(v_comp, seen_array, seen_array_n, comp_func) if not seen_array then return false, {v_comp}, 1 elseif seen_array_contains(v_comp, seen_array, seen_array_n, comp_func) then return true, seen_array, seen_array_n end seen_array_n = seen_array_n + 1 seen_array[seen_array_n] = v_comp return false, seen_array, seen_array_n end return function(list, options) -- Values are converted to comparison-keys using `key_func`, if supplied, or else the value itself is used as the comparison-key; these are then compared with `comp_func` to determine duplicates. -- To avoid comparing all comparison-keys with each other, which is O(n^2), `seen_map`, a lookup table, and `seen_array`, an array, are used as memos to store already-seen comparison-keys. -- Comparison-keys are stored in `seen_map` if comp_func(x, x) evaluates as true. -- This is always the case if `comp_func` is `rawequal`, `==` or `deep_equals`, unless the comparison-key is NaN. -- If `comp_func` is anything else, the value is checked against itself before storing it. -- If the comparison-key isn't found in `seen_map`, it's checked against each comparison-key stored in `seen_array`. -- This is never needed if `comp_func` is `rawequal`, which only returns true for primitive equality (i.e. `seen_map` is sufficient for everything). -- If `comp_func` is `==` or `deep_equals`, `seen_array` is used for data types which could have `__eq` metamethods, which can result in non-primitively equal values being deemed equal (i.e. new comparison-keys need to be compared in turn). -- If `comp_func` is anything else, all comparison-keys will need to be stored in `seen_array`. local key_func, comp_func, seen_array_check if options == nil then comp_func, seen_array_check = deep_equals or get_deep_equals(), default_seen_array_check else key_func, comp_func = options.key, options.comparison if comp_func == nil then comp_func, seen_array_check = deep_equals or get_deep_equals(), default_seen_array_check elseif comp_func == "==" then comp_func, seen_array_check = equal, default_seen_array_check -- `seen_array_check` is not necessary if `comp_func` is `rawequal`. elseif comp_func ~= rawequal then if comp_func == (deep_equals or get_deep_equals()) then seen_array_check = default_seen_array_check else seen_array_check = other_seen_array_check end end end local output, i, n, seen_map, seen_array, seen_array_n = {}, 1, 0 while true do local v = list[i] if v == nil then return output end local v_comp, seen = v if key_func ~= nil then v_comp = key_func(v_comp) end seen, seen_map = seen_map_check(seen_map, v_comp, comp_func) if not seen then if seen_array_check then seen, seen_array, seen_array_n = seen_array_check(v_comp, seen_array, seen_array_n, comp_func) end if not seen then n = n + 1 output[n] = v end end i = i + 1 end end 8he00zs6o0bh1d04bio2gq4rh3i45vu Module:table/reverse 828 8121 54996 2026-09-30T17:51:24Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local table_length_module = "Module:table/length" local function table_len(...) table_len = require(table_length_module) return table_len(...) end --[==[ Takes a list, and returns a new list with the values in the reverse order: { { "a", "b", "c" }} -> { { "c", "b", "a" }}.]==] return function(t) local list, t_len = {}, table_len(t) for i = 1, t_len do list[i] = t[t_len - i + 1] end return list end' 54996 Scribunto text/plain local table_length_module = "Module:table/length" local function table_len(...) table_len = require(table_length_module) return table_len(...) end --[==[ Takes a list, and returns a new list with the values in the reverse order: { { "a", "b", "c" }} -> { { "c", "b", "a" }}.]==] return function(t) local list, t_len = {}, table_len(t) for i = 1, t_len do list[i] = t[t_len - i + 1] end return list end 5ohrlsa4osfo3dybez4sja0s8ze1ftr Module:table/reverseIpairs 828 8122 54997 2026-09-30T17:51:57Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local ipairs = ipairs --[==[ Iterates through a table using `ipairs` in reverse. `__ipairs` metamethods will be used, including those which return arbitrary (i.e. non-array) keys, but note that this function assumes that the first return value is a key which can be used to retrieve a value from the input table via a table lookup. As such, `__ipairs` metamethods for which this assumption is not true will not work correctly. If the value `nil` is encountered ear...' 54997 Scribunto text/plain local ipairs = ipairs --[==[ Iterates through a table using `ipairs` in reverse. `__ipairs` metamethods will be used, including those which return arbitrary (i.e. non-array) keys, but note that this function assumes that the first return value is a key which can be used to retrieve a value from the input table via a table lookup. As such, `__ipairs` metamethods for which this assumption is not true will not work correctly. If the value `nil` is encountered early (e.g. because the table has been modified), the loop will terminate early.]==] return function(t) -- `__ipairs` metamethods can return arbitrary keys, so compile a list. local keys, i = {}, 0 for k in ipairs(t) do i = i + 1 keys[i] = k end return function() if i == 0 then return nil end local k = keys[i] -- Retrieve `v` from the table. These aren't stored during the initial -- ipairs loop, so that they can be modified during the loop. local v = t[k] -- Return if not an early nil. if v ~= nil then i = i - 1 return k, v end end end g7u5t487acciyypwe4jwka7ibmfy9m2 Module:Table/setParent 828 8123 54998 2026-09-30T17:52:21Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local table_get_unprotected_metatable = "Module:table/getUnprotectedMetatable" local table_index_ipairs_module = "Module:table/indexIpairs" local table_index_pairs_module = "Module:table/indexPairs" local rawset = rawset local require = require local setmetatable = setmetatable local function get_unprotected_metatable(...) get_unprotected_metatable = require(table_get_unprotected_metatable) return get_unprotected_metatable(...) end local index_ipairs local f...' 54998 Scribunto text/plain local table_get_unprotected_metatable = "Module:table/getUnprotectedMetatable" local table_index_ipairs_module = "Module:table/indexIpairs" local table_index_pairs_module = "Module:table/indexPairs" local rawset = rawset local require = require local setmetatable = setmetatable local function get_unprotected_metatable(...) get_unprotected_metatable = require(table_get_unprotected_metatable) return get_unprotected_metatable(...) end local index_ipairs local function get_index_ipairs() index_ipairs, get_index_ipairs = require(table_index_ipairs_module), nil return index_ipairs end local index_pairs local function get_index_pairs() index_pairs, get_index_pairs = require(table_index_pairs_module), nil return index_pairs end return function(t1, t2, setter) -- get_unprotected_metatable() returns nil if there's no metamethod or false -- if it's protected. A protected metatable will cause an error to be thrown -- once setmetatable() is called anyway, but this way avoids the problem of -- getmetatable() returning arbitrary values from a __metatable metamethod. local mt = get_unprotected_metatable(t1) -- Use rawset() if `t1` already has a metatable, as metamethods are accessed -- with rawget() when invoked. if mt then rawset(mt, "__index", t2) rawset(mt, "__ipairs", index_ipairs or get_index_ipairs()) rawset(mt, "__pairs", index_pairs or get_index_pairs()) if setter then rawset(mt, "__newindex", t2) end else mt = { __index = t2, __ipairs = index_ipairs or get_index_ipairs(), __pairs = index_pairs or get_index_pairs(), } if setter then mt.__newindex = t2 end end setmetatable(t1, mt) return t1 end 48v0hyjeqk1dtojaswx8l471lw9lk9m Module:table/size 828 8124 54999 2026-09-30T17:52:40Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local next = next local pairs = pairs --[==[ This returns the size of a key/value pair table. If `raw` is set, then metamethods will be ignored, giving the true table size. For arrays, it is faster to use `export.length`.]==] return function(t, raw) local i, iter, state, init = 0 if raw then iter, state, init = next, t, nil else iter, state, init = pairs(t) end for _ in iter, state, init do i = i + 1 end return i end' 54999 Scribunto text/plain local next = next local pairs = pairs --[==[ This returns the size of a key/value pair table. If `raw` is set, then metamethods will be ignored, giving the true table size. For arrays, it is faster to use `export.length`.]==] return function(t, raw) local i, iter, state, init = 0 if raw then iter, state, init = next, t, nil else iter, state, init = pairs(t) end for _ in iter, state, init do i = i + 1 end return i end cngitqlralebfbp34hnb48xd0vu0xxk Module:table/slice 828 8125 55000 2026-09-30T17:53:01Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local index_module = "Module:index" local table_length_module = "Module:table/length" local require = require local function index_range(...) index_range = require(index_module).range return index_range(...) end local function table_len(...) table_len = require(table_length_module) return table_len(...) end --[==[ Given a list, returns a new list consisting of the items between the start index `i` and end index `j` (inclusive). `i` defaults to `1`, and `j...' 55000 Scribunto text/plain local index_module = "Module:index" local table_length_module = "Module:table/length" local require = require local function index_range(...) index_range = require(index_module).range return index_range(...) end local function table_len(...) table_len = require(table_length_module) return table_len(...) end --[==[ Given a list, returns a new list consisting of the items between the start index `i` and end index `j` (inclusive). `i` defaults to `1`, and `j` defaults to the length of the input list.]==] return function(t, i, j) local slice, n = {}, 0 i, j = index_range(t, i, j, table_len) while not (j and i > j) do local v = t[i] if v == nil then return slice end n = n + 1 slice[n] = v i = i + 1 end return slice end 51m7d23hu7mq9wf29dau6rffwgjl02o Module:sparseConcat 828 8126 55001 2026-09-30T17:53:24Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local table_sparse_ipairs_module = "Module:table/sparseIpairs" local concat = table.concat local function sparse_ipairs(...) sparse_ipairs = require(table_sparse_ipairs_module) return sparse_ipairs(...) end --[==[ Concatenates all values in a table that are indexed by a number, in order. * {sparseConcat{ a, nil, c, d }} => {"acd"} * {sparseConcat{ nil, b, c, d }} => {"bcd"}]==] return function(t, sep, i, j) local list, k = {}, 0 for _, v in sparse_ipairs(t...' 55001 Scribunto text/plain local table_sparse_ipairs_module = "Module:table/sparseIpairs" local concat = table.concat local function sparse_ipairs(...) sparse_ipairs = require(table_sparse_ipairs_module) return sparse_ipairs(...) end --[==[ Concatenates all values in a table that are indexed by a number, in order. * {sparseConcat{ a, nil, c, d }} => {"acd"} * {sparseConcat{ nil, b, c, d }} => {"bcd"}]==] return function(t, sep, i, j) local list, k = {}, 0 for _, v in sparse_ipairs(t) do k = k + 1 list[k] = v end return concat(list, sep, i, j) end bjv07xjo7s2ffvu9stl8xil2x52wjqt Module:table/sparseConcat 828 8127 55002 2026-09-30T17:53:54Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local table_sparse_ipairs_module = "Module:table/sparseIpairs" local concat = table.concat local function sparse_ipairs(...) sparse_ipairs = require(table_sparse_ipairs_module) return sparse_ipairs(...) end --[==[ Concatenates all values in a table that are indexed by a number, in order. * {sparseConcat{ a, nil, c, d }} => {"acd"} * {sparseConcat{ nil, b, c, d }} => {"bcd"}]==] return function(t, sep, i, j) local list, k = {}, 0 for _, v in sparse_ipairs(t...' 55002 Scribunto text/plain local table_sparse_ipairs_module = "Module:table/sparseIpairs" local concat = table.concat local function sparse_ipairs(...) sparse_ipairs = require(table_sparse_ipairs_module) return sparse_ipairs(...) end --[==[ Concatenates all values in a table that are indexed by a number, in order. * {sparseConcat{ a, nil, c, d }} => {"acd"} * {sparseConcat{ nil, b, c, d }} => {"bcd"}]==] return function(t, sep, i, j) local list, k = {}, 0 for _, v in sparse_ipairs(t) do k = k + 1 list[k] = v end return concat(list, sep, i, j) end bjv07xjo7s2ffvu9stl8xil2x52wjqt Module:table/sparseIpairs 828 8128 55003 2026-09-30T17:54:10Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local table_num_keys_module = "Module:table/numKeys" local function num_keys(...) num_keys = require(table_num_keys_module) return num_keys(...) end --[==[ An iterator which works like `ipairs`, but which works for sparse arrays.]==] return function(t) local keys, i = num_keys(t), 0 return function() i = i + 1 local k = keys[i] if k ~= nil then return k, t[k] end end end' 55003 Scribunto text/plain local table_num_keys_module = "Module:table/numKeys" local function num_keys(...) num_keys = require(table_num_keys_module) return num_keys(...) end --[==[ An iterator which works like `ipairs`, but which works for sparse arrays.]==] return function(t) local keys, i = num_keys(t), 0 return function() i = i + 1 local k = keys[i] if k ~= nil then return k, t[k] end end end awvh1kj2qsubuy2anwvpmp6t7xxz299 beds 0 8129 55014 2026-09-30T18:44:31Z Deadend0914 7211 Gesceop tramet þe hafaþ '=={{sprǣc|en}}== ===Rihtstefn=== IPA:/bɛdz/ ===Nama=== {{head|en|noun form}} #''manigfeald of'' '''[[bed]]'''' 55014 wikitext text/x-wiki =={{sprǣc|en}}== ===Rihtstefn=== IPA:/bɛdz/ ===Nama=== {{head|en|noun form}} #''manigfeald of'' '''[[bed]]''' 2cvkcjq6kbjdh2q2xt3lonsa4bldncs girl 0 8130 55015 2026-09-30T18:53:56Z Deadend0914 7211 Gesceop tramet þe hafaþ '=={{sprǣc|en}}== ===Rihtstefn=== * IPA: /ˈɡɜːl/ ===Wordstǣr=== Of {{inh|en|enm|gerle}}, wēninga of {{inh|en|ang|*gyrele}}, of {{inh|en|gmw-pro|*gurilā}}.' 55015 wikitext text/x-wiki =={{sprǣc|en}}== ===Rihtstefn=== * IPA: /ˈɡɜːl/ ===Wordstǣr=== Of {{inh|en|enm|gerle}}, wēninga of {{inh|en|ang|*gyrele}}, of {{inh|en|gmw-pro|*gurilā}}. ald9y7sk9w3rrv9682zu1i7n4yek8w1 55016 55015 2026-09-30T18:58:33Z Deadend0914 7211 /* {{sprǣc|en}} */ 55016 wikitext text/x-wiki =={{sprǣc|en}}== ===Rihtstefn=== * IPA: /ˈɡɜːl/ ===Wordstǣr=== Of {{inh|en|enm|gerle}}, wēninga of {{inh|en|ang|*gyrele}}, of {{inh|en|gmw-pro|*gurilā}}. ===Nama=== {{en-noun|~}} # mægden #* a [[seven-year-old]] girl #*: seofonwintre mægden eiesgupludu2j37b2pss7scxmgdkcu8 55019 55016 2026-09-30T19:14:42Z Deadend0914 7211 /* Nama */ 55019 wikitext text/x-wiki =={{sprǣc|en}}== ===Rihtstefn=== * IPA: /ˈɡɜːl/ ===Wordstǣr=== Of {{inh|en|enm|gerle}}, wēninga of {{inh|en|ang|*gyrele}}, of {{inh|en|gmw-pro|*gurilā}}. ===Nama=== {{en-noun|~}} # [[mægden]] #* a [[seven-year-old]] girl #*: seofonwintre mægden gt32m5etv5dn2by2jsdx03jfdyy2kg3 Bysen:etymon 10 8131 55020 2026-09-30T19:17:13Z Deadend0914 7211 Gesceop tramet þe hafaþ '<includeonly>{{#invoke:etymon|main}}</includeonly><noinclude>{{documentation}}</noinclude>' 55020 wikitext text/x-wiki <includeonly>{{#invoke:etymon|main}}</includeonly><noinclude>{{documentation}}</noinclude> 5t3xwy26hk5cfvmzk7ny2oqnf3fvdz7 Module:etymon 828 8132 55021 2026-09-30T19:17:52Z Deadend0914 7211 Gesceop tramet þe hafaþ '--[=[ This module implements the {{etymon}} template for structured etymology data on Wiktionary. It enables the creation of etymology trees and text by parsing etymon chains, scraping linked pages for their own {{etymon}} data, and recursively building a tree of derivational relationships. Authors: - Original implementation: [[User:Ioaxxere]] - Full refactor (September 2025): [[User:Fenakhay]] ([[Special:Diff/86717746]]) Modules: - [[Module:etymon]]: main mod...' 55021 Scribunto text/plain --[=[ This module implements the {{etymon}} template for structured etymology data on Wiktionary. It enables the creation of etymology trees and text by parsing etymon chains, scraping linked pages for their own {{etymon}} data, and recursively building a tree of derivational relationships. Authors: - Original implementation: [[User:Ioaxxere]] - Full refactor (September 2025): [[User:Fenakhay]] ([[Special:Diff/86717746]]) Modules: - [[Module:etymon]]: main module handling parsing, validation, tree building, and page scraping - [[Module:etymon/data]]: keyword definitions, configuration, and status constants - [[Module:etymon/tree]]: etymology tree rendering - [[Module:etymon/text]]: etymology text generation - [[Module:etymon/categories]]: category generation logic - [[Module:etymon/tracking]]: tracking ]=] local export = {} local __state = { cached_etymon_args = {}, cached_etymon_pages = {}, cached_descendants_checks = {}, senseid_parent_etymon = {}, available_etymon_ids = {}, single_etymons = {}, entry_title = nil, entry_lang_code = nil, current_page_has_inline_etymology = false, current_page_has_redundant_etymology = false, used_idless_etymon = false, toplevel_has_inline_etymology = false, toplevel_redundant_etymology = false, toplevel_idless_etymon = false, has_mismatched_id = false, linked_page_multiple_etymons_idless = false, linked_page_partial_etymology_sections = false, partial_etymology_targets = {}, skip_partial_etymology_category = false, max_depth_reached = 0, total_nodes = 0, language_count = {}, toplevel_keyword_stats = {}, id_stats = nil, warnings = {}, } local function reset_invocation_state() __state.current_page_has_inline_etymology = false __state.current_page_has_redundant_etymology = false __state.used_idless_etymon = false __state.toplevel_has_inline_etymology = false __state.toplevel_redundant_etymology = false __state.toplevel_idless_etymon = false __state.has_mismatched_id = false __state.linked_page_multiple_etymons_idless = false __state.linked_page_partial_etymology_sections = false __state.max_depth_reached = 0 __state.total_nodes = 0 __state.language_count = {} __state.toplevel_keyword_stats = {} __state.warnings = {} end local M = require("Module:module loader").init({ require = { data = "Module:etymon/data", tree = "Module:etymon/tree", text = "Module:etymon/text", categories = "Module:etymon/categories", tracking = "Module:etymon/tracking", descendants = "Module:etymon/descendants", affix = "Module:affix", anchors = "Module:anchors", etydate = "Module:etydate", etymology = "Module:etymology", families = "Module:families", languages = "Module:languages", languages_errorgetby = "Module:languages/errorGetBy", links = "Module:links", pages = "Module:pages", parameters = "Module:parameters", string_utilities = "Module:string utilities", template_parser = "Module:template parser", utilities = "Module:utilities", debug = "Module:debug", en_utilities = "Module:en-utilities", parameter_utilities = "Module:parameter utilities", parse_utilities = "Module:parse utilities", template_styles = "Module:TemplateStyles", script_utilities = "Module:script utilities", JSON = "Module:JSON", yesno = "Module:yesno", }, loadData = { headword_data = "Module:headword/data", parameters_data = "Module:parameters/data", text_allowed = "Module:etymon/data/text_allowed", }, }) local Util = {} function Util.format_error(message, preview_only) if preview_only and not M.pages.is_preview() then return nil end return '<span class="error">' .. message .. '</span>' end function Util.add_warning(message, preview_only) local formatted = Util.format_error(message, preview_only) if formatted then table.insert(__state.warnings, formatted) end end function Util.is_text_param_allowed_for_lang(lang) if not lang or type(lang) ~= "table" then return false end local types = lang.getTypes and lang:getTypes() if types and types.family then local code = lang.getCode and lang:getCode() return code and M.text_allowed.families[code] == true end local full_code = lang.getFullCode and lang:getFullCode() if full_code and M.text_allowed.langs[full_code] then return true end if lang.inFamily then for family_code in pairs(M.text_allowed.families) do if lang:inFamily(family_code) then return true end end end return false end function Util.get_lang(code, no_error) if no_error then return M.languages.getByCode(code, nil, true) end return M.languages.getByCode(code, nil, true) or M.languages_errorgetby.code(code, true, true) end -- Match a term language against a text=:lang stop target (supports etymology-only codes). function Util.lang_matches_stop_code(term_lang, stop_code) if not term_lang or not stop_code or stop_code == "" then return false end local stop_lang = Util.get_lang(stop_code, true) if not stop_lang then return false end if term_lang:getCode() == stop_lang:getCode() then return true end if stop_lang:getFullCode() == stop_lang:getCode() then return term_lang:getFullCode() == stop_lang:getCode() end return false end function Util.get_family(code) return M.families.getByCode(code) end function Util.get_lang_exception(lang) -- Families have no language-specific exceptions if lang.getTypes and lang:getTypes().family then return nil end local code = lang:getCode() local lang_exceptions = M.data.config.lang_exceptions if lang_exceptions[code] then return lang_exceptions[code] end for norm_code, exc in pairs(lang_exceptions) do if exc.normalize_to and code == exc.normalize_to then return exc end if exc.normalize_from_families then local should_normalize = false for _, family in ipairs(exc.normalize_from_families) do if lang:inFamily(family) then should_normalize = true break end end if should_normalize and exc.normalize_exclude_families then for _, family in ipairs(exc.normalize_exclude_families) do if lang:inFamily(family) then should_normalize = false break end end end if should_normalize then local ret = {} for k, v in pairs(exc) do ret[k] = v end ret.suppress_tr = nil return ret end end end return nil end function Util.get_norm_lang(lang) local exc = Util.get_lang_exception(lang) if exc and exc.normalize_to then return M.languages.getByCode(exc.normalize_to) end return lang end function Util.resolve_context_lang(lang, node_args) if type(node_args) ~= "table" then return lang end if node_args.status == M.data.STATUS.INLINE then return lang end if not (lang.hasType and lang:hasType("etymology-only")) then return lang end local full = lang.getFull and lang:getFull() if not full or full:getCode() == lang:getCode() then return lang end if full.hasAncestor and full:hasAncestor(lang) then return lang end return full end -- Add default values for boolean modifiers (e.g., <unc> becomes <unc:1>) -- This is needed because Module:parse utilities expects boolean modifiers to have explicit values function Util.add_boolean_defaults(str, param_mods) local result = str for name, spec in pairs(param_mods) do if spec.type == "boolean" then -- Replace <name> with <name:1> (but not <name:...> which already has a value) result = result:gsub("<" .. name .. ">", "<" .. name .. ":1>") end end return result end local REQUEST_TEMPLATE_PARAM_MODS = { rfe = M.parameter_utilities.construct_param_mods { { param = { "sort", "y", "m", "fragment", "section" } }, { param = { "nocat", "box", "noes" }, type = "boolean" }, }, etystub = M.parameter_utilities.construct_param_mods { { param = "sort" }, { param = { "nocat", "nocap", "nodot" }, type = "boolean" }, }, } function Util.expand_request_template(frame, template_name, param_value, lang_code) local param_mods = REQUEST_TEMPLATE_PARAM_MODS[template_name] local with_defaults = Util.add_boolean_defaults(param_value, param_mods) local parsed = M.parse_utilities.parse_inline_modifiers(with_defaults, { param_mods = param_mods, generate_obj = function(text) if M.yesno(text, false) then return { is_boolean = true } end return { text = text } end, }) local template_args = { [1] = lang_code } for name in pairs(param_mods) do template_args[name] = parsed[name] end if not parsed.is_boolean then template_args[2] = parsed.text end return " " .. frame:expandTemplate({ title = template_name, args = template_args, }) end -- Centralized term formatting: handles suppress_term (-), unknown_term (empty/+), and regular terms function Util.format_term(term, is_toplevel, opts) opts = opts or {} -- suppress_term (-) returns nil if term.suppress_term then return nil end local lang = term.lang local exc = Util.get_lang_exception(lang) if is_toplevel then local display_text = term.alt or term.title or "" local sc = term.sc or lang:findBestScript(display_text) local bold_text = tostring(mw.html.create("strong") :addClass("selflink") :wikitext(display_text)) return M.script_utilities.tag_text(bold_text, lang, sc, "term") end local link_params = { lang = lang } link_params.term = not term.unknown_term and term.title or nil link_params.alt = term.alt link_params.id = (not term.unknown_term and term.id and term.id ~= "") and term.id or nil if not (exc and exc.suppress_tr) then link_params.tr = term.tr link_params.ts = term.ts else link_params.suppress_tr = true end link_params.lit = (opts.lit ~= "suppress") and term.lit or nil if opts.gloss ~= "suppress" then link_params.gloss = term.gloss end link_params.genders = term.genders if opts.pos ~= "suppress" then link_params.pos = term.pos link_params.ng = term.ng link_params.infl = term.infl end if exc and exc.suppress_tr then link_params.lit = nil end if opts.tree_ql ~= "suppress" then if term.q then link_params.q = term.q end if term.qq then link_params.qq = term.qq end if term.l then link_params.l = term.l end if term.ll then link_params.ll = term.ll end link_params.show_decorations = term.q or term.qq or term.l or term.ll end return M.links.full_link(link_params, "term") end local __is_content_page_cached function Util.is_content_page() if __is_content_page_cached == nil then __is_content_page_cached = M.pages.is_content_page(mw.title.getCurrentTitle()) end return __is_content_page_cached end local __page_data_cached function Util.get_page_data() if not __page_data_cached then __page_data_cached = M.headword_data.page end return __page_data_cached end -- Extract base keyword from param (without modifiers) local function get_keyword_base(param) if type(param) ~= "string" then return nil end local base = param:match("^:?([^<]+)") or param:gsub("^:", "") return base end local function is_keyword(param, allow_colon_less) if type(param) ~= "string" then return false end local keywords = M.data.keywords if param:sub(1, 1) == ":" then local base = get_keyword_base(param) return keywords[base] ~= nil end if allow_colon_less then local base = get_keyword_base(param) return keywords[base] ~= nil end return false end local function get_keyword(param, allow_colon_less) if type(param) ~= "string" then return nil end local keywords = M.data.keywords if param:sub(1, 1) == ":" then return get_keyword_base(param) end if allow_colon_less then local base = get_keyword_base(param) if keywords[base] then return base end end return nil end local function normalize_keyword(keyword) if keyword:sub(1, 1) == ":" then return keyword end return ":" .. keyword end -- Resolve keyword (possibly an alias) to its canonical form. Used only at input boundaries local function get_canonical_keyword(keyword) if not keyword then return keyword end return M.data.keyword_canonical[keyword] or keyword end local function is_affix_group_keyword(keyword) local config = keyword and M.data.keywords[keyword] return config and config.affix_categories or false end local function reject_removed_surf_keyword(param) local base = get_keyword_base(param) if base == "surf" then error("The `:surf` keyword has been removed. Use `<surf>` on a formation keyword instead (e.g. `:af<surf>`, `:bor<surf>`).") end end local function copy_keyword_info(source) local copy = {} for k, v in pairs(source) do copy[k] = v end return copy end local function lowercase_glossary_display(text) return text:gsub("(%[%[Appendix:Glossary#[^|]+|)([^%]])([^%]]*)%]%]", function(prefix, first, rest) return prefix .. mw.ustring.lower(first) .. rest .. "]]" end) end local function surf_should_keep_formation_phrase(base) if not base.phrase then return false end if base.glossary then return true end return not (base.phrase == "from" and (base.text == "From" or base.text == "from")) end -- Runtime overrides when <surf> is present on a keyword. local function get_effective_keyword_info(keyword, modifiers) local base = M.data.keywords[keyword] if not base or not modifiers or not modifiers.surf then return base end local effective = copy_keyword_info(base) local surf_text = "By [[Appendix:Glossary#surface_analysis|surface analysis]]," local surf_phrase = "by surface analysis," effective.new_sentence = true effective.invisible = "tree" if surf_should_keep_formation_phrase(base) then effective.phrase = surf_phrase .. " " .. base.phrase if base.text then effective.text = surf_text .. " " .. lowercase_glossary_display(base.text) else effective.text = surf_text .. " " .. base.phrase end else effective.text = surf_text effective.phrase = surf_phrase end return effective end -- Build text/phrase for nominalization with <g:code> (uses data module for codes only). local function get_nominalization_label_for_g(code) if not code or code == "" then return nil end local codes = M.data.nominalization_g_codes local adj = codes[code] if not adj and #code == 2 then local gender_adj = codes[code:sub(1, 1)] local number_adj = codes[code:sub(2, 2)] if gender_adj and number_adj then adj = gender_adj .. " " .. number_adj end end if not adj then return nil end local text = adj:gsub("^%l", function(c) return string.upper(c) end) .. " [[Appendix:Glossary#nominalization|nominalization]] of" local phrase = M.en_utilities.add_indefinite_article(adj .. " [[Appendix:Glossary#nominalization|nominalization]] of", false) return { text = text, phrase = phrase } end local EtymonParser = {} -- Keyword modifier definitions EtymonParser.keyword_param_mods = M.parameter_utilities.construct_param_mods { { group = "ref" }, { param = "conj" }, -- conjunction for alternatives: "and", "or", "and/or", etc. { param = { "unc", "surf" }, type = "boolean" }, { param = "text", restrict = { keywords = { "from", "derived" } } }, { param = "lit", restrict = { affix_group = true } }, { param = "g", restrict = { keywords = { "nominalization" } } }, { param = "senseid", restrict = { keywords = { "semantic loan" } } }, } -- Term modifier definitions EtymonParser.etymon_param_mods = M.parameter_utilities.construct_param_mods { {group = {"link", "q", "l", "ref", "infl"}, exclude = {"sc"}}, {param = {"ety", "postype"}}, {param = "unc", type = "boolean"}, {param = "aftype", restrict = {affix_group = true}}, {param = {"bor", "slbor", "lbor"}, type = "boolean", restrict = {affix_group = true}}, } local function get_clean_param_mods(param_mods) local clean = {} for mod_name, mod_def in pairs(param_mods) do clean[mod_name] = {} for key, value in pairs(mod_def) do if key ~= "restrict" then clean[mod_name][key] = value end end end return clean end function EtymonParser.check_modifier_restrictions(modifiers, current_keyword, param_mods) for mod_name, mod_value in pairs(modifiers) do -- Only check restrictions if the modifier has a non-false/nil value if mod_value then local mod_def = param_mods[mod_name] if mod_def and mod_def.restrict then if mod_def.restrict.affix_group then if not is_affix_group_keyword(current_keyword) then local mod_display = mod_value == true and "<" .. mod_name .. ">" or "<" .. mod_name .. ":" .. tostring(mod_value) .. ">" error("The modifier `" .. mod_display .. "` is only allowed for affix-group keywords (e.g. `:af`, `:blend`, `:univ`).") end elseif mod_def.restrict.keywords then local allowed_keywords = mod_def.restrict.keywords local is_allowed = false for _, allowed_keyword in ipairs(allowed_keywords) do if current_keyword == allowed_keyword then is_allowed = true break end end if not is_allowed then local keyword_list = {} for _, kw in ipairs(allowed_keywords) do table.insert(keyword_list, ":" .. kw) end local keyword_str = table.concat(keyword_list, #keyword_list == 2 and " or " or ", ") if #keyword_list > 2 then -- Replace last comma with "or" keyword_str = keyword_str:gsub(", ([^,]+)$", " or %1") end local mod_display = mod_value == true and "<" .. mod_name .. ">" or "<" .. mod_name .. ":" .. tostring(mod_value) .. ">" error("The modifier `" .. mod_display .. "` is only allowed for the keyword" .. (#keyword_list > 1 and "s " or " ") .. keyword_str .. ".") end end end end end end local TERM_RULE_DISALLOW = { suppress = { field = "suppress_term", label = "suppressed" }, unknown = { field = "unknown_term", label = "unknown" }, family = { field = "is_family", label = "family" }, } function EtymonParser.check_etymon_limits(count, limits, label, opts) if not limits then return end opts = opts or {} local min_etymons = limits.min_etymons if min_etymons == nil and not opts.skip_default_min then min_etymons = 1 end if min_etymons and count < min_etymons then if min_etymons > 1 then error("Detected " .. label .. " group with fewer than " .. min_etymons .. " etymons.") else error("Detected " .. label .. " with no etymons.") end end if limits.max_etymons and count > limits.max_etymons then local unit = (limits.max_etymons == 1) and "etymon" or "etymons" error("Detected " .. label .. " with more than " .. limits.max_etymons .. " " .. unit .. ".") end end function EtymonParser.check_term_rules(etymon_data, entry_lang, rules, label) label = label or "term" if rules and rules.disallow then local disallowed = {} for _, typ in ipairs(rules.disallow) do local spec = TERM_RULE_DISALLOW[typ] if spec and etymon_data[spec.field] then table.insert(disallowed, spec.label) end end if #disallowed > 0 then error(label .. " does not support " .. mw.text.listToText(disallowed, "or") .. " etymons.") end end if etymon_data.is_family then if rules and rules.family == "disallowed" then error(label .. " does not support family codes" .. (rules.family_suffix or ".")) elseif not etymon_data.suppress_term then error("Family codes require suppressed term (use family:-).") end end if rules then if rules.require_term and (not etymon_data.term or etymon_data.term == "") then error(label .. " requires a term for each listed form.") end if rules.entry_lang then if Util.get_norm_lang(etymon_data.lang):getFullCode() ~= Util.get_norm_lang(entry_lang):getFullCode() then error(label .. " terms must be in the entry language (" .. entry_lang:getFullCode() .. "), got '" .. etymon_data.lang:getFullCode() .. "'.") end end if rules.ancestor_check then M.etymology.check_ancestor(entry_lang, etymon_data.lang) end elseif etymon_data.is_family and not etymon_data.suppress_term then error("Family codes require suppressed term (use family:-).") end end function EtymonParser.check_keyword_term(etymon_data, entry_lang, keyword) local config = M.data.keywords[keyword] EtymonParser.check_term_rules(etymon_data, entry_lang, config and config.term_rules, "`:" .. keyword .. "`") end function EtymonParser.check_supplement_term(etymon_data, entry_lang, supplement_type) local config = M.data.supplements[supplement_type] EtymonParser.check_term_rules(etymon_data, entry_lang, config and config.term_rules, "|" .. supplement_type .. "=") end -- Parse keyword with modifiers (e.g., ":bor<unc>" or ":bor<ref:{{R:example}}>") function EtymonParser.parse_keyword_modifiers(param) if type(param) ~= "string" then return nil, {} end local base_keyword = get_keyword_base(param) if not base_keyword then return nil, {} end local canonical_keyword = get_canonical_keyword(base_keyword) -- Check if there are any modifiers if not param:find("<", 1, true) then return canonical_keyword, {} end -- Parse modifiers using the same mechanism as etymon parsing local rest_with_defaults = Util.add_boolean_defaults(param, EtymonParser.keyword_param_mods) local function generate_obj(ignored) return {} end local parsed = M.parse_utilities.parse_inline_modifiers(rest_with_defaults:gsub("^:?[^<]+", ""), { param_mods = get_clean_param_mods(EtymonParser.keyword_param_mods), generate_obj = generate_obj }) local modifiers = { unc = parsed.unc or false, refs = parsed.refs, text = parsed.text, lit = parsed.lit, conj = parsed.conj, g = parsed.g, surf = parsed.surf or false, senseid = parsed.senseid, } -- Validate modifiers against restrictions EtymonParser.check_modifier_restrictions(modifiers, canonical_keyword, EtymonParser.keyword_param_mods) return canonical_keyword, modifiers end local function normalize_keyword_param(keyword_with_mods) local trimmed = M.string_utilities.trim(keyword_with_mods) reject_removed_surf_keyword(trimmed:match("^:") and trimmed or (":" .. trimmed)) local base = get_keyword_base(trimmed) if not base or not M.data.keywords[base] then error("Invalid keyword '" .. trimmed .. "' in inline etymology") end local canonical_base = get_canonical_keyword(base) local without_colon = trimmed:gsub("^:", "") local mods_part = without_colon:sub(#base + 1) local kw_param = normalize_keyword(canonical_base .. mods_part) EtymonParser.parse_keyword_modifiers(kw_param) return kw_param end local function get_keyword_mod_names() local names = {} for mod_name in pairs(EtymonParser.keyword_param_mods) do names[mod_name] = true end return names end local function parse_inline_ety_run(ety_string) local body = ety_string or "" if body == "" then error("Empty inline etymology") end local keyword_mod_names = get_keyword_mod_names() local pos = 1 local len = #body local function parse_err(msg) error(msg .. " in inline etymology: '" .. body .. "'") end local function peek_double() return body:sub(pos, pos + 1) == "<<" end local function mod_name_from_unwrapped(unwrapped) return unwrapped:match("^<([^:>]+)") end local function is_keyword_mod(unwrapped) local name = mod_name_from_unwrapped(unwrapped) return name and keyword_mod_names[name] or false end local function read_double_bracket() if not peek_double() then return nil end local start = pos pos = pos + 2 while pos <= len - 1 do if body:sub(pos, pos + 1) == ">>" then local token = body:sub(start, pos + 1) pos = pos + 2 return token, token:sub(2, -2) end pos = pos + 1 end parse_err("Unmatched <<") end local function read_angle_cell() if body:sub(pos, pos) ~= "<" or peek_double() then return nil end local open = pos pos = pos + 1 local depth = 1 local i = pos while i <= len do local ch = body:sub(i, i) if ch == "<" then depth = depth + 1 elseif ch == ">" then depth = depth - 1 if depth == 0 then local inner = body:sub(open + 1, i - 1) pos = i + 1 return inner end end i = i + 1 end parse_err("Unmatched <") end local function read_bare_run() local start = pos while pos <= len and body:sub(pos, pos) ~= "<" do pos = pos + 1 end return body:sub(start, pos - 1) end local function absorb_double_keyword_mods(keyword_str) while peek_double() do local saved = pos local _, unwrapped = read_double_bracket() if is_keyword_mod(unwrapped) then keyword_str = keyword_str .. unwrapped else pos = saved break end end return keyword_str end local kw_start = pos while pos <= len and body:sub(pos, pos) ~= "<" do pos = pos + 1 end local keyword = body:sub(kw_start, pos - 1) if keyword:match("^%s*$") then parse_err("Missing keyword") end keyword = absorb_double_keyword_mods(keyword) local cells = {} while pos <= len do if peek_double() then local _, unwrapped = read_double_bracket() if is_keyword_mod(unwrapped) then parse_err("Unexpected keyword modifier " .. unwrapped .. " outside of a keyword") end table.insert(cells, "+" .. unwrapped) elseif body:sub(pos, pos) == "<" then local inner = read_angle_cell() if inner ~= "" then table.insert(cells, inner) end else local bare = read_bare_run() if bare ~= "" then if bare:sub(1, 1) ~= ":" then parse_err("Unexpected bare text '" .. bare .. "' (use :keyword for nested keywords in inline etymology)") end if not is_keyword(bare, true) then parse_err("Invalid keyword '" .. bare .. "' in inline etymology") end table.insert(cells, absorb_double_keyword_mods(bare)) end end end return { keyword = keyword, cells = cells, } end function EtymonParser.inline_ety_to_pipe(ety_string) local run = parse_inline_ety_run(ety_string) if not run.keyword or run.keyword:match("^%s*$") then return "|" end local pipe_parts = { normalize_keyword_param(M.string_utilities.trim(run.keyword)) } for _, segment in ipairs(run.cells) do if is_keyword(segment, true) then table.insert(pipe_parts, normalize_keyword_param(segment)) else table.insert(pipe_parts, segment) end end return "|" .. table.concat(pipe_parts, "|") .. "|" end function EtymonParser.pipe_to_inline_ety(pipe_string) local cells = {} for cell in pipe_string:gmatch("([^|]+)") do if cell ~= "" then table.insert(cells, cell) end end if #cells == 0 then return "" end local inline_parts = {} for index, cell in ipairs(cells) do local base = get_keyword_base(cell) if base and M.data.keywords[base] then local without_colon = cell:gsub("^:", "") local kw_base, mods = without_colon:match("^([^<]+)(.*)$") local inline_kw = (kw_base or without_colon) .. (mods or ""):gsub("<([^>]+)>", "<<%1>>") if index > 1 then inline_kw = ":" .. inline_kw end table.insert(inline_parts, inline_kw) elseif cell:sub(1, 1) == "+" then local mod = cell:sub(2) if mod:match("^<.->$") then mod = mod:sub(2, -2) end table.insert(inline_parts, "<<" .. mod .. ">>") else table.insert(inline_parts, "<" .. cell .. ">") end end return table.concat(inline_parts, "") end function EtymonParser.parse_inline_ety(ety_string, context_lang) local run = parse_inline_ety_run(ety_string) local keyword = M.string_utilities.trim(run.keyword) reject_removed_surf_keyword(":" .. keyword) if not is_keyword(keyword, true) then error("Invalid keyword '" .. keyword .. "' in inline etymology <ety:" .. keyword .. "...>") end local args = { context_lang:getCode(), normalize_keyword_param(keyword) } for _, segment in ipairs(run.cells) do if is_keyword(segment, true) then table.insert(args, normalize_keyword_param(segment)) else table.insert(args, segment) end end return args end function EtymonParser.parse_etymon(param, context_lang) if is_keyword(param) then return nil end if type(param) ~= "string" then return nil end local lang, rest local is_family = false local before_bracket = param:match("^([^<]*)") or param local lang_code, rest_match = before_bracket:match("^([a-zA-Z][a-zA-Z0-9._-]*):(.*)$") if lang_code then local potential_lang = Util.get_lang(lang_code, true) if potential_lang then lang = potential_lang rest = param:sub(#lang_code + 2) else local potential_family = Util.get_family(lang_code) if potential_family then lang = potential_family rest = param:sub(#lang_code + 2) is_family = true else lang = context_lang rest = param end end else lang = context_lang rest = param end M.tracking.track_term(rest) if rest == "" or rest == "+" then return { lang = lang, term = nil, unknown_term = true, is_family = is_family, } end if rest == "-" then return { lang = lang, term = nil, suppress_term = true, is_family = is_family, } end if not rest:find("<", 1, true) then return { lang = lang, term = M.string_utilities.trim(rest), is_family = is_family, } end local term_text = rest:match("^([^<]*)") or "" local is_unknown = (term_text == "" or term_text == "+") local is_suppress = (term_text == "-") local function generate_obj(ignored_term) return { term = (is_unknown or is_suppress) and nil or M.string_utilities.trim(term_text) } end local rest_with_defaults = Util.add_boolean_defaults(rest, EtymonParser.etymon_param_mods) local parsed_obj = M.parse_utilities.parse_inline_modifiers(rest_with_defaults, { param_mods = get_clean_param_mods(EtymonParser.etymon_param_mods), generate_obj = generate_obj }) if parsed_obj.id and parsed_obj.id:match("^!") then parsed_obj.id = parsed_obj.id:sub(2) parsed_obj.override = true end parsed_obj.lang = lang parsed_obj.is_family = is_family if is_unknown then parsed_obj.unknown_term = true elseif is_suppress then parsed_obj.suppress_term = true end return parsed_obj end function EtymonParser.validate(lang, args, id, title, pos, starts_with_lang_code) -- id is now optional, so only validate if provided if id then if mw.ustring.len(id) < 2 then error("The `id` parameter must have at least two characters.") end if id == title or id == Util.get_page_data().pagename then error("The `id` parameter must not be the same as the page title.") end end local valid_pos = { prefix = true, suffix = true, interfix = true, infix = true, root = true, word = true } if pos and not valid_pos[pos] then error("Unknown value provided for `pos`. Valid values: " .. table.concat(require("Module:table").keysToList(valid_pos), ", ") .. ".") end local current_keyword = "from" local current_keyword_explicit = false local keyword_etymons = {} local keywords = M.data.keywords local function checkKeyword() local config = keywords[current_keyword] if current_keyword == "from" and not current_keyword_explicit and #keyword_etymons == 0 then keyword_etymons = {} return end EtymonParser.check_etymon_limits(#keyword_etymons, config, "`:" .. current_keyword .. "`") keyword_etymons = {} end local start_index = starts_with_lang_code and 2 or 1 for i = start_index, #args do local param = args[i] if type(param) ~= "string" then elseif param:sub(1, 1) == ":" and not is_keyword(param) then reject_removed_surf_keyword(param) error("Invalid keyword '" .. param .. "'. Did you mean a valid keyword like ':bor', ':inh', etc.?") elseif is_keyword(param) then checkKeyword() current_keyword = get_canonical_keyword(get_keyword(param)) current_keyword_explicit = true else local etymon_data = EtymonParser.parse_etymon(param, lang) if etymon_data then table.insert(keyword_etymons, param) EtymonParser.check_keyword_term(etymon_data, lang, current_keyword) -- Check modifier restrictions EtymonParser.check_modifier_restrictions(etymon_data, current_keyword, EtymonParser.etymon_param_mods) -- postype must be "root" or "word" local VALID_POSTYPES = { root = true, word = true } if etymon_data.postype and not VALID_POSTYPES[etymon_data.postype] then error("Invalid <postype:" .. etymon_data.postype .. ">; must be \"root\" or \"word\".") end if etymon_data.ety then local inline_args = EtymonParser.parse_inline_ety(etymon_data.ety, etymon_data.lang) EtymonParser.validate(etymon_data.lang, inline_args, nil, nil, nil, true) end else table.insert(keyword_etymons, param) end end end checkKeyword() end local DataRetriever = {} local function format_etymon_id_hint(id_data, idx) local id = type(id_data) == "table" and id_data.id or id_data local pos = type(id_data) == "table" and id_data.pos if id and id ~= "" and id ~= "*" then return '"' .. id .. '"' end if pos and pos ~= "" then return "unnamed (|pos=" .. pos .. "|)" end return "etymon #" .. idx .. " (no |id= on page)" end local function etymon_target_page_link(page, norm_lang) return M.links.full_link({ term = page, lang = norm_lang, no_generate_alternants = true, }, "term") end -- Summarize {{etymon}} id slots on a linked page for preview warnings. local function summarize_available_etymon_ids(ids) local id_list = {} local all_idless = true local target_has_idless = false local any_pos = false for i, id_data in ipairs(ids) do local id = type(id_data) == "table" and id_data.id or id_data local pos = type(id_data) == "table" and id_data.pos if id and id ~= "" and id ~= "*" then all_idless = false else target_has_idless = true end if pos and pos ~= "" then any_pos = true end table.insert(id_list, format_etymon_id_hint(id_data, i)) end return { id_list = id_list, all_idless = all_idless, target_has_idless = target_has_idless, any_pos = any_pos, count = #ids, options_text = mw.text.listToText(id_list), } end local function ambiguous_etymon_suggestion(page_link, summary) if summary.all_idless then if summary.any_pos then return " None set `|id=` yet; add a unique `|id=` to each on " .. page_link .. ", then `<id:identifier>` after the term here. Section order / hints: " .. summary.options_text .. "." end return " None set `|id=` yet; add a unique `|id=` to each {{etymon}} in that section from top to bottom, then `<id:identifier>` after the term here (same value as `|id=`)." end return " Specify which one with `<id:identifier>` after the term. Options: " .. summary.options_text .. "." end local function warn_ambiguous_etymon_link(page, norm_lang, ids, is_toplevel) local page_link = etymon_target_page_link(page, norm_lang) local summary = summarize_available_etymon_ids(ids) if is_toplevel and summary.target_has_idless then __state.linked_page_multiple_etymons_idless = true end local lang_name = norm_lang:getCanonicalName() local lead = "Etymology link to " .. page_link .. " is ambiguous (" .. summary.count .. " {{etymon}} templates for " .. lang_name .. ")." Util.add_warning(lead .. ambiguous_etymon_suggestion(page_link, summary), true) end local function is_mismatched_explicit_id(base_key, cached_args, parent_etymon) return cached_args == M.data.STATUS.MISSING and not parent_etymon and #(__state.available_etymon_ids[base_key] or {}) > 0 end local function maybe_flag_partial_etymology_reference(base_key, etymon_data, cached_args, is_toplevel) if not is_toplevel or __state.skip_partial_etymology_category then return end if not __state.partial_etymology_targets[base_key] then return end if etymon_data.id and type(cached_args) == "table" then return end __state.linked_page_partial_etymology_sections = true end local function is_nonlemma_etymon_template(template_args) return template_args and M.yesno(template_args.nl, false) end local function warn_mismatched_explicit_id(page, norm_lang, base_key, etymon_id) local page_link = etymon_target_page_link(page, norm_lang) local summary = summarize_available_etymon_ids(__state.available_etymon_ids[base_key] or {}) local lang_name = norm_lang:getCanonicalName() local lead = "Etymology link to " .. page_link .. " uses `<id:" .. etymon_id .. ">`, but no {{etymon}} on that page has `|id=" .. etymon_id .. "|` for " .. lang_name .. "." Util.add_warning(lead .. " Valid IDs: " .. summary.options_text .. ".", true) end -- Given an etymon data, scrape its page and cache the result in the global state object. function DataRetriever.cache_page_etymons(etymon_page, etymon_title, key, etymon_lang, etymon_id, redirected_from, descendants_is_toplevel) local content = etymon_title:getContent() if not content then __state.cached_etymon_args[key] = M.data.STATUS.REDLINK return end -- Check if the linked page is a redirect. If it is, the template parsing -- code below will be effectively skipped, and `scrape_page` will be called -- again on the redirect target (see the bottom of this function) local lang_section_for_descendants = nil local redirect_target = etymon_title.redirect_target if not redirect_target then content = M.pages.get_section(content, etymon_lang:getFullName(), 2) if not content then __state.cached_etymon_args[key] = M.data.STATUS.MISSING return end lang_section_for_descendants = content end local etymon_lang_code = etymon_lang:getFullCode() local lang_page_key = etymon_lang_code .. ":" .. etymon_page local found_templates_for_lang = {} local found_ids = {} local get_node_class = M.template_parser.class_else_type -- Look for all {{etymon}} templates within the page content using the template parser -- This way the same page is never parsed more than once -- Build a map from senseids to their parent etymonids. local active_etymon_args = nil local etymology_section_count = 0 local etymology_sections_with_etymon = 0 local current_etymology_has_etymon = false local current_etymology_has_nonlemma = false local function finalize_current_etymology_section() if etymology_section_count == 0 then return end if current_etymology_has_etymon or current_etymology_has_nonlemma then etymology_sections_with_etymon = etymology_sections_with_etymon + 1 end current_etymology_has_etymon = false current_etymology_has_nonlemma = false end for node in M.template_parser.parse(content):iterate_nodes() do local node_class = get_node_class(node) if node_class == "heading" then -- A new L2 or etymology section acts as a barrier: an {{etymon}} usage -- used previously cannot be the parent of any subsequent senseids. -- Note that we don't have to check for L2s due to the usage of `M.pages.get_section` above. if node:get_name():find("^Etymology") then finalize_current_etymology_section() etymology_section_count = etymology_section_count + 1 active_etymon_args = nil end elseif node_class == "template" then local template_name = node:get_name() if template_name == "etymon" then local template_args = node:get_arguments() -- Check if this etymon is for our language if template_args[1] == etymon_lang_code then if is_nonlemma_etymon_template(template_args) then if etymology_section_count > 0 then current_etymology_has_nonlemma = true end else if etymology_section_count > 0 then current_etymology_has_etymon = true end table.insert(found_templates_for_lang, template_args) if template_args.id then local etymon_key = lang_page_key .. ":" .. template_args.id __state.cached_etymon_args[etymon_key] = template_args __state.cached_etymon_pages[etymon_key] = tostring(etymon_page) table.insert(found_ids, template_args.id) active_etymon_args = template_args else -- Store idless etymon with default key local etymon_key = lang_page_key .. ":*" __state.cached_etymon_args[etymon_key] = template_args __state.cached_etymon_pages[etymon_key] = tostring(etymon_page) table.insert(found_ids, "*") active_etymon_args = template_args end end end elseif active_etymon_args and template_name == "senseid" then local template_args = node:get_arguments() -- This should always be true for proper usages of {{senseid}}. if template_args[1] == etymon_lang_code and template_args[2] then local sense_id_key = lang_page_key .. ":" .. template_args[2] __state.senseid_parent_etymon[sense_id_key] = active_etymon_args __state.cached_etymon_pages[sense_id_key] = tostring(etymon_page) end end end end finalize_current_etymology_section() if lang_section_for_descendants and etymology_section_count > 1 and etymology_sections_with_etymon > 0 and etymology_sections_with_etymon < etymology_section_count then __state.partial_etymology_targets[lang_page_key] = true end if descendants_is_toplevel and lang_section_for_descendants and #found_templates_for_lang > 0 then M.descendants.cache_page_checks({ lang_section = lang_section_for_descendants, etymon_lang_code = etymon_lang_code, found_templates_for_lang = found_templates_for_lang, entry_title = __state.entry_title, entry_lang_code = __state.entry_lang_code, entry_lang = __state.entry_lang_code and Util.get_lang(__state.entry_lang_code, true) or nil, cached_descendants_checks = __state.cached_descendants_checks, lang_page_key = lang_page_key, redirected_from = redirected_from, }) end local id_data_list = {} for _, args in ipairs(found_templates_for_lang) do local id = args.id or "*" table.insert(id_data_list, { id = id, pos = args.pos }) end __state.available_etymon_ids[lang_page_key] = id_data_list if #found_templates_for_lang == 1 then __state.single_etymons[lang_page_key] = found_templates_for_lang[1] end if redirected_from and __state.available_etymon_ids[lang_page_key] then __state.available_etymon_ids[redirected_from] = __state.available_etymon_ids[redirected_from] or {} for _, id_data in ipairs(__state.available_etymon_ids[lang_page_key]) do table.insert(__state.available_etymon_ids[redirected_from], id_data) end end if __state.cached_etymon_args[key] ~= nil or __state.senseid_parent_etymon[key] ~= nil then -- All done! return elseif redirect_target and not redirected_from then -- Try scraping the redirect. etymon_page = redirect_target.prefixedText DataRetriever.cache_page_etymons(etymon_page, redirect_target, lang_page_key .. ":" .. etymon_id, etymon_lang, etymon_id, lang_page_key, descendants_is_toplevel) __state.cached_etymon_args[key] = __state.cached_etymon_args[etymon_lang_code .. ":" .. etymon_page .. ":" .. etymon_id] else __state.cached_etymon_args[key] = M.data.STATUS.MISSING end end local function has_linkable_term(etymon_data) if etymon_data.is_family or etymon_data.suppress_term or etymon_data.unknown_term then return false end local term = etymon_data.term if term == nil or term == "" then return false end return M.string_utilities.trim(term) ~= "" end local function map_affix_part(etymon_data) if etymon_data.alt or not has_linkable_term(etymon_data) or etymon_data.term:find("^%^") then return end local affix_type = etymon_data.aftype and (M.data.aftype_aliases[etymon_data.aftype] or etymon_data.aftype) or nil local _, link_term, display_term = M.affix.parse_term_for_affixes(etymon_data.term, etymon_data.lang, nil, affix_type, true, nil, etymon_data.id) if link_term ~= display_term then etymon_data.term = link_term etymon_data.alt = display_term end end local function record_term_id_tracking(etymon_data) if not has_linkable_term(etymon_data) then return end local term_page = M.links.get_link_page(etymon_data.term, etymon_data.lang) M.tracking.record_term_id_usage(__state.id_stats, etymon_data, term_page) end -- Given an etymon object, scrape its page (if necessary) and return its own etymon arguments as well as the page name. function DataRetriever.get_etymon_args(etymon_data, is_toplevel) if not has_linkable_term(etymon_data) then return M.data.STATUS.MISSING, nil, nil, nil end local page = M.links.get_link_page(etymon_data.term, etymon_data.lang) local norm_lang = Util.get_norm_lang(etymon_data.lang) local base_key = norm_lang:getFullCode() .. ":" .. page if etymon_data.id then local key = base_key .. ":" .. etymon_data.id local cached_args = __state.cached_etymon_args[key] or __state.senseid_parent_etymon[key] if cached_args == nil then local title = mw.title.new(page) if not title then error('Invalid page title "' .. page .. '" encountered.') end DataRetriever.cache_page_etymons(page, title, key, norm_lang, etymon_data.id, nil, is_toplevel) end cached_args = __state.cached_etymon_args[key] or __state.senseid_parent_etymon[key] -- refresh -- Get etymon_id from parent if this was resolved via senseid local parent_etymon = __state.senseid_parent_etymon[key] local resolved_etymon_id = parent_etymon and parent_etymon.id local descendants_check = M.descendants.get_lookup_check({ cached_descendants_checks = __state.cached_descendants_checks, is_toplevel = is_toplevel, base_key = base_key, lookup = { explicit_id = etymon_data.id, parent_etymon = parent_etymon, }, }) if is_toplevel and descendants_check == nil then local title = mw.title.new(page) if title then DataRetriever.cache_page_etymons(page, title, key, norm_lang, etymon_data.id, nil, true) descendants_check = M.descendants.get_lookup_check({ cached_descendants_checks = __state.cached_descendants_checks, is_toplevel = true, base_key = base_key, lookup = { explicit_id = etymon_data.id, parent_etymon = parent_etymon, }, }) end end local mismatched_id = is_mismatched_explicit_id(base_key, cached_args, parent_etymon) if mismatched_id and is_toplevel then __state.has_mismatched_id = true M.tracking.record_mismatched_id_usage(__state.id_stats, norm_lang, page, etymon_data.id) warn_mismatched_explicit_id(page, norm_lang, base_key, etymon_data.id) end maybe_flag_partial_etymology_reference(base_key, etymon_data, cached_args, is_toplevel) return cached_args, __state.cached_etymon_pages[key], resolved_etymon_id, descendants_check else __state.used_idless_etymon = true if is_toplevel then __state.toplevel_idless_etymon = true end if __state.available_etymon_ids[base_key] == nil then local title = mw.title.new(page) if not title then error('Invalid page title "' .. page .. '" encountered.') end DataRetriever.cache_page_etymons(page, title, base_key .. ":*", norm_lang, "*", nil, is_toplevel) end local ids = __state.available_etymon_ids[base_key] or {} local count = #ids -- Try to filter by postype if available and we have multiple candidates if count > 1 and etymon_data.postype then local matching_ids = {} for _, id_data in ipairs(ids) do if id_data.pos == etymon_data.postype then table.insert(matching_ids, id_data) end end if #matching_ids == 1 then local matched_id = matching_ids[1].id local matched_key = base_key .. ":" .. matched_id M.tracking.record_idless_resolution(__state.id_stats, norm_lang, page, "postype") local descendants_check = M.descendants.get_lookup_check({ cached_descendants_checks = __state.cached_descendants_checks, is_toplevel = is_toplevel, base_key = base_key, lookup = { id = matched_id }, }) if is_toplevel and descendants_check == nil then local title = mw.title.new(page) if title then DataRetriever.cache_page_etymons(page, title, base_key .. ":*", norm_lang, "*", nil, true) descendants_check = M.descendants.get_lookup_check({ cached_descendants_checks = __state.cached_descendants_checks, is_toplevel = true, base_key = base_key, lookup = { id = matched_id }, }) end end local matched_args = __state.cached_etymon_args[matched_key] maybe_flag_partial_etymology_reference(base_key, etymon_data, matched_args, is_toplevel) return matched_args, __state.cached_etymon_pages[matched_key], nil, descendants_check end end if count == 1 then local only_id_data = ids[1] local only_id = (type(only_id_data) == "table" and only_id_data.id) or only_id_data or "*" M.tracking.record_idless_resolution(__state.id_stats, norm_lang, page, "single") local descendants_check = M.descendants.get_lookup_check({ cached_descendants_checks = __state.cached_descendants_checks, is_toplevel = is_toplevel, base_key = base_key, lookup = { id_data = only_id_data }, }) if is_toplevel and descendants_check == nil then local title = mw.title.new(page) if title then DataRetriever.cache_page_etymons(page, title, base_key .. ":*", norm_lang, "*", nil, true) descendants_check = M.descendants.get_lookup_check({ cached_descendants_checks = __state.cached_descendants_checks, is_toplevel = true, base_key = base_key, lookup = { id_data = only_id_data }, }) end end local single_args = __state.single_etymons[base_key] maybe_flag_partial_etymology_reference(base_key, etymon_data, single_args, is_toplevel) return single_args, __state.cached_etymon_pages[base_key .. ":" .. only_id], nil, descendants_check elseif count > 1 then M.tracking.record_idless_resolution(__state.id_stats, norm_lang, page, "ambiguous") warn_ambiguous_etymon_link(page, norm_lang, ids, is_toplevel) maybe_flag_partial_etymology_reference(base_key, etymon_data, M.data.STATUS.AMBIGUOUS, is_toplevel) return M.data.STATUS.AMBIGUOUS, nil, nil, nil else M.tracking.record_idless_resolution(__state.id_stats, norm_lang, page, "missing") maybe_flag_partial_etymology_reference(base_key, etymon_data, M.data.STATUS.MISSING, is_toplevel) return M.data.STATUS.MISSING, nil, nil, nil end end end local function keyword_invisible_in_tree(keyword_info) if not keyword_info then return false end local inv = keyword_info.invisible return inv == "all" or inv == true or inv == "tree" end -- True when the node has at least one top-level child container visible in the tree. local function node_has_visible_tree_children(node) for _, container in ipairs(node.children or {}) do if not keyword_invisible_in_tree(container.keyword_info) then return true end end return false end -- Count visible term nodes in the tree. local function get_visible_tree_depth(node, skip_child_rendering) local max_depth = 1 if skip_child_rendering or not node then return max_depth end for _, container in ipairs(node.children or {}) do local keyword_info = container.keyword_info if not keyword_invisible_in_tree(keyword_info) then local skip_grandchildren = keyword_info and keyword_info.no_child_categories for _, term in ipairs(container.terms or {}) do if term.is_duplicate then if term.original_has_children then max_depth = math.max(max_depth, 2) end else max_depth = math.max(max_depth, 1 + get_visible_tree_depth(term, skip_grandchildren)) end end end end return max_depth end local function as_param_list(val) if val == nil then return {} end if type(val) == "table" then return val end if type(val) == "string" and val ~= "" then return { val } end return {} end local TreeBuilder = {} -- Build a unique key for deduplication in the seen table function TreeBuilder.build_key(lang, title, args) local norm_lang_code = Util.get_norm_lang(lang):getFullCode() local is_table = type(args) == "table" local id = (is_table and args.id) or "" if title then return norm_lang_code .. ":" .. M.links.get_link_page(title, lang) .. ":" .. id end if is_table and args.status == M.data.STATUS.INLINE then local content_parts = {} for i = 1, #args do content_parts[i] = tostring(args[i]) end return norm_lang_code .. ":*:" .. id .. "\0" .. table.concat(content_parts, "\0") end return norm_lang_code .. ":*:" .. id end -- Copy parsed etymon modifiers onto a tree/supplement term node. function TreeBuilder.apply_etymon_fields(term, etymon_data) term.id = etymon_data.id term.gloss = etymon_data.gloss term.tr = etymon_data.tr term.ts = etymon_data.ts term.alt = etymon_data.alt term.genders = etymon_data.genders term.pos = etymon_data.pos term.ng = etymon_data.ng term.infl = etymon_data.infl term.refs = etymon_data.refs term.is_uncertain = etymon_data.unc term.lit = etymon_data.lit term.q = etymon_data.q term.qq = etymon_data.qq term.l = etymon_data.l term.ll = etymon_data.ll term.suppress_term = etymon_data.suppress_term term.unknown_term = etymon_data.unknown_term term.is_family = etymon_data.is_family term.override = etymon_data.override term.aftype = etymon_data.aftype term.postype = etymon_data.postype term.bor = etymon_data.bor term.lbor = etymon_data.lbor term.slbor = etymon_data.slbor end function TreeBuilder.build_supplement_term(etymon_data, entry_lang, supplement_type) EtymonParser.check_supplement_term(etymon_data, entry_lang, supplement_type) local term = { lang = etymon_data.lang, title = etymon_data.term, children = {}, status = M.data.STATUS.OK, } TreeBuilder.apply_etymon_fields(term, etymon_data) return term end function TreeBuilder.build_supplement_terms(entry_lang, supplement_type, param_value) local terms = {} for _, term_param in ipairs(as_param_list(param_value)) do if type(term_param) == "string" and term_param ~= "" then local etymon_data = EtymonParser.parse_etymon(term_param, entry_lang) if etymon_data then table.insert(terms, TreeBuilder.build_supplement_term(etymon_data, entry_lang, supplement_type)) end end end return terms end -- Attach a |param= supplement defined in etymon_data.supplements (e.g. doublet=). function TreeBuilder.append_term_supplement(data_tree, entry_lang, supplement_type, param_value) local config = M.data.supplements[supplement_type] if not config then error("Unknown supplement '" .. tostring(supplement_type) .. "'.") end local terms = TreeBuilder.build_supplement_terms(entry_lang, supplement_type, param_value) if #terms == 0 then return end data_tree.supplements = data_tree.supplements or {} table.insert(data_tree.supplements, { type = supplement_type, config = config, terms = terms, }) M.tracking.record_keyword_usage(__state.toplevel_keyword_stats, supplement_type, entry_lang, entry_lang, true) end function TreeBuilder.build(lang, title, args, seen, depth, stop_recursion) seen = seen or {} depth = depth or 0 local is_toplevel = (depth == 0) if depth > __state.max_depth_reached then __state.max_depth_reached = depth end __state.total_nodes = __state.total_nodes + 1 local lang_code = lang:getCode() __state.language_count[lang_code] = (__state.language_count[lang_code] or 0) + 1 local current_id = (type(args) == "table" and args.id) or "" local key = TreeBuilder.build_key(lang, title, args) local node = { lang = lang, title = title, id = current_id, args = args, children = {}, status = M.data.STATUS.OK } if type(args) ~= "table" or seen[key] then node.status = args or M.data.STATUS.MISSING -- Mark as duplicate if we've seen this node before if seen[key] then node.is_duplicate = true node.duplicate_key = key local original_node = seen[key] if type(original_node) == "table" and original_node.children and #original_node.children > 0 then node.original_has_children = true end end return node end node.status = args.status or M.data.STATUS.OK seen[key] = node -- If stop_recursion is set, skip parsing children but check for visible children if stop_recursion then local keywords = M.data.keywords local has_visible_children = false for i = 2, #args do local param = args[i] if type(param) == "string" then local keyword_base = get_keyword_base(param) if keyword_base and keywords[keyword_base] then local _, kw_modifiers = EtymonParser.parse_keyword_modifiers(param:sub(1, 1) == ":" and param or (":" .. param)) if not keyword_invisible_in_tree(get_effective_keyword_info(keyword_base, kw_modifiers)) then has_visible_children = true break end elseif param:sub(1, 1) ~= ":" then -- It's a term (not a keyword), so there are visible children has_visible_children = true break end end end node.has_visible_children = has_visible_children return node end -- Parse args into keyword containers local current_keyword = "from" local current_keyword_modifiers = {} local current_container = nil local function ensure_container() if not current_container or current_container.keyword ~= current_keyword then local keyword_info = get_effective_keyword_info(current_keyword, current_keyword_modifiers) current_container = { keyword = current_keyword, keyword_info = keyword_info, keyword_modifiers = current_keyword_modifiers, terms = {}, } table.insert(node.children, current_container) -- Override keyword text/phrase for nominalization with <g:code> if current_keyword_modifiers.g and current_keyword == "nominalization" then local labels = get_nominalization_label_for_g(current_keyword_modifiers.g) if not labels then local codes = {} for c in pairs(M.data.nominalization_g_codes) do table.insert(codes, c) end table.sort(codes) error("Invalid <g:" .. tostring(current_keyword_modifiers.g) .. ">. Supported codes for nominalization: " .. table.concat(codes, ", ")) end current_container.keyword_info = copy_keyword_info(keyword_info) current_container.keyword_info.text = labels.text current_container.keyword_info.phrase = labels.phrase end end return current_container end local parse_context_lang = Util.resolve_context_lang(lang, args) for i = 2, #args do local param = args[i] if is_keyword(param) then local keyword, modifiers = EtymonParser.parse_keyword_modifiers(param) if not keyword then error("Invalid keyword '" .. param .. "'.") end current_keyword = keyword current_keyword_modifiers = modifiers current_container = nil -- Force new container for new keyword elseif type(param) == "string" and param:sub(1, 1) == ":" then reject_removed_surf_keyword(param) error("Invalid keyword '" .. param .. "'. Did you mean a valid keyword like ':bor', ':inh', etc.?") elseif type(param) == "string" then local etymon_data = EtymonParser.parse_etymon(param, parse_context_lang) if etymon_data and is_affix_group_keyword(current_keyword) then map_affix_part(etymon_data) end if etymon_data then -- Track keyword usage at top level M.tracking.record_keyword_usage(__state.toplevel_keyword_stats, current_keyword, lang, etymon_data.lang, is_toplevel) local term_node = {} local container -- Handle suppress_term (-) and unknown_term (empty or +) directly if etymon_data.suppress_term or etymon_data.unknown_term then container = ensure_container() if etymon_data.ety then local inline_args = EtymonParser.parse_inline_ety(etymon_data.ety, etymon_data.lang) inline_args.id = etymon_data.id inline_args.status = M.data.STATUS.INLINE term_node = TreeBuilder.build(etymon_data.lang, nil, inline_args, seen, depth + 1) else term_node = { lang = etymon_data.lang, children = {}, status = M.data.STATUS.OK, } end TreeBuilder.apply_etymon_fields(term_node, etymon_data) else -- Regular term: fetch arguments from page record_term_id_tracking(etymon_data) local etymon_args, page_of, resolved_etymon_id, descendants_check = DataRetriever.get_etymon_args(etymon_data, is_toplevel) -- Check for <ety> inline parameter doesn't override the scraped arguments, unless the latter are missing if etymon_data.ety then if etymon_args == M.data.STATUS.REDLINK or etymon_args == M.data.STATUS.MISSING then __state.current_page_has_inline_etymology = true if is_toplevel then __state.toplevel_has_inline_etymology = true end local inline_args = EtymonParser.parse_inline_ety(etymon_data.ety, etymon_data.lang) -- Track inline ety keywords too local inline_keyword = get_keyword(inline_args[2], true) if inline_keyword and #inline_args >= 3 then local inline_etymon = EtymonParser.parse_etymon(inline_args[3], etymon_data.lang) if inline_etymon then M.tracking.record_keyword_usage(__state.toplevel_keyword_stats, inline_keyword, etymon_data.lang, inline_etymon.lang, is_toplevel) end end inline_args.id = etymon_data.id inline_args.status = M.data.STATUS.INLINE etymon_args = inline_args term_node.page_of = __state.cached_etymon_pages[key] -- term node is on the same page as the parent else -- Scraped arguments exist, <ety> is redundant and ignored __state.current_page_has_redundant_etymology = true if is_toplevel then __state.toplevel_redundant_etymology = true end end end -- Ensure container exists before checking keyword info container = ensure_container() -- Check if current keyword has no_child_categories - if so, stop recursion local keyword_info = container.keyword_info local should_stop_recursion = (stop_recursion or (keyword_info and keyword_info.no_child_categories)) term_node = TreeBuilder.build(etymon_data.lang, etymon_data.term, etymon_args, seen, depth + 1, should_stop_recursion) term_node.target_key = Util.get_norm_lang(etymon_data.lang):getFullCode() .. ":" .. M.links.get_link_page(etymon_data.term, etymon_data.lang) term_node.etymon_id = resolved_etymon_id -- The actual etymon id when resolved via senseid term_node.page_of = page_of TreeBuilder.apply_etymon_fields(term_node, etymon_data) term_node.missing_descendants_header, term_node.missing_descendants_entry = M.descendants.get_term_sync_flags(current_keyword, term_node.status, descendants_check) end table.insert(container.terms, term_node) end end end return node end -- Convert etymology tree to JSON-serializable table local function tree_to_json(node) local obj = { term = node.title, lang = node.lang:getCode(), lang_name = node.lang:getCanonicalName(), id = (node.id and node.id ~= "") and node.id or nil, status = node.status, is_uncertain = node.is_uncertain or nil, is_duplicate = node.is_duplicate or nil, gloss = node.gloss, transliteration = node.tr, transcription = node.ts, alt = node.alt, g = node.genders, pos = node.pos, ng = node.ng, infl = node.infl, children = {}, } for _, container in ipairs(node.children or {}) do local keyword_info = container.keyword_info if keyword_info then local container_obj = { keyword = container.keyword, keyword_label = keyword_info.text, keyword_abbrev = keyword_info.abbrev, is_group = keyword_info.is_group or nil, is_invisible = keyword_info.invisible or nil, is_uncertain = (container.keyword_modifiers and container.keyword_modifiers.unc) or nil, terms = {}, } for _, term in ipairs(container.terms or {}) do table.insert(container_obj.terms, tree_to_json(term)) end table.insert(obj.children, container_obj) end end return obj end -- Build and return the etymology data tree for a given term. function export.get_tree(lang, title, args, options) options = options or {} __state.entry_title = title __state.entry_lang_code = lang:getCode() __state.id_stats = M.tracking.new_id_stats() __state.skip_partial_etymology_category = options.skip_partial_etymology_category == true if options.validate then EtymonParser.validate(lang, args, options.id, title, options.pos, false) end local lang_code = lang:getCode() local start_index = (args[1] == lang_code) and 2 or 1 local tree_args = { [1] = lang_code, id = options.id or args.id } for i = start_index, #args do table.insert(tree_args, args[i]) end __state.cached_etymon_args[lang_code .. ":" .. title .. ":" .. (tree_args.id or "")] = tree_args local ety_data_tree = TreeBuilder.build(lang, title, tree_args) if options.json then return M.JSON.toJSON(tree_to_json(ety_data_tree)) end return ety_data_tree end -- Given a language code, page name and optionally the id= parameter, -- render the tree and only the etymology tree for the relevant page. -- Fetches and parses the corresponding {{etymon}} from the requested page, -- and any further pages needed to render the tree. -- Parameters can be passed either through the #invoke or as -- template parameters *through* an #invoke. function export.render_tree_for_etymon_on_page(frame) local frame_args = frame.args local parent_args = frame:getParent().args local langcode = frame_args[1] or parent_args[1] local pagename = frame_args[2] or parent_args[2] local id = frame_args["id"] or parent_args["id"] local display_title = frame_args["title"] or parent_args["title"] local parsed_title = mw.title.new(pagename, 0) local title if parsed_title.namespace == 0 then title = M.pages.safe_page_name(parsed_title) elseif parsed_title.namespace == 118 then title = "*" .. M.pages.safe_page_name(parsed_title) else error("Unsupported namespace for render_tree_for_etymon_on_page: " .. parsed_title.namespace) end local lang = Util.get_lang(langcode) __state.entry_title = title __state.entry_lang_code = lang:getCode() __state.id_stats = M.tracking.new_id_stats() -- Construct etymon_data for DataRetriever.get_args. local etymon_data = { lang = lang, term = title, id = id } local args, pagename = DataRetriever.get_etymon_args(etymon_data, true) if args == M.data.STATUS.MISSING then error("The etymon template was not found (language " .. langcode .. ", title '" .. title .. "'" .. (id and ", ID '" .. id .. "'" or ", no ID given") .. "). Page contents may have changed in the interim.") end local tree_title = display_title or title if lang:stripDiacritics(M.links.remove_links(tree_title)) ~= lang:stripDiacritics(M.links.remove_links(title)) then M.tracking.track_title_pagename_mismatch(lang) end reset_invocation_state() local ety_data_tree = export.get_tree(lang, tree_title, args, { validate = true, id = id, }) local output = {} table.insert(output, M.template_styles("Module:etymon/styles.css")) table.insert(output, M.tree.render({ data_tree = ety_data_tree, format_term_func = function(term, is_toplevel) return Util.format_term(term, is_toplevel, { gloss = "suppress", pos = "suppress", lit = "suppress", tree_ql = "suppress", }) end, })) return table.concat(output) end function export.main(frame) local parent_args = frame:getParent().args local args = M.parameters.process(parent_args, M.parameters_data.etymon) local lang = args[1] local etymon_args = args[2] local id = args.id local title = args.title local text = args.text local tree = args.tree local etydate = args.etydate local doublet = args.doublet local rfe = args.rfe local etystub = args.etystub local is_nonlemma = M.yesno(args.nl, false) local page_data = Util.get_page_data() if not title then title = page_data.pagename if page_data.namespace == "Reconstruction" then title = "*" .. title end end local entry_pagename = page_data.pagename if page_data.namespace == "Reconstruction" then entry_pagename = "*" .. entry_pagename end if lang:stripDiacritics(M.links.remove_links(title)) ~= lang:stripDiacritics(M.links.remove_links(entry_pagename)) then M.tracking.track_title_pagename_mismatch(lang) end local current_L2 = M.pages.get_current_L2() if current_L2 then local norm_lang = Util.get_norm_lang(lang) local norm_name = norm_lang:getCanonicalName() if current_L2 ~= norm_name then local lang_desc = lang:getCode() .. " (" .. lang:getCanonicalName() .. ")" if norm_lang:getCode() ~= lang:getCode() then lang_desc = lang_desc .. ", normalized to " .. norm_lang:getCode() .. " (" .. norm_name .. ")" end error("Language '" .. lang_desc .. "' does not match the L2 header (" .. current_L2 .. ").") end end reset_invocation_state() local ety_data_tree = export.get_tree(lang, title, etymon_args, { validate = true, pos = args.pos, id = id, json = args.json, skip_partial_etymology_category = is_nonlemma, }) if args.json then return ety_data_tree end local output = {} local text_allowlist_mode = M.text_allowed.default_mode or "off" if text and text_allowlist_mode ~= "off" and not Util.is_text_param_allowed_for_lang(lang) then local msg = "Etymology texts (parameter <code>text=</code>) are not allowed for " .. lang:getFullName() .. "; see [[Template:etymon#Text allowlist|Template:etymon § Text allowlist]] for the list of languages that may use the <code>text=</code> parameter." if text_allowlist_mode == "error" then error(msg) else Util.add_warning(msg, true) end end local lang_exc = Util.get_lang_exception(lang) if lang_exc and lang_exc.disallow then local disallow = lang_exc.disallow local error_text = " for " .. lang:getFullName() if disallow.ref then error_text = error_text .. "; see " .. disallow.ref else error_text = error_text .. "." end if tree and disallow.tree then error("Etymology trees are not allowed" .. error_text) end if text and disallow.text then error("Etymology texts are not allowed" .. error_text) end end if etydate then local etydate_param_mods = M.parameter_utilities.construct_param_mods { { group = "ref" }, { param = "refn" }, { param = "nocap", type = "boolean" }, } local function generate_etydate_obj(etydate_text) local etydate_specs = {} for spec in etydate_text:gmatch("[^,]+") do table.insert(etydate_specs, mw.text.trim(spec)) end return { [1] = etydate_specs } end local parsed_etydate = M.parse_utilities.parse_inline_modifiers(etydate, { param_mods = etydate_param_mods, generate_obj = generate_etydate_obj }) local etydate_args = { [1] = parsed_etydate[1], nocap = parsed_etydate.nocap or false, } ety_data_tree.supplements = ety_data_tree.supplements or {} table.insert(ety_data_tree.supplements, { type = "etydate", etydate_text = M.etydate.format_etydate(etydate_args, { omit_refs = true }), etydate_refs = (parsed_etydate.refs and #parsed_etydate.refs > 0) and parsed_etydate.refs or nil, }) end TreeBuilder.append_term_supplement(ety_data_tree, lang, "doublet", doublet) local has_visible_children = node_has_visible_tree_children(ety_data_tree) -- Suppress trees for multiword entries and one-step chains local visible_tree_depth = get_visible_tree_depth(ety_data_tree) local is_trivial_tree = visible_tree_depth <= 2 local is_multiword = title:find("%s") ~= nil or title:find("_") ~= nil if tree and (is_multiword or is_trivial_tree) then tree = false end if tree then table.insert(output, M.template_styles("Module:etymon/styles.css")) table.insert(output, M.tree.render({ data_tree = ety_data_tree, format_term_func = function(term, is_toplevel) return Util.format_term(term, is_toplevel, { gloss = "suppress", pos = "suppress", lit = "suppress", tree_ql = "suppress", }) end, })) end local tree_disallowed = lang_exc and lang_exc.disallow and lang_exc.disallow.tree local ety_tree_json = M.JSON.toJSON(tree_to_json(ety_data_tree)) local anchor = M.anchors.etymonid(lang, id, { no_tree = args.notree, title = title, empty_tree = (not has_visible_children) or tree_disallowed, ety_tree_json = ety_tree_json, }) table.insert(output, anchor) local text_stop_lang_missing = nil if text then local max_depth, stop_at_blue_link, stop_at_lang, stop_at_lang_or_bluelink if text == "++" then max_depth, stop_at_blue_link = false, false elseif text == "+" then max_depth, stop_at_blue_link = 1, false elseif text == "*" then max_depth, stop_at_blue_link = false, true elseif text:match("^:[^*]+%*$") then -- Stop at a specific language OR first bluelink after it, e.g., ":ota*" -- If the target language is a redlink, continue to the first bluelink local lang_code = text:match("^:([^*]+)%*$") if lang_code and lang_code ~= "" then local lang_obj = Util.get_lang(lang_code, true) if lang_obj then stop_at_lang_or_bluelink = lang_code else Util.add_warning('Invalid language code "' .. lang_code .. '" in text parameter. Showing full chain instead.') max_depth, stop_at_blue_link = false, false end else Util.add_warning('Empty language code in text parameter. Showing full chain instead.') max_depth, stop_at_blue_link = false, false end elseif text:sub(1, 1) == ":" then -- Stop at a specific language, e.g., ":ar" stops at first Arabic term local lang_code = text:sub(2) if lang_code ~= "" then -- Validate the language code local lang_obj = Util.get_lang(lang_code, true) if lang_obj then stop_at_lang = lang_code else Util.add_warning('Invalid language code "' .. lang_code .. '" in text parameter. Showing full chain instead.') max_depth, stop_at_blue_link = false, false -- default to ++ end else Util.add_warning('Empty language code in text parameter. Showing full chain instead.') max_depth, stop_at_blue_link = false, false -- default to ++ end else local num = tonumber(text) if num and num >= 1 then max_depth, stop_at_blue_link = num, false else error('Invalid text value "' .. text .. '". Valid values are: "++" (full chain), "+" (first step only), "*" (until first blue link), a number (max steps), ":lang" (stop at language), or ":lang*" (stop at language or first bluelink if redlink)') end end local text_output, text_render_meta = M.text.render({ data_tree = ety_data_tree, format_term_func = Util.format_term, lang_matches_stop_code = Util.lang_matches_stop_code, max_depth = max_depth, stop_at_blue_link = stop_at_blue_link, curr_page = page_data.pagename, nodot = args.nodot, dot = args.dot, stop_at_lang = stop_at_lang, stop_at_lang_or_bluelink = stop_at_lang_or_bluelink, }) table.insert(output, text_output) if stop_at_lang and text_render_meta and not text_render_meta.stop_lang_reached then M.tracking.track_text_stop_lang_missing(lang, stop_at_lang) text_stop_lang_missing = stop_at_lang end end if rfe then table.insert(output, Util.expand_request_template(frame, "rfe", rfe, lang:getCode())) end if etystub then table.insert(output, Util.expand_request_template(frame, "etystub", etystub, lang:getCode())) end if is_nonlemma then table.insert(output, " " .. frame:expandTemplate({ title = "nonlemma", args = {}, })) end local categories = {} if Util.is_content_page() then M.tracking.track_tree_metrics({ max_depth_reached = __state.max_depth_reached, total_nodes = __state.total_nodes, language_count = __state.language_count, lang = lang, }) categories = M.categories.build({ data_tree = ety_data_tree, page_lang = lang, available_etymon_ids = __state.available_etymon_ids, senseid_parent_etymon = __state.senseid_parent_etymon, get_norm_lang_func = Util.get_norm_lang, lang_exc = lang_exc, suppress_categories = lang_exc and lang_exc.suppress_categories, nocat = args.nocat, tree = tree, text = text, exnihilo = args.exnihilo, toplevel_has_inline_etymology = __state.toplevel_has_inline_etymology, toplevel_redundant_etymology = __state.toplevel_redundant_etymology, toplevel_idless_etymon = __state.toplevel_idless_etymon, has_mismatched_id = __state.has_mismatched_id, linked_page_multiple_etymons_idless = __state.linked_page_multiple_etymons_idless, linked_page_partial_etymology_sections = __state.linked_page_partial_etymology_sections, text_stop_lang_missing = text_stop_lang_missing, }) M.tracking.track_keywords(__state.toplevel_keyword_stats, lang) M.tracking.track_page_id(lang, id) M.tracking.track_ids(__state.id_stats, lang) end if #categories > 0 then table.insert(output, M.categories.format(categories, lang)) end if __state.warnings then for i, warning in ipairs(__state.warnings) do table.insert(output, (i == 1 and "\n" or "") .. warning .. "\n") end end return table.concat(output) end return export a9o2xk8i75wom7ia6inch310f4dka6m Module:parameters/data 828 8133 55022 2026-09-30T19:18:20Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local list_to_set = require("Module:table").listToSet local alias_of_2 = {alias_of = 2} local boolean = {type = "boolean"} local boolean_default_false = {type = "boolean", default = false} local list = {list = true} local list_allow_holes_separate_no_index = {list = true, allow_holes = true, separate_no_index = true} local no_parameters = {} local required = {required = true} local required_default_ = {required = true, default = ""} local required_lang_default_u...' 55022 Scribunto text/plain local list_to_set = require("Module:table").listToSet local alias_of_2 = {alias_of = 2} local boolean = {type = "boolean"} local boolean_default_false = {type = "boolean", default = false} local list = {list = true} local list_allow_holes_separate_no_index = {list = true, allow_holes = true, separate_no_index = true} local no_parameters = {} local required = {required = true} local required_default_ = {required = true, default = ""} local required_lang_default_und = {required = true, type = "language", default = "und"} local type_labels = {type = "labels"} local type_qualifier = {type = "qualifier"} local type_references = {type = "references"} local m = {} -- [[Module:anchors]] m["anchor"] = { [1] = {required = true, list = true, disallow_holes = true}, } m["senseid"] = { [1] = required_lang_default_und, [2] = required_default_, id = alias_of_2, tag = {set = list_to_set{"li", "p"}, default = "li"}, } m["etymid"] = { [1] = required_lang_default_und, [2] = required_default_, id = alias_of_2, } -- [[Module:etymon]] m["etymon"] = { [1] = required_lang_default_und, [2] = {list = true, disallow_holes = true}, id = true, title = true, tree = boolean, text = true, exnihilo = boolean, etydate = true, doublet = {sublist = "comma without whitespace"}, rfe = true, etystub = true, nl = boolean, pos = true, notree = boolean_default_false, nocat = boolean_default_false, nodot = boolean_default_false, dot = true, json = boolean_default_false, } -- [[Module:transclude]] m["transclude"] = { [1] = {required = true, type = "language"}, [2] = {list = true, required = true}, id = true, sort = true, nogloss = boolean_default_false, no_truncate_gloss = boolean, include_place_extra_info = boolean, place_translation_follows = boolean, place_addl = true, lb = true, nolb = true, nocat = boolean, to = boolean, t = list, indent = true, dot = boolean, pagename = true, } -- [[Module:translations]] m["translation"] = { [1] = required_lang_default_und, [2] = true, [3] = list, alt = true, id = true, sc = {type = "script"}, tr = true, ts = true, lit = true, l = type_labels, ll = type_labels, q = type_qualifier, qq = type_qualifier, ref = type_references, } m["t-needed"] = { [1] = required_lang_default_und, [2] = {set = list_to_set{"usex", "quote"}}, nocat = boolean, sort = true, } m["trans-top"] = { [1] = true, id = true, ["column-width"] = true, } m["trans-top-also"] = { [1] = required, [2] = list, id = list_allow_holes_separate_no_index, ["column-width"] = true, } m["checktrans-top"] = { [1] = true, ["column-width"] = true, } m["trans-bottom"] = no_parameters m["trans-see"] = { [1] = required, [2] = list, id = list_allow_holes_separate_no_index, } m["translation subpage"] = no_parameters m["no equivalent translation"] = { [1] = required_lang_default_und, noend = boolean, } m["no attested translation"] = { [1] = required_lang_default_und, noend = boolean, sort = true, } m["not used"] = { [1] = required_lang_default_und, [2] = true, } return m 5s1ntnu4nh24xfe4y9oqjeouq18285i Module:languages/data/3/g 828 8134 55023 2026-09-30T19:18:46Z Deadend0914 7211 Gesceop tramet þe hafaþ '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["gaa"] = { "Ga", 33287, "alv-gda", "Latn", } m["gab"] = { "Gabri", 3441237, "cdc-est", "Latn", } m["gac"] = { "Mixed Great Andamanese", 5632963...' 55023 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["gaa"] = { "Ga", 33287, "alv-gda", "Latn", } m["gab"] = { "Gabri", 3441237, "cdc-est", "Latn", } m["gac"] = { "Mixed Great Andamanese", 56329630, "qfa-adn", "Latn", } m["gad"] = { -- not to be confused with gdk, gdg "Gaddang", 3438830, "phi", "Latn", } m["gae"] = { "Baniwa of Guainia", -- Warekena, distinguish from awd-war 1091095, "awd-nwk", "Latn", } m["gaf"] = { "Gende", 3100425, "ngf-gor", "Latn", } m["gag"] = { "Gagauz", 33457, "trk-ogz", "Latn, Cyrl", ancestors = "trk-oat", dotted_dotless_i = true, sort_key = { Latn = { from = { "i", -- Ensure "i" comes after "ı". "ä", "ç", "ê", "ı", "ö", "ş", "ţ", "ü" }, to = { "i" .. p[1], "a" .. p[1], "c" .. p[1], "e" .. p[1], "i", "o" .. p[1], "s" .. p[1], "t" .. p[1], "u" .. p[1] } }, }, } m["gah"] = { "Alekano", 3441595, "ngf-gah", "Latn", } m["gai"] = { "Borei", 6799756, "paa-ott", "Latn", } m["gaj"] = { "Gadsup", 5516467, "ngf-gau", "Latn", } m["gak"] = { "Gamkonora", 5520226, "paa-sah", "Latn", } m["gal"] = { "Galoli", 35322, "poz-tim", "Latn", } m["gam"] = { "Kandawo", 6361369, "ngf-jim", "Latn", } m["gan"] = { "Gan", 33475, "zhx", "Hants", ancestors = "ltc", generate_alternants = "zh-generatealternants", translit = "zh-translit", sort_key = "Hani-sortkey", } m["gao"] = { "Gants", 5521529, "ngf-eso", "Latn", } m["gap"] = { "Gal", 5517742, "ngf-han", "Latn", } m["gaq"] = { "Gata'", 3501920, "mun", "Orya", } m["gar"] = { "Galeya", 5518509, "poz-ocw", "Latn", } m["gas"] = { "Adiwasi Garasia", 12953522, "inc-bhi", "Deva, Gujr", ancestors = "bhb", } m["gat"] = { "Kenati", 4219330, "ngf-kgo", "Latn", } m["gau"] = { "Kondekor", 12952433, "dra-pgd", "Telu", } m["gaw"] = { "Nobonob", 11732205, "ngf-han", "Latn", } m["gay"] = { "Gayo", 33286, "poz-nws", "Latn", } m["gbb"] = { "Kaytetye", 6380709, "aus-rnd", "Latn", } m["gbd"] = { "Karadjeri", 3913837, "aus-pam", "Latn", } m["gbe"] = { "Niksek", 56375, "paa-sep", "Latn", } m["gbf"] = { "Gaikundi", 5517032, "paa-nnd", "Latn", } m["gbg"] = { "Gbanziri", 35306, "nic-nkg", "Latn", } m["gbh"] = { "Defi Gbe", 12952446, "alv-gbe", "Latn", } m["gbi"] = { "Galela", 3094570, "paa-gto", "Latn", } m["gbj"] = { "Bodo Gadaba", 3347070, "mun", "Orya", } m["gbk"] = { "Gaddi", 17455500, "him", "Deva, Takr", translit = {Deva = "hi-translit"}, } m["gbl"] = { "Gamit", 2731717, "inc-bhi", "Deva, Gujr", } m["gbm"] = { "Garhwali", 33459, "inc-pac", "Deva", translit = "hi-translit", } m["gbn"] = { "Mo'da", 12755683, "csu-bbk", "Latn", } m["gbo"] = { "Northern Grebo", 11157042, "grb", "Latn", } m["gbp"] = { "Gbaya-Bossangoa", 11011295, "gba-wes", "Latn", } m["gbq"] = { "Gbaya-Bozoum", 4952879, "gba-wes", "Latn", } m["gbr"] = { "Gbagyi", 11015105, "alv-ngb", "Latn", } m["gbs"] = { "Gbesi Gbe", 12952448, "alv-pph", "Latn", } m["gbu"] = { "Gagadu", 35677, "aus-arn", "Latn", } m["gbv"] = { "Gbanu", 3914945, "gba-eas", "Latn", } m["gbw"] = { "Gabi", 5515391, "aus-pam", "Latn", } m["gbx"] = { "Eastern Xwla Gbe", 18379975, "alv-pph", "Latn", } m["gby"] = { "Gbari", 3915451, "alv-ngb", "Latn", } m["gcc"] = { "Mali", 6743338, "paa-bai", "Latn", } m["gcd"] = { "Ganggalida", 3913765, "aus-tnk", "Latn", } m["gce"] = { "Galice", 20711, "ath-pco", "Latn", } m["gcf"] = { "Antillean Creole", 3006280, "crp", "Latn", ancestors = "fr", sort_key = s["roa-oil-sortkey"], } m["gcl"] = { "Grenadian Creole English", 4252500, "crp", "Latn", ancestors = "en", } m["gcn"] = { "Gaina", 11732195, "ngf-gko", "Latn", } m["gcr"] = { "Guianese Creole", 1363072, "crp", "Latn", ancestors = "fr", sort_key = s["roa-oil-sortkey"], } m["gct"] = { "Colonia Tovar German", 1138351, "gmw-hgm", "Latn", ancestors = "gsw", } m["gdb"] = { "Ollari", 33906, "dra-pgd", "Orya, Telu", translit = { Telu = "te-translit" }, } m["gdc"] = { "Gugu Badhun", 10510360, "aus-pam", "Latn", } m["gdd"] = { "Gedaged", 35292, "poz-ocw", "Latn", } m["gde"] = { "Gude", 3441230, "cdc-cbm", "Latn", } m["gdf"] = { "Guduf-Gava", 3441350, "cdc-cbm", "Latn", } m["gdg"] = { -- not to be confused with gad, gdk "Ga'dang", 5515189, "phi", "Latn", } m["gdh"] = { "Gadjerawang", 3913817, "aus-jar", "Latn", } m["gdi"] = { "Gundi", 11137851, "nic-nkb", "Latn", } m["gdj"] = { "Kurtjar", 5619931, "aus-pmn", "Latn", } m["gdk"] = { -- not to be confused with gad, gdg "Gadang", 56256, "cdc-est", "Latn", } m["gdl"] = { "Dirasha", 56809, "cus-eas", "Ethi", } m["gdm"] = { "Laal", 33436, "qfa-dis", -- Chad; unclassified, isolate or grouped with Adamawa or Chadic languages "Latn", } m["gdn"] = { "Umanakaina", 7881084, "ngf-dag", "Latn", } m["gdo"] = { "Godoberi", 56515, "cau-and", "Cyrl", display_text = {Cyrl = s["cau-Cyrl-displaytext"]}, strip_diacritics = {Cyrl = s["cau-Cyrl-stripdiacritics"]}, } m["gdq"] = { "Mehri", 13361, "sem-sar", "Arab, Latn", } m["gdr"] = { "Wipi", 8026711, "paa-etf", "Latn", } m["gds"] = { "Ghandruk Sign Language", 15971577, "sgn", } m["gdt"] = { "Kungardutyi", 6444517, "aus-kar", "Latn", } m["gdu"] = { "Gudu", 3441172, "cdc-cbm", "Latn", } m["gdx"] = { "Godwari", 3540922, "raj", "Deva", } m["gea"] = { "Geruma", 3438789, "cdc-wst", "Latn", } m["geb"] = { "Kire", 11129733, "paa-rub", "Latn", } m["gec"] = { "Gboloo Grebo", 11019342, "grb", "Latn", } m["ged"] = { "Gade", 3914459, "alv-nup", "Latn", } m["geg"] = { "Gengle", 3438345, "alv-mye", "Latn", ancestors = "kow", } m["geh"] = { "Hutterisch", 33385, "gmw-hgm", "Latn", ancestors = "bar", } m["gei"] = { "Gebe", 3100032, "poz-hce", "Latn", } m["gej"] = { "Gen", 33450, "alv-gbe", "Latn", } m["gek"] = { "Gerka", 3441277, "cdc-wst", "Latn", } m["gel"] = { "Fakkanci", 36627, "nic-knn", "Latn", } m["geq"] = { "Geme", 3915851, "znd", "Latn", } m["ges"] = { "Geser-Gorom", 5553579, "poz-cma", "Latn", } m["gev"] = { "Viya", 7937974, "bnt-tso", "Latn", } m["gew"] = { "Gera", 3438725, "cdc-wst", "Latn", } m["gex"] = { "Garre", 56618, "cus-som", "Latn", } m["gey"] = { "Enya", 5381452, "bnt-mbe", "Latn", } m["gez"] = { "Ge'ez", 35667, "sem-eth", "Ethi", translit = "Ethi-translit", } m["gfk"] = { "Patpatar", 3368846, "poz-ocw", "Latn", } m["gft"] = { "Gafat", 56910, "sem-eth", "Ethi, Latn", } m["gga"] = { "Gao", 3095228, "poz-ocw", "Latn", } m["ggb"] = { "Gbii", 3914390, "kro-wkr", "Latn", } m["ggd"] = { "Gugadj", 5615186, "aus-pmn", "Latn", } m["gge"] = { "Guragone", 5619801, "aus-arn", "Latn", } m["ggg"] = { "Gurgula", 5620032, "raj", "Arab", } m["ggk"] = { "Kungarakany", 6444516, "aus-arn", "Latn", } m["ggl"] = { "Ganglau", 5521140, "ngf-yag", "Latn", } m["ggt"] = { "Gitua", 3107865, "poz-ocw", "Latn", } m["ggu"] = { "Gban", 3913317, "dmn-nbe", "Latn", } m["ggw"] = { "Gogodala", 3512161, "ngf-gsu", "Latn", } m["gha"] = { "Ghadames", 56747, "ber", "Latn", -- and other scripts? } m["ghc"] = { "Classical Gaelic", 5128278, "cel-gae", "Latn, Latg", ancestors = "mga", } m["ghe"] = { "Southern Ghale", 12952453, "sit-tam", "Deva", } m["ghh"] = { "Northern Ghale", 22662104, "sit-tam", "Deva", } m["ghk"] = { "Geko Karen", 5530317, "kar", } m["ghl"] = { "Ghulfan", 16885737, "nub-hil", "Latn", -- and others? } m["ghn"] = { "Ghanongga", 3104772, "poz-ocw", "Latn", } m["gho"] = { "Ghomara", 35315, "ber", "Tfng, Latn", translit = {Tfng = "Tfng-translit"}, } m["ghr"] = { "Ghera", 22808992, "inc-hiw", } m["ghs"] = { "Guhu-Samane", 11732219, "ngf-gbi", "Latn", } m["ght"] = { "Kutang Ghale", 6448337, "sit-tam", "Tibt", override_translit = true, -- Tibt translit, display_text, strip_diacritics, sort_key in [[Module:scripts/data]] } m["gia"] = { "Kitja", 1284877, "aus-jar", "Latn", } m["gib"] = { "Gibanawa", 12953530, "crp", "Latn", ancestors = "ha", } m["gid"] = { "Gidar", 35265, "cdc-cbm", "Latn", } m["gie"] = { "Guébie", 63140714, "kro-did", "Latn", } m["gig"] = { "Goaria", 33269, "raj", "Arab", } m["gih"] = { "Githabul", 48987680, "aus-pam", "Latn", } m["gii"] = { "Girirra", 5564288, "cus-som", } m["gil"] = { "Gilbertese", 30898, "poz-mic", "Latn", } m["gim"] = { "Gimi (Papuan)", 11732209, "ngf-fgi", "Latn", } m["gin"] = { "Hinukh", 33283, "cau-wts", "Cyrl", translit = "gin-translit", display_text = {Cyrl = s["cau-Cyrl-displaytext"]}, strip_diacritics = {Cyrl = s["cau-Cyrl-stripdiacritics"]}, } m["gip"] = { "Gimi (Austronesian)", 12952457, "poz-ocw", "Latn", } m["giq"] = { "Green Gelao", 12953525, "gio", "Latn", } m["gir"] = { "Red Gelao", 3100264, "gio", } m["gis"] = { "North Giziga", 3515084, "cdc-cbm", } m["git"] = { "Gitxsan", 3107862, "nai-tsi", "Latn", } m["giu"] = { "Mulao", 11092831, "gio", } m["giw"] = { "White Gelao", 8843040, "gio", } m["gix"] = { "Gilima", 10977716, "nic-nkm", "Latn", } m["giy"] = { "Giyug", 5565906, } m["giz"] = { "South Giziga", 3502232, "cdc-cbm", } m["gji"] = { "Geji", 3914890, "cdc-wst", "Latn", } m["gjk"] = { "Kachi Koli", 12953646, "inc-wes", } m["gjm"] = { "Gunditjmara", 6448731, "aus-pam", "Latn", } m["gjn"] = { "Gonja", 35267, "alv-gng", "Latn", } m["gjr"] = { "Gurindji Kriol", 5620091, "qfa-mix", "Latn", ancestors = "gue, rop" } m["gju"] = { "Gojri", 3241731, "raj", "Aran, Deva, Takr", strip_diacritics = { Aran = { remove_diacritics = c.fathatan .. c.dammatan .. c.kasratan .. c.fatha .. c.damma .. c.kasra .. c.shadda .. c.sukun .. c.nunghunna .. c.smallv, from = {"ڵ", "ݩ"}, to = {"ل", "ن"} }, }, translit = {Aran = "ur-translit"}, } m["gka"] = { "Guya", 11732221, "ngf-war", "Latn", } m["gkd"] = { "Magi", 55621742, "ngf-ais", "Latn", } m["gke"] = { "Ndai", 6983667, "alv-mbm", } m["gkn"] = { "Gokana", 3075137, "nic-ogo", "Latn", } m["gko"] = { "Kok-Nar", 6426526, "aus-pmn", "Latn", } m["gkp"] = { "Guinea Kpelle", 11052867, "dmn-msw", "Latn, Kpel", ancestors = "kpe", } m["glc"] = { "Bon Gula", 289816, "alv-bua", } m["gld"] = { "Nanai", 13303, "tuw-nan", "Cyrl", translit = "gld-translit", strip_diacritics = {remove_diacritics = c.macron}, sort_key = { from = {"ё", "ӈ"}, to = {"е" .. p[1], "н" .. p[1]} }, } m["glh"] = { "Northwest Pashayi", 23713532, "inc-pas", "Arab", } m["glj"] = { "Kulaal", 33360, "alv-bua", } m["glk"] = { "Gilaki", 33657, "ira-csp", "Arab", } m["glo"] = { "Galambu", 2598797, "cdc-wst", "Latn", } m["glr"] = { "Glaro-Twabo", 3915313, "kro-wee", } m["glu"] = { "Gula", 5617176, "csu-bgr", "Latn", } m["glw"] = { "Glavda", 3441285, "cdc-cbm", "Latn", } m["gly"] = { "Gule", 3120736, "ssa-kom", } m["gma"] = { "Gambera", 10502327, "aus-wor", "Latn", } m["gmb"] = { "Gula'alaa", 3120733, "poz-sls", "Latn", } m["gmd"] = { "Mághdì", 3914475, "alv-bwj", } m["gmg"] = { "Magiyi", 16926155, "ngf-sog", "Latn", } m["gmh"] = { "Middle High German", 837985, "gmw-hgm", "Latn", strip_diacritics = { remove_diacritics = c.circ .. c.macron, from = {"Ë", "ë", "[ƷȤ]", "[ʒȥ]"}, to = {"E", "e", "Z", "z"} }, } m["gml"] = { "Middle Low German", 505674, "gmw-lgm", "Latn", strip_diacritics = {remove_diacritics = c.circ .. c.macron .. c.diaer}, } m["gmm"] = { "Gbaya-Mbodomo", 6799713, "gba-eas", "Latn", } m["gmn"] = { "Gimnime", 11016905, "alv-dur", "Latn", } m["gmr"] = { "Mirning", 6873793, "aus-pam", "Latn", } m["gmu"] = { "Gumalu", 5618027, "ngf-gum", "Latn", } m["gmv"] = { "Gamo", 16116386, "omv-nom", "Latn, Ethi", } m["gmx"] = { "Magoma", 16939552, "bnt-bki", } m["gmy"] = { "Mycenaean Greek", 668366, "grk", "Linb", translit = "Linb-translit", strip_diacritics = { remove_diacritics = "@" .. c.dotbelow }, display_text = { from = { "@", c.dotbelow }, to = { "", "" } }, } m["gmz"] = { "Mgbo", 6826835, "alv-igb", ancestors = "izi", } m["gna"] = { "Kaansa", 56802, "nic-gur", } m["gnb"] = { "Gangte", 12952442, "tbq-kuk", } m["gnc"] = { "Guanche", 35762, "ber", } m["gnd"] = { "Zulgo-Gemzek", 56800, "cdc-cbm", "Latn", } m["gne"] = { "Ganang", 63163361, "nic-plc", ancestors = "izr", } m["gng"] = { "Ngangam", 35888, "nic-grm", "Latn", } m["gnh"] = { "Lere", 3915319, "nic-jer", } m["gni"] = { "Gooniyandi", 2669219, "aus-bub", "Latn", } m["gnj"] = { "Ngen of Djonkro", 63170838, "dmn-nbe", "Latn", } m["gnk"] = { "ǁGana", 1975199, "khi-kal", "Latn", } m["gnl"] = { "Gangulu", 4916329, "aus-pam", "Latn", } m["gnm"] = { "Ginuman", 11732210, "ngf-dag", "Latn", } m["gnn"] = { "Gumatj", 10510745, "aus-yol", "Latn", } m["gnq"] = { "Gana", 5520523, "poz-san", "Latn", } m["gnr"] = { "Gureng Gureng", 5619998, "aus-pam", "Latn", } m["gnt"] = { "Guntai", 12952475, "paa-ton", "Latn", } m["gnu"] = { "Gnau", 3915810, "paa-trr", "Latn", } m["gnw"] = { "Western Bolivian Guarani", 3775037, "gn", "Latn", } m["gnz"] = { "Ganzi", 11137942, "nic-nkb", "Latn", } m["goa"] = { "Guro", 35251, "dmn-mda", "Latn", } m["gob"] = { "Playero", 3027923, "sai-guh", } m["goc"] = { "Gorakor", 12952463, "poz-ocw", "Latn", } m["god"] = { "Godié", 3914412, "kro-bet", } m["goe"] = { "Gongduk", 2669221, "sit", } m["gof"] = { "Gofa", 12631584, "omv-nom", "Latn, Ethi", } m["gog"] = { "Gogo", 3272630, "bnt-ruv", "Latn", } m["goh"] = { "Old High German", 35218, "gmw-hgm", "Latn, Runr", strip_diacritics = { remove_diacritics = c.circ .. c.macron .. c.diaer, from = {"[ƷȤ]", "[ʒȥ]"}, to = {"Z", "z"} }, translit = { Runr = "Runr-translit", }, } m["goi"] = { "Gobasi", 5575414, "ngf-est", "Latn", } m["goj"] = { "Gowlan", 12953532, "inc-sou", } -- gok is a spurious language, see [[w:Spurious languages]] m["gol"] = { "Gola", 35482, "alv", "Latn, Vaii", } m["gon"] = { "Gondi", 1775361, "dra-gon", "Telu, Gonm, Gong, Deva, Orya", translit = { Telu = "te-translit", Gong = "gon-Gong-translit", Gonm = "gon-Gonm-translit", }, } m["goo"] = { "Gone Dau", 3110470, "poz-pcc", "Latn", } m["gop"] = { "Yeretuar", 8052565, "poz-hce", "Latn", } m["goq"] = { "Gorap", 3110816, "crp", "Latn", ancestors = "ms", } m["gor"] = { "Gorontalo", 2501174, "phi", "Latn", } m["got"] = { "Gothic", 35722, "gme", "Goth, Runr, Latn", translit = {Goth = "Goth-translit"}, link_tr = true, strip_diacritics = {Latn = {remove_diacritics = c.macron}}, } m["gou"] = { "Gavar", 3441180, "cdc-cbm", } m["gov"] = { "Goo", 16927208, "dmn", "Latn", } m["gow"] = { "Gorwaa", 3437626, "cus-sou", "Latn", } m["gox"] = { "Gobu", 7194986, "bad-cnt", } m["goy"] = { "Goundo", 317636, "alv-kim", } m["goz"] = { "Gozarkhani", 5590235, "xme-ttc", ancestors = "xme-ttc-eas", } m["gpa"] = { "Gupa-Abawa", 3915352, "alv-ngb", "Latn", } m["gpn"] = { "Taiap", 56237, "qfa-dis", -- Papuan; isolate in Glottolog; relationship with Torricelli proposed by Usher "Latn", } m["gqa"] = { "Ga'anda", 56245, "cdc-cbm", "Latn", } m["gqi"] = { "Guiqiong", 3120647, "sit-qia", } m["gqn"] = { -- a variety of 'ter' "Kinikinao", 53386731, "awd", "Latn", } m["gqr"] = { "Gor", 759992, "csu-sar", "Latn", } m["gqu"] = { "Qau", 17284874, "gio", } m["gra"] = { "Rajput Garasia", 21041529, "inc-bhi", "Deva, Gujr", ancestors = "bhb", } m["grc"] = { "Ancient Greek", 35497, "grk", "Polyt, Cprt", translit = { Cprt = "Cprt-translit", }, override_translit = true, -- Polyt translit, display_text, strip_diacritics, sort_key in [[Module:scripts/data]] standard_chars = { Polyt = "ΑΆἈἉἊἋἌἍἎἏᾈᾉᾊᾋᾌᾍᾎᾏᾸᾹᾺᾼΒΓΔΕΈἘἙἚἛἜἝῈΖΗΉἨἩἪἫἬἭἮἯᾘᾙᾚᾛᾜᾝᾞᾟῊῌΘΙΊΪἸἹἺἻἼἽἾἿῘῙῚΚΛΜΝΞΟΌὈὉὊὋὌὍΠΡῬΡ̓ΣΤΥΎΫὙὛὝὟῨῩῪΦΧΨΩΏὨὩὪὫὬὭὮὯᾨᾩᾪᾫᾬᾭᾮᾯῸῺῼαάἀἁἂἃἄἅἆἇὰᾀᾁᾂᾃᾄᾅᾆᾇᾰᾱᾲᾳᾴᾶᾷβγδεέἐἑἒἓἔἕὲζηήἠἡἢἣἤἥἦἧὴᾐᾑᾒᾓᾔᾕᾖᾗῂῃῄῆῇθιίϊΐἰἱἲἳἴἵἶἷὶῐῑῒῖῗκλμνξοόὀὁὂὃὄὅὸπρῤῥςστυύϋΰὐὑὒὓὔὕὖὗὺῠῡῢῦῧφχψωώὠὡὢὣὤὥὦὧὼᾠᾡᾢᾣᾤᾥᾦᾧῲῳῴῶῷ·ͺ΄΅᾽᾿῀῁῍῎῏῝῞῟῭`´῾", Cprt = "𐠀𐠁𐠂𐠃𐠄𐠅𐠈𐠊𐠋𐠌𐠍𐠎𐠏𐠐𐠑𐠒𐠓𐠔𐠕𐠖𐠗𐠘𐠙𐠚𐠛𐠜𐠝𐠞𐠟𐠠𐠡𐠢𐠣𐠤𐠥𐠦𐠧𐠨𐠩𐠪𐠫𐠬𐠭𐠮𐠯𐠰𐠱𐠲𐠳𐠴𐠵𐠷𐠸𐠼𐠿", c.punc }, } m["grd"] = { "Guruntum", 3441272, "cdc-wst", "Latn", } m["grg"] = { "Madi", 6727664, "ngf-gmo", "Latn", } m["grh"] = { "Gbiri-Niragu", 3913936, "nic-kau", "Latn", } m["gri"] = { "Ghari", 3104782, "poz-sls", "Latn", } m["grj"] = { "Southern Grebo", 3914444, "grb", "Latn", } m["grm"] = { "Kota Marudu Talantang", 6433808, "poz-san", "Latn", } m["gro"] = { "Groma", 56551, "sit-tib", } m["grq"] = { "Gorovu", 56355, "paa-por", "Latn", } m["grs"] = { "Gresi", 5607612, "paa-nim", "Latn", } m["grt"] = { "Garo", 36137, "tbq-bdg", "Latn, Beng, Brai", } m["gru"] = { "Kistane", 13273, "sem-eth", "Latn, Ethi", } m["grv"] = { "Central Grebo", 18385114, "grb", "Latn", } m["grw"] = { "Gweda", 5623387, "poz-ocw", "Latn", } m["grx"] = { "Guriaso", 12631954, "qfa-unc", -- no consensus; may be Kwomtari per Baron (1983) and Usher (2020), but no connections accepted by -- Glottolog. "Latn", } m["gry"] = { "Barclayville Grebo", 11157342, "grb", "Latn", } m["grz"] = { "Guramalum", 3120935, "poz-ocw", "Latn", } m["gse"] = { "Ghanaian Sign Language", 35289, "sgn-asl", "Latn", -- when documented } m["gsg"] = { "German Sign Language", 33282, "sgn-gsl", "Sgnw", } m["gsl"] = { "Gusilay", 35439, "alv-jol", "Latn", } m["gsm"] = { "Guatemalan Sign Language", 2886781, "sgn", "Zxxx", -- unwrittten } m["gsn"] = { "Gusan", 11732224, "ngf-era", "Latn", } m["gso"] = { "Southwest Gbaya", 4919322, "gba-sou", "Latn", } m["gsp"] = { "Wasembo", 7971402, "ngf-mad", -- placed in under Rai Coast by Glottolog (under Greater Yaganon) and Pawley-Hammarström "Latn", } m["gss"] = { "Greek Sign Language", 3565084, "sgn", } m["gsw"] = { "Alemannic German", 131339, "gmw-hgm", "Latn", wikimedia_codes = "als", ancestors = "gmh", } m["gta"] = { "Guató", 3027940, "qfa-dis", -- isolate or Macro-Jê "Latn", } m["gtu"] = { "Aghu Tharrnggala", 16825981, "aus-pmn", "Latn", } m["gua"] = { "Shiki", 3913946, "nic-jrn", "Latn", } m["gub"] = { "Guajajára", 7699720, "tup-gua", "Latn", } m["guc"] = { "Wayuu", 891085, "awd-taa", "Latn", } m["gud"] = { "Yocoboué Dida", 21074781, "kro-did", "Latn", } m["gue"] = { "Gurindji", 10511016, "aus-pam", "Latn", } m["guf"] = { "Gupapuyngu", 10511004, "aus-yol", "Latn", } m["gug"] = { "Paraguayan Guarani", 17478066, "gn", "Latn", wikimedia_codes = "gn", ancestors = "gn-cls", } m["guh"] = { "Guahibo", 2669193, "sai-guh", "Latn", } m["gui"] = { "Eastern Bolivian Guarani", 2963912, "gn", "Latn", } m["guk"] = { "Gumuz", 2396970, "ssa", "Latn, Ethi", } m["gul"] = { "Gullah", 33395, "crp", "Latn", ancestors = "en", } m["gum"] = { "Guambiano", 2744745, "sai-bar", "Latn", } m["gun"] = { "Mbya Guarani", 3915584, "gn", "Latn", } m["guo"] = { "Guayabero", 2980375, "sai-guh", "Latn", } m["gup"] = { "Gunwinggu", 1406574, "aus-gun", "Latn", } m["guq"] = { "Aché", 383701, "tup", "Latn", } m["gur"] = { "Farefare", 35331, "nic-mre", "Latn", } m["gus"] = { "Guinean Sign Language", 15983937, "sgn-asl", -- sic, not sgn-fsl "Latn", -- when documented } m["gut"] = { "Maléku Jaíka", 3915782, "cba", "Latn", } m["guu"] = { "Yanomamö", 8048928, "sai-ynm", "Latn", } m["guv"] = { "Gey", 11137816, "alv-sav", "Latn", } m["guw"] = { "Gun", 3111668, "alv-gbe", "Latn", strip_diacritics = {remove_diacritics = c.grave .. c.acute .. c.macron}, } m["gux"] = { "Gourmanchéma", 35474, "nic-grm", "Latn", } m["guz"] = { "Gusii", 33603, "bnt-lok", "Latn", } m["gva"] = { "Kaskihá", 3033534, "sai-mas", "Latn", } m["gvc"] = { "Guanano", 3566001, "sai-tuc", "Latn", } m["gve"] = { "Duwet", 5317647, "poz-ocw", "Latn", } m["gvf"] = { "Golin", 3110291, "ngf-sim", "Latn", } m["gvj"] = { "Guajá", 3915506, "tup", "Latn", } m["gvl"] = { "Gulay", 641737, "csu-sar", "Latn", } m["gvm"] = { "Gurmana", 3913363, "nic-shi", "Latn", } m["gvn"] = { "Kuku-Yalanji", 5621973, "aus-pam", "Latn", } m["gvo"] = { "Gavião do Jiparaná", 5528335, "tup", "Latn", } m["gvp"] = { "Pará Gavião", 3365443, "sai-nje", "Latn", } m["gvr"] = { "Gurung", 2392342, "sit-tam", "Gukh, Deva", } m["gvs"] = { "Gumawana", 5618041, "poz-ocw", "Latn", } m["gvy"] = { "Guyani", 10511230, "aus-pam", "Latn", } m["gwa"] = { "Mbato", 3914941, "alv-ptn", "Latn", } m["gwb"] = { "Gwa", 5623219, "nic-jrn", "Latn", } m["gwc"] = { "Kalami", 1675961, "inc-koh", "Arab", strip_diacritics = { Arab = { -- character "ۂ" code U+06C2 to "ه" and "هٔ" (U+0647 + U+0654) to "ه"; hamzatu l-waṣli to a regular alif from = {"هٔ", "ۂ", "ٱ"}, to = {"ہ", "ہ", "ا"}, remove_diacritics = c.fathatan .. c.dammatan .. c.kasratan .. c.fatha .. c.damma .. c.kasra .. c.shadda .. c.sukun .. c.nunghunna .. c.superalef .. u(0x065e) }, }, } m["gwd"] = { "Gawwada", 3032135, "cus-eas", "Latn, Ethi", } m["gwe"] = { "Gweno", 3358211, "bnt-chg", "Latn", } m["gwf"] = { "Gowro", 3812403, "inc-koh", "Arab", } m["gwg"] = { "Moo", 6907057, "alv-bwj", "Latn", } m["gwi"] = { "Gwich'in", 21057, "ath-nor", "Latn", } m["gwj"] = { "Gcwi", 12631978, "khi-kal", "Latn", } m["gwm"] = { "Awngthim", 4830109, "aus-pmn", "Latn", } m["gwn"] = { "Gwandara", 56521, "cdc-wst", "Latn", } m["gwr"] = { "Gwere", 5623559, "bnt-nyg", "Latn", } m["gwt"] = { "Gawar-Bati", 33894, "inc-kun", "Arab", } m["gwu"] = { "Guwamu", 10511225, "aus-pam", "Latn", } m["gww"] = { "Kwini", 10551249, "aus-wor", "Latn", } m["gwx"] = { "Gua", 35422, "alv-gng", "Latn", } m["gxx"] = { "Wè Southern", 19921582, "kro-wee", "Latn", } m["gya"] = { "Northwest Gbaya", 36594, "gba-wes", "Latn", } m["gyb"] = { "Garus", 5524492, "ngf-han", "Latn", } m["gyd"] = { "Kayardild", 3913770, "aus-tnk", "Latn", } m["gye"] = { "Gyem", 5624046, "nic-jer", "Latn", } m["gyf"] = { "Gungabula", 10510783, "aus-pam", "Latn", } m["gyg"] = { "Gbayi", 11137618, "nic-ngd", "Latn", } m["gyi"] = { "Gyele", 35434, "bnt-mnj", "Latn", } m["gyl"] = { "Gayil", 5528771, "omv-aro", "Latn", } m["gym"] = { "Ngäbere", 3915581, "cba", "Latn", } m["gyn"] = { "Guyanese Creole English", 3305477, "crp", "Latn", ancestors = "en", } m["gyo"] = { "Gyalsumdo", 53575940, "sit-kyk", } m["gyr"] = { "Guarayu", 3118779, "tup-gua", "Latn", } m["gyy"] = { "Gunya", 10511001, "aus-pam", "Latn", } m["gza"] = { "Ganza", 5521556, "omv-mao", "Latn", } m["gzn"] = { "Gane", 3095108, "poz-hce", "Latn", } return require("Module:languages").finalizeData(m, "language") 0xlrgxv3ycxd1n2crqdbwwqsjj68ura Module:etymon/tracking 828 8135 55024 2026-09-30T19:19:13Z Deadend0914 7211 Gesceop tramet þe hafaþ '--[=[ Documentation: [[WT:Tracking#Etymon]]. ]=] local export = {} local M = require("Module:module loader").init({ require = { track = "Module:debug/track", }, }) local DEPTH_RANGES = { { min = 50, label = "extremely-deep" }, { min = 20, label = "20+" }, { min = 10, max = 19, label = "10-19" }, { min = 5, max = 9, label = "5-9" }, { min = 3, max = 4, label = "3-4" }, { max = 2, label = "1-2" }, } local NODE_RANGES = { { min = 100, label = "extre...' 55024 Scribunto text/plain --[=[ Documentation: [[WT:Tracking#Etymon]]. ]=] local export = {} local M = require("Module:module loader").init({ require = { track = "Module:debug/track", }, }) local DEPTH_RANGES = { { min = 50, label = "extremely-deep" }, { min = 20, label = "20+" }, { min = 10, max = 19, label = "10-19" }, { min = 5, max = 9, label = "5-9" }, { min = 3, max = 4, label = "3-4" }, { max = 2, label = "1-2" }, } local NODE_RANGES = { { min = 100, label = "extremely-large" }, { min = 50, label = "50+" }, { min = 20, max = 49, label = "20-49" }, { min = 10, max = 19, label = "10-19" }, { min = 5, max = 9, label = "5-9" }, { max = 4, label = "1-4" }, } local LANGUAGE_RANGES = { { min = 10, label = "10+" }, { min = 5, max = 9, label = "5-9" }, { min = 3, max = 4, label = "3-4" }, { exact = 2, label = "2" }, { exact = 1, label = "1" }, } local TERM_PAGE_NORMALIZERS = { { pattern = "^Reconstruction:[^/]+/(.+)$", normalize = function(term) if term:sub(1, 1) ~= "*" then return "*" .. term end return term end, }, { pattern = "^Appendix:[^/]+/(.+)$", normalize = function(term) return term end, }, } local function normalize_term_page(term_page) local page = tostring(term_page) for _, rule in ipairs(TERM_PAGE_NORMALIZERS) do local term = page:match(rule.pattern) if term then return rule.normalize(term) end end return page end local function sanitize_term_page(term_page) return normalize_term_page(term_page):gsub(":", "-"):gsub("/", "-"):gsub(" ", "_") end local function sanitize_track_segment(value) return tostring(value):gsub(":", "-"):gsub("/", "-"):gsub(" ", "_") end local function track_path(page_lang_code, path) M.track(path) if page_lang_code then M.track("etymon/lang/" .. page_lang_code .. "/" .. path:match("^etymon/(.+)$")) end end local function term_path(term_lang_code, term_page, ...) local safe_term = sanitize_term_page(term_page) local parts = { "etymon", "term", term_lang_code, safe_term } for i = 1, select("#", ...) do local segment = select(i, ...) if segment then table.insert(parts, segment) end end return table.concat(parts, "/") end local function term_id_path(term_lang_code, term_page, id_value, suffix) return term_path(term_lang_code, term_page, "id", sanitize_track_segment(id_value), suffix) end local function idless_term_path(term_lang_code, term_page, outcome) return term_path(term_lang_code, term_page, outcome) end local function mismatched_term_path(term_lang_code, term_page, id_value) return term_id_path(term_lang_code, term_page, id_value, "mismatched") end local function record_term_id(id_stats, term_lang_code, term_page, id_value, is_override) if not term_page or term_page == "" or not id_value or id_value == "" then return end id_stats.term_ids[term_lang_code] = id_stats.term_ids[term_lang_code] or {} id_stats.term_ids[term_lang_code][term_page] = id_stats.term_ids[term_lang_code][term_page] or {} local entry = id_stats.term_ids[term_lang_code][term_page][id_value] if not entry then entry = { count = 0, override = false } id_stats.term_ids[term_lang_code][term_page][id_value] = entry end entry.count = entry.count + 1 if is_override then entry.override = true end end local function record_idless_term(id_stats, term_lang_code, term_page) if not term_page or term_page == "" then return end id_stats.idless_terms[term_lang_code] = id_stats.idless_terms[term_lang_code] or {} local entry = id_stats.idless_terms[term_lang_code][term_page] if not entry then entry = { count = 0, outcomes = {} } id_stats.idless_terms[term_lang_code][term_page] = entry end entry.count = entry.count + 1 end local function track_ranges(base_key, value, ranges, lang_code) M.track("etymon/" .. base_key .. "/" .. value) if lang_code then M.track("etymon/lang/" .. lang_code .. "/" .. base_key .. "/" .. value) end for _, range in ipairs(ranges) do local matches = false if range.min and range.max then matches = value >= range.min and value <= range.max elseif range.min then matches = value >= range.min elseif range.max then matches = value <= range.max elseif range.exact then matches = value == range.exact end if matches then M.track("etymon/" .. base_key .. "/" .. range.label) if lang_code then M.track("etymon/lang/" .. lang_code .. "/" .. base_key .. "/" .. range.label) end break end end end function export.track_term(rest) if rest == "" then M.track("etymon/term/empty") elseif rest == "?" then M.track("etymon/term/question-mark") elseif rest == "-" then M.track("etymon/term/hyphen") end end function export.track_title_pagename_mismatch(lang) local lang_code = lang:getCode() M.track("etymon/title/pagename-mismatch-after-strip-diacritics") M.track("etymon/lang/" .. lang_code .. "/title/pagename-mismatch-after-strip-diacritics") end function export.record_keyword_usage(keyword_stats, keyword, target_lang, source_lang, is_toplevel) if not is_toplevel then return end if not keyword_stats[keyword] then keyword_stats[keyword] = { count = 0, target_langs = {}, source_langs = {}, } end local keyword_data = keyword_stats[keyword] keyword_data.count = keyword_data.count + 1 local target_code = target_lang:getCode() keyword_data.target_langs[target_code] = (keyword_data.target_langs[target_code] or 0) + 1 if source_lang then local source_code = source_lang:getCode() keyword_data.source_langs[source_code] = (keyword_data.source_langs[source_code] or 0) + 1 end end function export.track_tree_metrics(opts) local max_depth = opts.max_depth_reached if not max_depth or max_depth <= 0 then return end local lang_code = opts.lang:getCode() local total_nodes = opts.total_nodes local language_count = opts.language_count track_ranges("depth", max_depth, DEPTH_RANGES, lang_code) track_ranges("nodes", total_nodes, NODE_RANGES, lang_code) local unique_languages = 0 for _ in pairs(language_count) do unique_languages = unique_languages + 1 end track_ranges("unique-languages", unique_languages, LANGUAGE_RANGES, lang_code) if total_nodes == max_depth + 1 then track_ranges("linear-depth", max_depth, DEPTH_RANGES, lang_code) end end function export.track_keywords(keyword_stats, target_lang) local target_lang_code = target_lang:getCode() for keyword, keyword_data in pairs(keyword_stats) do M.track("etymon/keyword/" .. keyword) M.track("etymon/keyword/" .. keyword .. "/target/" .. target_lang_code) for source_code in pairs(keyword_data.source_langs) do M.track("etymon/keyword/" .. keyword .. "/source/" .. source_code) M.track("etymon/keyword/" .. keyword .. "/target/" .. target_lang_code .. "/source/" .. source_code) end end end function export.record_term_id_usage(id_stats, etymon_data, term_page) local term_lang_code = etymon_data.lang:getCode() if etymon_data.id and etymon_data.id ~= "" then record_term_id(id_stats, term_lang_code, term_page, etymon_data.id, etymon_data.override) else record_idless_term(id_stats, term_lang_code, term_page) end end function export.record_mismatched_id_usage(id_stats, term_lang, term_page, id_value) if not term_page or term_page == "" or not id_value or id_value == "" then return end local term_lang_code = term_lang:getCode() id_stats.mismatched_ids[term_lang_code] = id_stats.mismatched_ids[term_lang_code] or {} id_stats.mismatched_ids[term_lang_code][term_page] = id_stats.mismatched_ids[term_lang_code][term_page] or {} id_stats.mismatched_ids[term_lang_code][term_page][id_value] = (id_stats.mismatched_ids[term_lang_code][term_page][id_value] or 0) + 1 end function export.record_idless_resolution(id_stats, term_lang, term_page, outcome) if not term_page or term_page == "" then return end local term_lang_code = term_lang:getCode() id_stats.idless_terms[term_lang_code] = id_stats.idless_terms[term_lang_code] or {} local entry = id_stats.idless_terms[term_lang_code][term_page] if not entry then entry = { count = 0, outcomes = {} } id_stats.idless_terms[term_lang_code][term_page] = entry end entry.outcomes[outcome] = (entry.outcomes[outcome] or 0) + 1 end function export.track_text_stop_lang_missing(page_lang, stop_code) if not stop_code or stop_code == "" then return end local page_lang_code = page_lang:getCode() M.track("etymon/text/stop-lang/missing/" .. stop_code) M.track("etymon/lang/" .. page_lang_code .. "/text/stop-lang/missing/" .. stop_code) end function export.track_page_id(page_lang, id) local lang_code = page_lang:getCode() if id and id ~= "" then M.track("etymon/page-id/set") M.track("etymon/lang/" .. lang_code .. "/page-id/set") else M.track("etymon/page-id/unset") M.track("etymon/lang/" .. lang_code .. "/page-id/unset") end end function export.track_ids(id_stats, page_lang) local page_lang_code = page_lang:getCode() for term_lang_code, terms in pairs(id_stats.term_ids or {}) do for term_page, ids in pairs(terms) do for id_value, entry in pairs(ids) do if entry.count > 0 then track_path(page_lang_code, term_id_path(term_lang_code, term_page, id_value)) if entry.override then track_path(page_lang_code, term_id_path(term_lang_code, term_page, id_value, "override")) end end end end end for term_lang_code, terms in pairs(id_stats.idless_terms or {}) do for term_page, entry in pairs(terms) do if entry.count > 0 then track_path(page_lang_code, idless_term_path(term_lang_code, term_page)) end for outcome, count in pairs(entry.outcomes or {}) do if count > 0 then track_path(page_lang_code, idless_term_path(term_lang_code, term_page, outcome)) end end end end for term_lang_code, terms in pairs(id_stats.mismatched_ids or {}) do for term_page, ids in pairs(terms) do for id_value, count in pairs(ids) do if count > 0 then track_path(page_lang_code, mismatched_term_path(term_lang_code, term_page, id_value)) end end end end end function export.new_id_stats() return { term_ids = {}, idless_terms = {}, mismatched_ids = {}, } end return export 1a6uqo8jj8m8p5gt11ki7rx17qdrf11 Module:etymon/data 828 8136 55025 2026-09-30T19:19:40Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} export.STATUS = { OK = "ok", INLINE = "inline", MISSING = "missing", REDLINK = "redlink", AMBIGUOUS = "ambiguous", } export.TRANSITIVE = { ALWAYS = "always", -- always recurse into children NEVER = "never", -- never recurse into children CROSS_LANG = "cross_lang", -- only recurse when source lang differs from targe...' 55025 Scribunto text/plain local export = {} export.STATUS = { OK = "ok", INLINE = "inline", MISSING = "missing", REDLINK = "redlink", AMBIGUOUS = "ambiguous", } export.TRANSITIVE = { ALWAYS = "always", -- always recurse into children NEVER = "never", -- never recurse into children CROSS_LANG = "cross_lang", -- only recurse when source lang differs from target lang (but pos chain continues) CROSS_LANG_NO_INTERNAL_SOURCE = "cross_lang_no_internal_source", -- like CROSS_LANG, but source breaks for internal derivations in the same language context } -- Deep merge tables (nested tables are merged recursively, later values override earlier) local function deep_merge(...) local result = {} for _, t in ipairs({ ... }) do for k, v in pairs(t) do if type(v) == "table" and type(result[k]) == "table" then result[k] = deep_merge(result[k], v) else result[k] = v end end end return result end local function make_glossary_link(term, display_text) if not term then return display_text end return "[[Appendix:Glossary#" .. term:gsub(" ", "_") .. "|" .. display_text .. "]]" end -- Extract base word and connector from text like "Borrowed from" or "calque of" local function split_glossary_text(text) for _, pattern in ipairs({ "^(.-)(%s+[Oo][Ff])$", "^(.-)(%s+[Ff][Rr][Oo][Mm])$" }) do local base, rest = text:match(pattern) if base then return base, rest end end return text, "" end local TRANSITIVE = export.TRANSITIVE local function create_keyword(opts) local entry = { is_group = opts.is_group or false, abbrev = opts.abbrev, glossary = opts.glossary, transitive = opts.transitive or TRANSITIVE.ALWAYS, -- default "always" inherited_chain = opts.inherited_chain or false, affix_categories = opts.affix_categories or false, borrowing_type = opts.borrowing_type, specialized_borrowing = opts.specialized_borrowing, toplevel_category = opts.toplevel_category, no_child_categories = opts.no_child_categories or false, source_category_type = opts.source_category_type, invisible = (opts.invisible == true and "all") or opts.invisible or false, pos_override = opts.pos_override, new_sentence = opts.new_sentence or false, separate_clause = opts.separate_clause or false, default_conj = opts.default_conj, min_etymons = opts.min_etymons, max_etymons = opts.max_etymons, term_rules = opts.term_rules, aliases = opts.aliases, } -- Only set text/phrase when visible in text (invisible ~= "all" and ~= "text") local inv = entry.invisible if inv ~= "all" and inv ~= "text" then entry.phrase = opts.phrase if opts.text then if opts.glossary then local base_word, rest = split_glossary_text(opts.text) entry.text = make_glossary_link(opts.glossary, base_word) .. rest else entry.text = opts.text end end end return entry end -- Shared defaults for keyword groups local DEFAULTS = { -- Keywords that pass through inheritance chain inheritance = { transitive = TRANSITIVE.ALWAYS, inherited_chain = true, }, -- Standard transitive derivation transitive = { transitive = TRANSITIVE.ALWAYS, }, -- Standard for internal derivations: transitive across languages, but not within them internal_derivation = { transitive = TRANSITIVE.CROSS_LANG, }, -- Borrowing keywords borrowing = { transitive = TRANSITIVE.ALWAYS, }, -- Affix group keywords (compound words, blends, etc.) affix_group = { is_group = true, min_etymons = 2, transitive = TRANSITIVE.CROSS_LANG, affix_categories = true, }, -- Calque-like keywords (calque, partial calque, semantic loan) calque_like = { transitive = TRANSITIVE.NEVER, no_child_categories = true, new_sentence = true, }, -- Non-transitive influence influence_like = { transitive = TRANSITIVE.NEVER, no_child_categories = true, }, } export.keywords = { -- -- Inheritance keywords -- ["from"] = create_keyword(deep_merge(DEFAULTS.inheritance, { text = "From", phrase = "from", term_rules = { entry_lang = true }, })), ["inherited"] = create_keyword(deep_merge(DEFAULTS.inheritance, { text = "Inherited from", phrase = "from", glossary = "inherited", aliases = { "inh" }, term_rules = { family = "disallowed", family_suffix = "; use a specific language.", ancestor_check = true, }, })), -- -- Basic derivation keywords -- ["uder"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "From", phrase = "from", toplevel_category = "undefined derivations", })), ["derived"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Derived from", phrase = "from", abbrev = "der.", glossary = "derived terms", aliases = { "der" }, })), -- -- Affix/compound group keywords -- ["affix"] = create_keyword(deep_merge(DEFAULTS.affix_group, { text = "From", phrase = "from", min_etymons = 1, aliases = { "af" }, })), ["blend"] = create_keyword(deep_merge(DEFAULTS.affix_group, { text = "Blend of", phrase = "a blend of", abbrev = "blend", glossary = "blend", toplevel_category = "blends", })), ["univerbation"] = create_keyword(deep_merge(DEFAULTS.affix_group, { text = "Univerbation of", phrase = "univerbation of", abbrev = "univ.", glossary = "univerbation", toplevel_category = "univerbations", min_etymons = 1, aliases = { "univ" }, })), ["vrd-af"] = create_keyword(deep_merge(DEFAULTS.affix_group, { text = "Vṛddhi derivative of", phrase = "a vṛddhi derivative of", abbrev = "vṛd.", glossary = "vṛddhi derivative", toplevel_category = "vrddhi derivatives", })), ["sa-af"] = create_keyword(deep_merge(DEFAULTS.affix_group, { text = "[[Sanskritic]] formation from", phrase = "a [[Sanskritic]] formation of", toplevel_category = "Sanskritic formations", })), -- -- Borrowing keywords -- ["bor"] = create_keyword(deep_merge(DEFAULTS.borrowing, { text = "Borrowed from", phrase = "borrowed from", abbrev = "bor.", glossary = "loanword", borrowing_type = "borrowed", aliases = { "borrowed" }, })), ["lbor"] = create_keyword(deep_merge(DEFAULTS.borrowing, { text = "Learned borrowing from", phrase = "a learned borrowing from", abbrev = "lbor.", glossary = "learned borrowing", specialized_borrowing = "learned", })), ["obor"] = create_keyword(deep_merge(DEFAULTS.borrowing, { text = "Orthographic borrowing from", phrase = "an orthographic borrowing from", abbrev = "obor.", glossary = "orthographic borrowing", specialized_borrowing = "orthographic", })), ["slbor"] = create_keyword(deep_merge(DEFAULTS.borrowing, { text = "Semi-learned borrowing from", phrase = "a semi-learned borrowing from", abbrev = "slbor.", glossary = "semi-learned borrowing", specialized_borrowing = "semi-learned", aliases = { "slb" }, })), ["ubor"] = create_keyword(deep_merge(DEFAULTS.borrowing, { text = "Unadapted borrowing from", phrase = "an unadapted borrowing from", abbrev = "ubor.", glossary = "unadapted borrowing", specialized_borrowing = "unadapted", })), -- -- Calque-like keywords (non-transitive, start new sentence) -- ["calque"] = create_keyword(deep_merge(DEFAULTS.calque_like, { text = "Calque of", phrase = "a calque of", abbrev = "calq.", glossary = "calque", specialized_borrowing = "calque", aliases = { "cal", "clq" }, })), ["partial calque"] = create_keyword(deep_merge(DEFAULTS.calque_like, { text = "Partial calque of", phrase = "a partial calque of", abbrev = "pcalq.", glossary = "partial calque", specialized_borrowing = "partial-calque", aliases = { "pcal", "pclq" }, })), ["semantic loan"] = create_keyword(deep_merge(DEFAULTS.calque_like, { text = "Semantic loan from", phrase = "a semantic loan from", abbrev = "sl.", glossary = "semantic loan", specialized_borrowing = "semantic-loan", aliases = { "sl" }, })), ["psm"] = create_keyword(deep_merge(DEFAULTS.calque_like, { text = "Phono-semantic matching of", phrase = "a phono-semantic matching of", abbrev = "psm.", glossary = "phono-semantic matching", specialized_borrowing = "phono-semantic-matching", aliases = { "phono-semantic matching" }, })), -- -- Influence keywords (non-transitive, separate clause) -- ["influence"] = create_keyword(deep_merge(DEFAULTS.influence_like, { text = "Influenced by", phrase = "influenced by", abbrev = "influ.", glossary = "contamination", separate_clause = true, })), -- -- Morphological derivation keywords -- ["clipping"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Clipping of", phrase = "clipping of", abbrev = "clip.", glossary = "clipping", toplevel_category = "clippings", aliases = { "clip" }, })), ["ellipsis"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Ellipsis of", phrase = "ellipsis of", abbrev = "ellip.", glossary = "ellipsis", toplevel_category = "ellipses", aliases = { "ellip" }, })), ["back-formation"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Back-formation from", phrase = "a back-formation from", abbrev = "bf.", glossary = "back-formation", toplevel_category = "back-formations", aliases = { "bf" }, })), ["nominalization"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Nominalization of", phrase = "a nominalization of", abbrev = "nom.", glossary = "nominalization", toplevel_category = "nominalizations", aliases = { "nom" }, })), ["transliteration"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Transliteration of", phrase = "borrowed from", abbrev = "translit.", glossary = "transliteration", aliases = { "translit" }, })), ["vrd"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Vṛddhi derivative of", phrase = "a vṛddhi derivative of", abbrev = "vṛd.", glossary = "vṛddhi derivative", toplevel_category = "vrddhi derivatives", })), ["apheretic"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Apheretic form of", phrase = "an apheretic form of", abbrev = "aph.", glossary = "apheresis", aliases = { "apheresis", "aphetic" }, })), ["denominal"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Denominal verb from", phrase = "denominal verb from", abbrev = "denom.", glossary = "denominal", toplevel_category = "denominal verbs", aliases = { "denom" }, })), ["deverbal"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Deverbal from", phrase = "deverbal from", abbrev = "deverb.", glossary = "deverbal", toplevel_category = "deverbals", })), ["reduplication"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Reduplication of", phrase = "reduplication of", abbrev = "redup.", glossary = "reduplication", toplevel_category = "reduplications", aliases = { "rdp", "redup" }, })), ["abbreviation"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Abbreviation of", phrase = "abbreviation of", abbrev = "abbr.", glossary = "abbreviation", aliases = { "abbr", "abbrev" }, })), ["syllabic abbreviation"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Syllabic abbreviation of", phrase = "syllabic abbreviation of", abbrev = "syl. abbr.", glossary = "syllabic abbreviation", aliases = { "sylabbr", "sylabbrev" }, })), ["acronym"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Acronym of", phrase = "acronym of", abbrev = "acronym", glossary = "acronym", aliases = { "acro" }, })), ["initialism"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Initialism of", phrase = "initialism of", abbrev = "init.", glossary = "initialism", aliases = { "init" }, })), ["metathesis"] = create_keyword(deep_merge(DEFAULTS.transitive, { text = "Metathesis of", phrase = "metathesis of", abbrev = "meta.", glossary = "metathesis", toplevel_category = "words derived through metathesis", aliases = { "meta" }, })), -- -- Invisible keywords (no text output) -- ["root"] = create_keyword { transitive = TRANSITIVE.ALWAYS, invisible = "all", pos_override = "root", }, ["afeq"] = create_keyword(deep_merge(DEFAULTS.affix_group, { text = "From", phrase = "from", transitive = TRANSITIVE.NEVER, min_etymons = 1, invisible = "all", })), } -- Template-parameter supplements. export.supplements = { doublet = { text = "Doublet of", phrase = "doublet of", glossary = "doublet", toplevel_category = "doublets", default_conj = "and", term_rules = { entry_lang = true, require_term = true, disallow = { "suppress", "unknown", "family" }, }, }, } local aliases_to_register = {} local canonical_aliases = {} -- Map every keyword (canonical or alias) to its canonical form for consistent checks and tracking. export.keyword_canonical = {} for name, keyword_data in pairs(export.keywords) do export.keyword_canonical[name] = name if keyword_data.aliases then canonical_aliases[name] = keyword_data.aliases for _, alias in ipairs(keyword_data.aliases) do if export.keywords[alias] then error("Alias '" .. alias .. "' defined in keyword '" .. name .. "' collides with existing keyword '" .. alias .. "'.") end if aliases_to_register[alias] then error("Alias '" .. alias .. "' defined in keyword '" .. name .. "' is already claimed by another keyword.") end aliases_to_register[alias] = keyword_data export.keyword_canonical[alias] = name end keyword_data.aliases = nil end end for alias, data in pairs(aliases_to_register) do export.keywords[alias] = data end -- -- Language exception presets -- local EXCEPTION_PRESETS = { -- Fully disallowed: no tree, no text, no categories disallowed = { disallow = { tree = true, text = true }, suppress_categories = true, }, -- Suppress transliteration only no_translit = { suppress_tr = true, }, -- Suppress all categories only no_categories = { suppress_categories = true, }, } --[=[ Available exception options: disallow = { Related options for disallowing output: tree Disallow etymology trees for this language text Disallow etymology text generation for this language ref Reference link shown when tree/text is disallowed } suppress_tr Suppress transliteration in links suppress_categories Suppress all category generation normalize_to Normalize language code to a different code normalize_from_families Apply normalization to languages in these families normalize_exclude_families Exclude these families from normalization keyword_overrides Per-keyword categorisation overrides (e.g. { ["af"] = { transitive = TRANSITIVE.NEVER } }) ]=] local function create_exception(preset, overrides) local base = preset and EXCEPTION_PRESETS[preset] or {} return deep_merge(base, overrides or {}) end export.config = { lang_exceptions = { ["zh"] = create_exception("disallowed", { disallow = { ref = "[[Wiktionary:Beer parlour/2025/May#Template:etymon for Chinese]]" }, suppress_tr = true, normalize_to = "zh", normalize_from_families = { "zhx" }, normalize_exclude_families = { "qfa-cnt" }, }), }, } -- Supported codes for the nominalization <g:code> modifier (subset of common gender/number-style codes) -- Short names of the <aftype:> modifier, as [[Module:affix]] names affix types. export.aftype_aliases = { ["pre"] = "prefix", ["suf"] = "suffix", ["in"] = "infix", ["inter"] = "interfix", ["circum"] = "circumfix", ["naf"] = "non-affix", ["root"] = "non-affix", } export.nominalization_g_codes = { ["m"] = "masculine", ["f"] = "feminine", ["n"] = "neuter", ["c"] = "common", ["gneut"] = "gender-neutral", ["s"] = "singular", ["p"] = "plural", ["d"] = "dual", ["pauc"] = "paucal", ["mf"] = "masculine or feminine", ["fm"] = "masculine or feminine", ["mfn"] = "masculine, feminine or neuter", ["mnf"] = "masculine, feminine or neuter", ["fmn"] = "masculine, feminine or neuter", ["fnm"] = "masculine, feminine or neuter", ["nmf"] = "masculine, feminine or neuter", ["nfm"] = "masculine, feminine or neuter", } -- -- Propagate keyword overrides to aliases -- if export.config.lang_exceptions then for lang_code, exception in pairs(export.config.lang_exceptions) do if exception.keyword_overrides then for canonical, aliases in pairs(canonical_aliases) do if exception.keyword_overrides[canonical] then local override_data = exception.keyword_overrides[canonical] for _, alias in ipairs(aliases) do if not exception.keyword_overrides[alias] then exception.keyword_overrides[alias] = override_data end end end end end end end return export 5eomy2e0qlujstp3t9s9r5rlf8dlnhc Module:etymon/descendants 828 8137 55026 2026-09-30T19:20:04Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local M = require("Module:module loader").init({ require = { template_parser = "Module:template parser", links = "Module:links", languages = "Module:languages", }, }) local TRACKABLE_KEYWORDS = { inherited = true, bor = true, lbor = true, slbor = true, derived = true, uder = true, } local DESCENDANT_TEMPLATES = { ["desc"] = true, ["descendant"] = true, ["desctree"] = true, ["descendants tree"] = true, } local SKIPPED_TERMS...' 55026 Scribunto text/plain local export = {} local M = require("Module:module loader").init({ require = { template_parser = "Module:template parser", links = "Module:links", languages = "Module:languages", }, }) local TRACKABLE_KEYWORDS = { inherited = true, bor = true, lbor = true, slbor = true, derived = true, uder = true, } local DESCENDANT_TEMPLATES = { ["desc"] = true, ["descendant"] = true, ["desctree"] = true, ["descendants tree"] = true, } local SKIPPED_TERMS = { [""] = true, ["-"] = true, } local CHECK_MISSING_HEADER = { missing_header = true, missing_entry = false } local STRIPPED_REF_TEMPLATES = { ref = true, refn = true, } local ENABLE_DESCENDANTS_TRACKING = false local function _resolve_explicit_cache_id(explicit_id, parent_etymon) if parent_etymon then return parent_etymon.id or "*" end return explicit_id end local function _resolve_single_cache_id(id_data) return (type(id_data) == "table" and id_data.id) or id_data or "*" end local function _resolve_cache_id(lookup) if not lookup then return nil end if lookup.id then return lookup.id end if lookup.explicit_id then return _resolve_explicit_cache_id(lookup.explicit_id, lookup.parent_etymon) end if lookup.id_data ~= nil then return _resolve_single_cache_id(lookup.id_data) end return nil end local function _get_cached_check(cache_table, is_toplevel, base_key, cache_id) if not is_toplevel or not cache_table or not base_key or not cache_id then return nil end return cache_table[base_key .. ":" .. cache_id] end local function _store_checks(cache_table, lang_page_key, checks_by_id, redirected_from) if not cache_table or not lang_page_key or not checks_by_id then return end for id, check in pairs(checks_by_id) do cache_table[lang_page_key .. ":" .. id] = check end if redirected_from then for id, check in pairs(checks_by_id) do cache_table[redirected_from .. ":" .. id] = check end end end local function _strip_refs(value) if type(value) ~= "string" or value == "" then return value end local get_node_class = M.template_parser.class_else_type local function trim_local(text) return (type(text) == "string" and text:match("^%s*(.-)%s*$")) or "" end local function lower_local(text) return text and mw.ustring.lower(text) or text end local function template_name_unexpanded(template_node) return lower_local(trim_local(tostring(template_node[1]) or "")) end local function stringify_without_ref_templates(node) if type(node) ~= "table" then return node == nil and "" or tostring(node) end local node_class = get_node_class(node) if node_class == "template" and STRIPPED_REF_TEMPLATES[template_name_unexpanded(node)] then return "" end if node_class == "template" then return tostring(node) end if node_class == "heading" then return tostring(node) end if node_class and node_class ~= "wikitext" then return tostring(node) end local pieces = {} for i = 1, #node do pieces[i] = stringify_without_ref_templates(node[i]) end return table.concat(pieces) end local parsed = M.template_parser.parse(value) local cleaned = stringify_without_ref_templates(parsed) local changed = true while changed do local next_cleaned = mw.ustring.gsub(cleaned, "<%s*[Rr][Ee][Ff][^>]-/>", "") next_cleaned = mw.ustring.gsub(next_cleaned, "<%s*[Rr][Ee][Ff][^>]*>.-<%s*/%s*[Rr][Ee][Ff]%s*>", "") next_cleaned = mw.ustring.gsub(next_cleaned, "{{%s*[Rr][Ee][Ff]%s*}}", "") next_cleaned = mw.ustring.gsub(next_cleaned, "{{%s*[Rr][Ee][Ff]%s*|[^{}]-}}", "") changed = next_cleaned ~= cleaned cleaned = next_cleaned end return cleaned end local function _trim(value) if type(value) ~= "string" then return nil end return value:match("^%s*(.-)%s*$") end local function _lower(value) return value and mw.ustring.lower(value) or value end local function _is_index_in_range(index, start_index, end_index) return index and index >= start_index and index <= end_index end local function _build_parse_index(lang_section) local parsed = M.template_parser.parse(lang_section) local headings = {} local templates = {} for heading in parsed:iterate_nodes("heading") do local heading_name = heading:get_name() table.insert(headings, { index = heading.index or 1, level = heading.level or 0, name = heading_name and _lower(_trim(heading_name) or "") or nil, length = #tostring(heading), }) end for template in parsed:iterate_nodes("template") do local template_name = template:get_name() local template_args = template:get_arguments() table.insert(templates, { index = template.index or 1, name = template_name and _lower(_trim(template_name) or "") or "", args = template_args or {}, }) end return { headings = headings, templates = templates, } end local function _build_regions(lang_section, headings) local content_length = #lang_section local etymology_headings = {} for _, heading in ipairs(headings) do if heading.level == 3 and heading.name and mw.ustring.match(heading.name, "^etymology") then table.insert(etymology_headings, heading) end end if #etymology_headings == 0 then return { { start_index = 1, end_index = content_length } } end local regions = {} for i = 1, #etymology_headings do local heading = etymology_headings[i] local next_heading = etymology_headings[i + 1] local start_index = heading.index local end_index = next_heading and (next_heading.index - 1) or content_length if i == 1 and heading.index > 1 then local preamble = lang_section:sub(1, heading.index - 1) if preamble:match("%S") then start_index = 1 end end table.insert(regions, { start_index = start_index, end_index = end_index, }) end return regions end local function _count_region_etymons(region, templates, etymon_lang_code) local count = 0 for _, template in ipairs(templates) do if _is_index_in_range(template.index, region.start_index, region.end_index) and template.name == "etymon" and template.args[1] == etymon_lang_code then count = count + 1 end end return count end local function _get_descendants_sections(region, headings) local sections = {} for i = 1, #headings do local heading = headings[i] if _is_index_in_range(heading.index, region.start_index, region.end_index) and heading.name == "descendants" then local body_start = heading.index + heading.length local body_end = region.end_index for j = i + 1, #headings do local next_heading = headings[j] if next_heading.index > region.end_index then break end if next_heading.level <= heading.level then body_end = next_heading.index - 1 break end end table.insert(sections, { start_index = body_start, end_index = body_end, }) end end return sections end local function _template_lang_matches_entry(template_lang_code, entry_full_code, cache) local normalized_code = _trim(template_lang_code) if not normalized_code or normalized_code == "" then return false end local cached = cache[normalized_code] if cached ~= nil then return cached end local template_lang = M.languages.getByCode(normalized_code, nil, true) local is_match = template_lang and template_lang:getFullCode() == entry_full_code or false cache[normalized_code] = is_match return is_match end local function _template_lists_target(template_args, first_term_index, target_page_lower, entry_lang) for arg_index = first_term_index, #template_args do local template_term = _trim(template_args[arg_index]) if template_term and not SKIPPED_TERMS[template_term] then if _lower(template_term) == target_page_lower then return true end local template_page = M.links.get_link_page(template_term, entry_lang) if _lower(template_page) == target_page_lower then return true end end end return false end local function _is_target_listed_in_descendants(descendants_sections, templates, entry_title, entry_lang) if #descendants_sections == 0 then return false end local target_page = M.links.get_link_page(entry_title, entry_lang) local target_page_lower = _lower(target_page) local entry_full_code = entry_lang:getFullCode() local lang_match_cache = {} for _, section in ipairs(descendants_sections) do for _, template in ipairs(templates) do if _is_index_in_range(template.index, section.start_index, section.end_index) and DESCENDANT_TEMPLATES[template.name] then local lang_arg_index = nil for arg_index = 1, #template.args do if _template_lang_matches_entry(template.args[arg_index], entry_full_code, lang_match_cache) then lang_arg_index = arg_index break end end if lang_arg_index and _template_lists_target(template.args, lang_arg_index + 1, target_page_lower, entry_lang) then return true end end end end return false end local function _region_has_descendant_template(region, templates) for _, template in ipairs(templates) do if _is_index_in_range(template.index, region.start_index, region.end_index) and DESCENDANT_TEMPLATES[template.name] then return true end end return false end local function _get_region_check(region, parsed_index, entry_title, entry_lang) local descendants_sections = _get_descendants_sections(region, parsed_index.headings) if #descendants_sections == 0 then if _region_has_descendant_template(region, parsed_index.templates) then local fallback_sections = { { start_index = region.start_index, end_index = region.end_index, } } return { missing_header = false, missing_entry = not _is_target_listed_in_descendants(fallback_sections, parsed_index.templates, entry_title, entry_lang), } end return CHECK_MISSING_HEADER end return { missing_header = false, missing_entry = not _is_target_listed_in_descendants(descendants_sections, parsed_index.templates, entry_title, entry_lang), } end local function _build_checks_by_id(lang_section, etymon_lang_code, found_templates_for_lang, entry_title, entry_lang) local checks_by_id = {} local parsed_index = _build_parse_index(lang_section) local regions = _build_regions(lang_section, parsed_index.headings) local template_list_index = 1 local mapping_failed = false for _, region in ipairs(regions) do local region_etymon_count = _count_region_etymons(region, parsed_index.templates, etymon_lang_code) local region_check = nil if region_etymon_count > 0 then region_check = _get_region_check(region, parsed_index, entry_title, entry_lang) end for _ = 1, region_etymon_count do local found_template_args = found_templates_for_lang[template_list_index] if not found_template_args then mapping_failed = true break end checks_by_id[found_template_args.id or "*"] = region_check template_list_index = template_list_index + 1 end if mapping_failed then break end end if mapping_failed or template_list_index ~= (#found_templates_for_lang + 1) then local global_region = { start_index = 1, end_index = #lang_section } local fallback_check = _get_region_check(global_region, parsed_index, entry_title, entry_lang) checks_by_id = {} for _, template_args in ipairs(found_templates_for_lang) do checks_by_id[template_args.id or "*"] = fallback_check end end return checks_by_id end local function _compute_checks_for_page(opts) opts = opts or {} local found_templates_for_lang = opts.found_templates_for_lang or {} if #found_templates_for_lang == 0 then return {} end local entry_title = opts.entry_title local entry_lang = opts.entry_lang local etymon_lang_code = opts.etymon_lang_code if not entry_title or not entry_lang or not etymon_lang_code then return {} end local sanitized_lang_section = _strip_refs(opts.lang_section or "") return _build_checks_by_id( sanitized_lang_section, etymon_lang_code, found_templates_for_lang, entry_title, entry_lang ) end function export.cache_page_checks(opts) if not ENABLE_DESCENDANTS_TRACKING then return {} end opts = opts or {} local cache_table = opts.cached_descendants_checks local lang_page_key = opts.lang_page_key if not cache_table or not lang_page_key then return {} end local checks_by_id = _compute_checks_for_page(opts) _store_checks(cache_table, lang_page_key, checks_by_id, opts.redirected_from) return checks_by_id end function export.get_lookup_check(opts) if not ENABLE_DESCENDANTS_TRACKING then return nil end opts = opts or {} return _get_cached_check( opts.cached_descendants_checks, opts.is_toplevel, opts.base_key, _resolve_cache_id(opts.lookup) ) end function export.get_term_sync_flags(keyword, term_status, descendants_check) if not ENABLE_DESCENDANTS_TRACKING then return false, false end local should_track = term_status == "ok" and TRACKABLE_KEYWORDS[keyword] == true and descendants_check ~= nil if not should_track then return false, false end return descendants_check.missing_header or false, descendants_check.missing_entry or false end return export njoa75ldlbxvifpj024ykipfx44kfcd Module:etymon/data/text allowed 828 8138 55027 2026-09-30T19:20:31Z Deadend0914 7211 Gesceop tramet þe hafaþ '--[=[ Languages and families that may use the {{etymon}} `text=` parameter (language-community consensus). ]=] return { -- Mode: "off" = disabled, "warn" = warn only, "error" = enforce. default_mode = "warn", langs = { ["ak"] = true, ["amf"] = true, ["bg"] = true, ["bnt-sab-pro"] = true, ["cs"] = true, ["en"] = true, ["eo"] = true, ["es"] = true, ["ext"] = true, ["fa"] = true, ["gmw-msc"] = true, ["hsb"] = true, ["iir-pro"] = t...' 55027 Scribunto text/plain --[=[ Languages and families that may use the {{etymon}} `text=` parameter (language-community consensus). ]=] return { -- Mode: "off" = disabled, "warn" = warn only, "error" = enforce. default_mode = "warn", langs = { ["ak"] = true, ["amf"] = true, ["bg"] = true, ["bnt-sab-pro"] = true, ["cs"] = true, ["en"] = true, ["eo"] = true, ["es"] = true, ["ext"] = true, ["fa"] = true, ["gmw-msc"] = true, ["hsb"] = true, ["iir-pro"] = true, ["jbo"] = true, ["jdt"] = true, ["la"] = true, ["mul"] = true, ["ota"] = true, ["pap"] = true, ["ps"] = true, ["ro"] = true, ["sco"] = true, ["sk"] = true, ["sw"] = true, ["tg"] = true, ["tl"] = true, ["tr"] = true, ["uk"] = true, ["uz"] = true, ["sl"] = true, ["rsk"] = true, ["zlw-ocs"] = true, ["zlw-osk"] = true, ["zle-ono"] = true, ["zle-ort"] = true, ["sla-pro"] = true, ["eu"] = true, -- Austronesian ["map-pro"] = true, ["map-ata-pro"] = true, ["poz-pro"] = true, ["poz-btk-pro"] = true, ["poz-cet-pro"] = true, ["pqe-pro"] = true, ["poz-hce-pro"] = true, ["poz-oce-pro"] = true, ["poz-pol-pro"] = true, ["poz-pnp-pro"] = true, ["poz-pep-pro"] = true, ["poz-mic-pro"] = true, ["poz-lgx-pro"] = true, ["poz-msa-pro"] = true, ["poz-mcm-pro"] = true, ["cmc-pro"] = true, ["poz-mly-pro"] = true, ["poz-swa-pro"] = true, ["btk-pro"] = true, ["phi-pro"] = true, ["phi-kal-pro"] = true, ["poz-ssw-pro"] = true, ["dru-pro"] = true, ["crp-slb"] = true, }, families = { ["ber"] = true, -- Berber ["dra"] = true, -- Dravidian ["inc"] = true, -- Indo-Aryan ["iir-nur"] = true, -- Nuristani ["mun"] = true, -- Munda ["roa-gap"] = true, -- Galician-Portuguese ["sem-ara"] = true, -- Aramaic ["sem-arb"] = true, -- Arabic ["tup"] = true, -- Tupian ["bnt-sab"] = true, -- Lechitic ["zlw-lch"] = true, -- Lechitic }, } d2t0jck7ixvesghw7yzk9gdr2q8prgn Module:fun/trycall 828 8139 55028 2026-09-30T19:20:56Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local fun_is_callable_module = "Module:fun/isCallable" local error = error local format = string.format local pcall = pcall local type = type local function is_callable(...) is_callable = require(fun_is_callable_module) return is_callable(...) end local function catch_values(obj, success, ...) if success then return success, ... -- Error message will only take this exact form if `obj` is not callable, -- because it will contain a traceback if it was thr...' 55028 Scribunto text/plain local fun_is_callable_module = "Module:fun/isCallable" local error = error local format = string.format local pcall = pcall local type = type local function is_callable(...) is_callable = require(fun_is_callable_module) return is_callable(...) end local function catch_values(obj, success, ...) if success then return success, ... -- Error message will only take this exact form if `obj` is not callable, -- because it will contain a traceback if it was thrown further up the -- stack. elseif (...) == format("attempt to call a %s value", type(obj)) then return false end return error(...) end --[==[ A special form of {pcall()}, which returns {true} plus the result value(s) if {obj} is callable, or {false} if it isn't. Errors that occur within the called function are not protected.]==] return function(obj, ...) local callable = is_callable(obj, true) if callable then return true, obj(...) elseif callable == false then return false end -- If `callable` is nil, there's a protected metatable, so there's no way to -- check without doing a protected call. return catch_values(obj, pcall(obj, ...)) end b1dd95sq861pept04po2rb74sr1t9y1 Module:etymon/text 828 8140 55029 2026-09-30T19:21:25Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local loader = require("Module:module loader") local M = loader.init({ require = { en_utilities = "Module:en-utilities", references = "Module:references", senseno = "Module:senseno", }, loadData = { data = "Module:etymon/data", }, }) function export.render(opts) opts = opts or {} local data_tree = opts.data_tree local format_term_func = opts.format_term_func local max_depth = opts.max_depth local stop_at_blue_link = opts.stop...' 55029 Scribunto text/plain local export = {} local loader = require("Module:module loader") local M = loader.init({ require = { en_utilities = "Module:en-utilities", references = "Module:references", senseno = "Module:senseno", }, loadData = { data = "Module:etymon/data", }, }) function export.render(opts) opts = opts or {} local data_tree = opts.data_tree local format_term_func = opts.format_term_func local max_depth = opts.max_depth local stop_at_blue_link = opts.stop_at_blue_link local curr_page = opts.curr_page local nodot = opts.nodot and opts.nodot ~= "" and opts.nodot ~= "0" local function explicit_dot_override() if opts.dot == nil or opts.dot == false then return nil end local d = mw.text.trim(tostring(opts.dot)) if d == "" or d == "." then return nil end return d end local dot_override = explicit_dot_override() local function find_deepest_last_part(tree) if not tree or not tree.container_parts or #tree.container_parts == 0 then return nil end local last_part = tree.container_parts[#tree.container_parts] if last_part.continuation then return find_deepest_last_part(last_part.continuation) end return last_part end local function apply_final_punctuation_override(tree, punct) if not tree or punct == nil then return end local last_part = find_deepest_last_part(tree) if last_part then last_part.punctuation = punct end end local function apply_closing_punctuation_override(tree, is_last_segment) if not is_last_segment then return end local punct if nodot then punct = "" elseif dot_override ~= nil then punct = dot_override else return end apply_final_punctuation_override(tree, punct) end local function has_supplements() return data_tree.supplements and #data_tree.supplements > 0 end local stop_at_lang = opts.stop_at_lang local stop_at_lang_or_bluelink = opts.stop_at_lang_or_bluelink local lang_matches_stop_code = opts.lang_matches_stop_code local stop_lang_reached = false local function term_matches_stop_code(term_lang, stop_code) if lang_matches_stop_code then return lang_matches_stop_code(term_lang, stop_code) end return term_lang and term_lang:getCode() == stop_code end local children = data_tree.children local function has_text_supplements() if not data_tree.supplements then return false end for _, supplement in ipairs(data_tree.supplements) do if supplement.type == "doublet" and supplement.terms and #supplement.terms > 0 then return true end if supplement.type == "etydate" and supplement.etydate_text and supplement.etydate_text ~= "" then return true end end return false end if (not children or #children == 0) and not has_text_supplements() then if stop_at_lang then return "", { stop_lang_reached = false } end return "" end local entry_lang = data_tree.lang local function lowercase_glossary_link_display(wikitext) return wikitext:gsub("(%[%[Appendix:Glossary#[^|]+|)([^%]])([^%]]*)%]%]", function(prefix, first, rest) return prefix .. mw.ustring.lower(first) .. rest .. "]]" end) end local function format_sl_senseid_intro(senseid, keyword_text, keyword_phrase, capitalize_senseno, is_uncertain) local senseids = mw.text.split(senseid, "!!", true) local senseno_parts = {} for i, id in ipairs(senseids) do id = mw.text.trim(id) if id ~= "" then table.insert(senseno_parts, M.senseno.link_text(entry_lang:getCode(), { id }, { title = curr_page, uc = (i == 1 and capitalize_senseno) or nil, })) end end if #senseno_parts == 0 then return keyword_text, keyword_phrase end local senseno_text = mw.text.listToText(senseno_parts) local glossary_link = (keyword_text or ""):gsub(" from$", "") local glossary_lower = lowercase_glossary_link_display(glossary_link) if #senseno_parts > 1 then local plural_link = glossary_lower:gsub("|semantic loan%]%]", "|semantic loans]]") if is_uncertain then return senseno_text .. " are possibly " .. plural_link .. " from", senseno_text .. " are possibly semantic loans from" end return senseno_text .. " are " .. plural_link .. " from", senseno_text .. " are semantic loans from" end if is_uncertain then return senseno_text .. " is possibly a " .. glossary_lower .. " from", senseno_text .. " is possibly a semantic loan from" end return senseno_text .. " is a " .. glossary_lower .. " from", senseno_text .. " is a semantic loan from" end local function get_term_refs(term, defined_here) if defined_here and term.refs and next(term.refs) then return M.references.format_references(term.refs) end return "" end -- Build a text part for a single term local function build_term_part(term, current_lang, defined_here) local text = "" local new_lang = current_lang local lang_changed = term.lang:getCanonicalName() ~= current_lang:getCanonicalName() -- Use centralized format_term (handles suppress_term, unknown_term, and regular terms) local term_text = format_term_func(term) if lang_changed then new_lang = term.lang if term_text then text = term.lang:makeWikipediaLink() .. " " .. term_text elseif term.is_family then text = M.en_utilities.add_indefinite_article(term.lang:makeWikipediaLink() .. " language", false) else -- suppress_term with language change: show only language text = term.lang:makeWikipediaLink() end else text = term_text or "" end return { type = "term", text = text, refs = get_term_refs(term, defined_here), lang = new_lang, is_uncertain = term.is_uncertain or false, } end -- Build text parts for a container local function build_container_part(container, node, depth, allow_continuation, fallback_to_bluelink) local keyword_info = container.keyword_info local keyword_modifiers = container.keyword_modifiers or {} local terms = container.terms or {} if not keyword_info or #terms == 0 then return nil end -- Skip building text part when invisible in text ("all", "text", or true) local inv = keyword_info.invisible if inv == "all" or inv == true or inv == "text" then return nil end local is_group = keyword_info.is_group local keyword_uncertain = keyword_modifiers.unc or false -- Determine text and phrase (allowing for overrides) local intro_text = keyword_info.text local phrase = keyword_info.phrase local new_sentence = keyword_info.new_sentence or false if keyword_modifiers.text then -- User-provided override: assumed to be lowercase phrase = keyword_modifiers.text -- Auto-capitalize for intro text (e.g., "derived from" -> "Derived from") intro_text = mw.ustring.upper(phrase:sub(1, 1)) .. phrase:sub(2) end local defined_here = (node.page_of or curr_page) == curr_page -- Get keyword references local keyword_refs = "" if defined_here and keyword_modifiers.refs and next(keyword_modifiers.refs) then keyword_refs = M.references.format_references(keyword_modifiers.refs) end -- Build term parts local term_parts = {} local current_lang = node.lang for _, term in ipairs(terms) do local term_part = build_term_part(term, current_lang, defined_here) if term_part.text ~= "" then table.insert(term_parts, term_part) current_lang = term_part.lang end end -- Check uncertainty distribution local uncertain_count = 0 for _, term_part in ipairs(term_parts) do if term_part.is_uncertain then uncertain_count = uncertain_count + 1 end end -- If keyword itself is uncertain, treat all terms as uncertain local all_uncertain = keyword_uncertain or (uncertain_count == #term_parts and #term_parts > 0) if is_group and uncertain_count > 0 then all_uncertain = true end local has_mixed_uncertainty = not all_uncertain and uncertain_count > 0 -- Check if there are more steps (only if continuation is allowed) local has_more_steps = false local next_node = nil local first_term = terms[1] -- Check if we should stop at this language local reached_stop_lang = false if stop_at_lang then for _, term in ipairs(terms) do if term.lang and term_matches_stop_code(term.lang, stop_at_lang) then reached_stop_lang = true stop_lang_reached = true break end end elseif stop_at_lang_or_bluelink then -- Check if we should stop at this language, or at the first bluelink if it's a redlink for _, term in ipairs(terms) do if term.lang and term_matches_stop_code(term.lang, stop_at_lang_or_bluelink) then if first_term.status == M.data.STATUS.OK then reached_stop_lang = true else fallback_to_bluelink = true end break end end if fallback_to_bluelink and first_term.status == M.data.STATUS.OK then reached_stop_lang = true end end if allow_continuation and not is_group and #terms == 1 and not reached_stop_lang then local first_term_children = first_term.children if first_term_children and #first_term_children > 0 and (not max_depth or depth < max_depth) then local next_container = first_term_children[1] local next_keyword_info = next_container and next_container.keyword_info if not (next_keyword_info and next_keyword_info.invisible) then if stop_at_blue_link then if first_term.status ~= M.data.STATUS.OK then has_more_steps = true next_node = first_term end else has_more_steps = true next_node = first_term end end end end return { type = "container", intro_text = intro_text, phrase = phrase, senseid = keyword_modifiers.senseid, sl_keyword_text = keyword_modifiers.senseid and keyword_info.text or nil, is_uncertain = all_uncertain, has_mixed_uncertainty = has_mixed_uncertainty, term_parts = term_parts, is_group = is_group, has_more_steps = has_more_steps, next_node = next_node, new_sentence = new_sentence, separate_clause = keyword_info.separate_clause or false, conj = keyword_modifiers.conj or keyword_info.default_conj, -- custom conjunction: "and", "or", "and/or", etc. lit = keyword_modifiers.lit, keyword_refs = keyword_refs, fallback_to_bluelink = fallback_to_bluelink, } end -- Build the full tree of text parts local function build_text_tree(node, depth, allow_continuation, fallback_to_bluelink) local containers = node.children if not containers or #containers == 0 then return nil end local container_parts = {} -- Count containers that get a text part (invisible in text = "all", "text", or true) local visible_container_count = 0 for _, container in ipairs(containers) do local keyword_info = container.keyword_info local inv = keyword_info and keyword_info.invisible if not (inv == "all" or inv == true or inv == "text") then visible_container_count = visible_container_count + 1 end end -- If there are multiple visible containers at this level, don't allow continuation for any local has_multiple_containers = visible_container_count > 1 local should_allow_continuation = allow_continuation and not has_multiple_containers for _, container in ipairs(containers) do local part = build_container_part(container, node, depth, should_allow_continuation, fallback_to_bluelink) if part then -- Recursively build children if there are more steps if part.has_more_steps and part.next_node then part.continuation = build_text_tree(part.next_node, depth + 1, true, part.fallback_to_bluelink) end table.insert(container_parts, part) end end if #container_parts == 0 then return nil end return { type = "tree", container_parts = container_parts, depth = depth, } end -- Check if tree has mixed joining types local function container_join_kind(part) if part.type == "etydate" then return nil end if part.new_sentence or part.separate_clause then return "supplement" end return "or_join" end local function check_complexity(tree) if not tree then return nil end local parts = tree.container_parts if #parts <= 1 then -- Single container if parts[1] and parts[1].continuation then return check_complexity(parts[1].continuation) end return nil end -- Or-join containers must precede any supplemental (calque-like / influence) containers. local seen_supplement = false for _, part in ipairs(parts) do local kind = container_join_kind(part) if kind == "supplement" then seen_supplement = true elseif kind == "or_join" and seen_supplement then error( "Cannot generate etymology text: a main derivation step cannot follow a calque, semantic loan, or influence clause in the same list.") end end for _, part in ipairs(parts) do if part.continuation then check_complexity(part.continuation) end end return nil end -- Analyze tree and assign punctuation local function analyze_punctuation(tree, is_toplevel) if not tree then return end local parts = tree.container_parts local num_parts = #parts for i, part in ipairs(parts) do local is_first = (i == 1) local is_last = (i == num_parts) local next_part = parts[i + 1] -- Analyze term punctuation within container if part.term_parts then -- Terms use Oxford comma style: "A, B, or C" -- Custom conjunction can be specified via conj modifier (e.g., "and/or", "and") local num_terms = #part.term_parts local term_conj = part.conj or "or" -- default to "or" for j, term_part in ipairs(part.term_parts) do local is_last_term = (j == num_terms) if part.is_group then -- Group: terms joined with " + " term_part.joiner = is_last_term and "" or " + " elseif num_terms > 1 then -- Multiple terms not in a group: Oxford comma style if is_last_term then term_part.joiner = "" elseif j == num_terms - 1 then -- Second to last term if num_terms == 2 then term_part.joiner = " " .. term_conj .. " " else term_part.joiner = ", " .. term_conj .. " " end else term_part.joiner = ", " end else -- Single term term_part.joiner = "" end end end -- Determine container punctuation based on what comes next if part.continuation then -- Has continuation part.punctuation = "," -- Recursively analyze continuation analyze_punctuation(part.continuation, false) elseif is_last then -- Last container at this level (may still continue in part.continuation) part.punctuation = "." elseif next_part and next_part.new_sentence then -- Next container starts a new sentence part.punctuation = "." elseif next_part and next_part.separate_clause then -- Next container is a separate clause part.punctuation = "," else -- Not last, next is joined with "or" -- Containers use repeated "or" style: "A, or B, or C" part.punctuation = "," end -- Determine joiner to next part -- Containers use repeated "or" style: ", or" between each -- Custom conjunction can be specified via conj modifier local container_conj = part.conj or "or" -- default to "or" if not is_last then if next_part and next_part.new_sentence then -- New sentence part.joiner = " " elseif next_part and next_part.separate_clause then -- Separate clause part.joiner = " " else -- Same sentence: use custom conjunction or default "or" part.joiner = " " .. container_conj .. " " end else part.joiner = "" end -- Determine intro formatting -- Capitalize if first at top level, OR if this container starts a new sentence if (is_first and is_toplevel) or part.new_sentence then part.intro_capitalized = true part.use_full_intro = true else part.intro_capitalized = false part.use_full_intro = false end end end -- Assemble text from analyzed tree local function assemble_text(tree) if not tree then return "" end local result = "" for i, part in ipairs(tree.container_parts) do if part.type == "etydate" then result = result .. part.etydate_text if part.punctuation and part.punctuation ~= "" then result = result .. part.punctuation end if part.etydate_refs and next(part.etydate_refs) then result = result .. M.references.format_references(part.etydate_refs) end if part.joiner and part.joiner ~= "" then result = result .. part.joiner end else -- Build intro local intro_text = part.intro_text local phrase = part.phrase if part.senseid then intro_text, phrase = format_sl_senseid_intro( part.senseid, part.sl_keyword_text, part.phrase, part.intro_capitalized, part.is_uncertain ) end local intro if part.use_full_intro then if part.is_uncertain and not part.senseid then intro = "Possibly " .. phrase else intro = intro_text end else if part.is_uncertain and not part.senseid then intro = "possibly " .. phrase else intro = phrase end end result = result .. intro -- Build terms if #part.term_parts > 0 then result = result .. " " for j, term_part in ipairs(part.term_parts) do -- Add "possibly" prefix for uncertain terms when there's mixed uncertainty if part.has_mixed_uncertainty and term_part.is_uncertain then result = result .. "possibly " end result = result .. term_part.text -- Add joiner between terms if term_part.joiner ~= "" then -- Check if joiner contains comma (punctuation) local comma_pos = term_part.joiner:find(",") if comma_pos then -- Add up to and including comma result = result .. term_part.joiner:sub(1, comma_pos) -- Add refs after comma if term_part.refs ~= "" then result = result .. term_part.refs end -- Add rest of joiner result = result .. term_part.joiner:sub(comma_pos + 1) else -- No comma, add refs before joiner if term_part.refs ~= "" then result = result .. term_part.refs end result = result .. term_part.joiner end end end -- For the last term, add punctuation then refs local last_term = part.term_parts[#part.term_parts] if last_term and last_term.joiner == "" then if part.punctuation ~= "" then -- If we have literal text, punctuation goes AFTER it if part.lit then -- Add refs first (attached to term) if last_term.refs ~= "" then result = result .. last_term.refs end -- Add keyword refs if part.keyword_refs and part.keyword_refs ~= "" then result = result .. part.keyword_refs end -- Add literal text result = result .. ", literally “" .. part.lit .. "”" -- Add punctuation result = result .. part.punctuation else -- Normal behavior: punctuation then refs result = result .. part.punctuation if last_term.refs ~= "" then result = result .. last_term.refs end -- Add keyword refs after term refs if part.keyword_refs and part.keyword_refs ~= "" then result = result .. part.keyword_refs end end else -- No punctuation if last_term.refs ~= "" then result = result .. last_term.refs end -- Add keyword refs if part.keyword_refs and part.keyword_refs ~= "" then result = result .. part.keyword_refs end -- Add literal text if present (even without punctuation) if part.lit then result = result .. ", literally “" .. part.lit .. "”" end end end else -- No terms, just add punctuation and keyword refs if part.punctuation ~= "" then result = result .. part.punctuation end -- Add keyword refs even when there are no terms if part.keyword_refs and part.keyword_refs ~= "" then result = result .. part.keyword_refs end end -- Add continuation if part.continuation then result = result .. " " .. assemble_text(part.continuation) end -- Add joiner to next container if part.joiner ~= "" then result = result .. part.joiner end end end return result end local text_tree = build_text_tree(data_tree, 1, true, false) -- Supplements (doublets, etydate, …) are rendered outside the main derivation text tree. local function assemble_supplements() if not data_tree.supplements then return "" end local chunks = {} local pending_trees = {} local function flush_pending_trees() local num = #pending_trees for i, supplement_tree in ipairs(pending_trees) do analyze_punctuation(supplement_tree, true) apply_closing_punctuation_override(supplement_tree, i == num) local chunk = assemble_text(supplement_tree) if chunk ~= "" then table.insert(chunks, chunk) end end pending_trees = {} end for _, supplement in ipairs(data_tree.supplements) do local supplement_tree if supplement.type == "doublet" and supplement.config and supplement.terms and #supplement.terms > 0 then local config = supplement.config local term_parts = {} for _, term in ipairs(supplement.terms) do local term_part = build_term_part(term, entry_lang, true) if term_part.text ~= "" then table.insert(term_parts, term_part) end end if #term_parts == 0 then supplement_tree = nil else supplement_tree = { type = "tree", container_parts = { { type = "doublet", intro_text = config.text, phrase = config.phrase, term_parts = term_parts, conj = config.default_conj or "and", new_sentence = true, }, }, depth = 1, } end elseif supplement.type == "etydate" and supplement.etydate_text and supplement.etydate_text ~= "" then supplement_tree = { type = "tree", container_parts = { { type = "etydate", etydate_text = supplement.etydate_text, etydate_refs = supplement.etydate_refs, new_sentence = true, }, }, depth = 1, } end if supplement_tree then table.insert(pending_trees, supplement_tree) end end flush_pending_trees() return table.concat(chunks, " ") end if not text_tree then local supplement_text = assemble_supplements() if supplement_text == "" then if stop_at_lang then return "", { stop_lang_reached = false } end return "" end if stop_at_lang then return supplement_text, { stop_lang_reached = false } end return supplement_text end local rendered = "" if text_tree then check_complexity(text_tree) analyze_punctuation(text_tree, true) apply_closing_punctuation_override(text_tree, not has_supplements()) rendered = assemble_text(text_tree) end local supplement_text = assemble_supplements() if supplement_text ~= "" then if rendered ~= "" then rendered = rendered .. " " .. supplement_text else rendered = supplement_text end end if stop_at_lang then return rendered, { stop_lang_reached = stop_lang_reached } end return rendered end return export 55fpa9rl4jqvgxk9ggp38excqfa50nk Module:etymon/categories 828 8141 55030 2026-09-30T19:21:49Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local M = require("Module:module loader").init({ require = { etymology = "Module:etymology", affix = "Module:affix", etymology_specialized = "Module:etymology/specialized", utilities = "Module:utilities", roots = "Module:roots", }, loadData = { data = "Module:etymon/data", }, }) -- Evaluate whether a keyword is transitive for a given term local function is_transitive(transitive_mode, page_lang, term_lang) if transitive_mode ==...' 55030 Scribunto text/plain local export = {} local M = require("Module:module loader").init({ require = { etymology = "Module:etymology", affix = "Module:affix", etymology_specialized = "Module:etymology/specialized", utilities = "Module:utilities", roots = "Module:roots", }, loadData = { data = "Module:etymon/data", }, }) -- Evaluate whether a keyword is transitive for a given term local function is_transitive(transitive_mode, page_lang, term_lang) if transitive_mode == M.data.TRANSITIVE.ALWAYS then return true elseif transitive_mode == M.data.TRANSITIVE.NEVER then return false elseif transitive_mode == M.data.TRANSITIVE.CROSS_LANG then return page_lang:getCode() ~= term_lang:getCode() elseif transitive_mode == M.data.TRANSITIVE.CROSS_LANG_NO_INTERNAL_SOURCE then return page_lang:getCode() ~= term_lang:getCode() end error("Unknown transitive mode: " .. tostring(transitive_mode)) end -- Get keyword config with language-specific overrides local function get_keyword_config(keyword, lang_exc) local base_config = M.data.keywords[keyword] if not base_config then return nil -- Invalid keyword end local overrides = lang_exc and lang_exc.keyword_overrides and lang_exc.keyword_overrides[keyword] if not overrides then return base_config end -- Merge overrides into base config local merged = {} for k, v in pairs(base_config) do merged[k] = v end for k, v in pairs(overrides) do merged[k] = v end return merged end function export.get_cat_name(source) local _, cat_name = M.etymology.get_display_and_cat_name(source, true) return cat_name end -- Normalize affix type aliases local function add_category(categories, cat_name, sort_key, sort_base) if categories[cat_name] == nil then categories[cat_name] = { sort_key = sort_key, sort_base = sort_base, } return end local existing = categories[cat_name] if existing.sort_key == nil and sort_key ~= nil then existing.sort_key = sort_key end if existing.sort_base == nil and sort_base ~= nil then existing.sort_base = sort_base end end -- Collect affix categories from top-level group containers local function collect_affix_categories(node, page_lang, available_etymon_ids, senseid_parent_etymon, lang_exc) local parts = {} local part_index = 1 for _, container in ipairs(node.children or {}) do local config = container.keyword_info if config and config.affix_categories then for _, term in ipairs(container.terms or {}) do if not term.unknown_term then local part_data = { term = term.title, tr = term.tr, ts = term.ts, alt = term.alt, itemno = part_index, orig_index = part_index } -- Determine affix type: explicit aftype > pos=root > auto-detect local aftype = term.aftype if aftype then aftype = M.data.aftype_aliases[aftype] or aftype part_data.type = aftype elseif term.args and term.args.pos and term.args.pos == "root" then part_data.type = "non-affix" end if term.lang:getCode() ~= page_lang:getCode() then part_data.lang = term.lang end local target_ids = available_etymon_ids[term.target_key] local has_multiple_ids = target_ids and #target_ids > 1 local id_exists_in_disambiguation = false local matched_id = nil -- Count available senseids for the target page local senseid_count = 0 local target_prefix = term.target_key .. ":" if senseid_parent_etymon then for key, _ in pairs(senseid_parent_etymon) do if key:sub(1, #target_prefix) == target_prefix then senseid_count = senseid_count + 1 end end end local has_multiple_senseids = senseid_count > 1 if term.id then -- Check if user provided a valid senseid local senseid_key = term.target_key .. ":" .. term.id if senseid_parent_etymon and senseid_parent_etymon[senseid_key] then if has_multiple_senseids then -- Ambiguous senseid: use senseid matched_id = term.id id_exists_in_disambiguation = true elseif has_multiple_ids then -- Unique senseid but ambiguous etymon: use etymon ID matched_id = term.etymon_id or term.id id_exists_in_disambiguation = true end else -- Check if user provided a valid etymon ID if has_multiple_ids and target_ids then for _, id_data in ipairs(target_ids) do local stored_id = type(id_data) == "table" and id_data.id or id_data if stored_id == term.id then -- Ambiguous etymon: use etymon ID id_exists_in_disambiguation = true matched_id = term.id break end end end -- Fallback: check resolved etymon_id (e.g. from previous steps) if not id_exists_in_disambiguation and has_multiple_ids and term.etymon_id and target_ids then for _, id_data in ipairs(target_ids) do local stored_id = type(id_data) == "table" and id_data.id or id_data if stored_id == term.etymon_id then id_exists_in_disambiguation = true matched_id = term.etymon_id break end end end end end -- Use the matched ID if found if term.override or id_exists_in_disambiguation then part_data.id = matched_id or term.id end table.insert(parts, part_data) part_index = part_index + 1 end end end end if #parts == 0 then return {} end local affix_data = { lang = page_lang, parts = parts, pos = "term", sort_key = nil, } if #parts == 1 then affix_data.allow_no_affixes_or_compounds = true end local affix_categories = M.affix.get_affix_categories_only(affix_data) local result = {} for _, cat in ipairs(affix_categories) do if type(cat) == "table" then table.insert(result, { cat = cat.cat, sort_key = cat.sort_key, sort_base = cat.sort_base }) else table.insert(result, { cat = cat }) end end return result end local function lang_is_source(page_lang, source) return page_lang:getCode() == source:getCode() or page_lang:hasParent(source) end local function is_borrowing_keyword_config(config) return config and (config.borrowing_type or config.specialized_borrowing) end local function add_reborrow_category(categories, page_lang) local lang_name = page_lang:getFullName() add_category(categories, lang_name .. " terms borrowed back into " .. lang_name) end local function borrow_returns_to_page_lang(page_lang, source, in_foreign_branch) if not in_foreign_branch then return false end if source:getFullCode() == page_lang:getFullCode() then return true end return page_lang:hasParent(source) end local function node_borrows_from_lang(node, page_lang, visited, in_foreign_branch) visited = visited or {} if not node or visited[node] then return false end visited[node] = true if node.is_duplicate then if node.duplicate_of then return node_borrows_from_lang(node.duplicate_of, page_lang, visited, in_foreign_branch) end return false end local node_is_foreign = in_foreign_branch or (node.lang and node.lang:getFullCode() ~= page_lang:getFullCode()) for _, container in ipairs(node.children or {}) do if is_borrowing_keyword_config(container.keyword_info) then for _, child_term in ipairs(container.terms or {}) do if borrow_returns_to_page_lang(page_lang, child_term.lang, node_is_foreign) then return true end end end for _, child_term in ipairs(container.terms or {}) do if child_term.status == M.data.STATUS.OK or child_term.status == M.data.STATUS.INLINE then if node_borrows_from_lang(child_term, page_lang, visited, node_is_foreign) then return true end end end end return false end local function should_add_reborrow_category(page_lang, term) if page_lang:getCode() == term.lang:getCode() then return false end if term.status ~= M.data.STATUS.OK and term.status ~= M.data.STATUS.INLINE then return false end return node_borrows_from_lang(term, page_lang, {}, false) end -- Add borrowing-related categories (top-level only) local function collect_borrowing_categories(categories, page_lang, term, config, check_reborrow_path) if check_reborrow_path and should_add_reborrow_category(page_lang, term) then add_reborrow_category(categories, page_lang) end if config.borrowing_type == "borrowed" and not lang_is_source(page_lang, term.lang) then local temp_categories = {} M.etymology.insert_borrowed_cat(temp_categories, page_lang, term.lang) for _, cat in ipairs(temp_categories) do add_category(categories, cat) end end if config.specialized_borrowing and not lang_is_source(page_lang, term.lang) then local result = M.etymology_specialized.specialized_borrowing { bortype = config.specialized_borrowing, lang = page_lang, sources = { term.lang }, terms = { { lang = term.lang, term = "-" } }, notext = true, nocat = false, } for cat_name in result:gmatch("%[%[Category:([^%]]+)%]%]") do add_category(categories, cat_name) end end end -- Add source-based derivation categories (top-level only) local function collect_source_derivation_categories(categories, page_lang, term, config) if not config.source_category_type then return end local temp_categories = {} M.etymology.insert_source_cat_get_display { lang = page_lang, source = term.lang, categories = temp_categories, borrowing_type = config.source_category_type, nocat = false, } for _, cat in ipairs(temp_categories) do add_category(categories, cat) end end -- Add source language categories local function collect_source_categories(categories, page_lang, term, chain, get_norm_lang_func) if page_lang:getCode() == get_norm_lang_func(term.lang):getCode() then return end local temp_categories = {} M.etymology.insert_source_cat_get_display { lang = page_lang, source = term.lang, categories = temp_categories, nocat = false, } for _, cat in ipairs(temp_categories) do add_category(categories, cat) end if chain.inherited then temp_categories = {} M.etymology.insert_source_cat_get_display { lang = page_lang, source = term.lang, categories = temp_categories, borrowing_type = "terms inherited", nocat = false, } for _, cat in ipairs(temp_categories) do add_category(categories, cat) end end end -- Add root/word categories local function collect_pos_categories(categories, page_lang, root_title, term, available_etymon_ids, chain, get_norm_lang_func, lang_exc, keyword) local pos_types = { root = "root", word = "word" } -- Determine pos: from term's postype, keyword's pos_override, or args.pos local pos local config = get_keyword_config(keyword, lang_exc) if term.postype then -- Term-level postype modifier takes highest priority pos = term.postype elseif config and config.pos_override then pos = config.pos_override elseif type(term.args) == "table" and term.args.pos then pos = term.args.pos end local pos_type = pos_types[pos] if not pos_type or term.unknown_term then return end -- Skip root/word categories for descendants of affix groups -- if pos_type then -- return -- end local same_language = get_norm_lang_func(page_lang):getFullCode() == get_norm_lang_func(term.lang):getFullCode() -- Skip self-references if same_language and root_title == term.title then return end local entry_name if pos_type == "root" then entry_name = term.title M.roots.assert_root(term.lang, entry_name) else entry_name = term.lang:makeEntryName(term.title) end local lang_name = page_lang:getCanonicalName() local cat_name if chain.passed_through then local etymon_lang_name = export.get_cat_name(term.lang) cat_name = lang_name .. " terms derived from the " .. etymon_lang_name .. " " .. pos_type .. " " .. entry_name else cat_name = lang_name .. " terms belonging to the " .. pos_type .. " " .. entry_name end -- Add ID disambiguation if needed (for roots/words: use etymon_id if resolved via senseid, otherwise use id) local target_ids = available_etymon_ids[term.target_key] local effective_id = term.etymon_id or term.id -- etymon_id if senseid, otherwise id is already an etymon id if target_ids and effective_id then local same_pos_count = 0 for _, id_data in ipairs(target_ids) do if type(id_data) == "table" and id_data.pos == pos then same_pos_count = same_pos_count + 1 end end if same_pos_count > 1 then cat_name = cat_name .. " (" .. effective_id .. ")" end end add_category(categories, cat_name) end -- Compute chain state for a term based on parent chain and keyword config -- Hyphen patterns for affix detection (regular hyphen + script-specific) local AFFIX_HYPHEN_PATTERN = "[%-%־ـ᠊]" -- regular hyphen, Hebrew maqqef, Arabic tatweel, Mongolian hyphen -- Check if a term is an actual affix (not a non-affix member of an affix group) local function is_actual_affix(term) -- Check explicit aftype modifier if term.aftype then local normalized = M.data.aftype_aliases[term.aftype] or term.aftype return normalized ~= "non-affix" end -- Check if pos=root (treated as non-affix) if term.args and term.args.pos and term.args.pos == "root" then return false end -- Auto-detect by hyphen: prefix ends with -, suffix starts with -, etc. if term.title then local title = term.title -- Strip leading * for reconstructed terms before checking hyphens title = title:gsub("^%*", "") -- Check for hyphens at start or end (handles script-specific hyphens too) if title:match("^" .. AFFIX_HYPHEN_PATTERN) or title:match(AFFIX_HYPHEN_PATTERN .. "$") then return true end end -- Default: not an affix return false end local function compute_category_chain(parent_chain, config, page_lang, term_lang, get_norm_lang_func, parent_term_lang, term) -- Track if we're inside an actual affix (for suppressing root categories on descendants) -- Only set if the term is an actual affix (prefix, suffix, etc.), not a non-affix member local inside_affix = parent_chain.inside_affix if config.affix_categories and term and is_actual_affix(term) then inside_affix = true end -- If no_child_categories is set, disable everything if config.no_child_categories then return { passed_through = parent_chain.passed_through or page_lang:getCode() ~= get_norm_lang_func(term_lang):getCode(), inherited = false, source = false, pos = false, recurse = false, inside_affix = inside_affix, } end local term_is_transitive = is_transitive(config.transitive, page_lang, term_lang) local new_source = parent_chain.source and term_is_transitive -- For CROSS_LANG_NO_INTERNAL_SOURCE: track internal derivation language context -- Check if this term is internal relative to parent term's language (if parent_term_lang provided) -- or relative to page language (if no parent_term_lang) local internal_lang = parent_chain.internal_lang local is_internal_in_context = false if config.transitive == M.data.TRANSITIVE.CROSS_LANG_NO_INTERNAL_SOURCE then local check_lang = parent_term_lang or page_lang local term_lang_code = get_norm_lang_func(term_lang):getCode() local check_lang_code = get_norm_lang_func(check_lang):getCode() if internal_lang then -- Already in an internal derivation context: check if this term is also internal is_internal_in_context = term_lang_code == internal_lang else -- Check if this term is internal relative to parent term (or page if no parent) is_internal_in_context = term_lang_code == check_lang_code end end -- Source chain behavior for CROSS_LANG_NO_INTERNAL_SOURCE if config.transitive == M.data.TRANSITIVE.CROSS_LANG_NO_INTERNAL_SOURCE then if is_internal_in_context then -- Internal derivation new_source = false internal_lang = get_norm_lang_func(term_lang):getCode() else -- Cross-language new_source = parent_chain.source and term_is_transitive internal_lang = nil end end local new_pos = parent_chain.pos return { passed_through = parent_chain.passed_through or page_lang:getCode() ~= get_norm_lang_func(term_lang):getCode(), inherited = parent_chain.inherited and config.inherited_chain, source = new_source, pos = new_pos, internal_lang = internal_lang, recurse = new_source or new_pos, inside_affix = inside_affix, } end function export.render(opts) opts = opts or {} local data_tree = opts.data_tree local page_lang = opts.page_lang local available_etymon_ids = opts.available_etymon_ids local senseid_parent_etymon = opts.senseid_parent_etymon local get_norm_lang_func = opts.get_norm_lang_func local lang_exc = opts.lang_exc local categories = {} local seen = {} local lang_name = page_lang:getCanonicalName() local root_title = data_tree.title -- Collect the tree recursively local function collect(node, parent_chain, is_toplevel) -- Avoid processing same node twice if not node.unknown_term and node.title then local key = node.lang:getFullCode() .. ":" .. (node.title or "") .. ":" .. (node.id or "") if seen[key] then return end seen[key] = true end -- Collect affix categories at top level only if is_toplevel then local affix_cats = collect_affix_categories(node, page_lang, available_etymon_ids, senseid_parent_etymon, lang_exc) for _, cat in ipairs(affix_cats) do add_category(categories, lang_name .. " " .. cat.cat, cat.sort_key, cat.sort_base) end if node.supplements then for _, supplement in ipairs(node.supplements) do local config = supplement.config if config and config.toplevel_category then add_category(categories, lang_name .. " " .. config.toplevel_category) end end end end -- Process each container for _, container in ipairs(node.children or {}) do local keyword = container.keyword local config = get_keyword_config(keyword, lang_exc) -- Skip invalid keywords if config then -- Process each term in the container for _, term in ipairs(container.terms or {}) do local term_chain = compute_category_chain(parent_chain, config, page_lang, term.lang, get_norm_lang_func, node.lang, term) local no_child_categories = config.no_child_categories == true local term_is_transitive = is_transitive(config.transitive, page_lang, term.lang) -- Top-level only processing if is_toplevel then -- Missing/ambiguous etymon tracking if not term.unknown_term and (term.status == M.data.STATUS.MISSING or term.status == M.data.STATUS.REDLINK) then add_category(categories, lang_name .. " entries referencing missing etymons") end if not term.unknown_term and term.status == M.data.STATUS.AMBIGUOUS then add_category(categories, lang_name .. " entries referencing ambiguous etymons") end if term.missing_descendants_header then add_category(categories, lang_name .. " entries referencing etymons without Descendants sections") end if term.missing_descendants_entry then add_category(categories, lang_name .. " entries referencing etymons without this term in Descendants sections") end -- Top-level category (e.g., "undefined derivations") if config.toplevel_category then add_category(categories, lang_name .. " " .. config.toplevel_category) end -- Borrowing categories (bor, lbor, slbor, ubor, obor) if config.borrowing_type or config.specialized_borrowing then collect_borrowing_categories(categories, page_lang, term, config, true) end -- Borrowing categories from <bor>, <lbor>, or <slbor> modifiers on affix-group terms local kw_config = M.data.keywords[keyword] if kw_config and kw_config.affix_categories then if term.bor then local bor_config = { borrowing_type = "borrowed" } collect_borrowing_categories(categories, page_lang, term, bor_config, true) elseif term.lbor then local bor_config = { specialized_borrowing = "learned" } collect_borrowing_categories(categories, page_lang, term, bor_config, true) elseif term.slbor then local bor_config = { specialized_borrowing = "semi-learned" } collect_borrowing_categories(categories, page_lang, term, bor_config, true) end end -- Source-based derivation categories (sl, calque, pcal) if config.source_category_type then collect_source_derivation_categories(categories, page_lang, term, config) end -- Skip all child categorisation if no_child_categories is set if not no_child_categories then -- Source categories only if transitive if term_is_transitive then collect_source_categories(categories, page_lang, term, term_chain, get_norm_lang_func) end -- Pos categories always (unless no_child_categories) collect_pos_categories(categories, page_lang, root_title, term, available_etymon_ids, term_chain, get_norm_lang_func, lang_exc, keyword) end else -- Below top level, respect the parent chain if parent_chain.source then collect_source_categories(categories, page_lang, term, term_chain, get_norm_lang_func) end if parent_chain.pos then collect_pos_categories(categories, page_lang, root_title, term, available_etymon_ids, term_chain, get_norm_lang_func, lang_exc, keyword) end end -- Recurse into term's children if needed and status allows if term_chain.recurse and (term.status == M.data.STATUS.OK or term.status == M.data.STATUS.INLINE) then collect(term, term_chain, false) end end end end end -- Initial chain state local initial_chain = { passed_through = false, inherited = true, source = true, pos = true, internal_lang = nil, recurse = true, inside_affix = false, } collect(data_tree, initial_chain, true) local cat_list = {} for cat_name, sort_data in pairs(categories) do if sort_data.sort_key ~= nil or sort_data.sort_base ~= nil then table.insert(cat_list, { name = cat_name, sort_key = sort_data.sort_key, sort_base = sort_data.sort_base, }) else table.insert(cat_list, cat_name) end end return cat_list end function export.build(opts) opts = opts or {} local categories = {} if not opts.suppress_categories and not opts.nocat then categories = export.render({ data_tree = opts.data_tree, page_lang = opts.page_lang, available_etymon_ids = opts.available_etymon_ids, senseid_parent_etymon = opts.senseid_parent_etymon, get_norm_lang_func = opts.get_norm_lang_func, lang_exc = opts.lang_exc, }) end local page_lang = opts.page_lang if not page_lang then return categories end local lang_name = page_lang:getCanonicalName() table.insert(categories, "Pages with etymon") table.insert(categories, lang_name .. " entries with etymon") if opts.tree then table.insert(categories, "Pages with etymology trees") table.insert(categories, lang_name .. " entries with etymology trees") end if opts.text then table.insert(categories, lang_name .. " entries with etymology texts") end if opts.exnihilo then table.insert(categories, lang_name .. " terms coined ex nihilo") end if opts.toplevel_has_inline_etymology then table.insert(categories, "Pages with inline etymon for redlinks") end if opts.toplevel_redundant_etymology then table.insert(categories, "Pages with redundant inline etymon") end if opts.toplevel_idless_etymon then table.insert(categories, "Pages using etymon with no ID") end if opts.has_mismatched_id then table.insert(categories, lang_name .. " entries referencing etymons with mismatched IDs") end if opts.linked_page_multiple_etymons_idless then table.insert(categories, lang_name .. " entries referencing pages with multiple etymons missing IDs") end if opts.linked_page_partial_etymology_sections then table.insert(categories, lang_name .. " entries referencing pages with etymology sections missing etymons") end if opts.text_stop_lang_missing then table.insert(categories, "Pages with etymology text stop language not in chain") table.insert(categories, lang_name .. " entries with etymology text stop language not in chain") end return categories end function export.format(entries, lang) if type(entries) ~= "table" or #entries == 0 then return "" end local parts = {} for _, category in ipairs(entries) do if type(category) == "table" and type(category.name) == "string" then table.insert(parts, M.utilities.format_categories({ category.name }, lang, category.sort_key, category.sort_base)) elseif type(category) == "string" then table.insert(parts, M.utilities.format_categories({ category }, lang)) end end return table.concat(parts) end return export l7p4kvhjzzkv6mvr0d3ud5xek882jvl Bysen:goh-noun 10 8142 55032 2026-09-30T19:31:21Z Deadend0914 7211 Gesceop tramet þe hafaþ '{{#invoke:checkparams|error}}<!-- Validate template parameters -->{{head|goh|noun|head={{{head|}}}|sort={{{sort|}}}<!-- -->|g={{#switch:{{{1}}}|m|f|n|m-p|f-p|n-p={{{1}}}|?}}<!-- -->|g2={{#switch:{{{g2|}}}|m|f|n|m-p|f-p|n-p={{{g2}}}}}<!-- -->|{{#if:{{{2|}}}|plural}}<!-- -->|{{{2|}}}<!-- -->|{{#if:{{{pl2|}}}|or}}<!-- -->|{{{pl2|}}}<!-- -->}}<!-- --><noinclude>{{documentation}}</noinclude>' 55032 wikitext text/x-wiki {{#invoke:checkparams|error}}<!-- Validate template parameters -->{{head|goh|noun|head={{{head|}}}|sort={{{sort|}}}<!-- -->|g={{#switch:{{{1}}}|m|f|n|m-p|f-p|n-p={{{1}}}|?}}<!-- -->|g2={{#switch:{{{g2|}}}|m|f|n|m-p|f-p|n-p={{{g2}}}}}<!-- -->|{{#if:{{{2|}}}|plural}}<!-- -->|{{{2|}}}<!-- -->|{{#if:{{{pl2|}}}|or}}<!-- -->|{{{pl2|}}}<!-- -->}}<!-- --><noinclude>{{documentation}}</noinclude> fwmnmkv91lwtdilofa7xf5hwrog7q28 Bysen:der 10 8143 55036 2026-09-30T19:36:10Z Deadend0914 7211 Redirected page to [[Bysen:derived]] 55036 wikitext text/x-wiki #REDIRECT [[Bysen:derived]] 7b14fhut10xy4i0i72jaf9kdlidizni Bysen:derived 10 8144 55037 2026-09-30T19:36:25Z Deadend0914 7211 Gesceop tramet þe hafaþ '<includeonly>{{#invoke:etymology/templates|derived}}</includeonly><!-- --><noinclude>{{der|und|und|term}}{{documentation}}</noinclude>' 55037 wikitext text/x-wiki <includeonly>{{#invoke:etymology/templates|derived}}</includeonly><!-- --><noinclude>{{der|und|und|term}}{{documentation}}</noinclude> qy162q531c9y9k498mdiw259aiw0jl1 drink 0 8145 55041 2026-09-30T19:41:19Z Deadend0914 7211 Gesceop tramet þe hafaþ '=={{sprǣc|en}}== ==Rihtstefn=== * IPA: /ˈdɹɪŋk/, ===Wordstǣr=== {{inh+|en|enm|drink}}, of {{inh|en|ang|drynċ}}, of {{inh|en|gem-pro|*drunkiz}} ===Nama=== # drync' 55041 wikitext text/x-wiki =={{sprǣc|en}}== ==Rihtstefn=== * IPA: /ˈdɹɪŋk/, ===Wordstǣr=== {{inh+|en|enm|drink}}, of {{inh|en|ang|drynċ}}, of {{inh|en|gem-pro|*drunkiz}} ===Nama=== # drync gp0s0o3s3g66xpobfd3k9quo6j56mbo 55042 55041 2026-09-30T19:41:27Z Deadend0914 7211 /* Wordstǣr */ 55042 wikitext text/x-wiki =={{sprǣc|en}}== ==Rihtstefn=== * IPA: /ˈdɹɪŋk/, ===Wordstǣr=== {{inh+|en|enm|drink}}, of {{inh|en|ang|drync}}, of {{inh|en|gem-pro|*drunkiz}} ===Nama=== # drync dcv7q0qqdll1lgcdmamtcu7z0s8skz6 55043 55042 2026-09-30T19:41:53Z Deadend0914 7211 /* Nama */ 55043 wikitext text/x-wiki =={{sprǣc|en}}== ==Rihtstefn=== * IPA: /ˈdɹɪŋk/, ===Wordstǣr=== {{inh+|en|enm|drink}}, of {{inh|en|ang|drync}}, of {{inh|en|gem-pro|*drunkiz}} ===Nama=== {{en-noun|~}} # drync pli3db4zayfekb3ke29hbn0jzl31by1 55044 55043 2026-09-30T19:42:03Z Deadend0914 7211 /* Nama */ 55044 wikitext text/x-wiki =={{sprǣc|en}}== ==Rihtstefn=== * IPA: /ˈdɹɪŋk/, ===Wordstǣr=== {{inh+|en|enm|drink}}, of {{inh|en|ang|drync}}, of {{inh|en|gem-pro|*drunkiz}} ===Nama=== {{en-noun|~}} # [[drync]] cbse2g621xfzb6krfb0ju9jgoig4s14 undergo 0 8146 55045 2026-09-30T19:46:57Z Deadend0914 7211 Gesceop tramet þe hafaþ '=={{sprǣc|en}}== ===Rihstefn=== * IPA: /ˌʌndɚˈɡoʊ/ ===Wordstǣr=== Of {{inh|en|enm|undergon}}, of {{inh|en|ang|undergān}} ===Word=== {{en-verb|~es|~ing|underwent|~ne}} # undergān' 55045 wikitext text/x-wiki =={{sprǣc|en}}== ===Rihstefn=== * IPA: /ˌʌndɚˈɡoʊ/ ===Wordstǣr=== Of {{inh|en|enm|undergon}}, of {{inh|en|ang|undergān}} ===Word=== {{en-verb|~es|~ing|underwent|~ne}} # undergān 265x8vxywv4rfw0w1r9lcbnzhrgp8wu 55048 55045 2026-09-30T19:54:27Z Deadend0914 7211 /* Word */ 55048 wikitext text/x-wiki =={{sprǣc|en}}== ===Rihstefn=== * IPA: /ˌʌndɚˈɡoʊ/ ===Wordstǣr=== Of {{inh|en|enm|undergon}}, of {{inh|en|ang|undergān}} ===Word=== {{en-verb|~es|~ing|underwent|~ne}} # [[undergān]] tnkkrcbb1jqwgzovyjmkknzp8x2ggp0 Bysen:en-verb 10 8147 55046 2026-09-30T19:47:19Z Deadend0914 7211 Gesceop tramet þe hafaþ '{{#invoke:en-headword|show|verbs}}<!-- --><noinclude>{{documentation}}</noinclude>' 55046 wikitext text/x-wiki {{#invoke:en-headword|show|verbs}}<!-- --><noinclude>{{documentation}}</noinclude> 61usq2ftl97ga0ky6nna0er3yyqtbp9 Bysen:en-head 10 8148 55049 2026-09-30T19:55:00Z Deadend0914 7211 Gesceop tramet þe hafaþ '{{#invoke:en-headword|show}}<!-- --><noinclude>{{documentation}}</noinclude>' 55049 wikitext text/x-wiki {{#invoke:en-headword|show}}<!-- --><noinclude>{{documentation}}</noinclude> b61rollxgc9fbrkcs5t760xnqz1hw58 Module:qualifier 828 8149 55054 2026-09-30T22:06:09Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local concat = table.concat --[==[ Wrap text in one or more CSS classes. `classes` should be a string; separate multiple classes with a space. ]==] function export.wrap_css(text, classes) return ("<span class=\"%s\">%s</span>"):format(classes, text) end --[==[ Wrap text in one or more qualifier CSS classes. `suffix` is the suffix describing the type of content, e.g. `brac` for parens, `content` for content, `comma` for commas. CSS classes <c...' 55054 Scribunto text/plain local export = {} local concat = table.concat --[==[ Wrap text in one or more CSS classes. `classes` should be a string; separate multiple classes with a space. ]==] function export.wrap_css(text, classes) return ("<span class=\"%s\">%s</span>"):format(classes, text) end --[==[ Wrap text in one or more qualifier CSS classes. `suffix` is the suffix describing the type of content, e.g. `brac` for parens, `content` for content, `comma` for commas. CSS classes <code>ib-<var>suffix</var></code> and i<code>qualifier-<var>suffix</var></code> are added. ]==] function export.wrap_qualifier_css(text, suffix) local css_classes = ("ib-%s qualifier-%s"):format(suffix, suffix) return export.wrap_css(text, css_classes) end --[==[ Format one or more qualifiers. `data` is an object with the following fields: * `qualifiers`: A single qualifier or a list or qualifiers. * `open`: Override the open paren displayed before the qualifiers. If `false` or an empty string, no paren is displayed. * `close`: Override the close paren displayed before the qualifiers. If `false` or an empty string, no paren is displayed. * `opencontent`: Content to display before the qualifiers, after the open paren. * `closecontent`: Content to display after the qualifiers, before the close paren. * `no_ib_content`: Suppress wrapping the content with classes `ib-content` and `qualifier-content`. Parens and commas will still be wrapped in CSS. * `raw`: Suppress all CSS wrapping. ]==] function export.format_qualifiers(data) local qualifiers, open, close = data.qualifiers, data.open, data.close if type(qualifiers) ~= "table" then qualifiers = {qualifiers} end if not qualifiers[1] then return "" end local parts = {} local function ins(text) table.insert(parts, text) end local function wrap_qualifier_css(text, suffix) if data.raw then return text end return export.wrap_qualifier_css(text, suffix) end if open ~= false and open ~= ""then ins(wrap_qualifier_css(open or "(", "brac")) end if data.opencontent then ins(data.opencontent) end local content = concat(qualifiers, wrap_qualifier_css(",", "comma") .. " ") if not data.no_ib_content then content = wrap_qualifier_css(content, "content") end ins(content) if data.closecontent then ins(data.closecontent) end if close ~= false and close ~= "" then ins(wrap_qualifier_css(close or ")", "brac")) end return concat(parts) end --[==[ An older interface onto `format_qualifiers`. Eventually code should be converted to use the new entry point. ]==] function export.format_qualifier(qualifiers, open, close, opencontent, closecontent, no_ib_content) return export.format_qualifiers { qualifiers = qualifiers, open = open, close = close, opencontent = opencontent, closecontent = closecontent, no_ib_content = no_ib_content, } end local function format_qualifiers_with_clarification(qualifiers, clarification, openquote, closequote) local opencontent = export.wrap_css(clarification, "qualifier-clarification") .. export.wrap_css(openquote or "“", "qualifier-clarification qualifier-quote") local closecontent = export.wrap_css(closequote or "”", "qualifier-clarification qualifier-quote") return export.format_qualifiers { qualifiers = qualifiers, open = "(", close = ")", opencontent = opencontent, closecontent = closecontent, } end --[==[ Internal implementation of {{tl|sense}}. ]==] function export.sense(qualifiers) return export.format_qualifiers { qualifiers = qualifiers }.. export.wrap_css(":", "ib-colon sense-qualifier-colon") end --[==[ Internal implementation of {{tl|antsense}}. ]==] function export.antsense(qualifiers) return format_qualifiers_with_clarification(qualifiers, "antonym(s) of ") .. export.wrap_css(":", "ib-colon sense-qualifier-colon") end return export tc3tigewud11x9n9900h1tvmhjpvcah Module:labels/data/lang/en 828 8150 55055 2026-09-30T22:06:45Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local labels = {} ------------------------------------ North America ------------------------------------ labels["North America"] = { aliases = {"North American"}, display = "[[Canada]], [[American English|US]]", regional_categories = {"Canadian", "American"}, } labels["caricature Black"] = { display = "caricatures of Black speech", def = "any of various caricatures of Black speech by non-Black writers", aliases = {"caricature black", "a caricature of Bl...' 55055 Scribunto text/plain local labels = {} ------------------------------------ North America ------------------------------------ labels["North America"] = { aliases = {"North American"}, display = "[[Canada]], [[American English|US]]", regional_categories = {"Canadian", "American"}, } labels["caricature Black"] = { display = "caricatures of Black speech", def = "any of various caricatures of Black speech by non-Black writers", aliases = {"caricature black", "a caricature of Black speech", "caricatures of Black speech"}, plain_categories = "Caricatures of Black English", form_of_display = "a caricature of Black" -- for use in T:pronunciation spelling of. use in T:altform and T:altspell is not intended and looks bad. } ------------- Canada ------------- labels["Canada"] = { aliases = {"CA", "Canadian", "CanE", "Canadian English"}, Wikipedia = "Canadian English", regional_categories = "Canadian", parent = "rawposcat:North American English,Commonwealth", } labels["Acadia"] = { region = "colonial [[Acadia]]", aliases = {"Acadian"}, Wikipedia = true, regional_categories = "Acadian", parent = "Canada", } labels["Alberta"] = { Wikipedia = true, regional_categories = true, parent = "Canadian Prairies", } labels["Atlantic Canada"] = { Wikipedia = "Atlantic Canadian English", regional_categories = "Atlantic Canadian", parent = "Canada", } labels["British Columbia"] = { Wikipedia = true, regional_categories = true, parent = "Canada", } labels["Canadian Prairies"] = { the = true, Wikipedia = true, regional_categories = true, parent = "Canada", } labels["Labrador"] = { Wikipedia = true, regional_categories = true, parent = "Atlantic Canada", } labels["Manitoba"] = { Wikipedia = true, regional_categories = true, parent = "Canadian Prairies", } labels["Multicultural Toronto English"] = { def = "the {{w|multiethnolect|multi-ethnic dialect}} of {{catlink|Canadian English}} used in the {{w|Greater Toronto Area}}, particularly among young non-white working-class speakers", aliases = {"MTE"}, display = "MTE", Wikipedia = true, plain_categories = true, parent = "Ontario", } labels["New Brunswick"] = { Wikipedia = "Atlantic Canadian English", regional_categories = true, parent = "Atlantic Canada", } labels["Newfoundland"] = { Wikipedia = "Newfoundland English", regional_categories = true, parent = "Atlantic Canada", } labels["Northwest Territories"] = { region = "the [[Northwest Territories]] of [[Canada]]", Wikipedia = true, regional_categories = true, parent = "Canada", } labels["Northwestern Ontario"] = { aliases = {"northwestern Ontario", "Northwest Ontario", "northwest Ontario"}, Wikipedia = true, regional_categories = true, parent = "Ontario", } labels["Nova Scotia"] = { Wikipedia = "Atlantic Canadian English", regional_categories = true, parent = "Atlantic Canada", } labels["Nunavut"] = { Wikipedia = true, regional_categories = true, parent = "Canada", } labels["Ontario"] = { Wikipedia = true, regional_categories = true, parent = "Canada", } labels["Prince Edward Island"] = { Wikipedia = true, regional_categories = true, parent = "Atlantic Canada", } labels["Quebec"] = { aliases = {"Québec"}, Wikipedia = "Quebec English", regional_categories = true, parent = "Canada", } labels["Saskatchewan"] = { Wikipedia = true, regional_categories = true, parent = "Canadian Prairies", } labels["Yukon"] = { Wikipedia = true, regional_categories = true, parent = "Canada", } ------------- US ------------- labels["US"] = { region = "the [[United States]]", aliases = {"U.S.", "United States", "United States of America", "USA", "US English", "U.S. English", "America", "American", "American English"}, Wikipedia = "American English", regional_categories = "American", parent = "rawposcat:North American English", } labels["African-American Vernacular"] = { def = "the variety of [[English]] spoken, especially in urban communities, by most working-class and some middle-class [[African-American]]s", addl = "It is a [[sociolect]] with significantly different grammatical characteristics from {{w|Standard English}}, especially in {{w|tense-aspect}} and [[negation]] constructions. Many African-American communities maintain [[diglossia]] between African-American Vernacular English (AAVE) and Standard English.", aliases = {"AAVE", "African American Vernacular", "African American Vernacular English", "African-American Vernacular English", "BVE"}, Wikipedia = "African-American Vernacular English", regional_categories = true, parent = "African-American", } labels["African-American"] = { prep = "by", region = "[[African-American]]s in the [[United States]]", aliases = {"AA", "African-American English", "African American", "African American English", "AAE"}, Wikipedia = "African-American English", regional_categories = true, parent = "US", } labels["Alabama"] = { Wikipedia = true, regional_categories = true, parent = "Southern US", } labels["Alaska"] = { Wikipedia = true, regional_categories = true, parent = "Northwestern US", } labels["Appalachia"] = { aliases = {"Appalachian"}, Wikipedia = "Appalachian English", regional_categories = "Appalachian", parent = "US", } labels["Arizona"] = { Wikipedia = true, regional_categories = true, parent = "Southwestern US", } labels["Arkansas"] = { Wikipedia = true, regional_categories = "Arkansan", parent = "South Midland US", } labels["Baltimore"] = { Wikipedia = "Baltimore accent", regional_categories = true, parent = "Maryland", } labels["Boston"] = { Wikipedia = "Boston accent", regional_categories = true, parent = "Massachusetts", } labels["Cajun"] = { prep = "by", region = "[[Cajun]]s in {{w|Acadiana|Southern Louisiana}}", Wikipedia = "Cajun English", regional_categories = true, parent = "Louisiana", } labels["California"] = { Wikipedia = "California English", regional_categories = true, parent = "Western US", } labels["Chicago"] = { Wikipedia = {"Inland Northern American English", true}, regional_categories = true, parent = "Illinois", } labels["Cincinnati"] = { Wikipedia = "Midland American English#Cincinnati", regional_categories = true, parent = "Ohio,Kentucky", } labels["Colorado"] = { Wikipedia = true, regional_categories = true, parent = "Southwestern US", } labels["Connecticut"] = { Wikipedia = true, regional_categories = true, parent = "New England", } labels["District of Columbia"] = { region = "Washington, D.C.", aliases = {"DC", "Washington, DC"}, Wikipedia = true, regional_categories = "DC", parent = "Mid-Atlantic US", } labels["Eastern New England"] = { Wikipedia = "Eastern New England English", regional_categories = true, parent = "New England", } labels["Florida"] = { Wikipedia = true, regional_categories = true, parent = "Southern US", } labels["Georgia (US)"] = { region = "the state of [[Georgia]] in the [[United States]]", display = "Georgia", Wikipedia = "Georgia (U.S. state)", regional_categories = true, parent = "Southern US", } labels["Hawaii"] = { Wikipedia = true, regional_categories = "Hawaiian", parent = "Western US", } labels["Illinois"] = { Wikipedia = true, regional_categories = true, parent = "Midland US,Northern US", } labels["Indiana"] = { Wikipedia = true, regional_categories = true, parent = "Midland US,Northern US", } labels["Kentucky"] = { Wikipedia = true, regional_categories = true, parent = "South Midland US", } labels["Louisiana"] = { Wikipedia = true, regional_categories = true, parent = "Southern US", } labels["Maine"] = { Wikipedia = "Maine accent", regional_categories = true, parent = "New England", } labels["Maryland"] = { Wikipedia = true, regional_categories = true, parent = "Mid-Atlantic US", } labels["Massachusetts"] = { Wikipedia = true, regional_categories = true, parent = "New England", } labels["Memphis"] = { regional_categories = true, parent = "Southern US", } labels["Michigan"] = { Wikipedia = true, regional_categories = true, parent = "Upper Midwestern US", } labels["Mid-Atlantic US"] = { region = "the [[Mid-Atlantic]] United States", Wikipedia = "Mid-Atlantic American English", regional_categories = true, parent = "US", } labels["Midland US"] = { region = "the American [[Midland]]", Wikipedia = "Midland American English", regional_categories = true, parent = "Midwestern US", } labels["Midwestern US"] = { region = "the [[Midwest]] of the [[United States]]", aliases = {"Midwest US"}, Wikipedia = "Midwestern American English", regional_categories = true, parent = "US", } labels["Mississippi"] = { Wikipedia = true, regional_categories = true, parent = "Southern US", } labels["Missouri"] = { Wikipedia = true, regional_categories = true, parent = "Midland US", } labels["New England"] = { Wikipedia = "New England English", regional_categories = true, parent = "Northeastern US", } labels["New Jersey"] = { Wikipedia = "New Jersey English", regional_categories = true, parent = "Northeastern US", } labels["New Mexico"] = { Wikipedia = "Western American English#New Mexico", regional_categories = true, parent = "Southwestern US", } labels["New Orleans"] = { Wikipedia = "New Orleans English", regional_categories = true, parent = "Louisiana", } labels["New York City"] = { aliases = {"NYC"}, Wikipedia = "New York City English", regional_categories = true, parent = "New York", } labels["New York"] = { region = "the [[United States]] state of [[New York]]", aliases = {"NY"}, Wikipedia = "New York English (disambiguation)", regional_categories = true, parent = "Northeastern US", } labels["North Carolina"] = { Wikipedia = true, regional_categories = true, parent = "Southern US", } -- can be split off if enough entries in it arise; group with Midland US for now labels["North Midland US"] = { aliases = {"Northern Midland US"}, Wikipedia = "Midland American English", regional_categories = "Midland US", } labels["Northeastern US"] = { region = "the {{w|Northeastern United States}}", aliases = {"Northeast US"}, Wikipedia = {"Northern American English#Northeastern American English"}, regional_categories = true, parent = "Northern US", } -- can be split off if enough entries in it arise; group with California for now labels["Northern California"] = { Wikipedia = "California English", regional_categories = "California", } labels["Northwestern US"] = { region = "the {{w|Northwestern United States}}", aliases = {"Northwest US", "Pacific Northwest"}, Wikipedia = {"Pacific Northwest English", "Northwestern United States"}, regional_categories = true, parent = "Western US", } labels["Ohio"] = { Wikipedia = true, regional_categories = true, parent = "Midland US,Northern US", } labels["Oklahoma"] = { Wikipedia = true, regional_categories = true, parent = "South Midland US", } labels["Pennsylvania Dutch English"] = { prep = "by", region = "{{w|Pennsylvania Dutch}} people in south-central [[Pennsylvania]]", Wikipedia = true, plain_categories = true, parent = "Pennsylvania", } labels["Pennsylvania"] = { Wikipedia = true, regional_categories = true, parent = "Northeastern US", } -- can be split off if enough entries in it arise; group with Pennsylvania for now labels["Philadelphia"] = { Wikipedia = "Philadelphia English", regional_categories = true, parent = "Pennsylvania,Mid-Atlantic US", } -- can be split off if enough entries in it arise; group with Western Pennsylvania for now labels["Pittsburgh"] = { Wikipedia = "Western Pennsylvania English", regional_categories = "Western Pennsylvania", parent = "Pennsylvania", } labels["Pittsburghese"] = { Wikipedia = "Western Pennsylvania English", regional_categories = "Western Pennsylvania", parent = "Pennsylvania", } labels["Rhode Island"] = { Wikipedia = true, regional_categories = true, parent = "New England", } labels["South Carolina"] = { Wikipedia = true, regional_categories = true, parent = "Southern US", } -- can be split off if enough entries in it arise; group with Midland US for now labels["South Midland US"] = { aliases = {"Southern Midland US"}, Wikipedia = "Midland American English", regional_categories = "Midland US", } -- can be split off if enough entries in it arise; group with California for now labels["Southern California"] = { Wikipedia = "California English", regional_categories = "California", } labels["Southern US"] = { region = "the {{w|Southern United States}}", aliases = {"Southern American English", "southern US", "US South"}, Wikipedia = "Southern American English", regional_categories = true, parent = "US", } labels["Southwestern US"] = { region = "the {{w|Southwestern United States}}", aliases = {"southwestern US", "Southwest US", "southwest US"}, Wikipedia = {"Western American English", "Southwestern United States"}, regional_categories = true, parent = "Western US", } labels["St. Louis"] = { Wikipedia = "St. Louis dialect", regional_categories = true, parent = "Missouri,Illinois", } labels["St. Vincent"] = { Wikipedia = "Saint Vincent (Saint Vincent and the Grenadines)", regional_categories = "Saint Vincentian", parent = "Caribbean", } labels["Texas"] = { Wikipedia = "Texan English", regional_categories = true, parent = "Southern US,Southwestern US", } labels["Upper Midwestern US"] = { region = "the {{w|Upper Midwest}} of the [[United States]]", aliases = {"Upper Midwest US"}, Wikipedia = {"North-Central American English", "Upper Midwest"}, regional_categories = true, parent = "Midwestern US,Northern US", } labels["Vermont"] = { Wikipedia = {"New England English", "Vermont"}, regional_categories = true, parent = "New England", } labels["Virginia"] = { Wikipedia = true, regional_categories = true, parent = "Southern US", } labels["Western Pennsylvania"] = { aliases = {"Western Pennsylvania English"}, Wikipedia = "Western Pennsylvania English", regional_categories = true, parent = "Pennsylvania", } labels["Western US"] = { region = "the {{w|Western United States}}", aliases = {"western US"}, Wikipedia = {"Western American English", "Western United States"}, regional_categories = true, parent = "US", } labels["Wisconsin"] = { Wikipedia = true, regional_categories = true, parent = "Upper Midwestern US", } ------------------------------------ Australia and New Zealand ------------------------------------ labels["Australian Aboriginal"] = { prep = "by", region = "[[Aboriginal]] people in [[Australia]]", aliases = {"Australian aboriginal", "Australian Aboriginal English", "Australian aboriginal English", "Aboriginal Australian", "aboriginal Australian", "Aboriginal Australian English", "aboriginal Australian English"}, Wikipedia = "Australian Aboriginal English", regional_categories = true, parent = "Australia", } labels["Australia"] = { aliases = {"Australian", "AU", "AuE", "Aus", "AusE"}, Wikipedia = "Australian English", accent_Wikipedia = "Australian English phonology", regional_categories = "Australian", parent = "Oceania,Commonwealth", } labels["Canberra"] = { region = "the [[Australian Capital Territory]] ([[Canberra]])", Wikipedia = {"Variation in Australian English#Regional variation", true}, regional_categories = true, parent = "Australia", } labels["New South Wales"] = { aliases = {"NSW"}, Wikipedia = {"Variation in Australian English#Regional variation", true}, regional_categories = true, parent = "Australia", } labels["New Zealand"] = { aliases = {"NZ", "NZE"}, Wikipedia = "New Zealand English", accent_Wikipedia = "New Zealand English phonology", regional_categories = true, parent = "Oceania,Commonwealth", } labels["Northern Territory"] = { aliases = {"NT"}, Wikipedia = {"Variation in Australian English#Regional variation", true}, regional_categories = true, parent = "Australia", } labels["Northern US"] = { region = "the {{w|Northern United States}}", aliases = {"Northern American English", "northern US", "US North"}, Wikipedia = "Northern American English", regional_categories = true, parent = "US", } labels["Queensland"] = { Wikipedia = {"Variation in Australian English#Regional variation", true}, regional_categories = true, parent = "Australia", } labels["South Australia"] = { Wikipedia = "South Australian English", regional_categories = "South Australian", parent = "Australia", } labels["Tasmania"] = { Wikipedia = {"Variation in Australian English#Regional variation", true}, regional_categories = "Tasmanian", parent = "Australia", } labels["Victoria"] = { Wikipedia = {"Variation in Australian English#Regional variation", "Victoria (state)"}, regional_categories = true, parent = "Australia", } labels["Western Australia"] = { Wikipedia = "Western Australian English", regional_categories = "Western Australian", parent = "Australia", } ------------------------------------ Ireland ------------------------------------ labels["Cork"] = { Wikipedia = {"South-West Irish English", "Cork (city)"}, regional_categories = "Munster", } labels["Dublin"] = { Wikipedia = {"Dublin English", "Dublin", "DE"}, regional_categories = true, parent = "Ireland", } labels["Ireland"] = { aliases = {"Irish", "IE"}, Wikipedia = "Hiberno-English", regional_categories = "Irish", parent = "Europe", } labels["Munster"] = { Wikipedia = {"South-West Irish English", "Munster"}, regional_categories = true, parent = "Ireland", } ------------------------------------ United Kingdom ------------------------------------ labels["UK"] = { addl = "Not to be confused with [[:Category:British English forms|British spellings]], a spelling system used in some English-speaking countries of the world.", aliases = {"United Kingdom", "British", "Britain", "Great Britain"}, Wikipedia = "British English", regional_categories = "British", parent = "Europe,Commonwealth", country = "the United Kingdom", } labels["Antrim"] = { Wikipedia = "County Antrim", regional_categories = "Northern Irish", } labels["Bedfordshire"] = { Wikipedia = "Bedfordshire dialect", regional_categories = true, parent = "Southern England", } labels["Berkshire"] = { Wikipedia = true, regional_categories = true, parent = "Southern England", } labels["Birmingham"] = { Wikipedia = "Brummie dialect", regional_categories = true, parent = "West Midlands", } labels["Bristol"] = { region = "[[Bristol]], [[England]]", aliases = {"Bristolian"}, Wikipedia = "Bristolian dialect", regional_categories = "Bristolian", parent = "West Country", } labels["Caithness"] = { Wikipedia = true, regional_categories = true, parent = "Scotland", } labels["Cambridge University"] = { prep = "at", region = "{{w|Cambridge University}} in [[Cambridge]]", aliases = {"University of Cambridge", "Cantab"}, Wikipedia = "University of Cambridge", regional_categories = true, parent = "East Anglia", othercat = "en:Universities", } labels["Channel Islands"] = { the = true, Wikipedia = "Channel Island English", regional_categories = true, parent = "Europe,Commonwealth", } labels["Cockney"] = { prep = "by", region = "working-class [[Londoner]]s, especially in the [[East End]]", Wikipedia = "Cockney#Speech", regional_categories = true, parent = "London", } labels["Cornwall"] = { aliases = {"Cornish"}, Wikipedia = "Cornish dialect", regional_categories = "Cornish", parent = "West Country", } labels["Cumbria"] = { aliases = {"Cumbrian"}, Wikipedia = "Cumbrian dialect", regional_categories = "Cumbrian", parent = "Northern England", } labels["Derbyshire"] = { region = "[[Derbyshire]], which is geographically in the East Midlands but whose dialect is sometimes classified as West Midlands", Wikipedia = "Derbyshire dialect", regional_categories = true, parent = "East Midlands,West Midlands", } labels["Devon"] = { aliases = {"Devonshire"}, Wikipedia = {"West Country English", true}, regional_categories = "Devonian", parent = "West Country", } labels["Dorset"] = { Wikipedia = "Dorset dialect", regional_categories = true, parent = "West Country", } labels["Dundee"] = { Wikipedia = true, regional_categories = true, parent = "Scotland", } labels["Durham University"] = { prep = "at", region = "{{w|Durham University}} in [[Durham]]", Wikipedia = true, regional_categories = true, parent = "Durham", othercat = "en:Universities", } labels["Durham"] = { Wikipedia = "County Durham", regional_categories = true, parent = "Northumbria", } labels["East Anglia"] = { Wikipedia = "East Anglian English", regional_categories = "East Anglian", parent = "England", } labels["East Midlands"] = { region = "the [[East Midlands]] of [[England]]", Wikipedia = "East Midlands English", regional_categories = true, parent = "Midlands", } labels["England"] = { aliases = {"English"}, Wikipedia = "English language in England", regional_categories = "English", parent = "British", } labels["England and Wales"] = { aliases = {"E&W"}, Wikipedia = true, regional_categories = {"English", "Welsh"}, } labels["Essex"] = { Wikipedia = "Essex dialect", regional_categories = true, parent = "Southern England", } labels["Exmoor"] = { Wikipedia = true, regional_categories = {"Devonian", "Somerset"}, } labels["Geordie"] = { region = "Tyneside", aliases = {"Geordie English", "Tyneside"}, Wikipedia = true, plain_categories = true, parent = "Northumbria", } labels["Gloucestershire"] = { Wikipedia = {"West Country English", true}, regional_categories = true, parent = "West Country", } labels["Guernsey"] = { prep = "on", Wikipedia = "Channel Island English#Guernsey English", regional_categories = true, parent = "Channel Islands", } labels["Hartlepool"] = { Wikipedia = "Smoggie", regional_categories = "Teesside", } labels["Herefordshire"] = { Wikipedia = true, regional_categories = true, parent = "West Country", } labels["Isle of Man"] = { prep = "on", the = true, aliases = {"Manx"}, Wikipedia = "Manx English", regional_categories = "Manx", parent = "British", } labels["Isle of Wight"] = { prep = "on", the = true, Wikipedia = true, regional_categories = true, parent = "Southern England", } labels["Jersey"] = { prep = "on", Wikipedia = "Channel Island English#Jersey English", regional_categories = true, parent = "Channel Islands", } labels["Kent"] = { aliases = {"Kentish"}, Wikipedia = "Kentish dialect", regional_categories = "Kentish", parent = "Southern England", } labels["Lancashire"] = { Wikipedia = "Lancashire dialect", regional_categories = true, parent = "Northern England", } labels["Lewis"] = { prep = "on", region = "the [[Isle of Lewis]]", aliases = {"Isle of Lewis"}, Wikipedia = "Isle of Lewis", regional_categories = true, parent = "Scotland", } labels["Lincolnshire"] = { Wikipedia = "Lincolnshire dialect", regional_categories = true, parent = "East Midlands", } labels["Liverpool"] = { region = "the {{w|Liverpool City Region}}, comprising the [[metropolitan county]] of [[Merseyside]] and the [[Cheshire]] [[unitary authority]] of [[Halton]]", aliases = {"Scouse"}, Wikipedia = "Scouse", regional_categories = "Liverpudlian", parent = "Northern England", } labels["London"] = { Wikipedia = "Estuary English", regional_categories = true, parent = "Southern England", } labels["Manchester"] = { aliases = {"Mancunian"}, Wikipedia = "Manchester dialect", regional_categories = "Mancunian", parent = "Northern England", } labels["Mid-Ulster"] = { region = "central [[Ulster]]", aliases = {"Mid-Ulster English"}, Wikipedia = "Mid-Ulster English", regional_categories = true, parent = "Ulster,Northern Ireland", } labels["Midlands"] = { region = "the [[Midlands]] of [[England]]", aliases = {"English Midlands", "South Midlands"}, Wikipedia = "Midlands English", regional_categories = true, parent = "England", } labels["Multicultural London English"] = { prep = "by", region = "young, working-class people in multicultural parts of [[London]]", aliases = {"MLE"}, display = "MLE", Wikipedia = true, plain_categories = true, parent = "London", } labels["Norfolk"] = { Wikipedia = "Norfolk dialect", regional_categories = true, parent = "East Anglia", } labels["North Wales"] = { Wikipedia = {"Welsh English", true}, regional_categories = true, parent = "Wales", } labels["Northern England"] = { aliases = {"northern England", "North England", "north England"}, Wikipedia = "English language in Northern England", regional_categories = true, parent = "England", } labels["Northern Ireland"] = { aliases = {"Northern Irish", "NI"}, Wikipedia = "Ulster English", regional_categories = "Northern Irish", parent = "Ulster", } labels["Northern Isles"] = { display = "[[w:Orkney|Orkney]], [[w:Shetland|Shetland]]", regional_categories = {"Orkney", "Shetland"}, } labels["Northumberland"] = { Wikipedia = {"Northumberland"}, regional_categories = true, parent = "Northumbria", } labels["Northumbria"] = { aliases = {"Northumbrian", "Northeast England", "North-East England", "North East England"}, Wikipedia = {"Northumbrian dialect", "Northumbria (modern)"}, regional_categories = "Northumbrian", parent = "Northern England", } labels["Nottinghamshire"] = { Wikipedia = "Nottinghamshire dialect", regional_categories = true, parent = "East Midlands", } labels["Orkney"] = { prep = "on", aliases = {"Orcadian"}, Wikipedia = {true, "Highland English"}, regional_categories = true, parent = "Scotland", } labels["Oxbridge"] = { Wikipedia = true, regional_categories = {"Cambridge University", "Oxford University"}, } labels["Oxford City"] = { region = "the city of [[Oxford]] in [[England]]", Wikipedia = true, regional_categories = "Oxford", parent = "Oxfordshire", } labels["Oxford University"] = { prep = "at", aliases = {"University of Oxford", "Oxon"}, Wikipedia = "University of Oxford", regional_categories = true, parent = "Oxford City", othercat = "en:Universities", } labels["Oxfordshire"] = { Wikipedia = true, regional_categories = true, parent = "Southern England", } labels["Pitmatic"] = { Wikipedia = true, regional_categories = true, parent = "Northumbria", } labels["Potteries"] = { region = "Stoke-on-Trent", Wikipedia = "Potteries dialect", regional_categories = true, parent = "West Midlands", } labels["Scotland"] = { aliases = {"Scottish", "Scottish English", "ScE"}, Wikipedia = "Scottish English", regional_categories = "Scottish", parent = "British", } labels["Shetland"] = { region = "the [[Shetland Islands]]", aliases = {"Shetland Islands", "Shetlands"}, Wikipedia = {true, "Highland English"}, regional_categories = true, parent = "Scotland", } labels["Shropshire"] = { Wikipedia = true, regional_categories = true, parent = "West Midlands", } labels["Somerset"] = { Wikipedia = {"West Country English", true}, regional_categories = true, parent = "West Country", } -- eventually maybe break this out into its own category labels["South Midlands"] = { Wikipedia = {"Midlands English", "South Midlands"}, regional_categories = "Midlands", } labels["South Wales"] = { Wikipedia = {"Welsh English", true}, regional_categories = true, parent = "Wales", } labels["Southern England"] = { aliases = {"southern England", "South England", "south England", "Southern English"}, Wikipedia = "English in southern England", regional_categories = true, parent = "England", } labels["Suffolk"] = { Wikipedia = "Suffolk dialect", regional_categories = true, parent = "East Anglia", } labels["Sussex"] = { Wikipedia = "Sussex dialect", regional_categories = true, parent = "Southern England", } labels["Teesside"] = { Wikipedia = "Smoggie", regional_categories = true, parent = "Northumbria", } -- Tyneside: see Geordie labels["Ulster"] = { Wikipedia = "Ulster English", regional_categories = true, parent = "Ireland", } labels["Wales"] = { aliases = {"Welsh"}, Wikipedia = "Welsh English", regional_categories = "Welsh", parent = "British", } labels["Wearside"] = { Wikipedia = {"Mackem", true}, regional_categories = true, parent = "Northumbria", } labels["West Country"] = { the = true, aliases = {"West England", "west England"}, Wikipedia = "West Country English", regional_categories = true, parent = "England", } -- can be split off if enough entries in it arise; group with Cumbria for now labels["West Cumbria"] = { Wikipedia = "Cumbrian dialect", regional_categories = "Cumbrian", } labels["West Midlands"] = { region = "the [[West Midlands]] of [[England]]", Wikipedia = "West Midlands English", regional_categories = true, parent = "Midlands", } labels["Wiltshire"] = { Wikipedia = {"West Country English", true}, regional_categories = true, parent = "West Country", } labels["Yorkshire"] = { Wikipedia = "Yorkshire dialect", regional_categories = true, parent = "Northern England", } -------------------------------- South Asia -------------------------------- labels["South Asia"] = { aliases = {"Indic", "South Asian", "SA"}, Wikipedia = "South Asian English", regional_categories = "South Asian", parent = "Asia", } labels["Afghanistan"] = { Wikipedia = true, regional_categories = "Afghan", parent = "South Asia", } labels["Bangladesh"] = { Wikipedia = "Bangladeshi English", regional_categories = "Bangladeshi", parent = "South Asia,Commonwealth", } labels["Nepal"] = { Wikipedia = "Nepalese English", regional_categories = "Nepali", parent = "South Asia,Commonwealth", } labels["Sri Lanka"] = { aliases = {"Sri Lankan"}, Wikipedia = "Sri Lankan English", regional_categories = "Sri Lankan", parent = "South Asia,Commonwealth", } labels["Pakistan"] = { aliases = {"Pakistani"}, Wikipedia = "Pakistani English", regional_categories = "Pakistani", parent = "South Asia,Commonwealth", } labels["British Pakistani"] = { prep = "by", region = "{{w|British Pakistanis}}, i.e. [[British]] citizens of [[Pakistani]] origin", Wikipedia = "British Pakistanis", regional_categories = true, parent = "South Asia,British", } labels["British India"] = { fulldef = "Anglo-Indian terms or senses in English as used formerly by Britishers in {{w|British India}}", Wikipedia = "Indian English", -- The WP articles are strictly divided by geography, not historical period regional_categories = true, parent = "South Asia,British", othercat = "English terms with historical senses", } labels["India"] = { aliases = {"Indian", "Indian English", "InE"}, Wikipedia = "Indian English", accent_Wikipedia = "Indian English#Phonology", regional_categories = "Indian", parent = "South Asia,Commonwealth", } labels["North India"] = { Wikipedia = true, regional_categories = "North Indian", parent = "India", } labels["South India"] = { aliases = {"South Indian"}, Wikipedia = true, regional_categories = "South Indian", parent = "India", } labels["West Bengal"] = { Wikipedia = true, accent_Wikipedia = "Regional differences and dialects in Indian English#Bengali English", regional_categories = true, parent = "India", } labels["Hinglish"] = { def = "[[Hinglish]], an English-based [[creole]] incorporating many [[Hindi]] words; used informally in [[India]]", Wikipedia = true, plain_categories = true, parent = "North India", } labels["Tamil Nadu"] = { aliases = {"TN", "Tamilnadu"}, accent_Wikipedia = "Regional differences and dialects in Indian English#Southern Indian English", parent="South India" } labels["Kerala"] = { accent_Wikipedia = "Regional differences and dialects in Indian English#Malayali", aliases = { "Malayalam", "Mallu" }, parent = "South India" } ------------------------------------ World ------------------------------------ labels["Africa"] = { aliases = {"African"}, Wikipedia = "African English", regional_categories = "African", parent = true, } labels["Antarctica"] = { Wikipedia = "Antarctic English", regional_categories = "Antarctic", parent = true, } labels["Asia"] = { Wikipedia = "Asian English", regional_categories = "Asian", parent = true, } labels["Bahamas"] = { the = true, Wikipedia = "Bahamian English", regional_categories = "Bahaman", parent = "Caribbean", } labels["Barbados"] = { Wikipedia = "English in Barbados", regional_categories = "Barbadian", parent = "Caribbean", } labels["Belize"] = { Wikipedia = "Belizean English", regional_categories = "Belizean", parent = "Caribbean,Central America", } labels["Benglish"] = { def = "[[Benglish]], an English-based [[creole]] incorporating many [[Bengali]] words; used informally in [[Bangladesh]] and [[West Bengal]]", aliases = {"Banglish"}, Wikipedia = true, plain_categories = true, parent = "Bangladesh,West Bengal", } labels["Bermuda"] = { Wikipedia = "Bermudian English", regional_categories = "Bermudian", parent = "Caribbean,rawposcat:North American English,British", } labels["Botswana"] = { Wikipedia = "Botswana English", regional_categories = "Botswanan", parent = "Africa", } labels["Brunei"] = { Wikipedia = "Brunei English", regional_categories = "Bruneian", parent = "Southeast Asia", } labels["Cameroon"] = { aliases = {"CM", "Cameroonian", "Cameroonian English", "en-CM"}, Wikipedia = "Cameroonian English", regional_categories = "Cameroonian", parent = "Africa", } labels["Caribbean"] = { the = true, aliases = {"West Indies"}, Wikipedia = "Caribbean English", regional_categories = true, parent = true, } labels["Cebu"] = { Wikipedia = true, regional_categories = true, parent = "Philippines", } labels["Central America"] = { Wikipedia = true, regional_categories = "Central American", parent = "rawposcat:North American English", } labels["Ceylon"] = { Wikipedia = true, regional_categories = "Sri Lankan", } labels["China"] = { Wikipedia = true, regional_categories = "Chinese", parent = "East Asia", } labels["Chinese Filipino"] = { verb = "used", prep = "by", region = "Chinese Filipinos", aliases = {"Chinese-Filipino"}, -- Sociolect subset to Philippine English -- may also see "Hokaglish" in Wikipedia, although Hokaglish is the codeswitching form with a Hokkien or Tagalog base, just like Philippine English to "Conyo" (English-based) and "Taglish" (Tagalog-based), whereas this is the English variant itself subset to Philippine English. Wikipedia = "Chinese Filipino#Language", regional_categories = true, parent = "Philippines", } labels["Chinglish"] = { def = "[[English]] that has been influenced by [[Chinese]]", Wikipedia = true, plain_categories = true, parent = "China", } labels["Commonwealth"] = { region = "the [[Commonwealth of Nations]]", Wikipedia = "English in the Commonwealth of Nations", regional_categories = true, parent = true, } labels["Cuba"] = { Wikipedia = true, regional_categories = "Cuban", parent = "Caribbean", } labels["East Africa"] = { Wikipedia = true, regional_categories = "East African", parent = "Africa", } labels["East Asia"] = { Wikipedia = true, regional_categories = "East Asian", parent = "Asia", } labels["Egypt"] = { Wikipedia = true, regional_categories = "Egyptian", parent = "Africa,Middle East", } labels["Europe"] = { aliases = {"European"}, Wikipedia = "English language in Europe", regional_categories = "European", parent = true, } labels["Fiji"] = { Wikipedia = "Fijian English", regional_categories = "Fijian", parent = "Oceania,Commonwealth", } labels["Ghana"] = { Wikipedia = "Ghanaian English", regional_categories = "Ghanaian", parent = "West Africa", } labels["Guyana"] = { Wikipedia = {"Guyanese English", "Guyana"}, regional_categories = "Guyanese", parent = "Caribbean,South America", } labels["Hong Kong"] = { aliases = {"HK"}, Wikipedia = "Hong Kong English", regional_categories = true, parent = "China", } labels["Hungary"] = { Wikipedia = true, regional_categories = "Hungarian", parent = "Europe", } labels["Indonesia"] = { Wikipedia = "Indonesian English", regional_categories = "Indonesian", parent = "Southeast Asia", } labels["Israel"] = { Wikipedia = "Israeli English", regional_categories = "Israeli", parent = "Middle East", } labels["Japan"] = { Wikipedia = true, regional_categories = "Japanese", parent = "East Asia", } labels["Jamaica"] = { aliases = {"Jamaican English", "Jamaican"}, Wikipedia = "Jamaican English", regional_categories = "Jamaican", parent = "Caribbean,Commonwealth", } labels["Kenya"] = { Wikipedia = "Kenyan English", regional_categories = "Kenyan", parent = "East Africa", } labels["Liberia"] = { Wikipedia = "Liberian English", regional_categories = "Liberian", parent = "West Africa", } labels["Libya"] = { Wikipedia = true, regional_categories = "Libyan", parent = "Africa", } labels["Macau"] = { Wikipedia = true, regional_categories = "Macanese", parent = "China", } labels["Mainland China"] = { aliases = {"Mainland", "mainland", "mainland China"}, Wikipedia = true, regional_categories = true, parent = "China", } labels["Malaysia"] = { aliases = {"Malaysian"}, Wikipedia = "Malaysian English", regional_categories = "Malaysian", parent = "Southeast Asia,Commonwealth", } labels["Malta"] = { Wikipedia = "Maltese English", regional_categories = "Maltese", parent = "Europe", } labels["Manglish"] = { def = "[[Manglish]], an English-based [[creole]] incorporating [[Malay]], [[Chinese]], and [[Tamil]] words; used informally in [[Malaysia]]", Wikipedia = true, plain_categories = true, parent = "Malaysia", } labels["Mexico"] = { Wikipedia = {"Mexican English", "Mexico"}, regional_categories = "Mexican", parent = "rawposcat:North American English", } labels["Middle East"] = { the = true, Wikipedia = true, regional_categories = "Middle Eastern", parent = true, } labels["Myanmar"] = { aliases = {"Burma"}, Wikipedia = "Myanmar English", regional_categories = true, parent = "Southeast Asia", } labels["Namibia"] = { Wikipedia = "Namibian English", regional_categories = "Namibian", parent = "Africa", } labels["Natal"] = { Wikipedia = "KwaZulu-Natal", regional_categories = true, parent = "South Africa", } labels["Nigeria"] = { aliases = {"Nigerian"}, Wikipedia = "Nigerian English", regional_categories = "Nigerian", parent = "West Africa", } labels["Oceania"] = { Wikipedia = "Oceanian English", regional_categories = "Oceanian", parent = true, } labels["Palestine"] = { Wikipedia = true, regional_categories = "Palestinian", parent = "Middle East", } labels["Papua New Guinea"] = { Wikipedia = "Papua New Guinean English", regional_categories = "Papua New Guinean", parent = "Oceania,Commonwealth", } labels["Philippines"] = { the = true, aliases = {"Philippine", "Philippine English"}, Wikipedia = "Philippine English", regional_categories = "Philippine", parent = "Southeast Asia", } labels["Baguio"] = { Wikipedia = true, regional_categories = true, parent = "Philippines", } labels["Réunion"] = { Wikipedia = true, regional_categories = true, parent = "Africa", } labels["Rhodesia"] = { region = "the historical state of [[Rhodesia]]", Wikipedia = "Zimbabwean English", regional_categories = "Rhodesian", parent = "Africa", } labels["Rwanda"] = { Wikipedia = true, regional_categories = "Rwandan", parent = "Africa", } labels["Singapore"] = { aliases = {"SG", "Singaporean"}, Wikipedia = "Singapore English", regional_categories = true, parent = "Southeast Asia,Commonwealth", } labels["Singlish"] = { def = "[[Singlish]], an English-based [[creole]] incorporating many words of [[Chinese]], [[Malay]] and [[Indian]] origin; used informally in [[Singapore]]", Wikipedia = true, plain_categories = true, parent = "Singapore", } labels["Solomon Islands"] = { the = true, Wikipedia = "Solomon Islands English", regional_categories = true, parent = "Oceania", } labels["South Africa"] = { aliases = {"South African", "South African English", "ZA"}, Wikipedia = "South African English", accent_Wikipedia = "South African English phonology", accent_display = "General South African", regional_categories = "South African", parent = "Africa,Commonwealth", } labels["South America"] = { aliases = {"South American"}, Wikipedia = "South American English", regional_categories = "South American", parent = true, } labels["South Korea"] = { Wikipedia = "Korean English", regional_categories = "South Korean", parent = "East Asia", } labels["Southeast Asia"] = { aliases = {"Southeast Asian", "South-East Asia", "South-East Asian", "South-east Asia", "South-east Asian", "SEA"}, Wikipedia = "Southeast Asian English", regional_categories = "Southeast Asian", parent = "Asia", } labels["Taiwan"] = { aliases = {"Taiwanese"}, Wikipedia = true, regional_categories = "Taiwanese", parent = "East Asia", } labels["Tanzania"] = { aliases = {"Tanzanian"}, Wikipedia = true, regional_categories = "Tanzanian", parent = "East Africa", } labels["Thailand"] = { Wikipedia = "Tinglish", regional_categories = "Thai", parent = "Southeast Asia", } labels["Trinidad and Tobago"] = { aliases = {"Trinidad", "Tobago", "Trinidadian"}, Wikipedia = "Trinidadian and Tobagonian English", regional_categories = true, parent = "Caribbean", } labels["Uganda"] = { Wikipedia = "Ugandan English", regional_categories = "Ugandan", parent = "Africa", } labels["Vanuatu"] = { Wikipedia = "Vanuatuan English", regional_categories = true, parent = "Oceania", } labels["Vietnam"] = { Wikipedia = "Vietglish", regional_categories = "Vietnamese", parent = "Southeast Asia", } labels["West Africa"] = { aliases = {"West African"}, Wikipedia = true, regional_categories = "West African", parent = "Africa", } labels["Zimbabwe"] = { Wikipedia = "Zimbabwean English", regional_categories = true, parent = "Africa", } ------------------------------------ non-regional ------------------------------------ labels["DoggoLingo"] = { def = "[[DoggoLingo]]", display = "[[DoggoLingo]]", noreg = true, plain_categories = true, othercat = "English internet slang", parent = "internet slang", } labels["Early Modern"] = { prep = "from", region = "the late 15th to the mid-17th centuries", noreg = true, nolink = true, aliases = {"Early Modern English", "EME", "EMnE", "EModE"}, Wikipedia = "Early Modern English", regional_categories = true, parent = true, } labels["Late Modern"] = { prep = "from", region = "the mid-17th to the end of the 19th centuries", noreg = true, nolink = true, aliases = {"Late Modern English", "LME"}, Wikipedia = "Late Modern English", regional_categories = true, parent = true, } -- for French terms used in French contexts labels["Gallicism"] = { noreg = true, aliases = {"French", "Frenchism"}, plain_categories = true, Wikipedia = "Glossary of French words and expressions in English", othercat = "English terms by usage,English terms by orthographic property", } -- ideally this would sit at [[Module:category tree/pragmatic properties]] so it can be used for all languages, but those cats needs to begin with the language name... labels["non-native speakers' English"] = { display = "[[non-native speaker]]s' English", noreg = true, aliases = {"NNES", "NNSE"}, regional_categories = "Non-native speakers'", Wikipedia = "English as a second or foreign language", accent_Wikipedia = "Non-native pronunciations of English", othercat = "English nonstandard terms,English terms by usage,English terms by orthographic property", } labels["Polari"] = { def = "a form of cant slang used in [[Britain]] by some actors, circus and fairground showmen, professional wrestlers, merchant navy sailors, criminals, prostitutes, and the gay subculture", noreg = true, country = "the United Kingdom", Wikipedia = true, plain_categories = true, othercat = "British slang,English cant,English gay slang", parent = true, } -- Thieves' Cant is English-only, for other languages, use "criminal slang" labels["thieves' cant"] = { fulldef = "A secret language formerly used by thieves, beggars and hustlers of various kinds in [[Great Britain]] and to a lesser extent in other English-speaking countries", noreg = true, aliases = {"Thieves' Cant", "Thieves' cant", "thieves cant", "thieves'", "thieves"}, Wikipedia = true, -- FIXME: Currently pos_categories aren't recognized. -- pos_categories = "Thieves' Cant", plain_categories = "English Thieves' Cant", parent = true, othercat = "English cant", } ------------------------------------ English-specific qualifier labels ------------------------------------ labels["attributive"] = { display = "[[Appendix:English nouns#Attributive|attributive]]", } labels["attributively"] = { display = "[[Appendix:English nouns#Attributive|attributively]]", } ------------------------------------ supporting [[Template:standard spelling of]] et al. ------------------------------------ labels["American spelling"] = { aliases = {"American form", "US spelling", "US form", -- As in "color" vs. "colour" "or form", "-or form", "or spelling", "-or spelling", -- As in "meter" vs. "metre" "er form", "-er form", "er spelling", "-er spelling" }, Wikipedia = "American and British English spelling differences", form_of_display = "American", plain_categories = "American English forms", } labels["Australian spelling"] = { aliases = {"Australian form"}, Wikipedia = "Australian English#Spelling and style", form_of_display = "Australian", plain_categories = "Australian English forms", } labels["British spelling"] = { aliases = {"British form", "UK spelling", "UK form"}, Wikipedia = "American and British English spelling differences", form_of_display = "British", plain_categories = "British English forms", } labels["Commonwealth spelling"] = { aliases = {"Commonwealth form", -- As in "color" vs. "colour" "our form", "-our form", "our spelling", "-our spelling", -- As in "meter" vs. "metre" "re form", "-re form", "re spelling", "-re spelling" }, Wikipedia = "American and British English spelling differences", form_of_display = "Commonwealth", plain_categories = {"British English forms", "Canadian English forms", "Australian English forms"}, } labels["Canadian spelling"] = { aliases = {"Canadian form"}, Wikipedia = true, form_of_display = "Canadian", plain_categories = "Canadian English forms", } labels["Oxford British spelling"] = { aliases = {"Oxford", "Oxford form", "Oxford spelling", "en-GB-oxendict",}, display = "[[w:Oxford spelling|Oxford]] [[British English]]", plain_categories = "Oxford spellings", } labels["non-Oxford British spelling"] = { aliases = {"Non-Oxford British spelling", "non-Oxford British form", "Non-Oxford British form", "non-Oxford form", "Non-Oxford form", "non-Oxford", "Non-Oxford", "not Oxford", "Not Oxford"}, display = "non-[[w:Oxford spelling|Oxford]] [[British English]]", plain_categories = "British English forms", } labels["ise spelling"] = { aliases = { "ise", "-ise", "ise form", "-ise form", "-ise spelling", "isation", "-isation", "isation form", "-isation form", "isation spelling", "-isation spelling", "ise-form" -- backwards compatability }, display = "non-[[w:Oxford spelling|Oxford]] [[w:American and British English spelling differences|British spelling]]", form_of_display = "non-[[w:Oxford spelling|Oxford]] [[w:American and British English spelling differences|British]]", plain_categories = "British English forms", } labels["ize spelling"] = { aliases = { "ize", "-ize", "ize form", "-ize form", "-ize spelling", "ization", "-ization", "ization form", "-ization form", "ization spelling", "-ization spelling", "ize-form" -- backwards compatability }, display = "[[w:American and British English spelling differences|American]] and [[w:Oxford spelling|Oxford]] [[British English|British spelling]]", form_of_display = "[[w:American and British English spelling differences|American]] and [[w:Oxford spelling|Oxford]] [[British English|British]]", plain_categories = {"American English forms", "Oxford spellings"}, } ------------------------------------ supporting [[Template:inflection of]] ------------------------------------ -- This is added to an inflection line when something like {{infl of|en|make||th-form}} is used. Specifically, the form-of -- tag 'th-form' is a shortcut for '3-th|s|spres|ind' (see [[Module:form of/lang-data/en]]); the tag '3-th' displays as -- "third-person" (see [[Module:form of/lang-data/en]]) and attaches the following label (see [[Module:form of/cats]]). labels["archaic third singular"] = { display = "archaic", Wikipedia = "English verbs#Archaic forms", pos_categories = "archaic third-person singular forms", } -- This is added to an inflection line when something like {{infl of|en|make||st-form}} is used. See above. labels["archaic second singular present"] = { display = "archaic", Wikipedia = "English verbs#Archaic forms", pos_categories = "second-person singular forms", } -- This is added to an inflection line when something like {{infl of|en|make||st-past-form}} is used. See above. labels["archaic second singular past"] = { display = "archaic", Wikipedia = "English verbs#Archaic forms", pos_categories = "second-person singular past tense forms", } ------------------------------------ accent qualifiers ------------------------------------ -- Generate the inverse of a sound change. In general, we should do this for sound changes that are either -- extremely common (e.g. 'cot-caught') or dominant ('horse-hoarse', 'wine-whine') or are at least locally -- dominant (e.g. 'cheer-chair'). The idea is that when presenting the pronunciation of an area with a locally -- dominant pronunciation, we may want to also present the alternative pronunciation lacking the change, as long -- as it is found at least somewhere in the area. Hence, for New Zealand, which typically has the cheer-chair -- merger, we might might to present the non-merger pronunciation as well; but for a change like card-cord that -- is recessive everywhere, it's unlikely we'll need to specifically highlight the inverse pronunciation (which -- would be the standard, already covered elsewhere), and we can save memory and time by omitting the label. local function generate_non(key, display) if not labels[key] then error(("Internal error: No label definition for key '%s'"):format(key)) end local labval = mw.clone(labels[key]) labels["non-" .. key] = labval if labval.aliases then for i, alias in ipairs(labval.aliases) do labval.aliases[i] = "non-" .. alias end end if not display then display = labval.display or key if display:find("ing$") or display:find("[st]ion$") then -- e.g. "Canadian raising", "t-glottalization" display = "without " .. display else -- e.g. "cot-caught merger" display = "without the " .. display end end labval.display = display end labels["Anglicised"] = { aliases = {"Anglicized"}, Wikipedia = "Anglicisation#Anglicisation of non-English-language vocabulary and names", } labels["bad-lad split"] = { Wikipedia = true, display = "{{m|en||bad}}–{{m|en||lad}} split", } labels["Canadian raising"] = { aliases = {"North American raising"}, Wikipedia = true, } generate_non("Canadian raising") labels["Canadian shift"] = { aliases = {"Canadian Shift", "Canadian vowel shift", "Canadian Vowel Shift"}, Wikipedia = true, display = "Canadian vowel shift", } generate_non("Canadian shift") labels["card-cord"] = { Wikipedia = "Card-cord merger", display = "{{m|en||card}}–{{m|en||cord}} merger", } labels["cheer-chair"] = { aliases = {"near-square"}, Wikipedia = "near-square merger", display = "{{m|en||cheer}}–{{m|en||chair}} merger", } generate_non("cheer-chair") labels["cot-caught"] = { aliases = {"caught-cot"}, Wikipedia = "Cot–caught merger", display = "{{m|en||cot}}–{{m|en||caught}} merger", } generate_non("cot-caught") labels["cure-fir"] = { aliases = {"cure-nurse"}, Wikipedia = "Cure-nurse merger", display = "{{m|en||cure}}–{{m|en||fir}} merger", } generate_non("cure-fir") labels["doll-dole"] = { Wikipedia = "Doll-dole merger", display = "{{m|en||doll}}–{{m|en||dole}} merger", } labels["dough-door"] = { Wikipedia = "Dough-door merger", display = "{{m|en||dough}}–{{m|en||door}} merger", } generate_non("dough-door") labels["Estuary English"] = { Wikipedia = true, } labels["fair-fur"] = { aliases = {"square-nurse"}, Wikipedia = "Square-nurse merger", display = "{{m|en||fair}}–{{m|en||fur}} merger", } generate_non("fair-fur") labels["father-bother"] = { Wikipedia = "Father–bother merger", display = "{{m|en||father}}–{{m|en||bother}} merger", } generate_non("father-bother") labels["fern-fir-fur"] = { aliases = {"nurse merger"}, Wikipedia = "Fern-fir-fur merger", display = "{{m|en||fern}}–{{m|en||fir}}–{{m|en||fur}} merger", } generate_non("fern-fir-fur") labels["fool-fall"] = { Wikipedia = "English-language vowel changes before historic /l/#Fool–fall_merger", display = "{{m|en||fool}}–{{m|en||fall}} merger", } labels["foot-goose"] = { Wikipedia = "Foot-goose merger", display = "{{m|en||foot}}–{{m|en||goose}} merger", } -- Most labels of the form 'foo-bar' are mergers. Since this is rather a split, include the word "split" -- for clarity. labels["foot-strut split"] = { Wikipedia = "Phonological history of English close back vowels#FOOT–STRUT split", display = "{{m|en||foot}}–{{m|en||strut}} split", } generate_non("foot-strut split") labels["full-fool"] = { Wikipedia = "English-language vowel changes before historic /l/#Full–fool_merger", display = "{{m|en||full}}–{{m|en||fool}} merger", } labels["g-dropping"] = { aliases = {"g dropping"}, Wikipedia = "G-dropping", display = "{{m|en||g}}-dropping", } generate_non("g-dropping") labels["General American"] = { aliases = {"GenAm", "GA"}, Wikipedia = "General American English", } labels["glottalized"] = { aliases = {"glottalization", "glottalised", "glottalisation"}, Wikipedia = "Phonological history of English consonant clusters#Glottalization", } labels["goose split"] = { -- Phonemic split in some Southeastern England English variants. Wikipedia = "English-language vowel changes before historic /l/#GOOSE split", display = "{{m|en||goose}} split", } labels["gulf-golf"] = { Wikipedia = "English-language vowel changes before historic /l/#Gulf-golf merger", display = "{{m|en||gulf}}–{{m|en||golf}} merger", } labels["h-dropping"] = { Wikipedia = "H-dropping", display = "{{m|en||h}}-dropping", } generate_non("h-dropping") labels["happy-tensing"] = { aliases = {"happy tensing"}, Wikipedia = "Happy tensing", display = "{{m|en||happy}}-tensing", } generate_non("happy-tensing") labels["horse-hoarse"] = { Wikipedia = "horse–hoarse merger", display = "{{m|en||horse}}–{{m|en||hoarse}} merger", } generate_non("horse-hoarse") labels["hurry-furry"] = { Wikipedia = "hurry-furry merger", display = "{{m|en||hurry}}–{{m|en||furry}} merger", } generate_non("hurry-furry") labels["Inland Northern US"] = { aliases = {"Great Lakes", "Inland Northern", "Inland North", "Inland Northern American", "Inland Northern American English", "Inland Northern English", "Northern Cities Vowel Shift", "US Inland North", "northern cities vowel shift"}, Wikipedia = "Inland Northern American English", display = "Inland Northern American", } labels["intrusive r"] = { aliases = {"intrusive R", "intrusive-r", "intrusive-R", "intrusive -r-", "intrusive -R-", 'intrusive "r"', 'intrusive "R"', 'intrusive "-r-"', 'intrusive "-R-"', "intrusive /r/", "intrusive /ɹ/"}, Wikipedia = "Intrusive R", display = "intrusive R", } generate_non("intrusive r", "without intrusive R") labels["laxing"] = { Wikipedia = "Trisyllabic laxing" } generate_non("laxing") labels["linking r"] = { aliases = {"linking R", "linking-r", "linking-R", "linking -r-", "linking -R-", 'linking "r"', 'linking "R"', 'linking "-r-"', 'linking "-R-"', "linking /r/", "linking /ɹ/"}, Wikipedia = "Linking R", display = "linking R" } labels["linking w"] = { aliases = {"linking W", "linking-w", "linking-W", "linking -w-", "linking -W-", 'linking "w"', 'linking "W"', 'linking "-w-"', 'linking "-W-"', "linking /w/"}, Wiktionary = "Appendix:English_pronunciation#Linking_semivowels", display = "linking W" } labels["linking y"] = { aliases = {"linking Y", "linking-y", "linking-Y", "linking -y-", "linking -Y-", 'linking "y"', 'linking "Y"', 'linking "-y-"', 'linking "-Y-"', "linking /j/"}, Wiktionary = "Appendix:English_pronunciation#Linking_semivowels", display = "linking Y" } labels["l-vocalization"] = { aliases = {"l-vocalisation"}, Wikipedia = "L-vocalization#Modern English", display = "{{m|en||l}}-vocalization", } generate_non("l-vocalization") labels["Latinate"] = { Wikipedia = "Latin#Phonology", } labels["lot-cloth split"] = { Wikipedia = true, display = "{{m|en||lot}}–{{m|en||cloth}} split", } generate_non("lot-cloth split") labels["low-back-merger shift"] = { aliases = {"LBMS", "low back merger shift"}, Wikipedia = "Low-back-merger shift", display = "low-back-merger shift", } labels["Mary-marry-merry"] = { aliases = {"Mmmm"}, Wikipedia = "Mary–marry–merry merger", display = "{{m|en||Mary}}–{{m|en||marry}}–{{m|en||merry}} merger", } generate_non("Mary-marry-merry") table.insert(labels["non-Mary-marry-merry"].aliases, "nMmmm") labels["Mary-marry"] = { Wikipedia = "Mary–marry–merry merger", display = "{{m|en||Mary}}–{{m|en||marry}} merger", parent = "Mary-marry-merry", } generate_non("Mary-marry") labels["Mary-merry"] = { Wikipedia = "Mary–marry–merry merger", display = "{{m|en||Mary}}–{{m|en||merry}} merger", parent = "Mary-marry-merry", } generate_non("Mary-merry") labels["marry-merry"] = { Wikipedia = "Mary–marry–merry merger", display = "{{m|en||marry}}–{{m|en||merry}} merger", parent = "Mary-marry-merry", } generate_non("marry-merry") labels["merry-Murray"] = { aliases = {"Merry-Murray"}, Wikipedia = "Merry–Murray merger", display = "{{m|en||merry}}–{{m|en||Murray}} merger", } labels["mirror-nearer"] = { aliases = {"Sirius-serious"}, Wikipedia = "Mirror-nearer merger", display = "{{m|en||mirror}}–{{m|en||nearer}} merger", } generate_non("mirror-nearer") labels["nt-flapping"] = { Wikipedia = "Flapping#Distribution", display = "{{m|en||nt}}-flapping", } generate_non("nt-flapping") labels["pane-pain"] = { Wikipedia = "Pane–pain merger", display = "{{m|en||pane}}–{{m|en||pain}} merger" } generate_non("pane-pain") labels["paw-poor"] = { Wikipedia = "Rhoticity in English#/ɔː/–/ʊər/ merger", display = "{{m|en||paw}}–{{m|en||poor}} merger", } labels["pin-pen"] = { aliases = {"pen-pin"}, Wikipedia = "pin–pen merger", display = "{{m|en||pin}}–{{m|en||pen}} merger", } labels["pour-poor"] = { aliases = {"poor-pour", "cure-force"}, Wikipedia = "Cure–force merger", display = "{{m|en||pour}}–{{m|en||poor}} merger", } generate_non("pour-poor") labels["r-dissimilation"] = { Wikipedia = "Dissimilation", display = "{{m|en||r}}-dissimilation", } labels["rhotic"] = { Wikipedia = "Rhoticity in English", } labels["non-rhotic"] = { aliases = {"nonrhotic"}, Wikipedia = "Rhoticity in English", } labels["Received Pronunciation"] = { aliases = {"RP"}, Wikipedia = true, } labels["salary-celery"] = { Wikipedia = "Salary–celery merger", display = "{{m|en||salary}}–{{m|en||celery}} merger", } labels["show-sure"] = { Wikipedia = "Show-sure merger", display = "{{m|en||show}}–{{m|en||sure}} merger", } labels["Standard Southern British English"] = { aliases = {"SSB", "SSBE", "Standard Southern British"}, Wikipedia = "Standard Southern British", display = "Standard Southern British", } labels["stressed"] = { Wikipedia = "Stress and vowel reduction in English#Weak and strong forms of function words", display = "stressed form", } labels["tar-tire"] = { Wikipedia = "/aɪər/–/ɑr/ merger", display = "tar-tire merger", } labels["tar-tire-tower"] = { Wikipedia = "English-language vowel changes before historic /r/#/aɪə/–/aʊə/–/ɑː/ merger", display = "tar-tire-tower merger", } labels["t-flapping"] = { Wikipedia = "t-flapping", } generate_non("t-flapping") labels["t-glottalization"] = { aliases = {"t-glottalisation", "t-glottaling"}, Wikipedia = "T-glottalization", display = "{{m|en||t}}-glottalization", } generate_non("t-glottalization") labels["th-fronting"] = { Wikipedia = true, display = "{{m|en||th}}-fronting", } labels["th-stopping"] = { Wikipedia = true, display = "{{m|en||th}}-stopping", } labels['toe-tow'] = { Wikipedia = "Phonological history of English diphthongs#Toe–tow merger", display = "{{m|en||toe}}–{{m|en||tow}} merger" } generate_non("toe-tow") labels["trap-bath split"] = { Wikipedia = "trap–bath split", display = "{{m|en||trap}}–{{m|en||bath}} split", } generate_non("trap-bath split") labels["triphthong smoothing"] = { Wikipedia = "Triphthong smoothing", display = "triphthong smoothing", } generate_non("triphthong smoothing") labels["unstressed"] = { Wikipedia = "Stress and vowel reduction in English#Weak and strong forms of function words", display = "unstressed form", } labels["weak vowel"] = { aliases = {"weak vowel merger"}, Wikipedia = "Weak vowel merger", display = "weak vowel merger", } generate_non("weak vowel", "weak vowel distinction") labels["wine-whine"] = { Wikipedia = "wine–whine merger", display = "{{m|en||wine}}–{{m|en||whine}} merger", } generate_non("wine-whine") labels["yod-coalescence"] = { aliases = {"yod coalescence"}, Wikipedia = "yod-coalescence", } generate_non("yod-coalescence") labels["yod-dropping"] = { aliases = {"yod dropping"}, Wikipedia = "yod-dropping", } generate_non("yod-dropping") labels["ng-coalescence"] = { aliases = {"NG-coalescence", "ng coalescence", "NG coalescence"}, Wikipedia = "Ng coalescence", display = "{{m|en||ng}}-coalescence", } generate_non("ng-coalescence") labels["æ-raising"] = { aliases = {"æ-tensing", "/æ/ raising", "/æ/ tensing", "ae-raising", "ae-tensing"}, Wikipedia = "/æ/ raising", display = "{{IPAchar|/æ/}} raising", } generate_non("æ-raising") return require("Module:labels").finalize_data(labels) 38e2qdlxj2dbc8uiqukjhki5g7s10br Module:labels/data/qualifiers 828 8151 55056 2026-09-30T22:10:19Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local labels = {} -- Qualifiers and similar labels. -- NOTE: This module is loaded both by [[Module:labels]] and by [[Module:accent qualifier]]. -- Helper labels labels["_"] = { display = "", omit_preComma = true, omit_postComma = true, } labels[","] = { -- forced comma omit_preComma = true, omit_postComma = true, omit_preSpace = true, } labels[";"] = { omit_preComma = true, omit_postComma = true, omit_preSpace = true, } labels[":"] = { omit_preC...' 55056 Scribunto text/plain local labels = {} -- Qualifiers and similar labels. -- NOTE: This module is loaded both by [[Module:labels]] and by [[Module:accent qualifier]]. -- Helper labels labels["_"] = { display = "", omit_preComma = true, omit_postComma = true, } labels[","] = { -- forced comma omit_preComma = true, omit_postComma = true, omit_preSpace = true, } labels[";"] = { omit_preComma = true, omit_postComma = true, omit_preSpace = true, } labels[":"] = { omit_preComma = true, omit_postComma = true, omit_preSpace = true, } labels["?"] = { omit_preComma = true, omit_postComma = true, omit_preSpace = true, } labels["(?)"] = { omit_preComma = true, omit_postComma = true, omit_preSpace = true, } labels["-"] = { -- hyphen omit_preComma = true, omit_postComma = true, omit_preSpace = true, omit_postSpace = true, } labels["–"] = { -- en dash omit_preComma = true, omit_postComma = true, omit_preSpace = true, omit_postSpace = true, } labels["—"] = { -- em dash omit_preComma = true, omit_postComma = true, omit_preSpace = true, omit_postSpace = true, } labels["also"] = { omit_postComma = true, } labels["and"] = { aliases = {"&"}, omit_preComma = true, omit_postComma = true, } -- e.g. "informal, but formal in Louisiana" labels["but"] = { omit_postComma = true, } labels["by"] = { omit_preComma = true, omit_postComma = true, } -- e.g. "except in", "except with" etc. labels["except"] = { omit_preComma = true, omit_postComma = true, } labels["or"] = { omit_preComma = true, omit_postComma = true, } labels["outside"] = { aliases = {"except in"}, omit_preComma = true, omit_postComma = true, } labels["with"] = { aliases = {"+"}, omit_preComma = true, omit_postComma = true, } -- Qualifier labels labels["attested in"] = { omit_postComma = true, } labels["chiefly"] = { aliases = {"mainly", "mostly", "primarily", "Chiefly"}, omit_postComma = true, } labels["especially"] = { omit_postComma = true, } labels["excluding"] = { omit_postComma = true, } labels["exclusively"] = { aliases = {"strictly"}, omit_postComma = true, } labels["extremely"] = { omit_postComma = true, } labels["formerly"] = { omit_postComma = true, } labels["frequently"] = { omit_postComma = true, } -- e.g. "highly nonstandard" labels["highly"] = { omit_postComma = true, } labels["in"] = { omit_postComma = true, } labels["in a"] = { omit_postComma = true, } labels["in an"] = { omit_postComma = true, } labels["in the"] = { omit_postComma = true, } labels["including"] = { omit_postComma = true, } -- e.g. "less common" labels["less"] = { omit_postComma = true, } -- e.g. "many dialects" labels["many"] = { omit_postComma = true, } labels["markedly"] = { omit_postComma = true, } labels["mildly"] = { omit_postComma = true, } -- e.g. "more common" labels["more"] = { omit_postComma = true, } labels["now"] = { aliases = {"nowadays"}, omit_postComma = true, } labels["occasionally"] = { omit_postComma = true, } labels["of"] = { omit_postComma = true, } labels["of a"] = { omit_postComma = true, } labels["of an"] = { omit_postComma = true, } labels["of the"] = { omit_postComma = true, } labels["often"] = { aliases = {"commonly"}, omit_postComma = true, } labels["originally"] = { omit_postComma = true, } -- e.g. "law, otherwise archaic" labels["otherwise"] = { omit_postComma = true, } labels["particularly"] = { omit_postComma = true, } labels["possibly"] = { -- aliases = {"perhaps"}, omit_postComma = true, } labels["predominantly"] = { omit_postComma = true, } labels["rarely"] = { omit_postComma = true, } labels["rather"] = { omit_postComma = true, } labels["relatively"] = { omit_postComma = true, } labels["slightly"] = { omit_postComma = true, } labels["sometimes"] = { omit_postComma = true, } labels["somewhat"] = { omit_postComma = true, } labels["strongly"] = { omit_postComma = true, } labels["the"] = { omit_postComma = true, } -- e.g. "then colloquial, now dated" labels["then"] = { omit_postComma = true, } labels["typically"] = { omit_postComma = true, } labels["usually"] = { omit_postComma = true, } labels["very"] = { omit_postComma = true, } labels["with a"] = { omit_postComma = true, } labels["with an"] = { omit_postComma = true, } labels["with the"] = { omit_postComma = true, } labels["with respect to"] = { aliases = {"wrt"}, omit_postComma = true, } return require("Module:labels").finalize_data(labels) ow7lz65j1yqeur8mu3w2vng38trdiut Module:labels/data/regional 828 8152 55057 2026-09-30T22:11:34Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local labels = {} ------------------------------------------ Generic ------------------------------------------ --not sure where to put this labels["Classical"] = { aliases = {"classical"}, -- "ar", ca", "fa", "id", la", "zh" handled in lang-specific module langs = {"az", "ja", "jv", "kum", "ms", "quc", "sa", "tl"}, special_display = "[[Classical <canonical_name>]]", regional_categories = true, } labels["Epigraphic"] = { langs = {"grc", "pgd", "pra", "sa...' 55057 Scribunto text/plain local labels = {} ------------------------------------------ Generic ------------------------------------------ --not sure where to put this labels["Classical"] = { aliases = {"classical"}, -- "ar", ca", "fa", "id", la", "zh" handled in lang-specific module langs = {"az", "ja", "jv", "kum", "ms", "quc", "sa", "tl"}, special_display = "[[Classical <canonical_name>]]", regional_categories = true, } labels["Epigraphic"] = { langs = {"grc", "pgd", "pra", "sa"}, special_display = "[[w:Epigraphy|Epigraphic <canonical_name>]]", regional_categories = true, } labels["regional"] = { aliases = {"regionally"}, display = "[[regional#English|regional]]", regional_categories = true, } ------------------------------------------ Places ------------------------------------------ labels["Anatri"] = { aliases = {"Lower Chuvash"}, langs = {"cv"}, -- e.g. вот "fire" vs the Upper Chuvash / literary standard вут Wikipedia = true, regional_categories = true, } labels["Australia"] = { aliases = {"AU", "Australian"}, -- "de", "en", "mt", "zh" handled in lang-specific modules langs = {"el", "it", "ko", "ru"}, Wikipedia = true, regional_categories = "Australian", } labels["Black Isle"] = { langs = {"sco"}, -- conceivably also en, gd, perhaps enm, but -sche could only find sco Wikipedia = true, regional_categories = true, } -- WARNING: No existing languages or categories associated with label; add to `langs` as needed labels["Bogor"] = { langs = {}, Wikipedia = true, regional_categories = true, } labels["Brazil"] = { aliases = {"Brazilian"}, -- "pt" handled in lang-specific module langs = {"ja", "mch", "vec", "yi"}, Wikipedia = true, regional_categories = "Brazilian", } -- WARNING: No existing languages or categories associated with label; add to `langs` as needed labels["Brebes"] = { aliases = {"Brebian"}, langs = {}, Wikipedia = "Brebes Regency", regional_categories = true, } labels["Bukovina"] = { aliases = {"Bucovina", "Bukovinian", "Bukowina"}, langs = {"pl", "ro"}, Wikipedia = true, regional_categories = "Bukovinian", } -- WARNING: No existing languages or categories associated with label; add to `langs` as needed labels["Burundi"] = { aliases = {"Burundian"}, langs = {}, Wikipedia = true, regional_categories = "Burundian", } labels["Canada"] = { aliases = {"Canadian"}, -- "en", "fr", "zh" handled in lang-specific module langs = {"gd", "haa", "is", "ko", "ru", "tli", "vi"}, Wikipedia = true, regional_categories = "Canadian", } labels["China"] = { -- "en", "ko" handled in lang-specific module langs = {"ja", "khb", "kk", "mhx", "mn", "ug"}, Wikipedia = true, regional_categories = "Chinese", } labels["Cisalpine"] = { aliases = {"xcg"}, langs = {"cel-gau"}, Wikipedia = "Cisalpine Gaulish", regional_categories = "Cisalpine", } labels["Congo"] = { aliases = {"Democratic Republic of the Congo", "Democratic Republic of Congo", "DR Congo", "Congo-Kinshasa", "Republic of the Congo", "Republic of Congo", "Congo-Brazzaville", "Congolese"}, -- these could be split if need be -- "fr" handled in lang-specific module langs = {"avu", "yom"}, Wikipedia = true, regional_categories = "Congolese", } labels["Cyprus"] = { aliases = {"cypriot", "Cypriot"}, -- "ar", tr" handled in lang-specific module langs = {"el"}, Wikipedia = true, regional_categories = "Cypriot", } labels["Dobruja"] = { aliases = {"Dobrogea", "Dobrujan"}, langs = {"crh", "ro"}, Wikipedia = true, regional_categories = "Dobrujan", } -- WARNING: No existing languages or categories associated with label; add to `langs` as needed labels["Durban"] = { langs = {}, Wikipedia = true, regional_categories = true, } labels["Europe"] = { -- "en", "es", "fr", "pt" handled in lang-specific module langs = {"ur"}, Wikipedia = true, regional_categories = "European", } labels["France"] = { aliases = {"French"}, -- "fr", "zh" handled in lang-specific module langs = {"la", "lad", "nrf", "vi", "yi"}, Wikipedia = true, regional_categories = "French", } labels["India"] = { aliases = {"Indian"}, -- "en", "pa", "pt" handled in lang-specific module langs = {"bn", "dv", "fa", "ml", "ta", "ur"}, Wikipedia = true, regional_categories = "Indian", } labels["Indonesia"] = { aliases = {"Indonesian"}, -- "en", "zh" handled in lang-specific module langs = {"id", "jv", "ms", "nl"}, Wikipedia = true, regional_categories = "Indonesian", } labels["Israel"] = { aliases = {"Israeli"}, -- "ar", en" handled in lang-specific module langs = {"ajp", "he", "ru", "yi"}, Wikipedia = true, regional_categories = "Israeli", } -- WARNING: No existing languages or categories associated with label; add to `langs` as needed labels["Kalix"] = { langs = {}, Wikipedia = true, regional_categories = true, } -- FIXME: Move to Uyghur label data module labels["Kazakhstan"] = { aliases = {"Kazakhstani", "Kazakh"}, langs = {"ug"}, Wikipedia = true, regional_categories = "Kazakhstani", } -- WARNING: No existing languages or categories associated with label; add to `langs` as needed labels["Kemaliye"] = { langs = {}, Wikipedia = true, regional_categories = true, } -- WARNING: No existing languages or categories associated with label; add to `langs` as needed labels["Kitti"] = { langs = {}, Wikipedia = "Kitti, Federated States of Micronesia", regional_categories = true, } -- WARNING: No existing languages or categories associated with label; add to `langs` as needed labels["Kukkuzi"] = { langs = {}, Wikipedia = true, regional_categories = true, } -- WARNING: No existing languages or categories associated with label; add to `langs` as needed labels["Lucknow"] = { langs = {}, Wikipedia = true, regional_categories = true, } -- WARNING: No existing languages or categories associated with label; add to `langs` as needed labels["Luleå"] = { aliases = {"Lulea"}, langs = {}, Wikipedia = true, regional_categories = true, } labels["Lviv"] = { aliases = {"Lvov", "Lwow", "Lwów"}, langs = {"pl"}, Wikipedia = true, regional_categories = true, } -- WARNING: No existing languages or categories associated with label; add to `langs` as needed labels["Muş"] = { aliases = {"Mush"}, langs = {}, Wikipedia = true, regional_categories = true, } labels["Myanmar"] = { aliases = {"Myanmarese", "Burma", "Burmese"}, -- "en", "my", "zh" handled in lang-specific module; FIXME: move ksw and mnw to lang-specific modules langs = {"ksw", "mnw"}, Wikipedia = true, regional_categories = true, } labels["Nigeria"] = { aliases = {"Nigerian"}, -- "ar", en" handled in lang-specific module langs = {"ff", "guw", "ha", "yo"}, Wikipedia = true, regional_categories = "Nigerian", } labels["Palestine"] = { aliases = {"Palestinian"}, -- "ar", "en" handled in lang-specific module langs = {"ajp", "arc"}, Wikipedia = true, regional_categories = "Palestinian", } -- WARNING: No existing languages or categories associated with label; add to `langs` as needed labels["Priangan"] = { langs = {}, Wikipedia = "Parahyangan", regional_categories = true, } -- WARNING: No existing languages or categories associated with label; add to `langs` as needed labels["Rome"] = { aliases = {"Roma", "Romano"}, langs = {}, Wikipedia = true, regional_categories = "Roman", } labels["Scania"] = { aliases = {"Scanian", "Skanian", "Skåne"}, langs = {"gmq-oda", "sv"}, Wikipedia = true, regional_categories = "Scanian", } -- Silesia German, Silesia Polish; for differentiation between sli "Silesian East Central German" -- don't add Silesian as alias labels["Silesia"] = { langs = {"de", "pl"}, Wikipedia = true, } labels["South Africa"] = { aliases = {"South African"}, -- "de", "en", "pt" handled in lang-specific module langs = {"af", "nl", "st", "te", "yi", "zu"}, Wikipedia = true, regional_categories = "South African", } labels["Spain"] = { aliases = {"Spanish", "ES"}, -- "ca", "es" handled in lang-specific module langs = {"la"}, Wikipedia = true, regional_categories = "Spanish", } -- WARNING: No existing languages or categories associated with label; add to `langs` as needed labels["Surati"] = { langs = {}, Wikipedia = "Surat district", regional_categories = true, } -- WARNING: No existing languages or categories associated with label; add to `langs` as needed labels["Surgut"] = { langs = {}, Wikipedia = true, regional_categories = true, } labels["Suriname"] = { aliases = {"Surinamese"}, langs = {"car", "hns", "jv", "nl"}, Wikipedia = true, regional_categories = "Surinamese", } labels["Thailand"] = { aliases = {"Thai"}, -- "en", "zh" handled in lang-specific module langs = {"khb", "mnw", "th"}, Wikipedia = true, regional_categories = "Thai", } labels["Transalpine"] = { aliases = {"xtg"}, langs = {"cel-gau"}, Wikipedia = "Transalpine Gaulish", regional_categories = "Transalpine", } labels["UK"] = { aliases = {"United Kingdom", "Britain", "Brit", "British", "Great Britain"}, -- "en", "zh" handled in lang-specific module langs = {"bn", "ur", "vi"}, Wikipedia = "United Kingdom", regional_categories = "British", } -- WARNING: No existing languages or categories associated with label; add to `langs` as needed labels["Old Ukrainian"] = { langs = {}, Wikipedia = true, plain_categories = true, } labels["US"] = { aliases = {"U.S.", "United States", "United States of America", "USA", "America", "American"}, -- America/American: should these be aliases of 'North America'? -- DO NOT include "es" here, otherwise {{lb|es|American}} will categorize in [[:Category:American Spanish]]; see [[:Category:United States Spanish]]. -- "de", "en", "pt", "zh" handled in lang-specific module langs = {"hi", "is", "it", "ja", "ko", "nl", "ru", "tli", "ur", "vi", "yi"}, Wikipedia = "United States", regional_categories = "American", } -- WARNING: No existing languages or categories associated with label; add to `langs` as needed labels["Vilhelmina"] = { langs = {}, Wikipedia = true, regional_categories = true, } labels["Viryal"] = { aliases = {"Upper Chuvash"}, langs = {"cv"}, Wikipedia = true, regional_categories = true, } -- WARNING: No existing languages or categories associated with label; add to `langs` as needed labels["Wallonia"] = { aliases = {"Wallonian"}, langs = {}, Wikipedia = true, regional_categories = "Wallonian", } -- WARNING: No existing languages or categories associated with label; add to `langs` as needed labels["Special Region of Yogyakarta"] = { aliases = {"SR Yogyakarta"}, langs = {}, Wikipedia = true, regional_categories = true, } -- WARNING: No existing languages or categories associated with label; add to `langs` as needed labels["Zakarpattia"] = { langs = {}, Wikipedia = "Zakarpattia Oblast", regional_categories = true, } -- WARNING: No existing languages or categories associated with label; add to `langs` as needed labels["Zululand"] = { langs = {}, Wikipedia = true, regional_categories = true, } ------------------------------------------ Chinese romanizations ------------------------------------------ labels["Hanyu Pinyin"] = { aliases = {"Hanyu pinyin", "Pinyin", "pinyin"}, Wikidata = "Q42222", plain_categories = true, } labels["Postal Romanization"] = { aliases = {"Postal romanization", "postal romanization"}, Wikidata = "Q151868", plain_categories = true, } labels["Tongyong Pinyin"] = { aliases = {"Tongyong pinyin"}, Wikidata = "Q700739", plain_categories = true, } labels["Wade–Giles"] = { aliases = {"Wade-Giles"}, Wikidata = "Q208442", plain_categories = true, } return require("Module:labels").finalize_data(labels) 473c6skcpxp1apfry68vrgkn72k2wlr Module:labels/data/topical 828 8153 55058 2026-09-30T22:12:02Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local labels = {} -- To sort these, you first have to convert each label section into a single line, and then sort the lines, and undo -- the single-line conversion. This can be done using Vim commands, something like this: -- 1. Mark the first line to be changed using `ma`. -- 2. Go to the last line and use `'a,.s/\n/\\n/g` to convert newlines to \n sequences. -- 3. Use `'a,.s/\\n\\n/\r/g` to convert sequences of two \n's (marking section divisions) back to new...' 55058 Scribunto text/plain local labels = {} -- To sort these, you first have to convert each label section into a single line, and then sort the lines, and undo -- the single-line conversion. This can be done using Vim commands, something like this: -- 1. Mark the first line to be changed using `ma`. -- 2. Go to the last line and use `'a,.s/\n/\\n/g` to convert newlines to \n sequences. -- 3. Use `'a,.s/\\n\\n/\r/g` to convert sequences of two \n's (marking section divisions) back to newlines. -- 4. Go to the last line again and use `'a,.!sort -f -d` to sort. The `-f` makes it case-insensitive and the `-d` -- selects "dictionary order", which is needed to get 'yoga' to sort before 'yoga pose' instead of the other way -- around. -- 5. Go to the last line again and use `'a,.s/\\n/\r/g` to convert \n sequences back to newlines. -- 6. Go to the last line again and use `'a,.s/^labels/\rlabels/` to put an extra newline before each section. labels["3D printing"] = { aliases = {"3D printer", "3D printers"}, Wiktionary = "3D printing#Noun", Wikipedia = true, Wikidata = "Q229367", topical_categories = true, } labels["ABDL"] = { aliases = {"AB/DL"}, Wiktionary = true, Wikipedia = true, topical_categories = true, } labels["Abrahamism"] = { Wiktionary = "Abrahamism#Noun", topical_categories = true, } labels["accounting"] = { Wiktionary = "accounting#Noun", topical_categories = true, } labels["acoustics"] = { Wiktionary = true, topical_categories = true, } labels["acting"] = { Wiktionary = "acting#Noun", topical_categories = true, } labels["advertising"] = { Wiktionary = "advertising#Noun", topical_categories = true, } labels["aeronautics"] = { Wiktionary = true, topical_categories = true, } labels["aesthetic"] = { aliases = {"aesthetics"}, Wiktionary = true, topical_categories = "Aesthetics", } labels["age regression"] = { aliases = {"agere", "agereg"}, Wiktionary = true, topical_categories = "Age regression" } labels["ageplay"] = { aliases = {"age play"}, Wiktionary = true, topical_categories = true, } labels["agriculture"] = { aliases = {"farming"}, Wiktionary = true, topical_categories = true, } labels["Ahmadiyya"] = { aliases = {"Ahmadiyyat", "Ahmadi"}, Wiktionary = true, topical_categories = true, } labels["aircraft"] = { Wiktionary = true, topical_categories = true, } labels["aircraft components"] = { aliases = {"aircraft component"}, display = "[[aircraft]]", topical_categories = true, } labels["alchemy"] = { Wiktionary = true, topical_categories = true, } labels["alcoholic beverages"] = { aliases = {"alcohol"}, display = "[[alcoholic#Adjective|alcoholic]] [[beverage]]s", topical_categories = true, } labels["alcoholism"] = { Wiktionary = true, topical_categories = true, } labels["algebra"] = { Wiktionary = true, topical_categories = true, } labels["algebraic geometry"] = { Wiktionary = true, topical_categories = true, } labels["algebraic topology"] = { Wiktionary = true, topical_categories = true, } labels["algorithms"] = { aliases = {"algorithm"}, display = "[[computer science]]", topical_categories = true, } labels["alternative history"] = { aliases = {"alt hist", "alternate history"}, Wikidata = "Q224989", topical_categories = true, } labels["alternative medicine"] = { Wiktionary = true, topical_categories = true, } labels["alt-right"] = { aliases = {"altright", "Alt-right", "Altright"}, Wiktionary = true, topical_categories = true, } labels["amateur radio"] = { aliases = {"ham radio"}, Wiktionary = true, topical_categories = true, } labels["American football"] = { Wiktionary = true, topical_categories = "Football (American)", } labels["amino acid"] = { display = "[[biochemistry]]", topical_categories = "Amino acids", } labels["analytic geometry"] = { Wiktionary = true, topical_categories = "Geometry", } labels["analytical chemistry"] = { display = "[[analytical]] [[chemistry]]", topical_categories = true, } labels["anarchism"] = { Wiktionary = true, topical_categories = true, } labels["anatomy"] = { Wiktionary = true, topical_categories = true, } labels["Ancient Greece"] = { aliases = {"ancient Greece"}, Wiktionary = true, topical_categories = true, } labels["Ancient Rome"] = { aliases = {"ancient Rome"}, Wiktionary = true, topical_categories = true, } labels["Anglicanism"] = { aliases = {"Anglican", "Anglicanist", "Anglican Church"}, Wiktionary = true, topical_categories = true, } labels["animation"] = { Wiktionary = true, topical_categories = true, } labels["anime"] = { Wiktionary = true, topical_categories = "Japanese fiction", } labels["anthropology"] = { Wiktionary = true, topical_categories = true, } labels["Arabian god"] = { display = "[[Arabian]] [[mythology]]", topical_categories = "Arabian deities", } labels["arachnology"] = { Wiktionary = true, topical_categories = true, } labels["archaeological culture"] = { aliases = {"archeological culture", "archaeological cultures", "archeological cultures"}, display = "[[archaeology]]", topical_categories = "Archaeological cultures", } labels["archaeology"] = { aliases = {"archeology"}, Wiktionary = true, topical_categories = true, } labels["archery"] = { Wiktionary = true, topical_categories = true, } labels["architectural element"] = { aliases = {"architectural elements"}, display = "[[architecture]]", topical_categories = "Architectural elements", } labels["architecture"] = { Wiktionary = true, topical_categories = true, } labels["Argentine politics"] = { aliases = {"Argentina politics", "Argentinian politics"}, Wikipedia = "Politics of Argentina", topical_categories = true, } labels["arithmetic"] = { Wiktionary = true, topical_categories = true, } labels["Armenian mythology"] = { display = "[[Armenian]] [[mythology]]", topical_categories = true, } labels["art"] = { aliases = {"arts"}, Wiktionary = "art#Noun", topical_categories = true, } labels["Arthurian legend"] = { aliases = {"Arthurian mythology"}, Wikipedia = "Matter_of_Britain#Arthurian_legend", topical_categories = "Arthurian mythology", } labels["artificial intelligence"] = { aliases = {"AI"}, Wiktionary = true, topical_categories = true, } labels["artillery"] = { display = "[[weaponry]]", topical_categories = true, } labels["artistic work"] = { display = "[[art#Noun|art]]", topical_categories = "Artistic works", } labels["asterism"] = { display = "[[uranography]]", topical_categories = "Asterisms", } labels["asteroid"] = { display = "[[astronomy]]", topical_categories = {"Asteroids", "Astronomy"} } labels["astrology"] = { aliases = {"horoscope", "zodiac"}, Wiktionary = true, topical_categories = true, } labels["astronomy"] = { Wiktionary = true, topical_categories = true, } labels["astrophysics"] = { Wiktionary = true, topical_categories = true, } labels["Asturian mythology"] = { display = "[[Asturian]] [[mythology]]", topical_categories = true, } labels["athletics"] = { Wiktionary = true, topical_categories = true, } labels["Australian Aboriginal mythology"] = { Wikipedia = true, topical_categories = true, } labels["Australian politics"] = { Wikipedia = "Politics of Australia", topical_categories = true, } labels["Australian rules football"] = { aliases = {"Australian Rules football"}, Wiktionary = true, topical_categories = true, } labels["autism"] = { Wiktionary = true, Wikipedia = true, topical_categories = true, } labels["automotive"] = { aliases = {"automotives"}, Wiktionary = true, topical_categories = true, } labels["automotive parts"] = { aliases = {"automotive part", "auto part", "auto parts"}, display = "[[automotive]]", topical_categories = true, } labels["aviation"] = { aliases = {"air transport"}, Wiktionary = true, topical_categories = true, } labels["backgammon"] = { Wiktionary = true, topical_categories = true, } labels["bacteria"] = { display = "[[bacteriology]]", topical_categories = true, } labels["bacteriology"] = { Wiktionary = true, topical_categories = true, } labels["badminton"] = { Wiktionary = true, topical_categories = true, } labels["Baháʼí Faith"] = { aliases = {"Baháʼí", "Bahaʼi", "Bahá'í", "Baha'i", "Bahai", "Bahaʼi Faith", "Bahá'í Faith", "Baha'i Faith", "Bahai Faith"}, Wiktionary = true, topical_categories = true, } labels["baking"] = { Wiktionary = "baking#Noun", topical_categories = true, } labels["ball games"] = { aliases = {"ball sports"}, display = "[[ball game]]s", topical_categories = true, } labels["ballet"] = { Wiktionary = true, topical_categories = true, } labels["ballistics"] = { Wiktionary = true, topical_categories = true, } labels["Bangladeshi politics"] = { Wikipedia = "Politics of Bangladesh", topical_categories = true, } labels["banking"] = { Wiktionary = "banking#Noun", topical_categories = true, } labels["baseball"] = { Wiktionary = true, topical_categories = true, } labels["basketball"] = { Wiktionary = true, topical_categories = true, } labels["BDSM"] = { Wiktionary = true, topical_categories = true, } labels["beekeeping"] = { aliases = {"melittology", "apiology", "apidology"}, -- could potentially be split out Wiktionary = true, topical_categories = true, } labels["beer"] = { Wiktionary = true, topical_categories = true, } labels["betting"] = { aliases = {"bet", "bets"}, display = "[[gambling#Noun|gambling]]", topical_categories = true, } labels["biblical"] = { aliases = {"Bible", "bible", "Biblical"}, Wiktionary = "Bible", topical_categories = "Bible", } labels["biblical character"] = { aliases = {"Biblical character", "biblical figure", "Biblical figure"}, display = "[[Bible|biblical]]", topical_categories = "Biblical characters", } labels["bibliography"] = { Wiktionary = true, topical_categories = true, } labels["bicycle parts"] = { aliases = {"bicycle part"}, display = "[[w:List of bicycle parts|cycling]]", topical_categories = true, } labels["billiards"] = { aliases = {"cue sports"}, Wiktionary = true, topical_categories = true, } labels["bingo"] = { Wiktionary = true, topical_categories = true, } labels["biochemistry"] = { Wiktionary = true, topical_categories = true, } labels["biology"] = { aliases = {"biological"}, Wiktionary = true, topical_categories = true, } labels["biotechnology"] = { aliases = {"biotechnological"}, Wiktionary = true, topical_categories = true, } labels["birdwatching"] = { aliases = {"birding"}, Wiktionary = "birdwatching#Noun", topical_categories = true, } labels["blacksmithing"] = { aliases = {"blacksmith"}, Wiktionary = true, topical_categories = true, } labels["blogging"] = { aliases = {"blog"}, Wiktionary = "blogging#Noun", topical_categories = "Internet", } labels["board games"] = { aliases = {"board game"}, display = "[[board game]]s", topical_categories = true, } labels["board sports"] = { Wiktionary = "boardsport", topical_categories = true, } labels["bodybuilding"] = { Wiktionary = "bodybuilding#Noun", topical_categories = true, } labels["book of the Bible"] = { aliases = {"book of the bible", "books of the Bible", "books of the bible", "Biblical book", "biblical book"}, display = "[[Bible|biblical]]", topical_categories = "Books of the Bible", } labels["bookbinding"] = { Wiktionary = true, topical_categories = true, } labels["botany"] = { Wiktionary = true, topical_categories = true, } labels["bowling"] = { Wiktionary = "bowling#Noun", topical_categories = true, } labels["bowls"] = { aliases = {"lawn bowls", "crown green bowls"}, Wiktionary = true, topical_categories = "Bowls (game)", } labels["boxing"] = { Wiktionary = "boxing#Noun", topical_categories = true, } labels["brass instruments"] = { aliases = {"brass instrument"}, display = "[[music]]", topical_categories = true, } labels["Brazilian politics"] = { Wikipedia = "Politics of Brazil", topical_categories = true, } labels["brewing"] = { Wiktionary = "brewing#Noun", topical_categories = true, } labels["bridge"] = { Wiktionary = "bridge#English:_game", topical_categories = true, } labels["broadcasting"] = { Wiktionary = "broadcasting#Noun", topical_categories = true, } labels["bryology"] = { Wiktionary = true, topical_categories = true, } labels["Buddhism"] = { Wiktionary = true, topical_categories = true, } labels["Buddhist deity"] = { aliases = {"Buddhist god", "Buddhist goddess"}, display = "[[Buddhism]]", topical_categories = "Buddhist deities", } labels["Bulgarian politics"] = { Wikipedia = "Politics of Bulgaria", topical_categories = true, } labels["bullfighting"] = { aliases = {"bullfight"}, Wiktionary = true, topical_categories = true, } labels["business"] = { aliases = {"professional"}, Wiktionary = true, topical_categories = true, } labels["Byzantine Empire"] = { aliases = {"Byzantine"}, Wiktionary = true, topical_categories = true, } labels["calculus"] = { Wiktionary = true, topical_categories = true, } labels["calligraphy"] = { Wiktionary = true, topical_categories = true, } labels["Calvinism"] = { aliases = {"Calvinist", "Reformed Christianity", "Calvinist Church", "Reformed Church"}, Wikipedia = true, topical_categories = true, } labels["Canadian football"] = { Wiktionary = true, topical_categories = true, } labels["Canadian politics"] = { Wikipedia = "Politics of Canada", topical_categories = true, } labels["Candomblé"] = { aliases = {"candomblé"}, Wiktionary = true, topical_categories = true, } labels["canid"] = { display = "[[zoology]]", topical_categories = "Canids", } labels["canoeing"] = { aliases = {"canoe"}, Wiktionary = "canoeing#Noun", topical_categories = "Water sports", } labels["capitalism"] = { aliases = {"capitalist"}, Wiktionary = true, topical_categories = true, } labels["carbohydrate"] = { aliases = {"carbohydrates"}, display = "[[biochemistry]]", topical_categories = "Carbohydrates", } labels["carboxylic acid"] = { aliases = {"carboxylic acids"}, display = "[[organic chemistry]]", topical_categories = "Carboxylic acids", } labels["card games"] = { aliases = {"cards", "card game", "playing card"}, display = "[[card game]]s", topical_categories = true, } labels["cardiology"] = { Wiktionary = true, topical_categories = true, } labels["carpentry"] = { Wiktionary = true, topical_categories = true, } labels["cartography"] = { Wiktionary = true, topical_categories = true, } labels["cartomancy"] = { Wiktionary = true, topical_categories = true, } labels["castells"] = { Wiktionary = true, topical_categories = true, } labels["category theory"] = { Wiktionary = true, topical_categories = true, } labels["Catholicism"] = { aliases = {"catholicism", "Catholic", "catholic"}, Wiktionary = true, topical_categories = true, } labels["caving"] = { Wiktionary = "caving#Noun", topical_categories = true, } labels["cellular automata"] = { Wiktionary = true, topical_categories = true, } labels["Celtic mythology"] = { display = "[[Celtic]] [[mythology]]", topical_categories = true, } labels["ceramics"] = { Wiktionary = true, topical_categories = true, } labels["cheerleading"] = { Wiktionary = "cheerleading#Noun", topical_categories = true, } labels["chemical element"] = { display = "[[chemistry]]", topical_categories = "Chemical elements", } labels["chemical element symbol"] = { -- Compare "systematic chemical element symbol" and "obsolete chemical element symbol". display = "[[chemistry]]", plain_categories = "Chemical element symbols", } labels["chemical engineering"] = { Wiktionary = true, topical_categories = true, } labels["chemistry"] = { aliases = {"chemical"}, Wiktionary = true, topical_categories = true, } labels["chess"] = { Wiktionary = true, topical_categories = true, } labels["children's games"] = { aliases = {"children's game"}, display = "[[children|children's]] [[game]]s", topical_categories = true, } labels["Chilean politics"] = { Wikipedia = "Politics of Chile", topical_categories = true, } labels["Chinese astronomy"] = { display = "[[Chinese]] [[astronomy]]", topical_categories = true, } labels["Chinese calligraphy"] = { display = "[[Chinese]] [[calligraphy]]", topical_categories = "Calligraphy", } labels["Chinese constellation"] = { display = "[[Chinese]] [[astronomy]]", topical_categories = "Constellations", } labels["Chinese folk religion"] = { display = "[[Chinese]] [[folk religion]]", topical_categories = "Religion", } labels["Chinese linguistics"] = { display = "[[Chinese]] [[linguistics]]", topical_categories = "Linguistics", } labels["Chinese mythology"] = { display = "[[Chinese]] [[mythology]]", topical_categories = true, } labels["Chinese philosophy"] = { display = "[[Chinese]] [[philosophy]]", topical_categories = true, } labels["Chinese phonetics"] = { display = "[[Chinese]] [[phonetics]]", topical_categories = true, } labels["Chinese religion"] = { display = "[[Chinese]] [[religion]]", topical_categories = "Religion", } labels["Chinese star"] = { display = "[[Chinese]] [[astronomy]]", topical_categories = "Stars", } labels["Christianity"] = { aliases = {"christianity", "Christian", "christian"}, Wiktionary = true, topical_categories = true, } labels["Church of England"] = { aliases = {"C of E", "CofE"}, Wikipedia = true, topical_categories = true, } labels["Church of the East"] = { Wiktionary = true, topical_categories = true, } labels["cinematography"] = { aliases = {"filmology"}, Wiktionary = true, topical_categories = true, } labels["cladistics"] = { Wiktionary = true, topical_categories = "Taxonomy", } labels["classical mechanics"] = { Wiktionary = true, topical_categories = true, } labels["classical studies"] = { Wiktionary = true, topical_categories = true, } labels["climate change"] = { Wiktionary = true, topical_categories = true, } labels["climatology"] = { Wiktionary = true, topical_categories = true, } labels["climbing"] = { aliases = {"rock climbing"}, Wiktionary = "climbing#Noun", topical_categories = true, } labels["clinical psychology"] = { Wiktionary = true, topical_categories = true, } labels["clothing"] = { Wiktionary = "clothing#Noun", topical_categories = true, } labels["cloud computing"] = { Wiktionary = true, topical_categories = "Computing", } labels["cockfighting"] = { aliases = {"cockfight"}, Wiktionary = true, topical_categories = true, } labels["codicology"] = { Wiktionary = true, topical_categories = true, } labels["coenzyme"] = { aliases = {"coenzymes"}, display = "[[biochemistry]]", topical_categories = "Coenzymes", } labels["coins"] = { -- Do not merge with "numismatics", as the category is different. aliases = {"coin"}, display = "[[numismatics]]", topical_categories = true, } labels["collectible card games"] = { aliases = {"trading card games", "collectible cards", "trading cards"}, Wikipedia = true, topical_categories = true, } labels["combinatorics"] = { Wiktionary = true, topical_categories = true, } labels["comedy"] = { Wiktionary = true, topical_categories = true, } labels["comics"] = { Wiktionary = true, topical_categories = true, } labels["commerce"] = { Wiktionary = true, topical_categories = true, } labels["commercial law"] = { display = "[[commercial#Adjective|commercial]] [[law]]", topical_categories = true, } labels["communication"] = { aliases = {"communications"}, Wiktionary = true, topical_categories = true, } labels["communism"] = { aliases = {"Communism", "communist"}, Wiktionary = true, topical_categories = true, } labels["compilation"] = { aliases = {"compiler"}, display = "[[software]] [[compilation]]", topical_categories = true, } labels["complex analysis"] = { Wiktionary = true, topical_categories = true, } labels["computational linguistics"] = { Wiktionary = true, topical_categories = true, } labels["computer chess"] = { Wiktionary = true, topical_categories = true, } labels["computer games"] = { aliases = {"computer game", "computer gaming"}, display = "[[computer game]]s", topical_categories = "Video games", } labels["computer graphics"] = { Wiktionary = true, topical_categories = true, } labels["computer hardware"] = { display = "[[computer]] [[hardware]]", topical_categories = true, } labels["computer languages"] = { aliases = {"computer language", "programming language", "programming languages"}, display = "[[computer language]]s", topical_categories = true, } labels["computer science"] = { aliases = {"comp sci", "CompSci", "compsci"}, Wiktionary = true, topical_categories = true, } labels["computer security"] = { Wiktionary = true, topical_categories = true, } labels["computing"] = { aliases = {"computer", "computers"}, Wiktionary = "computing#Noun", topical_categories = true, } labels["computing theory"] = { aliases = {"comptheory", "computability theory"}, display = "[[computing#Noun|computing]] [[theory]]", topical_categories = "Theory of computing", } labels["conchology"] = { Wiktionary = true, topical_categories = true, } labels["Confucianism"] = { Wiktionary = true, topical_categories = true, } labels["conlanging"] = { aliases = {"conlang", "conlanger", "constructed languages", "constructed language"}, Wiktionary = true, topical_categories = true, } labels["conservatism"] = { aliases = {"conservative"}, Wiktionary = true, topical_categories = true, } labels["conspiracy theories"] = { aliases = {"conspiracy theory", "conspiracy"}, Wiktionary = "conspiracy theory#Noun", topical_categories = true, } labels["constellation"] = { display = "[[astronomy]]", topical_categories = "Constellations", } labels["construction"] = { Wiktionary = true, topical_categories = true, } labels["control theory"] = { Wiktionary = true, topical_categories = true, } labels["cooking"] = { aliases = {"culinary", "cuisine", "cookery", "gastronomy"}, Wiktionary = "cooking#Noun", topical_categories = true, } labels["cookware"] = { aliases = {"bakeware"}, display = "[[cooking#Noun|cooking]]", topical_categories = "Cookware and bakeware", } labels["Coptic Orthodoxy"] = { aliases = {"Coptic Orthodox", "Coptic Orthodox Church"}, Wikipedia = true, topical_categories = true, } labels["copyright"] = { aliases = {"copyright law", "intellectual property", "intellectual property law", "IP law"}, display = "[[copyright]] [[law]]", topical_categories = true, } labels["copyright license"] = { aliases = {"copyright licenses", "license", "copyright licence", "copyright licences", "licence"}, display = "[[w:Copyright license|copyright law]]", Wikipedia = true, topical_categories = "Copyright licenses", } labels["cosmetics"] = { aliases = {"cosmetology"}, Wiktionary = true, topical_categories = true, } labels["cosmology"] = { Wiktionary = true, topical_categories = true, } labels["creationism"] = { aliases = {"baraminology"}, Wiktionary = "creationism#English", topical_categories = true, } labels["cribbage"] = { Wiktionary = true, topical_categories = true, } labels["cricket"] = { Wiktionary = true, topical_categories = true, } labels["crime"] = { aliases = {"criminal"}, Wiktionary = true, topical_categories = true, } labels["criminal law"] = { Wiktionary = true, topical_categories = true, } labels["criminology"] = { Wiktionary = true, topical_categories = true, } labels["crochet"] = { aliases = {"crocheting"}, Wiktionary = true, topical_categories = true, } labels["croquet"] = { Wiktionary = true, topical_categories = true, } labels["crosswording"] = { aliases = {"crosswords", "cruciverbalism", "cryptic crosswords", "crossword puzzles"}, Wiktionary = true, topical_categories = true, } labels["cryptocurrencies"] = { aliases = {"cryptocurrency", "crypto"}, Wiktionary = "cryptocurrency", topical_categories = "Cryptocurrency", } labels["cryptography"] = { aliases = {"cryptographic"}, Wiktionary = true, topical_categories = true, } labels["cryptozoology"] = { Wiktionary = true, topical_categories = true, } labels["crystallography"] = { Wiktionary = true, topical_categories = true, } labels["cultural anthropology"] = { Wiktionary = true, topical_categories = true, } labels["curling"] = { Wiktionary = true, topical_categories = true, } labels["currencies"] = { -- Do not merge with "numismatics", as the category is different. aliases = {"currency"}, display = "[[numismatics]]", topical_categories = true, } labels["cybernetics"] = { aliases = {"cybernetic"}, Wiktionary = true, topical_categories = true, } labels["cybersecurity"] = { Wiktionary = true, topical_categories = "Networking", } labels["cycle racing"] = { aliases = {"cycle sport"}, Wikipedia = "cycle sport", topical_categories = true, } labels["cycling"] = { aliases = {"bicycling", "bicycle", "bike"}, Wiktionary = "cycling#Noun", topical_categories = true, } labels["cytology"] = { aliases = {"cell biology", "cellular biology"}, Wiktionary = true, topical_categories = true, } labels["dance"] = { aliases = {"dancing"}, Wiktionary = "dance#Noun", topical_categories = true, } labels["dances"] = { display = "[[dance#Noun|dance]]", topical_categories = true, } labels["darts"] = { Wiktionary = true, topical_categories = true, } labels["data management"] = { Wiktionary = true, topical_categories = true, } labels["data modeling"] = { Wiktionary = true, topical_categories = true, } labels["data structures"] = { aliases = {"data structure"}, display = "[[computer science]]", topical_categories = true, } labels["databases"] = { aliases = {"database"}, display = "[[database]]s", topical_categories = true, } labels["decision theory"] = { Wiktionary = true, topical_categories = true, } labels["deltiology"] = { Wiktionary = true, topical_categories = true, } labels["demography"] = { aliases = {"demographics"}, Wiktionary = true, topical_categories = true, } labels["demonym"] = { aliases = {"demonyms"}, Wiktionary = true, topical_categories = "Demonyms", } labels["demoscene"] = { topical_categories = true, } labels["dentistry"] = { aliases = {"dentist"}, Wiktionary = true, topical_categories = true, } labels["dermatology"] = { Wiktionary = true, topical_categories = true, } labels["design"] = { Wiktionary = "design#Noun", topical_categories = true, } labels["developmental psychology"] = { Wiktionary = true, topical_categories = "Psychology", } labels["dice games"] = { aliases = {"dice"}, display = "[[dice game]]s", topical_categories = true, } labels["dictation"] = { Wiktionary = true, topical_categories = true, } labels["differential geometry"] = { Wiktionary = true, topical_categories = true, } labels["diplomacy"] = { Wiktionary = true, topical_categories = true, } labels["disc golf"] = { Wiktionary = true, topical_categories = true, } labels["disease"] = { aliases = {"diseases"}, display = "[[pathology]]", topical_categories = "Diseases", } labels["divination"] = { Wiktionary = true, topical_categories = true, } labels["diving"] = { Wiktionary = "diving#Noun", topical_categories = true, } labels["dominoes"] = { Wiktionary = true, topical_categories = true, } labels["dou dizhu"] = { Wikipedia = true, topical_categories = true, } labels["drama"] = { Wiktionary = true, topical_categories = true, } labels["dressage"] = { Wiktionary = true, topical_categories = true, } labels["E number"] = { display = "[[food]] [[manufacture]]", plain_categories = "European food additive numbers", } labels["early Christianity"] = { aliases = {"early christianity", "Early Christianity", "early Church", "early church", "Early Church", "the early Church", "the early church", "the Early Church"}, Wikipedia = true, topical_categories = true, } labels["earth science"] = { Wiktionary = true, topical_categories = "Earth sciences", } labels["Eastern Catholicism"] = { aliases = {"Eastern Catholic"}, Wikipedia = true, topical_categories = true, } labels["Eastern Christianity"] = { aliases = {"Eastern christianity", "Eastern Christian", "Eastern christian", "Eastern Church", "Eastern church"}, Wikipedia = true, topical_categories = true, } labels["Eastern Orthodoxy"] = { aliases = {"Eastern Orthodox", "Eastern Orthodox Church"}, Wikipedia = true, topical_categories = true, } labels["eating disorders"] = { aliases = {"eating disorder"}, display = "[[eating disorder]]s", topical_categories = true, } labels["ecology"] = { Wiktionary = true, topical_categories = true, } labels["economics"] = { Wiktionary = true, topical_categories = true, } labels["education"] = { Wiktionary = true, topical_categories = true, } labels["Egyptian god"] = { aliases = {"Egyptian goddess", "Egyptian deity"}, display = "[[Egyptian]] [[mythology]]", topical_categories = "Egyptian deities", } labels["Egyptian mythology"] = { display = "[[Egyptian]] [[mythology]]", topical_categories = true, } labels["Egyptology"] = { Wiktionary = true, aliases = {"Ancient Egypt"}, topical_categories = "Ancient Egypt", } labels["electrencephalography"] = { Wiktionary = true, topical_categories = true, } labels["electrical engineering"] = { Wiktionary = true, topical_categories = true, } labels["electricity"] = { aliases = {"electrical"}, Wiktionary = true, topical_categories = true, } labels["electrochemistry"] = { aliases = {"electrochemical"}, Wiktionary = true, topical_categories = true, } labels["electrodynamics"] = { Wiktionary = true, topical_categories = true, } labels["electromagnetism"] = { Wiktionary = true, topical_categories = true, } labels["electronics"] = { Wiktionary = true, topical_categories = true, } labels["embryology"] = { Wiktionary = true, topical_categories = true, } labels["emergency medicine"] = { Wiktionary = true, topical_categories = true, } labels["emergency services"] = { Wiktionary = true, topical_categories = true, } labels["endocrinology"] = { Wiktionary = true, topical_categories = true, } labels["engineering"] = { Wiktionary = "engineering#Noun", topical_categories = true, } labels["enterprise engineering"] = { Wiktionary = true, topical_categories = true, } labels["entomology"] = { Wiktionary = true, topical_categories = true, } labels["enzyme"] = { aliases = {"enzymes"}, display = "[[biochemistry]]", topical_categories = "Enzymes", } labels["epidemiology"] = { Wiktionary = true, topical_categories = true, } labels["epigraphy"] = { Wiktionary = true, topical_categories = true, } labels["epistemology"] = { Wiktionary = true, topical_categories = true, } labels["equestrianism"] = { aliases = {"equestrian", "horses", "horsemanship"}, Wiktionary = true, topical_categories = true, } labels["espionage"] = { Wiktionary = true, topical_categories = true, } labels["ethics"] = { aliases = {"ethical"}, Wiktionary = true, topical_categories = true, } labels["ethnography"] = { Wiktionary = true, topical_categories = true, } labels["ethology"] = { Wiktionary = true, topical_categories = true, } labels["EU politics"] = { aliases = {"European Union politics"}, Wikipedia = "Politics of the European Union", topical_categories = true, } labels["European folklore"] = { display = "[[European]] [[folklore]]", topical_categories = true, } labels["European politics"] = { Wikipedia = "Politics of Europe", topical_categories = true, } labels["European Union"] = { aliases = {"EU"}, Wiktionary = true, topical_categories = true, } labels["Evangelicalism"] = { aliases = {"Evangelical", "evangelical", "Evangelical Christianity", "Evangelical Christian", "Evangelical Protestantism", "Evangelical Protestant"}, Wikipedia = true, topical_categories = true, } labels["evolutionary theory"] = { aliases = {"evolutionary biology"}, Wiktionary = true, topical_categories = true, } labels["exercise"] = { Wiktionary = true, topical_categories = true, } labels["eye color"] = { aliases = {"eye colour"}, display = "[[eye]] [[color]]", topical_categories = "Eye colors", } labels["eyewear"] = { Wiktionary = true, topical_categories = true, } labels["fairy tale"] = { -- names of fairy tales aliases = {"fairytale", "fairy-tale"}, Wiktionary = true, topical_categories = true, } labels["fairy tales"] = { -- relating to fairy tales aliases = {"fairytales", "fairy-tales"}, Wiktionary = true, topical_categories = true, } labels["falconry"] = { Wiktionary = true, topical_categories = true, } labels["fantasy"] = { Wiktionary = true, topical_categories = true, } labels["farriery"] = { Wiktionary = true, topical_categories = true, } labels["fascism"] = { Wiktionary = true, topical_categories = true, } labels["fashion"] = { Wiktionary = true, topical_categories = true, } labels["fatty acid"] = { display = "[[organic chemistry]]", topical_categories = "Fatty acids", } labels["felid"] = { aliases = {"cat"}, display = "[[zoology]]", topical_categories = "Felids", } labels["feminism"] = { Wiktionary = true, topical_categories = true, } labels["fencing"] = { Wiktionary = "fencing#Noun", topical_categories = true, } labels["feudalism"] = { Wiktionary = true, topical_categories = true, } labels["fiction"] = { aliases = {"fictional"}, Wiktionary = true, topical_categories = true, } labels["fictional character"] = { display = "[[fiction]]", topical_categories = "Fictional characters", } labels["field hockey"] = { Wiktionary = true, topical_categories = true, } labels["figure of speech"] = { display = "[[rhetoric]]", topical_categories = "Figures of speech", } labels["figure skating"] = { Wiktionary = true, topical_categories = true, } labels["file format"] = { Wiktionary = true, topical_categories = "File formats", } labels["film"] = { Wiktionary = "film#Noun", topical_categories = true, } labels["film genre"] = { aliases = {"cinema"}, display = "[[film#Noun|film]]", topical_categories = "Film genres", } labels["finance"] = { Wiktionary = "finance#Noun", topical_categories = true, } labels["Finnic mythology"] = { aliases = {"Finnish mythology"}, display = "[[Finnic]] [[mythology]]", topical_categories = true, } labels["firearms"] = { aliases = {"firearm"}, display = "[[firearm]]s", topical_categories = true, } labels["firefighting"] = { Wiktionary = true, topical_categories = true, } labels["fish"] = { display = "[[zoology]]", topical_categories = true, } labels["fishing"] = { aliases = {"angling"}, Wiktionary = "fishing#Noun", topical_categories = true, } labels["flamenco"] = { Wiktionary = true, topical_categories = true, } labels["fluid dynamics"] = { Wiktionary = true, topical_categories = true, } labels["fluid mechanics"] = { Wiktionary = true, topical_categories = "Mechanics", } labels["folklore"] = { Wiktionary = true, topical_categories = true, } labels["footwear"] = { Wiktionary = true, topical_categories = true, } labels["forestry"] = { Wiktionary = true, topical_categories = true, } labels["Forteana"] = { Wiktionary = true, topical_categories = true, } labels["Freemasonry"] = { aliases = {"freemasonry"}, Wiktionary = true, topical_categories = true, } labels["French politics"] = { Wikipedia = "Politics of France", topical_categories = true, } labels["functional analysis"] = { Wiktionary = true, topical_categories = true, } labels["functional group prefix"] = { display = "[[organic chemistry]]", topical_categories = "Functional group prefixes", } labels["functional group suffix"] = { display = "[[organic chemistry]]", topical_categories = "Functional group suffixes", } labels["functional programming"] = { Wiktionary = true, topical_categories = "Programming", } labels["furniture"] = { Wiktionary = true, topical_categories = true, } labels["furry fandom"] = { aliases = {"furry", "furry community", "fursuit", "kemonā", "kemona", "kemono", "kemonomimi"}, display = "[[furry#Noun|furry]] [[fandom]]", topical_categories = true, } labels["fuzzy logic"] = { Wiktionary = true, topical_categories = true, } labels["Gaelic football"] = { Wiktionary = true, topical_categories = true, } labels["galaxy"] = { display = "[[astronomy]]", topical_categories = "Galaxies", } labels["gambling"] = { Wiktionary = "gambling#Noun", topical_categories = true, } labels["game theory"] = { Wiktionary = true, topical_categories = true, } labels["games"] = { aliases = {"game"}, Wiktionary = "game#Noun", topical_categories = true, } labels["gaming"] = { Wiktionary = "gaming#Noun", topical_categories = true, } labels["gender critical"] = { aliases = {"gender-critical", "gender critical feminism", "gender-critical feminism", "GC", "GCF", "trans-exclusionary radical feminism", "TERF", "TERFism"}, Wiktionary = "gender-critical#Adjective", Wikipedia = "Gender-critical feminism", topical_categories = "Gender-critical feminism", } labels["genealogy"] = { Wiktionary = true, topical_categories = true, } labels["general semantics"] = { Wiktionary = true, topical_categories = true, } labels["genetic disorder"] = { display = "[[medical]] [[genetics]]", topical_categories = "Genetic disorders", } labels["genetics"] = { Wiktionary = true, topical_categories = true, } labels["geography"] = { Wiktionary = true, topical_categories = true, } labels["geological period"] = { Wikipedia = true, display ="[[geology]]", topical_categories = "Geological periods", } labels["geology"] = { Wiktionary = true, topical_categories = true, } labels["geometry"] = { aliases = {"geometric", "geometrical"}, Wiktionary = true, topical_categories = true, } labels["geomorphology"] = { Wiktionary = true, topical_categories = true, } labels["geopolitics"] = { Wiktionary = true, topical_categories = true, } labels["German politics"] = { Wikipedia = "Politics of Germany", topical_categories = true, } labels["Germanic god"] = { aliases = {"Germanic goddess", "Germanic deity"}, display = "[[Germanic]] [[mythology]]", topical_categories = "Germanic deities", } labels["Germanic paganism"] = { aliases = {"Asatru", "Ásatrú", "Germanic neopaganism", "Germanic Paganism", "Heathenry", "heathenry", "Norse neopaganism", "Norse paganism"}, display = "[[Germanic#Adjective|Germanic]] [[paganism]]", topical_categories = true, } labels["gerontology"] = { Wiktionary = true, topical_categories = true, } labels["gladiatorial combat"] = { Wikipedia = true, topical_categories = true, } labels["glassblowing"] = { Wiktionary = true, topical_categories = true, } labels["Gnosticism"] = { aliases = {"gnosticism"}, Wiktionary = true, topical_categories = true, } labels["go"] = { aliases = {"Go", "game of go", "game of Go"}, display = "{{l|en|go|id=game}}", topical_categories = true, } labels["golf"] = { aliases = {"golfing"}, Wiktionary = true, topical_categories = true, } labels["government"] = { Wiktionary = true, topical_categories = true, } labels["grammar"] = { aliases = {"grammatical"}, Wiktionary = true, topical_categories = true, } labels["grammatical case"] = { display = "[[grammar]]", topical_categories = "Grammatical cases", } labels["grammatical mood"] = { display = "[[grammar]]", topical_categories = "Grammatical moods", } labels["graph theory"] = { Wiktionary = true, topical_categories = true, } labels["graphic design"] = { Wiktionary = true, topical_categories = true, } labels["graphical user interface"] = { aliases = {"GUI"}, Wiktionary = true, topical_categories = true, } labels["Greek god"] = { aliases = {"Greek goddess", "Greek deity"}, display = "[[Greek]] [[mythology]]", topical_categories = "Greek deities", } labels["Greek mythology"] = { display = "[[Greek]] [[mythology]]", topical_categories = true, } labels["Greek Orthodoxy"] = { aliases = {"Greek Orthodox", "Greek Orthodox Church"}, Wikipedia = true, topical_categories = true, } labels["group theory"] = { Wiktionary = true, topical_categories = true, } labels["gun mechanisms"] = { aliases = {"firearm mechanism", "firearm mechanisms", "gun mechanism"}, display = "[[firearm]]s", topical_categories = true, } labels["gun sports"] = { aliases = {"shooting sports"}, display = "[[gun]] [[sport]]s", topical_categories = true, } labels["gymnastics"] = { Wiktionary = true, Wikipedia = true, topical_categories = true, } labels["gynaecology"] = { aliases = {"gynecology"}, Wiktionary = "gynecology", topical_categories = true, } labels["hair color"] = { aliases = {"hair colour"}, display = "[[hair]] [[color]]", topical_categories = "Hair colors", } labels["hairdressing"] = { Wiktionary = true, topical_categories = true, } labels["hanafuda"] = { Wikipedia = true, topical_categories = true, } labels["hand games"] = { aliases = {"hand game"}, display = "[[hand]] [[game]]s", topical_categories = true, } labels["handball"] = { Wiktionary = true, topical_categories = true, } labels["Hawaiian mythology"] = { display = "[[Hawaiian]] [[mythology]]", topical_categories = true, } labels["headwear"] = { display = "[[clothing#Noun|clothing]]", topical_categories = true, } labels["healthcare"] = { Wiktionary = true, topical_categories = true, } labels["helminthology"] = { Wiktionary = true, topical_categories = true, } labels["hematology"] = { aliases = {"haematology"}, Wiktionary = true, topical_categories = true, } labels["heraldic charge"] = { aliases = {"heraldiccharge"}, display = "[[heraldry]]", topical_categories = "Heraldic charges", } labels["heraldry"] = { Wiktionary = true, topical_categories = true, } labels["herbalism"] = { Wiktionary = true, topical_categories = true, } labels["herpetology"] = { Wiktionary = true, topical_categories = true, } labels["Hindu god"] = { display = "[[Hinduism]]", topical_categories = "Hindu deities", } labels["Hinduism"] = { Wiktionary = true, topical_categories = true, } labels["Hindutva"] = { Wiktionary = true, topical_categories = true, } labels["historical currencies"] = { aliases = {"historical currency"}, display = "[[numismatics]]", sense_categories = "historical", topical_categories = "Historical currencies", } labels["historical linguistics"] = { Wiktionary = true, topical_categories = "Linguistics", } labels["historical period"] = { aliases = {"historical periods"}, display = "[[history]]", topical_categories = "Historical periods", } labels["historiography"] = { Wiktionary = true, topical_categories = true, } labels["history"] = { Wiktionary = true, topical_categories = true, } labels["hockey"] = { display = "[[field hockey]] or [[ice hockey]]", topical_categories = {"Field hockey", "Ice hockey"}, } labels["homeopathy"] = { Wiktionary = true, topical_categories = true, } labels["Homestuck"] = { display = "''[[Homestuck]]''", Wiktionary = true, topical_categories = true, } labels["Hong Kong politics"] = { aliases = {"HK politics"}, Wikipedia = "Politics of Hong Kong", topical_categories = true, } labels["hormone"] = { display = "[[biochemistry]]", topical_categories = "Hormones", } labels["horse color"] = { aliases = {"horse colour"}, display = "[[horse]] [[color]]", topical_categories = "Horse colors", } labels["horse racing"] = { Wiktionary = true, topical_categories = true, } labels["horticulture"] = { aliases = {"gardening"}, Wiktionary = true, topical_categories = true, } labels["HTML"] = { Wiktionary = "Hypertext Markup Language", topical_categories = true, } labels["human resources"] = { aliases = {"HR"}, Wiktionary = true, topical_categories = true, } labels["humanities"] = { Wiktionary = true, topical_categories = true, } labels["hunting"] = { Wiktionary = "hunting#Noun", topical_categories = true, } labels["hurling"] = { Wiktionary = "hurling#Noun", topical_categories = true, } labels["hydroacoustics"] = { Wikipedia = true, topical_categories = true, } labels["hydrocarbon chain prefix"] = { display = "[[organic chemistry]]", topical_categories = "Hydrocarbon chain prefixes", } labels["hydrocarbon chain suffix"] = { display = "[[organic chemistry]]", topical_categories = "Hydrocarbon chain suffixes", } labels["hydrology"] = { Wiktionary = true, topical_categories = true, } labels["ice hockey"] = { Wiktionary = true, topical_categories = true, } labels["ichthyology"] = { Wiktionary = true, topical_categories = true, } labels["idol fandom"] = { aliases = {"idol"}, display = "[[idol]] [[fandom]]", topical_categories = true, } labels["immunochemistry"] = { Wiktionary = true, topical_categories = true, } labels["immunology"] = { Wiktionary = true, topical_categories = true, } labels["import/export"] = { aliases = {"import", "export"}, display = "[[import#Noun|import]]/[[export#Noun|export]]", topical_categories = true, } labels["incoterm"] = { display = "[[Incoterm]]", topical_categories = "Incoterms", } labels["Indian politics"] = { Wikipedia = "Politics of India", topical_categories = true, } labels["Indo-European studies"] = { aliases = {"indo-european studies"}, Wiktionary = true, topical_categories = true, } labels["Indonesian politics"] = { aliases = {"Indonesia politics"}, Wikipedia = "Politics of Indonesia", topical_categories = true, } labels["information science"] = { Wiktionary = true, topical_categories = true, } labels["information technology"] = { aliases = {"IT"}, Wiktionary = true, topical_categories = "Computing", } labels["information theory"] = { Wiktionary = true, topical_categories = true, } labels["inheritance law"] = { Wiktionary = true, topical_categories = true, } labels["inorganic chemistry"] = { Wiktionary = true, topical_categories = true, } labels["inorganic compound"] = { display = "[[inorganic chemistry]]", topical_categories = "Inorganic compounds", } labels["insurance"] = { Wiktionary = true, topical_categories = true, } labels["international law"] = { Wiktionary = true, topical_categories = true, } labels["international relations"] = { Wiktionary = true, topical_categories = true, } labels["international standards"] = { aliases = {"international standard", "ISO", "International Organization for Standardization", "International Organisation for Standardisation"}, Wikipedia = "International standard", } labels["Internet"] = { aliases = {"internet", "online"}, Wiktionary = true, topical_categories = true, } labels["Iranian mythology"] = { display = "[[Iranian]] [[mythology]]", topical_categories = true, } labels["Irish mythology"] = { display = "[[Irish]] [[mythology]]", topical_categories = true, } labels["Irish politics"] = { Wikipedia = "Politics of the Republic of Ireland", topical_categories = true, } labels["Islam"] = { aliases = {"islam", "Islamic", "Muslim"}, Wikipedia = true, topical_categories = true, } labels["Islamic finance"] = { aliases = {"Islamic banking", "Muslim finance", "Muslim banking", "Sharia-compliant finance"}, Wikipedia = true, topical_categories = true, } labels["Islamic law"] = { aliases = {"Islamic legal", "Sharia"}, Wikipedia = true, topical_categories = true, } labels["isotope"] = { display = "[[physics]]", topical_categories = "Isotopes", } labels["Jainism"] = { Wiktionary = true, Wikipedia = true, topical_categories = true, } labels["Japanese fiction"] = { -- aliases = {"anime", "manga", "anime and manga", "manga and anime"}, display = "[[Japanese#Adjective|Japanese]] [[fiction]]", Wikipedia = true, topical_categories = true, } labels["Japanese god"] = { display = "[[Japanese#Adjective|Japanese]] [[mythology]]", topical_categories = "Japanese deities", } labels["Japanese mythology"] = { display = "[[Japanese#Adjective|Japanese]] [[mythology]]", topical_categories = true, } labels["Japanese politics"] = { Wikipedia = "Politics of Japan", topical_categories = true, } labels["Japanese pornography"] = { aliases = {"Japanese porn", "hentai", "adult anime", "erotic anime", "ero anime"}, display = "[[Japanese#Adjective|Japanese]] [[pornography]]", Wikipedia = true, topical_categories = true, } labels["Java programming language"] = { aliases = {"JavaPL", "Java PL"}, Wikipedia = "Java (programming language)", topical_categories = true, } labels["jazz"] = { Wiktionary = "jazz#Noun", topical_categories = true, } labels["jewelry"] = { aliases = {"jewellery"}, Wiktionary = true, topical_categories = true, } labels["Jewish law"] = { aliases = {"Halacha", "Halachah", "Halakha", "Halakhah", "halacha", "halachah", "halakha", "halakhah", "Jewish Law", "jewish law"}, display = "[[Jewish]] [[law]]", topical_categories = true, } labels["journalism"] = { Wiktionary = true, topical_categories = true, } labels["journalism types"] = { aliases = {"journalism type"}, display = "[[journalism]]", topical_categories = true, } labels["Judaism"] = { Wiktionary = true, topical_categories = true, } labels["judo"] = { Wiktionary = true, topical_categories = true, } labels["juggling"] = { Wiktionary = "juggling#Noun", topical_categories = true, } labels["karuta"] = { Wiktionary = true, topical_categories = true, } labels["kendo"] = { Wiktionary = true, topical_categories = true, } labels["knitting"] = { Wiktionary = "knitting#Noun", topical_categories = true, } labels["Korean mythology"] = { display = "[[Korean#Adjective|Korean]] [[mythology]]", topical_categories = true, } labels["labour"] = { aliases = {"labor", "labour movement", "labor movement"}, Wiktionary = true, topical_categories = true, } labels["labour law"] = { aliases = {"labor law"}, Wiktionary = true, topical_categories = "Law", } labels["lacrosse"] = { Wiktionary = true, topical_categories = true, } labels["landforms"] = { display = "[[geography]]", topical_categories = true, } labels["law"] = { aliases = {"legal"}, Wiktionary = "law#English", topical_categories = true, } labels["law enforcement"] = { aliases = {"police", "policing"}, Wiktionary = true, topical_categories = true, } labels["leatherworking"] = { Wiktionary = true, topical_categories = true, } labels["leftism"] = { aliases = {"leftist"}, Wiktionary = true, topical_categories = true, } labels["letterpress"] = { aliases = {"metal type", "metal typesetting"}, display = "[[letterpress]] [[typography]]", topical_categories = "Typography", } labels["lexicography"] = { Wiktionary = true, topical_categories = true, } labels["LGBTQ"] = { aliases = {"LGBT", "LGBT+", "LGBT*", "LGBTQ+", "LGBTQ*", "LGBTQIA", "LGBTQIA+", "LGBTQIA*", "queer"}, Wiktionary = true, topical_categories = true, } labels["liberalism"] = { aliases = {"liberal"}, Wiktionary = true, topical_categories = true, } labels["library science"] = { Wiktionary = true, topical_categories = true, } labels["lichenology"] = { Wiktionary = true, topical_categories = true, } labels["limnology"] = { Wiktionary = true, topical_categories = "Ecology", } labels["linear algebra"] = { aliases = {"vector algebra"}, Wiktionary = true, topical_categories = true, } labels["linguistic morphology"] = { display = "[[linguistic]] [[morphology]]", topical_categories = true, } labels["linguistics"] = { aliases = {"linguistic", "philology"}, Wiktionary = true, topical_categories = true, } labels["lipid"] = { aliases = {"lipids"}, display = "[[biochemistry]]", topical_categories = "Lipids", } labels["literature"] = { Wiktionary = true, topical_categories = true, } labels["locksmithing"] = { Wiktionary = true, display = "[[locksmithing]]", topical_categories = true, } labels["logic"] = { Wiktionary = true, topical_categories = true, } labels["logical fallacy"] = { aliases = {"fallacies"}, display = "[[rhetoric]]", topical_categories = "Logical fallacies", } labels["logistics"] = { Wiktionary = true, topical_categories = true, } labels["looksmaxxing"] = { Wiktionary = "looksmaxxing#English: enhancement", topical_categories = true, } labels["luge"] = { Wiktionary = true, topical_categories = true, } labels["Lutheranism"] = { aliases = {"Lutheran", "Lutheranist", "Lutheran Church"}, Wikipedia = true, topical_categories = true, } labels["lutherie"] = { Wiktionary = true, topical_categories = true, } labels["machine learning"] = { aliases = {"ML"}, Wiktionary = true, topical_categories = true, } labels["machining"] = { Wiktionary = "machining#Noun", topical_categories = true, } labels["macroeconomics"] = { Wiktionary = true, topical_categories = "Economics", } labels["mahjong"] = { Wiktionary = true, topical_categories = true, } labels["malacology"] = { Wiktionary = true, topical_categories = true, } labels["Malaysian politics"] = { aliases = {"Malaysia politics"}, Wikipedia = "Politics of Malaysia", topical_categories = true, } labels["mammalogy"] = { Wiktionary = true, topical_categories = true, } labels["management"] = { Wiktionary = true, topical_categories = true, } labels["manga"] = { aliases = {"Japanese comics"}, Wiktionary = true, topical_categories = "Japanese fiction", } labels["manhua"] = { aliases = {"Chinese comics"}, Wiktionary = true, topical_categories = "Chinese fiction", } labels["manhwa"] = { aliases = {"Korean comics"}, Wiktionary = true, topical_categories = "Korean fiction", } labels["Manichaeism"] = { Wiktionary = true, topical_categories = true, } labels["manufacturing"] = { Wiktionary = "manufacturing#Noun", topical_categories = true, } labels["Maoism"] = { aliases = {"Maoist"}, Wiktionary = true, topical_categories = true, } labels["marching"] = { Wiktionary = "marching#Noun", topical_categories = true, } labels["marine biology"] = { aliases = {"coral science"}, Wiktionary = true, topical_categories = true, } labels["marketing"] = { Wiktionary = "marketing#Noun", topical_categories = true, } labels["martial arts"] = { Wiktionary = true, topical_categories = true, } labels["Marxism"] = { aliases = {"Marxist"}, Wiktionary = true, topical_categories = true, } labels["masonry"] = { Wiktionary = true, topical_categories = true, } labels["massage"] = { Wiktionary = true, topical_categories = true, } labels["materials science"] = { Wiktionary = true, topical_categories = true, } labels["mathematical analysis"] = { aliases = {"analysis"}, Wiktionary = true, topical_categories = true, } labels["mathematics"] = { aliases = {"math", "maths"}, Wiktionary = true, topical_categories = true, } labels["measure theory"] = { Wiktionary = true, topical_categories = true, } labels["mechanical engineering"] = { Wiktionary = true, topical_categories = true, } labels["mechanics"] = { Wiktionary = true, topical_categories = true, } labels["media"] = { Wiktionary = true, topical_categories = true, } labels["mediaeval folklore"] = { aliases = {"medieval folklore"}, display = "[[mediaeval]] [[folklore]]", topical_categories = "European folklore", } labels["medical genetics"] = { display = "[[medical]] [[genetics]]", topical_categories = true, } labels["medical sign"] = { aliases = {"medical symptoms", "symptom", "symptoms"}, display = "[[medicine]]", topical_categories = "Medical signs and symptoms", } labels["medicine"] = { aliases = {"medical"}, Wiktionary = true, topical_categories = true, } labels["Meitei god"] = { aliases = {"Meitei goddess", "Meitei deity"}, display = "[[Meitei]] [[mythology]]", topical_categories = "Meitei deities", } labels["mental health"] = { Wiktionary = true, topical_categories = true, } labels["Mesopotamian god"] = { aliases = {"Mesopotamian goddess", "Mesopotamian deity", "Mesopotamian diety" -- FIXME: existing typo }, display = "[[Mesopotamian]] [[mythology]]", topical_categories = "Mesopotamian deities", } labels["Mesopotamian mythology"] = { display = "[[Mesopotamian]] [[mythology]]", topical_categories = true, } labels["metadata"] = { Wiktionary = true, topical_categories = "Data management", } labels["metallurgy"] = { Wiktionary = true, topical_categories = true, } labels["metalworking"] = { Wiktionary = true, topical_categories = true, } labels["metamaterial"] = { display = "[[physics]]", topical_categories = "Metamaterials", } labels["metaphysics"] = { Wiktionary = true, topical_categories = true, } labels["meteorology"] = { Wiktionary = true, topical_categories = true, } labels["Methodism"] = { aliases = {"Methodist", "methodism", "methodist"}, Wiktionary = true, topical_categories = true, } labels["metrology"] = { Wiktionary = true, topical_categories = true, } labels["Mexican politics"] = { aliases = {"Mexico politics"}, Wikipedia = "Politics of Mexico", topical_categories = true, } labels["microbiology"] = { Wiktionary = true, topical_categories = true, } labels["microelectronics"] = { Wiktionary = true, topical_categories = true, } labels["micronationalism"] = { aliases = {"micronation", "micronations"}, Wiktionary = true, topical_categories = true, } labels["microscopy"] = { Wiktionary = true, topical_categories = true, } labels["military"] = { aliases = {"army"}, Wiktionary = true, topical_categories = true, } labels["military ranks"] = { aliases = {"military rank"}, display = "[[military]]", topical_categories = true, } labels["military unit"] = { display = "[[military]]", topical_categories = "Military units", } labels["milling"] = { Wiktionary = true, topical_categories = true, } labels["Minecraft"] = { display = "''[[Minecraft]]''", topical_categories = true, } labels["mineral"] = { display = "[[mineralogy]]", topical_categories = "Minerals", } labels["mineralogy"] = { Wiktionary = true, topical_categories = true, } labels["mining"] = { Wiktionary = "mining#Noun", topical_categories = true, } labels["mobile phones"] = { aliases = {"cell phone", "cell phones", "mobile phone", "mobile telephony", "smartphone", "smartphones", "mobile"}, display = "[[mobile telephone|mobile telephony]]", topical_categories = true, } labels["molecular biology"] = { Wiktionary = true, topical_categories = true, } labels["monarchy"] = { Wiktionary = true, topical_categories = true, } labels["money"] = { Wiktionary = true, topical_categories = true, } labels["Mormonism"] = { Wiktionary = true, topical_categories = true, } labels["motor racing"] = { -- There are other types of racing, but 99% of the time "racing" on its own refers to motorsports. aliases = {"motor sport", "motorsport", "motorsports", "racing"}, Wiktionary = true, topical_categories = true, } labels["motorcycling"] = { aliases = {"motorcycle", "motorcycles", "motorbike"}, Wiktionary = "motorcycling#Noun", topical_categories = "Motorcycles", } labels["multiplicity"] = { aliases = {"plurality", "polypsychism", "dissociative identity disorder", "DID"}, display = "{{l|en|multiplicity|id=multiple personalities}}", topical_categories = "Multiplicity (psychology)", } labels["muscle"] = { aliases = {"muscles"}, display = "[[anatomy]]", topical_categories = "Muscles", } labels["mushroom"] = { aliases = {"mushrooms"}, display = "[[mycology]]", topical_categories = "Mushrooms", } labels["music"] = { aliases = {"musical"}, Wiktionary = true, topical_categories = true, } labels["music genre"] = { display = "[[music]]", topical_categories = "Musical genres", } labels["music industry"] = { Wikipedia = true, topical_categories = true, } labels["musical instruments"] = { aliases = {"musical instrument"}, display = "[[music]]", topical_categories = true, } labels["musician"] = { display = "[[music]]", topical_categories = "Musicians", } labels["musicology"] = { Wiktionary = true, topical_categories = "Music", } labels["mycology"] = { Wiktionary = true, topical_categories = true, } labels["mysticism"] = { Wiktionary = true, topical_categories = true, } labels["mythological creature"] = { aliases = {"mythological creatures"}, display = "[[mythology]]", topical_categories = "Mythological creatures", } labels["mythology"] = { Wiktionary = true, topical_categories = true, } labels["nanotechnology"] = { Wiktionary = true, topical_categories = true, } labels["narratology"] = { Wiktionary = true, topical_categories = true, } labels["nautical"] = { Wiktionary = true, topical_categories = true, } labels["Navajo mythology"] = { display = "[[Navajo]] [[mythology]]", topical_categories = true, } labels["navigation"] = { Wiktionary = true, topical_categories = true, } labels["Nazism"] = { -- see also Neo-Nazism aliases = {"nazism", "Nazi", "nazi", "Nazis", "nazis"}, Wikipedia = true, topical_categories = true, } labels["nematology"] = { Wiktionary = true, topical_categories = "Zoology", } labels["neo-Nazism"] = { -- Often used to indicate Nazi-used jargon; compare "white supremacist ideology" aliases = {"Neo-Nazism", "Neo-nazism", "neo-nazism", "Neo-Nazi", "Neo-nazi", "neo-Nazi", "neo-nazi", "Neo-Nazis", "Neo-nazis", "neo-Nazis", "neo-nazis", "NeoNazism", "Neonazism", "neoNazism", "neonazism", "NeoNazi", "Neonazi", "neoNazi", "neonazi", "NeoNazis", "Neonazis", "neoNazis", "neonazis"}, Wikipedia = true, topical_categories = true, } labels["netball"] = { Wiktionary = true, topical_categories = true, } labels["networking"] = { Wiktionary = "networking#Noun", topical_categories = true, } labels["neuroanatomy"] = { Wiktionary = true, topical_categories = true, } labels["neurology"] = { Wiktionary = true, topical_categories = true, } labels["neuroscience"] = { Wiktionary = true, topical_categories = true, } labels["neurosurgery"] = { Wiktionary = true, topical_categories = true, } labels["neurotoxin"] = { display = "[[neurotoxicology]]", topical_categories = "Neurotoxins", } labels["neurotransmitter"] = { display = "[[biochemistry]]", topical_categories = "Neurotransmitters", } labels["New Zealand politics"] = { Wikipedia = "Politics of New Zealand", topical_categories = true, } labels["newspapers"] = { display = "[[newspaper]]s", topical_categories = true, } labels["Norse god"] = { aliases = {"Norse goddess", "Norse deity"}, display = "[[Norse]] [[mythology]]", topical_categories = "Norse deities", } labels["Norse mythology"] = { display = "[[Norse]] [[mythology]]", topical_categories = true, } labels["nuclear energy"] = { Wiktionary = true, topical_categories = true, } labels["nuclear physics"] = { Wiktionary = true, topical_categories = true, } labels["number theory"] = { Wiktionary = true, topical_categories = true, } labels["numismatics"] = { Wiktionary = true, topical_categories = "Currency", } labels["nutrition"] = { Wiktionary = true, topical_categories = true, } labels["object-oriented programming"] = { aliases = {"object-oriented", "OOP"}, Wiktionary = true, topical_categories = true, } labels["obsolete chemical element symbol"] = { display = "[[chemistry]], [[obsolete]]", plain_categories = "Obsolete chemical element symbols", } labels["obstetrics"] = { aliases = {"obstetric"}, Wiktionary = true, topical_categories = true, } labels["occult"] = { Wiktionary = true, topical_categories = true, } labels["oceanography"] = { Wiktionary = true, topical_categories = true, } labels["Odinani"] = { aliases = {"Odinala", "Omenala", "Odinana", "Omenana", "Igbo religion"}, Wikipedia = true, topical_categories = true, } labels["oil industry"] = { aliases = {"oil", "oil drilling", "petroleum industry", "petroleum"}, Wikipedia = true, topical_categories = true, } labels["oncology"] = { Wiktionary = true, topical_categories = true, } labels["online gaming"] = { aliases = {"online games", "MMO", "MMORPG"}, display = "[[online]] [[gaming#Noun|gaming]]", topical_categories = "Video games", } labels["onomastics"] = { Wiktionary = true, topical_categories = true, } labels["opera"] = { Wiktionary = true, topical_categories = true, } labels["operating systems"] = { display = "[[operating system]]s", topical_categories = "Software", } labels["ophthalmology"] = { Wiktionary = true, topical_categories = true, } labels["optics"] = { Wiktionary = true, topical_categories = true, } labels["organic chemistry"] = { Wiktionary = true, topical_categories = true, } labels["organic compound"] = { display = "[[organic chemistry]]", topical_categories = "Organic compounds", } labels["Oriental Orthodoxy"] = { aliases = {"Oriental Orthodox", "Oriental Orthodox Church"}, Wikipedia = true, topical_categories = true, } labels["ornithology"] = { Wiktionary = true, topical_categories = true, } labels["orthodontics"] = { Wiktionary = true, topical_categories = "Dentistry", } labels["orthography"] = { Wiktionary = true, topical_categories = true, } labels["paganism"] = { aliases = {"pagan", "neopagan", "neopaganism", "neo-pagan", "neo-paganism"}, Wiktionary = true, topical_categories = true, } labels["pain"] = { display = "[[medicine]]", topical_categories = true, } labels["paintball"] = { Wiktionary = true, topical_categories = true, } labels["painting"] = { Wiktionary = "painting#Noun", topical_categories = true, } labels["Pakistani politics"] = { Wikipedia = "Politics of Pakistan", topical_categories = true, } labels["palaeography"] = { aliases = {"paleography"}, Wiktionary = true, topical_categories = true, } labels["paleontology"] = { aliases = {"palaeontology"}, Wiktionary = true, topical_categories = true, } labels["Palestinian politics"] = { aliases = {"Palestine politics"}, Wikipedia = "Politics of the Palestinian National Authority", topical_categories = true, } labels["palmistry"] = { Wiktionary = true, topical_categories = true, } labels["palynology"] = { Wiktionary = true, topical_categories = true, } labels["papermaking"] = { Wiktionary = true, topical_categories = true, } labels["paraphilia"] = { aliases = {"paraphilias", "paraphilic", "fetish", "fetishes", "fetishism", "fetishistic", "fetishization", "fetishisation"}, Wiktionary = "paraphilia#Noun", topical_categories = "Paraphilias", } labels["parapsychology"] = { Wiktionary = true, topical_categories = true, } labels["parasitology"] = { Wiktionary = true, topical_categories = true, } labels["part of speech"] = { aliases = {"PoS"}, display = "[[grammar]]", topical_categories = "Parts of speech", } labels["particle"] = { aliases = {"subatomic particle", "subatomic particles"}, display = "[[particle physics]]", topical_categories = "Subatomic particles", } labels["particle physics"] = { Wiktionary = true, topical_categories = true, } labels["pasteurization"] = { aliases = {"pasteurisation"}, Wiktionary = true, topical_categories = true, } labels["patent law"] = { aliases = {"patents"}, display = "[[patent#Noun|patent]] [[law]]", topical_categories = true, } labels["pathology"] = { Wiktionary = true, topical_categories = true, } labels["pensions"] = { aliases = {"pension"}, display = "[[pension]]s", topical_categories = true, } labels["percussion instruments"] = { aliases = {"percussion instrument"}, display = "[[music]]", topical_categories = true, } labels["perfumery"] = { Wiktionary = true, topical_categories = true, } labels["Peruvian politics"] = { Wikipedia = "Politics of Peru", topical_categories = true, } labels["pesäpallo"] = { aliases = {"pesapallo"}, Wiktionary = true, topical_categories = true, } labels["petrochemistry"] = { Wiktionary = true, topical_categories = true, } labels["petrology"] = { Wiktionary = true, topical_categories = true, } labels["pharmaceutical drug"] = { display = "[[pharmacology]]", topical_categories = "Pharmaceutical drugs", } labels["pharmaceutical effect"] = { display = "[[pharmacology]]", topical_categories = "Pharmaceutical effects", } labels["pharmacology"] = { Wiktionary = true, topical_categories = true, } labels["pharmacy"] = { Wiktionary = true, topical_categories = true, } labels["pharyngology"] = { Wiktionary = true, topical_categories = true, } labels["philately"] = { Wiktionary = true, topical_categories = true, } labels["Philippine politics"] = { aliases = {"Filipino politics"}, Wikipedia = "Politics of the Philippines", topical_categories = true, } labels["Philmont Scout Ranch"] = { aliases = {"Philmont"}, Wikipedia = true, topical_categories = true, } labels["philosophy"] = { Wiktionary = true, topical_categories = true, } labels["phonetics"] = { aliases = {"phonetic"}, Wiktionary = true, topical_categories = true, } labels["phonology"] = { aliases = {"phonological"}, Wiktionary = true, topical_categories = true, } labels["photography"] = { aliases = {"photograph"}, Wiktionary = true, topical_categories = true, } labels["phrenology"] = { Wiktionary = true, topical_categories = true, } labels["phycology"] = { Wiktionary = true, topical_categories = true, } labels["physical chemistry"] = { Wiktionary = true, topical_categories = true, } labels["physical quantity"] = { display = "[[physics]]", topical_categories = "Physical quantities", } labels["physics"] = { Wiktionary = true, topical_categories = true, } labels["physiology"] = { Wiktionary = true, topical_categories = true, } labels["phytopathology"] = { Wiktionary = true, topical_categories = true, } labels["pinball"] = { Wiktionary = true, topical_categories = true, } labels["planetology"] = { Wiktionary = true, topical_categories = true, } labels["plant"] = { aliases = {"plants"}, display = "[[botany]]", topical_categories = "Plants", } labels["plant disease"] = { aliases = {"plant diseases"}, display = "[[phytopathology]]", topical_categories = "Plant diseases", } labels["plastic surgery"] = { Wiktionary = true, topical_categories = true, } labels["playground games"] = { aliases = {"playground game"}, display = "[[playground]] [[game]]s", topical_categories = true, } labels["poetry"] = { Wiktionary = true, topical_categories = true, } labels["poison"] = { display = "[[toxicology]]", topical_categories = "Poisons", } labels["Pokémon"] = { aliases = {"Pokemon"}, display = "''[[w:Pokémon|Pokémon]]''", Wikipedia = true, topical_categories = true, } labels["poker"] = { Wiktionary = true, topical_categories = true, } labels["poker slang"] = { display = "[[poker]] [[slang]]", topical_categories = "Poker", } labels["political science"] = { Wiktionary = true, topical_categories = true, } labels["political subdivision"] = { display = "[[government]]", topical_categories = "Political subdivisions", } labels["politics"] = { aliases = {"political"}, Wiktionary = true, topical_categories = true, } labels["pornography"] = { aliases = {"porn", "porno", "adult video", "adult videos"}, Wiktionary = true, topical_categories = true, } labels["Portuguese folklore"] = { display = "[[Portuguese#Adjective|Portuguese]] [[folklore]]", topical_categories = "European folklore", } labels["Portuguese politics"] = { Wikipedia = "Politics of Portugal", topical_categories = true, } labels["post"] = { aliases = {"mail", "postal"}, display = "[[postal]]", topical_categories = true, } labels["postal abbreviation"] = { aliases = {"postal abbr", "postal abbrev"}, display = "[[postal]]", topical_categories = "Postal abbreviations", } labels["potential theory"] = { Wiktionary = true, topical_categories = true, } labels["pottery"] = { Wiktionary = true, topical_categories = "Ceramics", } labels["pragmatics"] = { Wiktionary = true, topical_categories = true, } labels["printing"] = { Wiktionary = "printing#Noun", topical_categories = true, } labels["probability theory"] = { Wiktionary = true, topical_categories = true, } labels["professional wrestling"] = { aliases = {"pro wrestling"}, Wiktionary = true, topical_categories = true, } labels["programming"] = { aliases = {"computer programming"}, Wiktionary = "programming#Noun", topical_categories = true, } labels["property law"] = { aliases = {"land law", "real estate law"}, Wiktionary = true, topical_categories = true, } labels["prosody"] = { Wiktionary = true, topical_categories = true, } labels["protein"] = { aliases = {"proteins"}, display = "[[biochemistry]]", topical_categories = "Proteins", } labels["Protestantism"] = { aliases = {"protestantism", "Protestant", "protestant"}, Wiktionary = true, topical_categories = true, } labels["pseudoscience"] = { Wiktionary = true, topical_categories = true, } labels["psychiatry"] = { Wiktionary = true, topical_categories = true, } labels["psychoanalysis"] = { Wiktionary = true, topical_categories = true, } labels["psychology"] = { Wiktionary = true, topical_categories = true, } labels["psychotherapy"] = { Wiktionary = true, topical_categories = true, } labels["publishing"] = { Wiktionary = "publishing#Noun", topical_categories = true, } labels["pulmonology"] = { Wiktionary = true, topical_categories = true, } labels["pyrotechnics"] = { Wiktionary = true, aliases = { "firework", "fireworks" }, topical_categories = true, } labels["QAnon"] = { aliases = {"Qanon"}, Wikipedia = true, topical_categories = true, } labels["Quakerism"] = { Wiktionary = true, topical_categories = true, } labels["quantum computing"] = { Wiktionary = true, topical_categories = true, } labels["quantum mechanics"] = { aliases = {"quantum physics"}, Wiktionary = true, topical_categories = true, } labels["Quimbanda"] = { Wiktionary = true, topical_categories = true, } labels["radiation"] = { -- TODO: What kind of topic is "radiation"? Is it specific kinds of radiation? That would be a set-type category. display = "[[physics]]", topical_categories = true, } labels["radio"] = { Wiktionary = true, topical_categories = true, } labels["Raëlism"] = { Wiktionary = true, topical_categories = true, } labels["rail transport"] = { aliases = {"rail", "railroading", "railroads"}, Wiktionary = true, topical_categories = "Rail transportation", } labels["Rastafari"] = { aliases = {"Rasta", "rasta", "Rastafarian", "rastafarian", "Rastafarianism"}, Wiktionary = true, topical_categories = true, } labels["real estate"] = { Wiktionary = true, topical_categories = true, } labels["real tennis"] = { Wiktionary = true, topical_categories = "Tennis", } labels["recreational mathematics"] = { Wiktionary = true, topical_categories = "Mathematics", } labels["Reddit"] = { Wiktionary = true, topical_categories = true, } labels["regular expressions"] = { aliases = {"regex"}, display = "[[regular expression]]s", topical_categories = true, } labels["relativity"] = { Wiktionary = true, topical_categories = true, } labels["religion"] = { Wiktionary = true, topical_categories = true, } labels["rhetoric"] = { Wiktionary = true, topical_categories = true, } labels["rhythmic gymnastics"] = { Wiktionary = true, Wikipedia = true, topical_categories = true, } labels["road transport"] = { aliases = {"roads"}, Wikipedia = true, topical_categories = true, } labels["robotics"] = { Wiktionary = true, topical_categories = true, } labels["rock"] = { aliases = {"rocks"}, display = "[[geology]]", topical_categories = "Rocks", } labels["rock paper scissors"] = { topical_categories = true, } labels["rocketry"] = { Wiktionary = true, topical_categories = true, } labels["roleplaying games"] = { aliases = {"role playing games", "role-playing games", "RPG", "RPGs"}, display = "[[roleplaying game]]s", topical_categories = "Role-playing games", } labels["roller derby"] = { Wiktionary = true, topical_categories = true, } labels["Roman Catholicism"] = { aliases = {"Roman Catholic", "Roman Catholic Church"}, Wiktionary = true, topical_categories = true, } labels["Roman Empire"] = { Wiktionary = true, topical_categories = true, } labels["Roman god"] = { aliases = {"Roman goddess", "Roman deity"}, display = "[[Roman]] [[mythology]]", topical_categories = "Roman deities", } labels["Roman mythology"] = { display = "[[Roman]] [[mythology]]", topical_categories = true, } labels["Roman numerals"] = { display = "[[Roman numeral]]s", topical_categories = true, } labels["roofing"] = { Wiktionary = "roofing#Noun", topical_categories = true, } labels["rosiculture"] = { Wiktionary = true, topical_categories = true, } labels["rowing"] = { Wiktionary = "rowing#Noun", topical_categories = true, } labels["Rubik's Cube"] = { aliases = {"Rubik's cube", "Rubik's cubes", "Magic Cube", "magic cube"}, Wiktionary = "Rubik's cube", topical_categories = true, } labels["rugby"] = { Wiktionary = true, topical_categories = true, } labels["rugby league"] = { Wiktionary = true, topical_categories = true, } labels["rugby union"] = { Wiktionary = true, topical_categories = true, } labels["Russian Orthodoxy"] = { aliases = {"Russian Orthodox", "Russian Orthodox Church"}, Wikipedia = true, topical_categories = true, } labels["Russian space program"] = { aliases = {"Soviet space program", "Roscosmos"}, Wikipedia = true, topical_categories = true, } labels["sailing"] = { Wiktionary = "sailing#Noun", topical_categories = true, } labels["schools"] = { display = "[[education]]", topical_categories = true, } labels["science fiction"] = { aliases = {"scifi", "sci fi", "sci-fi"}, Wiktionary = true, topical_categories = true, } labels["sciences"] = { display = "[[sciences#English: all fields|sciences]]", aliases = {"science", "scientific"}, Wiktionary = true, topical_categories = true, } labels["Scientology"] = { Wiktionary = true, topical_categories = true, } labels["Scots law"] = { -- Note: this is the usual term, not "Scottish law". aliases = {"Scottish law", "Scotland law", "Scots Law", "Scottish Law", "Scotland Law"}, Wikipedia = true, topical_categories = true, } labels["Scouting"] = { aliases = {"scouting"}, display = "[[scouting]]", topical_categories = true, } labels["Scrabble"] = { display = "''[[Scrabble]]''", Wikipedia = true, topical_categories = true, } labels["scrapbooks"] = { display = "[[scrapbook]]s", topical_categories = true, } labels["sculpture"] = { Wiktionary = true, topical_categories = true, } labels["seduction community"] = { aliases = {"pickup artist", "pickup artists", "pickup artistry", "pickup community"}, Wikipedia = true, topical_categories = true, } labels["seismology"] = { Wiktionary = true, topical_categories = true, } labels["self-harm"] = { aliases = {"selfharm", "self harm", "self-harm community"}, Wiktionary = true, topical_categories = true, } labels["semantics"] = { Wiktionary = true, topical_categories = true, } labels["semiconductors"] = { display = "[[semiconductor]]s", topical_categories = true, } labels["semiotics"] = { Wiktionary = true, topical_categories = true, } labels["SEO"] = { Wiktionary = "search engine optimization", topical_categories = {"Internet", "Marketing"}, } labels["set theory"] = { Wiktionary = true, topical_categories = true, } labels["sewing"] = { Wiktionary = "sewing#Noun", topical_categories = true, } labels["sex"] = { Wiktionary = true, topical_categories = true, } labels["sex position"] = { display = "[[sex]]", topical_categories = "Sex positions", } labels["sexology"] = { Wiktionary = true, topical_categories = true, } labels["sexuality"] = { Wiktionary = true, topical_categories = true, } labels["Shaivism"] = { Wiktionary = true, topical_categories = true, } labels["shamanism"] = { aliases = {"Shamanism"}, Wiktionary = true, topical_categories = true, } labels["Shi'ism"] = { aliases = {"Shia", "Shi'ite", "Shi'i"}, display = "[[Shia Islam]]", topical_categories = true, } labels["Shinto"] = { Wiktionary = true, topical_categories = true, } labels["ship parts"] = { display = "[[nautical]]", topical_categories = "Ship parts", } labels["shipping"] = { Wiktionary = "shipping#Noun", topical_categories = true, } labels["shoemaking"] = { Wiktionary = true, topical_categories = true, } labels["shogi"] = { Wiktionary = true, topical_categories = true, } labels["signal processing"] = { Wikipedia = true, topical_categories = true, } labels["Sikhism"] = { aliases = {"Sikh"}, Wiktionary = true, topical_categories = true, } labels["Singaporean politics"] = { Wikipedia = "Politics of Singapore", topical_categories = true, } labels["singing"] = { Wiktionary = "singing#Noun", topical_categories = true, } labels["skateboarding"] = { Wiktionary = "skateboarding#Noun", topical_categories = true, } labels["skating"] = { Wiktionary = "skating#Noun", topical_categories = true, } labels["skeleton"] = { display = "[[anatomy]]", topical_categories = true, } labels["skiing"] = { Wiktionary = "skiing#Noun", topical_categories = true, } labels["skydiving"] = { Wiktionary = "skydiving#Noun", topical_categories = true, } labels["Slavic god"] = { display = "[[Slavic]] [[mythology]]", topical_categories = "Slavic deities", } labels["Slavic mythology"] = { display = "[[Slavic]] [[mythology]]", topical_categories = true, } labels["smoking"] = { Wiktionary = "smoking#Noun", topical_categories = true, } labels["snooker"] = { Wiktionary = "snooker#Noun", topical_categories = true, } labels["snowboarding"] = { Wiktionary = "snowboarding#Noun", topical_categories = true, } labels["soccer"] = { aliases = {"football", "association football"}, Wiktionary = true, topical_categories = "Football (soccer)", } labels["social media"] = { Wiktionary = true, topical_categories = true, } labels["social sciences"] = { aliases = {"social science"}, display = "[[social science]]s", topical_categories = true, } labels["socialism"] = { Wiktionary = true, topical_categories = true, } labels["sociolinguistics"] = { Wiktionary = true, topical_categories = true, } labels["sociology"] = { Wiktionary = true, topical_categories = true, } labels["softball"] = { Wiktionary = true, topical_categories = true, } labels["software"] = { Wiktionary = true, topical_categories = true, } labels["software architecture"] = { Wiktionary = true, topical_categories = {"Software engineering", "Programming"}, } labels["software engineering"] = { aliases = {"software development"}, Wiktionary = true, topical_categories = true, } labels["soil science"] = { Wiktionary = true, topical_categories = true, } labels["sorting algorithms"] = { aliases = {"sorting algorithm"}, display = "[[computer science]]", topical_categories = true, } labels["sound"] = { Wiktionary = "sound#Noun", topical_categories = true, } labels["sound engineering"] = { Wiktionary = true, topical_categories = true, } labels["South Korean idol fandom"] = { aliases = {"Korean idol fandom", "Korean idol"}, display = "[[South Korean]] [[idol]] [[fandom]]", topical_categories = true, } labels["South Park"] = { display = "''[[w:South Park|South Park]]''", Wikipedia = true, topical_categories = true, } labels["Soviet Union"] = { aliases = {"USSR", "Soviet"}, Wiktionary = true, topical_categories = true, } labels["space"] = { Wiktionary = true, topical_categories = true, } labels["space flight"] = { aliases = {"astronautics", "spaceflight", "space travel"}, Wiktionary = true, topical_categories = "Space flight", } labels["space industry"] = { Wikipedia = true, topical_categories = true, } labels["space science"] = { Wiktionary = true, topical_categories = true, } labels["spacecraft"] = { aliases = {"space craft"}, display = "[[space flight]]", Wiktionary = true, topical_categories = true, } labels["spacecraft components"] = { aliases = {"spacecraft component"}, display = "[[spacecraft]]", topical_categories = true, } labels["Spanish politics"] = { Wikipedia = "Politics of Spain", topical_categories = true, } labels["spectroscopy"] = { Wiktionary = true, topical_categories = true, } labels["speedrunning"] = { aliases = {"speedrun", "speedruns"}, Wiktionary = true, topical_categories = true, } labels["spinning"] = { Wiktionary = true, topical_categories = true, } labels["spiritualism"] = { Wiktionary = true, topical_categories = true, } labels["sports"] = { aliases = {"sport"}, Wiktionary = true, topical_categories = true, } labels["square dancing"] = { aliases = {"square dance"}, Wiktionary = true, topical_categories = true, } labels["squash"] = { Wikipedia = "Squash (sport)", topical_categories = true, } labels["standard of identity"] = { display = "[[standard of identity|standards of identity]]", topical_categories = "Standards of identity", } labels["star"] = { display = "[[astronomy]]", topical_categories = "Stars", } labels["Star Wars"] = { display = "''[[Star Wars]]''", topical_categories = true, } labels["statistical mechanics"] = { Wiktionary = true, topical_categories = true, } labels["statistics"] = { Wiktionary = true, topical_categories = true, } labels["steroid"] = { display = "[[biochemistry]]", topical_categories = "Steroids", } labels["steroid hormone"] = { aliases = {"steroid drug"}, display = "[[biochemistry]], [[steroids]]", topical_categories = "Hormones", } labels["stock market"] = { Wiktionary = true, topical_categories = true, } labels["stock ticker symbol"] = { aliases = {"stock symbol"}, Wiktionary = true, topical_categories = "Stock symbols for companies", } labels["string instruments"] = { aliases = {"string instrument"}, display = "[[music]]", topical_categories = true, } labels["subculture"] = { Wiktionary = true, topical_categories = "Culture", } labels["Sufism"] = { aliases = {"Sufi Islam"}, Wikipedia = true, topical_categories = true, } labels["sugar acid"] = { display = "[[organic chemistry]]", topical_categories = "Sugar acids", } labels["sumo"] = { Wiktionary = true, topical_categories = true, } labels["supply chain"] = { Wiktionary = true, topical_categories = true, } labels["surface feature"] = { display = "[[planetology]]", topical_categories = "Planetary nomenclature", } labels["surfing"] = { aliases = {"surf"}, Wiktionary = "surfing#Noun", topical_categories = true, } labels["surgery"] = { Wiktionary = true, topical_categories = true, } labels["surveying"] = { Wiktionary = "surveying#Noun", topical_categories = true, } labels["sushi"] = { Wiktionary = true, topical_categories = true, } labels["swimming"] = { Wiktionary = "swimming#Noun", topical_categories = true, } labels["Swiss politics"] = { Wikipedia = "Politics of Switzerland", topical_categories = true, } labels["swords"] = { display = "[[sword]]s", topical_categories = true, } labels["syntax"] = { Wiktionary = true, topical_categories = true, } labels["Syriac Orthodoxy"] = { aliases = {"Syriac Orthodox", "Syriac Orthodox Church"}, Wikipedia = true, topical_categories = true, } labels["systematic chemical element symbol"] = { display = "[[chemistry]]", plain_categories = "Systematic chemical element symbols", } labels["systematics"] = { Wiktionary = true, topical_categories = "Taxonomy", } labels["systems engineering"] = { Wiktionary = true, topical_categories = true, } labels["systems theory"] = { Wiktionary = true, topical_categories = true, } labels["table tennis"] = { Wiktionary = true, topical_categories = true, } labels["Taoism"] = { aliases = {"Daoism"}, Wiktionary = true, topical_categories = true, } labels["tarot"] = { Wiktionary = true, topical_categories = "Cartomancy", } labels["taxation"] = { aliases = {"tax", "taxes"}, Wiktionary = true, topical_categories = true, } labels["taxonomic name"] = { display = "[[taxonomy]]", topical_categories = "Taxonomic names", } labels["taxonomy"] = { Wiktionary = true, topical_categories = true, } labels["technology"] = { Wiktionary = true, topical_categories = true, } labels["telecommunications"] = { aliases = {"telecommunication", "telecom"}, Wiktionary = true, topical_categories = true, } labels["telegraphy"] = { Wiktionary = true, topical_categories = true, } labels["telephony"] = { aliases = {"telephone", "telephones"}, Wiktionary = true, topical_categories = true, } labels["television"] = { aliases = {"TV"}, Wiktionary = true, topical_categories = true, } labels["tennis"] = { Wiktionary = true, topical_categories = true, } labels["teratology"] = { Wiktionary = true, topical_categories = true, } labels["Tetris"] = { Wiktionary = true, topical_categories = true, } labels["textiles"] = { Wiktionary = true, topical_categories = true, } labels["textual criticism"] = { Wiktionary = true, topical_categories = true, } labels["theater"] = { aliases = {"theatre"}, Wiktionary = true, topical_categories = true, } labels["theology"] = { Wiktionary = true, topical_categories = true, } labels["thermodynamics"] = { Wiktionary = true, topical_categories = true, } labels["Thracian god"] = { aliases = {"Thracian goddess", "Thracian deity"}, display = "[[w:Thracian religion|Thracian religion]]", Wikipedia = "Thracian religion", topical_categories = "Thracian deities", } labels["Tibetan Buddhism"] = { Wiktionary = true, topical_categories = "Buddhism", } labels["tiddlywinks"] = { Wiktionary = true, topical_categories = true, } labels["TikTok aesthetic"] = { display = "[[TikTok]] aesthetic", topical_categories = "Aesthetics", } labels["timber industry"] = { aliases = {"timber", "wood industry", "lumber industry", "lumber", "logging"}, Wikipedia = true, topical_categories = true, } labels["time"] = { Wiktionary = true, topical_categories = true, } labels["tincture"] = { display = "[[heraldry]]", topical_categories = "Heraldic tinctures", } labels["topology"] = { Wiktionary = true, topical_categories = true, } labels["tort law"] = { Wiktionary = true, topical_categories = "Law", } labels["tourism"] = { aliases = {"tourist"}, Wiktionary = true, topical_categories = true, } labels["toxicology"] = { Wiktionary = true, topical_categories = true, } labels["trading"] = { aliases = {"trade"}, Wiktionary = "trading#Noun", topical_categories = true, } labels["trading cards"] = { display = "[[trading card]]s", topical_categories = true, } labels["traditional Chinese medicine"] = { aliases = {"TCM", "Chinese medicine"}, Wiktionary = true, topical_categories = true, } labels["traditional Korean medicine"] = { aliases = {"Korean medicine"}, topical_categories = true, } labels["transgender"] = { aliases = {"trans"}, Wiktionary = true, topical_categories = true, } labels["translation studies"] = { Wiktionary = true, topical_categories = true, } labels["transport"] = { aliases = {"transportation"}, Wiktionary = true, topical_categories = true, } labels["traumatology"] = { Wiktionary = true, topical_categories = "Emergency medicine", } labels["travel"] = { aliases = {"travelling"}, Wiktionary = true, topical_categories = true, } labels["trigonometric function"] = { display = "[[trigonometry]]", topical_categories = "Trigonometric functions", } labels["trigonometry"] = { Wiktionary = true, topical_categories = true, } labels["trust law"] = { Wiktionary = true, topical_categories = "Law", } labels["Tumblr aesthetic"] = { display = "[[Tumblr]] [[aesthetic]]", topical_categories = "Aesthetics", } labels["Twitter"] = { aliases = {"twitter", "X"}, Wiktionary = "Twitter#Proper noun", topical_categories = true, } labels["two-up"] = { Wiktionary = true, topical_categories = true, } labels["typography"] = { aliases = {"typesetting"}, Wiktionary = true, topical_categories = true, } labels["ufology"] = { Wiktionary = true, topical_categories = true, } labels["UK politics"] = { Wikipedia = "Politics of the United Kingdom", topical_categories = true, } labels["Umbanda"] = { Wiktionary = true, topical_categories = true, } labels["underwater diving"] = { aliases = {"scuba", "scuba diving"}, display = "[[underwater]] [[diving#Noun|diving]]", topical_categories = true, } labels["Unicode"] = { aliases = {"Unicode standard"}, Wikipedia = true, topical_categories = true, } labels["United Nations"] = { aliases = {"UN"}, display = "[[United Nations|UN]]", Wikipedia = true, topical_categories = true, } labels["Unix"] = { Wiktionary = true, topical_categories = true, } labels["urban studies"] = { aliases = {"urbanism", "urban planning"}, Wiktionary = true, topical_categories = true, } labels["urology"] = { Wiktionary = true, topical_categories = true, } labels["US politics"] = { Wikipedia = "Politics of the United States", topical_categories = true, } labels["US space program"] = { aliases = {"NASA"}, Wikipedia = true, topical_categories = true, } labels["Usenet"] = { aliases = {"newsgroup"}, Wiktionary = true, topical_categories = true, } labels["Vaishnavism"] = { aliases = {"Vaishnavist"}, Wiktionary = true, topical_categories = true, } labels["Valentinianism"] = { aliases = {"valentinianism", "Valentinianist", "valentinianist"}, Wikipedia = true, topical_categories = true, } labels["Vedic religion"] = { aliases = {"Vedic Hinduism", "Vedism", "Vedicism", "Ancient Hinduism", "ancient Hinduism"}, Wikipedia = "Historical Vedic religion", topical_categories = true, } labels["vegetable"] = { aliases = {"vegetables"}, Wiktionary = true, topical_categories = "Vegetables", } labels["vehicles"] = { aliases = {"vehicle"}, display = "[[vehicle]]s", topical_categories = true, } labels["Venezuelan politics"] = { aliases = {"Venezuela politics"}, Wikipedia = "Politics of Venezuela", topical_categories = true, } labels["veterinary disease"] = { display = "[[veterinary medicine]]", topical_categories = "Veterinary diseases", } labels["veterinary medicine"] = { aliases = {"veterinary", "vet"}, Wiktionary = true, topical_categories = true, } labels["video compression"] = { Wikipedia = true, topical_categories = true, } labels["video game genre"] = { display = "[[video game]]s", topical_categories = "Video game genres", } labels["video games"] = { aliases = {"video game", "video gaming"}, display = "[[video game]]s", topical_categories = true, } labels["virology"] = { Wiktionary = true, topical_categories = true, } labels["virus"] = { display = "[[virology]]", topical_categories = "Viruses", } labels["vitamin"] = { display = "[[biochemistry]]", topical_categories = "Vitamins", } labels["viticulture"] = { Wiktionary = true, topical_categories = {"Horticulture", "Wine"}, } labels["volcanology"] = { aliases = {"vulcanology"}, Wiktionary = true, topical_categories = true, } labels["volleyball"] = { Wiktionary = true, topical_categories = true, } labels["voodoo"] = { Wiktionary = true, topical_categories = true, } labels["VTuber"] = { aliases = {"Virtual YouTuber"}, Wiktionary = true, topical_categories = "Virtual YouTuber", } labels["war"] = { aliases = {"warfare"}, Wiktionary = true, topical_categories = true, } labels["water sports"] = { aliases = {"watersport", "watersports", "water sport"}, Wiktionary = "watersport", topical_categories = true, } labels["watercraft"] = { display = "[[nautical]]", topical_categories = true, } labels["weaponry"] = { aliases = {"weapon", "weapons"}, Wiktionary = true, topical_categories = "Weapons", } labels["weather"] = { topical_categories = true, } labels["weaving"] = { Wiktionary = "weaving#Noun", topical_categories = true, } labels["web design"] = { Wiktionary = true, topical_categories = true, aliases = {"Web design"} } labels["web development"] = { Wiktionary = true, topical_categories = {"Programming", "Web design"}, } labels["weightlifting"] = { Wiktionary = true, topical_categories = true, } labels["white supremacy"] = { -- Often used to indicate Nazi-used jargon; compare "neo-Nazism" aliases = {"white nationalism", "white nationalist", "white power", "white racism", "white supremacist ideology", "white supremacism", "white supremacist"}, Wikipedia = true, topical_categories = "White supremacist ideology", } labels["Wicca"] = { Wiktionary = true, topical_categories = true, } labels["wiki jargon"] = { aliases = {"wiki", "wikis"}, display = "[[wiki]] [[jargon]]", topical_categories = "Wiki", } labels["Wikimedia jargon"] = { aliases = { "Wikimedia", "Wiktionary", "Wiktionary jargon", "Wikipedia", "Wikipedia jargon", "WMF", "WMF jargon" -- technically not correct }, display = "[[w:Wikimedia movement|Wikimedia]] [[jargon]]", topical_categories = "Wikimedia", } labels["wind instruments"] = { aliases = {"wind instrument"}, display = "[[music]]", topical_categories = true, } labels["wine"] = { aliases = {"oenology", "winemaking"}, Wiktionary = true, topical_categories = true, } labels["winter sports"] = { display = "[[winter sport]]s", topical_categories = true, } labels["woodwind instruments"] = { aliases = {"woodwind instrument"}, display = "[[music]]", topical_categories = true, } labels["woodworking"] = { Wiktionary = true, topical_categories = true, } labels["World War I"] = { aliases = {"World War 1", "WWI", "WW I", "WW1", "WW 1"}, Wikipedia = true, topical_categories = true, } labels["World War II"] = { aliases = {"World War 2", "WWII", "WW II", "WW2", "WW 2"}, Wikipedia = true, topical_categories = true, } labels["wrestling"] = { Wiktionary = "wrestling#Noun", topical_categories = true, } labels["writing"] = { Wiktionary = "writing#Noun", topical_categories = true, } labels["xiangqi"] = { aliases = {"Chinese chess"}, Wiktionary = true, topical_categories = true, } labels["Yazidism"] = { aliases = {"Yezidism"}, Wiktionary = true, topical_categories = true, } labels["yoga"] = { Wiktionary = true, topical_categories = true, } labels["yoga pose"] = { aliases = {"asana"}, display = "[[yoga]]", topical_categories = "Yoga poses", } labels["zodiac constellations"] = { display = "[[astronomy]]", topical_categories = "Constellations in the zodiac", } labels["zoology"] = { Wiktionary = true, topical_categories = true, } labels["zootomy"] = { Wiktionary = true, topical_categories = "Animal body parts", } labels["Zoroastrianism"] = { Wiktionary = true, topical_categories = true, } -- Deprecated/do not use warning (ambiguous, unsuitable etc) labels["deprecated label"] = { aliases = {"emergency", "greekmyth", "industry", "morphology", "musici", "quantum", "vector"}, display = "<span style=\"color:var(--wikt-palette-red,red);\"><b>deprecated label</b></span>", deprecated = true, } return require("Module:labels").finalize_data(labels) nsu97upae3m1op5c4wmnz78e1hmnrn4 throw 0 8154 55060 2026-09-30T22:19:46Z Deadend0914 7211 Gesceop tramet þe hafaþ '=={{sprǣc|en}}== ===Rihtstefn=== * IPA: /θɹəʊ/ ===Wordstǣr=== {{inh+|en|enm|throwen}}, of {{inh|en|ang|þrāwan}}, of{{inh|en|gmw-pro|*þrāan}}, of{{inh|en|gem-pro|*þrēaną}}, of {{der|en|ine-pro|*terh₁-}}. ===Word=== {{en-verb|+|+|threw,~ed<l:unregol>|n,~ed<l:unregol>,threw<l:unregol>}}' 55060 wikitext text/x-wiki =={{sprǣc|en}}== ===Rihtstefn=== * IPA: /θɹəʊ/ ===Wordstǣr=== {{inh+|en|enm|throwen}}, of {{inh|en|ang|þrāwan}}, of{{inh|en|gmw-pro|*þrāan}}, of{{inh|en|gem-pro|*þrēaną}}, of {{der|en|ine-pro|*terh₁-}}. ===Word=== {{en-verb|+|+|threw,~ed<l:unregol>|n,~ed<l:unregol>,threw<l:unregol>}} 9yxj39d3n47c1hxtad26c17yxdsg2x6 55061 55060 2026-09-30T22:20:22Z Deadend0914 7211 /* Word */ 55061 wikitext text/x-wiki =={{sprǣc|en}}== ===Rihtstefn=== * IPA: /θɹəʊ/ ===Wordstǣr=== {{inh+|en|enm|throwen}}, of {{inh|en|ang|þrāwan}}, of{{inh|en|gmw-pro|*þrāan}}, of{{inh|en|gem-pro|*þrēaną}}, of {{der|en|ine-pro|*terh₁-}}. ===Word=== {{en-verb|+|+|threw,~ed<l:unregol>|n,~ed<l:unregol>,threw<l:unregol>}} # tō weorpenne davpqdj8nh7vut1y54l520fnl7r5tzj 55062 55061 2026-09-30T22:21:24Z Deadend0914 7211 /* Word */ 55062 wikitext text/x-wiki =={{sprǣc|en}}== ===Rihtstefn=== * IPA: /θɹəʊ/ ===Wordstǣr=== {{inh+|en|enm|throwen}}, of {{inh|en|ang|þrāwan}}, of{{inh|en|gmw-pro|*þrāan}}, of{{inh|en|gem-pro|*þrēaną}}, of {{der|en|ine-pro|*terh₁-}}. ===Word=== {{en-verb|+|+|threw,~ed<l:unregol>|n,~ed<l:unregol>,threw<l:unregol>}} # tō [[weorpan|weorpenne]] o9mxbcdahcv83e5wqv97rpcnaufce1b 55063 55062 2026-09-30T22:25:21Z Deadend0914 7211 /* Wordstǣr */ 55063 wikitext text/x-wiki =={{sprǣc|en}}== ===Rihtstefn=== * IPA: /θɹəʊ/ ===Wordstǣr=== {{inh+|en|enm|throwen}}, of {{inh|en|ang|þrāwan}}, of {{inh|en|gmw-pro|*þrāan}}, of {{inh|en|gem-pro|*þrēaną}}, of {{der|en|ine-pro|*terh₁-}}. ===Word=== {{en-verb|+|+|threw,~ed<l:unregol>|n,~ed<l:unregol>,threw<l:unregol>}} # tō [[weorpan|weorpenne]] p5h6h7xmautovjjntiv0pa6yj1torzv Bysen:desctree 10 8155 55064 2026-09-30T22:31:38Z Deadend0914 7211 Redirected page to [[Bysen:descendants tree]] 55064 wikitext text/x-wiki #REDIRECT [[Template:descendants tree]] b11sln24gbntbu621khvtp67ff4tuv0 Bysen:descendants tree 10 8156 55065 2026-09-30T22:31:55Z Deadend0914 7211 Gesceop tramet þe hafaþ '{{#invoke:etymology/templates/descendant|descendants_tree}}<noinclude>{{documentation}}</noinclude>' 55065 wikitext text/x-wiki {{#invoke:etymology/templates/descendant|descendants_tree}}<noinclude>{{documentation}}</noinclude> jextvnsqr4try0jwxxcgzfnuubngjor Module:descendants tree 828 8157 55066 2026-09-30T22:32:16Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local debug_track_module = "Module:debug/track" local string_pattern_escape_module = "Module:string/patternEscape" local string_remove_comments_module = "Module:string/removeComments" local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end local function pattern_escape(...) pattern_escape = require(string_pattern_escape_module) return pattern_escape(...) end local function remove_comments(......' 55066 Scribunto text/plain local export = {} local debug_track_module = "Module:debug/track" local string_pattern_escape_module = "Module:string/patternEscape" local string_remove_comments_module = "Module:string/removeComments" local function debug_track(...) debug_track = require(debug_track_module) return debug_track(...) end local function pattern_escape(...) pattern_escape = require(string_pattern_escape_module) return pattern_escape(...) end local function remove_comments(...) remove_comments = require(string_remove_comments_module) return remove_comments(...) end local function track(page) --[[Special:WhatLinksHere/Wiktionary:Tracking/descendants tree/PAGE]] return debug_track("descendants tree/" .. page) end local function preview_error(what, entry_name, language_name, reason) mw.log("Could not retrieve " .. what .. " for " .. language_name .. " in the entry [[" .. entry_name .. "]]: " .. reason .. ".") track(what .. " error") end local function get_content_after_senseid(content, entry_name, lang, id) local code = lang:getFullCode() local t_start, t_end -- UGH. We need to set `not_transcluded` to true otherwise the template parser won't find all the templates -- on large pages. We should probably default `not_transcluded` to true in general. for template in require("Module:template parser").find_templates(content, true) do local name = template:get_name() if name == "senseid" then local args = template:get_arguments() if args[1] == code and args[2] == id then t_start = template.index end elseif name == "etymid" then local args = template:get_arguments() if args[1] == code and args[2] == id then t_start = template.index elseif t_start ~= nil and t_end == nil then t_end = template.index end elseif name == "head" or name == "etymon" then local args = template:get_arguments() local id_arg = args.id if args[1] == code and id_arg == id then t_start = template.index elseif id_arg ~= nil and t_start ~= nil and t_end == nil then t_end = template.index end end end if t_start == nil then error("Could not find the correct senseid template in the entry [[" .. entry_name .. "]] (with language " .. code .. " and id '" .. id .. "')") end if t_end == nil then -- terminate on L2 or another "Etymology ..." header -- match L2 and remove it and everything after it content = string.gsub(content:sub(t_start), "\n==[^=].+$", "") -- match Etymology header and remove it and everything after it content = string.gsub(content, "\n===+%s*Etymology.+$", "") return content end return content:sub(t_start, t_end) end function export.get_alternative_forms(lang, entry_name, id, default_separator) local page = mw.title.new(entry_name) local content = page:getContent() local function alt_form_error(reason) preview_error("alternative forms", entry_name, lang:getFullName(), reason) end if not content then -- FIXME, should be an error alt_form_error("nonexistent page") track("alts-nonexistent-page") return "" end local _, index = string.find(content, "==[ \t]*" .. pattern_escape(lang:getFullName()) .. "[ \t]*==") if not index then -- FIXME, should be an error alt_form_error("L2 header for language not found") track("alts-lang-not-found") return "" end if id then content = get_content_after_senseid(content, entry_name, lang, id) index = 0 end local _, next_lang = string.find(content, "\n==[^=\n]+==", index, false) local _, index = string.find(content, "\n(====?=?)[ \t]*Alternative forms[ \t]*%1", index, false) if not index then _, index = string.find(content, "\n(====?=?)[ \t]*Alternative reconstructions[ \t]*%1", index, false) end if not index then -- FIXME, should be an error alt_form_error("'Alternative forms' section for language not found") track("alts-section-not-found") return "" end local langCodeRegex = pattern_escape(lang:getFullCode()) index = string.find(content, "{{alt[ei]?r?|" .. langCodeRegex .. "|[^|}]+", index) if (not index) or (next_lang and next_lang < index) then -- FIXME, should be an error alt_form_error("no 'alt' or 'alter' template in 'Alternative forms' section for language") track("alts-alter-not-found") return "" end local next_section = string.find(content, "\n(=+)[^=]+%1", index) local alternative_forms_section = string.sub(content, index, next_section) local terms_list = {} local altforms = require("Module:alternative forms") for template in require("Module:template parser").find_templates(alternative_forms_section) do if template:get_name() == "alter" then local args = template:get_arguments() if args[1] == lang:getFullCode() then saw_alter = true local formatted_altforms = altforms.display_alternative_forms(args, entry_name, "allow self link", default_separator) table.insert(terms_list, formatted_altforms) end end end if #terms_list == 0 then -- FIXME, should be an error alt_form_error("no terms in 'alt' or 'alter' template in 'Alternative forms' section for language") track("alts-no-terms-in-alter") return "" end return table.concat(terms_list, default_separator or ", ") end function export.get_descendants(lang, entry_name, id, noerror) local page = mw.title.new(entry_name) local content = page:getContent() local namespace = mw.title.getCurrentTitle().nsText local function desc_error(reason) preview_error("descendants", entry_name, lang:getFullName(), reason) end if not content then -- FIXME, should be an error desc_error("nonexistent page") track("desctree-nonexistent-page") return "" end -- Ignore HTML comments, columns and blank lines. content = remove_comments(content) :gsub("{{top%d}}%s", "") :gsub("{{mid%d}}%s", "") :gsub("{{bottom}}%s", "") :gsub("\n?{{(desc?%-%l+)|?[^}]*}}", function (template_name) if template_name == "desc-top" or template_name == "desc-bottom" or template_name == "des-top" or template_name == "des-mid" or template_name == "des-bottom" then return "" end end) :gsub("\n%s*\n", "\n") local _, index = string.find(content, "%f[^\n%z]==[ \t]*" .. lang:getFullName() .. "[ \t]*==", nil, true) if not index then _, index = string.find(content, "%f[^\n%z]==[ \t]*" .. pattern_escape(lang:getFullName()) .. "[ \t]*==", nil, false) end if not index then desc_error("L2 header for language not found") -- FIXME, should be an error track("desctree-lang-not-found") return "" end if id then content = get_content_after_senseid(content, entry_name, lang, id) index = 0 end local _, next_lang = string.find(content, "\n==[^=\n]+==", index, false) local _, index = string.find(content, "\n(====*)[ \t]*Descendants[ \t]*%1", index, false) local function desctree_no_descendants(with_lang_in_error) if noerror and (namespace == "" or namespace == "Reconstruction") then track("desctree-no-descendants") return "<small class=\"error previewonly\">(" .. "Please either change this template to {{desc}} " .. "or insert a ====Descendants==== section in [[" .. entry_name .. "#" .. lang:getFullName() .. "]])</small>" .. "[[Category:" .. lang:getFullName() .. " descendants to be fixed in desctree]]" else error(("No Descendants section was found in the entry [[%s]]%s"):format(entry_name, with_lang_in_error and (" under the header for %s"):format(lang:getFullName()) or "")) end end if not index then return desctree_no_descendants() elseif next_lang and next_lang < index then return desctree_no_descendants("with lang in error") end -- Skip past final equals sign. index = index + 1 -- Skip past spaces or tabs. while true do local new_index = string.match(content, "^[ \t]+()", index) if not new_index then break end index = new_index end local items = require("Module:array")() local frame = mw.getCurrentFrame() local previous_list_markers = "" -- Skip paragraphs at beginning of Descendants section. while true do local new_index = content:match("^\n[^%*:=][^\n]*()", index) if not new_index then break else index = new_index end end previous_index = 1 -- Find a consecutive series of list items that begins directly after the -- Descendants header. -- start_index and previous_index are used to check that list items are -- consecutive. for start_index, list_markers, item, index in string.gmatch(content:sub(index), "()\n([%*:]+) *([^\n]+)()") do if start_index ~= previous_index then break end -- Preprocess, but replace recursive calls to avoid template loop errors item = string.gsub(item, "{{desctree|", "{{#invoke:etymology/templates/descendant|descendants_tree|") item = frame:preprocess(item) local difference = #list_markers - #previous_list_markers if difference > 0 then for i = #previous_list_markers + 1, #list_markers do items:insert(list_markers:sub(i, i) == "*" and "<ul>" or "<dl>") end else if difference < 0 then for i = #previous_list_markers, #list_markers + 1, -1 do items:insert(previous_list_markers:sub(i, i) == "*" and "</li></ul>" or "</dd></dl>") end else items:insert(previous_list_markers:sub(-1, -1) == "*" and "</li>" or "</dd>") end if previous_list_markers:sub(#list_markers, #list_markers) ~= list_markers:sub(-1, -1) then items:insert(list_markers:sub(-1, -1) == "*" and "</dl><ul>" or "</ul><dl>") end end items:insert(list_markers:sub(-1, -1) == "*" and "<li>" or "<dd>") items:insert(item) previous_list_markers = list_markers previous_index = index end for i = #previous_list_markers, 1, -1 do items:insert(previous_list_markers:sub(i, i) == "*" and "</li></ul>" or "</dd></dl>") end return items:concat() end return export pqx7k3asjvcuripstqlhxn72h8o44nv deaþ 0 8158 55068 2026-09-30T22:36:16Z Deadend0914 7211 Redirected page to [[dēaþ]] 55068 wikitext text/x-wiki #redirect [[dēaþ]] 9s5nd4r5sf98jqqhibofrvaxtrn8jog Bysen:desc 10 8159 55069 2026-09-30T22:37:32Z Deadend0914 7211 Redirected page to [[Bysen:descendant]] 55069 wikitext text/x-wiki #REDIRECT [[Bysen:descendant]] nnsut0yu5ftp7jpywh922jj3qus8whg Bysen:descendant 10 8160 55070 2026-09-30T22:37:48Z Deadend0914 7211 Gesceop tramet þe hafaþ '{{#invoke:etymology/templates/descendant|descendant}}<noinclude> {{documentation}} </noinclude>' 55070 wikitext text/x-wiki {{#invoke:etymology/templates/descendant|descendant}}<noinclude> {{documentation}} </noinclude> 5i762qwxh1fn2n8ve51agjddncrfsgo Module:TemplateStyles 828 8161 55071 2026-09-30T22:38:08Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local frame = mw.getCurrentFrame() local args = {} return function (stylesheet) args.src = stylesheet return frame:extensionTag("templatestyles", nil, args) end' 55071 Scribunto text/plain local frame = mw.getCurrentFrame() local args = {} return function (stylesheet) args.src = stylesheet return frame:extensionTag("templatestyles", nil, args) end fpxp4rqu6felstio94845c8h0qu6kw7 Module:languages/data/3/s 828 8162 55072 2026-09-30T22:38:43Z Deadend0914 7211 Gesceop tramet þe hafaþ '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["saa"] = { "Saba", 3914885, "cdc-est", "Latn", } m["sab"] = { "Buglere", 3368506, "cba", "Latn", } m["sac"] = { "Fox", 12714767, "alg-sfk", "...' 55072 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["saa"] = { "Saba", 3914885, "cdc-est", "Latn", } m["sab"] = { "Buglere", 3368506, "cba", "Latn", } m["sac"] = { "Fox", 12714767, "alg-sfk", "Latn", } m["sad"] = { "Sandawe", 34016, "qfa-iso", "Latn", } m["sae"] = { "Sabanê", 3460478, "sai-nmk", "Latn", } m["saf"] = { "Safaliba", 36432, "nic-mre", "Latn", } m["sah"] = { "Yakut", 34299, "trk-nsb", "Cyrl", translit = "sah-translit", override_translit = true, } m["saj"] = { "Sahu", 7399757, "paa-sah", "Latn", } m["sak"] = { "Sake", 36425, "bnt-kel", "Latn", } m["sam"] = { "Samaritan Aramaic", 56612, "sem-arw", "Samr", translit = "Samr-translit", -- Samr strip_diacritics, sort_key in [[Module:scripts/data]] } m["sao"] = { "Sause", 4409155, "qfa-dis", -- isolate per Glottolog, possibly related to Kapauri in a Kapauri-Sause family under Greater Kwerba "Latn", } m["saq"] = { "Samburu", 56536, "sdv-lma", } m["sar"] = { "Saraveca", 3450556, "awd", "Latn", } m["sas"] = { "Sasak", 1294047, "poz-bss", "Latn, Bali, Java", } m["sat"] = { "Santali", 33965, "mun", "Olck", translit = "Olck-translit", override_translit = true, } m["sau"] = { "Saleman", 7404262, "poz-cet", } m["sav"] = { "Saafi-Saafi", 36308, "alv-cng", "Arab, Latn", } m["saw"] = { "Sawi", 677064, "ngf-awd", "Latn", } m["sax"] = { "Sa", 3460352, "poz-vnn", "Latn", } m["say"] = { "Saya", 3914431, "cdc-wst", "Latn", } m["saz"] = { "Saurashtra", 13292, "inc-wes", "Saur, Latn, Taml, Deva", translit = "saz-translit", ancestors = "inc-ogu", } m["sba"] = { "Ngambay", 2372207, "csu-sar", "Latn", } m["sbb"] = { "Simbo", 3484101, "poz-ocw", } m["sbc"] = { "Gele'", 3194847, "poz-aay", "Latn", } m["sbd"] = { "Southern Samo", 33122730, "dmn-sam", "Latn", } m["sbe"] = { "Saliba (New Guinea)", 3469737, "poz-ocw", } m["sbf"] = { "Shabo", 36342, "ssa", "Latn", } m["sbg"] = { "Seget", 7446237, "paa-wbh", "Latn", } m["sbh"] = { "Sori-Harengan", 36515, "poz-aay", "Latn", } m["sbi"] = { "Seti", 7456682, "paa-trr", "Latn", } m["sbj"] = { "Surbakhal", 759995, } m["sbk"] = { "Safwa", 4121160, "bnt-mby", "Latn", } m["sbl"] = { "Botolan Sambal", 4095195, "phi", "Latn", } m["sbm"] = { "Sagala", 11732610, "bnt-ruv", "Latn", } m["sbn"] = { "Sindhi Bhil", 25559289, "inc-snd", "Arab, Deva, Sind, Guru", ancestors = "sd", } m["sbo"] = { "Sabüm", 7396535, "mkh-asl", } m["sbp"] = { "Sangu (Tanzania)", 7418149, "bnt-bki", "Latn", } m["sbq"] = { "Sirva", 7514337, "ngf-nso", "Latn", } m["sbr"] = { "Sembakung Murut", 7449148, "poz-san", } m["sbs"] = { "Subiya", 6442073, "bnt-bot", "Latn", } m["sbt"] = { "Kimki", 6410160, "qfa-dis", -- Papuan; Glottolog and Foley treat as an isolate, Usher puts in South Pauwasi "Latn", } m["sbu"] = { "Stod Bhoti", 15622700, "sit-las", } m["sbv"] = { "Sabine", 65455885, "itc-sbl", "Latn", display_text = s["itc-Latn-displaytext"], strip_diacritics = s["itc-Latn-stripdiacritics"], sort_key = s["itc-Latn-sortkey"], } m["sbw"] = { "Simba", 36430, "bnt-tso", "Latn", } m["sbx"] = { "Seberuang", 12473470, "poz-mly", } m["sby"] = { "Soli", 7557754, "bnt-bot", "Latn", } m["sbz"] = { "Sara Kaba", 25559318, "csu-kab", "Latn", } m["scb"] = { "Chut", 2967709, "mkh-vie", "Latn", } m["sce"] = { "Dongxiang", 32947, "xgn-shr", "Arab, Latn", } m["scf"] = { "San Miguel Creole French", 12953094, "crp", "Latn", ancestors = "gcf", sort_key = s["roa-oil-sortkey"], } m["scg"] = { "Sanggau", 12473466, "day", } m["sch"] = { "Sakachep", 37054, "tbq-kuk", } m["sci"] = { "Sri Lankan Creole Malay", 1089151, "crp", "Latn", ancestors = "ms", } m["sck"] = { "Sadri", 765922, "inc-sad", "Deva, Kthi", translit = { Deva = "hi-translit", Kthi = "bho-Kthi-translit", }, } m["scl"] = { "Shina", 1353320, "inc-shn", "Aran, Deva", } m["scn"] = { "Sicilian", 33973, "roa-itr", ancestors = "scn-old", "Latn", } m["sco"] = { "Scots", 14549, "gmw-ang", "Latn", ancestors = "gmw-msc", } m["scp"] = { "Yolmo", 22662107, "sit-kyk", "Deva", } m["scq"] = { "Sa'och", 6583617, "mkh-pea", } m["scs"] = { "North Slavey", 20628, "den", "Latn", } m["scu"] = { "Shumcho", 22077739, "sit-kin", } m["scv"] = { "Sheni", 11015820, "nic-jer", "Latn", ancestors = "zir", } m["scw"] = { "Sha", 3438816, "cdc-wst", "Latn", } m["scx"] = { "Sicel", 36667, "itc", "Polyt", -- Polyt translit, display_text, strip_diacritics, sort_key in [[Module:scripts/data]] } m["scz"] = { "Shetland", 3069598, "qfa-mix", "Latn", ancestors = "nrn, gmw-msc", standard_chars = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZzØøÖüÜü0123456789" .. c.punc, } m["sda"] = { "Toraja-Sa'dan", 36673, "poz-ssw", "Latn", } m["sdb"] = { "Shabak", 3289596, "ira-zgr", ancestors = "hac", } m["sdc"] = { "Sassarese", 845441, "roa-itr", "Latn", } m["sde"] = { "Surubu", 3913336, "nic-kau", "Latn", } m["sdf"] = { "Sarli", 7424256, "ira-zgr", ancestors = "hac", } m["sdg"] = { "Savi", 3474654, "inc-dng", } m["sdh"] = { "Southern Kurdish", 1496597, "ku", "Arab", translit = "sdh-translit", strip_diacritics = {remove_diacritics = c.kasra .. c.sukun}, } m["sdj"] = { "Suundi", 7650407, "bnt-kng", "Latn", } m["sdk"] = { "Sos Kundi", 7563811, "paa-nnd", "Latn", } m["sdl"] = { "Saudi Arabian Sign Language", 3504160, "sgn", } m["sdm"] = { "Semandang", 7449012, "day", } m["sdn"] = { "Gallurese", 612220, "roa-itr", "Latn", ancestors = "co", } m["sdo"] = { "Bukar-Sadung Bidayuh", 2927799, "day", "Latn", } m["sdp"] = { "Sherdukpen", 7494785, "sit-khm", } m["sdr"] = { "Oraon Sadri", 12953860, "inc-bih", } m["sds"] = { "Tunisian Berber", 5329732, "ber", "Latn", } m["sdu"] = { "Sarudu", 7424700, "poz-cet", } m["sdx"] = { "Sibu Melanau", 18642842, "poz-bnn", } m["sea"] = { "Semai", 3135426, "mkh-asl", "Latn", } -- seb is a duplicate code of spp m["sec"] = { "Sechelt", 7442898, "sal", "Latn", } m["sed"] = { "Sedang", 56448, "mkh-nbn", "Latn", } m["see"] = { "Seneca", 1185133, "iro-nor", "Latn", } m["sef"] = { "Cebaara", 10975121, "alv-snr", "Latn", } m["seg"] = { "Segeju", 17584599, "bnt-mij", "Latn", } m["seh"] = { "Sena", 2964008, "bnt-sna", "Latn", } m["sei"] = { "Seri", 36583, "qfa-iso", "Latn", } m["sej"] = { "Sene", 7450252, "ngf-ehu", "Latn", } m["sek"] = { "Sekani", 28562, "ath-nor", "Latn", } m["sen"] = { "Nanerige", 36002, "alv-sma", } m["seo"] = { "Asaba", 7630513, "qfa-dis", -- Papuan; isolate or unclassified in Glottolog; Sepik language in Foley (2018) "Latn", } m["sep"] = { "Sicite", 56787, "alv-sma", } m["seq"] = { "Senara", -- not to be confused with Syenara (shz) 35210, "alv-snr", } m["ser"] = { "Serrano", 3479942, "azc-tak", "Latn", } m["ses"] = { "Koyraboro Senni", 35655, "son", "Latn", } m["set"] = { "Sentani", 3441672, "paa-sen", "Latn", } m["seu"] = { "Serui-Laut", 7455503, "poz-hce", "Latn", } m["sev"] = { "Nyarafolo", 36306, "alv-snr", } m["sew"] = { "Sewa Bay", 7458126, "poz-ocw", } m["sey"] = { "Secoya", 3477218, "sai-tuc", "Latn", } m["sez"] = { "Senthang Chin", 7451223, "tbq-kuk", } m["sfb"] = { "French Belgian Sign Language", 3217332, "sgn", } m["sfe"] = { "Eastern Subanun", 63311321, "phi", "Latn", } m["sfm"] = { "Small Flowery Miao", 7542773, "hmn", } m["sfs"] = { "South African Sign Language", 3322093, "sgn", } m["sfw"] = { "Sehwi", 36593, "alv-ctn", "Latn", } m["sga"] = { "Old Irish", 35308, "cel-gae", "Latn, Ogam", strip_diacritics = {remove_diacritics = c.dotabove .. c.diaer .. "·"}, sort_key = "sga-sortkey", standard_chars = "AaÁáBbCcDdEeÉéFfGgHhIiÍíLlMmNnOoÓóPpRrSsTtUuÚú0123456789ᚁᚂᚃᚄᚅᚆᚇᚈᚉᚊᚋᚌᚍᚎᚏᚐᚑᚒᚓᚔ" .. c.punc, } m["sgb"] = { "Mag-Anchi Ayta", 4356243, "phi", "Latn", } m["sgc"] = { "Kipsigis", 56339, "sdv-nma", } m["sgd"] = { "Surigaonon", 34140, "phi", "Latn", } m["sge"] = { "Segai", 7446180, } m["sgg"] = { "Swiss-German Sign Language", 35150, "sgn", } m["sgh"] = { "Shughni", 34053, "ira-shr", "Latn, Cyrl, Arab", translit = "sgh-translit", override_translit = true, } m["sgi"] = { "Suga", 36475, "nic-mmb", "Latn", } m["sgk"] = { "Sangkong", 2945610, "tbq-bis", } m["sgm"] = { "Singa", 7522797, "bnt-lok", "Latn", } m["sgp"] = { "Singpho", 7524158, "sit-jnp", "Latn", } m["sgr"] = { "Sangisari", 3394363, "ira-kms", "Arab", } m["sgs"] = { "Samogitian", 213434, "bat-eas", "Latn", wikimedia_codes = "bat-smg", ancestors = "olt", display_text = "lt-common", strip_diacritics = "lt-common", sort_key = "lt-common", } m["sgt"] = { "Brokpake", 56603, "sit-tib", "Tibt", override_translit = true, -- Tibt translit, display_text, strip_diacritics, sort_key in [[Module:scripts/data]] } m["sgu"] = { "Salas", 7403694, "poz-cma", } m["sgw"] = { "Sebat Bet Gurage", 2707343, "sem-eth", "Ethi", } m["sgx"] = { "Sierra Leone Sign Language", 7511448, "sgn-asl", } m["sgy"] = { "Sanglechi", 3472220, "ira-sgi", } m["sgz"] = { "Sursurunga", 36511, "poz-ocw", "Latn", } m["sha"] = { "Shall-Zwall", 3915355, "nic-beo", } m["shb"] = { "Ninam", 3436586, "sai-ynm", "Latn", } m["shc"] = { "Sonde", 7560881, "bnt-pen", "Latn", } m["shd"] = { "Kundal Shahi", 6444265, "inc-shn", "Aran", } m["she"] = { "Sheko", 3183355, "omv-diz", } m["shg"] = { "Shua", 3501092, "khi-kal", "Latn", } m["shh"] = { "Shoshone", 33811, "azc-num", "Latn", } m["shi"] = { "Tashelhit", 34152, "ber", "Latn, Arab, Tfng, Hebr", ancestors = "shi-med", translit = { Tfng = "Tfng-translit", }, strip_diacritics = { Arab = "ar-stripdiacritics", }, -- Hebr display_text, strip_diacritics, sort_key in [[Module:scripts/data]] } m["shj"] = { "Shatt", 56344, "sdv-daj", } m["shk"] = { "Shilluk", 36486, "sdv-lon", "Latn", } m["shm"] = { "Shahrudi", 7462280, "xme-ttc", "Arab, Latn", ancestors = "xme-ttc-cen", } m["shn"] = { "Shan", 56482, "tai-swe", "Mymr", translit = "shn-translit", sort_key = { from = {"[ၢႃ]", "ဵ", "ႅ", "ႇ", "ႈ", "း", "ႉ", "ႊ"}, to = {"ာ", "ေ", "ႄ", "႒", "႓", "႔", "႕", "႖"} }, } m["sho"] = { "Shanga", 3913931, "dmn-bbu", "Latn", } m["shp"] = { "Shipibo-Conibo", 2671988, "sai-pan", "Latn", } m["shq"] = { "Sala", 10961665, "bnt-bot", "Latn", } m["shr"] = { "Shi", 3481999, "bnt-shh", "Latn", } m["shs"] = { "Shuswap", 3482685, "sal", "Latn", } m["sht"] = { "Shasta", 56396, "nai-shs", "Latn", } m["shu"] = { "Chadian Arabic", 56497, "sem-arb", "Arab", strip_diacritics = { remove_diacritics = c.kashida .. c.fathatan .. c.dammatan .. c.kasratan .. c.fatha .. c.damma .. c.kasra .. c.shadda .. c.sukun .. c.superalef, from = {u(0x0671)}, to = {u(0x0627)} }, } m["shv"] = { "Shehri", 33445, "sem-sar", "Arab, Latn", } m["shw"] = { "Shwai", 36527, "alv-hei", } m["shx"] = { "She", 2605689, "hmn", } m["shy"] = { "Tachawit", 33274, "ber", "Tfng, Arab, Latn", translit = "Tfng-translit", } m["shz"] = { "Syenara", -- not to be confused with Senara (seq) 36316, "alv-snr", } m["sia"] = { "Akkala Sami", 35241, "smi", "Cyrl, Latn", translit = "sia-translit", display_text = { from = {"'"}, to = {"ˈ"} }, strip_diacritics = {remove_diacritics = "'ˈ"}, } m["sib"] = { "Sebop", 7442799, "poz-swa", "Latn", } m["sid"] = { "Sidamo", 33786, "cus-hec", "Latn, Ethi", } m["sie"] = { "Simaa", 7517329, "bnt-kav", "Latn", } m["sif"] = { "Siamou", 36252, } m["sig"] = { "Paasaal", 36426, "nic-sis", "Latn", } m["sih"] = { "Sîshëë", 8072753, "poz-cln", "Latn", } m["sii"] = { "Shom Peng", 1039346, "aav", } m["sij"] = { "Numbami", 3346277, "poz-ocw", "Latn", } m["sik"] = { "Sikiana", 3443734, "sai-prk", "Latn", } m["sil"] = { "Tumulung Sisaala", 25383006, "nic-sis", "Latn", } m["sim"] = { "Mende (New Guinea)", 7446815, "paa-sep", "Latn", } m["sip"] = { "Sikkimese", 35285, "sit-tib", "Tibt", ancestors = "xct", override_translit = true, -- Tibt translit, display_text, strip_diacritics, sort_key in [[Module:scripts/data]] } m["siq"] = { "Sonia", 7561770, "ngf-bos", "Latn", } m["sir"] = { "Siri", 3438729, "cdc-wst", "Latn", } m["sis"] = { "Siuslaw", 2315424, } m["siu"] = { "Sinagen", 7521655, "paa-wap", "Latn", } m["siv"] = { "Sumariup", 7636966, "paa-sep", "Latn", } m["siw"] = { "Siwai", 7532519, "paa-sbo", "Latn", } m["six"] = { "Sumau", 7637021, "ngf-pek", "Latn", } m["siy"] = { "Sivandi", 13269, "xme", "Arab, Latn", ancestors = "xme-mid", } m["siz"] = { "Siwi", 36814, "ber", "Tfng, Arab, Latn", } m["sja"] = { "Epena", 3055682, "sai-chc", "Latn", } m["sjb"] = { "Sajau Basap", 4684353, "poz-bnn", } m["sjc"] = { "Shaojiang Min", 3431451, "zhx-inm", "Hants", generate_alternants = "zh-generatealternants", sort_key = "Hani-sortkey", } m["sjd"] = { "Kildin Sami", 33656, "smi", "Cyrl", translit = "sjd-translit", display_text = { from = {"'"}, to = {"ˈ"} }, strip_diacritics = {remove_diacritics = "'ˈ"}, } m["sje"] = { "Pite Sami", 56314, "smi", "Latn", display_text = { from = {"'"}, to = {"ˈ"} }, strip_diacritics = {remove_diacritics = c.macron .. "'ˈ"}, sort_key = "sje-sortkey", } m["sjg"] = { "Assangori", 3502255, "sdv-tmn", } m["sjk"] = { "Kemi Sami", 35871, "smi", "Latn", display_text = { from = {"'"}, to = {"ˈ"} }, strip_diacritics = {remove_diacritics = "'ˈ"}, } m["sjl"] = { "Miji", 6845470, "sit-hrs", } m["sjm"] = { "Mapun", 3287253, "poz-sbj", "Latn", } m["sjn"] = { "Sindarin", 56437, "art", "Latn, Teng", type = "appendix-constructed", } m["sjo"] = { "Xibe", 13223, "tuw-jrc", "sjo-Mong", ancestors = "mnc", } m["sjp"] = { "Surjapuri", 7645351, "inc-krd", "Deva, as-Beng, Kthi", } m["sjr"] = { "Siar-Lak", 3482907, "poz-ocw", } m["sjs"] = { "Senhaja de Srair", 56744, "ber", "Latn, Tfng, Arab", strip_diacritics = { Arab = "ar-stripdiacritics", }, translit = { Tfng = "Tfng-translit", } } m["sjt"] = { "Ter Sami", 36656, "smi", "Cyrl, Latn", display_text = { from = {"'"}, to = {"ˈ"} }, strip_diacritics = {remove_diacritics = "'ˈ"}, translit = "sjt-translit", } m["sju"] = { "Ume Sami", 56415, "smi", "Latn", strip_diacritics = {remove_diacritics = c.macron .. "'ˈ"}, display_text = { from = {"'"}, to = {"ˈ"} }, sort_key = "sju-sortkey", } m["sjw"] = { "Shawnee", 2669206, "alg", "Latn", } -- [ska] Skagit is treated as part of [lut] Lushootseed m["skb"] = { "Saek", 36437, "tai-nor", "Thai", sort_key = "Thai-sortkey", } m["skc"] = { "Ma Manda", 6720783, "ngf-era", "Latn", } m["skd"] = { "Southern Sierra Miwok", 3492334, "nai-utn", "Latn", } m["ske"] = { "Ske", 7534244, "poz-vnn", "Latn", } m["skf"] = { "Mekéns", 3304806, "tup", "Latn", } m["skh"] = { "Sikule", 3121081, "poz-nws", } m["ski"] = { "Sika", 33960, "poz-cet", "Latn", } m["skj"] = { -- compare 'ths' "Seke", 30226846, "sit-tam", } m["skk"] = { "Sok", 12953887, "mkh-ban", } m["skm"] = { "Sakam", 6448517, "ngf-uru", "Latn", } m["skn"] = { "Kolibugan Subanon", 18755617, "phi", "Latn", } m["sko"] = { "Seko Tengah", 15613270, "poz", } m["skp"] = { "Sekapan", 7447132, "poz-bnn", "Latn", } m["skq"] = { "Sininkere", 3914896, "dmn-snb", "Latn", } m["skr"] = { "Saraiki", 33902, "inc-pan", "Aran, Mult, Deva", strip_diacritics = {remove_diacritics = c.fathatan .. c.dammatan .. c.kasratan .. c.fatha .. c.damma .. c.kasra .. c.shadda .. c.sukun .. c.nunghunna}, translit = { Aran = "pa-Aran-translit", Deva = "hi-translit", Mult = "Mult-translit", }, } m["sks"] = { "Maia", 12952760, "ngf-kau", "Latn", } m["skt"] = { "Sakata", 36691, "bnt-bnm", "Latn", } m["sku"] = { "Sakao", 3298421, "poz-vnn", "Latn", } m["skv"] = { "Skou", 3915200, "paa-isk", "Latn", } m["skw"] = { "Skepi Creole Dutch", 2522153, "crp", "Latn", ancestors = "nl", } m["skx"] = { "Seko Padang", 15613282, "poz-ssw", "Latn", } m["sky"] = { "Sikaiana", 7439242, "poz-pnp", "Latn", } m["skz"] = { "Sekar", 7447136, "poz-cet", } m["slc"] = { "Saliba (Colombia)", 3441097, nil, "Latn", } m["sld"] = { "Sisaala", 11020264, "nic-sis", "Latn", } m["sle"] = { "Sholaga", 7500203, "dra-kan", "Knda", -- Knda translit in [[Module:scripts/data]] } m["slf"] = { "Swiss-Italian Sign Language", 12953479, "sgn", } m["slg"] = { "Selungai Murut", 7448844, "poz-san", } -- [slh] Southern Puget Sound Salish is treated as part of [lut] Lushootseed -- "sli" "Silesian German" IS SUBSUMED INTO "gmw-ecg" "East Central German" m["slj"] = { "Salumá", 7406296, "sai-prk", "Latn", } m["sll"] = { "Salt-Yui", 7405785, "ngf-sim", "Latn", } m["slm"] = { "Pangutaran Sama", 3362086, "poz-sbj", "Latn", } m["sln"] = { "Salinan", 1568938, "qfa-iso", "Latn", } m["slp"] = { "Lamaholot", 6480777, "poz-cet", "Latn", } m["slr"] = { "Salar", 33963, "trk-ogz", "Arab, Latn", ancestors = "trk-eog", } m["sls"] = { "Singapore Sign Language", 7512563, "sgn", } m["slt"] = { "Sila", 7514021, "tbq-sil", } m["slu"] = { "Selaru", 7447500, "poz-cet", "Latn", } m["slw"] = { "Sialum", 7506694, "ngf-ehu", "Latn", } m["slx"] = { "Salampasu", 7403607, "bnt-lun", "Latn", } m["sly"] = { "Selayar", 7447520, "poz-ssw", } m["slz"] = { "Ma'ya", 2291492, "poz-hce", "Latn", } m["sma"] = { "Southern Sami", 13293, "smi", "Latn", display_text = { from = {"'"}, to = {"ˈ"} }, strip_diacritics = {remove_diacritics = "'ˈ"}, sort_key = "sma-sortkey", } m["smb"] = { "Simbari", 7517427, "ngf-bsi", "Latn", } m["smc"] = { "Som", 7559081, "ngf-uru", "Latn", } m["smd"] = { "Sama", 6407456, "bnt-kmb", "Latn", } m["smf"] = { "Auwe", 3502072, "paa-war", "Latn", ancestors = "dnd", } m["smg"] = { "Simbali", 56692, "paa-bai", "Latn", } m["smh"] = { "Samei", 7409269, "tbq-axi", } m["smj"] = { "Lule Sami", 56322, "smi", "Latn", display_text = { from = {"'"}, to = {"ˈ"} }, strip_diacritics = {remove_diacritics = c.macron .. "'ˈ"}, sort_key = "smj-sortkey", } m["smk"] = { "Bolinao", 2669235, "phi", "Latn, Tglg", } m["sml"] = { "Central Sama", 3470593, "poz-sbj", "Latn", } m["smm"] = { "Musasa", 6940122, "inc-bih", } m["smn"] = { "Inari Sami", 33462, "smi", "Latn", display_text = { from = {"'"}, to = {"ˈ"} }, strip_diacritics = {remove_diacritics = c.dotbelow .. "'ˈ"}, sort_key = "smn-sortkey", } m["smp"] = { "Samaritan Hebrew", 56502, "sem-can", "Samr", translit = "Samr-translit", -- Samr strip_diacritics, sort_key in [[Module:scripts/data]] ancestors = "hbo", } m["smq"] = { "Samo", 7409884, "ngf-est", "Latn", } m["smr"] = { "Simeulue", 2992833, "poz-nws", "Latn", } m["sms"] = { "Skolt Sami", 13271, "smi", "Latn", display_text = { from = {"'"}, to = {"ˈ"} }, strip_diacritics = {remove_diacritics = c.dotbelow .. "'ˈ"}, sort_key = "sms-sortkey", } m["smt"] = { "Simte", 7521268, "tbq-kuk", } m["smu"] = { "Somray", 6583612, "mkh-pea", } m["smv"] = { "Samvedi", 6345632, "inc-sou", } m["smw"] = { "Sumbawa", 3182585, "poz-bss", "Latn", } m["smx"] = { "Samba", 11120157, "bnt-pen", "Latn", } m["smy"] = { "Semnani", 14531212, "xme", "Arab, Latn", } m["smz"] = { "Simeku", 7517534, "paa-sbo", "Latn", } m["snb"] = { "Sebuyau", 7442836, "poz-mly", "Latn", } m["snc"] = { "Sinaugoro", 4170719, "poz-ocw", "Latn", } m["sne"] = { "Bau Bidayuh", 2891938, "day", "Latn", } m["snf"] = { "Noon", 36304, "alv-cng", "Latn", } m["sng"] = { "Sanga (Congo)", 3438316, "bnt-lub", "Latn", } m["sni"] = { "Sensi", 7451029, "sai-pan", "Latn", } m["snj"] = { "Riverain Sango", 25559751, "crp", "Latn", ancestors = "ngb", } m["snk"] = { "Soninke", 36660, "dmn-snb", "Latn", } m["snl"] = { "Sangil", 3472206, "phi", "Latn", } m["snm"] = { "Southern Ma'di", 15637273, "csu-mma", } m["snn"] = { "Siona", 3485116, "sai-tuc", "Latn", } -- [sno] Snohomish is treated as part of [lut] Lushootseed m["snp"] = { "Siane", 7506812, "ngf-gor", "Latn", } m["snq"] = { "Sangu (Gabon)", 36609, "bnt-sir", "Latn", } m["snr"] = { "Sihan", 7513400, "ngf-gum", "Latn", } m["sns"] = { "Nahavaq", 2160435, "poz-vnc", "Latn", } m["snu"] = { "Senggi", 7929052, "paa-war", "Latn", } m["snv"] = { "Sa'ban", 3474891, "poz-swa", "Latn", } m["snw"] = { "Selee", 36272, "alv-ntg", "Latn", } m["snx"] = { "Sam", 7408387, "ngf-min", "Latn", } m["sny"] = { "Saniyo-Hiyewe", 7418302, "paa-sep", "Latn", } m["snz"] = { "Kou", 7525035, -- also 4803639 "ngf-eva", "Latn", } m["soa"] = { "Thai Song", 7709159, "tai-swe", "Tavt, Thai", --translit = "Tavt-translit", sort_key = { from = {"([ꪵꪶꪹꪻꪼ])([ꪀ-ꪯ])", "([เแโใไ])([ก-ฮ])"}, to = {"%2%1", "%2%1"} }, } m["sob"] = { "Sobei", 3121035, "poz-ocw", "Latn", } m["soc"] = { "Soko", 7555138, "bnt-ske", "Latn", } m["sod"] = { "Songoora", 7561296, "bnt-lgb", "Latn", } m["soe"] = { "Songomeno", 5713543, "bnt-bsh", "Latn", } m["sog"] = { "Sogdian", 205979, "ira-sgc", "Sogd, Mani, Syrc, Sogo", translit = { Sogd = "Sogd-translit", -- Mani translit in [[Module:scripts/data]] Sogo = "Sogo-translit", }, } m["soh"] = { "Aka (Sudan)", 3450949, "sdv-eje", "Latn", } m["soi"] = { "Sonha", 12953890, "inc-eas", } m["sok"] = { "Sokoro", 3441303, "cdc-est", "Latn", } m["sol"] = { "Solos", 3489591, "poz-ocw", } m["soo"] = { "Nsong", 12953148, "bnt-bdz", "Latn", } m["sop"] = { "Songe", 3130911, "bnt-lbn", "Latn", } m["soq"] = { "Kanasi", 11732656, "ngf-dag", "Latn", } m["sor"] = { "Somrai", 3123566, "cdc-est", "Latn", } m["sos"] = { "Seenku", 36274, "dmn-smg", } m["sou"] = { "Southern Thai", 56508, "tai-swe", "Thai", sort_key = "Thai-sortkey", } m["sov"] = { "Sonsorolese", 13281, "poz-mic", "Latn", } m["sow"] = { "Sowanda", 7571845, "paa-war", "Latn", } m["sox"] = { "Swo", 36604, "bnt-mka", "Latn", } m["soy"] = { "Miyobe", 35913, "alv-sav", "Latn", } m["soz"] = { "Temi", 13278, "bnt-kka", "Latn", } m["spb"] = { "Sepa (Indonesia)", 18603687, "poz-cma", "Latn", } m["spc"] = { "Sapé", 2888158, nil, "Latn", } m["spd"] = { "Saep", 7398312, "ngf-yag", "Latn", } m["spe"] = { "Sepa (New Guinea)", 7451725, "poz-ocw", "Latn", } m["spg"] = { "Sian", 7506806, "poz-bnn", } m["spi"] = { "Saponi", 3915418, "paa-rsa", "Latn", } m["spk"] = { "Sengo", 7450584, "paa-nnd", "Latn", } m["spl"] = { "Selepet", 7447917, "ngf-kab", "Latn", } m["spm"] = { "Sepen", 4701931, "paa-rub", "Latn", } m["spn"] = { "Sanapaná", 3033556, "sai-mas", "Latn", } m["spo"] = { "Spokane", 3493704, "sal", } m["spp"] = { "Supyire", 56284, "alv-sma", "Latn", } m["spr"] = { "Saparua", 7420921, "poz-cma", "Latn", } m["sps"] = { "Saposa", 3473187, "poz-ocw", "Latn", } m["spt"] = { "Spiti Bhoti", 22080879, "sit-las", } m["spu"] = { "Sapuan", 7421168, "mkh-ban", "Latn", -- in the limited documentation that so far exists } m["spv"] = { "Sambalpuri", 6433240, "inc-eas", "Orya", translit = "or-translit", ancestors = "or", } m["spx"] = { "South Picene", 36688, "itc-sbl", "Ital, Latn", -- Ital translit in [[Module:scripts/data]] display_text = { Latn = s["itc-Latn-displaytext"] }, strip_diacritics = { Latn = s["itc-Latn-stripdiacritics"], }, sort_key = { Latn = s["itc-Latn-sortkey"], }, } m["spy"] = { "Sabaot", 7395896, "sdv-kln", } m["sqa"] = { "Shama-Sambuga", 3914392, "nic-kmk", "Latn", } m["sqh"] = { "Shau", 3913925, "nic-jer", "Latn", } m["sqk"] = { "Albanian Sign Language", 4709168, "sgn", } m["sqm"] = { "Suma", 11008431, "gba-wes", } m["sqn"] = { "Susquehannock", 3505736, "iro-nor", "Latn", } m["sqo"] = { "Sorkhei", 3491964, "ira-kms", } m["sqq"] = { "Sou", 16979751, "mkh-ban", } m["sqr"] = { "Siculo-Arabic", 1069489, "sem-arb", "Arab", } m["sqs"] = { "Sri Lankan Sign Language", 3915466, "sgn", } m["sqt"] = { "Soqotri", 13283, "sem-sar", "Arab, Latn", } m["squ"] = { "Squamish", 2484579, "sal", "Latn", } m["sra"] = { "Saruga", 7424699, "ngf-han", "Latn", } m["srb"] = { "Sora", 13284, "mun", "Sora, Latn, Orya", } m["sre"] = { "Sara", 33957, "day", } m["srf"] = { "Nafi", 6958174, "poz-ocw", } m["srg"] = { "Sulod", 7636489, "phi", "Latn", } m["srh"] = { "Sarikoli", 33873, "ira-shr", "Latn, Arab, Cyrl", translit = "srh-translit", } m["sri"] = { "Siriano", 3485264, "sai-tuc", "Latn", } m["srk"] = { "Serudung Murut", 7455497, "poz-san", } m["srl"] = { "Isirawa", 4203802, "paa-gkw", "Latn", } m["srm"] = { "Saramaccan", 33779, "crp", "Latn", ancestors = "en, pt", } m["srn"] = { "Sranan Tongo", 33989, "crp", "Latn", ancestors = "en", } m["srq"] = { "Sirionó", 3027953, "tup-gua", "Latn", } m["srr"] = { "Serer", 36284, "alv-fwo", "Latn", } m["srs"] = { "Tsuut'ina", 20825, "ath-nor", "Latn", } m["srt"] = { "Sauri", 7427547, "paa-egb", "Latn", } m["sru"] = { "Suruí", 7646993, "tup", "Latn", } m["srv"] = { "Waray Sorsogon", 18755610, "phi", "Latn", } m["srw"] = { "Serua", 14916905, "poz-cet", } m["srx"] = { "Sirmauri", 7530505, "him", } m["sry"] = { "Sera", 7452602, "poz-ocw", "Latn", } m["srz"] = { "Shahmirzadi", 12953126, "ira-msh", "Arab", } m["ssb"] = { "Southern Sama", 3470594, "poz-sbj", "Latn", } m["ssc"] = { "Suba-Simbiti", 7630687, "bnt-lok", "Latn", } m["ssd"] = { "Siroi", 10771067, "ngf-rai", "Latn", } m["sse"] = { "Balangingi", 2880535, "poz-sbj", "Latn", } m["ssf"] = { "Thao", 676492, "map", "Latn", } m["ssg"] = { "Seimat", 3182581, "poz-aay", "Latn", } m["ssh"] = { "Shihhi Arabic", 56571, "sem-arb", "Arab", strip_diacritics = { remove_diacritics = c.kashida .. c.fathatan .. c.dammatan .. c.kasratan .. c.fatha .. c.damma .. c.kasra .. c.shadda .. c.sukun .. c.superalef, from = {u(0x0671)}, to = {u(0x0627)} }, } m["ssi"] = { "Sansi", 3309366, "inc-nwe", } m["ssj"] = { "Sausi", 7427605, "ngf-eva", "Latn", } m["ssk"] = { "Sunam", 11002210, "sit-kin", } m["ssl"] = { "Western Sisaala", 11154776, "nic-sis", "Latn", } m["ssm"] = { "Semnam", 7449713, "mkh-asl", "Latn", } m["sso"] = { "Sissano", 7530937, "poz-ocw", "Latn", } m["ssp"] = { "Spanish Sign Language", 3100814, "sgn", } m["ssq"] = { "So'a", 7572120, "poz-cet", "Latn", } m["ssr"] = { "Swiss-French Sign Language", 12953483, "sgn", } m["sss"] = { "Sô", 3082037, "mkh-kat", } m["sst"] = { "Sinasina", 7521813, "ngf-sim", "Latn", } m["ssu"] = { "Susuami", 7649752, "ngf-woj", "Latn", } m["ssv"] = { "Shark Bay", 7489783, "poz-vnn", "Latn", } m["ssx"] = { "Samberigi", 7409020, "ngf-sak", "Latn", } m["ssy"] = { "Saho", 36353, "cus-eas", "Latn, Ethi, Arab", } m["ssz"] = { "Sengseng", 7450601, "poz-ocw", "Latn", } m["stb"] = { "Northern Subanen", 12953892, "phi", "Latn", } m["std"] = { "Sentinelese", 568377, "qfa-unc", -- presumed Ongan } m["ste"] = { "Liana-Seti", 6539924, "poz-cma", } m["stf"] = { "Seta", 7456326, "paa-trr", "Latn", } m["stg"] = { "Trieng", 22694648, "mkh-ban", } m["sth"] = { "Shelta", 36705, "qfa-mix", "Latn", ancestors = "ga, en", } m["sti"] = { "Bulo Stieng", 15771431, "mkh-ban", "Khmr, Latn", } m["stj"] = { "Matya Samo", 10974879, "dmn-sam", "Latn", } m["stk"] = { "Arammba", 3502094, "paa-ton", "Latn", } m["stm"] = { "Setaman", 7456333, "ngf-mok", "Latn", } m["stn"] = { "Owa", 1324132, "poz-sls", "Latn", } m["sto"] = { "Stoney", 3033570, "sio-dkt", "Latn", } m["stp"] = { "Southeastern Tepehuan", 12953917, "azc-pim", "Latn", } m["stq"] = { "Saterland Frisian", 27154, "gmw-fri", "Latn", } m["str"] = { "Saanich", 36444, "sal", "Latn", } m["sts"] = { "Shumashti", 33777, "inc-kun", "Arab", } m["stt"] = { "Budeh Stieng", 12953891, "mkh-ban", } m["stu"] = { "Samtao", 25559550, "mkh-pal", } m["stv"] = { "Silt'e", 33880, "sem-eth", "Ethi", } m["stw"] = { "Satawalese", 28477, "poz-mic", "Latn", } m["sty"] = { "Siberian Tatar", 4418344, "trk-kno", "Cyrl", } m["sua"] = { "Sulka", 7636341, "qfa-iso", -- Papuan; isolate in Glottolog and Palmer (2018) "Latn", } m["sub"] = { "Suku", 12953160, "bnt-yak", "Latn", } m["suc"] = { "Western Subanon", 16113894, "phi", "Latn", } m["sue"] = { "Suena", 7634386, "ngf-nbi", "Latn", } m["sug"] = { "Suganga", 7634706, "ngf-msu", "Latn", } m["sui"] = { "Suki", 2089984, "ngf-gsu", "Latn", } m["suk"] = { "Sukuma", 2638144, "bnt-tkm", "Latn", } m["suo"] = { "Bouni", 65043887, "paa-pio", "Latn", } m["suq"] = { "Suri", 5364172, "sdv", } m["sur"] = { "Mwaghavul", 3440486, "cdc-wst", "Latn", } m["sus"] = { "Susu", 33990, "dmn-sya", "Latn", } m["sut"] = { "Subtiaba", 3915405, "omq", "Latn", } m["suv"] = { "Puroik", 56408, "sit-khb", "Beng, Deva, Latn", ancestors = "sit-khp-pro", } m["suw"] = { "Sumbwa", 7637055, "bnt-glb", "Latn", } m["sux"] = { "Sumerian", 36790, "qfa-iso", "Xsux, Latn", } m["suy"] = { "Suyá", 3505859, "sai-nje", "Latn", } m["suz"] = { "Sunwar", 56549, "sit-kiw", "Deva, Sunu" } m["sva"] = { "Svan", 34067, "ccs", "Geor, Cyrl", translit = { Geor = "sva-translit", }, override_translit = true, } m["svb"] = { "Ulau-Suain", 7878769, "poz-ocw", "Latn", } m["svc"] = { "Vincentian Creole English", 3501785, "crp", "Latn", ancestors = "en", } m["sve"] = { "Serili", 7454834, "poz-tim", } m["svk"] = { "Slovakian Sign Language", 7541557, "sgn", } m["svm"] = { "Slavomolisano", 36254, "zls", "Latn", ancestors = "sh", } m["svs"] = { "Savosavo", 3130296, "qfa-dis", -- Papuan; isolate in Glottolog; in the tentative Central Solomons family by Ross (2005) and Pedrós -- (2015) "Latn", } m["svx"] = { "Skalvian", 3486125, "bat-wes", "Latn", } m["swb"] = { "Maore Comorian", 34075, "bnt-com", "Latn", sort_key = "bnt-com-sortkey", } m["swf"] = { "Sere", 7453056, "nic-ser", "Latn", } m["swg"] = { "Swabian", 327274, "gmw-hgm", "Latn", ancestors = "gsw", } m["swi"] = { "Sui", 3112388, "qfa-kms", "Latn, Shui, Hani", sort_key = {Hani = "Hani-sortkey"}, } m["swj"] = { "Sira", 36599, "bnt-sir", "Latn", } m["swl"] = { "Swedish Sign Language", 36558, "sgn", } m["swm"] = { "Samosa", 7410037, "ngf-nwh", "Latn", } m["swn"] = { "Sokna", 2988323, "ber", } m["swo"] = { "Shanenawa", 61974839, "sai-pan", "Latn", } m["swp"] = { "Suau", 3502368, "poz-ocw", } m["swq"] = { "Sharwa", 56791, "cdc-cbm", "Latn", } m["swr"] = { "Saweru", 3474649, "paa-ysa", "Latn", } m["sws"] = { "Seluwasan", 7448845, "poz-cet", "Latn", } m["swt"] = { "Sawila", 7428639, "paa-alp", "Latn", } m["swu"] = { "Suwawa", 7650588, "phi", "Latn", } m["sww"] = { "Sowa", 7571843, "poz-vnn", "Latn", } m["swx"] = { "Suruahá", 3114402, "auf", } m["swy"] = { "Sarua", 56261, "cdc-est", "Latn", } m["sxb"] = { "Suba", 33916, "bnt-lok", "Latn", } m["sxc"] = { "Sicanian", 36335, "qfa-unc", -- extinct; lack of data: only names deciphered "Polyt", -- Polyt translit, display_text, strip_diacritics, sort_key in [[Module:scripts/data]] } m["sxe"] = { "Sighu", 36431, "bnt-kel", "Latn", } m["sxg"] = { "Shixing", 56337, "sit-nax", "Latn", } m["sxk"] = { "Southern Kalapuya", 3192122, "nai-klp", } m["sxl"] = { "Selonian", 36491, "bat-eas", "Latn", } m["sxm"] = { "Samre", 6583615, "mkh-pea", } m["sxn"] = { "Sangir", 25714758, "phi", "Latn", } m["sxo"] = { "Sorothaptic", 2762254, } m["sxr"] = { "Saaroa", 716599, "map", "Latn", } m["sxs"] = { "Sasaru", 3913384, "alv-yek", "Latn", } -- "sxu" "Upper Saxon" IS SUBSUMED INTO "gmw-ecg" "East Central German" m["sxw"] = { "Saxwe Gbe", 7428892, "alv-pph", "Latn", } m["sya"] = { "Siang", 3482903, } m["syb"] = { "Central Subanen", 12953893, "phi", "Latn", } m["syc"] = { "Classical Syriac", 33538, "sem-are", "Syrc", strip_diacritics = {remove_diacritics = c.macron .. c.diaer .. c.macronbelow .. u(0x0730) .. "-" .. u(0x0748)}, } m["syi"] = { "Seki", 36547, "bnt-kel", "Latn", } m["syk"] = { "Sukur", 56292, "cdc-cbm", "Latn", } m["syl"] = { "Sylheti", 2044560, "inc-bas", "Sylo, Beng", ancestors = "inc-obn", translit = "syl-translit", } m["sym"] = { "Maya Samo", 10950421, "dmn-sam", "Latn", } m["syn"] = { "Senaya", 33914, "sem-nna", } m["syo"] = { "Suoy", 7641864, "mkh-pea", } m["sys"] = { "Sinyar", 56840, "csu", "Latn", } m["syw"] = { "Kagate", 12952538, "sit-kyk", "Deva", } m["syx"] = { "Osamayi", 7408415, "bnt-kel", "Latn", } m["syy"] = { "Al-Sayyid Bedouin Sign Language", 2915457, "sgn", } m["sza"] = { "Semelai", 3111827, "mkh-asl", "Latn", } m["szb"] = { "Ngalum", 11732516, "ngf-okk", "Latn", } m["szc"] = { "Semaq Beri", 7449119, "mkh-asl", "Latn", } m["szd"] = { "Seru", 7455488, "poz-bnn", "Latn", } m["sze"] = { "Seze", 373683, "omv-mao", "Latn", } m["szg"] = { "Sengele", 7450555, "bnt-mon", "Latn", } m["szl"] = { "Silesian", 30319, "zlw-lch", "Latn", ancestors = "zlw-opl", } m["szn"] = { "Sula", 3503403, "poz-cma", "Latn", } m["szp"] = { "Suabo", 7630429, "ngf-sbh", "Latn", } m["szs"] = { "Solomon Islands Sign Language", 26449924, "sgn", } m["szv"] = { "Isubu", 35431, "bnt-saw", "Latn", } m["szw"] = { "Sawai", 3447258, "poz-hce", "Latn", } m["szy"] = { "Sakizaya", 718269, "map", "Latn", } return require("Module:languages").finalizeData(m, "language") 4tyniq3gtiv672879n154vrotvnoo1n Module:etymology/style.css 828 8163 55073 2026-09-30T22:39:19Z Deadend0914 7211 Gesceop tramet þe hafaþ '.desc-arr[title] { cursor: help } .desc-arr[title="uncertain"] { font-size:.7em; vertical-align: super }' 55073 sanitized-css text/css .desc-arr[title] { cursor: help } .desc-arr[title="uncertain"] { font-size:.7em; vertical-align: super } g0m44bo1acdws3qgtcpwoxx5bbrst6w Bysen:enm-noun 10 8164 55074 2026-09-30T22:40:13Z Deadend0914 7211 Gesceop tramet þe hafaþ '{{#invoke:checkparams|error}}<!-- Validate template parameters -->{{head|enm|noun|head={{{head|}}}|sort={{{sort|}}}|var={{{var|}}}|g={{{g|}}}|g2={{{g2|}}}|g3={{{g3|}}}}}<!-- -->&#32;(<!-- -->{{#ifeq:{{{1}}}|-<!-- -->|<!-- -->''{{glossary|uncountable}}''{{cln|enm|uncountable nouns}}<!-- -->|{{#ifeq:{{{1}}}|singular only |<!-- -->''{{glossary|singular only}}''{{cln|enm|singularia tantum}}<!-- -->|<!-- -->''plural'' <!-- -->'''{{l-self|enm|{{{pl|{{{...' 55074 wikitext text/x-wiki {{#invoke:checkparams|error}}<!-- Validate template parameters -->{{head|enm|noun|head={{{head|}}}|sort={{{sort|}}}|var={{{var|}}}|g={{{g|}}}|g2={{{g2|}}}|g3={{{g3|}}}}}<!-- -->&#32;(<!-- -->{{#ifeq:{{{1}}}|-<!-- -->|<!-- -->''{{glossary|uncountable}}''{{cln|enm|uncountable nouns}}<!-- -->|{{#ifeq:{{{1}}}|singular only |<!-- -->''{{glossary|singular only}}''{{cln|enm|singularia tantum}}<!-- -->|<!-- -->''plural'' <!-- -->'''{{l-self|enm|{{{pl|{{{1|{{pagename}}s}}}}}}|accel-form=p}}'''<!-- -->{{#if:{{{pl2|}}}<!-- -->|<!-- -->&#32;''or'' {{#if:{{{pl2_qual|}}}<!-- -->|<!-- -->{{q|{{{pl2_qual}}}}}<!-- -->&#32;<!-- -->}}<!-- -->'''{{l-self|enm|{{{pl2}}}|accel-form=p}}'''<!-- -->}}<!-- -->{{#if:{{{pl3|}}}<!-- -->|<!-- -->&#32;''or'' {{#if:{{{pl3_qual|}}}<!-- -->|<!-- -->{{q|{{{pl3_qual}}}}}<!-- -->&#32;<!-- -->}}<!-- -->'''{{l-self|enm|{{{pl3}}}|accel-form=p}}'''<!-- -->}}<!-- -->{{#if:{{{pl4|}}}<!-- -->|<!-- -->&#32;''or'' {{#if:{{{pl4_qual|}}}<!-- -->|<!-- -->{{q|{{{pl4_qual}}}}}<!-- -->&#32;<!-- -->}}<!-- -->'''{{l-self|enm|{{{pl4}}}|accel-form=p}}'''<!-- -->}}<!-- -->{{#if:{{{pl5|}}}<!-- -->|<!-- -->&#32;''or'' {{#if:{{{pl5_qual|}}}<!-- -->|<!-- -->{{q|{{{pl5_qual}}}}}<!-- -->&#32;<!-- -->}}<!-- -->'''{{l-self|enm|{{{pl5}}}|accel-form=p}}'''<!-- -->}}<!-- -->{{#if:{{{pl6|}}}<!-- -->|<!-- -->&#32;''or'' {{#if:{{{pl6_qual|}}}<!-- -->|<!-- -->{{q|{{{pl6_qual}}}}}<!-- -->&#32;<!-- -->}}<!-- -->'''{{l-self|enm|{{{pl6}}}|accel-form=p}}'''<!-- -->}}}}<!-- -->}}<!-- -->{{#if:{{{gen|}}}<!-- -->|<!-- -->,&#32;''genitive singular'' <!-- -->'''{{l-self|enm|{{{gen}}}|accel-form=gen{{!}}s}}'''<!-- -->{{#if:{{{gen2|}}}<!-- -->|<!-- -->&#32;''or'' <!-- -->'''{{l-self|enm|{{{gen2}}}|accel-form=gen{{!}}s}}'''<!-- -->}}<!-- -->{{#if:{{{gen3|}}}<!-- -->|<!-- -->&#32;''or'' <!-- -->'''{{l-self|enm|{{{gen3}}}|accel-form=gen{{!}}s}}'''<!-- -->}}<!-- -->}}<!-- -->{{#if:{{{dat|}}}<!-- -->|<!-- -->,&#32;''dative singular'' <!-- -->'''{{l-self|enm|{{{dat}}}|accel-form=dat{{!}}s}}'''<!-- -->{{#if:{{{dat2|}}}<!-- -->|<!-- -->&#32;''or'' <!-- -->'''{{l-self|enm|{{{dat2}}}|accel-form=dat{{!}}s}}'''<!-- -->}}<!-- -->{{#if:{{{dat3|}}}<!-- -->|<!-- -->&#32;''or'' <!-- -->'''{{l-self|enm|{{{dat3}}}|accel-form=dat{{!}}s}}'''<!-- -->}}<!-- -->}}<!-- -->{{#if:{{{gen_pl|}}}<!-- -->|<!-- -->,&#32;''genitive plural'' <!-- -->'''{{l-self|enm|{{{gen_pl}}}|accel-form=gen{{!}}p}}'''<!-- -->{{#if:{{{gen_pl2|}}}<!-- -->|<!-- -->&#32;''or'' <!-- -->'''{{l-self|enm|{{{gen_pl2}}}|accel-form=gen{{!}}p}}'''<!-- -->}}<!-- -->{{#if:{{{gen_pl3|}}}<!-- -->|<!-- -->&#32;''or'' <!-- -->'''{{l-self|enm|{{{gen_pl3}}}|accel-form=gen{{!}}p}}'''<!-- -->}}<!-- -->}}<!-- -->)<!-- --><noinclude>{{documentation}}</noinclude> dxzn2calmp1edw9sxva4fkkqmbxh2ow 55085 55074 2026-09-30T22:56:27Z Deadend0914 7211 55085 wikitext text/x-wiki {{#invoke:checkparams|error}}<!-- Validate template parameters -->{{head|enm|noun|head={{{head|}}}|sort={{{sort|}}}|var={{{var|}}}|g={{{g|}}}|g2={{{g2|}}}|g3={{{g3|}}}}}<!-- -->&#32;(<!-- -->{{#ifeq:{{{1}}}|-<!-- -->|<!-- -->''{{glossary|untellendlīc}}''{{cln|enm|uncountable nouns}}<!-- -->|{{#ifeq:{{{1}}}|singular only |<!-- -->''{{glossary|ānlīc anfeald}}''{{cln|enm|singularia tantum}}<!-- -->|<!-- -->''mnf.'' <!-- -->'''{{l-self|enm|{{{pl|{{{1|{{pagename}}s}}}}}}|accel-form=p}}'''<!-- -->{{#if:{{{pl2|}}}<!-- -->|<!-- -->&#32;''or'' {{#if:{{{pl2_qual|}}}<!-- -->|<!-- -->{{q|{{{pl2_qual}}}}}<!-- -->&#32;<!-- -->}}<!-- -->'''{{l-self|enm|{{{pl2}}}|accel-form=p}}'''<!-- -->}}<!-- -->{{#if:{{{pl3|}}}<!-- -->|<!-- -->&#32;''or'' {{#if:{{{pl3_qual|}}}<!-- -->|<!-- -->{{q|{{{pl3_qual}}}}}<!-- -->&#32;<!-- -->}}<!-- -->'''{{l-self|enm|{{{pl3}}}|accel-form=p}}'''<!-- -->}}<!-- -->{{#if:{{{pl4|}}}<!-- -->|<!-- -->&#32;''or'' {{#if:{{{pl4_qual|}}}<!-- -->|<!-- -->{{q|{{{pl4_qual}}}}}<!-- -->&#32;<!-- -->}}<!-- -->'''{{l-self|enm|{{{pl4}}}|accel-form=p}}'''<!-- -->}}<!-- -->{{#if:{{{pl5|}}}<!-- -->|<!-- -->&#32;''or'' {{#if:{{{pl5_qual|}}}<!-- -->|<!-- -->{{q|{{{pl5_qual}}}}}<!-- -->&#32;<!-- -->}}<!-- -->'''{{l-self|enm|{{{pl5}}}|accel-form=p}}'''<!-- -->}}<!-- -->{{#if:{{{pl6|}}}<!-- -->|<!-- -->&#32;''or'' {{#if:{{{pl6_qual|}}}<!-- -->|<!-- -->{{q|{{{pl6_qual}}}}}<!-- -->&#32;<!-- -->}}<!-- -->'''{{l-self|enm|{{{pl6}}}|accel-form=p}}'''<!-- -->}}}}<!-- -->}}<!-- -->{{#if:{{{gen|}}}<!-- -->|<!-- -->,&#32;''genitive singular'' <!-- -->'''{{l-self|enm|{{{gen}}}|accel-form=gen{{!}}s}}'''<!-- -->{{#if:{{{gen2|}}}<!-- -->|<!-- -->&#32;''or'' <!-- -->'''{{l-self|enm|{{{gen2}}}|accel-form=gen{{!}}s}}'''<!-- -->}}<!-- -->{{#if:{{{gen3|}}}<!-- -->|<!-- -->&#32;''or'' <!-- -->'''{{l-self|enm|{{{gen3}}}|accel-form=gen{{!}}s}}'''<!-- -->}}<!-- -->}}<!-- -->{{#if:{{{dat|}}}<!-- -->|<!-- -->,&#32;''dative singular'' <!-- -->'''{{l-self|enm|{{{dat}}}|accel-form=dat{{!}}s}}'''<!-- -->{{#if:{{{dat2|}}}<!-- -->|<!-- -->&#32;''or'' <!-- -->'''{{l-self|enm|{{{dat2}}}|accel-form=dat{{!}}s}}'''<!-- -->}}<!-- -->{{#if:{{{dat3|}}}<!-- -->|<!-- -->&#32;''or'' <!-- -->'''{{l-self|enm|{{{dat3}}}|accel-form=dat{{!}}s}}'''<!-- -->}}<!-- -->}}<!-- -->{{#if:{{{gen_pl|}}}<!-- -->|<!-- -->,&#32;''genitive plural'' <!-- -->'''{{l-self|enm|{{{gen_pl}}}|accel-form=gen{{!}}p}}'''<!-- -->{{#if:{{{gen_pl2|}}}<!-- -->|<!-- -->&#32;''or'' <!-- -->'''{{l-self|enm|{{{gen_pl2}}}|accel-form=gen{{!}}p}}'''<!-- -->}}<!-- -->{{#if:{{{gen_pl3|}}}<!-- -->|<!-- -->&#32;''or'' <!-- -->'''{{l-self|enm|{{{gen_pl3}}}|accel-form=gen{{!}}p}}'''<!-- -->}}<!-- -->}}<!-- -->)<!-- --><noinclude>{{documentation}}</noinclude> c6op0nte8pskvn2defddsxhr4hvxamt Bysen:l-self 10 8165 55075 2026-09-30T22:40:38Z Deadend0914 7211 Gesceop tramet þe hafaþ '<includeonly><onlyinclude>{{safesubst:<noinclude/>#invoke:links/templates|l_term_t|notself=1}}</onlyinclude></includeonly><!-- -->{{l-self|en|term}}{{documentation}}' 55075 wikitext text/x-wiki <includeonly><onlyinclude>{{safesubst:<noinclude/>#invoke:links/templates|l_term_t|notself=1}}</onlyinclude></includeonly><!-- -->{{l-self|en|term}}{{documentation}} jhwc06ur1a5nmigfv5a7izr06tlqmpk Module:links/templates 828 8166 55076 2026-09-30T22:41:02Z Deadend0914 7211 Gesceop tramet þe hafaþ '-- Prevent substitution. if mw.isSubsting() then return require("Module:unsubst") end local export = {} local links_module = "Module:links" local process_params = require("Module:parameters").process local remove = table.remove local upper = require("Module:string utilities").upper --[=[ Modules used: [[Module:links]] [[Module:languages]] [[Module:scripts]] [[Module:parameters]] [[Module:debug]] ]=] do local function get_args(frame) -- `compat` is a...' 55076 Scribunto text/plain -- Prevent substitution. if mw.isSubsting() then return require("Module:unsubst") end local export = {} local links_module = "Module:links" local process_params = require("Module:parameters").process local remove = table.remove local upper = require("Module:string utilities").upper --[=[ Modules used: [[Module:links]] [[Module:languages]] [[Module:scripts]] [[Module:parameters]] [[Module:debug]] ]=] do local function get_args(frame) -- `compat` is a compatibility mode for {{term}}. -- If given a nonempty value, the function uses lang= to specify the -- language, and all the positional parameters shift one number lower. local iargs = frame.args iargs.compat = iargs.compat and iargs.compat ~= "" iargs.langname = iargs.langname and iargs.langname ~= "" iargs.notself = iargs.notself and iargs.notself ~= "" local alias_of_4 = {alias_of = 4} local boolean = {type = "boolean"} local params = { [1] = {required = true, type = "language", default = "und"}, [2] = true, [3] = true, [4] = true, g = {list = true, type = "genders", flatten = true}, gloss = alias_of_4, id = true, lit = true, ng = true, pos = true, sc = {type = "script"}, t = alias_of_4, tr = true, ts = true, q = {type = "qualifier"}, qq = {type = "qualifier"}, l = {type = "labels"}, ll = {type = "labels"}, ref = {type = "references"}, ["accel-form"] = true, ["accel-translit"] = true, ["accel-lemma"] = true, ["accel-lemma-translit"] = true, ["accel-gender"] = true, ["accel-nostore"] = boolean, } if iargs.compat then params.lang = {type = "language", default = "und"} remove(params, 1) alias_of_4.alias_of = 3 end if iargs.langname then params.w = boolean end return process_params(frame:getParent().args, params), iargs end -- Used in [[Template:l]] and [[Template:m]]. function export.l_term_t(frame) local args, iargs = get_args(frame) local compat = iargs.compat local lang = args[compat and "lang" or 1] -- Tracking for und. if not compat and lang:getCode() == "und" then require("Module:debug").track("link/und") end local term = args[(compat and 1 or 2)] local alt = args[(compat and 2 or 3)] term = term ~= "" and term or nil if not term and not alt and iargs.demo then term = iargs.demo end local langname = iargs.langname and ( args.w and lang:makeWikipediaLink() or lang:getCanonicalName() ) or nil if langname and term == "-" then return langname end -- Forward the information to full_link return (langname and langname .. " " or "") .. require(links_module).full_link( { lang = lang, sc = args.sc, track_sc = true, term = term, alt = alt, gloss = args[4], id = args.id, tr = args.tr, ts = args.ts, genders = args.g, pos = args.pos, ng = args.ng, lit = args.lit, q = args.q, qq = args.qq, l = args.l, ll = args.ll, refs = args.ref, show_decorations = true, accel = args["accel-form"] and { form = args["accel-form"], translit = args["accel-translit"], lemma = args["accel-lemma"], lemma_translit = args["accel-lemma-translit"], gender = args["accel-gender"], nostore = args["accel-nostore"], } or nil }, iargs.face, not iargs.notself ) end -- Used in [[Template:link-annotations]]. function export.l_annotations_t(frame) local args, iargs = get_args(frame) -- Forward the information to format_link_annotations return require(links_module).format_link_annotations( { lang = args[1], tr = { args.tr }, ts = { args.ts }, genders = args.g, pos = args.pos, ng = args.ng, lit = args.lit }, iargs.face ) end end -- Used in [[Template:ll]]. do local function get_args(frame) return process_params(frame:getParent().args, { [1] = {required = true, type = "language", default = "und"}, [2] = {allow_empty = true}, [3] = true, id = true, sc = {type = "script"}, }) end function export.ll(frame) local args = get_args(frame) local lang = args[1] local sc = args.sc local term = args[2] term = term ~= "" and term or nil return require(links_module).language_link{ lang = lang, sc = sc, term = term, alt = args[3], id = args.id } or "<small>[Term?]</small>" .. require("Module:utilities").format_categories( {lang:getFullName() .. " term requests"}, lang, "-", nil, nil, sc ) end end function export.def_t(frame) local args = process_params(frame:getParent().args, { [1] = {required = true, default = ""}, }) local face = frame.args.face local ret = require("Module:script utilities").tag_definition(require(links_module).embedded_language_links{ term = args[1], lang = require("Module:languages").getByCode("en"), sc = require("Module:scripts").getByCode("Latn") }, face) if face == "non-gloss" then return ret end return '<span class="mention-gloss-paren">(</span>' .. ret .. '<span class="mention-gloss-paren">)</span>' end function export.linkify_t(frame) local args = process_params(frame:getParent().args, { [1] = {required = true, default = ""}, }) args[1] = mw.text.trim(args[1]) if args[1] == "" or args[1]:find("[[", nil, true) then return args[1] end return "[[" .. args[1] .. "]]" end function export.cap_t(frame) local args = process_params(frame:getParent().args, { [1] = {required = true}, [2] = true, lang = {type = "language", default = "en"}, }) local term = args[1] return require(links_module).full_link{ lang = args.lang, term = term, alt = term:gsub("^.[\128-\191]*", upper) .. (args[2] or "") } end function export.section_link_t(frame) local args = process_params(frame:getParent().args, { [1] = {}, }) return require(links_module).section_link(args[1]) end return export notqdjaat0bxru1ch2nr8ozilppfmpp Bysen:glossary 10 8167 55077 2026-09-30T22:41:32Z Deadend0914 7211 Gesceop tramet þe hafaþ '{{#invoke:glossary|link}}<!-- --><noinclude> {{documentation}} </noinclude>' 55077 wikitext text/x-wiki {{#invoke:glossary|link}}<!-- --><noinclude> {{documentation}} </noinclude> 2gfc2bdgt00us8bb4kn5xfj7lxil6dj Module:glossary 828 8168 55078 2026-09-30T22:41:53Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local anchors_module = "Module:anchors" local glossary_data_module = "Module:glossary/data" local parameters_module = "Module:parameters" local string_nowiki_module = "Module:string/nowiki" local anchor_encode = mw.uri.anchorEncode local require = require --[==[ Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenienc...' 55078 Scribunto text/plain local export = {} local anchors_module = "Module:anchors" local glossary_data_module = "Module:glossary/data" local parameters_module = "Module:parameters" local string_nowiki_module = "Module:string/nowiki" local anchor_encode = mw.uri.anchorEncode local require = require --[==[ Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==] local function normalize_anchor(...) normalize_anchor = require(anchors_module).normalize_anchor return normalize_anchor(...) end local function nowiki(...) nowiki = require(string_nowiki_module) return nowiki(...) end local function process_params(...) process_params = require(parameters_module).process return process_params(...) 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 data local function get_data() data, get_data = mw.loadData(glossary_data_module), nil return data end function export.def(frame) local required_default_ = {required = true, default = ""} local args = process_params(frame:getParent().args, { [1] = required_default_, [2] = required_default_, }) local term = args[1] return "; <span id=\"" .. anchor_encode(term) .. "\">" .. term .. "</span>\n: " .. args[2] end local function get_link(formatted_anchor, text, anchor) return "[[Appendix:Glossary#" .. nowiki(formatted_anchor) .. "|" .. (text or anchor) .. "]]" end -- TODO: make this externally-callable. function export.link(frame) local args = process_params(frame:getParent().args, { [1] = {required = true, default = ""}, [2] = true, }) local anchor, text = args[1], args[2] local formatted_anchor = normalize_anchor(anchor) if (data or get_data())[formatted_anchor] then return get_link(formatted_anchor, text, anchor) end local lower_anchor = anchor:ulower() formatted_anchor = normalize_anchor(lower_anchor) local link = get_link(formatted_anchor, text, anchor) if data[formatted_anchor] or formatted_anchor == "" then return link end mw.log("The anchor " .. lower_anchor .. (lower_anchor ~= anchor and " or " .. anchor or "") .. " does not exist in Appendix:Glossary.") return link .. "[[Category:Pages linking to anchors not found in Appendix:Glossary|" .. lower_anchor .. "]]" end return export 08kgb6yt06e00j6kl77b3kn5idr7nal Module:glossary/data 828 8169 55079 2026-09-30T22:42:22Z Deadend0914 7211 Gesceop tramet þe hafaþ '-- [[Module:glossary]] and [[Module:get IDs]] both process anchor strings with -- normalize_anchor, which handles character escapes, spaces, underscores etc. return require("Module:table/listToSet")(require("Module:get IDs").get("Appendix:Glossary"))' 55079 Scribunto text/plain -- [[Module:glossary]] and [[Module:get IDs]] both process anchor strings with -- normalize_anchor, which handles character escapes, spaces, underscores etc. return require("Module:table/listToSet")(require("Module:get IDs").get("Appendix:Glossary")) r80o7ymm49ef5njwjnzw2jqshvfp4nr Module:get IDs 828 8170 55080 2026-09-30T22:42:44Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local anchors_module = "Module:anchors" local parameters_module = "Module:parameters" local string_nowiki_module = "Module:string/nowiki" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local template_parser_module = "Module:template parser" local anchor_encode = mw.uri.anchorEncode local byte = string.byte local concat = table.concat local format = string.format local get -- Defined below. local g...' 55080 Scribunto text/plain local export = {} local anchors_module = "Module:anchors" local parameters_module = "Module:parameters" local string_nowiki_module = "Module:string/nowiki" local string_utilities_module = "Module:string utilities" local table_module = "Module:table" local template_parser_module = "Module:template parser" local anchor_encode = mw.uri.anchorEncode local byte = string.byte local concat = table.concat local format = string.format local get -- Defined below. local gsub = string.gsub local lower = string.lower local new_title = mw.title.new local pcall = pcall local require = require --[==[ Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==] local function class_else_type(...) class_else_type = require(template_parser_module).class_else_type return class_else_type(...) end local function decode_entities(...) decode_entities = require(string_utilities_module).decode_entities return decode_entities(...) end local function normalize_anchor(...) normalize_anchor = require(anchors_module).normalize_anchor return normalize_anchor(...) end local function nowiki(...) nowiki = require(string_nowiki_module) return nowiki(...) end local function parse(...) parse = require(template_parser_module).parse return parse(...) end local function process_params(...) process_params = require(parameters_module).process return process_params(...) 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 parameters_data local function get_parameters_data() parameters_data, get_parameters_data = mw.loadData("Module:parameters/data"), nil return parameters_data end local template_names local function get_template_names() template_names = require(table_module).listToSet{ "anchor", "etymid", "etymon", "senseid", "trans-see", "trans-top", "trans-top-also", "transclude" } get_template_names = nil return template_names end local function handle_heading(name, seen, ids) -- Repeated IDs are disambiguated by appending _N, where N is an -- incremented integer. When determining this, ASCII characters are case- -- insensitive, but all other characters are case-sensitive. local check = lower(name) if seen[check] then local i, name_lower = 1, check repeat i = i + 1 check = format("%s_%d", name_lower, i) until not seen[check] name = format("%s_%d", name, i) end seen[check] = true ids[#ids + 1] = name end local function pseudo_percent_encode(ch) return format(".%02X", byte(ch)) end local function handle_template(name, node, ids) local args, success = node:get_arguments() success, args = pcall(process_params, args, (parameters_data or get_parameters_data())[name]) if not success then return elseif name == "anchor" then args = args[1] for i = 1, #args do ids[#ids + 1] = normalize_anchor(args[i]) end return end local id1, id2 if name == "trans-top" or name == "trans-see" or name == "trans-top-also" then id1 = "Translations-" if name == "trans-top" then id2 = args.id or args[1] else id2 = args.id.default or args[1] end else id1 = args[1]:getCanonicalName() .. ": " if name == "etymon" or name == "transclude" then id2 = args["id"] else id2 = args[2] end end if id2 then ids[#ids + 1] = normalize_anchor(id1 .. id2) end end function export.get(pagename, force_transclusion) local page, seen, ids = new_title(pagename), {}, {} for node in parse((page.redirectTarget or page):getContent() or error("Could not retrieve the page content of \"" .. pagename .. "\".")):iterate_nodes() do local node_class = class_else_type(node) if node_class == "heading" then local name = node:get_anchor() if name ~= nil then handle_heading(name, seen, ids) -- Headings get a fallback ID, which uses pseudo-percent-encoding -- where "%" is replaced by "." (e.g. "!" is ".21"). local name2 = gsub(name, "[^%w%-.:_]", pseudo_percent_encode) if name2 ~= name then handle_heading(name2, seen, ids) end end elseif node_class == "template" then local name = node:get_name(nil, force_transclusion) if (template_names or get_template_names())[name] then handle_template(name, node, ids) end end end return ids end get = export.get -- Used by [[MediaWiki:Gadget-OrangeLinks.js]]. function export.show(frame) local output = {} for i, pagename in ipairs(frame.args) do output[i] = nowiki(concat(get(pagename, true), " ")) end return concat(output, "\n\n") end return export hj1g7rdgmd847d41eflsi8p4spx5anb Bysen:lg 10 8171 55081 2026-09-30T22:49:08Z Deadend0914 7211 Redirected page to [[Bysen:glossary]] 55081 wikitext text/x-wiki #REDIRECT [[Template:glossary]] b2vde0oz3rhjfdc3n0td64qkis98kir Module:string/nowiki 828 8172 55082 2026-09-30T22:49:38Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local table_list_to_set_module = "Module:table/listToSet" local byte = string.byte local char = string.char local find = string.find local format = string.format local gsub = string.gsub local lower = string.lower local sub = string.sub local function list_to_set(...) list_to_set = require(table_list_to_set_module) return list_to_set(...) end local function escape_byte(_, b) return format("&#%d;", b) end local absolute local function get_absolute() absolu...' 55082 Scribunto text/plain local table_list_to_set_module = "Module:table/listToSet" local byte = string.byte local char = string.char local find = string.find local format = string.format local gsub = string.gsub local lower = string.lower local sub = string.sub local function list_to_set(...) list_to_set = require(table_list_to_set_module) return list_to_set(...) end local function escape_byte(_, b) return format("&#%d;", b) end local absolute local function get_absolute() absolute, get_absolute = list_to_set({"\"", "&", "'", ";", "<", "=", ">", "[", "]", "{", "|", "}"}, function(_, ch) return format("&#%d;", byte(ch)) end), nil return absolute end local after_newline local function get_after_newline() after_newline, get_after_newline = list_to_set({0x9, 0xA, 0xD, 0x20, 0x21, 0x23, 0x2A, 0x3A}, escape_byte), nil return after_newline end local scheme_chars, unused_scheme_chars, uri_schemes local function get_scheme_chars() local _uri_schemes = {"bitcoin", "geo", "magnet", "mailto", "matrix", "news", "sip", "sips", "sms", "tel", "urn", "xmpp"} uri_schemes, get_scheme_chars = list_to_set(_uri_schemes), nil _uri_schemes = table.concat(_uri_schemes) -- Add alphanumeric characters used in a listed scheme to `scheme_chars`, -- and those that aren't to `unused_scheme_chars`. scheme_chars, unused_scheme_chars = {}, {} for i = 0x61, 0x7A do local chars = find(_uri_schemes, char(i), nil, true) and scheme_chars or unused_scheme_chars chars[i] = true chars[i - 0x20] = true end unused_scheme_chars[0x5F] = true -- _ return scheme_chars end local after_magic_link local function get_after_magic_link() after_magic_link, get_after_magic_link = list_to_set({0x9, 0xA, 0xC, 0xD, 0x20}, escape_byte), nil return after_magic_link end -- Temporary variables. No risk of collisions across stack levels, since there's -- no way for nowiki() to be called recursively. local _str, esc, esc_next, esc_next2 local function nowiki(loc, ch) if esc_next ~= nil then esc, esc_next, esc_next2 = esc_next, esc_next2, nil return esc or nil end esc = (absolute or get_absolute())[ch] if esc then return esc elseif ch == "\n" or ch == "\r" then esc_next = (after_newline or get_after_newline())[byte(_str, loc + 1)] elseif ch == "!" then if byte(_str, loc + 1) == 0x21 then -- ! esc_next = "&#33;" end elseif ch == ":" then if sub(_str, loc + 1, loc + 2) == "//" then return "&#58;" end local n, b = 0 repeat n = n + 1 b = byte(_str, loc - n) until not (scheme_chars or get_scheme_chars())[b] -- Abort on an unused scheme character, as it can't be a scheme. if unused_scheme_chars[b] then return -- Otherwise, check if the the word before the colon matches a scheme. elseif uri_schemes[lower(sub(_str, loc - n + 1, loc - 1))] then return "&#58;" end elseif ch == "-" then local prev = byte(_str, loc - 1) if (prev == 0xA or prev == 0xD) and sub(_str, loc + 1, loc + 3) == "---" then return "&#45;" end elseif ch == "I" then local esc_sp = (after_magic_link or get_after_magic_link())[byte(_str, loc + 4)] if esc_sp and sub(_str, loc + 1, loc + 3) == "SBN" then esc_next = esc_sp end elseif ch == "P" then local esc_sp = (after_magic_link or get_after_magic_link())[byte(_str, loc + 4)] if esc_sp and sub(_str, loc + 1, loc + 3) == "MID" then esc_next, esc_next2 = false, esc_sp -- to avoid escaping "I" in "PMID" end elseif ch == "R" then local esc_sp = (after_magic_link or get_after_magic_link())[byte(_str, loc + 3)] if esc_sp and sub(_str, loc + 1, loc + 2) == "FC" then esc_next = esc_sp end elseif ch == "_" then if byte(_str, loc + 1) == 0x5F then -- _ esc_next = "&#95;" end elseif ch == "~" then if sub(_str, loc + 1, loc + 2) == "~~" then esc_next, esc_next2 = false, "&#126;" end end end local first local function get_first() first, get_first = list_to_set({0x9, 0xA, 0xD, 0x20, 0x21, 0x23, 0x2A, 0x2B, 0x2D, 0x3A, 0x5F, 0x7E}, escape_byte), nil return first end local last local function get_last() last, get_last = list_to_set({0x9, 0xA, 0xC, 0xD, 0x20, 0x21, 0x3A, 0x5F, 0x7E}, escape_byte), nil return last end return function(str) if #str == 0 then return str end _str, esc, esc_next, esc_next2 = str, nil, nil, nil str = gsub(str, "()([%s!\"#&'*%-:;<=>IPR[%]_{|}~])", nowiki) local b = byte(str, 1) local esc_first = (first or get_first())[b] if #str == 1 then return esc_first or (last or get_last())[b] or str end local esc_last = (last or get_last())[byte(str, -1)] if esc_first then if esc_last then return esc_first .. sub(str, 2, -2) .. esc_last end return esc_first .. sub(str, 2) elseif esc_last then return sub(str, 1, -2) .. esc_last end return str end iqdxq0iiw3rpvozlhmhirkfu3sj92s0 Appendix:Glossary 0 8173 55083 2026-09-30T22:52:35Z Deadend0914 7211 Gesceop tramet þe hafaþ '==0-9== ; 1st hād : Seoh '''{{lg|forma had}}''' ; 2nd hād : Seoh '''{{lg|oþer had}}''' ; 3rd hād : Seoh '''{{lg|þridda had}}'''' 55083 wikitext text/x-wiki ==0-9== ; 1st hād : Seoh '''{{lg|forma had}}''' ; 2nd hād : Seoh '''{{lg|oþer had}}''' ; 3rd hād : Seoh '''{{lg|þridda had}}''' d2w9x47hcom1218yp86q5qkj5a15ooz deth 0 8174 55084 2026-09-30T22:52:48Z Deadend0914 7211 Gesceop tramet þe hafaþ '=={{sprǣc|enm}}== ===Rihtstefn=== * IPA: /dɛ̝ːθ/ ===Wordstǣr=== {{inh+|enm|ang|dēaþ}}, from {{inh|enm|gmw-pro|*dauþu}}, from {{inh|enm|gem-pro|*dauþuz}}. ===Nama=== {{enm-noun|pl=es|-}} ===Tudru=== * {{desctree|en|death|qq=obsolete {{en|deth}}|noalts=1}} * {{desc|gmw-msc|dethe|deith|deth}} ** {{desc|sco|daeth|daith|death|deeth|deith}}' 55084 wikitext text/x-wiki =={{sprǣc|enm}}== ===Rihtstefn=== * IPA: /dɛ̝ːθ/ ===Wordstǣr=== {{inh+|enm|ang|dēaþ}}, from {{inh|enm|gmw-pro|*dauþu}}, from {{inh|enm|gem-pro|*dauþuz}}. ===Nama=== {{enm-noun|pl=es|-}} ===Tudru=== * {{desctree|en|death|qq=obsolete {{en|deth}}|noalts=1}} * {{desc|gmw-msc|dethe|deith|deth}} ** {{desc|sco|daeth|daith|death|deeth|deith}} lbae2fr3q8dvqyaj0tyly957gh8ui66 Bysen:ang-decl-noun-o-f 10 8175 55087 2026-09-30T23:30:55Z Deadend0914 7211 Gesceop tramet þe hafaþ '{{#invoke:checkparams|error}}<!-- Validate template parameters -->{{ang-decl-noun<!-- -->|type=strang ''ō''-stefn<!-- -->|1={{{nomsg|{{{1}}}{{#if:{{{short|}}}|u}}}}}<!-- -->|3={{{1}}}e<!-- -->|5={{{1}}}e<!-- -->|7={{{1}}}e<!-- -->|2={{{1}}}a,{{{1}}}e<!-- -->|4={{{1}}}a,{{{1}}}e<!-- -->|6={{{1}}}a<!-- -->|8={{{1}}}um<!-- -->|num={{{num|}}}<!-- -->|title={{{title|}}}<!-- -->}}<!-- -->{{cln|ang|ō-stem nouns}}<!-- --><noinclude>{{documentation}}</noinclude>' 55087 wikitext text/x-wiki {{#invoke:checkparams|error}}<!-- Validate template parameters -->{{ang-decl-noun<!-- -->|type=strang ''ō''-stefn<!-- -->|1={{{nomsg|{{{1}}}{{#if:{{{short|}}}|u}}}}}<!-- -->|3={{{1}}}e<!-- -->|5={{{1}}}e<!-- -->|7={{{1}}}e<!-- -->|2={{{1}}}a,{{{1}}}e<!-- -->|4={{{1}}}a,{{{1}}}e<!-- -->|6={{{1}}}a<!-- -->|8={{{1}}}um<!-- -->|num={{{num|}}}<!-- -->|title={{{title|}}}<!-- -->}}<!-- -->{{cln|ang|ō-stem nouns}}<!-- --><noinclude>{{documentation}}</noinclude> fec77elb369wfipkafvdzcdhkfu40yc worulddǣl 0 8176 55092 2026-10-01T00:02:57Z Deadend0914 7211 Gesceop tramet þe hafaþ '=={{sprǣc|ang}}== ===Wordstǣr=== * Of ''[[woruld]]'' and ''[[dǣl]]'' ===Werlic Nama=== # (gelīcaþ nīweword) þā micle [[dǣl|dǣlas]] of landum, wiþ þā [[sǣ]] ===Wendunga=== * Nīwenglisc: [[continent]]' 55092 wikitext text/x-wiki =={{sprǣc|ang}}== ===Wordstǣr=== * Of ''[[woruld]]'' and ''[[dǣl]]'' ===Werlic Nama=== # (gelīcaþ nīweword) þā micle [[dǣl|dǣlas]] of landum, wiþ þā [[sǣ]] ===Wendunga=== * Nīwenglisc: [[continent]] sy8bhmw9i3l23rnap2z0uqx64vtiwel 55093 55092 2026-10-01T00:03:08Z Deadend0914 7211 /* Werlic Nama */ 55093 wikitext text/x-wiki =={{sprǣc|ang}}== ===Wordstǣr=== * Of ''[[woruld]]'' and ''[[dǣl]]'' ===Werlic Nama=== # ''(gelīcaþ nīweword)'' þā micle [[dǣl|dǣlas]] of landum, wiþ þā [[sǣ]] ===Wendunga=== * Nīwenglisc: [[continent]] drd6wzz95p0u7tbeifvvo80k1xcim8z 55094 55093 2026-10-01T00:04:05Z Deadend0914 7211 /* {{sprǣc|ang}} */ 55094 wikitext text/x-wiki =={{sprǣc|ang}}== ===Wordstǣr=== * Of ''[[woruld]]'' and ''[[dǣl]]'' ===Werlic Nama=== {{ang-noun|m|head=worulddǣl}} # ''(gelīcaþ nīweword)'' þā micle [[dǣl|dǣlas]] of landum, wiþ þā [[sǣ]] ====Declīnung==== {{ang-decl-noun-i-m|dǣl}} ===Wendunga=== * Nīwenglisc: [[continent]] 3yl22rjgkgor200p90j4fo16px24yh7 55097 55094 2026-10-01T00:05:41Z Deadend0914 7211 /* Werlic Nama */ 55097 wikitext text/x-wiki =={{sprǣc|ang}}== ===Wordstǣr=== * Of ''[[woruld]]'' and ''[[dǣl]]'' ===Werlic Nama=== {{ang-noun|m|head=worulddǣl}} # ''(gelīcaþ nīweword)'' þā micle [[dǣl|dǣlas]] of landum, wiþ þā [[sǣ]] ====Declīnung==== {{ang-decl-noun-i-m|worulddǣl}} ===Wendunga=== * Nīwenglisc: [[continent]] e2nsj5ur61n9sina9hnjaq3omxh5331 Bysen:ang-decl-noun-i-m 10 8177 55095 2026-10-01T00:04:27Z Deadend0914 7211 Gesceop tramet þe hafaþ '{{#invoke:checkparams|error}}<!-- Validate template parameters -->{{ang-decl-noun<!-- -->|type=strong ''i''-stem<!-- -->|1={{{1}}}{{#if:{{{short|}}}|e}}<!-- -->|3={{{1}}}{{#if:{{{short|}}}|e}}<!-- -->|5={{{1}}}es<!-- -->|7={{{1}}}e<!-- -->|2={{{1}}}as<!-- -->|4={{{1}}}as<!-- -->|6={{{1}}}a<!-- -->|8={{{1}}}um<!-- -->|num={{{num|}}}<!-- -->|title={{{title|}}}<!-- -->}}<!-- -->{{cln|ang|i-stem nouns}}<!-- --><noinclude>{{documentation}}</noinclude>' 55095 wikitext text/x-wiki {{#invoke:checkparams|error}}<!-- Validate template parameters -->{{ang-decl-noun<!-- -->|type=strong ''i''-stem<!-- -->|1={{{1}}}{{#if:{{{short|}}}|e}}<!-- -->|3={{{1}}}{{#if:{{{short|}}}|e}}<!-- -->|5={{{1}}}es<!-- -->|7={{{1}}}e<!-- -->|2={{{1}}}as<!-- -->|4={{{1}}}as<!-- -->|6={{{1}}}a<!-- -->|8={{{1}}}um<!-- -->|num={{{num|}}}<!-- -->|title={{{title|}}}<!-- -->}}<!-- -->{{cln|ang|i-stem nouns}}<!-- --><noinclude>{{documentation}}</noinclude> 1w7259893fsfot07msbc56mj8r7eqw3 55096 55095 2026-10-01T00:04:47Z Deadend0914 7211 55096 wikitext text/x-wiki {{#invoke:checkparams|error}}<!-- Validate template parameters -->{{ang-decl-noun<!-- -->|type=strang ''i''-stefn<!-- -->|1={{{1}}}{{#if:{{{short|}}}|e}}<!-- -->|3={{{1}}}{{#if:{{{short|}}}|e}}<!-- -->|5={{{1}}}es<!-- -->|7={{{1}}}e<!-- -->|2={{{1}}}as<!-- -->|4={{{1}}}as<!-- -->|6={{{1}}}a<!-- -->|8={{{1}}}um<!-- -->|num={{{num|}}}<!-- -->|title={{{title|}}}<!-- -->}}<!-- -->{{cln|ang|i-stem nouns}}<!-- --><noinclude>{{documentation}}</noinclude> my5e8azk9b5uel99yll103rnclajmx5 worulddæl 0 8178 55098 2026-10-01T00:06:59Z Deadend0914 7211 Redirected page to [[worulddǣl]] 55098 wikitext text/x-wiki #redirect [[worulddǣl]] q3uasdo60uvfzmb6yx7y9vjkddwxhs5 Bysen:dercat 10 8179 55099 2026-10-01T00:09:23Z Deadend0914 7211 Gesceop tramet þe hafaþ '<includeonly>{{#invoke:dercat|dercat}}</includeonly><noinclude>{{documentation}}</noinclude>' 55099 wikitext text/x-wiki <includeonly>{{#invoke:dercat|dercat}}</includeonly><noinclude>{{documentation}}</noinclude> 5cd5b1zpnezetgb5ibnpc1g49queo4o Module:dercat 828 8180 55100 2026-10-01T00:09:56Z Deadend0914 7211 Gesceop tramet þe hafaþ 'local export = {} local etymology_module = "Module:etymology" local parameters_module = "Module:parameters" local utilities_module = "Module:utilities" local insert = table.insert local require = require --[==[ Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after...' 55100 Scribunto text/plain local export = {} local etymology_module = "Module:etymology" local parameters_module = "Module:parameters" local utilities_module = "Module:utilities" local insert = table.insert local require = require --[==[ Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==] local function check_ancestor(...) check_ancestor = require(etymology_module).check_ancestor return check_ancestor(...) end local function format_categories(...) format_categories = require(utilities_module).format_categories return format_categories(...) end local function process_params(...) process_params = require(parameters_module).process return process_params(...) end local function language_or_string(val) return val == "<" and "string" or "language" end function export.dercat(frame) local args = process_params(frame:getParent().args, { [1] = {required = true, type = "full language"}, [2] = {list = true, required = true, type = language_or_string, family = true}, ["inh"] = {type = "number"}, }) local target_lang, langs, categories, cutoff = args[1], args[2], {} local target_lang_code, target_lang_name = target_lang:getCode(), target_lang:getCanonicalName() for i = 1, #langs do local source_lang = langs[i] if source_lang == "<" then -- Disallow "inh" and "<" together. if args.inh then error(("Cannot specify parameter \"inh\" if \"<\" has been set (see parameter %d): use one or the other."):format(i)) -- Disallow multiple "<"s. elseif cutoff then -- Collate keys for all "<"s in the input; all keys need to be offset by 1, since args[1] isn't in the table. local lt = {cutoff + 1, i + 1} while langs[i] do i = i + 1 if langs[i] == "<" then insert(lt, i + 1) end end error(("Cannot specify \"<\" more than once, but currently used in parameters %s."):format(mw.text.listToText(lt))) end cutoff = i -- Add inheritance categories for all languages encountered so far. for j = 1, i - 1 do local inh_source_lang = langs[j] check_ancestor(target_lang, inh_source_lang) insert(categories, j * 2, target_lang_name .. " terms inherited from " .. inh_source_lang:getDisplayForm()) end else -- Generate the "derived from" category, accounting for the special case when a language derives from itself. -- Note: "borrowed back into" uses :getCanonicalName(), while the other categories use :getDisplayForm(). insert(categories, target_lang_name .. " terms " .. (target_lang_code == source_lang:getCode() and "borrowed back into " .. source_lang:getCanonicalName() or "derived from " .. source_lang:getDisplayForm() )) if args.inh and i <= args.inh then check_ancestor(target_lang, source_lang) insert(categories, target_lang_name .. " terms inherited from " .. source_lang:getDisplayForm()) end end end return format_categories(categories, target_lang) end return export q3dqmzjxe1e513c038q440p97jfu4t3