Вікісловник ukwiktionary https://uk.wiktionary.org/wiki/%D0%93%D0%BE%D0%BB%D0%BE%D0%B2%D0%BD%D0%B0_%D1%81%D1%82%D0%BE%D1%80%D1%96%D0%BD%D0%BA%D0%B0 MediaWiki 1.48.0-wmf.1 case-sensitive Медіа Спеціальна Обговорення Користувач Обговорення користувача Вікісловник Обговорення Вікісловника Файл Обговорення файлу MediaWiki Обговорення MediaWiki Шаблон Обговорення шаблону Довідка Обговорення довідки Категорія Обговорення категорії Додаток Обговорення додатка Індекс Обговорення індексу Субтитри Обговорення субтитрів Модуль Обговорення модуля Подія Обговорення події plameniak 0 9468 575011 562883 2026-10-29T08:51:14Z Ігнат Лостюк 22957 575011 wikitext text/x-wiki {{=sk=}} === Морфосинтаксичні ознаки === {{імен sk m2|склади={{склади|plameniak}}|plameniak}} {{морфо|}} === Вимова === * {{transcriptions|plameɲɪ̯ak|plameɲɪ̯aki}} * {{audio|Sk-plameniak.ogg|прослухати вимову}} === Семантичні властивості === {{іл|Lightmatter_flamingo2.jpg|Фламінго}} ===Значення=== # [[фламінго]]{{семантика|синоніми=|антоніми=|гіпероніми=vták|гіпоніми=}} {{приклад2|текст=|переклад=}} {{списки семантичних зв'язків}} ==== Холоніми ==== # ==== Мероніми ==== # <!-- ==== Асоціації ==== --> ===Усталені та термінологічні словосполучення, фразеологізми === * ==== Колокації ==== ====Прислів’я та приказки ==== ===Споріднені слова=== {{спорідн| |згруб= |змп= plameniačik |власні назви= |прізвища= |топоніми= |іменники= |прикметники= |дієприкметники= |дієслова= |прислівники= }} {{довжина слова|9|lang=sk}} {{Категорія|мова=sk|Птахи|||||}} eq9o8r18pu326ose8ka36bwyixoiw1f Шаблон:відмінки sk 10 59431 575006 270712 2026-10-29T08:41:03Z Ігнат Лостюк 22957 575006 wikitext text/x-wiki {| style="float:right; margin-left:0.5em; margin-bottom:0.5em; border: 1px solid #6699CC; border-collapse:collapse;" cellpadding="2" rules="all" width="210" ! bgcolor="#EEF9FF" | [[відмінок]] ! bgcolor="#EEF9FF" | [[однина]] ! bgcolor="#EEF9FF" | [[множина]] |- | bgcolor="#EEF9FF" | [[називний|Н.]] | bgcolor="#ffffff" | {{{nom-sg| }}} | bgcolor="#ffffff" | {{{nom-pl| }}} |- | bgcolor="#EEF9FF" | [[родовий|Р.]] | bgcolor="#ffffff" | {{{gen-sg| }}} | bgcolor="#ffffff" | {{{gen-pl| }}} |- | bgcolor="#EEF9FF" | [[давальний|Д.]] | bgcolor="#ffffff" | {{{dat-sg| }}} | bgcolor="#ffffff" | {{{dat-pl| }}} |- | bgcolor="#EEF9FF" | [[знахідний|З.]] | bgcolor="#ffffff" | {{{acc-sg|{{{acc-sg|}}}}}} | bgcolor="#ffffff" | {{{acc-pl|{{{acc-pl|}}}}}} |- | bgcolor="#EEF9FF" | [[орудний|Ор.]] | bgcolor="#ffffff" | {{{ins-sg| }}} | bgcolor="#ffffff" | {{{ins-pl| }}} |- | bgcolor="#EEF9FF" | [[місцевий|М.]] | bgcolor="#ffffff" | {{{loc-sg|{{{loc-sg|}}}}}} | bgcolor="#ffffff" | {{{loc-pl|{{{loc-pl|}}}}}} |- class="{{{vok-sg|hiddenStructure}}}" | bgcolor="#eeeeee" | [[кличний|Кл.]] | bgcolor="#eeeeee" | {{{voc-sg|{{{voc-sg|}}}}}} | bgcolor="#eeeeee" | {{{voc-pl|{{{voc-pl|}}}}}} |}<includeonly>{{#ifeq:{{NAMESPACE}}|{{ns:0}}|[[Категорія:Словацькі іменники]]}}</includeonly><noinclude>[[Категорія:Шаблони словозмін/sk]]</noinclude> 50yzdtzab8brduoa9y3tz213a4gxxu6 Montenegro 0 88454 574999 567271 2026-10-28T13:57:29Z Ігнат Лостюк 22957 574999 wikitext text/x-wiki {{=en=}} === Морфосинтаксичні ознаки === {{імен en s|склади={{склади|Mon|te|neg|ro}}|Montenegro}} {{топонім|en}} {{морфо-en}} === Вимова === * {{transcription-en-us|mɒn.tɪˈnɛɡ.ɹəʊ], [mɒn.tɪˈneɪ.ɡɹəʊ|ˌmɑn.təˈneɪ.ɡɹoʊ], [ˌmɑn.tɪˈni.ɡɹoʊ}} * {{audio2|LL-Q1860 (eng)-Soundguys-Montenegro.wav|прослухати вимову}} === Семантичні властивості === {{іл|Flag_of_Montenegro.svg|Flag of '''Montenegro'''}} ==== Значення ==== # {{геогр.|en}} [[Чорногорія]] {{семантика|синоніми=|антоніми=|гіпероніми=country|гіпоніми=}} {{приклад|текст=|переклад=|джерело=}} {{списки семантичних зв’язків}} ===Усталені та термінологічні словосполучення, фразеологізми === * ==== Колокації ==== ====Прислів'я та приказки ==== ===Споріднені слова=== {{спорідн| |згруб= |змп= |власні назви= |прізвища= |топоніми= |іменники=Montenegrin, Montenegran |прикметники=Montenegrin, Montenegran |дієприкметники= |дієслова= |прислівники= }} === Етимологія === Від {{lang|vec|Montenegro}}, (від {{lang|vec|mónte}} + [[negro]]). === Джерела === {{довжина слова|10|lang=en}} {{Категорія|мова=en|Країни|Країни Європи|Латинізми|||}} 75uny0p9g0mxs47q3gkwlgdl5il7ho2 піца 0 90272 575003 574709 2026-10-28T19:00:58Z Ігнат Лостюк 22957 575003 wikitext text/x-wiki = {{-uk-}} = === Морфосинтаксичні ознаки === {{імен uk 1*a f una|склади={{склади|пі́|ца}}|пі́ц|піц}} {{морфо-uk|піц|+а}} === Вимова === * {{transcriptions-uk|пі́ца|пі́ци}} * {{audio2|LL-Q8798 (ukr)-Bicolino34-піца.wav|Прослухати вимову}} * {{транскрипція|п’і́ца}} === Семантичні властивості === {{іл|Brier-hill-pizza.jpg}} ==== Значення ==== # {{кулін.|uk}} італійська національна [[страва]], [[печений]] [[млинець]] із кислого (дріжджового) тіста з [[сир]]ом, [[помідор]]ами та різними добавками на його поверхні {{семантика|синоніми=|антоніми=|гіпероніми=страва, їжа|гіпоніми=}} {{приклад2|||джерело=}} {{списки семантичних зв'язків}} === Усталені та термінологічні словосполучення, фразеологізми === * === Споріднені слова === {{спорідн| |згруб= |змп= |власні назви= |прізвища= |топоніми= |іменники=піцерія |прикметники= |дієприкметники= |дієслова= |прислівники= |повн= }} === Етимологія === Від {{lang|it|pizza}}. {{пор.}} {{lang|ru|пицца}}. === Переклад === {{переклад| |ab= |abq= |ady= |af= |agh= |ain.kana= |ain.lat= |ain= |aja= |akz= |ale= |als= |alt= |an= |ang= |ar={{t|ar|بيتزا|f|tr=bītzā}} |arc.jud= |arc.syr= |art= |asm= |ast= |av= |ave= |ay= |az= |ba= |bar= |bat-smg= |be={{t|be|піца|f}} |bg={{t|bg|пица|f}} |bm= |bn= |br= |bs= |bua= |ca= |ce= |chm= |chu.cyr= |chu.glag= |cjs= |co= |crh= |cs={{t|cs|pizza|f}} |csb= |cv= |cy= |da={{t|da|pizza|c}} |dar= |de={{t|de|Pizza|f}} |dsb= |dv= |el={{t|el|πίτσα|f|tr=pítsa}} |en={{t|en|pizza}} |eo={{t|eo|pico}} |es={{t|es|pizza|f}} |et= |eu={{t|eu|pizza}} |eve= |evn= |ewe= |fa={{t|fa|پیتزا|tr=pitzâ}} |fi={{t|fi|pizza}}, {{t|fi|pitsa}} |fo={{t|fo|pitsa|f}} |fr={{t|fr|pizza|f}} |frp= |fur= |fy= |ga= |gag= |gd= |gl={{t|gl|pizza|f}} |gld= |gn= |grc= |gu= |gv= |ha= |haw= |he= |hi={{t|hi|पिज़ा|m|tr=pizā}}, {{t|hi|पिज़्ज़ा|m|tr=pizzā}} |hr= |hsb= |ht= |hu= |hy={{t|hy|պիցցա|tr=picʻcʻa}} |ia= |id= |ik= |inh= |io= |is= |it={{t|it|pizza|f}} |itl= |iu= |ja={{t|ja|ピザ|tr=piza}}, {{t|ja|ピッツァ|tr=pittsa}}, {{t|ja|ピザパイ|tr=pizapai}} |jbo= |jv= |ka= |kaa= |kbd= |kim= |kjh= |kk= |kl= |km= |kn= |ko={{t|ko|피자|tr=pija}} (В Південній Кореї), {{t|ko|삐짜|tr=ppijja}} (В Північній Кореї) |koi= |kok= |kom= |krc= |krl= |ku= |kum= |kw= |ky= |la= |lad= |lb={{t|lb|Pizza|f}} |lbe= |lez= |li= |lmo= |ln= |lo= |lt= |lv= |mas= |mdf= |mg= |mi={{t|mi|parehe}} |mk={{t|mk|пица|f}} |ml= |mn={{t|mn|пицца}} |mo= |mr={{t|mr|पिझ्झा|m|tr=pijhjhā}} |ms={{t|ms|pizza}} |mt= |myv= |na= |nah= |nap= |nds={{t|nds|Pieza|m}} |nio= |nl={{t|nl|pizza|f}} |nn={{t|nn|pizza|m}} |no={{t|no|pizza|m}} |nov= |nv={{t|nv|náneeskaadítéél}} |oc= |oj= |os= |pa={{t|pa|ਪੀਜ਼ਾ|m|tr=pīzā}} |pap= |pl={{t|pl|pizza|f}} |pms= |prs= |ps= |pt= |qu= |rap= |rm= |ro={{t|ro|pizza|f}} |roh= |rom= |ru={{t|ru|пицца|f}} |rue= |rw= |sa= |sah= |sc= |scn= |sco= |sd= |se= |sjd= |sk= |sl={{t|sl|pica|f}}, {{t|sl|pizza|f}} |slovio-c= |slovio-l= |sm= |so= |sq={{t|sq|picë|f}} |sr={{t|sr|пица|f}} |sr-c= |sr-l={{t|sr|pica|f}} |su= |sv= |sw= |szl= |ta= |tab= |te= |tet= |tg= |th= |tk={{t|tk|pissa}} |tl= |tn= |tpi= |tr={{t|tr|pizza}} |tt.cyr={{t|tt|Пицца}} |tt.lat={{t|tt|Pitstsa}} |tt= |ttt= |ty= |tyv= |udm= |ug= |ur= |uz= |vep= |vi= |vo= |vot= |vro= |wa= |wo= |xal= |xh= |xsr= |yi= |yo= |yrk= |ze= |zh= |zh-cn= |zh-tw= |zu= |zza= }} === Джерела === <references /> * <!-- {{СУМ20-слов}} --> * <!-- {{СУМ-11}} --> {{довжина слова|4|lang=uk}} <!-- {{Категорія|Слова, відсутні у ВТССУМ|}} --> {{Категорія|мова=uk|Предметні слова|Страва|Італійська кухня|||}} {{=be=|{{PAGENAME}}}} === Морфосинтаксичні ознаки === {{імен be 1a f una|склади={{склади|пі́|ца}}|пі́ц|піц}} {{морфо-be|}} === Вимова === * {{transcriptions-be|пі́ца|пі́цы}} * {{audio2|LL-Q9091 (bel)-Ssvb-піца.wav|прослухати вимову}} === Семантичні властивості === ==== Значення ==== # {{as uk}} {{семантика|синоніми=|антоніми=|гіпероніми=страва, ежа|гіпоніми=}} {{приклад2|текст=|переклад=}} {{списки семантичних зв’язків}} ==== Холоніми ==== # ==== Мероніми ==== # ===Усталені та термінологічні словосполучення, фразеологізми === * ==== Колокації ==== ====Прислів'я та приказки ==== ===Споріднені слова=== {{спорідн| |згруб= |змп= |власні назви= |іменники=піцэрыя |прикметники= |дієслова= |прислівники= }} === Етимологія === Від {{етимологія|be}} === Джерела === {{довжина слова|4|lang=be}} {{Категорія|мова=be|Предметні слова|Страва|Італійська кухня|||}} hmnpuqfkwbn6f8mie1cr435xisvs4io Montenegros 0 90859 575000 2026-10-28T13:59:00Z Ігнат Лостюк 22957 Створена сторінка: = {{-en-}} = === Морфосинтаксичні ознаки === <b>{{склади|Mon|te|ne|gros}}</b> === Вимова === * {{transcription-en-us|mɒn.tɪˈnɛɡ.ɹəʊz], [mɒn.tɪˈneɪ.ɡɹəʊz|ˌmɑn.təˈneɪ.ɡɹoʊs], [ˌmɑn.tɪˈni.ɡɹoʊs}} * {{audio2|en-.ogg|прослухати вимову}} === Значення === # {{Форма-ім|мова=en|позначка=|число=мн|база=Montenegro}}. 575000 wikitext text/x-wiki = {{-en-}} = === Морфосинтаксичні ознаки === <b>{{склади|Mon|te|ne|gros}}</b> === Вимова === * {{transcription-en-us|mɒn.tɪˈnɛɡ.ɹəʊz], [mɒn.tɪˈneɪ.ɡɹəʊz|ˌmɑn.təˈneɪ.ɡɹoʊs], [ˌmɑn.tɪˈni.ɡɹoʊs}} * {{audio2|en-.ogg|прослухати вимову}} === Значення === # {{Форма-ім|мова=en|позначка=|число=мн|база=Montenegro}}. dzcstzc2g7f3jtdgxkt85smnope5bj9 асексуал 0 90860 575001 2026-10-28T14:07:57Z Ігнат Лостюк 22957 Створена сторінка: = {{-uk-}} = === Морфосинтаксичні ознаки === {{імен uk 1a m a|склади={{склади|а|сек|суа́л}}|асексуа́л}} {{морфо-uk|а-|сексуал}} === Вимова === * {{transcriptions-uk|асексуа́л|асексуа́ли}} * {{audio2|LL-Q8798 (ukr)-Tohaomg-{{subst:PAGENAME}}.wav|Прослухати вимову}} * {{транскрипція|ас{{еи}}ксуа́л}} === Семантичні... 575001 wikitext text/x-wiki = {{-uk-}} = === Морфосинтаксичні ознаки === {{імен uk 1a m a|склади={{склади|а|сек|суа́л}}|асексуа́л}} {{морфо-uk|а-|сексуал}} === Вимова === * {{transcriptions-uk|асексуа́л|асексуа́ли}} * {{audio2|LL-Q8798 (ukr)-Tohaomg-асексуал.wav|Прослухати вимову}} * {{транскрипція|ас{{еи}}ксуа́л}} === Семантичні властивості === ==== Значення ==== # Людина, яка не відчуває або відчуває слабкий статевий [[потяг]] до інших людей {{семантика|синоніми=|антоніми=|гіпероніми=ЛГБТ|гіпоніми=}} {{приклад2|||джерело=}} {{списки семантичних зв'язків}} === Усталені та термінологічні словосполучення, фразеологізми === * === Споріднені слова === {{спорідн| |згруб= |змп= |власні назви= |прізвища= |топоніми= |іменники=асексуалка, асексуальність |прикметники=асексуальний |дієприкметники= |дієслова= |прислівники= |повн= }} === Етимологія === Від {{етимологія:|так}} === Переклад === {{переклад| |ab= |abq= |ady= |af= |agh= |ain.kana= |ain.lat= |ain= |aja= |akz= |ale= |als= |alt= |an= |ang= |ar={{t|ar|لاجنسي|m|tr=lājinsiyy}} |arc.jud= |arc.syr= |art= |asm= |ast= |av= |ave= |ay= |az= |ba= |bar= |bat-smg= |be= |bg= |bm= |bn= |br= |bs= |bua= |ca= |ce= |chm= |chu.cyr= |chu.glag= |cjs= |co= |crh= |cs= |csb= |cv= |cy= |da= |dar= |de= |dsb= |dv= |el= |en={{t|en|asexual}} |eo= |es={{t|es|asexual|m}} |et= |eu= |eve= |evn= |ewe= |fa= |fi= |fo= |fr= |frp= |fur= |fy= |ga= |gag= |gd= |gl= |gld= |gn= |grc= |gu= |gv= |ha= |haw= |he= |hi= |hr= |hsb= |ht= |hu= |hy= |ia= |id= |ik= |inh= |io= |is= |it= |itl= |iu= |ja= |jbo= |jv= |ka= |kaa= |kbd= |kim= |kjh= |kk= |kl= |km= |kn= |ko= |koi= |kok= |kom= |krc= |krl= |ku= |kum= |kw= |ky= |la= |lad= |lb= |lbe= |lez= |li= |lmo= |ln= |lo= |lt= |lv= |mas= |mdf= |mg= |mi= |mk= |ml= |mn= |mo= |mr= |ms= |mt= |myv= |na= |nah= |nap= |nds= |nio= |nl= |nn= |no= |nov= |nv= |oc= |oj= |os= |pa= |pap= |pl={{t|pl|aseksualista|m}}, {{t|pl|aseksualistka|f}} |pms= |prs= |ps= |pt= |qu= |rap= |rm= |ro= |roh= |rom= |ru={{t|ru|асексуалист|m}}, {{t|ru|асексуал|m}} |rue= |rw= |sa= |sah= |sc= |scn= |sco= |sd= |se= |sjd= |sk= |sl= |slovio-c= |slovio-l= |sm= |so= |sq= |sr= |sr-c= |sr-l= |su= |sv= |sw= |szl= |ta= |tab= |te= |tet= |tg= |th= |tk= |tl= |tn= |tpi= |tr= |tt.cyr= |tt.lat= |tt= |ttt= |ty= |tyv= |udm= |ug= |ur= |uz= |vep= |vi= |vo= |vot= |vro= |wa= |wo= |xal= |xh= |xsr= |yi= |yo= |yrk= |ze= |zh= |zh-cn= |zh-tw= |zu= |zza= }} === Джерела === <references /> * <!-- {{СУМ20-слов}} --> * <!-- {{СУМ-11}} --> {{довжина слова|8|lang=uk}} <!-- {{Категорія|Слова, відсутні у ВТССУМ|}} --> {{Категорія|мова=uk|ЛГБТ|||||}} pjxdblw0ppbaldomczqd34ebdqvy4wh 575002 575001 2026-10-28T14:10:47Z Ігнат Лостюк 22957 575002 wikitext text/x-wiki = {{-uk-}} = === Морфосинтаксичні ознаки === {{імен uk 1a m a|склади={{склади|а|сек|суа́л}}|асексуа́л}} {{морфо-uk|а-|сексуал}} === Вимова === * {{transcriptions-uk|асексуа́л|асексуа́ли}} * {{audio2|LL-Q8798 (ukr)-Tohaomg-асексуал.wav|Прослухати вимову}} * {{транскрипція|ас{{еи}}ксуа́л}} === Семантичні властивості === ==== Значення ==== # Людина, яка не відчуває або відчуває слабкий статевий [[потяг]] до інших людей {{семантика|синоніми=|антоніми=|гіпероніми=ЛГБТ|гіпоніми=}} {{приклад2|||джерело=}} {{списки семантичних зв'язків}} === Усталені та термінологічні словосполучення, фразеологізми === * === Споріднені слова === {{спорідн| |згруб= |змп= |власні назви= |прізвища= |топоніми= |іменники=асексуалка, асексуальність |прикметники=асексуальний |дієприкметники= |дієслова= |прислівники= |повн= }} === Етимологія === Від {{етимологія:|так}} === Переклад === {{переклад| |ab= |abq= |ady= |af= |agh= |ain.kana= |ain.lat= |ain= |aja= |akz= |ale= |als= |alt= |an= |ang= |ar={{t|ar|لاجنسي|m|tr=lājinsiyy}} |arc.jud= |arc.syr= |art= |asm= |ast= |av= |ave= |ay= |az= |ba= |bar= |bat-smg= |be= |bg= |bm= |bn= |br= |bs= |bua= |ca= |ce= |chm= |chu.cyr= |chu.glag= |cjs= |co= |crh= |cs= |csb= |cv= |cy= |da= |dar= |de= |dsb= |dv= |el= |en={{t|en|asexual}} |eo= |es={{t|es|asexual|m}} |et= |eu= |eve= |evn= |ewe= |fa= |fi= |fo= |fr= |frp= |fur= |fy= |ga= |gag= |gd= |gl= |gld= |gn= |grc= |gu= |gv= |ha= |haw= |he= |hi= |hr= |hsb= |ht= |hu= |hy= |ia= |id= |ik= |inh= |io= |is= |it= |itl= |iu= |ja= |jbo= |jv= |ka= |kaa= |kbd= |kim= |kjh= |kk= |kl= |km= |kn= |ko= |koi= |kok= |kom= |krc= |krl= |ku= |kum= |kw= |ky= |la= |lad= |lb= |lbe= |lez= |li= |lmo= |ln= |lo= |lt= |lv= |mas= |mdf= |mg= |mi= |mk= |ml= |mn= |mo= |mr= |ms= |mt= |myv= |na= |nah= |nap= |nds= |nio= |nl= |nn= |no= |nov= |nv= |oc= |oj= |os= |pa= |pap= |pl={{t|pl|aseksualista|m}}, {{t|pl|aseksualistka|f}} |pms= |prs= |ps= |pt= |qu= |rap= |rm= |ro= |roh= |rom= |ru={{t|ru|асексуалист|m}}, {{t|ru|асексуал|m}} |rue= |rw= |sa= |sah= |sc= |scn= |sco= |sd= |se= |sjd= |sk= |sl= |slovio-c= |slovio-l= |sm= |so= |sq= |sr= |sr-c= |sr-l= |su= |sv= |sw= |szl= |ta= |tab= |te= |tet= |tg= |th= |tk= |tl= |tn= |tpi= |tr= |tt.cyr= |tt.lat= |tt= |ttt= |ty= |tyv= |udm= |ug= |ur= |uz= |vep= |vi= |vo= |vot= |vro= |wa= |wo= |xal= |xh= |xsr= |yi= |yo= |yrk= |ze= |zh= |zh-cn= |zh-tw= |zu= |zza= }} === Джерела === <references /> * <!-- {{СУМ20-слов}} --> * <!-- {{СУМ-11}} --> {{довжина слова|8|lang=uk}} <!-- {{Категорія|Слова, відсутні у ВТССУМ|}} --> {{Категорія|мова=uk|ЛГБТ|||||}} {{=ru=|{{PAGENAME}}}} === Морфосинтаксичні ознаки === {{імен ru 1a m a|склади={{склади|а|сек|суа́л}}|асексуа́л}} {{морфо-ru|а-|сексуал}} === Вимова === * {{transcriptions-ru|асексуа́л|асексуа́лы}} * {{audio2|Ru-асексуал.ogg|прослухати вимову}} === Семантичні властивості === ==== Значення ==== # {{as uk}} {{семантика|синоніми=|антоніми=|гіпероніми=ЛГБТ|гіпоніми=}} {{приклад2|текст=|переклад=}} {{списки семантичних зв’язків}} ==== Холоніми ==== # ==== Мероніми ==== # ===Усталені та термінологічні словосполучення, фразеологізми === * ==== Колокації ==== ====Прислів'я та приказки ==== ===Споріднені слова=== {{спорідн| |згруб= |змп= |власні назви= |прізвища= |топоніми= |іменники=асексуалка, асексуальность |прикметники=асексуальный |дієслова= |прислівники= }} === Етимологія === Від {{етимологія|ru}} === Джерела === {{довжина слова|8|lang=ru}} {{Категорія|мова=ru|ЛГБТ}} e59xl3mkp8qs94ybhobl50edthsmqei Шаблон:імен nl m 10 90861 575004 2026-10-28T21:25:37Z Ігнат Лостюк 22957 Створена сторінка: <b>{{{склади|}}}</b> Іменник, чоловічий рід.<includeonly>[[Категорія:Нідерландські іменники]][[Категорія:Чоловічий рід/nl]]</includeonly><noinclude>[[Категорія:Шаблони словозміни/nl]]</noinclude> 575004 wikitext text/x-wiki <b>{{{склади|}}}</b> Іменник, чоловічий рід.<includeonly>[[Категорія:Нідерландські іменники]][[Категорія:Чоловічий рід/nl]]</includeonly><noinclude>[[Категорія:Шаблони словозміни/nl]]</noinclude> cu5by6vg7qu5z3df2yjzktc1xkdnxzb Benelux 0 90862 575005 2026-10-28T21:29:08Z Ігнат Лостюк 22957 Створена сторінка: {{=en=}} === Морфосинтаксичні ознаки === {{імен en |склади={{склади|Be|ne|lux}}||}} {{топонім|en}} {{морфо-en}} === Вимова === * {{transcription|ˈbɛnəlʌks}} * {{audio2|LL-Q1860 (eng)-Vealhurl-Benelux.wav|прослухати вимову}} === Семантичні властивості === ==== Значення ==== # {{політ.|en}} [[Бенілюкс]] (економічний союз) {... 575005 wikitext text/x-wiki {{=en=}} === Морфосинтаксичні ознаки === {{імен en |склади={{склади|Be|ne|lux}}||}} {{топонім|en}} {{морфо-en}} === Вимова === * {{transcription|ˈbɛnəlʌks}} * {{audio2|LL-Q1860 (eng)-Vealhurl-Benelux.wav|прослухати вимову}} === Семантичні властивості === ==== Значення ==== # {{політ.|en}} [[Бенілюкс]] (економічний союз) {{семантика|синоніми=|антоніми=|гіпероніми=|гіпоніми=}} {{приклад|текст=|переклад=|джерело=}} # {{геогр.|en}} [[Бенілюкс]] (регіон Європи) {{семантика|синоніми=Low Countries|антоніми=|гіпероніми=Europe|гіпоніми=}} {{приклад|текст=|переклад=|джерело=}} {{списки семантичних зв’язків}} ===Усталені та термінологічні словосполучення, фразеологізми === * ==== Колокації ==== ====Прислів'я та приказки ==== ===Споріднені слова=== {{спорідн| |згруб= |змп= |власні назви= |прізвища= |топоніми= |іменники=Beneluxer, Beneluxian |прикметники=Beneluxian |дієприкметники= |дієслова= |прислівники= }} === Етимологія === Від {{lang|nl|Benelux|Бенілюкс}}, [[België|'''Be'''lgië]] + [[Nederland|'''Ne'''derland]] + [[Luxemburg|'''Lux'''emburg]]. === Джерела === {{довжина слова|7|lang=en}} {{Категорія|мова=en|Європа|||||}} {{=nl=}} ===Морфосинтаксичні ознаки=== {{імен nl m|склади={{склади|Be|ne|lux}}|}} {{морфо|}} ===Вимова=== * {{transcription|ˌbeː.nəˈlyks], [ˈbeː.nəˌlyks}} * {{audio2|Nl-Benelux.ogg|прослухати вимову}} ===Семантичні властивості=== ==== Значення ==== # {{політ.|nl}} [[Бенілюкс]] (економічний союз) {{семантика|синоніми=|антоніми=|гіпероніми=|гіпоніми=}} {{приклад|текст=|переклад=|джерело=}} # {{геогр.|nl}} [[Бенілюкс]] (регіон Європи) {{семантика|синоніми=Lage Landen|антоніми=|гіпероніми=Europa|гіпоніми=}} {{приклад|текст=|переклад=|джерело=}} {{списки семантичних зв’язків}} ===Усталені та термінологічні словосполучення, фразеологізми === * ==== Колокації ==== ====Прислів'я та приказки ==== ===Споріднені слова=== {{спорідн| |згруб= |змп= |власні назви= |прізвища= |топоніми= |іменники= |прикметники= |дієслова= |прислівники= }} ===Етимологія=== Від [[нідерландська мова|нідерл.]] [[België|'''Be'''lgië]] + [[Nederland|'''Ne'''derland]] + [[Luxemburg|'''Lux'''emburg]]. === Джерела === {{довжина слова|7|lang=nl}} {{Категорія|мова=nl|Європа|||||}} lu74nlea893p35929t4nb1ixcotav8j Шаблон:transcriptions-sk 10 90863 575007 2026-10-29T08:43:26Z Ігнат Лостюк 22957 Створена сторінка: {{{{{|safesubst:}}}ifsubst|<includeonly>{{transcriptions|{{{{{|safesubst:}}}#invoke:sk-pron|ipa|{{{phon|{{{1|{{{{{|safesubst:}}}PAGENAME}}}}}}}}|adj={{{adj|}}}|gem={{{gem|}}}|pal={{{pal|}}}}}|{{ subst:#if:{{{2|}}}|{{{{{|safesubst:}}}#invoke:sk-pron|ipa|{{{2}}}|adj={{{adj|}}}|gem={{{gem|}}}|pal={{{pal|}}}}}}}{{ subst:#if:{{{3|}}}|{{subst:!}}{{{3}}}}}{{ subst:#if:{{{4|}}}|{{subst:!}}{{{4}}}}}{{ subst:#if:{{{норма|}}}|{{subst:!}}норма={{{норм... 575007 wikitext text/x-wiki {{{{{|safesubst:}}}ifsubst|<includeonly>{{transcriptions|{{{{{|safesubst:}}}#invoke:sk-pron|ipa|{{{phon|{{{1|{{{{{|safesubst:}}}PAGENAME}}}}}}}}|adj={{{adj|}}}|gem={{{gem|}}}|pal={{{pal|}}}}}|{{ subst:#if:{{{2|}}}|{{{{{|safesubst:}}}#invoke:sk-pron|ipa|{{{2}}}|adj={{{adj|}}}|gem={{{gem|}}}|pal={{{pal|}}}}}}}{{ subst:#if:{{{3|}}}|{{subst:!}}{{{3}}}}}{{ subst:#if:{{{4|}}}|{{subst:!}}{{{4}}}}}{{ subst:#if:{{{норма|}}}|{{subst:!}}норма={{{норма}}}}}{{ subst:#if:{{{джерело|}}}|{{subst:!}}джерело={{{джерело}}}}}}}</includeonly>|<includeonly>{{transcriptions|{{#invoke:sk-pron|ipa|{{{phon|{{{1|{{PAGENAME}}}}}}}}|adj={{{adj|}}}|gem={{{gem|}}}|pal={{{pal|}}}}}|2={{#if:{{{2|}}}|{{#invoke:sk-pron|ipa|{{{2}}}|adj={{{adj|}}}|gem={{{gem|}}}|pal={{{pal|}}}}}}}|3={{{3|}}}|4={{{4|}}}|норма={{{норма|}}}|джерело={{{джерело|}}}|lang=sk}}</includeonly>}}{{#if:{{{3|}}}||{{#ifexist:Media:Sk-{{PAGENAME}}.ogg|[[Категорія:Озвучені на Вікісховищі]]}}}}<noinclude>{{documentation}}</noinclude> o36dw00sql7ng6vc60iofsaolygg3t9 Модуль:sk-pron 828 90864 575008 2026-10-29T08:43:50Z Ігнат Лостюк 22957 Створена сторінка: local export = {} -- ============================================================================= -- [[ DATA TABLES ]] -- ============================================================================= -- [[ 1. Phonological Sets ]] -- local diphthongs_set = { ["ia"]=true, ["ie"]=true, ["iu"]=true, ["ô"]=true } local bigraphs_set = { ["ch"]=true, ["dz"]=true, ["dž"]=true } -- Combined Vowels (Short & Long + Foreign) local vowels_set = { ["a"]=true, ["... 575008 Scribunto text/plain local export = {} -- ============================================================================= -- [[ DATA TABLES ]] -- ============================================================================= -- [[ 1. Phonological Sets ]] -- local diphthongs_set = { ["ia"]=true, ["ie"]=true, ["iu"]=true, ["ô"]=true } local bigraphs_set = { ["ch"]=true, ["dz"]=true, ["dž"]=true } -- Combined Vowels (Short & Long + Foreign) local vowels_set = { ["a"]=true, ["e"]=true, ["i"]=true, ["o"]=true, ["u"]=true, ["y"]=true, ["ä"]=true, ["ö"]=true, ["ü"]=true, ["á"]=true, ["é"]=true, ["í"]=true, ["ó"]=true, ["ú"]=true, ["ý"]=true, ["ő"]=true, ["ű"]=true } -- Voicing & Sonorants local sonorants = { ['m']=true, ['n']=true, ['ň']=true, ['l']=true, ['ľ']=true, ['ĺ']=true, ['r']=true, ['ŕ']=true, ['j']=true, ['v']=true } local voiced_paired = { ['b']=true, ['d']=true, ['ď']=true, ['g']=true, ['dz']=true, ['dž']=true, ['h']=true, ['z']=true, ['ž']=true, ['w']=true } local voiceless_paired = { ['p']=true, ['t']=true, ['ť']=true, ['k']=true, ['c']=true, ['č']=true, ['ch']=true, ['s']=true, ['š']=true, ['f']=true } local voicing_map = { -- Voiced to Voiceless ['b']='p', ['d']='t', ['ď']='ť', ['g']='k', ['dz']='c', ['dž']='č', ['h']='ch', ['w']='f', ['z']='s', ['ž']='š', -- Voiceless to Voiced ['p']='b', ['t']='d', ['ť']='ď', ['k']='g', ['c']='dz', ['č']='dž', ['ch']='h', ['f']='w', ['s']='z', ['š']='ž' } -- [[ 2. Assimilation & Encoding ]] -- local dental_mergers = { ['z']='dzdz', ['dz']='dzdz', ['s']='cc', ['c']='cc', ['ž']='dždž', ['dž']='dždž', ['š']='čč', ['č']='čč' } -- Mergers: Encodes t.s -> 15, d.z -> 26, etc. local c1_enc = {['t']=1, ['d']=2, ['ť']=3, ['ď']=4} local c2_enc = { ['c']=1, ['dz']=2, ['č']=3, ['dž']=4, ['s']=5, ['z']=6, ['š']=7, ['ž']=8 } -- Decoding: Used by IPA renderer to convert "15" -> t.ts local c1_dec = { 't', 'd', 'c', 'ɟ' } local c2_dec = { 't͡s', 'd͡z', 't͡ʃ', 'd͡ʒ', 's', 'z', 'ʃ', 'ʒ' } -- [[ 3. Morphological & Lexical Exceptions ]] -- local prefixes = { ['pod']=true, ['ob']=true, ['od']=true, ['ráz']=true, ['roz']=true, ['bez']=true, ['ab']=true, ['vz']=true, ['pred']=true, ['nad']=true } local prepositions_set = { ["bez"]=true, ["cez"]=true, ["do"]=true, ["k"]=true, ["ku"]=true, ["na"]=true, ["nad"]=true, ["o"]=true, ["od"]=true, ["po"]=true, ["pod"]=true, ["pre"]=true, ["pred"]=true, ["pri"]=true, ["s"]=true, ["so"]=true, ["u"]=true, ["v"]=true, ["vo"]=true, ["z"]=true, ["za"]=true, ["zo"]=true } local clitics = { ["byť"]=true, ["som"]=true, ["si"]=true, ["je"]=true, ["sme"]=true, ["ste"]=true, ["sú"]=true, ["bol"]=true, ["mi"]=true, ["ti"]=true, ["mu"]=true, ["jej"]=true, ["ma"]=true, ["ťa"]=true, ["sa"]=true, ["ho"]=true, ["ju"]=true, ["nám"]=true, ["vám"]=true, ["im"]=true, ["nás"]=true, ["vás"]=true, ["ich"]=true, ["ten"]=true, ["tá"]=true, ["to"]=true, ["on"]=true, ["a"]=true, ["i"]=true, ["aj"]=true, ["že"]=true, ["keď"]=true, ["až"]=true, ["len"]=true, ["či"]=true, ["kde"]=true, ["ba"]=true, ["ak"]=true, ["už"]=true, ["by"]=true } local preposition_blockers = { ['mnou']=true, ['mne']=true, ['tebou']=true, ['ňou']=true, ['ním']=true, ['nami']=true, ['vami']=true, ['nimi']=true, ['tebe']=true, ['nemu']=true, ['nej']=true, ['nám']=true, ['vám']=true, ['nim']=true } -- [[ 4. IPA Output Maps ]] -- local ipa_map = { -- Vowels ['a']='a', ['á']='aː', ['ä']='æ', ['e']='e', ['é']='eː', ['i']='i', ['í']='iː', ['o']='ɔ', ['ó']='ɔː', ['ô']='u̯ɔ', ['u']='u', ['ú']='uː', ['y']='i', ['ý']='iː', ['ö']='ø', ['ő']='øː', ['ü']='y', ['ű']='yː', -- Consonants ['b']='b', ['c']='t͡s', ['č']='t͡ʃ', ['d']='d', ['ď']='ɟ', ['g']='ɡ', ['h']='ɦ', ['j']='j', ['k']='k', ['l']='l', ['ľ']='ʎ', ['m']='m', ['n']='n', ['ň']='ɲ', ['p']='p', ['r']='r', ['ř']='r̝', ['ŕ']='r̩ː', ['ĺ']='l̩ː', ['s']='s', ['š']='ʃ', ['t']='t', ['ť']='c', ['z']='z', ['ž']='ʒ', ['x']='x', -- Special ['v']='v', ['w']='w', ['F']='F', ['f']='f', ['ů']='ů', ["'"]='ˈ', -- Digraphs/Multigraphs ['dz']='d͡z', ['dž']='d͡ʒ', ['ch']='x', ['dzdz']='d͡zː', ['dždž']='d͡ʒː', ['ia']='ɪ̯a', ['ie']='ɪ̯e', ['iu']='ɪ̯u' } local variant_map = { ["H"]={"ɣ","ɦ"}, ["W"]={"ʋ","v"}, ["U"]={"u̯","ʋ"}, ["J"]={"ɪ̯","j"}, ["N"]={"n","n̠"} } -- Module-level state variables local IS_MO_VARIANT = false local NAMESPACE = "" local PAGENAME = "" local LOAN = 0 local HARD_SUFFIX = 0 local AS_IMP = 0 -- ============================================================================= -- [[ HELPER FUNCTIONS ]] -- ============================================================================= function get_voiced_counterpart(char) return voicing_map[char] end -- Tokenizer (Preserves digraphs) function get_graphemes(word) local units = {} local len = mw.ustring.len(word) local i = 1 local is_loan = (LOAN == 1) while i <= len do local two_char = mw.ustring.sub(word, i, i + 1) if bigraphs_set[two_char] or (diphthongs_set[two_char] and not is_loan) then table.insert(units, two_char) i = i + 2 else table.insert(units, mw.ustring.sub(word, i, i)) i = i + 1 end end return units end -- Resolve Cluster (Contact Assimilation Logic) function resolve_cluster(c1, c2, is_boundary) -- GUARD: v/f complex logic if c1 == 'v' or c1 == 'f' then return nil end -- 1. SPECIAL: <h> or <ch> logic if c1 == 'ch' or c1 == 'h' then if (voiced_paired[c2] or sonorants[c2]) and is_boundary then return 'H' .. c2 end end -- 2. Standard Assimilation local c1_new = c1 if (voiceless_paired[c1] and (voiced_paired[c2] or (is_boundary and (sonorants[c2] or vowels_set[c2])))) or (voiced_paired[c1] and voiceless_paired[c2]) then c1_new = voicing_map[c1] or c1 end -- 3. Mergers & Gemination if c1_enc[c1_new] and dental_mergers[c2] then if is_boundary then local d1, d2 = c1_enc[c1_new], c2_enc[c2] if d1 and d2 then return d1 .. d2 end end return dental_mergers[c2] end if c1_new ~= c1 then return c1_new .. c2 end return nil end function apply_assimilation(tokens, i, j, is_boundary) if IS_MO_VARIANT and i == #tokens - 3 then return end local res = resolve_cluster(tokens[i], tokens[j], is_boundary) if res then local res_tokens = get_graphemes(res) if #res_tokens == 2 then tokens[i] = res_tokens[1]; tokens[j] = res_tokens[2] elseif #res_tokens == 1 then tokens[i] = res_tokens[1]; tokens[j] = "" end end end local function check_vowel(units, i, d, len) while i > 0 and i <= len do local t = units[i] if vowels_set[t] or diphthongs_set[t] then return true end if t ~= "'" and t ~= "-" and t ~= " " and t ~= "_" then return false end i = i + d end return false end function split_into_syllables(word) local units = get_graphemes(word) local nuc, syl, len = {}, {}, #units -- Helper: Check if index k is a Nucleus local function is_nuc(k) local u = units[k]; if not u then return false end if diphthongs_set[u] or vowels_set[u] then return true end if not (u == 'r' or u == 'l' or u == 'ŕ' or u == 'ĺ') then return false end return not (check_vowel(units, k-1, -1, len) or check_vowel(units, k+1, 1, len)) end for i = 1, len do if is_nuc(i) then table.insert(nuc, i) end end if #nuc == 0 then return {table.concat(units)} end local start = 1 for k = 1, #nuc - 1 do local curr, next_n = nuc[k], nuc[k+1] local split = curr -- Default: Split after vowel (V-CV) local c_cnt, first_c = 0, nil for j = curr + 1, next_n - 1 do local c = units[j] if c == "-" or c == " " then split = j; c_cnt = -1; break -- 1. Hard Boundary elseif c == "'" then split = j - 1; c_cnt = -1; break -- 2. Stress Boundary elseif c ~= "." then c_cnt = c_cnt + 1; first_c = first_c or j end end if c_cnt >= 2 then split = first_c end -- 3. Cluster Rule (VC-CV) table.insert(syl, table.concat(units, "", start, split)) start = split + 1 end table.insert(syl, table.concat(units, "", start)) return syl end -- [[ Core IPA Transformation Logic ]] -- local function ipa_transform_worker(word) -- 1. Preprocess: Strip punctuation but map boundaries local bound, idx = {}, 0 word = mw.ustring.gsub(word, ".", function(c) if c == "-" or c == "." then bound[idx] = true; return "" end idx = idx + 1 end) -- Returns cleaned word, populates 'bound' local sub = mw.ustring.sub local w_len, res, i = idx, {}, 1 local function is_C(c) return not c or c == "" or (not vowels_set[c] and not diphthongs_set[c]) end while i <= w_len do local c1 = sub(word, i, i) if c1 == "+" then i = i + 1 else local nxt = sub(word, i+1, i+1) local c2_raw = (nxt == "+") and sub(word, i+2, i+2) or nxt local c2, c4 = c1 .. c2_raw, sub(word, i, i+3) local jump = (nxt == "+") and 1 or 0 local sym, step, raw = nil, 1, c1 if c4 == "dzdz" or c4 == "dždž" then sym, step, raw = ipa_map[c4], 4, c4 elseif (c1 == c2_raw or c2 == "čš") and ipa_map[c1] and not vowels_set[c1] then sym, step, raw = ipa_map[c1] .. "ː", 2 + jump, c1 elseif (ipa_map[c2] and not diphthongs_set[c2]) or (diphthongs_set[c2] and (LOAN~=1 or jump>0) and not bound[i]) then sym, step, raw = ipa_map[c2], 2 + jump, c2 -- treat as diphthong only if native or explicitly marked with + else sym = ipa_map[c1] or c1 end if sym then if (raw=='r' or raw=='l') and is_C(i>1 and sub(word,i-1,i-1)) and is_C(sub(word,i+step,i+step)) then sym = sym .. '̩' -- mark liquid as syllabic if sandwiched between non-vowels end table.insert(res, sym) end i = i + step end end local out = table.concat(res) -- Post-processing for loanword endings (-ovan-) if LOAN == 1 then local sfx = mw.ustring.match(word, ".*ovan(.+)$") if sfx then local m = ipa_map local fix = { ["ie"]=m["ie"], ["ia"]=m["ia"], ["iu"]=m["iu"], ["í"]=m["í"], ["ím"]=m["í"]..(m["m"] or 'm'), ["iam"]=m["ia"]..(m["m"] or 'm'), ["iach"]=m["ia"]..(m["x"] or 'x'), ["iami"]=m["ia"]..(m["m"] or 'm')..(m["i"] or 'i') } if fix[sfx] then local bad = (m["n"] or 'n') .. sfx:gsub(".", function(c) return m[c] or c end) out = mw.ustring.gsub(out, bad:gsub("[%p]", "%%%0") .. "$", (m['ň'] or 'ɲ') .. fix[sfx]) -- correct [n] to [ň] before soft vowels in loan suffixes end end end return out end -- [[ 2. STAGE 2: PHONETIC RESOLVER ]] -- function resolve_phonetic_base(base_text) local res = {} local len = mw.ustring.len(base_text) local i = 1 -- Map for standard phonetic realization local map = { ['v']='ʋ', ['w']='v', ['F']='v', ['f']='f', ['ů']='u̯', ['I']='ɪ̯' } local sub = mw.ustring.sub -- Local ref while i <= len do local c = sub(base_text, i, i) local symbol = map[c] or c local next_c = (i < len) and sub(base_text, i+1, i+1) or "" local next_resolved = map[next_c] or next_c -- Apply FULL Nasal Assimilation if c == 'm' then if next_resolved == 'f' or next_resolved == 'v' or next_resolved == 'ʋ' then symbol = 'ɱ' end elseif c == 'n' then if next_resolved == 'k' or next_resolved == 'ɡ' then symbol = 'ŋ' elseif next_resolved == 's' or next_resolved == 'z' or next_resolved == 'ʃ' or next_resolved == 'ʒ' then symbol = 'N' elseif next_resolved == 'x' then symbol = 'ɰ̃' elseif next_resolved == 'p' or next_resolved == 'b' then symbol = 'm' end end table.insert(res, symbol) i = i + 1 end return table.concat(res) end -- [[ 3. FINAL GENERATION ]] -- local function expand_variants(txt) local find, sub, gsub = mw.ustring.find, mw.ustring.sub, mw.ustring.gsub -- 1. Digits: Binary Branch (All-Split vs All-Merged) if find(txt, "%d[ˈ%s_]*%d") then local res = {} for m = 1, 2 do local s = gsub(txt, "(%d)([ˈ%s_]*)(%d)", function(d1, sep, d2) d1, d2 = tonumber(d1), tonumber(d2) if m == 1 then -- Split (Unassimilated) local f = ((d1+d2)%2==1) and (d1 + (d2%2==0 and 1 or -1)) or d1 return (c1_dec[f] or "") .. sep .. (c2_dec[d2] or "") else -- Merged (Assimilated) local c = c2_dec[d2>4 and d2-4 or d2] or "" -- If stressed: Unreleased + Short (t̚ ˈt͡s). If not: Long (t͡sː) return find(sep, "ˈ") and (sub(c,1,1).."̚ ˈ"..c) or (c.."ː") end end) for _, v in ipairs(expand_variants(s)) do table.insert(res, v) end end return res end -- 2. Letters: Recursive Expansion (HWUJN) local s, e, k = find(txt, "([HWUJN])") if not s then return {txt} end -- Base case local res, h, t = {}, sub(txt, 1, s-1), sub(txt, e+1) for _, r in ipairs(variant_map[k]) do for _, sub in ipairs(expand_variants(h..r..t)) do table.insert(res, sub) end end return res end local function format_ipa_output(res, frame) local distinct = table.concat(res.high) ~= table.concat(res.low) local args = { "sk", "/" .. res.phonemic .. "/" } local function add(list, tag) for i, ipa in ipairs(list) do local qs = {} if distinct and tag and i == 1 then table.insert(qs, tag) end if mw.ustring.find(ipa, "ɦ ?ˈ?ɦ") then table.insert(qs, "rare") end local q = #qs > 0 and "<q:" .. table.concat(qs, ", ") .. ">" or "" table.insert(args, "[" .. ipa .. "]" .. q) end end add(res.high, "high register") if distinct then add(res.low, "common") end return frame:expandTemplate{ title = "IPA", args = args } end local function get_ipa_nuclei(ipa) local s = mw.ustring.gsub(ipa, "[/ %[%]ˈ%.]", "") local nuclei, skip, sub = {}, 0, mw.ustring.sub mw.ustring.gsub(s, "()([rluɪaæeiɔuøy])", function(p, c) if p < skip then return end local nuc, len = nil, 1 local nxt = sub(s, p+1, p+1) if (c=="r" or c=="l") and nxt=="̩" then len = (sub(s, p+2, p+2)=="ː") and 3 or 2 nuc = sub(s, p, p+len-1) elseif (c=="u" or c=="ɪ") and nxt=="̯" then len = 3; nuc = sub(s, p, p+2) elseif mw.ustring.find(c, "[aæeiɔuøy]") then if sub(s, p+1, p+2) == "u̯" then len = 3; nuc = sub(s, p, p+2) else len = (nxt=="ː") and 2 or 1; nuc = sub(s, p, p+len-1) end end if nuc then table.insert(nuclei, {ipa=nuc, s=p, e=p+len-1}); skip = p+len end end) return nuclei, s end local function format_rhymes(frame, nuclei, s) local c = #nuclei if c == 0 then return "" end local rhyme = mw.ustring.sub(s, (c > 1 and nuclei[c-1] or nuclei[1]).s) return "\n*" .. frame:expandTemplate{title="rhymes", args={"sk", rhyme, s=c}} end local function format_hyphenation(word, ipa, frame, respelling, nuclei) if #nuclei <= 1 then return "" end local sub, find, lower, len = mw.ustring.sub, mw.ustring.find, mw.ustring.lower, mw.ustring.len local w_lower = lower(word) -- 1. Parse Respelling (Boundaries & Forbidden Nuclei) local forced, forbidden, idx, last = {}, {}, 0, "" if respelling then local clean_check = mw.ustring.gsub(lower(respelling), "[%.%-%+]", "") if clean_check == w_lower then for i = 1, len(respelling) do local c = sub(respelling, i, i) if c == "+" then if last ~= "i" and last ~= "í" then forbidden[idx + 1] = true end elseif c:match("[%.%-]") then if idx > 0 then forced[idx] = true end else idx = idx + 1; last = lower(c) end end end end local map = { ['a']='a', ['aː']='á', ['æ']='ä', ['e']='e', ['eː']='é', ['i']='[iy]', ['iː']='[íý]', ['o']='o', ['oː']='ó', ['ɔ']='o', ['ɔː']='ó', ['u']='u', ['uː']='ú', ['u̯ɔ']='ô', ['ɔu̯']='ou', ['ø']='ö', ['øː']='ő', ['y']='ü', ['yː']='ű', ['ɪ̯a']='ia', ['ɪ̯e']='ie', ['ɪ̯u']='iu', ['r̩']='r', ['r̩ː']='ŕ', ['l̩']='l', ['l̩ː']='ĺ' } -- Helper: Find next valid nucleus (skips forbidden off-glides) local function get_nuc(pat, start) while true do local s, e = find(w_lower, pat, start) if not s or not forbidden[s] then return s, e end start = s + 1 end end local args, pos = {"sk"}, 1 local seg_start = 1 for i = 1, #nuclei do local s, e = get_nuc(map[nuclei[i].ipa] or ".", pos) if not s then return "" end if i < #nuclei then local s2, _ = get_nuc(map[nuclei[i+1].ipa] or ".", e + 1) if not s2 then return "" end -- Default: Split before last consonant (V-CV) or between consonants (VC-CV) local clust = sub(w_lower, e+1, s2-1) -- If cluster has space, DO NOT split. if find(clust, " ") then pos = e + 1 else local c_len = len(clust) local off = 0 if c_len >= 2 then local dig = sub(clust, 1, 2) off = (dig == "ch" or dig == "dz" or dig == "dž") and (c_len > 2 and 2 or 0) or 1 end for k = e, s2 - 1 do if forced[k] then off = k - e; break end end table.insert(args, sub(word, seg_start, e + off)) seg_start = e + off + 1 pos = e + off + 1 end else table.insert(args, sub(word, seg_start)) end end return "\n*" .. frame:expandTemplate{title="hyph", args=args} end -- ============================================================================= -- [[ PROCESSING STEPS ]] -- ============================================================================= function step_foreign_graphemes(word) word = mw.ustring.lower(word) word = mw.ustring.gsub(word, "ů", "ú") word = mw.ustring.gsub(word, "x", "ks") word = mw.ustring.gsub(word, "w", "v") word = mw.ustring.gsub(word, "qu", "kv") word = mw.ustring.gsub(word, "[,']", "") return word end local function is_empty_or_space(w, k) local x = mw.ustring.sub(w, k, k) return x == "" or x == " " end function step_softening(w) if LOAN == 1 then return w end local soft = {['d']='ď',['t']='ť',['n']='ň',['l']='ľ'} local sub = mw.ustring.sub local res, len = {}, mw.ustring.len(w) for i = 1, len do local c = sub(w, i, i) local s = false if soft[c] then local n1 = sub(w, i+1, i+1) local n2 = sub(w, i+1, i+2) if n2 == "ia" or n2 == "ie" or n2 == "iu" then s = true elseif n1 == "i" or n1 == "í" or n1 == "e" then s = true if HARD_SUFFIX == 1 then local n3 = sub(w, i+1, i+3) if (n1 == "i" or n1 == "í" or n1 == "e") and is_empty_or_space(w, i+2) then s = false elseif n2 == "ej" and is_empty_or_space(w, i+3) then s = false elseif (n3 == "eho" or n3 == "emu") and is_empty_or_space(w, i+4) then s = false end end end end table.insert(res, s and soft[c] or c) end return table.concat(res) end function step_prosody(word) -- 1. Merge Prepositions word = mw.ustring.gsub(word, "%f[%a](%a+) +", function(w) return prepositions_set[mw.ustring.lower(w)] and (w .. "_") or nil end) -- 2. Add Stress return mw.ustring.gsub(word, "%S+", function(w) local raw = mw.ustring.lower(mw.ustring.gsub(w, "_", "")) -- Exclusions: Clitics OR Starts with hyphen OR Single letter OR No syllabic nucleus if clitics[raw] or mw.ustring.sub(w, 1, 1) == "-" or mw.ustring.sub(w, -1) == "-" or mw.ustring.len(w) == 1 or not mw.ustring.find(raw, "[aáäeéiíoóuúyýöőüűôrŕlĺ]") then return w end return "'" .. w end) end function step_simplification(word) local patterns = { "s[tť]s[tk]", "[pd][tz]sk", "[tďdz]st", "[ztd]šť", "[zdt]sk", "ptč" } local function resolve(w) local c1 = mw.ustring.sub(w, 1, 1) if c1 == "s" then return c1 .. mw.ustring.sub(w, 4) elseif c1 == "p" then return c1 .. mw.ustring.sub(w, 3) else w = mw.ustring.gsub(w, "z", "") if mw.ustring.len(w) <= 2 then return w end local c2 = mw.ustring.sub(w, 2, 2) local head = (c2 == "s") and "c" or "č" return head .. mw.ustring.sub(w, 3) end end for _, pat in ipairs(patterns) do word = mw.ustring.gsub(word, pat, resolve) end return word end function step_assimilation(word) -- [[ 1. Pre-processing & Morphological Exceptions ]] -- word = mw.ustring.gsub(word, "('[nv]ášmu)(%f[%A])", function(match) return mw.ustring.gsub(match, "š", "ž") end) -- 2. sme -> zme (Only if surrounded by boundaries) if mw.ustring.find(word, "sme") then word = mw.ustring.gsub(mw.ustring.gsub(word, "^sme$", "zme"), "^sme(%s)", "zme%1") word = mw.ustring.gsub(mw.ustring.gsub(word, "(%s)sme$", "%1zme"), "(%s)sme(%s)", "%1zme%2") end -- A. Prefix "eks-" (eks- + vowel/voiced -> egz-) local v_set = "[aeiouyáéíóúýäôglmnňrjvzhdbžg]" word = mw.ustring.gsub(word, "^('?)eks("..v_set..")", "%1egz%2") word = mw.ustring.gsub(word, "([%s_])('?)eks("..v_set..")", "%1%2egz%3") word = mw.ustring.gsub(word, "^('?)dis("..v_set..")", "%1diz%2") word = mw.ustring.gsub(word, "([%s_])('?)dis("..v_set..")", "%1%diz%3") -- B. Preposition Vocalization (so/ku -> zo/gu) word = mw.ustring.gsub(word, "([%s'])([sk][ou])(_)([^%s%._]+)", function(before, prep, sep, next_w) if not preposition_blockers[next_w] then local voiced = (prep == "so") and "zo" or "gu" return before .. voiced .. sep .. next_w end end) -- C. Imperative Suffix "-me" (Voice preceding consonant) if AS_IMP == 1 then word = mw.ustring.gsub(word, "([ptsťkfcčš])me", function(c) return (voicing_map[c] or c) .. "me" end) end -- [[ 2. Main Assimilation Logic ]] -- local tokens = get_graphemes(word) local len = #tokens local str = table.concat(tokens) -- Check for "-mo" variant trigger if mw.ustring.find(word, "[ptsčškcfx]%.mo%f[%A]") or mw.ustring.find(word, "ch%.mo%f[%A]") then IS_MO_VARIANT = true end -- Helpers local function prev_C(k) while k >= 1 do local t = tokens[k]; if t ~= "'" and t ~= "_" and t ~= " " and t ~= "-" and t ~= "." then return k end k = k - 1 end end local function is_bound(k) return k < 1 or k > len or tokens[k] == " " or tokens[k] == "_" or tokens[k] == "-" end local function prev_bound(k) local l, idx = 0, k - 1 while idx - l >= 1 do local t = tokens[idx - l] if t == "'" or t == "_" or t == " " or t == "-" then break end l = l + 1 end return l end local function match_prefix(start_idx, end_idx) local diff = end_idx - start_idx + 1 if diff == 2 then return prefixes[tokens[start_idx] .. tokens[start_idx+1]] elseif diff == 3 then return prefixes[tokens[start_idx] .. tokens[start_idx+1] .. tokens[start_idx+2]] elseif diff == 4 then return prefixes[tokens[start_idx] .. tokens[start_idx+1] .. tokens[start_idx+2] .. tokens[start_idx+3]] end return false end local simple = not mw.ustring.find(str, "[ %-_]") local w_offset = (simple and tokens[1] == "'") and 1 or 0 -- p_len = Exact count of characters before 'i' local p_len = simple and (len - w_offset - 1) or prev_bound(len) -- Reverse Loop (Regressive Assimilation) for i = len, 2, -1 do -- Reset state if we crossed a word boundary if not simple and is_bound(i+1) then p_len = prev_bound(i) end local is_prefix = false -- A. Prefix Check if p_len >= 2 and p_len <= 4 then if match_prefix(i - p_len, i - 1) then apply_assimilation(tokens, i - 1, i, true) is_prefix = true end end -- B. General Assimilation (If not prefix) if not is_prefix then local tgt = prev_C(i-1) if tgt then local is_opt = false for k = tgt + 1, i - 1 do if tokens[k] == " " or tokens[k] == "-" or tokens[k] == "_" then is_opt = true; break end end -- Handles both Devoicing (standard) and Voicing (at boundaries) apply_assimilation(tokens, tgt, i, is_opt) end end p_len = p_len - 1 end return table.concat(tokens) end local function is_nuc(c) return vowels_set[c] or diphthongs_set[c] or c == 'r' or c == 'l' or c == 'ŕ' or c == 'ĺ' end function step_v_f_j_realization(word) word = mw.ustring.gsub(mw.ustring.gsub(word, "anje[lľ]", "aňɪ̯el"), "_", "") word = mw.ustring.gsub(mw.ustring.gsub(word, "%+u", "ů"), "%+i", "I") local syllables = split_into_syllables(word) local res_syls = {} local clean_chars = {} local char_map = {} local idx = 1 for s_i, syl in ipairs(syllables) do char_map[s_i] = {} for c_i = 1, mw.ustring.len(syl) do local c = mw.ustring.sub(syl, c_i, c_i) if c ~= "'" and c ~= "." and c ~= "-" then clean_chars[idx] = c char_map[s_i][c_i] = idx idx = idx + 1 end end end for s_i, syl in ipairs(syllables) do local s_len, res = mw.ustring.len(syl), {} local nuc_i = nil for k = 1, s_len do if is_nuc(mw.ustring.sub(syl, k, k)) then nuc_i = k; break end end for c_i = 1, s_len do local c = mw.ustring.sub(syl, c_i, c_i) local out = c if c ~= "'" and c ~= "." and c ~= "-" then local flat_idx = char_map[s_i][c_i] local prev = clean_chars[flat_idx - 1] local nxt = clean_chars[flat_idx + 1] local after_nxt = clean_chars[flat_idx + 2] -- 2. DETECT POSITIONS (Ignore ' and .) local is_onset = (mw.ustring.gsub(mw.ustring.sub(syl, 1, c_i-1), "['%.%-]", "") == "") local is_coda = nuc_i and (c_i > nuc_i) if c == 'u' then if prev == 'o' and (not nxt or nxt == ' ') then out = "ů" end elseif c == 'v' then if is_coda and prev and is_nuc(prev) then local is_u_prev = (prev == 'u' or prev == 'ú') local is_amb_next = (nxt and (nxt == 'n' or nxt == 'ň' or nxt == 'l' or nxt == 'ľ' or nxt == 'r')) out = (is_u_prev or is_amb_next) and "U" or "ů" else local prev_nuc = false if s_i > 1 then prev_nuc = prev and is_nuc(prev) or false end local next_is_vn = (nxt and (nxt == 'n' or nxt == 'ň')) if is_onset and (s_i == 1 or prev_nuc) and next_is_vn then out = "W" else local next_is_z = (nxt == 'z') local after_z = next_is_z and after_nxt local vz_unv = (next_is_z and after_z and voiceless_paired[after_z]) if vz_unv or (nxt and voiceless_paired[nxt]) then out = "f" elseif nxt and voiced_paired[nxt] and not sonorants[nxt] then out = "w" else out = "v" end end end elseif c == 'f' then if is_coda and nxt and (voiced_paired[nxt] or sonorants[nxt]) then out = "F" end elseif c == 'j' then local prev_is_iy = (prev == 'i' or prev == 'í' or prev == 'y' or prev == 'ý') if is_onset then out = "j" elseif not vowels_set[prev] or prev_is_iy then out = "J" else out = "I" end end end table.insert(res, out) end table.insert(res_syls, table.concat(res)) end return table.concat(res_syls) end function step_final_devoicing(word) local tokens = get_graphemes(word) local i = #tokens while i > 0 do local c = tokens[i] if voiced_paired[c] then tokens[i] = voicing_map[c]; i = i - 1 elseif voiceless_paired[c] then i = i - 1 else break end end return table.concat(tokens) end function generate_all_transcriptions(word, original_word) local gsub = mw.ustring.gsub -- A. Base IPA local base_ipa = ipa_transform_worker(word) -- B. Phonemic local phonemic = gsub(gsub(base_ipa, "ˈ", ""), "[vwFůWU]", "v") phonemic = gsub(gsub(phonemic, "J", "j"), "H", "ɦ") phonemic = gsub(gsub(phonemic, "ä", "æ"), "ľ", "ʎ") phonemic = gsub(phonemic, "I", "j") -- Decode Dental Clusters phonemic = gsub(phonemic, "(%d['%s_]*%d)", function(k) local d1, sep, d2 = mw.ustring.match(k, "(%d)(['%s_]*)(%d)") d1, d2 = tonumber(d1), tonumber(d2) local d1_fix = ((d1+d2)%2==1) and (d1 + (d2%2==0 and 1 or -1)) or d1 return (c1_dec[d1_fix] or "") .. sep .. (c2_dec[d2] or "") end) -- C. Phonetic Base local phonetic_base = resolve_phonetic_base(base_ipa) local roots = { phonetic_base } -- D. Handle "-mo" Variant if IS_MO_VARIANT and original_word then local c = mw.ustring.sub(original_word, -4, -4) -- grab the consonant immediately preceding the ".mo" suffix local c_voiced = voicing_map[c] if c_voiced then local ipa_voiceless = (ipa_map[c] or c) local ipa_voiced = (ipa_map[c_voiced] or c_voiced) local pattern = mw.ustring.gsub(ipa_voiceless, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") .. "mɔ$" -- escape IPA chars for regex and target the end of string local new_root, count = mw.ustring.gsub(phonetic_base, pattern, ipa_voiced .. "mɔ") if count > 0 then table.insert(roots, new_root) end end end -- E. Expand Variants local bases_expanded = {} for _, root in ipairs(roots) do local expansions = expand_variants(root) for _, e in ipairs(expansions) do table.insert(bases_expanded, e) end end -- F. Generate Registers local high_res, low_res = {}, {} for _, ipa in ipairs(bases_expanded) do table.insert(high_res, (gsub(ipa, "æ", "ɛɐ̯"))) local l = gsub(gsub(gsub(ipa, "æ", "e"), "ʎ", "l"), "l ?ˈ?l", "lː") table.insert(low_res, (gsub(l, "n([fvʋ])", "ɱ%1"))) end return { phonemic = phonemic, high = high_res, low = low_res } end -- ============================================================================= -- [[ MAIN EXPORT ]] -- ============================================================================= function export.show(frame) local args = frame:getParent().args local title = mw.title.getCurrentTitle() local t = args["t"] or "" NAMESPACE = title.nsText PAGENAME = (NAMESPACE == "") and title.text or (args["pagename"] or "Pagename not specified") LOAN = mw.ustring.find(t, "loan") and 1 or 0 HARD_SUFFIX = mw.ustring.find(t, "adj") and 1 or 0 AS_IMP = mw.ustring.find(t, "imp") and 1 or 0 IS_MO_VARIANT = false local word = (args["1"] ~= nil) and args["1"] or PAGENAME local original_word = word local manual_hard = (args["1"] ~= nil and mw.ustring.find(args["1"], "[DTNL]")) -- Display Switches local show_rhymes = (args["r"] ~= "0") and not mw.ustring.find(word, "^%-") and not mw.ustring.find(word, "%-$") local show_hyph = (args["h"] ~= "0") local audio = args["a"] or false local audio_accent = args["aa"] or "" -- Pipeline if not manual_hard then word = step_foreign_graphemes(word) end word = step_softening(word) if manual_hard then word = step_foreign_graphemes(word) end word = step_prosody(word) word = step_simplification(word) word = step_assimilation(word) word = step_v_f_j_realization(word) word = step_final_devoicing(word) local final = generate_all_transcriptions(word, original_word) local nuclei, clean_ipa = get_ipa_nuclei(final.phonemic) local out = format_ipa_output(final, frame) if audio then out = out .. "\n*" .. frame:expandTemplate{title="audio", args={"sk", audio, a=audio_accent}} end if show_rhymes then out = out .. format_rhymes(frame, nuclei, clean_ipa) end if show_hyph then out = out .. format_hyphenation(PAGENAME, final.phonemic, frame, original_word, nuclei) end return out end return export tmz5mzaq9nr0y7rz6oiwhctv35yogps 575009 575008 2026-10-29T08:46:15Z Ігнат Лостюк 22957 575009 Scribunto text/plain local export = {} local m_IPA = require("Модуль:IPA") local sk = require("Модуль:languages-data").getByCode("sk") -- ============================================================================= -- [[ DATA TABLES ]] -- ============================================================================= -- [[ 1. Phonological Sets ]] -- local diphthongs_set = { ["ia"]=true, ["ie"]=true, ["iu"]=true, ["ô"]=true } local bigraphs_set = { ["ch"]=true, ["dz"]=true, ["dž"]=true } -- Combined Vowels (Short & Long + Foreign) local vowels_set = { ["a"]=true, ["e"]=true, ["i"]=true, ["o"]=true, ["u"]=true, ["y"]=true, ["ä"]=true, ["ö"]=true, ["ü"]=true, ["á"]=true, ["é"]=true, ["í"]=true, ["ó"]=true, ["ú"]=true, ["ý"]=true, ["ő"]=true, ["ű"]=true } -- Voicing & Sonorants local sonorants = { ['m']=true, ['n']=true, ['ň']=true, ['l']=true, ['ľ']=true, ['ĺ']=true, ['r']=true, ['ŕ']=true, ['j']=true, ['v']=true } local voiced_paired = { ['b']=true, ['d']=true, ['ď']=true, ['g']=true, ['dz']=true, ['dž']=true, ['h']=true, ['z']=true, ['ž']=true, ['w']=true } local voiceless_paired = { ['p']=true, ['t']=true, ['ť']=true, ['k']=true, ['c']=true, ['č']=true, ['ch']=true, ['s']=true, ['š']=true, ['f']=true } local voicing_map = { -- Voiced to Voiceless ['b']='p', ['d']='t', ['ď']='ť', ['g']='k', ['dz']='c', ['dž']='č', ['h']='ch', ['w']='f', ['z']='s', ['ž']='š', -- Voiceless to Voiced ['p']='b', ['t']='d', ['ť']='ď', ['k']='g', ['c']='dz', ['č']='dž', ['ch']='h', ['f']='w', ['s']='z', ['š']='ž' } -- [[ 2. Assimilation & Encoding ]] -- local dental_mergers = { ['z']='dzdz', ['dz']='dzdz', ['s']='cc', ['c']='cc', ['ž']='dždž', ['dž']='dždž', ['š']='čč', ['č']='čč' } -- Mergers: Encodes t.s -> 15, d.z -> 26, etc. local c1_enc = {['t']=1, ['d']=2, ['ť']=3, ['ď']=4} local c2_enc = { ['c']=1, ['dz']=2, ['č']=3, ['dž']=4, ['s']=5, ['z']=6, ['š']=7, ['ž']=8 } -- Decoding: Used by IPA renderer to convert "15" -> t.ts local c1_dec = { 't', 'd', 'c', 'ɟ' } local c2_dec = { 't͡s', 'd͡z', 't͡ʃ', 'd͡ʒ', 's', 'z', 'ʃ', 'ʒ' } -- [[ 3. Morphological & Lexical Exceptions ]] -- local prefixes = { ['pod']=true, ['ob']=true, ['od']=true, ['ráz']=true, ['roz']=true, ['bez']=true, ['ab']=true, ['vz']=true, ['pred']=true, ['nad']=true } local prepositions_set = { ["bez"]=true, ["cez"]=true, ["do"]=true, ["k"]=true, ["ku"]=true, ["na"]=true, ["nad"]=true, ["o"]=true, ["od"]=true, ["po"]=true, ["pod"]=true, ["pre"]=true, ["pred"]=true, ["pri"]=true, ["s"]=true, ["so"]=true, ["u"]=true, ["v"]=true, ["vo"]=true, ["z"]=true, ["za"]=true, ["zo"]=true } local clitics = { ["byť"]=true, ["som"]=true, ["si"]=true, ["je"]=true, ["sme"]=true, ["ste"]=true, ["sú"]=true, ["bol"]=true, ["mi"]=true, ["ti"]=true, ["mu"]=true, ["jej"]=true, ["ma"]=true, ["ťa"]=true, ["sa"]=true, ["ho"]=true, ["ju"]=true, ["nám"]=true, ["vám"]=true, ["im"]=true, ["nás"]=true, ["vás"]=true, ["ich"]=true, ["ten"]=true, ["tá"]=true, ["to"]=true, ["on"]=true, ["a"]=true, ["i"]=true, ["aj"]=true, ["že"]=true, ["keď"]=true, ["až"]=true, ["len"]=true, ["či"]=true, ["kde"]=true, ["ba"]=true, ["ak"]=true, ["už"]=true, ["by"]=true } local preposition_blockers = { ['mnou']=true, ['mne']=true, ['tebou']=true, ['ňou']=true, ['ním']=true, ['nami']=true, ['vami']=true, ['nimi']=true, ['tebe']=true, ['nemu']=true, ['nej']=true, ['nám']=true, ['vám']=true, ['nim']=true } -- [[ 4. IPA Output Maps ]] -- local ipa_map = { -- Vowels ['a']='a', ['á']='aː', ['ä']='æ', ['e']='e', ['é']='eː', ['i']='i', ['í']='iː', ['o']='ɔ', ['ó']='ɔː', ['ô']='u̯ɔ', ['u']='u', ['ú']='uː', ['y']='i', ['ý']='iː', ['ö']='ø', ['ő']='øː', ['ü']='y', ['ű']='yː', -- Consonants ['b']='b', ['c']='t͡s', ['č']='t͡ʃ', ['d']='d', ['ď']='ɟ', ['g']='ɡ', ['h']='ɦ', ['j']='j', ['k']='k', ['l']='l', ['ľ']='ʎ', ['m']='m', ['n']='n', ['ň']='ɲ', ['p']='p', ['r']='r', ['ř']='r̝', ['ŕ']='r̩ː', ['ĺ']='l̩ː', ['s']='s', ['š']='ʃ', ['t']='t', ['ť']='c', ['z']='z', ['ž']='ʒ', ['x']='x', -- Special ['v']='v', ['w']='w', ['F']='F', ['f']='f', ['ů']='ů', ["'"]='ˈ', -- Digraphs/Multigraphs ['dz']='d͡z', ['dž']='d͡ʒ', ['ch']='x', ['dzdz']='d͡zː', ['dždž']='d͡ʒː', ['ia']='ɪ̯a', ['ie']='ɪ̯e', ['iu']='ɪ̯u' } local variant_map = { ["H"]={"ɣ","ɦ"}, ["W"]={"ʋ","v"}, ["U"]={"u̯","ʋ"}, ["J"]={"ɪ̯","j"}, ["N"]={"n","n̠"} } -- Module-level state variables local IS_MO_VARIANT = false local NAMESPACE = "" local PAGENAME = "" local LOAN = 0 local HARD_SUFFIX = 0 local AS_IMP = 0 -- ============================================================================= -- [[ HELPER FUNCTIONS ]] -- ============================================================================= function get_voiced_counterpart(char) return voicing_map[char] end -- Tokenizer (Preserves digraphs) function get_graphemes(word) local units = {} local len = mw.ustring.len(word) local i = 1 local is_loan = (LOAN == 1) while i <= len do local two_char = mw.ustring.sub(word, i, i + 1) if bigraphs_set[two_char] or (diphthongs_set[two_char] and not is_loan) then table.insert(units, two_char) i = i + 2 else table.insert(units, mw.ustring.sub(word, i, i)) i = i + 1 end end return units end -- Resolve Cluster (Contact Assimilation Logic) function resolve_cluster(c1, c2, is_boundary) -- GUARD: v/f complex logic if c1 == 'v' or c1 == 'f' then return nil end -- 1. SPECIAL: <h> or <ch> logic if c1 == 'ch' or c1 == 'h' then if (voiced_paired[c2] or sonorants[c2]) and is_boundary then return 'H' .. c2 end end -- 2. Standard Assimilation local c1_new = c1 if (voiceless_paired[c1] and (voiced_paired[c2] or (is_boundary and (sonorants[c2] or vowels_set[c2])))) or (voiced_paired[c1] and voiceless_paired[c2]) then c1_new = voicing_map[c1] or c1 end -- 3. Mergers & Gemination if c1_enc[c1_new] and dental_mergers[c2] then if is_boundary then local d1, d2 = c1_enc[c1_new], c2_enc[c2] if d1 and d2 then return d1 .. d2 end end return dental_mergers[c2] end if c1_new ~= c1 then return c1_new .. c2 end return nil end function apply_assimilation(tokens, i, j, is_boundary) if IS_MO_VARIANT and i == #tokens - 3 then return end local res = resolve_cluster(tokens[i], tokens[j], is_boundary) if res then local res_tokens = get_graphemes(res) if #res_tokens == 2 then tokens[i] = res_tokens[1]; tokens[j] = res_tokens[2] elseif #res_tokens == 1 then tokens[i] = res_tokens[1]; tokens[j] = "" end end end local function check_vowel(units, i, d, len) while i > 0 and i <= len do local t = units[i] if vowels_set[t] or diphthongs_set[t] then return true end if t ~= "'" and t ~= "-" and t ~= " " and t ~= "_" then return false end i = i + d end return false end function split_into_syllables(word) local units = get_graphemes(word) local nuc, syl, len = {}, {}, #units -- Helper: Check if index k is a Nucleus local function is_nuc(k) local u = units[k]; if not u then return false end if diphthongs_set[u] or vowels_set[u] then return true end if not (u == 'r' or u == 'l' or u == 'ŕ' or u == 'ĺ') then return false end return not (check_vowel(units, k-1, -1, len) or check_vowel(units, k+1, 1, len)) end for i = 1, len do if is_nuc(i) then table.insert(nuc, i) end end if #nuc == 0 then return {table.concat(units)} end local start = 1 for k = 1, #nuc - 1 do local curr, next_n = nuc[k], nuc[k+1] local split = curr -- Default: Split after vowel (V-CV) local c_cnt, first_c = 0, nil for j = curr + 1, next_n - 1 do local c = units[j] if c == "-" or c == " " then split = j; c_cnt = -1; break -- 1. Hard Boundary elseif c == "'" then split = j - 1; c_cnt = -1; break -- 2. Stress Boundary elseif c ~= "." then c_cnt = c_cnt + 1; first_c = first_c or j end end if c_cnt >= 2 then split = first_c end -- 3. Cluster Rule (VC-CV) table.insert(syl, table.concat(units, "", start, split)) start = split + 1 end table.insert(syl, table.concat(units, "", start)) return syl end -- [[ Core IPA Transformation Logic ]] -- local function ipa_transform_worker(word) -- 1. Preprocess: Strip punctuation but map boundaries local bound, idx = {}, 0 word = mw.ustring.gsub(word, ".", function(c) if c == "-" or c == "." then bound[idx] = true; return "" end idx = idx + 1 end) -- Returns cleaned word, populates 'bound' local sub = mw.ustring.sub local w_len, res, i = idx, {}, 1 local function is_C(c) return not c or c == "" or (not vowels_set[c] and not diphthongs_set[c]) end while i <= w_len do local c1 = sub(word, i, i) if c1 == "+" then i = i + 1 else local nxt = sub(word, i+1, i+1) local c2_raw = (nxt == "+") and sub(word, i+2, i+2) or nxt local c2, c4 = c1 .. c2_raw, sub(word, i, i+3) local jump = (nxt == "+") and 1 or 0 local sym, step, raw = nil, 1, c1 if c4 == "dzdz" or c4 == "dždž" then sym, step, raw = ipa_map[c4], 4, c4 elseif (c1 == c2_raw or c2 == "čš") and ipa_map[c1] and not vowels_set[c1] then sym, step, raw = ipa_map[c1] .. "ː", 2 + jump, c1 elseif (ipa_map[c2] and not diphthongs_set[c2]) or (diphthongs_set[c2] and (LOAN~=1 or jump>0) and not bound[i]) then sym, step, raw = ipa_map[c2], 2 + jump, c2 -- treat as diphthong only if native or explicitly marked with + else sym = ipa_map[c1] or c1 end if sym then if (raw=='r' or raw=='l') and is_C(i>1 and sub(word,i-1,i-1)) and is_C(sub(word,i+step,i+step)) then sym = sym .. '̩' -- mark liquid as syllabic if sandwiched between non-vowels end table.insert(res, sym) end i = i + step end end local out = table.concat(res) -- Post-processing for loanword endings (-ovan-) if LOAN == 1 then local sfx = mw.ustring.match(word, ".*ovan(.+)$") if sfx then local m = ipa_map local fix = { ["ie"]=m["ie"], ["ia"]=m["ia"], ["iu"]=m["iu"], ["í"]=m["í"], ["ím"]=m["í"]..(m["m"] or 'm'), ["iam"]=m["ia"]..(m["m"] or 'm'), ["iach"]=m["ia"]..(m["x"] or 'x'), ["iami"]=m["ia"]..(m["m"] or 'm')..(m["i"] or 'i') } if fix[sfx] then local bad = (m["n"] or 'n') .. sfx:gsub(".", function(c) return m[c] or c end) out = mw.ustring.gsub(out, bad:gsub("[%p]", "%%%0") .. "$", (m['ň'] or 'ɲ') .. fix[sfx]) -- correct [n] to [ň] before soft vowels in loan suffixes end end end return out end -- [[ 2. STAGE 2: PHONETIC RESOLVER ]] -- function resolve_phonetic_base(base_text) local res = {} local len = mw.ustring.len(base_text) local i = 1 -- Map for standard phonetic realization local map = { ['v']='ʋ', ['w']='v', ['F']='v', ['f']='f', ['ů']='u̯', ['I']='ɪ̯' } local sub = mw.ustring.sub -- Local ref while i <= len do local c = sub(base_text, i, i) local symbol = map[c] or c local next_c = (i < len) and sub(base_text, i+1, i+1) or "" local next_resolved = map[next_c] or next_c -- Apply FULL Nasal Assimilation if c == 'm' then if next_resolved == 'f' or next_resolved == 'v' or next_resolved == 'ʋ' then symbol = 'ɱ' end elseif c == 'n' then if next_resolved == 'k' or next_resolved == 'ɡ' then symbol = 'ŋ' elseif next_resolved == 's' or next_resolved == 'z' or next_resolved == 'ʃ' or next_resolved == 'ʒ' then symbol = 'N' elseif next_resolved == 'x' then symbol = 'ɰ̃' elseif next_resolved == 'p' or next_resolved == 'b' then symbol = 'm' end end table.insert(res, symbol) i = i + 1 end return table.concat(res) end -- [[ 3. FINAL GENERATION ]] -- local function expand_variants(txt) local find, sub, gsub = mw.ustring.find, mw.ustring.sub, mw.ustring.gsub -- 1. Digits: Binary Branch (All-Split vs All-Merged) if find(txt, "%d[ˈ%s_]*%d") then local res = {} for m = 1, 2 do local s = gsub(txt, "(%d)([ˈ%s_]*)(%d)", function(d1, sep, d2) d1, d2 = tonumber(d1), tonumber(d2) if m == 1 then -- Split (Unassimilated) local f = ((d1+d2)%2==1) and (d1 + (d2%2==0 and 1 or -1)) or d1 return (c1_dec[f] or "") .. sep .. (c2_dec[d2] or "") else -- Merged (Assimilated) local c = c2_dec[d2>4 and d2-4 or d2] or "" -- If stressed: Unreleased + Short (t̚ ˈt͡s). If not: Long (t͡sː) return find(sep, "ˈ") and (sub(c,1,1).."̚ ˈ"..c) or (c.."ː") end end) for _, v in ipairs(expand_variants(s)) do table.insert(res, v) end end return res end -- 2. Letters: Recursive Expansion (HWUJN) local s, e, k = find(txt, "([HWUJN])") if not s then return {txt} end -- Base case local res, h, t = {}, sub(txt, 1, s-1), sub(txt, e+1) for _, r in ipairs(variant_map[k]) do for _, sub in ipairs(expand_variants(h..r..t)) do table.insert(res, sub) end end return res end local function format_ipa_output(res, frame) local distinct = table.concat(res.high) ~= table.concat(res.low) local args = { "sk", "/" .. res.phonemic .. "/" } local function add(list, tag) for i, ipa in ipairs(list) do local qs = {} if distinct and tag and i == 1 then table.insert(qs, tag) end if mw.ustring.find(ipa, "ɦ ?ˈ?ɦ") then table.insert(qs, "rare") end local q = #qs > 0 and "<q:" .. table.concat(qs, ", ") .. ">" or "" table.insert(args, "[" .. ipa .. "]" .. q) end end add(res.high, "high register") if distinct then add(res.low, "common") end return frame:expandTemplate{ title = "IPA", args = args } end local function get_ipa_nuclei(ipa) local s = mw.ustring.gsub(ipa, "[/ %[%]ˈ%.]", "") local nuclei, skip, sub = {}, 0, mw.ustring.sub mw.ustring.gsub(s, "()([rluɪaæeiɔuøy])", function(p, c) if p < skip then return end local nuc, len = nil, 1 local nxt = sub(s, p+1, p+1) if (c=="r" or c=="l") and nxt=="̩" then len = (sub(s, p+2, p+2)=="ː") and 3 or 2 nuc = sub(s, p, p+len-1) elseif (c=="u" or c=="ɪ") and nxt=="̯" then len = 3; nuc = sub(s, p, p+2) elseif mw.ustring.find(c, "[aæeiɔuøy]") then if sub(s, p+1, p+2) == "u̯" then len = 3; nuc = sub(s, p, p+2) else len = (nxt=="ː") and 2 or 1; nuc = sub(s, p, p+len-1) end end if nuc then table.insert(nuclei, {ipa=nuc, s=p, e=p+len-1}); skip = p+len end end) return nuclei, s end local function format_rhymes(frame, nuclei, s) local c = #nuclei if c == 0 then return "" end local rhyme = mw.ustring.sub(s, (c > 1 and nuclei[c-1] or nuclei[1]).s) return "\n*" .. frame:expandTemplate{title="rhymes", args={"sk", rhyme, s=c}} end local function format_hyphenation(word, ipa, frame, respelling, nuclei) if #nuclei <= 1 then return "" end local sub, find, lower, len = mw.ustring.sub, mw.ustring.find, mw.ustring.lower, mw.ustring.len local w_lower = lower(word) -- 1. Parse Respelling (Boundaries & Forbidden Nuclei) local forced, forbidden, idx, last = {}, {}, 0, "" if respelling then local clean_check = mw.ustring.gsub(lower(respelling), "[%.%-%+]", "") if clean_check == w_lower then for i = 1, len(respelling) do local c = sub(respelling, i, i) if c == "+" then if last ~= "i" and last ~= "í" then forbidden[idx + 1] = true end elseif c:match("[%.%-]") then if idx > 0 then forced[idx] = true end else idx = idx + 1; last = lower(c) end end end end local map = { ['a']='a', ['aː']='á', ['æ']='ä', ['e']='e', ['eː']='é', ['i']='[iy]', ['iː']='[íý]', ['o']='o', ['oː']='ó', ['ɔ']='o', ['ɔː']='ó', ['u']='u', ['uː']='ú', ['u̯ɔ']='ô', ['ɔu̯']='ou', ['ø']='ö', ['øː']='ő', ['y']='ü', ['yː']='ű', ['ɪ̯a']='ia', ['ɪ̯e']='ie', ['ɪ̯u']='iu', ['r̩']='r', ['r̩ː']='ŕ', ['l̩']='l', ['l̩ː']='ĺ' } -- Helper: Find next valid nucleus (skips forbidden off-glides) local function get_nuc(pat, start) while true do local s, e = find(w_lower, pat, start) if not s or not forbidden[s] then return s, e end start = s + 1 end end local args, pos = {"sk"}, 1 local seg_start = 1 for i = 1, #nuclei do local s, e = get_nuc(map[nuclei[i].ipa] or ".", pos) if not s then return "" end if i < #nuclei then local s2, _ = get_nuc(map[nuclei[i+1].ipa] or ".", e + 1) if not s2 then return "" end -- Default: Split before last consonant (V-CV) or between consonants (VC-CV) local clust = sub(w_lower, e+1, s2-1) -- If cluster has space, DO NOT split. if find(clust, " ") then pos = e + 1 else local c_len = len(clust) local off = 0 if c_len >= 2 then local dig = sub(clust, 1, 2) off = (dig == "ch" or dig == "dz" or dig == "dž") and (c_len > 2 and 2 or 0) or 1 end for k = e, s2 - 1 do if forced[k] then off = k - e; break end end table.insert(args, sub(word, seg_start, e + off)) seg_start = e + off + 1 pos = e + off + 1 end else table.insert(args, sub(word, seg_start)) end end return "\n*" .. frame:expandTemplate{title="hyph", args=args} end -- ============================================================================= -- [[ PROCESSING STEPS ]] -- ============================================================================= function step_foreign_graphemes(word) word = mw.ustring.lower(word) word = mw.ustring.gsub(word, "ů", "ú") word = mw.ustring.gsub(word, "x", "ks") word = mw.ustring.gsub(word, "w", "v") word = mw.ustring.gsub(word, "qu", "kv") word = mw.ustring.gsub(word, "[,']", "") return word end local function is_empty_or_space(w, k) local x = mw.ustring.sub(w, k, k) return x == "" or x == " " end function step_softening(w) if LOAN == 1 then return w end local soft = {['d']='ď',['t']='ť',['n']='ň',['l']='ľ'} local sub = mw.ustring.sub local res, len = {}, mw.ustring.len(w) for i = 1, len do local c = sub(w, i, i) local s = false if soft[c] then local n1 = sub(w, i+1, i+1) local n2 = sub(w, i+1, i+2) if n2 == "ia" or n2 == "ie" or n2 == "iu" then s = true elseif n1 == "i" or n1 == "í" or n1 == "e" then s = true if HARD_SUFFIX == 1 then local n3 = sub(w, i+1, i+3) if (n1 == "i" or n1 == "í" or n1 == "e") and is_empty_or_space(w, i+2) then s = false elseif n2 == "ej" and is_empty_or_space(w, i+3) then s = false elseif (n3 == "eho" or n3 == "emu") and is_empty_or_space(w, i+4) then s = false end end end end table.insert(res, s and soft[c] or c) end return table.concat(res) end function step_prosody(word) -- 1. Merge Prepositions word = mw.ustring.gsub(word, "%f[%a](%a+) +", function(w) return prepositions_set[mw.ustring.lower(w)] and (w .. "_") or nil end) -- 2. Add Stress return mw.ustring.gsub(word, "%S+", function(w) local raw = mw.ustring.lower(mw.ustring.gsub(w, "_", "")) -- Exclusions: Clitics OR Starts with hyphen OR Single letter OR No syllabic nucleus if clitics[raw] or mw.ustring.sub(w, 1, 1) == "-" or mw.ustring.sub(w, -1) == "-" or mw.ustring.len(w) == 1 or not mw.ustring.find(raw, "[aáäeéiíoóuúyýöőüűôrŕlĺ]") then return w end return "'" .. w end) end function step_simplification(word) local patterns = { "s[tť]s[tk]", "[pd][tz]sk", "[tďdz]st", "[ztd]šť", "[zdt]sk", "ptč" } local function resolve(w) local c1 = mw.ustring.sub(w, 1, 1) if c1 == "s" then return c1 .. mw.ustring.sub(w, 4) elseif c1 == "p" then return c1 .. mw.ustring.sub(w, 3) else w = mw.ustring.gsub(w, "z", "") if mw.ustring.len(w) <= 2 then return w end local c2 = mw.ustring.sub(w, 2, 2) local head = (c2 == "s") and "c" or "č" return head .. mw.ustring.sub(w, 3) end end for _, pat in ipairs(patterns) do word = mw.ustring.gsub(word, pat, resolve) end return word end function step_assimilation(word) -- [[ 1. Pre-processing & Morphological Exceptions ]] -- word = mw.ustring.gsub(word, "('[nv]ášmu)(%f[%A])", function(match) return mw.ustring.gsub(match, "š", "ž") end) -- 2. sme -> zme (Only if surrounded by boundaries) if mw.ustring.find(word, "sme") then word = mw.ustring.gsub(mw.ustring.gsub(word, "^sme$", "zme"), "^sme(%s)", "zme%1") word = mw.ustring.gsub(mw.ustring.gsub(word, "(%s)sme$", "%1zme"), "(%s)sme(%s)", "%1zme%2") end -- A. Prefix "eks-" (eks- + vowel/voiced -> egz-) local v_set = "[aeiouyáéíóúýäôglmnňrjvzhdbžg]" word = mw.ustring.gsub(word, "^('?)eks("..v_set..")", "%1egz%2") word = mw.ustring.gsub(word, "([%s_])('?)eks("..v_set..")", "%1%2egz%3") word = mw.ustring.gsub(word, "^('?)dis("..v_set..")", "%1diz%2") word = mw.ustring.gsub(word, "([%s_])('?)dis("..v_set..")", "%1%diz%3") -- B. Preposition Vocalization (so/ku -> zo/gu) word = mw.ustring.gsub(word, "([%s'])([sk][ou])(_)([^%s%._]+)", function(before, prep, sep, next_w) if not preposition_blockers[next_w] then local voiced = (prep == "so") and "zo" or "gu" return before .. voiced .. sep .. next_w end end) -- C. Imperative Suffix "-me" (Voice preceding consonant) if AS_IMP == 1 then word = mw.ustring.gsub(word, "([ptsťkfcčš])me", function(c) return (voicing_map[c] or c) .. "me" end) end -- [[ 2. Main Assimilation Logic ]] -- local tokens = get_graphemes(word) local len = #tokens local str = table.concat(tokens) -- Check for "-mo" variant trigger if mw.ustring.find(word, "[ptsčškcfx]%.mo%f[%A]") or mw.ustring.find(word, "ch%.mo%f[%A]") then IS_MO_VARIANT = true end -- Helpers local function prev_C(k) while k >= 1 do local t = tokens[k]; if t ~= "'" and t ~= "_" and t ~= " " and t ~= "-" and t ~= "." then return k end k = k - 1 end end local function is_bound(k) return k < 1 or k > len or tokens[k] == " " or tokens[k] == "_" or tokens[k] == "-" end local function prev_bound(k) local l, idx = 0, k - 1 while idx - l >= 1 do local t = tokens[idx - l] if t == "'" or t == "_" or t == " " or t == "-" then break end l = l + 1 end return l end local function match_prefix(start_idx, end_idx) local diff = end_idx - start_idx + 1 if diff == 2 then return prefixes[tokens[start_idx] .. tokens[start_idx+1]] elseif diff == 3 then return prefixes[tokens[start_idx] .. tokens[start_idx+1] .. tokens[start_idx+2]] elseif diff == 4 then return prefixes[tokens[start_idx] .. tokens[start_idx+1] .. tokens[start_idx+2] .. tokens[start_idx+3]] end return false end local simple = not mw.ustring.find(str, "[ %-_]") local w_offset = (simple and tokens[1] == "'") and 1 or 0 -- p_len = Exact count of characters before 'i' local p_len = simple and (len - w_offset - 1) or prev_bound(len) -- Reverse Loop (Regressive Assimilation) for i = len, 2, -1 do -- Reset state if we crossed a word boundary if not simple and is_bound(i+1) then p_len = prev_bound(i) end local is_prefix = false -- A. Prefix Check if p_len >= 2 and p_len <= 4 then if match_prefix(i - p_len, i - 1) then apply_assimilation(tokens, i - 1, i, true) is_prefix = true end end -- B. General Assimilation (If not prefix) if not is_prefix then local tgt = prev_C(i-1) if tgt then local is_opt = false for k = tgt + 1, i - 1 do if tokens[k] == " " or tokens[k] == "-" or tokens[k] == "_" then is_opt = true; break end end -- Handles both Devoicing (standard) and Voicing (at boundaries) apply_assimilation(tokens, tgt, i, is_opt) end end p_len = p_len - 1 end return table.concat(tokens) end local function is_nuc(c) return vowels_set[c] or diphthongs_set[c] or c == 'r' or c == 'l' or c == 'ŕ' or c == 'ĺ' end function step_v_f_j_realization(word) word = mw.ustring.gsub(mw.ustring.gsub(word, "anje[lľ]", "aňɪ̯el"), "_", "") word = mw.ustring.gsub(mw.ustring.gsub(word, "%+u", "ů"), "%+i", "I") local syllables = split_into_syllables(word) local res_syls = {} local clean_chars = {} local char_map = {} local idx = 1 for s_i, syl in ipairs(syllables) do char_map[s_i] = {} for c_i = 1, mw.ustring.len(syl) do local c = mw.ustring.sub(syl, c_i, c_i) if c ~= "'" and c ~= "." and c ~= "-" then clean_chars[idx] = c char_map[s_i][c_i] = idx idx = idx + 1 end end end for s_i, syl in ipairs(syllables) do local s_len, res = mw.ustring.len(syl), {} local nuc_i = nil for k = 1, s_len do if is_nuc(mw.ustring.sub(syl, k, k)) then nuc_i = k; break end end for c_i = 1, s_len do local c = mw.ustring.sub(syl, c_i, c_i) local out = c if c ~= "'" and c ~= "." and c ~= "-" then local flat_idx = char_map[s_i][c_i] local prev = clean_chars[flat_idx - 1] local nxt = clean_chars[flat_idx + 1] local after_nxt = clean_chars[flat_idx + 2] -- 2. DETECT POSITIONS (Ignore ' and .) local is_onset = (mw.ustring.gsub(mw.ustring.sub(syl, 1, c_i-1), "['%.%-]", "") == "") local is_coda = nuc_i and (c_i > nuc_i) if c == 'u' then if prev == 'o' and (not nxt or nxt == ' ') then out = "ů" end elseif c == 'v' then if is_coda and prev and is_nuc(prev) then local is_u_prev = (prev == 'u' or prev == 'ú') local is_amb_next = (nxt and (nxt == 'n' or nxt == 'ň' or nxt == 'l' or nxt == 'ľ' or nxt == 'r')) out = (is_u_prev or is_amb_next) and "U" or "ů" else local prev_nuc = false if s_i > 1 then prev_nuc = prev and is_nuc(prev) or false end local next_is_vn = (nxt and (nxt == 'n' or nxt == 'ň')) if is_onset and (s_i == 1 or prev_nuc) and next_is_vn then out = "W" else local next_is_z = (nxt == 'z') local after_z = next_is_z and after_nxt local vz_unv = (next_is_z and after_z and voiceless_paired[after_z]) if vz_unv or (nxt and voiceless_paired[nxt]) then out = "f" elseif nxt and voiced_paired[nxt] and not sonorants[nxt] then out = "w" else out = "v" end end end elseif c == 'f' then if is_coda and nxt and (voiced_paired[nxt] or sonorants[nxt]) then out = "F" end elseif c == 'j' then local prev_is_iy = (prev == 'i' or prev == 'í' or prev == 'y' or prev == 'ý') if is_onset then out = "j" elseif not vowels_set[prev] or prev_is_iy then out = "J" else out = "I" end end end table.insert(res, out) end table.insert(res_syls, table.concat(res)) end return table.concat(res_syls) end function step_final_devoicing(word) local tokens = get_graphemes(word) local i = #tokens while i > 0 do local c = tokens[i] if voiced_paired[c] then tokens[i] = voicing_map[c]; i = i - 1 elseif voiceless_paired[c] then i = i - 1 else break end end return table.concat(tokens) end function generate_all_transcriptions(word, original_word) local gsub = mw.ustring.gsub -- A. Base IPA local base_ipa = ipa_transform_worker(word) -- B. Phonemic local phonemic = gsub(gsub(base_ipa, "ˈ", ""), "[vwFůWU]", "v") phonemic = gsub(gsub(phonemic, "J", "j"), "H", "ɦ") phonemic = gsub(gsub(phonemic, "ä", "æ"), "ľ", "ʎ") phonemic = gsub(phonemic, "I", "j") -- Decode Dental Clusters phonemic = gsub(phonemic, "(%d['%s_]*%d)", function(k) local d1, sep, d2 = mw.ustring.match(k, "(%d)(['%s_]*)(%d)") d1, d2 = tonumber(d1), tonumber(d2) local d1_fix = ((d1+d2)%2==1) and (d1 + (d2%2==0 and 1 or -1)) or d1 return (c1_dec[d1_fix] or "") .. sep .. (c2_dec[d2] or "") end) -- C. Phonetic Base local phonetic_base = resolve_phonetic_base(base_ipa) local roots = { phonetic_base } -- D. Handle "-mo" Variant if IS_MO_VARIANT and original_word then local c = mw.ustring.sub(original_word, -4, -4) -- grab the consonant immediately preceding the ".mo" suffix local c_voiced = voicing_map[c] if c_voiced then local ipa_voiceless = (ipa_map[c] or c) local ipa_voiced = (ipa_map[c_voiced] or c_voiced) local pattern = mw.ustring.gsub(ipa_voiceless, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") .. "mɔ$" -- escape IPA chars for regex and target the end of string local new_root, count = mw.ustring.gsub(phonetic_base, pattern, ipa_voiced .. "mɔ") if count > 0 then table.insert(roots, new_root) end end end -- E. Expand Variants local bases_expanded = {} for _, root in ipairs(roots) do local expansions = expand_variants(root) for _, e in ipairs(expansions) do table.insert(bases_expanded, e) end end -- F. Generate Registers local high_res, low_res = {}, {} for _, ipa in ipairs(bases_expanded) do table.insert(high_res, (gsub(ipa, "æ", "ɛɐ̯"))) local l = gsub(gsub(gsub(ipa, "æ", "e"), "ʎ", "l"), "l ?ˈ?l", "lː") table.insert(low_res, (gsub(l, "n([fvʋ])", "ɱ%1"))) end return { phonemic = phonemic, high = high_res, low = low_res } end -- ============================================================================= -- [[ MAIN EXPORT ]] -- ============================================================================= function export.show(frame) local args = frame:getParent().args local title = mw.title.getCurrentTitle() local t = args["t"] or "" NAMESPACE = title.nsText PAGENAME = (NAMESPACE == "") and title.text or (args["pagename"] or "Pagename not specified") LOAN = mw.ustring.find(t, "loan") and 1 or 0 HARD_SUFFIX = mw.ustring.find(t, "adj") and 1 or 0 AS_IMP = mw.ustring.find(t, "imp") and 1 or 0 IS_MO_VARIANT = false local word = (args["1"] ~= nil) and args["1"] or PAGENAME local original_word = word local manual_hard = (args["1"] ~= nil and mw.ustring.find(args["1"], "[DTNL]")) -- Display Switches local show_rhymes = (args["r"] ~= "0") and not mw.ustring.find(word, "^%-") and not mw.ustring.find(word, "%-$") local show_hyph = (args["h"] ~= "0") local audio = args["a"] or false local audio_accent = args["aa"] or "" -- Pipeline if not manual_hard then word = step_foreign_graphemes(word) end word = step_softening(word) if manual_hard then word = step_foreign_graphemes(word) end word = step_prosody(word) word = step_simplification(word) word = step_assimilation(word) word = step_v_f_j_realization(word) word = step_final_devoicing(word) local final = generate_all_transcriptions(word, original_word) local nuclei, clean_ipa = get_ipa_nuclei(final.phonemic) local out = format_ipa_output(final, frame) if audio then out = out .. "\n*" .. frame:expandTemplate{title="audio", args={"sk", audio, a=audio_accent}} end if show_rhymes then out = out .. format_rhymes(frame, nuclei, clean_ipa) end if show_hyph then out = out .. format_hyphenation(PAGENAME, final.phonemic, frame, original_word, nuclei) end return out end return export 8t19cs677ehivu4812tnsh89ed8woti 575010 575009 2026-10-29T08:48:37Z Ігнат Лостюк 22957 575010 Scribunto text/plain local export = {} local m_table = require("Module:TableTools") local m_IPA = require("Модуль:IPA") local sk = require("Модуль:languages-data").getByCode("sk") local u = mw.ustring.char local rfind = mw.ustring.find local rsubn = mw.ustring.gsub local rmatch = mw.ustring.match local rsplit = mw.text.split local usub = mw.ustring.sub local ulen = mw.ustring.len -- version of rsubn() that discards all but the first return value local function rsub(term, foo, bar) local retval = rsubn(term, foo, bar) return retval end -- apply function repeatedly until no change local function do_sub_repeatedly(term, fun) while true do local new_term = fun(term) if new_term == term then return term end term = new_term end end -- apply rsub() repeatedly until no change local function rsub_repeatedly(term, foo, bar) while true do local new_term = rsub(term, foo, bar) if new_term == term then return term end term = new_term end end -- ============================================================================= -- [[ DATA TABLES ]] -- ============================================================================= -- [[ 1. Phonological Sets ]] -- local diphthongs_set = { ["ia"]=true, ["ie"]=true, ["iu"]=true, ["ô"]=true } local bigraphs_set = { ["ch"]=true, ["dz"]=true, ["dž"]=true } -- Combined Vowels (Short & Long + Foreign) local vowels_set = { ["a"]=true, ["e"]=true, ["i"]=true, ["o"]=true, ["u"]=true, ["y"]=true, ["ä"]=true, ["ö"]=true, ["ü"]=true, ["á"]=true, ["é"]=true, ["í"]=true, ["ó"]=true, ["ú"]=true, ["ý"]=true, ["ő"]=true, ["ű"]=true } -- Voicing & Sonorants local sonorants = { ['m']=true, ['n']=true, ['ň']=true, ['l']=true, ['ľ']=true, ['ĺ']=true, ['r']=true, ['ŕ']=true, ['j']=true, ['v']=true } local voiced_paired = { ['b']=true, ['d']=true, ['ď']=true, ['g']=true, ['dz']=true, ['dž']=true, ['h']=true, ['z']=true, ['ž']=true, ['w']=true } local voiceless_paired = { ['p']=true, ['t']=true, ['ť']=true, ['k']=true, ['c']=true, ['č']=true, ['ch']=true, ['s']=true, ['š']=true, ['f']=true } local voicing_map = { -- Voiced to Voiceless ['b']='p', ['d']='t', ['ď']='ť', ['g']='k', ['dz']='c', ['dž']='č', ['h']='ch', ['w']='f', ['z']='s', ['ž']='š', -- Voiceless to Voiced ['p']='b', ['t']='d', ['ť']='ď', ['k']='g', ['c']='dz', ['č']='dž', ['ch']='h', ['f']='w', ['s']='z', ['š']='ž' } -- [[ 2. Assimilation & Encoding ]] -- local dental_mergers = { ['z']='dzdz', ['dz']='dzdz', ['s']='cc', ['c']='cc', ['ž']='dždž', ['dž']='dždž', ['š']='čč', ['č']='čč' } -- Mergers: Encodes t.s -> 15, d.z -> 26, etc. local c1_enc = {['t']=1, ['d']=2, ['ť']=3, ['ď']=4} local c2_enc = { ['c']=1, ['dz']=2, ['č']=3, ['dž']=4, ['s']=5, ['z']=6, ['š']=7, ['ž']=8 } -- Decoding: Used by IPA renderer to convert "15" -> t.ts local c1_dec = { 't', 'd', 'c', 'ɟ' } local c2_dec = { 't͡s', 'd͡z', 't͡ʃ', 'd͡ʒ', 's', 'z', 'ʃ', 'ʒ' } -- [[ 3. Morphological & Lexical Exceptions ]] -- local prefixes = { ['pod']=true, ['ob']=true, ['od']=true, ['ráz']=true, ['roz']=true, ['bez']=true, ['ab']=true, ['vz']=true, ['pred']=true, ['nad']=true } local prepositions_set = { ["bez"]=true, ["cez"]=true, ["do"]=true, ["k"]=true, ["ku"]=true, ["na"]=true, ["nad"]=true, ["o"]=true, ["od"]=true, ["po"]=true, ["pod"]=true, ["pre"]=true, ["pred"]=true, ["pri"]=true, ["s"]=true, ["so"]=true, ["u"]=true, ["v"]=true, ["vo"]=true, ["z"]=true, ["za"]=true, ["zo"]=true } local clitics = { ["byť"]=true, ["som"]=true, ["si"]=true, ["je"]=true, ["sme"]=true, ["ste"]=true, ["sú"]=true, ["bol"]=true, ["mi"]=true, ["ti"]=true, ["mu"]=true, ["jej"]=true, ["ma"]=true, ["ťa"]=true, ["sa"]=true, ["ho"]=true, ["ju"]=true, ["nám"]=true, ["vám"]=true, ["im"]=true, ["nás"]=true, ["vás"]=true, ["ich"]=true, ["ten"]=true, ["tá"]=true, ["to"]=true, ["on"]=true, ["a"]=true, ["i"]=true, ["aj"]=true, ["že"]=true, ["keď"]=true, ["až"]=true, ["len"]=true, ["či"]=true, ["kde"]=true, ["ba"]=true, ["ak"]=true, ["už"]=true, ["by"]=true } local preposition_blockers = { ['mnou']=true, ['mne']=true, ['tebou']=true, ['ňou']=true, ['ním']=true, ['nami']=true, ['vami']=true, ['nimi']=true, ['tebe']=true, ['nemu']=true, ['nej']=true, ['nám']=true, ['vám']=true, ['nim']=true } -- [[ 4. IPA Output Maps ]] -- local ipa_map = { -- Vowels ['a']='a', ['á']='aː', ['ä']='æ', ['e']='e', ['é']='eː', ['i']='i', ['í']='iː', ['o']='ɔ', ['ó']='ɔː', ['ô']='u̯ɔ', ['u']='u', ['ú']='uː', ['y']='i', ['ý']='iː', ['ö']='ø', ['ő']='øː', ['ü']='y', ['ű']='yː', -- Consonants ['b']='b', ['c']='t͡s', ['č']='t͡ʃ', ['d']='d', ['ď']='ɟ', ['g']='ɡ', ['h']='ɦ', ['j']='j', ['k']='k', ['l']='l', ['ľ']='ʎ', ['m']='m', ['n']='n', ['ň']='ɲ', ['p']='p', ['r']='r', ['ř']='r̝', ['ŕ']='r̩ː', ['ĺ']='l̩ː', ['s']='s', ['š']='ʃ', ['t']='t', ['ť']='c', ['z']='z', ['ž']='ʒ', ['x']='x', -- Special ['v']='v', ['w']='w', ['F']='F', ['f']='f', ['ů']='ů', ["'"]='ˈ', -- Digraphs/Multigraphs ['dz']='d͡z', ['dž']='d͡ʒ', ['ch']='x', ['dzdz']='d͡zː', ['dždž']='d͡ʒː', ['ia']='ɪ̯a', ['ie']='ɪ̯e', ['iu']='ɪ̯u' } local variant_map = { ["H"]={"ɣ","ɦ"}, ["W"]={"ʋ","v"}, ["U"]={"u̯","ʋ"}, ["J"]={"ɪ̯","j"}, ["N"]={"n","n̠"} } -- Module-level state variables local IS_MO_VARIANT = false local NAMESPACE = "" local PAGENAME = "" local LOAN = 0 local HARD_SUFFIX = 0 local AS_IMP = 0 -- ============================================================================= -- [[ HELPER FUNCTIONS ]] -- ============================================================================= function get_voiced_counterpart(char) return voicing_map[char] end -- Tokenizer (Preserves digraphs) function get_graphemes(word) local units = {} local len = mw.ustring.len(word) local i = 1 local is_loan = (LOAN == 1) while i <= len do local two_char = mw.ustring.sub(word, i, i + 1) if bigraphs_set[two_char] or (diphthongs_set[two_char] and not is_loan) then table.insert(units, two_char) i = i + 2 else table.insert(units, mw.ustring.sub(word, i, i)) i = i + 1 end end return units end -- Resolve Cluster (Contact Assimilation Logic) function resolve_cluster(c1, c2, is_boundary) -- GUARD: v/f complex logic if c1 == 'v' or c1 == 'f' then return nil end -- 1. SPECIAL: <h> or <ch> logic if c1 == 'ch' or c1 == 'h' then if (voiced_paired[c2] or sonorants[c2]) and is_boundary then return 'H' .. c2 end end -- 2. Standard Assimilation local c1_new = c1 if (voiceless_paired[c1] and (voiced_paired[c2] or (is_boundary and (sonorants[c2] or vowels_set[c2])))) or (voiced_paired[c1] and voiceless_paired[c2]) then c1_new = voicing_map[c1] or c1 end -- 3. Mergers & Gemination if c1_enc[c1_new] and dental_mergers[c2] then if is_boundary then local d1, d2 = c1_enc[c1_new], c2_enc[c2] if d1 and d2 then return d1 .. d2 end end return dental_mergers[c2] end if c1_new ~= c1 then return c1_new .. c2 end return nil end function apply_assimilation(tokens, i, j, is_boundary) if IS_MO_VARIANT and i == #tokens - 3 then return end local res = resolve_cluster(tokens[i], tokens[j], is_boundary) if res then local res_tokens = get_graphemes(res) if #res_tokens == 2 then tokens[i] = res_tokens[1]; tokens[j] = res_tokens[2] elseif #res_tokens == 1 then tokens[i] = res_tokens[1]; tokens[j] = "" end end end local function check_vowel(units, i, d, len) while i > 0 and i <= len do local t = units[i] if vowels_set[t] or diphthongs_set[t] then return true end if t ~= "'" and t ~= "-" and t ~= " " and t ~= "_" then return false end i = i + d end return false end function split_into_syllables(word) local units = get_graphemes(word) local nuc, syl, len = {}, {}, #units -- Helper: Check if index k is a Nucleus local function is_nuc(k) local u = units[k]; if not u then return false end if diphthongs_set[u] or vowels_set[u] then return true end if not (u == 'r' or u == 'l' or u == 'ŕ' or u == 'ĺ') then return false end return not (check_vowel(units, k-1, -1, len) or check_vowel(units, k+1, 1, len)) end for i = 1, len do if is_nuc(i) then table.insert(nuc, i) end end if #nuc == 0 then return {table.concat(units)} end local start = 1 for k = 1, #nuc - 1 do local curr, next_n = nuc[k], nuc[k+1] local split = curr -- Default: Split after vowel (V-CV) local c_cnt, first_c = 0, nil for j = curr + 1, next_n - 1 do local c = units[j] if c == "-" or c == " " then split = j; c_cnt = -1; break -- 1. Hard Boundary elseif c == "'" then split = j - 1; c_cnt = -1; break -- 2. Stress Boundary elseif c ~= "." then c_cnt = c_cnt + 1; first_c = first_c or j end end if c_cnt >= 2 then split = first_c end -- 3. Cluster Rule (VC-CV) table.insert(syl, table.concat(units, "", start, split)) start = split + 1 end table.insert(syl, table.concat(units, "", start)) return syl end -- [[ Core IPA Transformation Logic ]] -- local function ipa_transform_worker(word) -- 1. Preprocess: Strip punctuation but map boundaries local bound, idx = {}, 0 word = mw.ustring.gsub(word, ".", function(c) if c == "-" or c == "." then bound[idx] = true; return "" end idx = idx + 1 end) -- Returns cleaned word, populates 'bound' local sub = mw.ustring.sub local w_len, res, i = idx, {}, 1 local function is_C(c) return not c or c == "" or (not vowels_set[c] and not diphthongs_set[c]) end while i <= w_len do local c1 = sub(word, i, i) if c1 == "+" then i = i + 1 else local nxt = sub(word, i+1, i+1) local c2_raw = (nxt == "+") and sub(word, i+2, i+2) or nxt local c2, c4 = c1 .. c2_raw, sub(word, i, i+3) local jump = (nxt == "+") and 1 or 0 local sym, step, raw = nil, 1, c1 if c4 == "dzdz" or c4 == "dždž" then sym, step, raw = ipa_map[c4], 4, c4 elseif (c1 == c2_raw or c2 == "čš") and ipa_map[c1] and not vowels_set[c1] then sym, step, raw = ipa_map[c1] .. "ː", 2 + jump, c1 elseif (ipa_map[c2] and not diphthongs_set[c2]) or (diphthongs_set[c2] and (LOAN~=1 or jump>0) and not bound[i]) then sym, step, raw = ipa_map[c2], 2 + jump, c2 -- treat as diphthong only if native or explicitly marked with + else sym = ipa_map[c1] or c1 end if sym then if (raw=='r' or raw=='l') and is_C(i>1 and sub(word,i-1,i-1)) and is_C(sub(word,i+step,i+step)) then sym = sym .. '̩' -- mark liquid as syllabic if sandwiched between non-vowels end table.insert(res, sym) end i = i + step end end local out = table.concat(res) -- Post-processing for loanword endings (-ovan-) if LOAN == 1 then local sfx = mw.ustring.match(word, ".*ovan(.+)$") if sfx then local m = ipa_map local fix = { ["ie"]=m["ie"], ["ia"]=m["ia"], ["iu"]=m["iu"], ["í"]=m["í"], ["ím"]=m["í"]..(m["m"] or 'm'), ["iam"]=m["ia"]..(m["m"] or 'm'), ["iach"]=m["ia"]..(m["x"] or 'x'), ["iami"]=m["ia"]..(m["m"] or 'm')..(m["i"] or 'i') } if fix[sfx] then local bad = (m["n"] or 'n') .. sfx:gsub(".", function(c) return m[c] or c end) out = mw.ustring.gsub(out, bad:gsub("[%p]", "%%%0") .. "$", (m['ň'] or 'ɲ') .. fix[sfx]) -- correct [n] to [ň] before soft vowels in loan suffixes end end end return out end -- [[ 2. STAGE 2: PHONETIC RESOLVER ]] -- function resolve_phonetic_base(base_text) local res = {} local len = mw.ustring.len(base_text) local i = 1 -- Map for standard phonetic realization local map = { ['v']='ʋ', ['w']='v', ['F']='v', ['f']='f', ['ů']='u̯', ['I']='ɪ̯' } local sub = mw.ustring.sub -- Local ref while i <= len do local c = sub(base_text, i, i) local symbol = map[c] or c local next_c = (i < len) and sub(base_text, i+1, i+1) or "" local next_resolved = map[next_c] or next_c -- Apply FULL Nasal Assimilation if c == 'm' then if next_resolved == 'f' or next_resolved == 'v' or next_resolved == 'ʋ' then symbol = 'ɱ' end elseif c == 'n' then if next_resolved == 'k' or next_resolved == 'ɡ' then symbol = 'ŋ' elseif next_resolved == 's' or next_resolved == 'z' or next_resolved == 'ʃ' or next_resolved == 'ʒ' then symbol = 'N' elseif next_resolved == 'x' then symbol = 'ɰ̃' elseif next_resolved == 'p' or next_resolved == 'b' then symbol = 'm' end end table.insert(res, symbol) i = i + 1 end return table.concat(res) end -- [[ 3. FINAL GENERATION ]] -- local function expand_variants(txt) local find, sub, gsub = mw.ustring.find, mw.ustring.sub, mw.ustring.gsub -- 1. Digits: Binary Branch (All-Split vs All-Merged) if find(txt, "%d[ˈ%s_]*%d") then local res = {} for m = 1, 2 do local s = gsub(txt, "(%d)([ˈ%s_]*)(%d)", function(d1, sep, d2) d1, d2 = tonumber(d1), tonumber(d2) if m == 1 then -- Split (Unassimilated) local f = ((d1+d2)%2==1) and (d1 + (d2%2==0 and 1 or -1)) or d1 return (c1_dec[f] or "") .. sep .. (c2_dec[d2] or "") else -- Merged (Assimilated) local c = c2_dec[d2>4 and d2-4 or d2] or "" -- If stressed: Unreleased + Short (t̚ ˈt͡s). If not: Long (t͡sː) return find(sep, "ˈ") and (sub(c,1,1).."̚ ˈ"..c) or (c.."ː") end end) for _, v in ipairs(expand_variants(s)) do table.insert(res, v) end end return res end -- 2. Letters: Recursive Expansion (HWUJN) local s, e, k = find(txt, "([HWUJN])") if not s then return {txt} end -- Base case local res, h, t = {}, sub(txt, 1, s-1), sub(txt, e+1) for _, r in ipairs(variant_map[k]) do for _, sub in ipairs(expand_variants(h..r..t)) do table.insert(res, sub) end end return res end local function format_ipa_output(res, frame) local distinct = table.concat(res.high) ~= table.concat(res.low) local args = { "sk", "/" .. res.phonemic .. "/" } local function add(list, tag) for i, ipa in ipairs(list) do local qs = {} if distinct and tag and i == 1 then table.insert(qs, tag) end if mw.ustring.find(ipa, "ɦ ?ˈ?ɦ") then table.insert(qs, "rare") end local q = #qs > 0 and "<q:" .. table.concat(qs, ", ") .. ">" or "" table.insert(args, "[" .. ipa .. "]" .. q) end end add(res.high, "high register") if distinct then add(res.low, "common") end return frame:expandTemplate{ title = "IPA", args = args } end local function get_ipa_nuclei(ipa) local s = mw.ustring.gsub(ipa, "[/ %[%]ˈ%.]", "") local nuclei, skip, sub = {}, 0, mw.ustring.sub mw.ustring.gsub(s, "()([rluɪaæeiɔuøy])", function(p, c) if p < skip then return end local nuc, len = nil, 1 local nxt = sub(s, p+1, p+1) if (c=="r" or c=="l") and nxt=="̩" then len = (sub(s, p+2, p+2)=="ː") and 3 or 2 nuc = sub(s, p, p+len-1) elseif (c=="u" or c=="ɪ") and nxt=="̯" then len = 3; nuc = sub(s, p, p+2) elseif mw.ustring.find(c, "[aæeiɔuøy]") then if sub(s, p+1, p+2) == "u̯" then len = 3; nuc = sub(s, p, p+2) else len = (nxt=="ː") and 2 or 1; nuc = sub(s, p, p+len-1) end end if nuc then table.insert(nuclei, {ipa=nuc, s=p, e=p+len-1}); skip = p+len end end) return nuclei, s end local function format_rhymes(frame, nuclei, s) local c = #nuclei if c == 0 then return "" end local rhyme = mw.ustring.sub(s, (c > 1 and nuclei[c-1] or nuclei[1]).s) return "\n*" .. frame:expandTemplate{title="rhymes", args={"sk", rhyme, s=c}} end local function format_hyphenation(word, ipa, frame, respelling, nuclei) if #nuclei <= 1 then return "" end local sub, find, lower, len = mw.ustring.sub, mw.ustring.find, mw.ustring.lower, mw.ustring.len local w_lower = lower(word) -- 1. Parse Respelling (Boundaries & Forbidden Nuclei) local forced, forbidden, idx, last = {}, {}, 0, "" if respelling then local clean_check = mw.ustring.gsub(lower(respelling), "[%.%-%+]", "") if clean_check == w_lower then for i = 1, len(respelling) do local c = sub(respelling, i, i) if c == "+" then if last ~= "i" and last ~= "í" then forbidden[idx + 1] = true end elseif c:match("[%.%-]") then if idx > 0 then forced[idx] = true end else idx = idx + 1; last = lower(c) end end end end local map = { ['a']='a', ['aː']='á', ['æ']='ä', ['e']='e', ['eː']='é', ['i']='[iy]', ['iː']='[íý]', ['o']='o', ['oː']='ó', ['ɔ']='o', ['ɔː']='ó', ['u']='u', ['uː']='ú', ['u̯ɔ']='ô', ['ɔu̯']='ou', ['ø']='ö', ['øː']='ő', ['y']='ü', ['yː']='ű', ['ɪ̯a']='ia', ['ɪ̯e']='ie', ['ɪ̯u']='iu', ['r̩']='r', ['r̩ː']='ŕ', ['l̩']='l', ['l̩ː']='ĺ' } -- Helper: Find next valid nucleus (skips forbidden off-glides) local function get_nuc(pat, start) while true do local s, e = find(w_lower, pat, start) if not s or not forbidden[s] then return s, e end start = s + 1 end end local args, pos = {"sk"}, 1 local seg_start = 1 for i = 1, #nuclei do local s, e = get_nuc(map[nuclei[i].ipa] or ".", pos) if not s then return "" end if i < #nuclei then local s2, _ = get_nuc(map[nuclei[i+1].ipa] or ".", e + 1) if not s2 then return "" end -- Default: Split before last consonant (V-CV) or between consonants (VC-CV) local clust = sub(w_lower, e+1, s2-1) -- If cluster has space, DO NOT split. if find(clust, " ") then pos = e + 1 else local c_len = len(clust) local off = 0 if c_len >= 2 then local dig = sub(clust, 1, 2) off = (dig == "ch" or dig == "dz" or dig == "dž") and (c_len > 2 and 2 or 0) or 1 end for k = e, s2 - 1 do if forced[k] then off = k - e; break end end table.insert(args, sub(word, seg_start, e + off)) seg_start = e + off + 1 pos = e + off + 1 end else table.insert(args, sub(word, seg_start)) end end return "\n*" .. frame:expandTemplate{title="hyph", args=args} end -- ============================================================================= -- [[ PROCESSING STEPS ]] -- ============================================================================= function step_foreign_graphemes(word) word = mw.ustring.lower(word) word = mw.ustring.gsub(word, "ů", "ú") word = mw.ustring.gsub(word, "x", "ks") word = mw.ustring.gsub(word, "w", "v") word = mw.ustring.gsub(word, "qu", "kv") word = mw.ustring.gsub(word, "[,']", "") return word end local function is_empty_or_space(w, k) local x = mw.ustring.sub(w, k, k) return x == "" or x == " " end function step_softening(w) if LOAN == 1 then return w end local soft = {['d']='ď',['t']='ť',['n']='ň',['l']='ľ'} local sub = mw.ustring.sub local res, len = {}, mw.ustring.len(w) for i = 1, len do local c = sub(w, i, i) local s = false if soft[c] then local n1 = sub(w, i+1, i+1) local n2 = sub(w, i+1, i+2) if n2 == "ia" or n2 == "ie" or n2 == "iu" then s = true elseif n1 == "i" or n1 == "í" or n1 == "e" then s = true if HARD_SUFFIX == 1 then local n3 = sub(w, i+1, i+3) if (n1 == "i" or n1 == "í" or n1 == "e") and is_empty_or_space(w, i+2) then s = false elseif n2 == "ej" and is_empty_or_space(w, i+3) then s = false elseif (n3 == "eho" or n3 == "emu") and is_empty_or_space(w, i+4) then s = false end end end end table.insert(res, s and soft[c] or c) end return table.concat(res) end function step_prosody(word) -- 1. Merge Prepositions word = mw.ustring.gsub(word, "%f[%a](%a+) +", function(w) return prepositions_set[mw.ustring.lower(w)] and (w .. "_") or nil end) -- 2. Add Stress return mw.ustring.gsub(word, "%S+", function(w) local raw = mw.ustring.lower(mw.ustring.gsub(w, "_", "")) -- Exclusions: Clitics OR Starts with hyphen OR Single letter OR No syllabic nucleus if clitics[raw] or mw.ustring.sub(w, 1, 1) == "-" or mw.ustring.sub(w, -1) == "-" or mw.ustring.len(w) == 1 or not mw.ustring.find(raw, "[aáäeéiíoóuúyýöőüűôrŕlĺ]") then return w end return "'" .. w end) end function step_simplification(word) local patterns = { "s[tť]s[tk]", "[pd][tz]sk", "[tďdz]st", "[ztd]šť", "[zdt]sk", "ptč" } local function resolve(w) local c1 = mw.ustring.sub(w, 1, 1) if c1 == "s" then return c1 .. mw.ustring.sub(w, 4) elseif c1 == "p" then return c1 .. mw.ustring.sub(w, 3) else w = mw.ustring.gsub(w, "z", "") if mw.ustring.len(w) <= 2 then return w end local c2 = mw.ustring.sub(w, 2, 2) local head = (c2 == "s") and "c" or "č" return head .. mw.ustring.sub(w, 3) end end for _, pat in ipairs(patterns) do word = mw.ustring.gsub(word, pat, resolve) end return word end function step_assimilation(word) -- [[ 1. Pre-processing & Morphological Exceptions ]] -- word = mw.ustring.gsub(word, "('[nv]ášmu)(%f[%A])", function(match) return mw.ustring.gsub(match, "š", "ž") end) -- 2. sme -> zme (Only if surrounded by boundaries) if mw.ustring.find(word, "sme") then word = mw.ustring.gsub(mw.ustring.gsub(word, "^sme$", "zme"), "^sme(%s)", "zme%1") word = mw.ustring.gsub(mw.ustring.gsub(word, "(%s)sme$", "%1zme"), "(%s)sme(%s)", "%1zme%2") end -- A. Prefix "eks-" (eks- + vowel/voiced -> egz-) local v_set = "[aeiouyáéíóúýäôglmnňrjvzhdbžg]" word = mw.ustring.gsub(word, "^('?)eks("..v_set..")", "%1egz%2") word = mw.ustring.gsub(word, "([%s_])('?)eks("..v_set..")", "%1%2egz%3") word = mw.ustring.gsub(word, "^('?)dis("..v_set..")", "%1diz%2") word = mw.ustring.gsub(word, "([%s_])('?)dis("..v_set..")", "%1%diz%3") -- B. Preposition Vocalization (so/ku -> zo/gu) word = mw.ustring.gsub(word, "([%s'])([sk][ou])(_)([^%s%._]+)", function(before, prep, sep, next_w) if not preposition_blockers[next_w] then local voiced = (prep == "so") and "zo" or "gu" return before .. voiced .. sep .. next_w end end) -- C. Imperative Suffix "-me" (Voice preceding consonant) if AS_IMP == 1 then word = mw.ustring.gsub(word, "([ptsťkfcčš])me", function(c) return (voicing_map[c] or c) .. "me" end) end -- [[ 2. Main Assimilation Logic ]] -- local tokens = get_graphemes(word) local len = #tokens local str = table.concat(tokens) -- Check for "-mo" variant trigger if mw.ustring.find(word, "[ptsčškcfx]%.mo%f[%A]") or mw.ustring.find(word, "ch%.mo%f[%A]") then IS_MO_VARIANT = true end -- Helpers local function prev_C(k) while k >= 1 do local t = tokens[k]; if t ~= "'" and t ~= "_" and t ~= " " and t ~= "-" and t ~= "." then return k end k = k - 1 end end local function is_bound(k) return k < 1 or k > len or tokens[k] == " " or tokens[k] == "_" or tokens[k] == "-" end local function prev_bound(k) local l, idx = 0, k - 1 while idx - l >= 1 do local t = tokens[idx - l] if t == "'" or t == "_" or t == " " or t == "-" then break end l = l + 1 end return l end local function match_prefix(start_idx, end_idx) local diff = end_idx - start_idx + 1 if diff == 2 then return prefixes[tokens[start_idx] .. tokens[start_idx+1]] elseif diff == 3 then return prefixes[tokens[start_idx] .. tokens[start_idx+1] .. tokens[start_idx+2]] elseif diff == 4 then return prefixes[tokens[start_idx] .. tokens[start_idx+1] .. tokens[start_idx+2] .. tokens[start_idx+3]] end return false end local simple = not mw.ustring.find(str, "[ %-_]") local w_offset = (simple and tokens[1] == "'") and 1 or 0 -- p_len = Exact count of characters before 'i' local p_len = simple and (len - w_offset - 1) or prev_bound(len) -- Reverse Loop (Regressive Assimilation) for i = len, 2, -1 do -- Reset state if we crossed a word boundary if not simple and is_bound(i+1) then p_len = prev_bound(i) end local is_prefix = false -- A. Prefix Check if p_len >= 2 and p_len <= 4 then if match_prefix(i - p_len, i - 1) then apply_assimilation(tokens, i - 1, i, true) is_prefix = true end end -- B. General Assimilation (If not prefix) if not is_prefix then local tgt = prev_C(i-1) if tgt then local is_opt = false for k = tgt + 1, i - 1 do if tokens[k] == " " or tokens[k] == "-" or tokens[k] == "_" then is_opt = true; break end end -- Handles both Devoicing (standard) and Voicing (at boundaries) apply_assimilation(tokens, tgt, i, is_opt) end end p_len = p_len - 1 end return table.concat(tokens) end local function is_nuc(c) return vowels_set[c] or diphthongs_set[c] or c == 'r' or c == 'l' or c == 'ŕ' or c == 'ĺ' end function step_v_f_j_realization(word) word = mw.ustring.gsub(mw.ustring.gsub(word, "anje[lľ]", "aňɪ̯el"), "_", "") word = mw.ustring.gsub(mw.ustring.gsub(word, "%+u", "ů"), "%+i", "I") local syllables = split_into_syllables(word) local res_syls = {} local clean_chars = {} local char_map = {} local idx = 1 for s_i, syl in ipairs(syllables) do char_map[s_i] = {} for c_i = 1, mw.ustring.len(syl) do local c = mw.ustring.sub(syl, c_i, c_i) if c ~= "'" and c ~= "." and c ~= "-" then clean_chars[idx] = c char_map[s_i][c_i] = idx idx = idx + 1 end end end for s_i, syl in ipairs(syllables) do local s_len, res = mw.ustring.len(syl), {} local nuc_i = nil for k = 1, s_len do if is_nuc(mw.ustring.sub(syl, k, k)) then nuc_i = k; break end end for c_i = 1, s_len do local c = mw.ustring.sub(syl, c_i, c_i) local out = c if c ~= "'" and c ~= "." and c ~= "-" then local flat_idx = char_map[s_i][c_i] local prev = clean_chars[flat_idx - 1] local nxt = clean_chars[flat_idx + 1] local after_nxt = clean_chars[flat_idx + 2] -- 2. DETECT POSITIONS (Ignore ' and .) local is_onset = (mw.ustring.gsub(mw.ustring.sub(syl, 1, c_i-1), "['%.%-]", "") == "") local is_coda = nuc_i and (c_i > nuc_i) if c == 'u' then if prev == 'o' and (not nxt or nxt == ' ') then out = "ů" end elseif c == 'v' then if is_coda and prev and is_nuc(prev) then local is_u_prev = (prev == 'u' or prev == 'ú') local is_amb_next = (nxt and (nxt == 'n' or nxt == 'ň' or nxt == 'l' or nxt == 'ľ' or nxt == 'r')) out = (is_u_prev or is_amb_next) and "U" or "ů" else local prev_nuc = false if s_i > 1 then prev_nuc = prev and is_nuc(prev) or false end local next_is_vn = (nxt and (nxt == 'n' or nxt == 'ň')) if is_onset and (s_i == 1 or prev_nuc) and next_is_vn then out = "W" else local next_is_z = (nxt == 'z') local after_z = next_is_z and after_nxt local vz_unv = (next_is_z and after_z and voiceless_paired[after_z]) if vz_unv or (nxt and voiceless_paired[nxt]) then out = "f" elseif nxt and voiced_paired[nxt] and not sonorants[nxt] then out = "w" else out = "v" end end end elseif c == 'f' then if is_coda and nxt and (voiced_paired[nxt] or sonorants[nxt]) then out = "F" end elseif c == 'j' then local prev_is_iy = (prev == 'i' or prev == 'í' or prev == 'y' or prev == 'ý') if is_onset then out = "j" elseif not vowels_set[prev] or prev_is_iy then out = "J" else out = "I" end end end table.insert(res, out) end table.insert(res_syls, table.concat(res)) end return table.concat(res_syls) end function step_final_devoicing(word) local tokens = get_graphemes(word) local i = #tokens while i > 0 do local c = tokens[i] if voiced_paired[c] then tokens[i] = voicing_map[c]; i = i - 1 elseif voiceless_paired[c] then i = i - 1 else break end end return table.concat(tokens) end function generate_all_transcriptions(word, original_word) local gsub = mw.ustring.gsub -- A. Base IPA local base_ipa = ipa_transform_worker(word) -- B. Phonemic local phonemic = gsub(gsub(base_ipa, "ˈ", ""), "[vwFůWU]", "v") phonemic = gsub(gsub(phonemic, "J", "j"), "H", "ɦ") phonemic = gsub(gsub(phonemic, "ä", "æ"), "ľ", "ʎ") phonemic = gsub(phonemic, "I", "j") -- Decode Dental Clusters phonemic = gsub(phonemic, "(%d['%s_]*%d)", function(k) local d1, sep, d2 = mw.ustring.match(k, "(%d)(['%s_]*)(%d)") d1, d2 = tonumber(d1), tonumber(d2) local d1_fix = ((d1+d2)%2==1) and (d1 + (d2%2==0 and 1 or -1)) or d1 return (c1_dec[d1_fix] or "") .. sep .. (c2_dec[d2] or "") end) -- C. Phonetic Base local phonetic_base = resolve_phonetic_base(base_ipa) local roots = { phonetic_base } -- D. Handle "-mo" Variant if IS_MO_VARIANT and original_word then local c = mw.ustring.sub(original_word, -4, -4) -- grab the consonant immediately preceding the ".mo" suffix local c_voiced = voicing_map[c] if c_voiced then local ipa_voiceless = (ipa_map[c] or c) local ipa_voiced = (ipa_map[c_voiced] or c_voiced) local pattern = mw.ustring.gsub(ipa_voiceless, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") .. "mɔ$" -- escape IPA chars for regex and target the end of string local new_root, count = mw.ustring.gsub(phonetic_base, pattern, ipa_voiced .. "mɔ") if count > 0 then table.insert(roots, new_root) end end end -- E. Expand Variants local bases_expanded = {} for _, root in ipairs(roots) do local expansions = expand_variants(root) for _, e in ipairs(expansions) do table.insert(bases_expanded, e) end end -- F. Generate Registers local high_res, low_res = {}, {} for _, ipa in ipairs(bases_expanded) do table.insert(high_res, (gsub(ipa, "æ", "ɛɐ̯"))) local l = gsub(gsub(gsub(ipa, "æ", "e"), "ʎ", "l"), "l ?ˈ?l", "lː") table.insert(low_res, (gsub(l, "n([fvʋ])", "ɱ%1"))) end return { phonemic = phonemic, high = high_res, low = low_res } end -- ============================================================================= -- [[ MAIN EXPORT ]] -- ============================================================================= function export.show(frame) local args = frame:getParent().args local title = mw.title.getCurrentTitle() local t = args["t"] or "" NAMESPACE = title.nsText PAGENAME = (NAMESPACE == "") and title.text or (args["pagename"] or "Pagename not specified") LOAN = mw.ustring.find(t, "loan") and 1 or 0 HARD_SUFFIX = mw.ustring.find(t, "adj") and 1 or 0 AS_IMP = mw.ustring.find(t, "imp") and 1 or 0 IS_MO_VARIANT = false local word = (args["1"] ~= nil) and args["1"] or PAGENAME local original_word = word local manual_hard = (args["1"] ~= nil and mw.ustring.find(args["1"], "[DTNL]")) -- Display Switches local show_rhymes = (args["r"] ~= "0") and not mw.ustring.find(word, "^%-") and not mw.ustring.find(word, "%-$") local show_hyph = (args["h"] ~= "0") local audio = args["a"] or false local audio_accent = args["aa"] or "" -- Pipeline if not manual_hard then word = step_foreign_graphemes(word) end word = step_softening(word) if manual_hard then word = step_foreign_graphemes(word) end word = step_prosody(word) word = step_simplification(word) word = step_assimilation(word) word = step_v_f_j_realization(word) word = step_final_devoicing(word) local final = generate_all_transcriptions(word, original_word) local nuclei, clean_ipa = get_ipa_nuclei(final.phonemic) local out = format_ipa_output(final, frame) if audio then out = out .. "\n*" .. frame:expandTemplate{title="audio", args={"sk", audio, a=audio_accent}} end if show_rhymes then out = out .. format_rhymes(frame, nuclei, clean_ipa) end if show_hyph then out = out .. format_hyphenation(PAGENAME, final.phonemic, frame, original_word, nuclei) end return out end return export q8ngbwbmkptruur2cs34fz3fwbwj1ji