Module:la-pronunc

Definition from Wiktionary, the free dictionary
Jump to: navigation, search
The following documentation is located at Module:la-pronunc/documentation. [edit]
See also: subpages of this module.

This module is not to be directly used. It is used by Template:la-pronunc, see there for usage.


local export = {}
 
local letters_ipa = {
	["a"] = "a",
	["e"] = "e",
	["i"] = "i",
	["o"] = "o",
	["u"] = "u",
	["y"] = "y",
 
	["ā"] = "aː",
	["ē"] = "eː",
	["ī"] = "iː",
	["ō"] = "oː",
	["ū"] = "uː",
	["ȳ"] = "yː",
 
	["ae"] = "ae̯",
	["oe"] = "oe̯",
	["ei"] = "ei̯",
	["au"] = "au̯",
	["eu"] = "eu̯",
 
	["b"] = "b",
	["d"] = "d",
	["f"] = "f",
 
	["c"] = "k",
	["g"] = "ɡ",
	["v"] = "w",
	["x"] = "ks",
 
	["ph"] = "pʰ",
	["th"] = "tʰ",
	["ch"] = "kʰ",
	["qv"] = "kʷ",
	["gv"] = "ɡʷ",
}
 
 
local phonetic_vowels = {
	["e"] = "ɛ",
	["i"] = "ɪ",
	["o"] = "ɔ",
	["u"] = "ʊ",
	["y"] = "ʏ",
}
 
 
local vowels = {
	"a", "e", "i", "o", "u", "y",
	"aː", "eː", "iː", "oː", "uː", "yː",
	"ae̯", "oe̯", "ei̯", "au̯", "eu̯",
}
 
 
local onsets = {
	"b", "p", "pʰ", "d", "t", "tʰ", "ɡ", "k", "kʰ", "kʷ", "ɡʷ",
	"f", "s", "h", "z",
	"l", "m", "n", "r", "j", "w",
 
	"bl", "pl", "pʰl", "br", "pr", "pʰr", 
	"dr", "tr", "tʰr",
	"ɡl", "kl", "kʰl", "ɡr", "kr", "kʰr", "ɡn",
	"fl", "fr",
 
	"sp", "st", "sk", "skʷ", "sl", "sm", "sn", "sw",
	"spr", "str", "skr",
	"spl", "skl",
}
 
local codas = {
	"b", "p", "pʰ", "d", "t", "tʰ", "ɡ", "k", "kʰ",
	"f", "s",
	"l", "m", "n", "r",
 
	"sp", "st", "sk",
	"spʰ", "stʰ", "skʰ",
 
	"lp", "lt", "lk",
	"lb", "ld", "lɡ",
	"lpʰ", "ltʰ", "lkʰ",
	"lf",
 
	"rp", "rt", "rk",
	"rb", "rd", "rɡ",
	"rpʰ", "rtʰ", "rkʰ",
	"rf",
 
	"mp", "nt", "nk",
	"mb", "nd", "nɡ",
	"mpʰ", "ntʰ", "nkʰ",
 
	"lm", "rl", "rm", "rn",
 
	"ps", "ts", "ks", "ls", "ns", "rs",
	"lks", "nks", "rks",
	"lms", "rls", "rms", "rns",
}
 
local breves = {
	["ă"] = "a",
	["ĕ"] = "e",
	["ĭ"] = "i",
	["ŭ"] = "u",
	["æ"] = "ae",
	["œ"] = "oe",
}
 
for i, val in ipairs(vowels) do
	vowels[val] = true
end
 
for i, val in ipairs(onsets) do
	onsets[val] = true
end
 
for i, val in ipairs(codas) do
	codas[val] = true
end
 
 
local function letters_to_ipa(word)
	local phonemes = {}
 
	while mw.ustring.len(word) > 0 do
		local longestmatch = ""
 
		for letter, ipa in pairs(letters_ipa) do
			if mw.ustring.len(letter) > mw.ustring.len(longestmatch) and mw.ustring.sub(word, 1, mw.ustring.len(letter)) == letter then
				longestmatch = letter
			end
		end
 
		if mw.ustring.len(longestmatch) > 0 then
			table.insert(phonemes, letters_ipa[longestmatch])
			word = mw.ustring.sub(word, mw.ustring.len(longestmatch) + 1)
		else
			table.insert(phonemes, mw.ustring.sub(word, 1, 1))
			word = mw.ustring.sub(word, 2)
		end
	end
 
	return phonemes
end
 
 
local function get_onset(syll)
	local consonants = {}
 
	for i = 1, #syll do
		if vowels[syll[i]] then
			break
		end
 
		table.insert(consonants, syll[i])
	end
 
	return table.concat(consonants)
end
 
 
local function get_coda(syll)
	local consonants = {}
 
	for i = #syll, 1, -1 do
		if vowels[syll[i]] then
			break
		end
 
		table.insert(consonants, 1, syll[i])
	end
 
	return table.concat(consonants)
end
 
 
-- Split the word into syllables of CV shape
local function split_syllables(remainder)
	local syllables = {}
	local syll = {}
 
	while #remainder > 0 do
		local phoneme = table.remove(remainder, 1)
 
		if phoneme == "." then
			if #syll > 0 then
				table.insert(syllables, syll)
				syll = {}
			end
		elseif vowels[phoneme] then
			table.insert(syll, phoneme)
			table.insert(syllables, syll)
			syll = {}
		else
			table.insert(syll, phoneme)
		end
	end
 
	-- If there are phonemes left, then the word ends in a consonant
	-- Add them to the last syllable
	for _, phoneme in ipairs(syll) do
		table.insert(syllables[#syllables], phoneme)
	end
 
	-- Split consonant clusters between syllables
	for i, current in ipairs(syllables) do
		if i > 1 then
			local previous = syllables[i-1]
			local onset = get_onset(current)
 
			-- Shift over consonants until the syllable onset is valid
			while not (onset == "" or onsets[onset]) do
				table.insert(previous, table.remove(current, 1))
				onset = get_onset(current)
			end
 
			-- If the preceding syllable still ends with a vowel, and the current one begins with s + another consonant, or with gn, then shift it over
			if get_coda(previous) == "" and ((current[1] == "s" and not vowels[current[2]]) or (current[1] =="g" and current[2] == "n")) then
				table.insert(previous, table.remove(current, 1))
			end
		end
	end
 
	for i, syll in ipairs(syllables) do
		local onset = get_onset(syll)
		local coda = get_coda(syll)
 
		if not (onset == "" or onsets[onset]) then
			require("Module:debug").track("la-pronunc/bad onset")
		end
 
		if not (coda == "" or codas[coda]) then
			require("Module:debug").track("la-pronunc/bad coda")
		end
	end
 
	return syllables
end
 
 
local function detect_accent(syllables)
	-- Detect accent placement
	if #syllables > 2 then
		-- Does the penultimate syllable end in a single vowel?
		local penult = syllables[#syllables-1]
 
		if mw.ustring.find(penult[#penult], "^[aeiouy]$") then
			return #syllables - 2
		else
			return #syllables - 1
		end
	elseif #syllables == 2 then
		return #syllables - 1
	end
end
 
 
local function convert_word(word, phonetic)
	-- Normalize spelling
	word = mw.ustring.gsub(word, "w", "v")
	word = mw.ustring.gsub(word, "([aeiouyāēīōūȳ])v([^aeiouyāēīōūȳ])", "%1u%2")
	word = mw.ustring.gsub(word, "qu", "qv")
	word = mw.ustring.gsub(word, "ngu([aeiouyāēīōūȳ])", "ngv%1")
 
	word = mw.ustring.gsub(word, "^i([aeiouyāēīōūȳ])", "j%1")
	word = mw.ustring.gsub(word, "^u([aeiouyāēīōūȳ])", "v%1")
	word = mw.ustring.gsub(word, "([aeiouyāēīōūȳ])i([aeiouyāēīōūȳ])", "%1j%2")
	word = mw.ustring.gsub(word, "([aeiouyāēīōūȳ])u([aeiouyāēīōūȳ])", "%1v%2")
 
	-- Vowel length before nasal + fricative is allophonic
	word = mw.ustring.gsub(word, "ā([mn][fs])", "a%1")
	word = mw.ustring.gsub(word, "ē([mn][fs])", "e%1")
	word = mw.ustring.gsub(word, "ī([mn][fs])", "i%1")
	word = mw.ustring.gsub(word, "ō([mn][fs])", "o%1")
	word = mw.ustring.gsub(word, "ū([mn][fs])", "u%1")
	word = mw.ustring.gsub(word, "ȳ([mn][fs])", "y%1")
 
	-- Apply some basic phoneme-level assimilations
	word = mw.ustring.gsub(word, "xs", "ks")
	word = mw.ustring.gsub(word, "b([st])", "p%1")
	word = mw.ustring.gsub(word, "d([st])", "t%1")
	word = mw.ustring.gsub(word, "g([st])", "k%1")
	word = mw.ustring.gsub(word, "n([bp])", "m%1")
 
	-- Convert word to IPA
	local phonemes = letters_to_ipa(word)
 
	-- Split into syllables
	local syllables = split_syllables(phonemes)
 
	-- Add accent
	local accent = detect_accent(syllables)
 
	if phonetic then
		for i, syll in ipairs(syllables) do
			for j, phoneme in ipairs(syll) do
				syll[j] = phonetic_vowels[phoneme] or phoneme
			end
		end
	end
 
	for i, syll in ipairs(syllables) do
		syllables[i] = (i == accent and "ˈ" or "") .. table.concat(syll)
	end
 
	word = (mw.ustring.gsub(table.concat(syllables, "."), "%.ˈ", "ˈ"))
 
	if phonetic then
		word = mw.ustring.gsub(word, "ɡ([.ˈ]?)n", "ŋ%1n")
		word = mw.ustring.gsub(word, "n([.ˈ]?)([kɡ])", "ŋ%1%2")
 
		word = mw.ustring.gsub(word, "ʷ([eɛiɪyʏ])", "ᶣ%1")
 
		-- Nasal vowels
		word = mw.ustring.gsub(word, "a[nm]$", "ã")
		word = mw.ustring.gsub(word, "[eɛ][nm]$", "ẽ")
		word = mw.ustring.gsub(word, "[iɪ][nm]$", "ĩ")
		word = mw.ustring.gsub(word, "[oɔ][nm]$", "õ")
		word = mw.ustring.gsub(word, "[uʊ][nm]$", "ũ")
		word = mw.ustring.gsub(word, "[yʏ][nm]$", "ỹ")
		word = mw.ustring.gsub(word, "a[nm]([%.ˈ]?[sf])", "ãː%1")
		word = mw.ustring.gsub(word, "[eɛ][nm]([%.ˈ]?[sf])", "ẽː%1")
		word = mw.ustring.gsub(word, "[iɪ][nm]([%.ˈ]?[sf])", "ĩː%1")
		word = mw.ustring.gsub(word, "[oɔ][nm]([%.ˈ]?[sf])", "õː%1")
		word = mw.ustring.gsub(word, "[uʊ][nm]([%.ˈ]?[sf])", "ũː%1")
		word = mw.ustring.gsub(word, "[yʏ][nm]([%.ˈ]?[sf])", "ỹː%1")
 
		--L pinguis
		word = mw.ustring.gsub(word, "l", "ɫ")
		word = mw.ustring.gsub(word, "ɫ(%.?)ɫ", "l%1l")
		word = mw.ustring.gsub(word, "ɫ(%.?[iɪyʏ])", "l%1")
	end
 
	return word
end
 
 
local function convert_words(words, phonetic)
	words = mw.ustring.lower(words)
	words = mw.ustring.gsub(words,'[,?!:;()\'"%-]', '')
	words = mw.ustring.gsub(words,'[ăĕĭŭæœ]', breves)
	if mw.ustring.find(words,'[^a-zA-Zāēīōūȳ,.?!:;()\'" ]') then error('no extra punctuations allowed than sentence markers') end
 
	local result = {}
 
	for word in mw.text.gsplit(words, " ") do
		table.insert(result, convert_word(word, phonetic))
	end
 
	return table.concat(result, " ")
end
 
 
function export.show(words, phonetic)
	if type(words) == "table" then -- assume a frame
		words = words.args[1]:lower() or mw.title.getCurrentTitle().text:lower()
	end
 
	return convert_words(words, phonetic)
end
 
 
function export.allophone(word)
	return export.show(word, true)
end
 
 
local unassimilated_prefixes = {
	'ab','ad','circum','con','contrā','dis','ex','īn','inter','ob','per','sub','subter'
}
 
 
function export.track(words)
	if type(words) == "table" then -- assume a frame
		words = words.args[1]:lower() or mw.title.getCurrentTitle().text:lower()
	end
 
	words = mw.ustring.lower(words)
	words = mw.ustring.gsub(words,'[,.?!:;()\'"%-]','')
	words = mw.ustring.gsub(words,'[ăĕĭŭæœ]', breves)
 
	for word in mw.text.gsplit(words, " ") do
		for _,prefix in ipairs(unassimilated_prefixes) do
			if mw.ustring.find(word,prefix..'i[aeiouāēīōū]') then
				--return '[[Category:Latin terms needing attention]]'
			end
		end
	end
end
 
return export