Pronunciation module for Hungarian. See {{hu-IPA}}
.
local export = {}
local gsub = mw.ustring.gsub
local match = mw.ustring.match
local replace_set = {
= {
= 'ʦ',
= 'ʧṡ', = 'ṡṡ', = 'ʦʦ', = 'sṡ',
= 'ɟɟ', = 'ɲɲ', ty'] = 'cc',
},
= {
= 'ʤ', = 'ʤʤ', = 'ṡṡ', = 'ss',
= 'ʦʦ', = 'ʦʦ', = 'ʧʧ',
= 'cṡ', = 'ʦʦ', = 'ʦʦ',
= 'ɲɟ', = 'ʤʒ',
= 'ɟɟ', = 'ɲɲ', = 'cc',
= 'jj', = 'cc',
= 'rr',
},
= {
= 'ʧ', = 'ʣ',
= 'ɟ', = 'j', = 'ɲ',
= 'c', = 'jj', = 'ɲɲ',
= 'cc', = 'ɟɟ', = 'ʦʦ',
= 'ʦʦ', = 'ʧʧ', = 'ʧʧ',
= 'cʃ', = 'cʧ',
= 'kv', = 'ṡ', = 'ʃʃ', = 'ʒ',
},
= {
= 'ʃ', = 'ʧʧ',
= 'v', = 'kṡ',
},
}
local replace_cons = {
= 'ʦ', = 'ʧ', = 'ʧʧ', = 'ʤʒ', = 'ʧṡ',
= 'ʦʦ', = 'ɟɟ', = 'ʧʧ', = 'ʦʦ', = 'cc',
= 'ʣ', = 'ʣʣ',
= 'ʤ', = 'ʤʤ', = 'ʧs',
= 'ɟ', = 'ɟɟ', = 'cʧ', = 'ɟɟ', = 'cʃ', = 'cṡ',
= 'jj', = 'jj', = 'rr', = 'rr', = 'j',
= 'ɲɟ', = 'ɲɲ', = 'ɲɲ', = 'ɲ', = 'ɲɲ',
= 'k',
= 'ʃ', = 'ṡṡ', = 'ṡ', = 'ʃʃ', = 'ṡṡ',
= 'ʦʦ', = 'cc', = 'ʧʧ', = 'ʦʦ', = 'ʦʦ',
= 'ʦʦ', = 'cc', = 'ʧʧ',
= 'cc', = 'c', = 'cc',
= 'v',
= 'kṡ',
= 'ʒ', = 'ʒʒ', = 'ʃʃ', = 'ʃs',
}
local replace_vowels = {
= 'i',
= 'ɒ', = 'aː', = 'a',
= 'ɛ', = 'eː', = 'e',
= 'i', = 'iː',
= 'o', = 'oː',
= 'ø', = 'øː',
= 'u', = 'uː',
= 'y', = 'yː',
}
local back_replace = {
= 't͡s', = 'd͡z',
= 't͡ʃ', = 'd͡ʒ',
= 's',
= 'ɡ', = 'x',
= '',
}
local nasal_assim = {
= 'ŋ', = 'ŋ',
= 'ɲ', = 'ɲ', = 'ɲ',
= 'ɱ', = 'ɱ',
= 'm', = 'm', = 'm',
}
local voicing_assim = {
= {
= 'p', = 'f',
= 't', = 'ṡ', = 'ʦ',
= 'ʃ', = 'ʧ',
= 'c', = 'ç',
= 'k',
},
= {
= 'b', = 'v',
= 'd', = 'z', = 'ʣ',
= 'ʒ', = 'ʤ',
= 'ɟ',
= 'g',
},
}
local sonorant = {
= true, = true, = true,
= true, = true, = true,
}
local unstressed_words = {
-- indefinite articles liaise with the following word
{'ɒ', 'liaise_next'}, -- indef. article 'a'
{'ɒz', 'liaise_next'}, -- indef. article 'az'
-- these particles liaise with the preceding word (when followed by a space, to avoid other words starting with these letters being involved)
{'iʃ', 'liaise_prev'}, -- 'is'
{'ʃɛ', 'liaise_prev'}, -- 'se'
{'ʃɛm', 'liaise_prev'}, -- 'sem'
{'hɒ', 'liaise_prev'}, -- 'ha'
-- conjuntions/relative pronouns lose their accents (no need to liaise in either direction)
{'vɒɟ', 'liaise_none'}, -- 'vagy'
{'dɛ', 'liaise_none'}, -- 'de'
{'hoɟ', 'liaise_none'}, -- 'hogy' (although it may be stressed too in certain senses)
{'ɒmi', 'liaise_none'}, -- 'ami'
{'ɒki', 'liaise_none'}, -- 'aki'
{'ɒhol', 'liaise_none'}, -- 'ahol'
{'mint', 'liaise_none'}, -- 'mint'
{'eːʃ', 'liaise_none'}, -- 'és'
-- these particles which take focus remove the accent on the following word
{'nɛm', 'remove_next'}, -- 'nem'
{'nɛ', 'remove_next'}, -- 'ne'
}
function export.IPA(frame)
local args = type(frame) == 'string' and { frame } or frame:getParent().args
local result = {}
if args and args ~= '' then
args = { args }
end
args = (not args) and { mw.title.getCurrentTitle().text } or args
for _, text in ipairs(args) do
text = mw.ustring.lower(text)
local non_i, i_vowels, vowels = '()', '()', '()'
if mw.ustring.len(gsub(text, '', '')) == 1 then
text = gsub(text, ' ', '#')
end
-- j-allophony
text = gsub(text, '()j$', '%1ç')
text = gsub(text, '()j(#?)(.?)', function(prev, sep, succ)
return (succ == '' or not match(succ, vowels)) and (prev .. 'ʝ' .. sep .. succ) or (prev .. 'j' .. sep .. succ) end)
-- h-allophony
local post_conv = {}
for word in mw.text.gsplit(text, " ", true) do
word = gsub(word, 'ch$', 'χχ')
word = gsub(word, 'ch#', 'χχ#')
word = gsub(word, '(.)c(hz)$', '%1c#%2')
word = gsub(word, vowels .. 'hh' .. vowels, '%1χχ%2')
word = gsub(word, vowels .. 'cch' .. vowels, '%1χχ%2')
word = gsub(word, 'ch', 'h')
word = gsub(word, '(.?)(.?)h(.?)', function(penul, prev, succ)
if prev == '' and penul ~= '' then
prev, penul = penul, ''
end
if succ == '' or match(succ, '') then
return penul .. prev .. 'χ' .. succ
elseif match(succ, vowels) and (match(prev, vowels) and prev ~= succ) or (sonorant or sonorant) then
return penul .. prev .. 'ɦ' .. succ
else
return penul .. prev .. 'h' .. succ
end end)
table.insert(post_conv, word)
end
text = table.concat(post_conv, " ")
-- adding hiatus 'j'
text = gsub(text, non_i .. i_vowels, '%1j%2')
text = gsub(text, i_vowels .. non_i, '%1j%2')
-- converting to IPA symbols
text = gsub(text, '(+)', function(cons_clus)
if replace_cons then
return replace_cons
else
for i = 1, 4 do
for source, replace in pairs(replace_set) do
cons_clus = gsub(cons_clus, source, replace)
end
end
return cons_clus
end end)
text = gsub(text, 'qu', 'kv')
text = gsub(text, '.', replace_vowels)
-- adding stress marks to words
text = match(text, '^') and ('ˈ' .. gsub(text, ' ', ' ˈ')) or text
-- word boundaries
text = gsub(text, '', '')
-- nasal assimilation
text = gsub(text, 'n(n?)(#?)()', function(repet, sep, cons)
return nasal_assim .. (repet ~="" and nasal_assim or "") .. sep .. cons
end)
text = gsub(text, 'm(#?)()', 'ɱ%1%2')
local cons, opt_cons = '()', '(?)'
text = gsub(text, '(.)#(.)', function(prev, succ)
return (voicing_assim ~= succ and voicing_assim ~= succ) and prev .. succ or prev .. '#' .. succ end)
-- voicing and devoicing assimilations
text = gsub(text, '(+)(#?)', function(prev_cons, next_cons)
return gsub(prev_cons, '.', voicing_assim) .. next_cons
end)
text = gsub(text, '(+)(#?)', function(prev_cons, next_cons)
return gsub(prev_cons, '.', voicing_assim) .. next_cons
end)
-- geminate notation
text = gsub(text, cons .. '%1%1', '%1ː')
text = gsub(text, cons .. '(#?)%1', '%1ː%2')
-- degemination when preceded or followed by a consonant
text = gsub(text, opt_cons .. cons .. 'ː' .. opt_cons, function(prev_cons, gem_cons, next_cons)
return prev_cons .. gem_cons .. (prev_cons .. next_cons ~= "" and '' or 'ː') .. next_cons end)
-- back-replacing special characters
text = gsub(text, '.', back_replace)
-- dealing with unstressed particles and their liaison behavior
for _, word_info in ipairs(unstressed_words) do
local word, liaison_type = word_info, word_info
if liaison_type == 'liaise_next' then
text = gsub(text, 'ˈ' .. word .. ' ', word)
elseif liaison_type == 'liaise_prev' then
text = gsub(text, 'ˈ' .. word .. ' ', word .. ' ')
elseif liaison_type == 'liaise_none' then
text = gsub(text, 'ˈ' .. word .. ' ', word .. ' ')
elseif liaison_type == 'remove_next' then
text = gsub(text, word .. ' ˈ', word .. ' ')
end
end
-- removing the primary stress mark if another such mark is manually supplied
text = gsub(text, 'ˈˈ', 'ˈ')
-- likewise if a secondary stress mark is manually supplied
text = gsub(text, 'ˈˌ', 'ˌ')
-- adding a space before primary and secondary stress marks in case this space is missing
text = gsub(text, '()()', function(position, stress)
if position ~= 1 then
return ' ' .. stress
end
end)
-- replacing any double spaces (created by the previous command) with single ones
text = gsub(text, ' ', ' ')
table.insert(result, '')
end
table.insert(result, 1, "hu")
if (type(frame) == 'string') then
return mw.ustring.sub(result, 2, mw.ustring.len(result) - 1)
else
return frame:expandTemplate{ title = "IPA", args = result}
end
end
return export