local export = {}
local gsub = mw.ustring.gsub
local U = mw.ustring.char
local grave = U(0x300)
local acute = U(0x301)
local stress = U(0x2C8)
local secondary_stress = U(0x2CC)
local tie = U(0x361)
local fronted = U(0x31F)
-- 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",
["ф"] = "f",
["х"] = "x",
["ц"] = "t" .. tie .. "s",
["ч"] = "t" .. tie .. "ʃ",
["ш"] = "ʃ",
["щ"] = "ʃt",
["ъ"] = "ɤ",
["ь"] = "ʲ",
["ю"] = "ʲu",
["я"] = "ʲa",
[grave] = stress,
[acute] = secondary_stress
}
local accent = "[" .. stress .. secondary_stress .. "]"
local devoicing = {
['b'] = 'p', ['d'] = 't', ['ɡ'] = 'k',
['z'] = 's', ['ʒ'] = 'ʃ',
['v'] = 'f'
}
local voicing = {
['p'] = 'b', ['t'] = 'd', ['k'] = 'ɡ',
['s'] = 'z', ['ʃ'] = 'ʒ', ['x'] = 'ɣ',
['f'] = 'v'
}
local function convert(word, conv)
IPA = mw.ustring.toNFD(mw.ustring.lower(word))
IPA = gsub(IPA, '.', phonetic_chars_map)
-- Mark word boundaries
IPA = gsub(IPA, "(%s+)", "#%1#")
IPA = "#" .. IPA .. "#"
-- Convert verbal and definite endings
if conv == true then
IPA = gsub(IPA, "a(" .. stress .. "t?#)", "ɤ%1")
end
-- Change ʲ to j after vowels
IPA = gsub(IPA, "([aɛiɔuɤ])ʲ", "%1j")
-- Move stress
IPA = gsub(IPA, "([szʃʒ]?[bdɡptkxf]?[rlmnvj]?ʲ?[aɛiɔuɤ])(" .. accent .. ")", "%2%1")
IPA = gsub(IPA, "([td]" .. tie .. " [szʃʒ]ʲ?)([aɛiɔuɤ])(" .. accent .. ")", "%2%1")
IPA = gsub(IPA, "#([^#aɛiɔuɤ]*)(" .. accent .. ")", "#%2%1")
return IPA
end
function export.toIPA(word, conv)
IPA = convert(word, conv)
-- Vowel reduction
IPA = gsub(IPA, "a([^aɛiɔuɤ]*" .. accent .. ")", "ɐ%1")
IPA = gsub(IPA, "(#[^#" .. stress .. secondary_stress .. "]*)(.)", function(a, b)
if b == '#' then return a .. b else return gsub(a, "[aɔɤu]", { ['a'] = 'ə', ['ɔ'] = 'o', ['ɤ'] = 'ə', ['u'] = 'ʊ' }) .. b end end)
IPA = gsub(IPA, "(" .. accent .. "[^aɛiɔuɤ#]*[aɛiɔuɤ])([^#" .. stress .. secondary_stress .. "]*)", function(a, b)
return a .. gsub(b, "[aɔɤu]", { ['a'] = 'ə', ['ɔ'] = 'o', ['ɤ'] = 'ə', ['u'] = 'ʊ' }) end)
IPA = gsub(IPA, "ʊ([^aɛiɔuɤəɐoʊ]*" .. accent .. ")", "u%1")
-- Vowel accommodation
IPA = gsub(IPA, "ʲ[aɐə](.)ʲ", "ʲæ%1ʲ")
IPA = gsub(IPA, "ʲu(.)ʲ", "ʲʉ%1ʲ")
IPA = gsub(IPA, "([ʃʒʲ])([aouɤ])", "%1%2" .. fronted)
IPA = gsub(IPA, "([ʃʒ])ɛ", "%1e")
-- Palatalisation
IPA = gsub(IPA, "([kɡxl])([ie])", "%1ʲ%2")
-- Hard l
IPA = gsub(IPA, "l([^ʲ])", "ɫ%1")
-- Voicing assimilation
IPA = gsub(IPA, "([bdɡzʒv" .. tie .. "]*)(" .. accent .. "?[ptksʃfx#])", function(a, b)
return gsub(a, '.', devoicing) .. b end)
IPA = gsub(IPA, "([ptksʃfx" .. tie .. "]*)(" .. accent .. "?[bdɡzʒ])", function(a, b)
return gsub(a, '.', voicing) .. b end)
IPA = gsub(IPA, "n(" .. accent .. "?[ɡk]+)", "ŋ%1")
IPA = gsub(IPA, "m(" .. accent .. "?[fv]+)", "ɱ%1")
-- Sibilant assimilation
IPA = gsub(IPA, "[sz](" .. accent .. "?[td]?" .. tie .. "?)([ʃʒ])", "%2%1%2")
-- Reduce consonant clusters
IPA = gsub(IPA, "([szʃʒ])[td]([tdknml])", "%1%2")
IPA = gsub(IPA, "([sʃ])t#", "%1(t)#")
-- Strip hashes
IPA = gsub(IPA, "#", "")
return IPA
end
function export.show(frame)
local params = {
[1] = {},
["back_ending"] = { type="boolean", default="false" }
}
local title = mw.title.getCurrentTitle()
local args = require("Module:parameters").process(frame:getParent().args, params)
local term = args[1] or title.nsText == "Template" and "прѝмер" or title.text
local IPA = export.toIPA(term, args.conv)
IPA = "[" .. IPA .. "]"
IPA = require("Module:IPA").format_IPA_full(require("Module:languages").getByCode("bg"), { { pron = IPA } } )
return IPA
end
return export