Jump to content

Module:bg-pronunciation

Wiktionary වෙතින්

This module automatically converts Bulgarian orthography to a phonetic transcription in the International Phonetic Alphabet. It also generates hyphenations and syllabifications. It supports generating all various parts of pronunciation sections together, including the above and audio and rhymes, using the show_all entry point.

All tests passed. (refresh)

TextExpectedActual
test_hyphenation:
Passedвисочина́ (visočiná)ви‧со‧чи‧нави‧со‧чи‧на
Passedсестра́ (sestrá)сес‧трасес‧тра
Passedпле́нник (plénnik)плен‧никплен‧ник
Passedпреодоле́я (preodoléja)пре‧одо‧леяпре‧одо‧лея
Passedмаои́зъм (maoízǎm)мао‧изъммао‧изъм
Passedма́йка (májka)май‧камай‧ка
Passedа́йс.берг (ájs.berg)айс‧бергайс‧берг
Passedмайо́р (majór)ма‧йорма‧йор
Passedфризьо́р (frizjór)фри‧зьорфри‧зьор
Passedсуджу́к (sudžúk)су‧джуксу‧джук
Passedнад.живе́я (nad.živéja)над‧жи‧веянад‧жи‧вея
Passedсестра́ (sestrá)сес‧трасес‧тра
Passedпоту́ри (potúri)по‧ту‧рипо‧ту‧ри
Passedслани́на (slanína)сла‧ни‧насла‧ни‧на
Passedпра́жа (práža)пра‧жапра‧жа
Passedспринцо́вка (sprincóvka)сприн‧цов‧касприн‧цов‧ка
Passedпъ́ржа (pǎ́rža)пър‧жапър‧жа
Passedя́ркост (járkost)яр‧костяр‧кост
Passedрало (ralo)ра‧лора‧ло
Passedбели́ло (belílo)бе‧ли‧лобе‧ли‧ло
Passedшеви́ца (ševíca)ше‧ви‧цаше‧ви‧ца
Passedдои́ло (doílo)до‧илодо‧ило
Passedнача́ло (načálo)на‧ча‧лона‧ча‧ло
Passedхи́трост (hítrost)хит‧ростхит‧рост
Passedхи́тър (hítǎr)хи‧търхи‧тър
Passedшеви́ца (ševíca)ше‧ви‧цаше‧ви‧ца
Passedвдлъ́бна (vdlǎ́bna)вдлъб‧навдлъб‧на
Passedразма́хам (razmáham)раз‧ма‧хамраз‧ма‧хам
Passedу́кор (úkor)укорукор
Passedупори́т (uporít)упо‧ритупо‧рит
Passedо́сем (ósem)осемосем
Passedоце́нка (océnka)оцен‧каоцен‧ка
Passedле́я (léja)леялея
Passedа́з (áz)азаз
Passedтя́ (tjá)тятя
Passedе́ (é)ее
Passedме́н (mén)менмен
Passedстра́ст (strást)страстстраст
Passedпръст (prǎst)пръстпръст
Passedшофьо́р (šofjór)шо‧фьоршо‧фьор
Passedфотьо́йл (fotjójl)фо‧тьойлфо‧тьойл
Passedбельо́ (beljó)бе‧льобе‧льо
Passedшедьо́вър (šedjóvǎr)ше‧дьо‧върше‧дьо‧вър
Passedме́ниджър (ménidžǎr)ме‧ни‧джърме‧ни‧джър
Passedджудже́ (džudžé)джу‧джеджу‧дже
Passedжа́р-пти́ца (žár-ptíca)жар-пти‧цажар-пти‧ца
Passedмора́лно-нра́вствен (morálno-nrávstven)мо‧рал‧но-нрав‧ственмо‧рал‧но-нрав‧ствен
Passedкандида́т-студе́нт (kandidát-studént)кан‧ди‧дат-сту‧денткан‧ди‧дат-сту‧дент
Passedмини́стър-председа́тел (minístǎr-predsedátel)ми‧нис‧тър-пред‧се‧да‧телми‧нис‧тър-пред‧се‧да‧тел
Passedчлен-кореспонде́нт (člen-korespondént)член-ко‧рес‧пон‧дентчлен-ко‧рес‧пон‧дент
Passedби́знес администра́ция (bíznes administrácija)биз‧нес ад‧ми‧нис‧тра‧циябиз‧нес ад‧ми‧нис‧тра‧ция
Passedе́кшън геро́й (ékšǎn gerój)ек‧шън ге‧ройек‧шън ге‧рой
Passedте́нис ко́рт (ténis kórt)те‧нис кортте‧нис корт
Passedзаме́стник мини́стър-председа́тел (zaméstnik minístǎr-predsedátel)за‧мес‧тник ми‧нис‧тър-пред‧се‧да‧телза‧мес‧тник ми‧нис‧тър-пред‧се‧да‧тел
Passedзаме́стник нача́лник-управле́ние (zaméstnik načálnik-upravlénie)за‧мес‧тник на‧чал‧ник-уп‧рав‧ле‧ниеза‧мес‧тник на‧чал‧ник-уп‧рав‧ле‧ние
PassedSIM ка́рта (SIM kárta)SIM кар‧таSIM кар‧та
PassedVIP зо́на (VIP zóna)VIP зо‧наVIP зо‧на
TextExpectedActual
test_ipa:
Passedкъ́ща (kǎ́šta)ˈkɤʃtɐˈkɤʃtɐ
Passedсгъстя́ се (sgǎstjá se), endschwa=truezɡɐˈstʲɤ̟ sɛzɡɐˈstʲɤ̟ sɛ
Passedсгъстя́ се (sgǎstjá se) (respelled сгъстя̣́ се)zɡɐˈstʲɤ̟ sɛzɡɐˈstʲɤ̟ sɛ
Passedа̀бдики́ращ (àbdikírašt)ˌabdiˈkirɐʃtˌabdiˈkirɐʃt
Passedбезшу́мен (bezšúmen)bɛʃˈʃu̟mɛnbɛʃˈʃu̟mɛn
Passedщастли́в (štastlív)ʃtɐˈslifʃtɐˈslif
Passedнародността́ (narodnosttá)nɐrodnoˈstanɐrodnoˈsta
Passedя (ja)ja̟ja̟
Passedюг (jug)ju̟kju̟k
Passedяйце́ (jajcé)jɐjˈt͡sɛjɐjˈt͡sɛ
Passedизя́м (izjám)iˈzʲa̟miˈzʲa̟m
Passedучи́лище (učílište)oˈt͡ʃiliʃtɛoˈt͡ʃiliʃtɛ
Passedчорбаджи́я (čorbadžíja)t͡ʃo̟rbɐˈdʒijɐt͡ʃo̟rbɐˈdʒijɐ
Passedуби́йца (ubíjca)oˈbijt͡sɐoˈbijt͡sɐ
Passedбезбра́чие (bezbráčie)bɛzˈbrat͡ʃiɛbɛzˈbrat͡ʃiɛ
Passedизмра́ (izmrá) (respelled из.мра́)izˈmraizˈmra
Passedсала́та (saláta)sɐˈɫatɐsɐˈɫatɐ
Passedшега́ (šegá)ʃɛˈɡaʃɛˈɡa
Passedжена́ (žená)ʒɛˈnaʒɛˈna
Passedинти́мен (intímen)inˈtimɛninˈtimɛn
Passedпосо́лство (posólstvo)poˈsɔɫstvopoˈsɔɫstvo
Passedъ́гъл (ǎ́gǎl)ˈɤɡɐɫˈɤɡɐɫ
Passedусу́квам (usúkvam)oˈsukvɐmoˈsukvɐm
Passedле́ща (léšta)ˈlɛʃtɐˈlɛʃtɐ
Passedлипа́ (lipá)liˈpaliˈpa
Passedокеа́н (okeán)okɛˈanokɛˈan
Passedмеки́ца (mekíca)mɛˈkit͡sɐmɛˈkit͡sɐ
Passedла́гер (láger)ˈɫaɡɛrˈɫaɡɛr
Passedмаги́я (magíja)mɐˈɡijɐmɐˈɡijɐ
Passedхем (hem)xɛmxɛm
Passedхимн (himn)ximnximn
Passedтулу́п (tulúp)toˈɫuptoˈɫup
Passedжа̀р-пти́ца (žàr-ptíca)ˌʒa̟r-pˈtit͡sɐˌʒa̟r-pˈtit͡sɐ
Passedв о́фис (v ófis)f ˈɔfisf ˈɔfis
Passedвъв Фра́нция (vǎv Fráncija)vɐf ˈfrant͡sijɐvɐf ˈfrant͡sijɐ
Passedня́колко (njákolko)ˈnʲa̟koɫkoˈnʲa̟koɫko
Passedв Япо́ния (v Japónija)f jɐˈpɔnijɐf jɐˈpɔnijɐ
Passedавтоплу́г (avtoplúg)ɐftoˈpɫukɐftoˈpɫuk
Passedуе́бса́йт (uébsájt) (respelled ўе́бса́йт)ˈwɛpˈsajtˈwɛpˈsajt
Passedуе́лски (uélski) (respelled ўе́лски)ˈwɛɫskiˈwɛɫski
Passedуе́стърн (uéstǎrn) (respelled ўе́стърн)ˈwɛstɐrnˈwɛstɐrn
PassedО́уен (Óuen) (respelled О́ўен)ˈɔwɛnˈɔwɛn
Passedно́ухау (nóuhau) (respelled но́ўхаў)ˈnɔwxɐwˈnɔwxɐw
PassedДжо́узеф (Džóuzef) (respelled Джо́ўзеф)ˈdʒɔwzɛfˈdʒɔwzɛf
Passedбо́улинг (bóuling) (respelled бо́ўлинг)ˈbɔwliŋkˈbɔwliŋk
Passedдаунло́уд (daunlóud) (respelled даўнло́ўд)dɐwnˈɫɔwtdɐwnˈɫɔwt
Passedуи́ски (uíski) (respelled ўи́ски)ˈwiskiˈwiski
Passedуи́кенд (uíkend) (respelled ўи́кенд)ˈwikɛntˈwikɛnt
PassedУо́руик (Uóruik) (respelled Ўо́рўик)ˈwɔrwikˈwɔrwik
PassedХе́лоуин (Hélouin) (respelled Хе́лоўин)ˈxɛɫowinˈxɛɫowin
TextExpectedActual
test_rhymes:
Passedграм (gram)-am-am
Passedпрям (prjam)-am-am
Passedсам (sam)-am-am
Passedедва́м (edvám)-am-am
Passedхипопота́м (hipopotám)-am-am
Passedли́чен (líčen)-it͡ʃɛn-it͡ʃɛn
Passedграфи́чен (grafíčen)-it͡ʃɛn-it͡ʃɛn
Passedпсихи́чен (psihíčen)-it͡ʃɛn-it͡ʃɛn
Passedритми́чен (ritmíčen)-it͡ʃɛn-it͡ʃɛn
Passedцини́чен (ciníčen)-it͡ʃɛn-it͡ʃɛn
Passedчасти́чен (častíčen)-it͡ʃɛn-it͡ʃɛn
Passedсеби́чен (sebíčen)-it͡ʃɛn-it͡ʃɛn
Passedкатегори́чен (kategoríčen)-it͡ʃɛn-it͡ʃɛn
Passedмелоди́чен (melodíčen)-it͡ʃɛn-it͡ʃɛn
Passedмикроскопи́чен (mikroskopíčen)-it͡ʃɛn-it͡ʃɛn
Passedетимологи́чен (etimologíčen)-it͡ʃɛn-it͡ʃɛn
Passedепидемиологи́чен (epidemiologíčen)-it͡ʃɛn-it͡ʃɛn
Passedзов (zov)-ɔf-ɔf
Passedнов (nov)-ɔf-ɔf
Passedоф (of)-ɔf-ɔf
Passedкарто́ф (kartóf)-ɔf-ɔf
Passedпищо́в (pištóv)-ɔf-ɔf
PassedЦветко́в (Cvetkóv)-ɔf-ɔf
PassedПопо́в (Popóv)-ɔf-ɔf
PassedСвищо́в (Svištóv)-ɔf-ɔf
Passedнадсло́в (nadslóv)-ɔf-ɔf
Passedапостро́ф (apostróf)-ɔf-ɔf
Passedполугото́в (polugotóv)-ɔf-ɔf
Passedвселе́на (vseléna)-ɛnɐ-ɛnɐ
PassedЕле́на (Eléna)-ɛnɐ-ɛnɐ
Passedге́та (géta)-ɛtɐ-ɛtɐ
Passedче́та (čéta)-ɛtɐ-ɛtɐ
Passedбаре́та (baréta)-ɛtɐ-ɛtɐ
Passedкасе́та (kaséta)-ɛtɐ-ɛtɐ
Passedсоле́та (soléta)-ɛtɐ-ɛtɐ
Passedраке́та (rakéta)-ɛtɐ-ɛtɐ
Passedкаре́та (karéta)-ɛtɐ-ɛtɐ
Passedкле́чка (kléčka)-ɛt͡ʃkɐ-ɛt͡ʃkɐ
Passedпе́чка (péčka)-ɛt͡ʃkɐ-ɛt͡ʃkɐ
Passedапте́чка (aptéčka)-ɛt͡ʃkɐ-ɛt͡ʃkɐ
Passedпресе́чка (preséčka)-ɛt͡ʃkɐ-ɛt͡ʃkɐ
Passedюрде́чка (jurdéčka)-ɛt͡ʃkɐ-ɛt͡ʃkɐ
Passedбуболе́чка (buboléčka)-ɛt͡ʃkɐ-ɛt͡ʃkɐ
Passedвън (vǎn)-ɤn-ɤn
Passedзвън (zvǎn)-ɤn-ɤn
Passedмрън (mrǎn)-ɤn-ɤn
Passedтрън (trǎn)-ɤn-ɤn
Passedсън (sǎn)-ɤn-ɤn
Passedалтъ́н (altǎ́n)-ɤn-ɤn
Passedсакъ́н (sakǎ́n)-ɤn-ɤn
Passedнасъ́н (nasǎ́n)-ɤn-ɤn
Passedгръмотръ́н (grǎmotrǎ́n)-ɤn-ɤn
Passedхун (hun)-un-un
Passedбасту́н (bastún)-un-un
PassedПеру́н (Perún)-un-un
Passedхарпу́н (harpún)-un-un
Passedшогу́н (šogún)-un-un
Passedсапу́н (sapún)-un-un
Passedсаму́н (samún)-un-un
PassedНепту́н (Neptún)-un-un
PassedКамеру́н (Kamerún)-un-un
Passedопеку́н (opekún)-un-un
Passedму́ра (múra)-urɐ-urɐ
Passedсу́ра (súra)-urɐ-urɐ
Passedброшу́ра (brošúra)-urɐ-urɐ
Passedкулту́ра (kultúra)-urɐ-urɐ
Passedфризу́ра (frizúra)-urɐ-urɐ
Passedмату́ра (matúra)-urɐ-urɐ
Passedцезу́ра (cezúra)-urɐ-urɐ
Passedагенту́ра (agentúra)-urɐ-urɐ
Passedармату́ра (armatúra)-urɐ-urɐ
Passedсаламу́ра (salamúra)-urɐ-urɐ
Passedархитекту́ра (arhitektúra)-urɐ-urɐ
Passedаквакулту́ра (akvakultúra)-urɐ-urɐ
Passedагрикулту́ра (agrikultúra)-urɐ-urɐ
Passedдва (dva)-va-va
Passedбрава́ (bravá)-va-va
Passedтрева́ (trevá)-va-va
Passedглава́ (glavá)-va-va
Passedтова́ (tová)-va-va
Passedедва́ (edvá)-va-va
Passedбаклава́ (baklavá)-va-va
Passedзатова́ (zatová)-va-va
Passedтетива́ (tetivá)-va-va
Passedбоа́ (boá)-a-a
PassedБеноа́ (Benoá)-a-a
Passedаншоа́ (anšoá)-a-a
Passedамплоа́ (amploá)-a-a
PassedФрансоа́ (Fransoá)-a-a
Passedбуржоа́ (buržoá)-a-a
PassedДелакроа́ (Delakroá)-a-a
Passedсвета́ (svetá)-tɤ-tɤ
Passedсъвестта́ (sǎvesttá)-tɤ-tɤ
Passedпесента́ (pesentá)-tɤ-tɤ
Passedобластта́ (oblasttá)-tɤ-tɤ
Passedболестта́ (bolesttá)-tɤ-tɤ
Passedмъдростта́ (mǎdrosttá)-tɤ-tɤ
Passedразсъдливостта́ (razsǎdlivosttá)-tɤ-tɤ
Passedболя́ (boljá)-lʲɤ-lʲɤ
Passedбедя́ (bedjá)-dʲɤ-dʲɤ
Passedбумтя́ (bumtjá)-tʲɤ-tʲɤ
Passedвися́ (visjá)-sʲɤ-sʲɤ
Passedбода́ (bodá)-dɤ-dɤ
Passedведа́ (vedá)-dɤ-dɤ
Passedвъзпроизведа́ (vǎzproizvedá)-dɤ-dɤ
Passedзда (zda)-dɤ-dɤ
Passedбера́ (berá)-rɤ-rɤ
Passedвъзпра́ (vǎzprá)-rɤ-rɤ
Passedвра (vra)-rɤ-rɤ
Passedнавра́ (navrá)-rɤ-rɤ
Passedбуя́ (bujá)-jɤ-jɤ
Passedдоя́ (dojá)-jɤ-jɤ
Passedбезпокоя́ (bezpokojá)-jɤ-jɤ
Passedгреба́ (grebá)-bɤ-bɤ
Passedизмета́ (izmetá)-tɤ-tɤ
Passedлежа́ (ležá)-ʒɤ-ʒɤ
Passedдържа́ (dǎržá)-ʒɤ-ʒɤ
Passedдължа́ (dǎlžá)-ʒɤ-ʒɤ
Passedкова́ (ková)-vɤ-vɤ
Passedдередже́ (deredžé)-dʒɛ-dʒɛ
Passedкоскоджа́ (koskodžá)-dʒa-dʒa
Passedхвърча́ (hvǎrčá)-t͡ʃɤ-t͡ʃɤ
Passedхълца́ (hǎlcá)-t͡sɤ-t͡sɤ
TextExpectedActual
test_syllabification:
Passedа (a)аа
Passedв (v)вв
Passedе (e)ее
Passedи (i)ии
Passedѝ (ì)ѝѝ
Passedо (o)оо
Passedс (s)сс
Passedу (u)уу
Passedаз (az)азаз
Passedти (ti)тити
Passedтой (toj)тойтой
Passedтя (tja)тятя
Passedвъв (vǎv)въввъв
Passedсъс (sǎs)съссъс
Passedпринц (princ)принцпринц
Passedспринт (sprint)спринтспринт
Passedглист (glist)глистглист
Passedскункс (skunks)скунксскункс
Passedами́ (amí)а‧миа‧ми
Passedала́ (alá)а‧лаа‧ла
Passedако́ (akó)а‧коа‧ко
Passedуви́ (uví)у‧виу‧ви
Passedили́ (ilí)и‧лии‧ли
Passedсаламу́ра (salamúra)са‧ла‧му‧раса‧ла‧му‧ра
Passedбараба́н (barabán)ба‧ра‧банба‧ра‧бан
Passedсполу́ка (spolúka)спо‧лу‧каспо‧лу‧ка
Passedща́вя (štávja)ща‧вяща‧вя
Passedстри́на (strína)стри‧настри‧на
Passedкога́то (kogáto)ко‧га‧токо‧га‧то
Passedизя́м (izjám)и‧зями‧зям
Passedстари́цата (starícata)ста‧ри‧ца‧таста‧ри‧ца‧та
Passedполу́чените (polúčenite)по‧лу‧че‧ни‧тепо‧лу‧че‧ни‧те
Passedподоба́ващите (podobávaštite)по‧до‧ба‧ва‧щи‧тепо‧до‧ба‧ва‧щи‧те
Passedобуча́ващите (obučávaštite)о‧бу‧ча‧ва‧щи‧тео‧бу‧ча‧ва‧щи‧те
Passedджудже́ (džudžé)джу‧джеджу‧дже
Passedсуджу́к (sudžúk)су‧джуксу‧джук
Passedдамаджа́на (damadžána)да‧ма‧джа‧нада‧ма‧джа‧на
Passedджа́джите (džádžite)джа‧джи‧теджа‧джи‧те
Passedкойо́т (kojót)ко‧йотко‧йот
Passedмайоне́за (majonéza)ма‧йо‧не‧зама‧йо‧не‧за
Passedпейорати́вен (pejoratíven)пе‧йо‧ра‧ти‧венпе‧йо‧ра‧ти‧вен
Passedмайо́р (majór)ма‧йорма‧йор
Passedбези́менен (bezímenen)бе‧зи‧ме‧ненбе‧зи‧ме‧нен
Passedизопача́вам (izopačávam)и‧зо‧па‧ча‧вами‧зо‧па‧ча‧вам
Passedоти́вам (otívam)о‧ти‧вамо‧ти‧вам
Passedразора́н (razorán)ра‧зо‧ранра‧зо‧ран
Passedбульо́н (buljón)бу‧льонбу‧льон
Passedфризьо́р (frizjór)фри‧зьорфри‧зьор
Passedшедьо́вър (šedjóvǎr)ше‧дьо‧върше‧дьо‧вър
Passedгьозу́м (gjozúm)гьо‧зумгьо‧зум
Passedликьо́р (likjór)ли‧кьорли‧кьор
Passedвоа́л (voál)во‧алво‧ал
Passedмаои́зъм (maoízǎm)ма‧о‧и‧зъмма‧о‧и‧зъм
Passedфее́рия (feérija)фе‧е‧ри‧яфе‧е‧ри‧я
Passedвоайо́р (voajór)во‧а‧йорво‧а‧йор
Passedмиока́рд (miokárd)ми‧о‧кардми‧о‧кард
Passedкьо́поолу (kjópoolu)кьо‧по‧о‧лукьо‧по‧о‧лу
Passedаятола́х (ajatoláh)а‧я‧то‧лаха‧я‧то‧лах
Passedава́рия (avárija)а‧ва‧ри‧яа‧ва‧ри‧я
Passedпози́ции (pozícii)по‧зи‧ци‧ипо‧зи‧ци‧и
Passedхазя́и (hazjái)ха‧зя‧иха‧зя‧и
Passedдерибе́и (deribéi)де‧ри‧бе‧иде‧ри‧бе‧и
Passedпреодоле́я (preodoléja)пре‧о‧до‧ле‧япре‧о‧до‧ле‧я
Passedнащре́к (naštrék)на‧щрекна‧щрек
Passedпоощря́вам (pooštrjávam)по‧о‧щря‧вампо‧о‧щря‧вам
Passedзащрихо́вам (zaštrihóvam)за‧щри‧хо‧вамза‧щри‧хо‧вам
Passedпоощри́телен (pooštrítelen)по‧о‧щри‧те‧ленпо‧о‧щри‧те‧лен
Passedизщра́кване (izštrákvane)из‧щрак‧ва‧неиз‧щрак‧ва‧не
PassedВа́йерщра̀с (Vájerštràs)Ва‧йер‧щрасВа‧йер‧щрас
PassedКьо́нигщра̀се (Kjónigštràse)Кьо‧ниг‧щра‧сеКьо‧ниг‧щра‧се
Passedо́бщност (óbštnost)общ‧ностобщ‧ност
Passedвсъ́щност (vsǎ́štnost)всъщ‧ноствсъщ‧ност
Passedпомо́щник (pomóštnik)по‧мощ‧никпо‧мощ‧ник
Passedчорапога́щник (čorapogáštnik)чо‧ра‧по‧гащ‧никчо‧ра‧по‧гащ‧ник
Passedно́щница (nóštnica)нощ‧ни‧цанощ‧ни‧ца
Passedчудо́вищност (čudóvištnost)чу‧до‧вищ‧ностчу‧до‧вищ‧ност
Passedнемощливо (nemoštlivo)не‧мощ‧ли‧воне‧мощ‧ли‧во
Passedсъобща́вам (sǎobštávam)съ‧об‧ща‧вамсъ‧об‧ща‧вам
Passedвъобще́ (vǎobšté)въ‧об‧щевъ‧об‧ще
Passedма́нджа (mándža)ман‧джаман‧джа
Passedкалайджи́я (kalajdžíja)ка‧лай‧джи‧яка‧лай‧джи‧я
Passedавджи́я (avdžíja)ав‧джи‧яав‧джи‧я
Passedизджва́ка (izdžváka)из‧джва‧каиз‧джва‧ка
Passedпле́нник (plénnik)плен‧никплен‧ник
Passedма́йка (májka)май‧камай‧ка
Passedпро̀фаши́стки (pròfašístki)про‧фа‧шист‧кипро‧фа‧шист‧ки
Passedгле́дка (glédka)глед‧каглед‧ка
Passedкра́чка (kráčka)крач‧какрач‧ка
Passedце́дка (cédka)цед‧кацед‧ка
Passedзвезда́ (zvezdá)звез‧дазвез‧да
Passedспринцо́вка (sprincóvka)сприн‧цов‧касприн‧цов‧ка
Passedбъ́рзо (bǎ́rzo)бър‧зобър‧зо
Passedма́лко (málko)мал‧комал‧ко
Passedпо́сле (pósle)по‧слепо‧сле
Passedпа́ртия (pártija)пар‧ти‧япар‧ти‧я
Passedгла́нцов (gláncov)глан‧цовглан‧цов
Passedпе́пелник (pépelnik)пе‧пел‧никпе‧пел‧ник
Passedпи́лци (pílci)пил‧ципил‧ци
Passedаншоа́ (anšoá)ан‧шо‧аан‧шо‧а
Passedядро́ (jadró)я‧дроя‧дро
Passedирони́чност (ironíčnost)и‧ро‧нич‧ности‧ро‧нич‧ност
Passedпрофилакти́чна (profilaktíčna)про‧фи‧лак‧тич‧напро‧фи‧лак‧тич‧на
Passedбо́цна (bócna)боц‧набоц‧на
Passedспе́цна (spécna)спец‧наспец‧на
Passedбичме́ (bičmé)бич‧мебич‧ме
Passedкръ́чма (krǎ́čma)кръч‧макръч‧ма
Passedбо́цман (bócman)боц‧манбоц‧ман
Passedсачма́ (sačmá)сач‧масач‧ма
PassedРи́чмънд (Ríčmǎnd)Рич‧мъндРич‧мънд
Passedми́чман (míčman)мич‧манмич‧ман
Passedразче́ша (razčéša)раз‧че‧шараз‧че‧ша
Passedпе́цма (pécma)пец‧мапец‧ма
Passedсестра́ (sestrá)се‧страсе‧стра
Passedца́рство (cárstvo)цар‧ствоцар‧ство
Passedнра́вствен (nrávstven)нрав‧ственнрав‧ствен
Passedма́ндраджия (mándradžija)ман‧дра‧джи‧яман‧дра‧джи‧я
Passedмизансце́н (mizanscén)ми‧зан‧сценми‧зан‧сцен
Passedстра́нство (stránstvo)стран‧ствостран‧ство
Passedпростра́нство (prostránstvo)про‧стран‧ствопро‧стран‧ство
Passedро́бство (róbstvo)роб‧створоб‧ство
Passedтранспо́рт (transpórt)тран‧спорттран‧спорт
Passedпосви́кна (posvíkna)по‧свик‧напо‧свик‧на
Passedскръ́ндза (skrǎ́ndza)скрън‧дзаскрън‧дза
Passedгодзи́ла (godzíla)год‧зи‧лагод‧зи‧ла
Passedкамика́дзе (kamikádze)ка‧ми‧кад‧зека‧ми‧кад‧зе
Passedнадживе́я (nadživéja)на‧джи‧ве‧яна‧джи‧ве‧я
Passedскръ́н.дза (skrǎ́n.dza)скрън‧дзаскрън‧дза
Passedго.дзи́ла (go.dzíla)го‧дзи‧лаго‧дзи‧ла
Passedкамика́.дзе (kamiká.dze)ка‧ми‧ка‧дзека‧ми‧ка‧дзе
Passedнад.живе́я (nad.živéja)над‧жи‧ве‧янад‧жи‧ве‧я
Passedбезси́лен (bezsílen)без‧си‧ленбез‧си‧лен
Passedбезшу́мен (bezšúmen)без‧шу‧менбез‧шу‧мен
Passedбезвъзвра́тен (bezvǎzvráten)без‧въз‧вра‧тенбез‧въз‧вра‧тен
Passedбезхабе́рен (bezhabéren)без‧ха‧бе‧ренбез‧ха‧бе‧рен
Passedбезстра́шен (bezstrášen)без‧стра‧шенбез‧стра‧шен
Passedбезхле́бна (bezhlébna)без‧хле‧бнабез‧хле‧бна
Passedбезвре́мие (bezvrémie)без‧вре‧ми‧ебез‧вре‧ми‧е
Passedбезме́рен (bezméren)без‧ме‧ренбез‧ме‧рен
Passedбезли́чен (bezlíčen)без‧ли‧ченбез‧ли‧чен
Passedбезнака́зан (beznakázan)без‧на‧ка‧занбез‧на‧ка‧зан
Passedбезразбо́рен (bezrazbóren)без‧раз‧бо‧ренбез‧раз‧бо‧рен
Passedбезде́тен (bezdéten)без‧де‧тенбез‧де‧тен
Passedбезпардо́нен (bezpardónen)без‧пар‧до‧ненбез‧пар‧до‧нен
Passedбезтеле́сен (beztelésen)без‧те‧ле‧сенбез‧те‧ле‧сен
Passedбезгла́в (bezgláv)без‧главбез‧глав
Passedбезче́стен (bezčésten)без‧че‧стенбез‧че‧стен
Passedбезпризо́рен (bezprizóren)без‧при‧зо‧ренбез‧при‧зо‧рен
Passedбезгре́шен (bezgréšen)без‧гре‧шенбез‧гре‧шен
Passedбезкра́ен (bezkráen)без‧кра‧енбез‧кра‧ен
Passedбезбре́жен (bezbréžen)без‧бре‧женбез‧бре‧жен
Passedбе́здна (bézdna)безд‧набезд‧на
Passedизхвъ́рлям (izhvǎ́rljam)из‧хвър‧лямиз‧хвър‧лям
Passedизсте́на (izsténa)из‧сте‧наиз‧сте‧на
Passedи́звор (ízvor)из‧вориз‧вор
Passedизво́звам (izvózvam)из‧воз‧вамиз‧воз‧вам
Passedизвла́ча (izvláča)из‧вла‧чаиз‧вла‧ча
Passedизхра́чване (izhráčvane)из‧храч‧ва‧неиз‧храч‧ва‧не
Passedизшму́гна (izšmúgna)из‧шмуг‧наиз‧шмуг‧на
Passedизживя́ното (izživjánoto)из‧жи‧вя‧но‧тоиз‧жи‧вя‧но‧то
Passedизнена́да (iznenáda)из‧не‧на‧даиз‧не‧на‧да
Passedизлъ́гах (izlǎ́gah)из‧лъ‧гахиз‧лъ‧гах
Passedизмя́на (izmjána)из‧мя‧наиз‧мя‧на
Passedи́зрод (ízrod)из‧родиз‧род
Passedизтрезви́телно (iztrezvítelno)из‧трез‧ви‧тел‧ноиз‧трез‧ви‧тел‧но
Passedизпростя́вял (izprostjávjal)из‧про‧стя‧вялиз‧про‧стя‧вял
Passedизклю́чвам (izkljúčvam)из‧ключ‧вамиз‧ключ‧вам
Passedизбли́за (izblíza)из‧бли‧заиз‧бли‧за
Passedнадсло́в (nadslóv)над‧словнад‧слов
Passedнадхвъ́рлен (nadhvǎ́rlen)над‧хвър‧леннад‧хвър‧лен
Passedнадви́квам (nadvíkvam)над‧вик‧вамнад‧вик‧вам
Passedнадве́ (nadvé)над‧венад‧ве
Passedнадгро́бен (nadgróben)над‧гро‧беннад‧гро‧бен
Passedна́дпис (nádpis)над‧писнад‧пис
Passedнадценя́вам (nadcenjávam)над‧це‧ня‧вамнад‧це‧ня‧вам
Passedнадделе́я (naddeléja)над‧де‧ле‧янад‧де‧ле‧я
Passedнад.ра́ствам (nad.rástvam)над‧ра‧ствамнад‧ра‧ствам
Passedнадмо́щие (nadmóštie)над‧мо‧щи‧енад‧мо‧щи‧е
Passedненадми́нат (nenadmínat)не‧над‧ми‧натне‧над‧ми‧нат
Passedбезнадзо́рен (beznadzóren)без‧над‧зо‧ренбез‧над‧зо‧рен
Passedна́дница (nádnica)над‧ни‧цанад‧ни‧ца
Passedнадме́нност (nadménnost)над‧мен‧ностнад‧мен‧ност
Passedна.длъ́ж (na.dlǎ́ž)на‧длъжна‧длъж
Passedнадробе́н (nadrobén)на‧дро‧бенна‧дро‧бен
Passedнадръ́нкам (nadrǎ́nkam)на‧дрън‧камна‧дрън‧кам
Passedнадра́скам (nadráskam)на‧дра‧скамна‧дра‧скам
Passedнадру́сам (nadrúsam)на‧дру‧самна‧дру‧сам
Passedнадра́н (nadrán)на‧дранна‧дран
Passedподстрека́тел (podstrekátel)под‧стре‧ка‧телпод‧стре‧ка‧тел
Passedподхо́д (podhód)под‧ходпод‧ход
Passedподви́жен (podvížen)под‧ви‧женпод‧ви‧жен
Passedподзагла́вие (podzaglávie)под‧за‧гла‧ви‧епод‧за‧гла‧ви‧е
Passedподкла́ждам (podkláždam)под‧клаж‧дампод‧клаж‧дам
Passedподбо́р (podbór)под‧борпод‧бор
Passedподпи́рам (podpíram)под‧пи‧рампод‧пи‧рам
Passedподценя́вам (podcenjávam)под‧це‧ня‧вампод‧це‧ня‧вам
Passedподновя́вам (podnovjávam)под‧но‧вя‧вампод‧но‧вя‧вам
Passedподма́мвам (podmámvam)под‧мам‧вампод‧мам‧вам
Passedпо́длост (pódlost)под‧лостпод‧лост
Passedпод.разделе́ние (pod.razdelénie)под‧раз‧де‧ле‧ни‧епод‧раз‧де‧ле‧ни‧е
Passedподро́бен (podróben)по‧дро‧бенпо‧дро‧бен
Passedподража́вам (podražávam)по‧дра‧жа‧вампо‧дра‧жа‧вам
Passedподре́мя (podrémja)по‧дре‧мяпо‧дре‧мя
Passedподру́сам (podrúsam)по‧дру‧сампо‧дру‧сам
Passedбезизра́зен (bezizrázen)бе‧зиз‧ра‧зенбе‧зиз‧ра‧зен
Passedбезизра́зност (bezizráznost)бе‧зиз‧ра‧зностбе‧зиз‧ра‧зност
Passedбезвъзме́зден (bezvǎzmézden)без‧въз‧мез‧денбез‧въз‧мез‧ден
Passedбезвъзду́шен (bezvǎzdúšen)без‧въз‧ду‧шенбез‧въз‧ду‧шен
Passedбезразли́чен (bezrazlíčen)без‧раз‧ли‧ченбез‧раз‧ли‧чен
Passedбезразбо́рност (bezrazbórnost)без‧раз‧бор‧ностбез‧раз‧бор‧ност
Passedбезпредме́тен (bezpredméten)без‧пред‧ме‧тенбез‧пред‧ме‧тен
Passedпоизпра́вя (poizprávja)по‧из‧пра‧вяпо‧из‧пра‧вя
Passedпоизмъ́ча (poizmǎ́ča)по‧из‧мъ‧чапо‧из‧мъ‧ча
Passedпоизгла́дя (poizgládja)по‧из‧гла‧дяпо‧из‧гла‧дя
Passedпроизноше́ние (proiznošénie)про‧из‧но‧ше‧ни‧епро‧из‧но‧ше‧ни‧е
Passedпроизти́ча (proiztíča)про‧из‧ти‧чапро‧из‧ти‧ча
Passedнаизми́слил (naizmíslil)на‧из‧ми‧слилна‧из‧ми‧слил
Passedнаизле́злите (naizlézlite)на‧из‧ле‧зли‧тена‧из‧ле‧зли‧те
Passedпредразположе́ние (predrazpoložénie)пред‧раз‧по‧ло‧же‧ни‧епред‧раз‧по‧ло‧же‧ни‧е
Passedпреразгле́ждане (prerazgléždane)пре‧раз‧глеж‧да‧непре‧раз‧глеж‧да‧не
Passedпреразпределе́ние (prerazpredelénie)пре‧раз‧пре‧де‧ле‧ни‧епре‧раз‧пре‧де‧ле‧ни‧е
Passedпрера́зказ (prerázkaz)пре‧раз‧казпре‧раз‧каз
Passedпревъзмо́гна (prevǎzmógna)пре‧въз‧мог‧напре‧въз‧мог‧на
Passedпревъзпита́ние (prevǎzpitánie)пре‧въз‧пи‧та‧ни‧епре‧въз‧пи‧та‧ни‧е
Passedпреизда́вам (preizdávam)пре‧из‧да‧вампре‧из‧да‧вам
Passedпреизби́рам (preizbíram)пре‧из‧би‧рампре‧из‧би‧рам
Passedневъзмо́жен (nevǎzmóžen)не‧въз‧мо‧женне‧въз‧мо‧жен
Passedневъзпи́тан (nevǎzpítan)не‧въз‧пи‧танне‧въз‧пи‧тан
Passedнеизбе́жен (neizbéžen)не‧из‧бе‧женне‧из‧бе‧жен
Passedнеизме́нност (neizménnost)не‧из‧мен‧ностне‧из‧мен‧ност
Passedнеразде́лен (nerazdélen)не‧раз‧де‧ленне‧раз‧де‧лен
Passedнеразположе́ние (nerazpoložénie)не‧раз‧по‧ло‧же‧ни‧ене‧раз‧по‧ло‧же‧ни‧е
Passedпоразми́сля (porazmíslja)по‧раз‧ми‧сляпо‧раз‧ми‧сля
Passedпораздру́сам (porazdrúsam)по‧раз‧дру‧сампо‧раз‧дру‧сам
Passedнаразка́зах (narazkázah)на‧раз‧ка‧захна‧раз‧ка‧зах
Passedнаразлепи́л (narazlepíl)на‧раз‧ле‧пилна‧раз‧ле‧пил
Passedнеотло́жен (neotlóžen)не‧от‧ло‧женне‧от‧ло‧жен
Passedнеотмени́м (neotmením)не‧от‧ме‧нимне‧от‧ме‧ним
Passedпоотло́жа (pootlóža)по‧от‧ло‧жапо‧от‧ло‧жа
Passedпоотми́на (pootmína)по‧от‧ми‧напо‧от‧ми‧на
Passedуе́лски (uélski)у‧ел‧скиу‧ел‧ски
Passedуе́бсайт (uébsajt)у‧еб‧сайту‧еб‧сайт
Passedуе́стърн (uéstǎrn)у‧е‧стърну‧е‧стърн
PassedО́уен (Óuen)О‧у‧енО‧у‧ен
Passedно́уха̀у (nóuhàu)но‧у‧ха‧уно‧у‧ха‧у
PassedДжо́узеф (Džóuzef)Джо‧у‧зефДжо‧у‧зеф
Passedбо́улинг (bóuling)бо‧у‧лингбо‧у‧линг
Passedда̀унло́уд (dàunlóud)да‧ун‧ло‧удда‧ун‧ло‧уд
Passedуи́ски (uíski)у‧и‧скиу‧и‧ски
Passedуи́кенд (uíkend)у‧и‧кенду‧и‧кенд
PassedУо́руик (Uóruik)У‧о‧ру‧икУ‧о‧ру‧ик
PassedХе́лоуин (Hélouin)Хе‧ло‧у‧инХе‧ло‧у‧ин
Passedўе́лскиуел‧скиуел‧ски
Passedўе́бсайтуеб‧сайтуеб‧сайт
Passedўе́стърнуе‧стърнуе‧стърн
PassedО́ўенО‧уенО‧уен
Passedно́ўха̀ўноу‧хауноу‧хау
PassedДжо́ўзефДжоу‧зефДжоу‧зеф
Passedбо́ўлингбоу‧лингбоу‧линг
Passedда̀ўн.ло́ўддаун‧лоуддаун‧лоуд
Passedўи́скиуи‧скиуи‧ски
Passedўи́кендуи‧кендуи‧кенд
PassedЎо́рўикУор‧уикУор‧уик
PassedХе́лоўинХе‧ло‧уинХе‧ло‧уин
Passedўи́нд.сърфингуинд‧сър‧фингуинд‧сър‧финг
Passedра́зни хо́ра-ра́зни вку́сове (rázni hóra-rázni vkúsove)раз‧ни хо‧ра-раз‧ни вку‧со‧вераз‧ни хо‧ра-раз‧ни вку‧со‧ве
Passedакуше́р-гинеколо́г (akušér-ginekológ)а‧ку‧шер-ги‧не‧ко‧лога‧ку‧шер-ги‧не‧ко‧лог
Passedна́й-напре́д (náj-napréd)най-на‧преднай-на‧пред
Passedампер-ча́с (amper-čás)ам‧пер-часам‧пер-час
Passedга́-га́ (gá-gá)га-гага-га
Passedпи́-пи́ (pí-pí)пи-пипи-пи
PassedГвине́я-Биса́у (Gvinéja-Bisáu)Гви‧не‧я-Би‧са‧уГви‧не‧я-Би‧са‧у
Passedша́м-фъстъ́к (šám-fǎstǎ́k)шам-фъ‧стъкшам-фъ‧стък
Passedвълна́-уби́ец (vǎlná-ubíec)въл‧на-у‧би‧ецвъл‧на-у‧би‧ец
Passedакуше́р-гинеколо́г (akušér-ginekológ)а‧ку‧шер-ги‧не‧ко‧лога‧ку‧шер-ги‧не‧ко‧лог
Passedпо́-добре́ къ́сно, отко́лкото ни́кога (pó-dobré kǎ́sno, otkólkoto níkoga)по-до‧бре къ‧сно, от‧кол‧ко‧то ни‧ко‧гапо-до‧бре къ‧сно, от‧кол‧ко‧то ни‧ко‧га
Passedзени́тно-раке́тен (zenítno-rakéten)зе‧нит‧но-ра‧ке‧тензе‧нит‧но-ра‧ке‧тен
Passedго́ре-до́лу (góre-dólu)го‧ре-до‧луго‧ре-до‧лу
Passedна́й-по́сле (náj-pósle)най-по‧сленай-по‧сле
Passedчик-чири́к (čik-čirík)чик-чи‧рикчик-чи‧рик
Passedсре́ден ро́д (sréden ród)сре‧ден родсре‧ден род
Passedбожа́ кра́вичка (božá krávička)бо‧жа кра‧вич‧кабо‧жа кра‧вич‧ка
PassedСъедине́ни америка́нски ща́ти (Sǎedinéni amerikánski štáti)Съ‧е‧ди‧не‧ни а‧ме‧ри‧кан‧ски ща‧тиСъ‧е‧ди‧не‧ни а‧ме‧ри‧кан‧ски ща‧ти
Passedот мла́дих до ста́рих (ot mládih do stárih)от мла‧дих до ста‧рихот мла‧дих до ста‧рих
Passedсо кро́тце, со бла́го и со ма́лко кюте́к (so krótce, so blágo i so málko kjuték)со крот‧це, со бла‧го и со мал‧ко кю‧тексо крот‧це, со бла‧го и со мал‧ко кю‧тек
  • Тилков, Димитър; Бояджиев, Тодор; Георгиева, Елена; Пенчев, Йордан; Станков, Валентин (1998), Граматика на съвременния български книжовен език (බල්ගේරියානු බසින්), 3rd edition, volume 1, Sofia: ABAGAR

local export = {}

local substring = mw.ustring.sub
local rsubn = mw.ustring.gsub
local rmatch = mw.ustring.match
local rsplit = mw.text.split
local rlen = mw.ustring.len
local U = require("Module:string/char")
local lang = require("Module:languages").getByCode("bg")
local script = require("Module:scripts").getByCode("Cyrl")
local ipa_module = "Module:IPA"
local audio_module = "Module:audio"
local headword_data_module = "Module:headword/data"
local homophones_module = "Module:homophones"
local hyphenation_module = "Module:hyphenation"
local parameters_module = "Module:parameters"
local rhymes_module = "Module:rhymes"
local table_module = "Module:table"
local tracking_module = "Module:debug/track"

local GRAVE = U(0x300)
local ACUTE = U(0x301)
local BREVE = U(0x306)
local PRIMARY = U(0x2C8)
local SECONDARY = U(0x2CC)
local TIE = U(0x361)
local FRONTED = U(0x31F)
local DOTUNDER = U(0x323)
local HYPH = U(0x2027)
local BREAK_MARKER = "."
local vowels = "aɤɔuɛiɐo"
local vowels_c = "[" .. vowels .. "]"
local cons = "bvɡdʒzjklɫwmnprstfxʃɣʲ" .. TIE
local cons_c = "[" .. cons .. "]"
local hcons_c = "[бвгджзйклмнпрстфхшщьчц#БВГДЖЗЙКЛМНПРСТФХШЩЬЧЦ=]"
local hvowels_c = "[аъоуеияѝюАЪОУЕИЯЍЮ]"
local capital_letters_c = "[БВГДЖЗЙКЛМНПРСТФХШЩЬЧЦАЪОУЕИЯЍЮ]"
local accents = PRIMARY .. SECONDARY
local accents_c = "[" .. accents .. "]"

-- single characters that map to IPA sounds
local phonetic_chars_map = {
	["а"] = "a",
	["б"] = "b",
	["в"] = "v",
	["г"] = "ɡ",
	["д"] = "d",
	["е"] = "ɛ",
	["ж"] = "ʒ",
	["з"] = "z",
	["и"] = "i",
	["й"] = "j",
	["к"] = "k",
	["л"] = "l",
	["м"] = "m",
	["н"] = "n",
	["о"] = "ɔ",
	["п"] = "p",
	["р"] = "r",
	["с"] = "s",
	["т"] = "t",
	["у"] = "u",
	["ў"] = "w",
	["ф"] = "f",
	["х"] = "x",
	["ц"] = "t" .. TIE .. "s",
	["ч"] = "t" .. TIE .. "ʃ",
	["ш"] = "ʃ",
	["щ"] = "ʃt",
	["ъ"] = "ɤ",
	["ь"] = "ʲ",
	["ю"] = "ʲu",
	["я"] = "ʲa",

	[GRAVE] = SECONDARY,
	[ACUTE] = PRIMARY
}

local devoicing = {
	["b"] = "p", ["d"] = "t", ["ɡ"] = "k",
	["z"] = "s", ["ʒ"] = "ʃ",
	["v"] = "f"
}

local voicing = {
	["p"] = "b", ["t"] = "d", ["k"] = "ɡ",
	["s"] = "z", ["ʃ"] = "ʒ", ["x"] = "ɣ",
	["f"] = "v"
}


-- Prefixes where, if they occur at the beginning of the word and the stress is on the next syllable, we place the
-- syllable division directly after the prefix. For example, the default syllable-breaking algorithm would convert
-- безбра́чие to беˈзбрачие; but because it begins with без-, we convert it to безˈбрачие. Note that we don't (yet?)
-- convert измра́ to изˈмра instead of default измˈра, although we probably should.
--
-- Think twice before putting prefixes like на-, пре- and от- here, because of the existence of над-, пред-, and о-,
-- which are also prefixes.
local IPA_prefixes = {"bɛz", "vɤz", "vɤzproiz", "iz", "naiz", "poiz", "prɛvɤz", "proiz", "raz"}


-- version of rsubn() that discards all but the first return value
local function rsub(term, foo, bar)
	local retval = rsubn(term, foo, bar)
	return retval
end

-- version of rsubn() that discards all but the count value
local function count_matches(term, pattern)
	local _, match_count = rsubn(term, pattern, "")
	return match_count
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

local function char_at(str, index)
	return substring(str, index, index)
end

local function starts_with(str, substr)
	return substring(str, 1, rlen(substr)) == substr
end

local function count_vowels(word)
	return count_matches(word, hvowels_c)
end

local function count_capital_letters(word)
	return count_matches(word, capital_letters_c)	
end

local function count_accents(ipa)
	return count_matches(ipa, accents_c)
end

local function count_ipa_vowels(ipa)
	return count_matches(ipa, vowels_c)
end

function export.remove_pron_notations(text, remove_grave)
	text = rsub(text, "[." .. DOTUNDER .. "]", "")
	text = rsub(text, "ў", "у")
	text = rsub(text, "Ў", "У")

	-- Remove grave accents from annotations but maybe not from phonetic respelling
	if remove_grave then
		text = mw.ustring.toNFC(rsub(mw.ustring.toNFD(text), GRAVE, ""))
	end
	return text
end

function export.toIPA(term, endschwa)
	if type(term) == "table" then -- called from a template or a bot
		endschwa = term.args.endschwa
		term = term.args[1]
	end

	local origterm = term

	term = mw.ustring.toNFD(mw.ustring.lower(term))
	term = rsub(term, "у" .. BREVE, "ў") -- recompose ў
	term = rsub(term, "и" .. BREVE, "й") -- recompose й

	if term:find(GRAVE) and not term:find(ACUTE) then
		error("Use acute accent, not grave accent, for primary stress: " .. origterm)
	end

	-- allow DOTUNDER to signal same as endschwa=1
	term = rsub(term, "а(" .. accents_c .. "?)" .. DOTUNDER, "ъ%1")
	term = rsub(term, "я(" .. accents_c .. "?)" .. DOTUNDER, "ʲɤ%1")
	term = rsub(term, ".", phonetic_chars_map)

	-- Mark word boundaries
	term = rsub(term, "(%s+)", "#%1#")
	term = "#" .. term .. "#"

	-- Convert verbal and definite endings
	if endschwa then
		term = rsub(term, "a(" .. PRIMARY .. "?t?#)", "ɤ%1")
	end

	-- Change ʲ to j after vowels or word-initially
	term = rsub(term, "([" .. vowels .. "#]" .. accents_c .. "?)ʲ", "%1j")

	-------------------- Move stress ---------------

	-- First, move leftwards over the vowel.
	term = rsub(term, "(" .. vowels_c .. ")(" .. accents_c .. ")", "%2%1")
	-- Then, move leftwards over j or soft sign.
	term = rsub(term, "([jʲ])(" .. accents_c .. ")", "%2%1")
	-- Then, move leftwards over a single consonant.
	term = rsub(term, "(" .. cons_c .. ")(" .. accents_c .. ")", "%2%1")
	-- Then, move leftwards over Cl/Cr combinations where C is an obstruent (NOTE: IPA ɡ).
	term = rsub(term, "([bdɡptkxfv]" .. ")(" .. accents_c .. ")([rl])", "%2%1%3")
	-- Then, move leftwards over kv/gv (NOTE: IPA ɡ).
	term = rsub(term, "([kɡ]" .. ")(" .. accents_c .. ")(v)", "%2%1%3")
	-- Then, move leftwards over sC combinations, where C is a stop or resonant (NOTE: IPA ɡ).
	term = rsub(term, "([sz]" .. ")(" .. accents_c .. ")([bdɡptkvlrmn])", "%2%1%3")
	-- Then, move leftwards over affricates not followed by a consonant.
	term = rsub(term, "([td]" .. TIE .. "?)(" .. accents_c .. ")([szʃʒ][" .. vowels .. "ʲ])", "%2%1%3")
	-- If we ended up in the middle of a tied affricate, move to its right.
	term = rsub(term, "(" .. TIE .. ")(" .. accents_c .. ")(" .. cons_c .. ")", "%1%3%2")
	-- Then, move leftwards over any remaining consonants at the beginning of a word.
	term = rsub(term, "#(" .. cons_c .. "*)(" .. accents_c .. ")", "#%2%1")
	-- Then correct for known prefixes.
	for _, prefix in ipairs(IPA_prefixes) do
		local prefix_prefix, prefix_final_cons = rmatch(prefix, "^(.-)(" .. cons_c .. "*)$")
		if prefix_final_cons then
			-- Check for accent moved too far to the left into a prefix, e.g. безбрачие accented as беˈзбрачие instead
			-- of безˈбрачие
			term = rsub(term, "#(" .. prefix_prefix .. ")(" .. accents_c .. ")(" .. prefix_final_cons .. ")", "#%1%3%2")
		end
	end
	-- If the previous substitution resulted in a stress occuring immediately after a consonant
	-- but before a palatalizer mark, then put the stress before the consonant.
	term = rsub(term, "(" .. cons_c .. ")" .. "(" .. accents_c .. ")" .. "ʲ", "%2%1ʲ")
	-- Finally, if there is an explicit syllable boundary in the cluster of consonants where the stress is, put it there.
	-- First check for accent to the right of the explicit syllable boundary.
	term = rsub(term, "(" .. cons_c .. "*)%.(" .. cons_c .. "*)(" .. accents_c .. ")(" .. cons_c .. "*)", "%1%3%2%4")
	-- Then check for accent to the left of the explicit syllable boundary.
	term = rsub(term, "(" .. cons_c .. "*)(" .. accents_c .. ")(" .. cons_c .. "*)%.(" .. cons_c .. "*)", "%1%3%2%4")
	-- Finally, remove any remaining syllable boundaries.
	term = rsub(term, "%.", "")

	-------------------- Vowel reduction (in unstressed syllables) ---------------
	local function reduce_vowel(vowel)
		return rsub(vowel, "[aɔɤu]", { ["a"] = "ɐ", ["ɔ"] = "o", ["ɤ"] = "ɐ", ["u"] = "o" })
	end

	-- Reduce all vowels before the stress, except if the word has no accent at all. (FIXME: This is presumably
	-- intended for single-syllable words without accents, but if the word is multisyllabic without accents,
	-- presumably all vowels should be reduced.)

	term = rsub(term, "(#[^#" .. accents .. "]*)(.-#)", function(a, b)
		if count_vowels(origterm) <= 1 then
			return a .. b
		else
			return reduce_vowel(a) .. b
		end
	end)
	-- Reduce all vowels after the accent except the first vowel after the accent mark (which is stressed).
	term = rsub(term, "(" .. accents_c .. "[^aɛiɔuɤ#]*[aɛiɔuɤ])([^#" .. accents .. "]*)", function(a, b)
		return a .. reduce_vowel(b)
	end)

	-------------------- Vowel assimilation to adjacent consonants (fronting/raising) ---------------
	term = rsub(term, "([ʃʒʲj])([aouɤ])", "%1%2" .. FRONTED)

	-- Hard l
	term = rsub_repeatedly(term, "l([^ʲɛi])", "ɫ%1")


	-- Voicing assimilation
	term = rsub(term, "([bdɡzʒv" .. TIE .. "]*)(" .. accents_c .. "?[ptksʃfx#])", function(a, b)
		return rsub(a, ".", devoicing) .. b end)
	term = rsub(term, "([ptksʃfx" .. TIE .. "]*)(" .. accents_c .. "?[bdɡzʒ])", function(a, b)
		return rsub(a, ".", voicing) .. b end)
	term = rsub(term, "n(" .. accents_c .. "?[ɡk]+)", "ŋ%1")
	term = rsub(term, "m(" .. accents_c .. "?[fv]+)", "ɱ%1")

	-- Sibilant assimilation
	term = rsub(term, "[sz](" .. accents_c .. "?[td]?" .. TIE .. "?)([ʃʒ])", "%2%1%2")

	-- Reduce consonant clusters
	term = rsub(term, "([szʃʒ])[td](" .. accents_c .. "?)([tdknml])", "%2%1%3")

	-- Strip hashes
	term = rsub(term, "#", "")

	return term
end

----Syllabification code----
-- Authorship: Chernorizets
-- Lua port: Kiril Kovachev

local function set_of(t)
	local out = {}

	for _, v in pairs(t) do
		out[v] = true
	end

	return out
end

local function in_set(set, value)
	return set[value] == true
end

-- Classification of letters by phonetic category
local vowels_syllab = set_of {"а", "ъ", "о", "у", "е", "и", "ю", "я"}
local sonorants     = set_of { "л", "м", "н", "р", "й", "ў"}
local stops         = set_of {"б", "п", "г", "к", "д", "т"}
local fricatives    = set_of {"в", "ф", "ж", "ш", "з", "с", "х"}
local affricates    = set_of {"ч", "ц"}

local function is_vowel(ch)
    return in_set(vowels_syllab, ch)
end

local function is_palatalizer(ch)
	return ch == "ь"
end

local function is_sonorant(ch)
    return in_set(sonorants, ch)
end

local function is_stop(ch)
	return in_set(stops, ch)
end

local function is_fricative(ch)
	return in_set(fricatives, ch)
end

local function is_affricate(ch)
    return in_set(affricates, ch)
end

--[[
Sonority objects:

Sonority objects take the form of a table with the following attributes:
{
	rank (int): the numerical value representing the position of the sound in the sonority hierarchy;
	first_index (int): the index of the first letter that makes up the sound within the word.
	    The index of the first letter in a word with this sonority rank.
        The affricates "дж" and "дз" are represented by two letters each, but
        for sonority purposes they function as a "unit", hence we just need
		the index of the first letter of the affricate.
}


--]]

local function new_sonority(rank, first_index)
	return {
		["rank"] = rank,
		["first_index"] = first_index
	}
end

local function get_sonority_rank(ch)
    if is_fricative(ch) then
        return 1
	end

    if is_stop(ch) or is_affricate(ch) then
        return 2
    end

    if is_sonorant(ch) then
        return 3
    end

    if is_vowel(ch) then
        return 4
	end

    return 0
end

-- Get the representation of a word as a list of sequential sonority objects, stored in a table.
-- Their representation is just {[1] = (sonority object #1), [2] = (sonority object #2)} etc.
-- Please see above for description of sonority objects' layout.
local function get_sonority_model(word, start_idx, end_idx)
    local sonorities = {}

	word = mw.ustring.lower(word)

	local i = start_idx
	while i < end_idx do
	    local curr = char_at(word, i)
        if curr == "щ" then
            -- One letter representing 2 sounds - decompose it.
            table.insert(sonorities, new_sonority(get_sonority_rank("ш"), i))
            table.insert(sonorities, new_sonority(get_sonority_rank("т"), i));
        elseif curr == "д" then
            -- Handle affricates with 'д' - only 'дж' here for illustration.
            local next_char = (i == end_idx - 1 and " ") or char_at(word, i+1)

			local should_skip = false
            if next_char == "ж" then
                table.insert(sonorities, new_sonority(2, i)) -- 2 = affricate sonority rank
                i = i + 1 -- Skip over the 'ж'
                should_skip = true
            end

            if not should_skip then table.insert(sonorities, new_sonority(get_sonority_rank("д"), i)) end

        elseif not is_palatalizer(curr) then
            -- Skip over 'ь' since it doesn't change the sonority.
            table.insert(sonorities, new_sonority(get_sonority_rank(curr), i))
        end
        i = i + 1
	end

    return sonorities
end

-- Forced breaks when the user inputs a break marker into the input string
-- word: string; start and end are integers indexing the string
local function find_forced_break(word, range_start, range_end)
        if range_start >= range_end then return -1 end

        local marker_pos = mw.ustring.find(word, BREAK_MARKER, range_start, true) or -1
        return marker_pos >= range_end and -1 or marker_pos
end

local function strip_forced_breaks(segment)
    return rsub(segment, "[.]", "");
end

---- Morphological prefix handling
--[[
	This code brings morphological prefix awareness to syllabification.
	This is necessary, because following the principle of rising sonority
	alone fails to determine syllable boundaries correctly in some cases
    — that is, when certain prefixes should be kept together as a first syllable.
]]

--[[
	Affected prefixes. Each of them ends in a consonant that can be followed
	by another consonant of a higher sonority in some words. In such cases,
	naive syllable breaking would chop off the prefix's last consonant, and
	glue it to the onset of the next syllable.
]]

local prefixes = {
	-- без- family
    "без",

    -- из- family
    "безиз", "наиз", "поиз", "произ", "преиз", "неиз", "из",

    -- въз- family
    "безвъз", "превъз", "невъз", "въз",

    -- раз- family
    "безраз", "предраз", "пораз", "нараз", "прераз", "нераз", "раз",

    -- от- family
    "неот", "поот", "от",

    -- ending in fricatives
    "екс", "таз", "дис",

    -- ending in stops
    "пред"
}

--[[
    Finds the (zero-based) separation point between a
    morphological prefix and the rest of the word.
    By convention, that's the index of the first character
    after the prefix.

    word: the word to check for prefixes

    return -1 if no prefix found, or if the separation point
    is handled by the sonority model. A non-zero index otherwise.
]]

local function followed_by_higher_sonority_cons(prefix, word) -- prefix, word are both strings
	prefix = mw.ustring.lower(prefix)
	word = mw.ustring.lower(word)

    local prefix_last_char = char_at(prefix, rlen(prefix))
    local first_char_after_prefix = char_at(word, rlen(prefix) + 1)

    -- Prefixes followed by vowels do, in fact, get broken up.
    if is_vowel(first_char_after_prefix) then return false end

    return get_sonority_rank(prefix_last_char) < get_sonority_rank(first_char_after_prefix)
end

local function find_separation_points(word)
	local matching_prefixes = {}

	word = mw.ustring.lower(word)

	for _, prefix in pairs(prefixes) do
		if starts_with(word, prefix) and followed_by_higher_sonority_cons(prefix, word) then
			table.insert(matching_prefixes, rlen(prefix) + 1)
		end
	end

	return matching_prefixes

end

---- Main syllabification code

---Context objects:
--[[ encoded as a table like
{
	word (string),
	prefix_separation_points (table[int])
}

]]

local function new_context(word, pos)
	return {
		["word"] = word,
		["prefix_separation_points"] = pos
	}
end

--[[
    Consonant clusters that exhibit rising sonority, but should be
    broken up regardless to produce natural-sounding syllables.
    The breakpoint for clusters of 3 or more consonants can vary –
    here we provide a zero-based offset within the cluster for each.
]]
local sonority_exception_break = {
	["км"] = 1, ["гм"] = 1, ["дм"] = 1, ["вм"] = 1,
    ["зм"] = 1, ["цм"] = 1, ["чм"] = 1,
    ["дн"] = 1, ["вн"] = 1, ["тн"] = 1, ["чн"] = 1,
    ["кн"] = 1, ["гн"] = 1, ["цн"] = 1,
    ["зд"] = 1, ["зч"] = 1, ["зц"] = 1,
    ["вк"] = 1, ["вг"] = 1, ["дл"] = 1, ["жд"] = 1,
    ["згн"] = 1, ["здн"] = 2, ["вдж"] = 1
}

local sonority_exception_keep = {
    "ств", "св", "вс"
}

local function normalize_word(word)
    if word == nil then return "" end

    word = rsub(rsub(word, "^\\s+", ""), "\\s+^", "") -- Strip spaces
    return word
end

local function normalize_syllable(syllable)
    local normalized = strip_forced_breaks(syllable)
    normalized = rsub(normalized, "ў", "у")
    normalized = rsub(normalized, "Ў", "У")
    return normalized
end

local function find_rising_sonority_break(sonorities)
    local prev_rank = -1;

    for _, curr in pairs(sonorities) do
        if curr.rank <= prev_rank then
            -- Found a break.
            return curr.first_index
        end

        prev_rank = curr.rank
    end

    -- There was no rising sonority break. Start syllable at first index.
    return sonorities[1].first_index
end

local function matches(str, substr, start_idx, end_idx)
    local strlen = end_idx - start_idx
    if strlen ~= rlen(substr) then return false end

	str = mw.ustring.lower(str)
	substr = mw.ustring.lower(substr)

	local i = start_idx
	local j = 1
	while i < end_idx do
		if char_at(str, i) ~= char_at(substr, j) then return false end
		i = i + 1
		j = j + 1
	end

    return true
end

-- ctx: context object
-- left and right vowels: integers
-- sonority break: integer
local function fixup_syllable_onset(ctx, left_vowel, sonority_break, right_vowel)
    local word = mw.ustring.lower(ctx.word)

    -- 'щр' is a syllable onset when in front of a vowel.
    -- Although 'щ' + sonorant technically follows rising sonority, syllables
    -- like щнV, щлV etc. are unnatural and incorrect. In such cases, we treat
    -- the sonorant as the onset of the next syllable.
    if char_at(word, right_vowel - 2) == "щ" then
        local penult = char_at(word, right_vowel - 1)

        if penult == "р" then return (right_vowel - 2) end
        if is_sonorant(penult) then return (right_vowel - 1) end
    end

    -- Check for situations where we shouldn't break the cluster.
    local match_found = false
    for _, cluster in pairs(sonority_exception_keep) do
    	if matches(word, cluster, left_vowel + 1, right_vowel) then
    		match_found = true
    		break
    	end
    end

    if (match_found) then return left_vowel + 1 end -- syllable onset == beginning of cluster

    -- Check for situations where we should break the cluster even if
    -- it obeys the principle of rising sonority.
    local maybe_cluster = nil
    for cluster, _ in pairs(sonority_exception_break) do
    	if matches(word, cluster, left_vowel + 1, right_vowel) then
    		maybe_cluster = cluster
    		break
    	end
    end

    if maybe_cluster ~= nil then
        local offset = sonority_exception_break[maybe_cluster]
        return left_vowel + 1 + offset
    end

    local separation_points = ctx.prefix_separation_points
    local separation_match = nil
    for _, pos in pairs(separation_points) do
    	if pos > left_vowel and pos < right_vowel then
    		separation_match = pos
    		break
    	end
    end

    if separation_match ~= nil then return separation_match else return sonority_break end
end

-- ctx: context object
-- left/right vowels: integers
local function find_next_syllable_onset(ctx, left_vowel, right_vowel)
    local n_cons = right_vowel - left_vowel - 1

    -- No consonants - syllable starts on rightVowel
    if n_cons == 0 then return right_vowel end

    -- Check for forced breaks
    local break_pos = find_forced_break(ctx.word, left_vowel + 1, right_vowel)
    if break_pos ~= -1 then return break_pos + 1 end

    -- Single consonant between two vowels - starts a syllable
    if n_cons == 1 then return left_vowel + 1 end

    -- Two or more consonants between the vowels. Find the point (if any)
    -- where we break from rising sonority, and treat it as the tentative
    -- onset of a new syllable.
    local sonorities = get_sonority_model(ctx.word, left_vowel + 1, right_vowel)
    local sonority_break = find_rising_sonority_break(sonorities)

    -- Apply exceptions to the rising sonority principle to avoid
    -- unnatural-sounding syllables.
    return fixup_syllable_onset(ctx, left_vowel, sonority_break, right_vowel)
end

local function deaccent(term)
	return rsub(term, "[" .. ACUTE .. GRAVE .. DOTUNDER .. "]", "")
end

local function deaccent_all(term)
	term = rsub(term, "ў", "у")
	term = rsub(term, "Ў", "У")
	return deaccent(term)
end

-- Returns a table of strings (list)
local function syllabify_poly(word)
    local syllables = {}

    local ctx = new_context(word, find_separation_points(word))

    local prev_vowel = -1
    local prev_onset = 1;

    for i = 1, rlen(word) do
	    if is_vowel(mw.ustring.lower(char_at(word, i))) then
	        -- A vowel, yay!
	        local should_skip = false
	        if prev_vowel == -1 then
	            prev_vowel = i
	            should_skip = true;
	        end

	        -- This is not the first vowel we've seen. In-between
	        -- the previous vowel and this one, there is a syllable
	        -- break, and the first character after the break starts
	        -- a new syllable.
	        if not should_skip then
		        local next_onset = find_next_syllable_onset(ctx, prev_vowel, i)
		        table.insert(syllables, substring(word, prev_onset, next_onset - 1))
		        prev_vowel = i
		        prev_onset = next_onset
			end
	    end

    end

    -- Add the last syllable
    table.insert(syllables, substring(word, prev_onset))

    return syllables
end

function export.syllabify_word(word)
    local norm = normalize_word(word)

    if rlen(norm) == 0 then return "" end;

    local n_vowels = count_vowels(norm)
    local syllables = n_vowels <= 1 and {norm} or syllabify_poly(norm)

	local out = {}
	for k, v in pairs(syllables) do
		out[k] = normalize_syllable(v)
	end

    return table.concat(out, HYPH)
end

local function tokenize_words(term)
	local out = {}
	local prev_index = 1
	local len = rlen(term)
	for i = 1, len do
		local current_char = char_at(term, i)
		if current_char == "-" or current_char == " " then
			table.insert(out, substring(term, prev_index, i))
			prev_index = i + 1
		end
	end
	table.insert(out, substring(term, prev_index, len))
	return out
end

function export.syllabify(term)
	term = deaccent(term)
	local words = tokenize_words(term)

	local out = {}
	for _, word in pairs(words) do
		table.insert(out, export.syllabify_word(word))
	end
	return table.concat(out, "")
end

---Hyphenation

-- Hyphenate a word from its existing syllabification
function export.hyphenate(syllabification)
    -- Source: http://logic.fmi.uni-sofia.bg/hyphenation/hyph-bg.html#hyphenation-rules-between-1983-and-2012
    -- Also note: the rules from 2012 onward, which encode the modern standard, are entirely
    -- backwards-compatible with the previous standard. Thus our code can generate valid 2012
    -- hyphenations despite only explicitly implementing the older (1983) rules.

    ---Pre-processing----
	local word = deaccent(syllabification)
    word = rsub_repeatedly(word, HYPH .. "дж", HYPH .. "#")
    word = rsub_repeatedly(word, "дж$", "#")
    word = rsub_repeatedly(word, "^дж", "#")
    word = rsub_repeatedly(word, "(" .. hvowels_c .. ")" .. HYPH .. "(" .. hcons_c .. ")(" .. rsub(hcons_c, "[ьЬ]", "") .. "+)", "%1%2" .. HYPH .. "%3")
    word = rsub_repeatedly(word, "(" .. rsub(hcons_c, "[йЙ]", "") .. ")(" .. hcons_c .. "+)" .. HYPH, "%1" .. HYPH .. "%2")
    word = rsub_repeatedly(word, "^(" .. hvowels_c .. ")" .. HYPH, "%1")
    word = rsub_repeatedly(word, HYPH .. "(" .. hvowels_c .. ")$", "%1")
    word = rsub_repeatedly(word, "(" .. hvowels_c .. ")" .. HYPH .. "(" .. hvowels_c .. ")" .. HYPH .. "(" .. hvowels_c .. ")", "%1%2" .. HYPH .. "%3")
    word = rsub_repeatedly(word, HYPH .. "(" .. hvowels_c .. ")" .. HYPH .. "(" .. hcons_c .. ")", HYPH .. "%1%2")
    word = rsub_repeatedly(word, "#", "дж")
    return word
end

-- Hyphenate a word directly, no need to calculate its syllabification beforehand (used in test suite)
function export.hyphenate_total(word)
	local syllabification = export.syllabify(word)
	return export.hyphenate(syllabification)
end

local function get_anntext(term, ann)
	if ann == "1" or ann == "y" then
		-- remove secondary stress annotations
		return "'''" .. export.remove_pron_notations(term, true) .. "''':&#32;"
	elseif ann then
		return "'''" .. ann .. "''':&#32;"
	else
		return ""
	end
end

local HYPHENATION_LABEL = "Hyphenation<sup>([[Appendix:Bulgarian hyphenation#Hyphenation|key]])</sup>"
local SYLLABIFICATION_LABEL = "Syllabification<sup>([[Appendix:Bulgarian hyphenation#Syllabification|key]])</sup>"

local function format_hyphenation(hyphenation, label)
	hyphenation = deaccent(hyphenation) -- remove grave/acute accent
	local syllables = rsplit(hyphenation, HYPH)
	label = label or HYPHENATION_LABEL

	return require(hyphenation_module).format_hyphenations {
		lang = lang,
		hyphs = { { hyph = syllables } },
		sc = script,
		caption = label,
	}

end

local function format_syllabification(syllabification)
	return format_hyphenation(syllabification, SYLLABIFICATION_LABEL)
end

-- Display syllabification and hyphenation, together if the same, and on separate lines if not;
-- allows specifying an indentation level, if the hyphenation must be indented to more than one level.
local function render_bg_hyph(term, indentation, syllabification, hyphenation)
	syllabification = syllabification or export.syllabify(term)
	hyphenation = hyphenation or export.hyphenate(syllabification)

	local out = ""
	-- Users must put a * (or **) before the template usage
	if syllabification == hyphenation then
		if syllabification ~= "-" then
			out = format_syllabification(syllabification)
		end
	else
		local syllabification_text = format_syllabification(syllabification)
		local hyphenation_text = format_hyphenation(hyphenation)
		if syllabification ~= "-" then
			out = syllabification_text
		end
		if hyphenation ~= "-" then
			if syllabification == "-" then
				out = hyphenation_text
			else
				out = out .. "\n" .. mw.ustring.rep("*", indentation) .. " " .. hyphenation_text
			end
		end
	end
	
	return out
end

-- Entry point to {{bg-hyph}}
function export.show_hyphenation(frame)
	local params = {
		[1] = {},
		["indent"] = { type = "number" }
	}

	local title = mw.title.getCurrentTitle()

	local args = require(parameters_module).process(frame:getParent().args, params)
	local term = args[1] or title.nsText == "Template" and "при́мер" or title.text
	local indent = args["indent"] or 1
	
	return render_bg_hyph(term, indent)
end

function export.get_rhymes(ipa)
	ipa = rsub(ipa, FRONTED, "")
	
	local length = rlen(ipa)
	local i = length
	local vowels_seen = 0
	local final_consonant_found = false
	-- March until accent is found
	while i > 0 do
		-- Track vowel or final consonant if found
		if vowels_seen == 0 and rmatch(char_at(ipa, i), cons_c) then
			final_consonant_found = true
		end
		if rmatch(char_at(ipa, i), vowels_c) then
			vowels_seen = vowels_seen + 1
		end
		if rmatch(char_at(ipa, i), accents_c) then
			-- Note whether letter before the accent was vowel
			local final_vowel_cluster = rmatch(ipa, vowels_c .. accents_c .. vowels_c .. "$")
			
			-- March until the vowel first following the accent is found
			while i <= length and not rmatch(char_at(ipa, i), vowels_c) do
				i = i + 1
			end
			-- March back if only a single word-final vowel was previously spotted – 
			-- this corresponds to final-syllable-stressed words, whose rhyme
			-- needs to include a consonant according to Bulgarian rhyming rules.
			if vowels_seen <= 1 and not final_consonant_found and not final_vowel_cluster then
				while i > 1 and not rmatch(char_at(ipa, i), rsub(cons_c, "ʲ", "")) do
					i = i - 1
				end

				-- Account for affricates (note: this can only occur in
				-- consonant-rhyme, i.e. final-stressed words)
				if i > 1 and char_at(ipa, i-1) == TIE then
					-- If a tie is present, there must be a letter before it as well.
					i = i - 2
				elseif i > 1 and char_at(ipa, i) == "ʒ" and char_at(ipa, i-1) == "d" then
					-- Treat [dʒ] sequence as an affricate – this can have some edge cases.
					-- In future, the module should distinguish [d.ʒ] and [d͡ʒ]. 
					i = i - 1					
				end
			end
			return substring(ipa, i)
		end
		i = i - 1
	end
	local n_vowels = count_ipa_vowels(ipa)
	if n_vowels == 1 then
		i = length
		if rmatch(char_at(ipa, i), cons_c) then
			while i > 1 and not rmatch(char_at(ipa, i), vowels_c) do
				i = i - 1
			end
		else
			while i > 1 and not rmatch(char_at(ipa, i), rsub(cons_c, "ʲ", "")) do
				i = i - 1
			end
		end
		if i > 1 and char_at(ipa, i - 1) == TIE then
			i = i - 2
		end
		return substring(ipa, i)
	end
	return nil
end

-- Render a single IPA transcription as wikitext (with optional qualifiers + accent labels)
local function format_ipa(ipa, q, qq, a, aa)
	-- Introduce narrow transcription brackets
	ipa = "[" .. ipa .. "]"
	local ipa_data = {
		lang = lang,
		items = {{ pron = ipa }},
		q = q,
		qq = qq,
		a = a,
		aa = aa,
	}
	return require(ipa_module).format_IPA_full(ipa_data)
end

function export.show(frame)
	local params = {
		[1] = {},
		["endschwa"] = { type = "boolean" },
		["ann"] = {},
		["q"] = { type = "qualifier" },
		["qq"] = { type = "qualifier" },
		["a"] = { type = "labels" },
		["aa"] = { type = "labels" },
		["pagename"] = {},
	}

	local args = require(parameters_module).process(frame:getParent().args, params)
	local term = args[1] or args.pagename or mw.title.getCurrentTitle().nsText == "Template" and "при́мер" or
		mw.loadData(headword_data_module).pagename

	local ipa = export.toIPA(term, args.endschwa)
	local ipa_text = format_ipa(ipa, args.q, args.qq, args.a, args.aa)
	local anntext = get_anntext(term, args.ann)

	-- Terms with a lack of stress despite not being monosyllabic
	if count_vowels(term) > 1 and count_accents(ipa) == 0 then
		require(tracking_module)("bg-IPA/no stress")
	end

	return anntext .. ipa_text
end

-- Convert rhyme suffix and optional syllable count to wikitext
local function format_rhymes(rhyme_suffix, syllable_counts, q, qq, l, ll)
	return require(rhymes_module).format_rhymes({
		lang = lang,
		rhymes = {
			{rhyme = rhyme_suffix, num_syl = syllable_counts, q = q, qq = qq, a = l, aa = ll},
		},
	})
end

-- Entry point for {{bg-rhymes}}
function export.show_rhymes(frame)
	-- TODO: add qualifiers / labels
	local params = {
		[1] = {},
		["pagename"] = {},
		["s"] = { sublist = true, type = "number", },
		["q"] = { type = "qualifier", },
		["qq"] = { type = "qualifier", },
		["a"] = { type = "labels", },
		["aa"] = { type = "labels", },
	}

	local args = require(parameters_module).process(frame:getParent().args, params)
	local term = args[1] or args.pagename or mw.title.getCurrentTitle().nsText == "Template" and "при́мер" or
		mw.loadData(headword_data_module).pagename

	local ipa = export.toIPA(term, args.endschwa)
	local rhymes = export.get_rhymes(ipa)
	local syllable_counts = args.s or {count_vowels(term)}
	return format_rhymes(rhymes, syllable_counts, args.q, args.qq, args.a, args.aa)
end

-- Matches patterns such as: Bg-къща.ogg<Sofia>,
-- with capture groups [1]="Bg-къща.ogg", [2]="Sofia"
local accent_qualifier_pattern = "(.-)<(.+)>"

-- Matches speaker profiles, i.e. a shorthand for representing a given
-- speaker and the properties of their audio
local speaker_profile_pattern = "%[([^:~]-)([:~]?)([^:~]*)%]"

local speaker_profiles = require("Module:bg-pronunciation/speaker profiles").speaker_profiles
local audio_functions = require("Module:bg-pronunciation/speaker profiles").audio_functions
-- Return a list of audio formatted as wikitext 
local function format_audio_list(list, ipa, pagename, corresponding_respelling)
	local request_rfap = false
	if list == nil then
		return {}, request_rfap
	end
	
	-- Audio list is separated by # symbols.
	local file_specs = rsplit(list, "#")
	local audios = {}
	for _, spec in pairs(file_specs) do
		-- Try to match speaker profile
		local speaker_profile_name, separator, term_override = rmatch(spec, speaker_profile_pattern)
		-- FIXME
		if speaker_profile_name == "" and term_override ~= "" then
			speaker_profile_name = term_override
			term_override = nil
		end
		if speaker_profile_name then
			local speaker_profile = speaker_profiles[speaker_profile_name]
			if not speaker_profile then
				error("Speaker profile named '" .. speaker_profile_name .. "' does not exist")
			end
			-- Track uses of a particular speaker profile
			require(tracking_module)("bg-pr/speaker-profile/" .. speaker_profile_name)
			-- E.g. [ABC~2] on page="човек" is equivalent to: [ABC:човек-2].
			if separator == "~" then
				term_override = mw.loadData(headword_data_module).pagename .. "-" .. term_override 
			end
			if term_override == "" then
				term_override = corresponding_respelling
			end
			local renderer_name = speaker_profile["renderer"]
			local filename = term_override or pagename
			local rendered = audio_functions[renderer_name].display(speaker_profile, filename, {ipa=ipa})
			if rendered then
				table.insert(audios, rendered)
			else
				request_rfap = true
			end
		else
			-- Attempt to match qualifiers; if none given, then the entire spec is simply interpreted as a file name.
			local filename, accents_string = rmatch(spec, accent_qualifier_pattern)
			filename = filename or spec
			local accents_list = accents_string and rsplit(accents_string, ",") or nil
			table.insert(audios,
				require(audio_module).format_audio({
					lang = lang,
					file = filename,
					a = accents_list,
				})
			)
		end
	end
	return audios, request_rfap
end

local function format_homophones(homophones_list)
	local homophones_data = {}
	for _, hmp in pairs(homophones_list) do
		table.insert(homophones_data, {
			term = hmp
		})
	end

	return require(homophones_module).format_homophones({
		lang = lang,
		homophones = homophones_data,
	})
end

-- Entry point for {{bg-pr}}
function export.show_all(frame)
	local params = {
		[1] = { list = true, disallow_holes = true},
		["q"] = { list = true, type = "qualifier", allow_holes=true },
		["qq"] = { list = true, type = "qualifier", allow_holes=true },
		["l"] = { list = true, type = "labels", allow_holes=true },
		["ll"] = { list = true, type = "labels", allow_holes=true },
		["ann"] = { list = true, allow_holes=true, separate_no_index = true },
		["audio"] = { list = true, allow_holes=true },
		["a"] = { alias_of = "audio", list = true, allow_holes=true },
		["rhymes"] = { list = true, separate_no_index = true, allow_holes=true },
		["s"] = { list = true, type = "number", separate_no_index = true, allow_holes=true },
		["syllabification"] = { list = true, separate_no_index = true, allow_holes=true },
		["syl"] = { alias_of = "syllabification", list = true, separate_no_index = true, allow_holes=true },
		["hyphenation"] = { list = true, separate_no_index = true, allow_holes=true },
		["hyph"] = { alias_of = "hyphenation", list = true, separate_no_index = true, allow_holes=true },
		["homophones"] = { list = true, allow_holes=true },
		["hs"] = { list = true, separate_no_index = true, allow_holes=true},  -- Hyphenation and syllabification override at once
		["hmp"] = { alias_of = "homophones", list = true, allow_holes=true },
		["endreduce"] = { list = true, allow_holes=true },
		["endschwa"] = { alias_of = "endreduce", list = true, allow_holes=true },
		["raw"] = { list = true, allow_holes=true },
		["pagename"] = {},
	}
	local args = require(parameters_module).process(frame:getParent().args, params)
	local pagename = args.pagename or mw.title.getCurrentTitle().nsText == "Template" and "при́мер" or
		mw.loadData(headword_data_module).pagename

	-- Ensure at least one pronunciation line present (unlikely to be desired in the case of multisyllabic words, though)
	if #args[1] == 0 then
		args[1] = {pagename}
	end

	-- Returns the hyphenation for a term if one is given by the user,
	-- or automatically generate one from the respelling if it meets the criteria:
	--[[
		- must have more than one vowel
		- the respelling given must be the same as the page title (when accents
		  are removed)
	--]]
	local syllabify = export.syllabify
	local function hyphenate(x) return export.hyphenate(export.syllabify(x)) end
	local function deal_with_hyph_syl_respellings(given, respelling, hyphenate_or_syllabify)
		if given == "+" then
			return hyphenate_or_syllabify(respelling)
		elseif given == "#" then
			return hyphenate_or_syllabify(pagename)
		elseif given then
			return rsub(given, "%.", HYPH)
		elseif count_vowels(respelling) == 1 or rsub(deaccent_all(respelling), "%.", "") ~= pagename then
			return "-"
		else
			return hyphenate_or_syllabify(respelling)
		end
	end
	
		-- Return nil if a rhyme should not be added based on the term.
	--[[ The current rules are:
		- if a manual override is given by the user, that will be the rhyme regardless
		- if no override is given and the term contains a space or hyphen, return nil
		- if there is no space and hyphen, but there is more than 1 capital letter,
		  then the term will be diagonosed as an abbreviation, and nil will be returned
		  (this logic will help to keep rhymes for names, e.g. Петър)
		- if there is more than 1 accent mark (primary or secondary combined)
		  in the IPA, then nil is returned
		- if all the above guards fail, the module will generate a rhyme
		  (despite appearances, this will actually be most words anyway)
	--]]
	local function deal_with_rhymes(override, ipa)
		if override then
			return override
		end
		if count_capital_letters(pagename) > 1 then
			return nil
		end
		if count_accents(ipa) > 1 then
			return nil
		end
		if rmatch(ipa, "[ %-]") or rmatch(pagename, "[ %-]") then
			return nil	
		end
		return export.get_rhymes(ipa)
	end

	-- Track whether an audio file has been included using a speaker profile but doesn't yet exist; automatically {{rfap}} if so.
	local request_rfap = false
	-- Build up wikitext output for each pronunciation line
	local pronunciation_lines = {}
	for i, respelling in pairs(args[1]) do
		local q = args.q[i]
		local qq = args.qq[i]
		local l = args.l[i]
		local ll = args.ll[i]
		local num_syllables = args.s.default or args.s[i] or count_vowels(respelling)
		local ipa = args.raw[i] or export.toIPA(respelling, args.endreduce[i])
		local audio, request_rfap_local = format_audio_list(args.audio.default or args.audio[i], ipa, pagename, respelling)
		local rhymes = deal_with_rhymes(args.rhymes.default or args.rhymes[i], ipa) or "-"
		local homophones = args.homophones[i] and rsplit(args.homophones[i], "#")
		local syllabification = deal_with_hyph_syl_respellings(args.hs.default or args.hs[1] or args.syllabification.default or args.syllabification[i], respelling, syllabify)
		local hyphenation = deal_with_hyph_syl_respellings(args.hs.default or args.hs[1] or args.hyphenation.default or args.hyphenation[i], respelling, hyphenate)
		local ann = args.ann.default or args.ann[i]
		table.insert(pronunciation_lines, {
			term = respelling,
			q = q,
			qq = qq,
			l = l,
			ll = ll,
			num_syllables = num_syllables,
			ipa = ipa,
			audio = audio,
			rhymes = rhymes,
			homophones = homophones,
			syllabification = syllabification,
			hyphenation = hyphenation,
			ann = ann,
		})
		if args.rhymes[i] then
			require(tracking_module)("bg-pr/manual rhyme")
			if export.get_rhymes(pronunciation_lines[i]["ipa"]) ~= args.rhymes[i] then
				-- Manual rhyme actually changes the displayed rhyme
				require(tracking_module)("bg-pr/rhyme override discrepancy")
			end
		end
		if request_rfap_local then
			request_rfap = true
		end
		-- Terms with a lack of stress despite not being monosyllabic
		if count_vowels(respelling) > 1 and count_accents(ipa) == 0 then
			require(tracking_module)("bg-pr/no stress")
		end

	end

	-- This function is used to check whether all pronunciation lines have the same value for
	-- a particular property. If they do, then that property should only be rendered once,
	-- underneath each IPA.
	-- If the properties are different, then each IPA should have that property value specified
	-- indented underneath it, e.g. multiple IPAs with different audio files should have
	-- their audios indented beneath each IPA.
	local function all_the_same(property, eq) -- `eq` allows the notion of "the_same" to be overridden
		eq = eq or function(a, b) return a == b end  -- Use "==" as default definition of equality
		local first = pronunciation_lines[1][property]
		for _, pronunciation_line in pairs(pronunciation_lines) do
			if not eq(pronunciation_line[property], first) then
				return first, false
			end
		end
		return first, true
	end

	-- Render overall output as text
	local output_lines = {}
	-- Check whether values for each property are all the same,
	-- in which case they can all be merged at the end of the template,
	-- instead of being duplicated per-pronunciation-line.
	local first_hyphenation, all_hyphenations_the_same = all_the_same("hyphenation")
	local first_syllabification, all_syllabifications_the_same = all_the_same("syllabification")
	local first_rhyme, all_rhymes_the_same = all_the_same("rhymes")
	local first_homophones, all_homophones_the_same = all_the_same("homophones", require(table_module).deepEquals)
	local first_audio, all_audio_the_same = all_the_same("audio", require(table_module).deepEquals)

	-- Generate text for each pronunciation line.
	for _, pronunciation_line in pairs(pronunciation_lines) do
		-- Unpack data
		local ipa = pronunciation_line["ipa"]
		local term = pronunciation_line["term"]
		local ann = pronunciation_line["ann"]
		local q = pronunciation_line["q"]
		local qq = pronunciation_line["qq"]
		local l = pronunciation_line["l"]
		local ll = pronunciation_line["ll"]
		local audios = pronunciation_line["audio"]
		local rhymes = pronunciation_line["rhymes"]
		local homophones = pronunciation_line["homophones"]
		local hyphenation = pronunciation_line["hyphenation"]
		local syllabification = pronunciation_line["syllabification"]
					
		local ipa_text = format_ipa(ipa, q, qq, l, ll)
		local ann_text = get_anntext(term, ann)
		local out = {"* " .. ann_text .. ipa_text}

		if audios and not all_audio_the_same then
			for _, audio in pairs(audios) do
				table.insert(out, "** " .. audio)
			end
		end

		if rhymes ~= "-" and not all_rhymes_the_same then
			local num_syllables = pronunciation_line["num_syllables"]
			local rhymes_text = format_rhymes(rhymes, {num_syllables})
			table.insert(out, "** " .. rhymes_text)
		end

		if homophones and not all_homophones_the_same then
			local homophones_text = format_homophones(homophones)
			table.insert(out, "** " .. homophones_text)
		end

		local syllabification_text = format_syllabification(syllabification)
		if syllabification == hyphenation and syllabification ~= "-" then
			if not (all_hyphenations_the_same and all_syllabifications_the_same) then
				table.insert(out, "** " .. syllabification_text)
			end
		else
			local hyphenation_text = format_hyphenation(hyphenation)
			if not all_syllabifications_the_same and syllabification ~= "-" then
				table.insert(out, "** " .. syllabification_text)
			end
			if not all_hyphenations_the_same and hyphenation ~= "-" then
				table.insert(out, "** " .. hyphenation_text)
			end
		end
		-- Produce output for one pronunciation line at a time
		table.insert(output_lines, table.concat(out, "\n"))
	end

	-- Group audios all together if they are all identical
	if all_audio_the_same and first_audio then
		for _, audio in pairs(first_audio) do
			table.insert(output_lines, "* " .. audio)
		end
	end

	-- Group rhymes all together if they are all identical				
	if all_rhymes_the_same and first_rhyme ~= "-" then
		-- Take all syllable counts for all pronunciation lines
		-- to inform the syllable count of the rhyme.
		local num_syllables = {}

		for _, line in pairs(pronunciation_lines) do
			table.insert(num_syllables, line["num_syllables"])
		end

		local rhymes_text = format_rhymes(first_rhyme, num_syllables)
		table.insert(output_lines, "* " .. rhymes_text)
	end

	-- Group homophones all together if they are all identical
	if all_homophones_the_same and first_homophones then
		local homophones_text = format_homophones(first_homophones)
		table.insert(output_lines, "* " .. homophones_text)
	end
	
	-- If some hyphenations or syllabifications are absent, then the outermost text should not include these,
	-- rather they should have been appended to the relevant pronunciations lines above.
	if all_hyphenations_the_same and all_syllabifications_the_same and first_hyphenation == first_syllabification and first_hyphenation ~= "-" then
		local syllabification_text = format_syllabification(first_hyphenation)
		table.insert(output_lines, "* " .. syllabification_text)
	elseif first_hyphenation ~= first_syllabification then
		if all_syllabifications_the_same and first_syllabification ~= "-" then
			local syllabification_text = format_syllabification(first_syllabification)
			table.insert(output_lines, "* " .. syllabification_text)
		end
		if all_hyphenations_the_same and first_hyphenation ~= "-" then
			local hyphenation_text = format_hyphenation(first_hyphenation)
			table.insert(output_lines, "* " .. hyphenation_text)
		end
	end
	if request_rfap then
		table.insert(output_lines, frame:preprocess("{{rfap|bg}}"))
	end
	return table.concat(output_lines, "\n")
end

return export
"https://si.wiktionary.org/w/index.php?title=Module:bg-pronunciation&oldid=234873" වෙතින් සම්ප්‍රවේශනය කෙරිණි