Module:bg-pronunciation
පෙනුම
- පහත දැක්වෙන උපදෙස්, Module:bg-pronunciation/documentation හි පිහිටා ඇත. Module:bg-pronunciation/documentation]]. [සංස්කරණය] Categories were auto-generated by Module:module categorization. [edit]
- ප්රයෝජනවත් සබැඳි: උප පිටු ලැයිස්තුව • සබැඳි • transclusions • testcases • sandbox
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.
Testcases
[සංස්කරණය]All tests passed. (refresh)
| Text | Expected | Actual | |
|---|---|---|---|
| височина́ (visočiná) | ви‧со‧чи‧на | ви‧со‧чи‧на | |
| сестра́ (sestrá) | сес‧тра | сес‧тра | |
| пле́нник (plénnik) | плен‧ник | плен‧ник | |
| преодоле́я (preodoléja) | пре‧одо‧лея | пре‧одо‧лея | |
| маои́зъм (maoízǎm) | мао‧изъм | мао‧изъм | |
| ма́йка (májka) | май‧ка | май‧ка | |
| а́йс.берг (ájs.berg) | айс‧берг | айс‧берг | |
| майо́р (majór) | ма‧йор | ма‧йор | |
| фризьо́р (frizjór) | фри‧зьор | фри‧зьор | |
| суджу́к (sudžúk) | су‧джук | су‧джук | |
| над.живе́я (nad.živéja) | над‧жи‧вея | над‧жи‧вея | |
| сестра́ (sestrá) | сес‧тра | сес‧тра | |
| поту́ри (potúri) | по‧ту‧ри | по‧ту‧ри | |
| слани́на (slanína) | сла‧ни‧на | сла‧ни‧на | |
| пра́жа (práža) | пра‧жа | пра‧жа | |
| спринцо́вка (sprincóvka) | сприн‧цов‧ка | сприн‧цов‧ка | |
| пъ́ржа (pǎ́rža) | пър‧жа | пър‧жа | |
| я́ркост (járkost) | яр‧кост | яр‧кост | |
| рало (ralo) | ра‧ло | ра‧ло | |
| бели́ло (belílo) | бе‧ли‧ло | бе‧ли‧ло | |
| шеви́ца (ševíca) | ше‧ви‧ца | ше‧ви‧ца | |
| дои́ло (doílo) | до‧ило | до‧ило | |
| нача́ло (načálo) | на‧ча‧ло | на‧ча‧ло | |
| хи́трост (hítrost) | хит‧рост | хит‧рост | |
| хи́тър (hítǎr) | хи‧тър | хи‧тър | |
| шеви́ца (ševíca) | ше‧ви‧ца | ше‧ви‧ца | |
| вдлъ́бна (vdlǎ́bna) | вдлъб‧на | вдлъб‧на | |
| разма́хам (razmáham) | раз‧ма‧хам | раз‧ма‧хам | |
| у́кор (úkor) | укор | укор | |
| упори́т (uporít) | упо‧рит | упо‧рит | |
| о́сем (ósem) | осем | осем | |
| оце́нка (océnka) | оцен‧ка | оцен‧ка | |
| ле́я (léja) | лея | лея | |
| а́з (áz) | аз | аз | |
| тя́ (tjá) | тя | тя | |
| е́ (é) | е | е | |
| ме́н (mén) | мен | мен | |
| стра́ст (strást) | страст | страст | |
| пръст (prǎst) | пръст | пръст | |
| шофьо́р (šofjór) | шо‧фьор | шо‧фьор | |
| фотьо́йл (fotjójl) | фо‧тьойл | фо‧тьойл | |
| бельо́ (beljó) | бе‧льо | бе‧льо | |
| шедьо́вър (šedjóvǎr) | ше‧дьо‧вър | ше‧дьо‧вър | |
| ме́ниджър (ménidžǎr) | ме‧ни‧джър | ме‧ни‧джър | |
| джудже́ (džudžé) | джу‧дже | джу‧дже | |
| жа́р-пти́ца (žár-ptíca) | жар-пти‧ца | жар-пти‧ца | |
| мора́лно-нра́вствен (morálno-nrávstven) | мо‧рал‧но-нрав‧ствен | мо‧рал‧но-нрав‧ствен | |
| кандида́т-студе́нт (kandidát-studént) | кан‧ди‧дат-сту‧дент | кан‧ди‧дат-сту‧дент | |
| мини́стър-председа́тел (minístǎr-predsedátel) | ми‧нис‧тър-пред‧се‧да‧тел | ми‧нис‧тър-пред‧се‧да‧тел | |
| член-кореспонде́нт (člen-korespondént) | член-ко‧рес‧пон‧дент | член-ко‧рес‧пон‧дент | |
| би́знес администра́ция (bíznes administrácija) | биз‧нес ад‧ми‧нис‧тра‧ция | биз‧нес ад‧ми‧нис‧тра‧ция | |
| е́кшън геро́й (ékšǎn gerój) | ек‧шън ге‧рой | ек‧шън ге‧рой | |
| те́нис ко́рт (ténis kórt) | те‧нис корт | те‧нис корт | |
| заме́стник мини́стър-председа́тел (zaméstnik minístǎr-predsedátel) | за‧мес‧тник ми‧нис‧тър-пред‧се‧да‧тел | за‧мес‧тник ми‧нис‧тър-пред‧се‧да‧тел | |
| заме́стник нача́лник-управле́ние (zaméstnik načálnik-upravlénie) | за‧мес‧тник на‧чал‧ник-уп‧рав‧ле‧ние | за‧мес‧тник на‧чал‧ник-уп‧рав‧ле‧ние | |
| SIM ка́рта (SIM kárta) | SIM кар‧та | SIM кар‧та | |
| VIP зо́на (VIP zóna) | VIP зо‧на | VIP зо‧на |
| Text | Expected | Actual | |
|---|---|---|---|
| къ́ща (kǎ́šta) | ˈkɤʃtɐ | ˈkɤʃtɐ | |
| сгъстя́ се (sgǎstjá se), endschwa=true | zɡɐˈstʲɤ̟ sɛ | zɡɐˈstʲɤ̟ sɛ | |
| сгъстя́ се (sgǎstjá se) (respelled сгъстя̣́ се) | zɡɐˈstʲɤ̟ sɛ | zɡɐˈstʲɤ̟ sɛ | |
| а̀бдики́ращ (àbdikírašt) | ˌabdiˈkirɐʃt | ˌabdiˈkirɐʃt | |
| безшу́мен (bezšúmen) | bɛʃˈʃu̟mɛn | bɛʃˈʃu̟mɛn | |
| щастли́в (štastlív) | ʃtɐˈslif | ʃtɐˈslif | |
| народността́ (narodnosttá) | nɐrodnoˈsta | nɐrodnoˈsta | |
| я (ja) | ja̟ | ja̟ | |
| юг (jug) | ju̟k | ju̟k | |
| яйце́ (jajcé) | jɐjˈt͡sɛ | jɐjˈt͡sɛ | |
| изя́м (izjám) | iˈzʲa̟m | iˈzʲa̟m | |
| учи́лище (učílište) | oˈt͡ʃiliʃtɛ | oˈt͡ʃiliʃtɛ | |
| чорбаджи́я (čorbadžíja) | t͡ʃo̟rbɐˈdʒijɐ | t͡ʃo̟rbɐˈdʒijɐ | |
| уби́йца (ubíjca) | oˈbijt͡sɐ | oˈbijt͡sɐ | |
| безбра́чие (bezbráčie) | bɛzˈbrat͡ʃiɛ | bɛzˈbrat͡ʃiɛ | |
| измра́ (izmrá) (respelled из.мра́) | izˈmra | izˈmra | |
| сала́та (saláta) | sɐˈɫatɐ | sɐˈɫatɐ | |
| шега́ (šegá) | ʃɛˈɡa | ʃɛˈɡa | |
| жена́ (žená) | ʒɛˈna | ʒɛˈna | |
| инти́мен (intímen) | inˈtimɛn | inˈtimɛn | |
| посо́лство (posólstvo) | poˈsɔɫstvo | poˈsɔɫstvo | |
| ъ́гъл (ǎ́gǎl) | ˈɤɡɐɫ | ˈɤɡɐɫ | |
| усу́квам (usúkvam) | oˈsukvɐm | oˈsukvɐm | |
| ле́ща (léšta) | ˈlɛʃtɐ | ˈlɛʃtɐ | |
| липа́ (lipá) | liˈpa | liˈpa | |
| океа́н (okeán) | okɛˈan | okɛˈan | |
| меки́ца (mekíca) | mɛˈkit͡sɐ | mɛˈkit͡sɐ | |
| ла́гер (láger) | ˈɫaɡɛr | ˈɫaɡɛr | |
| маги́я (magíja) | mɐˈɡijɐ | mɐˈɡijɐ | |
| хем (hem) | xɛm | xɛm | |
| химн (himn) | ximn | ximn | |
| тулу́п (tulúp) | toˈɫup | toˈɫup | |
| жа̀р-пти́ца (žàr-ptíca) | ˌʒa̟r-pˈtit͡sɐ | ˌʒa̟r-pˈtit͡sɐ | |
| в о́фис (v ófis) | f ˈɔfis | f ˈɔfis | |
| във Фра́нция (vǎv Fráncija) | vɐf ˈfrant͡sijɐ | vɐf ˈfrant͡sijɐ | |
| ня́колко (njákolko) | ˈnʲa̟koɫko | ˈnʲa̟koɫko | |
| в Япо́ния (v Japónija) | f jɐˈpɔnijɐ | f jɐˈpɔnijɐ | |
| автоплу́г (avtoplúg) | ɐftoˈpɫuk | ɐftoˈpɫuk | |
| уе́бса́йт (uébsájt) (respelled ўе́бса́йт) | ˈwɛpˈsajt | ˈwɛpˈsajt | |
| уе́лски (uélski) (respelled ўе́лски) | ˈwɛɫski | ˈwɛɫski | |
| уе́стърн (uéstǎrn) (respelled ўе́стърн) | ˈwɛstɐrn | ˈwɛstɐrn | |
| О́уен (Óuen) (respelled О́ўен) | ˈɔwɛn | ˈɔwɛn | |
| но́ухау (nóuhau) (respelled но́ўхаў) | ˈnɔwxɐw | ˈnɔwxɐw | |
| Джо́узеф (Džóuzef) (respelled Джо́ўзеф) | ˈdʒɔwzɛf | ˈdʒɔwzɛf | |
| бо́улинг (bóuling) (respelled бо́ўлинг) | ˈbɔwliŋk | ˈbɔwliŋk | |
| даунло́уд (daunlóud) (respelled даўнло́ўд) | dɐwnˈɫɔwt | dɐwnˈɫɔwt | |
| уи́ски (uíski) (respelled ўи́ски) | ˈwiski | ˈwiski | |
| уи́кенд (uíkend) (respelled ўи́кенд) | ˈwikɛnt | ˈwikɛnt | |
| Уо́руик (Uóruik) (respelled Ўо́рўик) | ˈwɔrwik | ˈwɔrwik | |
| Хе́лоуин (Hélouin) (respelled Хе́лоўин) | ˈxɛɫowin | ˈxɛɫowin |
| Text | Expected | Actual | |
|---|---|---|---|
| а (a) | а | а | |
| в (v) | в | в | |
| е (e) | е | е | |
| и (i) | и | и | |
| ѝ (ì) | ѝ | ѝ | |
| о (o) | о | о | |
| с (s) | с | с | |
| у (u) | у | у | |
| аз (az) | аз | аз | |
| ти (ti) | ти | ти | |
| той (toj) | той | той | |
| тя (tja) | тя | тя | |
| във (vǎv) | във | във | |
| със (sǎs) | със | със | |
| принц (princ) | принц | принц | |
| спринт (sprint) | спринт | спринт | |
| глист (glist) | глист | глист | |
| скункс (skunks) | скункс | скункс | |
| ами́ (amí) | а‧ми | а‧ми | |
| ала́ (alá) | а‧ла | а‧ла | |
| ако́ (akó) | а‧ко | а‧ко | |
| уви́ (uví) | у‧ви | у‧ви | |
| или́ (ilí) | и‧ли | и‧ли | |
| саламу́ра (salamúra) | са‧ла‧му‧ра | са‧ла‧му‧ра | |
| бараба́н (barabán) | ба‧ра‧бан | ба‧ра‧бан | |
| сполу́ка (spolúka) | спо‧лу‧ка | спо‧лу‧ка | |
| ща́вя (štávja) | ща‧вя | ща‧вя | |
| стри́на (strína) | стри‧на | стри‧на | |
| кога́то (kogáto) | ко‧га‧то | ко‧га‧то | |
| изя́м (izjám) | и‧зям | и‧зям | |
| стари́цата (starícata) | ста‧ри‧ца‧та | ста‧ри‧ца‧та | |
| полу́чените (polúčenite) | по‧лу‧че‧ни‧те | по‧лу‧че‧ни‧те | |
| подоба́ващите (podobávaštite) | по‧до‧ба‧ва‧щи‧те | по‧до‧ба‧ва‧щи‧те | |
| обуча́ващите (obučávaštite) | о‧бу‧ча‧ва‧щи‧те | о‧бу‧ча‧ва‧щи‧те | |
| джудже́ (džudžé) | джу‧дже | джу‧дже | |
| суджу́к (sudžúk) | су‧джук | су‧джук | |
| дамаджа́на (damadžána) | да‧ма‧джа‧на | да‧ма‧джа‧на | |
| джа́джите (džádžite) | джа‧джи‧те | джа‧джи‧те | |
| койо́т (kojót) | ко‧йот | ко‧йот | |
| майоне́за (majonéza) | ма‧йо‧не‧за | ма‧йо‧не‧за | |
| пейорати́вен (pejoratíven) | пе‧йо‧ра‧ти‧вен | пе‧йо‧ра‧ти‧вен | |
| майо́р (majór) | ма‧йор | ма‧йор | |
| бези́менен (bezímenen) | бе‧зи‧ме‧нен | бе‧зи‧ме‧нен | |
| изопача́вам (izopačávam) | и‧зо‧па‧ча‧вам | и‧зо‧па‧ча‧вам | |
| оти́вам (otívam) | о‧ти‧вам | о‧ти‧вам | |
| разора́н (razorán) | ра‧зо‧ран | ра‧зо‧ран | |
| бульо́н (buljón) | бу‧льон | бу‧льон | |
| фризьо́р (frizjór) | фри‧зьор | фри‧зьор | |
| шедьо́вър (šedjóvǎr) | ше‧дьо‧вър | ше‧дьо‧вър | |
| гьозу́м (gjozúm) | гьо‧зум | гьо‧зум | |
| ликьо́р (likjór) | ли‧кьор | ли‧кьор | |
| воа́л (voál) | во‧ал | во‧ал | |
| маои́зъм (maoízǎm) | ма‧о‧и‧зъм | ма‧о‧и‧зъм | |
| фее́рия (feérija) | фе‧е‧ри‧я | фе‧е‧ри‧я | |
| воайо́р (voajór) | во‧а‧йор | во‧а‧йор | |
| миока́рд (miokárd) | ми‧о‧кард | ми‧о‧кард | |
| кьо́поолу (kjópoolu) | кьо‧по‧о‧лу | кьо‧по‧о‧лу | |
| аятола́х (ajatoláh) | а‧я‧то‧лах | а‧я‧то‧лах | |
| ава́рия (avárija) | а‧ва‧ри‧я | а‧ва‧ри‧я | |
| пози́ции (pozícii) | по‧зи‧ци‧и | по‧зи‧ци‧и | |
| хазя́и (hazjái) | ха‧зя‧и | ха‧зя‧и | |
| дерибе́и (deribéi) | де‧ри‧бе‧и | де‧ри‧бе‧и | |
| преодоле́я (preodoléja) | пре‧о‧до‧ле‧я | пре‧о‧до‧ле‧я | |
| нащре́к (naštrék) | на‧щрек | на‧щрек | |
| поощря́вам (pooštrjávam) | по‧о‧щря‧вам | по‧о‧щря‧вам | |
| защрихо́вам (zaštrihóvam) | за‧щри‧хо‧вам | за‧щри‧хо‧вам | |
| поощри́телен (pooštrítelen) | по‧о‧щри‧те‧лен | по‧о‧щри‧те‧лен | |
| изщра́кване (izštrákvane) | из‧щрак‧ва‧не | из‧щрак‧ва‧не | |
| Ва́йерщра̀с (Vájerštràs) | Ва‧йер‧щрас | Ва‧йер‧щрас | |
| Кьо́нигщра̀се (Kjónigštràse) | Кьо‧ниг‧щра‧се | Кьо‧ниг‧щра‧се | |
| о́бщност (óbštnost) | общ‧ност | общ‧ност | |
| всъ́щност (vsǎ́štnost) | всъщ‧ност | всъщ‧ност | |
| помо́щник (pomóštnik) | по‧мощ‧ник | по‧мощ‧ник | |
| чорапога́щник (čorapogáštnik) | чо‧ра‧по‧гащ‧ник | чо‧ра‧по‧гащ‧ник | |
| но́щница (nóštnica) | нощ‧ни‧ца | нощ‧ни‧ца | |
| чудо́вищност (čudóvištnost) | чу‧до‧вищ‧ност | чу‧до‧вищ‧ност | |
| немощливо (nemoštlivo) | не‧мощ‧ли‧во | не‧мощ‧ли‧во | |
| съобща́вам (sǎobštávam) | съ‧об‧ща‧вам | съ‧об‧ща‧вам | |
| въобще́ (vǎobšté) | въ‧об‧ще | въ‧об‧ще | |
| ма́нджа (mándža) | ман‧джа | ман‧джа | |
| калайджи́я (kalajdžíja) | ка‧лай‧джи‧я | ка‧лай‧джи‧я | |
| авджи́я (avdžíja) | ав‧джи‧я | ав‧джи‧я | |
| изджва́ка (izdžváka) | из‧джва‧ка | из‧джва‧ка | |
| пле́нник (plénnik) | плен‧ник | плен‧ник | |
| ма́йка (májka) | май‧ка | май‧ка | |
| про̀фаши́стки (pròfašístki) | про‧фа‧шист‧ки | про‧фа‧шист‧ки | |
| гле́дка (glédka) | глед‧ка | глед‧ка | |
| кра́чка (kráčka) | крач‧ка | крач‧ка | |
| це́дка (cédka) | цед‧ка | цед‧ка | |
| звезда́ (zvezdá) | звез‧да | звез‧да | |
| спринцо́вка (sprincóvka) | сприн‧цов‧ка | сприн‧цов‧ка | |
| бъ́рзо (bǎ́rzo) | бър‧зо | бър‧зо | |
| ма́лко (málko) | мал‧ко | мал‧ко | |
| по́сле (pósle) | по‧сле | по‧сле | |
| па́ртия (pártija) | пар‧ти‧я | пар‧ти‧я | |
| гла́нцов (gláncov) | глан‧цов | глан‧цов | |
| пе́пелник (pépelnik) | пе‧пел‧ник | пе‧пел‧ник | |
| пи́лци (pílci) | пил‧ци | пил‧ци | |
| аншоа́ (anšoá) | ан‧шо‧а | ан‧шо‧а | |
| ядро́ (jadró) | я‧дро | я‧дро | |
| ирони́чност (ironíčnost) | и‧ро‧нич‧ност | и‧ро‧нич‧ност | |
| профилакти́чна (profilaktíčna) | про‧фи‧лак‧тич‧на | про‧фи‧лак‧тич‧на | |
| бо́цна (bócna) | боц‧на | боц‧на | |
| спе́цна (spécna) | спец‧на | спец‧на | |
| бичме́ (bičmé) | бич‧ме | бич‧ме | |
| кръ́чма (krǎ́čma) | кръч‧ма | кръч‧ма | |
| бо́цман (bócman) | боц‧ман | боц‧ман | |
| сачма́ (sačmá) | сач‧ма | сач‧ма | |
| Ри́чмънд (Ríčmǎnd) | Рич‧мънд | Рич‧мънд | |
| ми́чман (míčman) | мич‧ман | мич‧ман | |
| разче́ша (razčéša) | раз‧че‧ша | раз‧че‧ша | |
| пе́цма (pécma) | пец‧ма | пец‧ма | |
| сестра́ (sestrá) | се‧стра | се‧стра | |
| ца́рство (cárstvo) | цар‧ство | цар‧ство | |
| нра́вствен (nrávstven) | нрав‧ствен | нрав‧ствен | |
| ма́ндраджия (mándradžija) | ман‧дра‧джи‧я | ман‧дра‧джи‧я | |
| мизансце́н (mizanscén) | ми‧зан‧сцен | ми‧зан‧сцен | |
| стра́нство (stránstvo) | стран‧ство | стран‧ство | |
| простра́нство (prostránstvo) | про‧стран‧ство | про‧стран‧ство | |
| ро́бство (róbstvo) | роб‧ство | роб‧ство | |
| транспо́рт (transpórt) | тран‧спорт | тран‧спорт | |
| посви́кна (posvíkna) | по‧свик‧на | по‧свик‧на | |
| скръ́ндза (skrǎ́ndza) | скрън‧дза | скрън‧дза | |
| годзи́ла (godzíla) | год‧зи‧ла | год‧зи‧ла | |
| камика́дзе (kamikádze) | ка‧ми‧кад‧зе | ка‧ми‧кад‧зе | |
| надживе́я (nadživéja) | на‧джи‧ве‧я | на‧джи‧ве‧я | |
| скръ́н.дза (skrǎ́n.dza) | скрън‧дза | скрън‧дза | |
| го.дзи́ла (go.dzíla) | го‧дзи‧ла | го‧дзи‧ла | |
| камика́.дзе (kamiká.dze) | ка‧ми‧ка‧дзе | ка‧ми‧ка‧дзе | |
| над.живе́я (nad.živéja) | над‧жи‧ве‧я | над‧жи‧ве‧я | |
| безси́лен (bezsílen) | без‧си‧лен | без‧си‧лен | |
| безшу́мен (bezšúmen) | без‧шу‧мен | без‧шу‧мен | |
| безвъзвра́тен (bezvǎzvráten) | без‧въз‧вра‧тен | без‧въз‧вра‧тен | |
| безхабе́рен (bezhabéren) | без‧ха‧бе‧рен | без‧ха‧бе‧рен | |
| безстра́шен (bezstrášen) | без‧стра‧шен | без‧стра‧шен | |
| безхле́бна (bezhlébna) | без‧хле‧бна | без‧хле‧бна | |
| безвре́мие (bezvrémie) | без‧вре‧ми‧е | без‧вре‧ми‧е | |
| безме́рен (bezméren) | без‧ме‧рен | без‧ме‧рен | |
| безли́чен (bezlíčen) | без‧ли‧чен | без‧ли‧чен | |
| безнака́зан (beznakázan) | без‧на‧ка‧зан | без‧на‧ка‧зан | |
| безразбо́рен (bezrazbóren) | без‧раз‧бо‧рен | без‧раз‧бо‧рен | |
| безде́тен (bezdéten) | без‧де‧тен | без‧де‧тен | |
| безпардо́нен (bezpardónen) | без‧пар‧до‧нен | без‧пар‧до‧нен | |
| безтеле́сен (beztelésen) | без‧те‧ле‧сен | без‧те‧ле‧сен | |
| безгла́в (bezgláv) | без‧глав | без‧глав | |
| безче́стен (bezčésten) | без‧че‧стен | без‧че‧стен | |
| безпризо́рен (bezprizóren) | без‧при‧зо‧рен | без‧при‧зо‧рен | |
| безгре́шен (bezgréšen) | без‧гре‧шен | без‧гре‧шен | |
| безкра́ен (bezkráen) | без‧кра‧ен | без‧кра‧ен | |
| безбре́жен (bezbréžen) | без‧бре‧жен | без‧бре‧жен | |
| бе́здна (bézdna) | безд‧на | безд‧на | |
| изхвъ́рлям (izhvǎ́rljam) | из‧хвър‧лям | из‧хвър‧лям | |
| изсте́на (izsténa) | из‧сте‧на | из‧сте‧на | |
| и́звор (ízvor) | из‧вор | из‧вор | |
| изво́звам (izvózvam) | из‧воз‧вам | из‧воз‧вам | |
| извла́ча (izvláča) | из‧вла‧ча | из‧вла‧ча | |
| изхра́чване (izhráčvane) | из‧храч‧ва‧не | из‧храч‧ва‧не | |
| изшму́гна (izšmúgna) | из‧шмуг‧на | из‧шмуг‧на | |
| изживя́ното (izživjánoto) | из‧жи‧вя‧но‧то | из‧жи‧вя‧но‧то | |
| изнена́да (iznenáda) | из‧не‧на‧да | из‧не‧на‧да | |
| излъ́гах (izlǎ́gah) | из‧лъ‧гах | из‧лъ‧гах | |
| измя́на (izmjána) | из‧мя‧на | из‧мя‧на | |
| и́зрод (ízrod) | из‧род | из‧род | |
| изтрезви́телно (iztrezvítelno) | из‧трез‧ви‧тел‧но | из‧трез‧ви‧тел‧но | |
| изпростя́вял (izprostjávjal) | из‧про‧стя‧вял | из‧про‧стя‧вял | |
| изклю́чвам (izkljúčvam) | из‧ключ‧вам | из‧ключ‧вам | |
| избли́за (izblíza) | из‧бли‧за | из‧бли‧за | |
| надсло́в (nadslóv) | над‧слов | над‧слов | |
| надхвъ́рлен (nadhvǎ́rlen) | над‧хвър‧лен | над‧хвър‧лен | |
| надви́квам (nadvíkvam) | над‧вик‧вам | над‧вик‧вам | |
| надве́ (nadvé) | над‧ве | над‧ве | |
| надгро́бен (nadgróben) | над‧гро‧бен | над‧гро‧бен | |
| на́дпис (nádpis) | над‧пис | над‧пис | |
| надценя́вам (nadcenjávam) | над‧це‧ня‧вам | над‧це‧ня‧вам | |
| надделе́я (naddeléja) | над‧де‧ле‧я | над‧де‧ле‧я | |
| над.ра́ствам (nad.rástvam) | над‧ра‧ствам | над‧ра‧ствам | |
| надмо́щие (nadmóštie) | над‧мо‧щи‧е | над‧мо‧щи‧е | |
| ненадми́нат (nenadmínat) | не‧над‧ми‧нат | не‧над‧ми‧нат | |
| безнадзо́рен (beznadzóren) | без‧над‧зо‧рен | без‧над‧зо‧рен | |
| на́дница (nádnica) | над‧ни‧ца | над‧ни‧ца | |
| надме́нност (nadménnost) | над‧мен‧ност | над‧мен‧ност | |
| на.длъ́ж (na.dlǎ́ž) | на‧длъж | на‧длъж | |
| надробе́н (nadrobén) | на‧дро‧бен | на‧дро‧бен | |
| надръ́нкам (nadrǎ́nkam) | на‧дрън‧кам | на‧дрън‧кам | |
| надра́скам (nadráskam) | на‧дра‧скам | на‧дра‧скам | |
| надру́сам (nadrúsam) | на‧дру‧сам | на‧дру‧сам | |
| надра́н (nadrán) | на‧дран | на‧дран | |
| подстрека́тел (podstrekátel) | под‧стре‧ка‧тел | под‧стре‧ка‧тел | |
| подхо́д (podhód) | под‧ход | под‧ход | |
| подви́жен (podvížen) | под‧ви‧жен | под‧ви‧жен | |
| подзагла́вие (podzaglávie) | под‧за‧гла‧ви‧е | под‧за‧гла‧ви‧е | |
| подкла́ждам (podkláždam) | под‧клаж‧дам | под‧клаж‧дам | |
| подбо́р (podbór) | под‧бор | под‧бор | |
| подпи́рам (podpíram) | под‧пи‧рам | под‧пи‧рам | |
| подценя́вам (podcenjávam) | под‧це‧ня‧вам | под‧це‧ня‧вам | |
| подновя́вам (podnovjávam) | под‧но‧вя‧вам | под‧но‧вя‧вам | |
| подма́мвам (podmámvam) | под‧мам‧вам | под‧мам‧вам | |
| по́длост (pódlost) | под‧лост | под‧лост | |
| под.разделе́ние (pod.razdelénie) | под‧раз‧де‧ле‧ни‧е | под‧раз‧де‧ле‧ни‧е | |
| подро́бен (podróben) | по‧дро‧бен | по‧дро‧бен | |
| подража́вам (podražávam) | по‧дра‧жа‧вам | по‧дра‧жа‧вам | |
| подре́мя (podrémja) | по‧дре‧мя | по‧дре‧мя | |
| подру́сам (podrúsam) | по‧дру‧сам | по‧дру‧сам | |
| безизра́зен (bezizrázen) | бе‧зиз‧ра‧зен | бе‧зиз‧ра‧зен | |
| безизра́зност (bezizráznost) | бе‧зиз‧ра‧зност | бе‧зиз‧ра‧зност | |
| безвъзме́зден (bezvǎzmézden) | без‧въз‧мез‧ден | без‧въз‧мез‧ден | |
| безвъзду́шен (bezvǎzdúšen) | без‧въз‧ду‧шен | без‧въз‧ду‧шен | |
| безразли́чен (bezrazlíčen) | без‧раз‧ли‧чен | без‧раз‧ли‧чен | |
| безразбо́рност (bezrazbórnost) | без‧раз‧бор‧ност | без‧раз‧бор‧ност | |
| безпредме́тен (bezpredméten) | без‧пред‧ме‧тен | без‧пред‧ме‧тен | |
| поизпра́вя (poizprávja) | по‧из‧пра‧вя | по‧из‧пра‧вя | |
| поизмъ́ча (poizmǎ́ča) | по‧из‧мъ‧ча | по‧из‧мъ‧ча | |
| поизгла́дя (poizgládja) | по‧из‧гла‧дя | по‧из‧гла‧дя | |
| произноше́ние (proiznošénie) | про‧из‧но‧ше‧ни‧е | про‧из‧но‧ше‧ни‧е | |
| произти́ча (proiztíča) | про‧из‧ти‧ча | про‧из‧ти‧ча | |
| наизми́слил (naizmíslil) | на‧из‧ми‧слил | на‧из‧ми‧слил | |
| наизле́злите (naizlézlite) | на‧из‧ле‧зли‧те | на‧из‧ле‧зли‧те | |
| предразположе́ние (predrazpoložénie) | пред‧раз‧по‧ло‧же‧ни‧е | пред‧раз‧по‧ло‧же‧ни‧е | |
| преразгле́ждане (prerazgléždane) | пре‧раз‧глеж‧да‧не | пре‧раз‧глеж‧да‧не | |
| преразпределе́ние (prerazpredelénie) | пре‧раз‧пре‧де‧ле‧ни‧е | пре‧раз‧пре‧де‧ле‧ни‧е | |
| прера́зказ (prerázkaz) | пре‧раз‧каз | пре‧раз‧каз | |
| превъзмо́гна (prevǎzmógna) | пре‧въз‧мог‧на | пре‧въз‧мог‧на | |
| превъзпита́ние (prevǎzpitánie) | пре‧въз‧пи‧та‧ни‧е | пре‧въз‧пи‧та‧ни‧е | |
| преизда́вам (preizdávam) | пре‧из‧да‧вам | пре‧из‧да‧вам | |
| преизби́рам (preizbíram) | пре‧из‧би‧рам | пре‧из‧би‧рам | |
| невъзмо́жен (nevǎzmóžen) | не‧въз‧мо‧жен | не‧въз‧мо‧жен | |
| невъзпи́тан (nevǎzpítan) | не‧въз‧пи‧тан | не‧въз‧пи‧тан | |
| неизбе́жен (neizbéžen) | не‧из‧бе‧жен | не‧из‧бе‧жен | |
| неизме́нност (neizménnost) | не‧из‧мен‧ност | не‧из‧мен‧ност | |
| неразде́лен (nerazdélen) | не‧раз‧де‧лен | не‧раз‧де‧лен | |
| неразположе́ние (nerazpoložénie) | не‧раз‧по‧ло‧же‧ни‧е | не‧раз‧по‧ло‧же‧ни‧е | |
| поразми́сля (porazmíslja) | по‧раз‧ми‧сля | по‧раз‧ми‧сля | |
| пораздру́сам (porazdrúsam) | по‧раз‧дру‧сам | по‧раз‧дру‧сам | |
| наразка́зах (narazkázah) | на‧раз‧ка‧зах | на‧раз‧ка‧зах | |
| наразлепи́л (narazlepíl) | на‧раз‧ле‧пил | на‧раз‧ле‧пил | |
| неотло́жен (neotlóžen) | не‧от‧ло‧жен | не‧от‧ло‧жен | |
| неотмени́м (neotmením) | не‧от‧ме‧ним | не‧от‧ме‧ним | |
| поотло́жа (pootlóža) | по‧от‧ло‧жа | по‧от‧ло‧жа | |
| поотми́на (pootmína) | по‧от‧ми‧на | по‧от‧ми‧на | |
| уе́лски (uélski) | у‧ел‧ски | у‧ел‧ски | |
| уе́бсайт (uébsajt) | у‧еб‧сайт | у‧еб‧сайт | |
| уе́стърн (uéstǎrn) | у‧е‧стърн | у‧е‧стърн | |
| О́уен (Óuen) | О‧у‧ен | О‧у‧ен | |
| но́уха̀у (nóuhàu) | но‧у‧ха‧у | но‧у‧ха‧у | |
| Джо́узеф (Džóuzef) | Джо‧у‧зеф | Джо‧у‧зеф | |
| бо́улинг (bóuling) | бо‧у‧линг | бо‧у‧линг | |
| да̀унло́уд (dàunlóud) | да‧ун‧ло‧уд | да‧ун‧ло‧уд | |
| уи́ски (uíski) | у‧и‧ски | у‧и‧ски | |
| уи́кенд (uíkend) | у‧и‧кенд | у‧и‧кенд | |
| Уо́руик (Uóruik) | У‧о‧ру‧ик | У‧о‧ру‧ик | |
| Хе́лоуин (Hélouin) | Хе‧ло‧у‧ин | Хе‧ло‧у‧ин | |
| ўе́лски | уел‧ски | уел‧ски | |
| ўе́бсайт | уеб‧сайт | уеб‧сайт | |
| ўе́стърн | уе‧стърн | уе‧стърн | |
| О́ўен | О‧уен | О‧уен | |
| но́ўха̀ў | ноу‧хау | ноу‧хау | |
| Джо́ўзеф | Джоу‧зеф | Джоу‧зеф | |
| бо́ўлинг | боу‧линг | боу‧линг | |
| да̀ўн.ло́ўд | даун‧лоуд | даун‧лоуд | |
| ўи́ски | уи‧ски | уи‧ски | |
| ўи́кенд | уи‧кенд | уи‧кенд | |
| Ўо́рўик | Уор‧уик | Уор‧уик | |
| Хе́лоўин | Хе‧ло‧уин | Хе‧ло‧уин | |
| ўи́нд.сърфинг | уинд‧сър‧финг | уинд‧сър‧финг | |
| ра́зни хо́ра-ра́зни вку́сове (rázni hóra-rázni vkúsove) | раз‧ни хо‧ра-раз‧ни вку‧со‧ве | раз‧ни хо‧ра-раз‧ни вку‧со‧ве | |
| акуше́р-гинеколо́г (akušér-ginekológ) | а‧ку‧шер-ги‧не‧ко‧лог | а‧ку‧шер-ги‧не‧ко‧лог | |
| на́й-напре́д (náj-napréd) | най-на‧пред | най-на‧пред | |
| ампер-ча́с (amper-čás) | ам‧пер-час | ам‧пер-час | |
| га́-га́ (gá-gá) | га-га | га-га | |
| пи́-пи́ (pí-pí) | пи-пи | пи-пи | |
| Гвине́я-Биса́у (Gvinéja-Bisáu) | Гви‧не‧я-Би‧са‧у | Гви‧не‧я-Би‧са‧у | |
| ша́м-фъстъ́к (šám-fǎstǎ́k) | шам-фъ‧стък | шам-фъ‧стък | |
| вълна́-уби́ец (vǎlná-ubíec) | въл‧на-у‧би‧ец | въл‧на-у‧би‧ец | |
| акуше́р-гинеколо́г (akušér-ginekológ) | а‧ку‧шер-ги‧не‧ко‧лог | а‧ку‧шер-ги‧не‧ко‧лог | |
| по́-добре́ къ́сно, отко́лкото ни́кога (pó-dobré kǎ́sno, otkólkoto níkoga) | по-до‧бре къ‧сно, от‧кол‧ко‧то ни‧ко‧га | по-до‧бре къ‧сно, от‧кол‧ко‧то ни‧ко‧га | |
| зени́тно-раке́тен (zenítno-rakéten) | зе‧нит‧но-ра‧ке‧тен | зе‧нит‧но-ра‧ке‧тен | |
| го́ре-до́лу (góre-dólu) | го‧ре-до‧лу | го‧ре-до‧лу | |
| на́й-по́сле (náj-pósle) | най-по‧сле | най-по‧сле | |
| чик-чири́к (čik-čirík) | чик-чи‧рик | чик-чи‧рик | |
| сре́ден ро́д (sréden ród) | сре‧ден род | сре‧ден род | |
| божа́ кра́вичка (božá krávička) | бо‧жа кра‧вич‧ка | бо‧жа кра‧вич‧ка | |
| Съедине́ни америка́нски ща́ти (Sǎedinéni amerikánski štáti) | Съ‧е‧ди‧не‧ни а‧ме‧ри‧кан‧ски ща‧ти | Съ‧е‧ди‧не‧ни а‧ме‧ри‧кан‧ски ща‧ти | |
| от мла́дих до ста́рих (ot mládih do stárih) | от мла‧дих до ста‧рих | от мла‧дих до ста‧рих | |
| со кро́тце, со бла́го и со ма́лко кюте́к (so krótce, so blágo i so málko kjuték) | со крот‧це, со бла‧го и со мал‧ко кю‧тек | со крот‧це, со бла‧го и со мал‧ко кю‧тек |
References
[සංස්කරණය]- Тилков, Димитър; Бояджиев, Тодор; Георгиева, Елена; Пенчев, Йордан; Станков, Валентин (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) .. "''': "
elseif ann then
return "'''" .. ann .. "''': "
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