Template:Zm-IPA
local export = {}
local m_params = require("Module:parameters") local m_IPA = require("Module:IPA") local m_syllables = require("Module:syllables") local m_template_link = require("Module:template link")
local lang = require("Module:languages").getByCode("cs") local sc = require("Module:scripts").getByCode("Latn")
function export.tag_text(text, face) return require("Module:script utilities").tag_text(text, lang, sc, face) end
function export.link(term, face) return require("Module:links").full_link( { term = term, lang = lang, sc = sc }, face ) end
local U = mw.ustring.char local sub = mw.ustring.sub local gsub = mw.ustring.gsub local match = mw.ustring.match local gmatch = mw.ustring.gmatch local find = mw.ustring.find
local long = "ː" local nonsyllabic = U(0x32F) -- inverted breve below local syllabic = U(0x0329) local syllabic_below = U(0x030D) local raised = U(0x31D) -- uptack below local voiceless = U(0x30A) -- ring above local caron = U(0x30C) -- combining caron local tie = U(0x361) -- combining double inverted breve local primary_stress = "ˈ" local secondary_stress = "ˌ"
local data = { ["c"] = "t" .. tie .. "s", ["č"] = "t" .. tie .. "ʃ", ["dź"] = "d" .. tie .. "ʑ", ["e"] = "e", ["è"] = "ɛ", ["g"] = "ɡ", ["h"] = "x", ["i"] = "i", ["nj"] = "ɲ", ["q"] = "k", ["š"] = "ʃ", ["t"] = "t", ["ć"] = "t" .. tie .. "ɕ", ["x"] = "ks", ["y"] = "ɪ", ["ž"] = "ʒ", ["ai"] = "ɛ" .. long, ["au"] = "ɔ" .. long, ["\""] = primary_stress, ["%"] = secondary_stress, ["?"] = "ʔ", }
-- Add data["a"] = "a", data["b"] = "b", etc. for character in gmatch("abdfjklmnoprstuvz ", ".") do data[character] = character end
--[[ This allows multiple-character sounds to be replaced with single characters to make them easier to process. ]]
local multiple_to_single = { ["t" .. tie .. "s" ] = "ʦ", ["t" .. tie .. "ɕ" ] = "ʨ", ["d" .. tie .. "ʑ" ] = "ʥ", ["t" .. tie .. "ʃ" ] = "ʧ", ["d" .. tie .. "z" ] = "ʣ", ["d" .. tie .. "ʒ" ] = "ʤ", }
--[[ "voiceless" and "voiced" are obstruents only; sonorants are not involved in voicing assimilation. ]]
-- ʦ, ʧ, "ṙ" replace t͡s, t͡ʃ, r̝̊ local voiceless = { "p", "t", "k", "f", "s", "ʃ", "x", "ʦ", "ʧ", "ʨ", "ʔ" } -- "ʣ", ʤ, ř replace d͡z, d͡ʒ, r̝ local voiced = { "b", "d", "ʥ", "ɡ", "v", "z", "ʒ", "ʣ", "ʤ" } local sonorants = { "m", "n", "ɲ", "r", "l", "j", } local consonant = "[" .. table.concat(sonorants) .. "ŋ" .. table.concat(voiceless) .. table.concat(voiced) .. "]" assimil_consonants = {} assimil_consonants.voiceless = voiceless assimil_consonants.voiced = voiced
local features = {} local indices = {} for index, consonant in pairs(voiceless) do if not features[consonant] then features[consonant] = {} end features[consonant]["voicing"] = "voiceless" indices[consonant] = index end
for index, consonant in pairs (voiced) do if not features[consonant] then features[consonant] = {} end features[consonant]["voicing"] = "voiced" indices[consonant] = index end
local short_vowel = "[aeɛiɪou]" local long_vowel = "[ɛɔ]" .. long local syllabic_consonant = "[mnrl]" .. syllabic
-- all but v and r̝ local causing_assimilation = gsub( "[" .. table.concat(voiceless) .. table.concat(voiced) .. "ʔ]", "[vř]", "" )
local assimilable = "[" .. table.concat(voiceless):gsub("ʔ", "") .. table.concat(voiced) .. "]"
local function regressively_assimilate(IPA) IPA = gsub( IPA, "(" .. assimilable .. "+)(" .. causing_assimilation .. ")", function (assimilated, assimilator) local voicing = features[assimilator] and features[assimilator].voicing or error('The consonant "' .. consonant .. '" is not recognized by the function "regressively_assimilate".') return gsub( assimilated, ".", function (consonant) return assimil_consonants[voicing][indices[consonant]] end) .. assimilator end)
IPA = gsub(IPA, "smus", "zmus")
return IPA end
local function devoice_finally(IPA) local obstruent = "[" .. table.concat(voiced) .. table.concat(voiceless) .. "]"
IPA = gsub( IPA, "(" .. obstruent .. "+)#", function (final_obstruents) return gsub( final_obstruents, ".", function (obstruent) return voiceless[indices[obstruent]] end) .. "#" end)
return IPA end
local function devoice_fricative_r(IPA) -- all but r̝̊, which is added by this function local voiceless = gsub("[" .. table.concat(voiceless) .. "]", "ṙ", "")
-- ř represents r̝, "ṙ" represents r̝̊ IPA = gsub(IPA, "(" .. voiceless .. ")" .. "ř", "%1ṙ") IPA = gsub(IPA, "ř" .. "(" .. voiceless .. ")", "ṙ%1")
return IPA end
local function syllabicize_sonorants(IPA) -- all except ɲ and j local sonorant = gsub("[" .. table.concat(sonorants) .. "]", "[ɲj]", "") local obstruent = "[" .. table.concat(voiced) .. table.concat(voiceless) .. "]"
-- between a consonant and an obstruent IPA = gsub( IPA, "(" .. consonant .. "+" .. sonorant .. ")(" .. consonant .. ")", "%1" .. syllabic .. "%2" )
-- at the end of a word after an obstruent IPA = gsub(IPA, "(" .. obstruent .. sonorant .. ")#", "%1" .. syllabic)
return IPA end
local function assimilate_nasal(IPA) local velar = "[ɡk]"
IPA = gsub(IPA, "n(" .. velar .. ")", "ŋ%1")
return IPA end
local function add_stress(IPA) local syllable_count = m_syllables.getVowels(IPA, lang)
if not ( nostress or find(IPA, ".#.") or find(IPA, primary_stress) ) then IPA = primary_stress .. IPA end
return IPA end
local function syllabify(IPA) local syllables = {}
local working_string = IPA
local noninitial_cluster = match(working_string, ".(" .. consonant .. consonant .. ").") local has_cluster = noninitial_cluster and not find(noninitial_cluster, "(.)%1")
if not ( has_cluster or find(working_string, " ") ) then while #working_string > 0 do local syllable = match(working_string, "^" .. consonant .. "*" .. diphthong) or match(working_string, "^" .. consonant .. "*" .. long_vowel) or match(working_string, "^" .. consonant .. "*" .. short_vowel) or match(working_string, "^" .. consonant .. "*" .. syllabic_consonant) if syllable then table.insert(syllables, syllable) working_string = gsub(working_string, syllable, "", 1) elseif find(working_string, "^" .. consonant .. "+$") or find(working_string, primary_stress) then
syllables[#syllables] = syllables[#syllables] .. working_string working_string = "" else error('The function "syllabify" could not find a syllable ' .. 'in the IPA transcription "' .. working_string .. '".') end end end
if #syllables > 0 then IPA = table.concat(syllables, ".") end
return IPA end
local function apply_rules(IPA) --[[ Adds # at word boundaries and in place of spaces, to unify treatment of initial and final conditions. # is commonly used in phonological rule notation to represent word boundaries. ]] IPA = "#" .. IPA .. "#" IPA = gsub(IPA, "%s+", "#")
-- Handle consonantal prepositions: v, z. IPA = gsub( IPA, "(#[vz])#(.)", function (preposition, initial_sound) if find(initial_sound, short_vowel) then return preposition .. "ʔ" .. initial_sound else return preposition .. initial_sound end end)
for sound, character in pairs(multiple_to_single) do IPA = gsub(IPA, sound, character) end
IPA = regressively_assimilate(IPA) IPA = devoice_finally(IPA) IPA = devoice_fricative_r(IPA) IPA = syllabicize_sonorants(IPA) IPA = assimilate_nasal(IPA) IPA = add_stress(IPA, nostress)
for sound, character in pairs(multiple_to_single) do IPA = gsub(IPA, character, sound) end
--[[ This replaces double (geminate) with single consonants, and changes a stop plus affricate to affricate: for instance, [tt͡s] to [t͡s]. ]] IPA = gsub(IPA, "(" .. consonant .. ")%1", "%1")
-- Replace # with space or remove it. IPA = gsub(IPA, "([^" .. primary_stress .. secondary_stress .. "])#(.)", "%1 %2") IPA = gsub(IPA, "#", "")
return IPA
end
function export.toIPA(term, nostress) local IPA = {}
local transcription = mw.ustring.lower(term) transcription = gsub(transcription, "^%-", "") transcription = gsub(transcription, "%-?$", "") transcription = gsub(transcription, "nn", "n") -- similar operation is applied to IPA above
for regex, replacement in pairs(replacements) do transcription = gsub(transcription, regex, replacement) end transcription = mw.ustring.toNFC(transcription) -- Recompose combining caron.
local working_string = transcription
while mw.ustring.len(working_string) > 0 do local IPA_letter
local letter = sub(working_string, 1, 1) local twoletters = sub(working_string, 1, 2) or ""
if data[twoletters] then IPA_letter = data[twoletters] working_string = sub(working_string, 3) else IPA_letter = data[letter] or error('The letter "' .. tostring(letter) .. '" is not a member of the Czech alphabet.') working_string = sub(working_string, 2) end
table.insert(IPA, IPA_letter) end
IPA = table.concat(IPA) IPA = apply_rules(IPA, nostress)
return IPA, transcription end
function export.show(frame) local params = { [1] = {}, ["nostress"] = { type = "boolean" }, }
local args = m_params.process(frame:getParent().args, params) local title = mw.title.getCurrentTitle() local namespace = title.nsText local term = args[1] or namespace == "Template" and "příklad" or title.text
local IPA = export.toIPA(term, nostress)
IPA = "[" .. IPA .. "]" IPA = m_IPA.format_IPA_full(lang, { { pron = IPA } } )
return IPA end
function export.example(frame) local output = { [[
]] } local row local namespace = mw.title.getCurrentTitle().nsText if namespace == "Template" then table.insert( output, [[headword | code | result
]] ) row = [[ | ||
---|---|---|---|---|
link | template_code | IPA
]] else table.insert( output, [[ |
headword | result
]] ) row = [[ |
link | IPA
]] end local params = { [1] = { required = true }, } local args = m_params.process(frame:getParent().args, params) local terms = mw.text.split(args[1] or "příklad", ", ") for _, term in ipairs(terms) do local template_parameter local respelling_regex = "[%a\"%?%% ]+" local respelling = match(term, "(" .. respelling_regex .. ") %(") or match(term, respelling_regex) local entry = match(term, "%(([%a ]+)%)") or respelling local link = export.link(entry) local IPA, transcribable = export.toIPA(respelling) IPA = m_IPA.format_IPA_full(lang, { { pron = "[" .. IPA .. "]" } } ) if term ~= respelling then template_parameter = respelling end if term ~= transcribable then link = link .. " (" .. export.tag_text(transcribable) .. ")" end template_code = m_template_link.format_link{ "cs-IPA", template_parameter } local content = { link = link, template_code = template_code, IPA = IPA } local function add_content(name) if content[name] then return content[name] else error('No content for "' .. name .. '".') end end local current_row = gsub(row, "[%a_]+", add_content) table.insert(output, current_row) end table.insert(output, "|}") return table.concat(output) end return export |