Module:zm-pron: Difference between revisions

From Linguifex
Jump to navigation Jump to search
No edit summary
No edit summary
Line 1: Line 1:
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("zm")
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 sub = mw.ustring.sub
local gsub = mw.ustring.gsub
local find = mw.ustring.find
local match = mw.ustring.match
local match = mw.ustring.match
local gmatch = mw.ustring.gmatch
local gmatch = mw.ustring.gmatch
local find = mw.ustring.find
local gsub = mw.ustring.gsub
 
local u = mw.ustring.char
local long = "ː"
local split = mw.text.split
local nonsyllabic = U(0x32F) -- inverted breve below
local gsplit = mw.text.gsplit
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 = {
-- To avoid weird annoying cursor behavior
["c"] = "t" .. tie .. "s",
local TILDE, NASAL = u(0x0303), u(0x0303) -- COMBINING TILDE ̃◌
["č"] = "t" .. tie .. "ʃ",
local TILDEBELOW, CREAKY = u(0x0330), u(0x0330) -- COMBINING TILDE BELOW ̰◌
["dź"] = "d" .. tie .. "ʑ",
local SPH = CREAKY .. CREAKY -- sphincteric or strident vowel
["e"] = "e",
local GRAVE = u(0x0300) -- COMBINING GRAVE ACCENT ̀◌
["è"] = "ɛ",
local HIGHFALL = "˥˦"
["g"] = "ɡ",
local SYLLABIC = u(0x0329) -- COMBINING VERTICAL LINE BELOW ̩◌
["h"] = "x",
local SYLLABICA = u(0x030D) -- COMBINING VERTICAL LINE ABOVE ̍◌
["i"] = "i",
local DENTAL = u(0x032A) -- COMBINING BRIDGE BELOW ̪◌
["nj"] = "ɲ",
local INTERDENTAL = DENTAL .. u(0x0346) -- COMBINING BRIDGE BELOW AND ABOVE ̪͆◌
["q"] = "k",
local VOICELESS = u(0x0325) -- COMBINING RING BELOW ̥◌
["š"] = "ʃ",
local AFFR = u(0x0361) -- COMBINING DOUBLE INVERTED BREVE ͡
["t"] = "t",
["ć"] = "t" .. tie .. "ɕ",
["x"] = "ks",
["y"] = "ɪ",
["ž"] = "ʒ",
["ai"] = "ɛ" .. long,
["au"] = "ɔ" .. long,
["\""] = primary_stress,
["%"] = secondary_stress,
["?"] = "ʔ",
}


-- Add data["a"] = "a", data["b"] = "b", etc.
local back_vowel = "uɯɔɑ"
for character in gmatch("abdfjklmnoprstuvz ", ".") do
local front_vowel = "iea"
data[character] = character
local vowel = "[" .. back_vowel .. front_vowel .. "]"
end
local oral_to_nasal = {["a"] = "ã", ["i"] = "ĩ", ["ɔ"] = "ṍ", ["u"] = "ᴍ"} -- ṍ = ɔ̃
local nasal_to_oral = {["ã"] = "a" .. NASAL, ["ĩ"] = "i" .. NASAL, ["ṍ"] = "ɔ" .. NASAL}
local modal_to_glottal = {["a"] = "à", ["e"] = "è", ["i"] = "ì", ["ɔ"] = "ò", ["u"] = "ù"}
local glottal_to_modal = {["à"] = "a" .. SPH, ["è"] = "e" .. SPH, ["ì"] = "i" .. SPH, ["ò"] = "ɔ" .. CREAKY, ["ù"] = "u" .. CREAKY}
local nasalized = "[ãĩṍᴍ]"
local glottalic = "[àèìòù]"
local oral = "[aeiouàèìòù]"


--[[ This allows multiple-character sounds to be replaced
local sonorant = "[rmnɳɖɲlŋᴟ]"
with single characters to make them easier to process. ]]
local click = "ǀǃʘǂǁ"


local multiple_to_single = {
local function same(foo, bar)
["t" .. tie .. "s" ] = "ʦ",
foo, bar = mw.ustring.toNFD(foo), mw.ustring.toNFD(bar) -- decompose diacritics
["t" .. tie .. "ɕ" ] = "ʨ",
foo, bar = match(foo, "^."), match(bar, "^.") -- sort out the letter
["d" .. tie .. "ʑ" ] = "ʥ",
return foo == bar and true or false
["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
end


for index, consonant in pairs (voiced) do
local export = {}
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 rules = {
 
{"ʇ", "ǀ"}, {"õ", "ṍ"}, {"o", "ɔ"}, {"ṭ", "ʈ"}, {"j", "ɟ"}, {"ñ", "ɲ"}, {"ch", "tʃ"}, {"ʈr", "ʈʂ"}, {"ṇ", "ɳ"}, {"ṣ", "ʂ"}, {"š", "ʃ"},
local function regressively_assimilate(IPA)
{"([" .. front_vowel .. "])", "s%1"},
IPA = gsub(
{"k([" .. front_vowel .. "])", "kx%1"}, {"k([" .. back_vowel .. "])", "q%1"},
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")
{"([ḛḭṵaɔ]" .. TILDEBELOW .. "?)", {["ḛ"] = "è", ["ḭ"] = "ì", ["ṵ"] = "ù", ["a" .. TILDEBELOW] = "à", ["ɔ" .. TILDEBELOW] = "ò"}},
return IPA
{"([" .. click .. "])(" .. glottalic .. ")", "%1ˀ%2"}, -- ꞰV̰ = ꞰˀV̰
end
-- tell apart between regular and syllabic <m>
 
{"(" .. oral .. ")m(" .. vowel .. ")", "%1ᴟ%2"}, {"([uùm]ʼ?)m", "%1ᴍ"}, {"m(ʼᴍ)", "ᴍ%1"}, {"(" .. oral .. ")m", "%1ᴍ"},
local function devoice_finally(IPA)
{"(" .. sonorant .. ")(" .. glottalic .. ")", "%1" .. CREAKY .. "%2"}, -- MV̰ > M̰V̰
local obstruent = "[" .. table.concat(voiced) .. table.concat(voiceless) .. "]"
{"[mᴟ](" .. vowel .. ")", "ᴟᵇ%1"},  {"ŋ(" .. vowel .. ")", "ŋᶢ%1"},  {"ɳ(" .. vowel .. ")", "ᶯɖ%1"},
{"(" .. vowel .. ")(".. vowel .. ")", function(s1, s2) return same(s1, s2) and s1 .. "ː˧" or s1 .. s2 .. "˧" end},
{"(" .. vowel .. ")(" .. nasalized .. ")",
function(s1, s2)
return same(s1, s2) and s2 .. "ː" .. HIGHFALL or oral_to_nasal[s1] .. s2 .. HIGHFALL
end
},
{"(" .. glottalic .. ")([" .. front_vowel .. "])",
function(s1, s2)
return same(s1, s2) and s1 .. "ː˦" or s1 .. modal_to_glottal[s2] .. "˦"
end
},
{"(" .. glottalic .. ")([" .. back_vowel .. "])",
function(s1, s2)
return same(s1, s2) and s1 .. "ː˨" or s1 .. modal_to_glottal[s2] .. "˨"
end
},
{"(" .. glottalic .. ")(" .. nasalized .. ")",
function(s1, s2)
return same(s1, s2) and glottal_to_modal[s1] .. NASAL .. "ː˧" or glottal_to_modal[s1] .. NASAL .. s2 .. "˧"
end
},
{"(" .. glottalic .. "ʼ[" .. front_vowel .. "])", "%1˦"}, {"(" .. glottalic .. "ʼ[" .. back_vowel .. "])", "%1˨"},
{"(" .. glottalic .. ")ʼ(" .. nasalized .. ")", function(s1, s2) return glottal_to_modal[s1] .. NASAL .. "ʔ" .. s2 .. HIGHFALL end},
IPA = gsub(
{"([^uɯɔɑieaʼ])m([^uɯɔɑieaʼ])", "%1ᴍ%2"}, {"u" .. CREAKY .. NASAL, "ᴍ" .. CREAKY},
IPA,
{"ᴍᴍ", "m" .. SYLLABIC .. "ː"}, {"ùᴍ", "m" .. CREAKY .. SYLLABICA .. "ː˧"},
"(" .. obstruent .. "+)#",
{"ᴍʼᴍ", "m" .. SYLLABICA .. CREAKY .. "ʔm" .. SYLLABIC .. "˧"},  {"ᴍ" .. CREAKY .. "ᴍ", "m" .. CREAKY .. SYLLABICA .. "ː"},
function (final_obstruents)
{"ᴍ" .. CREAKY, "m" .. CREAKY .. SYLLABICA}, {"ṍṍ", "ṍː"}, {"ṍ", "ɔ" .. NASAL}, {"(" .. glottalic .. ")", function(s1) return glottal_to_modal[s1] end},
return gsub(
{"ᴍ", "m" .. SYLLABIC},
final_obstruents,
".",
function (obstruent)
return voiceless[indices[obstruent]]
end)
.. "#"
end)
return IPA
{"mᵇm", "mm"},  {"ŋᶢm", "ŋm"}, {"ᶯɖm", "ɳm"},
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̝̊
{"ɴ", "ᵑ"}, {"([" .. click .. "])ʼ", "%1ˀ"}, {"(ᵑ[" .. click .. "])x", "%1ʁ"}, {"([" .. click .. "])x", "%1χ"}, {"([^ʈɬcq])ʼ", "%1ʔ"},
IPA = gsub(IPA, "(" .. voiceless .. ")" .. "ř", "%1ṙ")
{"^ʼ", "ʔ"}, {"ŋ([" .. back_vowel .. "])", "ɴ%1"}, {"(" .. vowel .. ")ᵑ", "%1" .. NASAL .. "ᵑ"},
IPA = gsub(IPA, "ř" .. "(" .. voiceless .. ")", "%1")
{"([" .. click .. "][qʛχʁ]?ʼ?)a", "%1ɑ"}, {"([" .. click .. "][qʛχʁ]?ʼ?)ã", "%1ɑ" .. NASAL}, {"ɑ" .. SPH, "ɑ" .. CREAKY}, -- A-backening
{"([ʈʂɳɖ])u", "%1ɯ"}, {"([ʘǃǁ][qχʁʛ]?ʼ?)u", "%1ɯ"}, -- U-unrounding
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
{"([ʈtk])([ʂʃɬxs])", "%1" .. AFFR .. "%2"}, {"ᴟ", "m"},
IPA = gsub(
{"s", "s" .. DENTAL}, {"ṯ", "t" .. INTERDENTAL .. AFFR .. "s" .. DENTAL}, {"ṉ", "n" .. INTERDENTAL .. VOICELESS},
IPA,
{"([" .. click .. "])([qχʁʛ])", "%1" .. AFFR .. "%2"}, {"u" .. NASAL, "m" .. SYLLABIC}, {"ɔ" .. CREAKY .. "ɔ" .. CREAKY, "ɔ" .. CREAKY .. "ː"},
"(" .. 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]"
 
function export.crux(term)
term = term:gsub("N", "ɴ"); term = term:gsub("Ɠ", "ʛ")
term = mw.ustring.lower(term)
IPA = gsub(IPA, "n(" .. velar .. ")", "ŋ%1")
for _, rule in ipairs(rules) do
term = gsub(term, rule[1], rule[2])
end
return IPA
return term
end
end


local function add_stress(IPA)
function IPA_span(items)
local syllable_count = m_syllables.getVowels(IPA, lang)
local bits = {}
for _, item in ipairs(items) do
if not ( nostress or find(IPA, ".#.") or find(IPA, primary_stress) ) then
local bit = "<span style=\"font-size:110%;font-family:'Gentium','DejaVu Sans','Segoe UI',sans-serif>" .. item.pron .. "</span>"
IPA = primary_stress .. IPA
table.insert(bits, bit)
end
end
return table.concat(bits)
return IPA
end
end


local function syllabify(IPA)
function format_IPA(items)
local syllables = {}
return "[[w:IPA chart|IPA]]<sup>([[IPA for e|key]])</sup>:&#32;" .. IPA_span(items)
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
end


local function apply_rules(IPA)
function line_format(pronunciation)
--[[ Adds # at word boundaries and in place of spaces, to
local full_pronunciations = {}
unify treatment of initial and final conditions.
local IPA_args = {{pron = '[' .. pronunciation .. ']'}}
# is commonly used in phonological rule notation
table.insert(full_pronunciations, format_IPA(IPA_args))
to represent word boundaries. ]]
return table.concat(full_pronunciations)
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
end


function export.toIPA(term, nostress)
function separate_word(term)
local IPA = {}
local result = {}
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
for word in gsplit(term, " ") do
local IPA_letter
table.insert(result, export.crux(word))
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
end
IPA = table.concat(IPA)
return table.concat(result, " ")
IPA = apply_rules(IPA, nostress)
return IPA, transcription
end
end


function export.show(frame)
function export.show(frame)
local params = {
local params = {
[1] = {},
[1] = { default = mw.title.getCurrentTitle().nsText == 'Template' and "ǂAː Ṇṵĩ" or mw.title.getCurrentTitle().text },
["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
 
IPA = m_IPA.format_IPA_full(lang, { { pron = IPA } } )
return IPA
end
 
function export.example(frame)
local output = {
[[
{| class="wikitable"
]]
}
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 = require("Module:parameters").process(frame:getParent().args, params)
local term = args[1]
local args = m_params.process(frame:getParent().args, params)
local ipa = "* "
local terms = mw.text.split(args[1] or "příklad", ", ")
ipa = ipa .. line_format(separate_word(term))
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)
return ipa
end
end


return export
return export

Revision as of 05:40, 29 April 2022



local sub = mw.ustring.sub
local find = mw.ustring.find
local match = mw.ustring.match
local gmatch = mw.ustring.gmatch
local gsub = mw.ustring.gsub
local u = mw.ustring.char
local split = mw.text.split
local gsplit = mw.text.gsplit

-- To avoid weird annoying cursor behavior
local TILDE, NASAL = u(0x0303), u(0x0303) -- COMBINING TILDE ̃◌
local TILDEBELOW, CREAKY = u(0x0330), u(0x0330) -- COMBINING TILDE BELOW ̰◌
local SPH = CREAKY .. CREAKY -- sphincteric or strident vowel
local GRAVE = u(0x0300) -- COMBINING GRAVE ACCENT ̀◌
local HIGHFALL = "˥˦"
local SYLLABIC = u(0x0329) -- COMBINING VERTICAL LINE BELOW ̩◌
local SYLLABICA = u(0x030D) -- COMBINING VERTICAL LINE ABOVE ̍◌
local DENTAL = u(0x032A) -- COMBINING BRIDGE BELOW ̪◌
local INTERDENTAL = DENTAL .. u(0x0346) -- COMBINING BRIDGE BELOW AND ABOVE ̪͆◌
local VOICELESS = u(0x0325) -- COMBINING RING BELOW ̥◌
local AFFR = u(0x0361) -- COMBINING DOUBLE INVERTED BREVE ͡

local back_vowel = "uɯɔɑ"
local front_vowel = "iea"
local vowel = "[" .. back_vowel .. front_vowel .. "]"
local oral_to_nasal = {["a"] = "ã", ["i"] = "ĩ", ["ɔ"] = "ṍ", ["u"] = "ᴍ"} -- ṍ = ɔ̃
local nasal_to_oral = {["ã"] = "a" .. NASAL, ["ĩ"] = "i" .. NASAL, ["ṍ"] = "ɔ" .. NASAL}
local modal_to_glottal = {["a"] = "à", ["e"] = "è", ["i"] = "ì", ["ɔ"] = "ò", ["u"] = "ù"}
local glottal_to_modal = {["à"] = "a" .. SPH, ["è"] = "e" .. SPH, ["ì"] = "i" .. SPH, ["ò"] = "ɔ" .. CREAKY, ["ù"] = "u" .. CREAKY}
local nasalized = "[ãĩṍᴍ]"
local glottalic = "[àèìòù]"
local oral = "[aeiouàèìòù]"

local sonorant = "[rmnɳɖɲlŋᴟ]"
local click = "ǀǃʘǂǁ"

local function same(foo, bar)
	foo, bar = mw.ustring.toNFD(foo), mw.ustring.toNFD(bar) -- decompose diacritics
	foo, bar = match(foo, "^."), match(bar, "^.") -- sort out the letter
	return foo == bar and true or false
end

local export = {}

local rules = {
	{"ʇ", "ǀ"}, {"õ", "ṍ"}, {"o", "ɔ"}, {"ṭ", "ʈ"}, {"j", "ɟ"}, {"ñ", "ɲ"}, {"ch", "tʃ"}, {"ʈr", "ʈʂ"}, {"ṇ", "ɳ"}, {"ṣ", "ʂ"},  {"š", "ʃ"},
	{"ṯ([" .. front_vowel .. "])", "s%1"},
	{"k([" .. front_vowel .. "])", "kx%1"}, {"k([" .. back_vowel .. "])", "q%1"},
	
	{"([ḛḭṵaɔ]" .. TILDEBELOW .. "?)", {["ḛ"] = "è", ["ḭ"] = "ì", ["ṵ"] = "ù", ["a" .. TILDEBELOW] = "à", ["ɔ" .. TILDEBELOW] = "ò"}},
	
	{"([" .. click .. "])(" .. glottalic .. ")", "%1ˀ%2"}, -- ꞰV̰ = ꞰˀV̰
	 -- tell apart between regular and syllabic <m>
	{"(" .. oral .. ")m(" .. vowel .. ")", "%1ᴟ%2"}, {"([uùm]ʼ?)m", "%1ᴍ"}, {"m(ʼᴍ)", "ᴍ%1"}, {"(" .. oral .. ")m", "%1ᴍ"},
	{"(" .. sonorant .. ")(" .. glottalic .. ")", "%1" .. CREAKY .. "%2"}, -- MV̰ > M̰V̰ 
	{"[mᴟ](" .. vowel .. ")", "ᴟᵇ%1"},  {"ŋ(" .. vowel .. ")", "ŋᶢ%1"},  {"ɳ(" .. vowel .. ")", "ᶯɖ%1"},
	{"(" .. vowel .. ")(".. vowel .. ")", function(s1, s2) return same(s1, s2) and s1 .. "ː˧" or s1 .. s2 .. "˧" end},
	{"(" .. vowel .. ")(" .. nasalized .. ")",
		function(s1, s2)
			return same(s1, s2) and s2 .. "ː" .. HIGHFALL or oral_to_nasal[s1] .. s2 .. HIGHFALL
		end
	},
	{"(" .. glottalic .. ")([" .. front_vowel .. "])",
		function(s1, s2)
			return same(s1, s2) and s1 .. "ː˦" or s1 .. modal_to_glottal[s2] .. "˦"
		end
	},
	{"(" .. glottalic .. ")([" .. back_vowel .. "])",
		function(s1, s2)
			return same(s1, s2) and s1 .. "ː˨" or s1 .. modal_to_glottal[s2] .. "˨"
		end
	},
	{"(" .. glottalic .. ")(" .. nasalized .. ")",
		function(s1, s2)
			return same(s1, s2) and glottal_to_modal[s1] .. NASAL .. "ː˧" or glottal_to_modal[s1] .. NASAL .. s2 .. "˧"
		end
	},
	{"(" .. glottalic .. "ʼ[" .. front_vowel .. "])", "%1˦"}, {"(" .. glottalic .. "ʼ[" .. back_vowel .. "])", "%1˨"},
	{"(" .. glottalic .. ")ʼ(" .. nasalized .. ")", function(s1, s2) return glottal_to_modal[s1] .. NASAL .. "ʔ" .. s2 .. HIGHFALL end},
	
	{"([^uɯɔɑieaʼ])m([^uɯɔɑieaʼ])", "%1ᴍ%2"}, {"u" .. CREAKY .. NASAL, "ᴍ" .. CREAKY},
	{"ᴍᴍ", "m" .. SYLLABIC .. "ː"}, {"ùᴍ", "m" .. CREAKY .. SYLLABICA .. "ː˧"},
	{"ᴍʼᴍ", "m" .. SYLLABICA .. CREAKY .. "ʔm" .. SYLLABIC .. "˧"},  {"ᴍ" .. CREAKY .. "ᴍ", "m" .. CREAKY .. SYLLABICA .. "ː"},
	{"ᴍ" .. CREAKY, "m" .. CREAKY .. SYLLABICA}, {"ṍṍ", "ṍː"}, {"ṍ", "ɔ" .. NASAL}, {"(" .. glottalic .. ")", function(s1) return glottal_to_modal[s1] end},
	{"ᴍ", "m" .. SYLLABIC},
	
	{"mᵇm", "mm"},  {"ŋᶢm", "ŋm"},  {"ᶯɖm", "ɳm"},
	
	{"ɴ", "ᵑ"}, {"([" .. click .. "])ʼ", "%1ˀ"}, {"(ᵑ[" .. click .. "])x", "%1ʁ"}, {"([" .. click .. "])x", "%1χ"}, {"([^ʈɬcq])ʼ", "%1ʔ"},
	{"^ʼ", "ʔ"}, {"ŋ([" .. back_vowel .. "])", "ɴ%1"}, {"(" .. vowel .. ")ᵑ", "%1" .. NASAL .. "ᵑ"}, 
	{"([" .. click .. "][qʛχʁ]?ʼ?)a", "%1ɑ"}, {"([" .. click .. "][qʛχʁ]?ʼ?)ã", "%1ɑ" .. NASAL}, {"ɑ" .. SPH, "ɑ" .. CREAKY}, -- A-backening
	{"([ʈʂɳɖ])u", "%1ɯ"}, {"([ʘǃǁ][qχʁʛ]?ʼ?)u", "%1ɯ"}, -- U-unrounding
	
	
	{"([ʈtk])([ʂʃɬxs])", "%1" .. AFFR .. "%2"}, {"ᴟ", "m"},
	{"s", "s" .. DENTAL}, {"ṯ", "t" .. INTERDENTAL .. AFFR .. "s" .. DENTAL}, {"ṉ", "n" .. INTERDENTAL .. VOICELESS},
	{"([" .. click .. "])([qχʁʛ])", "%1" .. AFFR .. "%2"}, {"u" .. NASAL, "m" .. SYLLABIC}, {"ɔ" .. CREAKY .. "ɔ" .. CREAKY, "ɔ" .. CREAKY .. "ː"},
	

}

function export.crux(term)
	term = term:gsub("N", "ɴ"); term = term:gsub("Ɠ", "ʛ")
	term = mw.ustring.lower(term)
	
	for _, rule in ipairs(rules) do
		term = gsub(term, rule[1], rule[2])
	end
	
	return term
end

function IPA_span(items)
	local bits = {}
	for _, item in ipairs(items) do
		local bit = "<span style=\"font-size:110%;font-family:'Gentium','DejaVu Sans','Segoe UI',sans-serif>" .. item.pron .. "</span>"
		table.insert(bits, bit)
	end
	return table.concat(bits)
end

function format_IPA(items)
	return "[[w:IPA chart|IPA]]<sup>([[IPA for e|key]])</sup>:&#32;" .. IPA_span(items)
end

function line_format(pronunciation)
	local full_pronunciations = {}
	local IPA_args = {{pron = '[' .. pronunciation .. ']'}}
	table.insert(full_pronunciations, format_IPA(IPA_args))
	return table.concat(full_pronunciations)
end

function separate_word(term)
	local result = {}
	
	for word in gsplit(term, " ") do
		table.insert(result, export.crux(word))
	end
	
	return table.concat(result, " ")
end

function export.show(frame)
	local params = {
		[1] = { default = mw.title.getCurrentTitle().nsText == 'Template' and "ǂAː Ṇṵĩ" or mw.title.getCurrentTitle().text },	
	}
	local args = require("Module:parameters").process(frame:getParent().args, params)
	local term = args[1]
	
	local ipa = "* "
	ipa = ipa .. line_format(separate_word(term))
	
	return ipa
end

return export