Mòdul:rmq-x-ca-pron

De Viccionari
Icona de documentació de mòdul Documentació del mòdul[mostra] [modifica] [refresca]

A continuació es mostra la documentació transclosa de la subpàgina /ús. [salta a la caixa de codi]


Mòdul de suport per la plantilla {{rmq-x-ca-pron}} que genera la pronúncia del caló català. Vegeu la documentació de la plantilla.

És una adaptació de Mòdul:ca-pron per al català central amb algunes particularitats.

local p = {}

local function list_true(list)
	for i, val in ipairs(list) do
		list[val] = true
	end
	return list
end

local valid_onsets = list_true({
	"b", "bl", "br",
	"c", "cl", "cr",
	"ç",
	"d", "dr",
	"f", "fl", "fr",
	"g", "gl", "gr", "gu", "gü",
	"h",
	"i",
	"j",
	"k", "kl", "kr",
	"l", "ll",
	"m",
	"n", "ny", "ñ",
	"p", "pl", "pr",
	"qu", "qü",
	"r", "rr",
	"s", "ss",
	"t", "tg", "tj", "tr", "ts", "tx", "tz",
	"u",
	"v", "vl",
	"w",
	"x",
	"z",
})

local function fix_y(word)
	-- hint for voiced palatal nasal
	word = mw.ustring.gsub(word, "ny", "ñ")
	
	-- y > vowel /i/ else consonant /j/
	word = mw.ustring.gsub(word, "y([^aeiouàèéêíòóôúïü])", "i%1") -- vowel if not next to another vowel
	word = mw.ustring.gsub(word, "([^aeiouàèéêíòóôúïü·%-%.])y", "%1i") -- excluding also syllables separators
	
	return word
end

local function word_fixes(word)
	word = mw.ustring.gsub(word, "%-([rs]?)", "-%1%1")
	word = mw.ustring.gsub(word, "rç$", "rrs") -- silent r only in plurals -rs
	word = fix_y(word) -- ny > ñ else y > i vowel or consonant
	word = mw.ustring.gsub(word, "l·l", "l")
	word = mw.ustring.gsub(word, "^x", "tx")
	
	return word
end

local function split_vowels(vowels)
	local syllables = {{onset = "", vowel = mw.ustring.sub(vowels, 1, 1), coda = ""}}
	vowels = mw.ustring.sub(vowels, 2)
	
	while vowels ~= "" do
		local syll = {onset = "", vowel = "", coda = ""}
		syll.onset, syll.vowel, vowels = mw.ustring.match(vowels, "^([iu]?)(.)(.-)$")
		table.insert(syllables, syll)
	end
	
	local count = #syllables
	
	if count >= 2 and (syllables[count].vowel == "i" or syllables[count].vowel == "u") then
		syllables[count - 1].coda = syllables[count].vowel
		syllables[count] = nil
	end
	
	return syllables
end

-- Split the word into syllables
local function split_syllables(remainder)
	local syllables = {}
	
	while remainder ~= "" do
		local consonants, vowels
		
		consonants, remainder = mw.ustring.match(remainder, "^([^aàāeèéêëēiíïoòóôōuúü]*)(.-)$")
		vowels, remainder = mw.ustring.match(remainder, "^([aàāeèéêëēiíïoòóôōuúü]*)(.-)$")
		
		if vowels == "" then
			syllables[#syllables].coda = syllables[#syllables].coda .. consonants
		else
			local onset = consonants
			local first_vowel = mw.ustring.sub(vowels, 1, 1)
			
			if (mw.ustring.find(onset, "[gq]$") and (first_vowel == "ü" or (first_vowel == "u" and vowels ~= "u"))) or
				(onset == "" and #syllables == 0 and first_vowel == "i" and vowels ~= "i" ) then
				onset = onset .. mw.ustring.sub(vowels, 1, 1)
				vowels = mw.ustring.sub(vowels, 2)
			end
			
			local vsyllables = split_vowels(vowels)
			vsyllables[1].onset = onset .. vsyllables[1].onset
			
			for _, s in ipairs(vsyllables) do
				table.insert(syllables, s)
			end
		end
	end
	
	-- Shift over consonants from the onset to the preceding coda,
	-- until the syllable onset is valid
	for i = 2, #syllables do
		local current = syllables[i]
		local previous = syllables[i-1]
		
		while not (current.onset == "" or valid_onsets[current.onset]) do
			local letter = mw.ustring.sub(current.onset, 1, 1)
			current.onset = mw.ustring.sub(current.onset, 2)
			if not mw.ustring.find(letter, "[·%-%.]") then --syllables separators
				previous.coda = previous.coda .. letter
			else
				break
			end
		end
	end
	
	-- Detect stress
	for i, syll in ipairs(syllables) do
		if mw.ustring.find(syll.vowel, "^[àèéêëíòóôú]$") then
			syllables.stress = i -- primary stress: the last one stressed
			syll.stressed = true
		end
	end
	
	if not syllables.stress then
		local count = #syllables
		
		if count == 1 then
			syllables.stress = 1
		else
			local final = syllables[count]
			
			if final.coda == "" or final.coda == "s" or (final.coda == "n" and (final.vowel == "e" or final.vowel == "i" or final.vowel == "ï")) then
				syllables.stress = count - 1
			else
				syllables.stress = count
			end
		end
		syllables[syllables.stress].stressed = true
	end
	
	return syllables
end

local IPA_vowels = {
	["a"] = "a", ["à"] = "a",
	["e"] = "e", ["è"] = "ɛ", ["é"] = "e",
	["i"] = "i", ["í"] = "i", ["ï"] = "i",
	["o"] = "o", ["ò"] = "ɔ", ["ó"] = "o",
	["u"] = "u", ["ú"] = "u", ["ü"] = "u",
}

local function replace_context_free(cons)
	cons = mw.ustring.gsub(cons, "r", "ɾ")
	cons = mw.ustring.gsub(cons, "ɾɾ", "r")
	cons = mw.ustring.gsub(cons, "ss", "s")
	cons = mw.ustring.gsub(cons, "ll", "ʎ")
	cons = mw.ustring.gsub(cons, "ñ", "ɲ") -- hint ny > ñ
	
	cons = mw.ustring.gsub(cons, "[dt]j", "d͡ʒ")
	cons = mw.ustring.gsub(cons, "tx", "t͡ʃ")
	cons = mw.ustring.gsub(cons, "ts", "t͡s")
	cons = mw.ustring.gsub(cons, "[dt]z", "d͡z")
	
	cons = mw.ustring.gsub(cons, "ç", "s")
	cons = mw.ustring.gsub(cons, "[cq]", "k")
	cons = mw.ustring.gsub(cons, "g", "ɡ")
	cons = mw.ustring.gsub(cons, "j", "ʒ")
	cons = mw.ustring.gsub(cons, "x", "ʃ")
	cons = mw.ustring.gsub(cons, "h", "x")
	
	cons = mw.ustring.gsub(cons, "[iy]", "j")  -- must be after j > ʒ and fix_y
	cons = mw.ustring.gsub(cons, "[uü]", "w")
	
	return cons
end

local function postprocess_general(syllables)
	syllables = mw.clone(syllables)
	
	local voiced = list_true({"b", "d", "ɡ", "m", "n", "ɲ", "l", "ʎ", "r", "ɾ", "v", "z", "ʒ"})
	local voiceless = list_true({"p", "t", "k", "f", "s", "ʃ", ""})
	local devoicing = {["b"]="p", ["d"]="t", ["ɡ"]="k"}
	local voicing = {["p"]="b", ["t"]="d", ["k"]="ɡ", ["f"]="v", ["s"]="z", ["ʃ"]="ʒ"}
	
	for i = 1, #syllables do
		local current = syllables[i]
		local previous = syllables[i - 1]
		
		-- Coda consonant losses
		if i < #syllables or (i == #syllables and mw.ustring.find(current.coda, "s$")) then
			current.coda = mw.ustring.gsub(current.coda, "m[pb]", "m")
			current.coda = mw.ustring.gsub(current.coda, "([ln])[td]", "%1")
		end
		
		-- Consonant assimilations
		if i > 1 then
			-- t + lateral/nasal assimilation
			if mw.ustring.find(current.onset, "^ʎ") then
				previous.coda = mw.ustring.gsub(previous.coda, "t$", "ʎ")
			end
			
			-- nasal + labial > labialized assimilation
			if mw.ustring.find(current.onset, "^[mbp]") then
				previous.coda = mw.ustring.gsub(previous.coda, "n$", "m")
			elseif mw.ustring.find(current.onset, "^[fv]") then
				previous.coda = mw.ustring.gsub(previous.coda, "[mn]$", "ɱ")
			
			-- n + velar > velarized assimilation
			elseif mw.ustring.find(current.onset, "^[ɡk]") then
				previous.coda = mw.ustring.gsub(previous.coda, "n$", "ŋ")
			
			-- l/n + palatal > palatalized assimilation
			elseif mw.ustring.find(current.onset, "^[ʒʎʃɲ]")
			or mw.ustring.find(current.onset, "^t͡ʃ")
			or mw.ustring.find(current.onset, "^d͡ʒ")
			then
				if not (previous.coda == "l" and current.onset == "ʒ") then
					previous.coda = mw.ustring.gsub(previous.coda, "[ln]$", {["l"] = "ʎ", ["n"] = "ɲ"})
				end
			end
			
			-- ɡʒ > d͡ʒ
			if previous.coda == "ɡ" and current.onset == "ʒ" then
				previous.coda = ""
				current.onset = "d͡ʒ"
			end
		end
		
		current.coda = mw.ustring.gsub(current.coda, "[mn]([fv])", "ɱ%1")
		
		current.coda = mw.ustring.gsub(current.coda, "n[kɡ]", "ŋk")
		
		current.coda = mw.ustring.gsub(current.coda, "n([ʃʒ])", "ɲ%1")
		current.coda = mw.ustring.gsub(current.coda, "n(t͡ʃ)", "ɲ%1")
		current.coda = mw.ustring.gsub(current.coda, "n(d͡ʒ)", "ɲ%1")
		
		current.coda = mw.ustring.gsub(current.coda, "lʃ", "ʎʃ")
		current.coda = mw.ustring.gsub(current.coda, "l(t͡ʃ)", "ʎ%1")
	
		-- Voicing or devoicing
		if i > 1 then
			local coda_letter = mw.ustring.sub(previous.coda, -1)
			local onset_letter = mw.ustring.sub(current.onset, 1, 1)
			if voiced[onset_letter] and voicing[coda_letter] then
				previous.coda = mw.ustring.gsub(previous.coda, coda_letter .. "$", voicing[coda_letter])
			elseif voiceless[onset_letter] and devoicing[coda_letter] then
				previous.coda = mw.ustring.gsub(previous.coda, coda_letter .. "$", devoicing[coda_letter])
			end
			previous.coda = mw.ustring.gsub(previous.coda, "[bd]s", {["bs"] = "ps", ["ds"] = "ts"})
		end
		
		-- Allophones of r
		if i == 1 then
			current.onset = mw.ustring.gsub(current.onset, "^ɾ", "r")
		end
		
		if i > 1 and mw.ustring.find(previous.coda, "[ln]$") then
			current.onset = mw.ustring.gsub(current.onset, "^ɾ", "r")
		end
		
	end
	
	-- Final devoicing
	local final = syllables[#syllables].coda
	
	final = mw.ustring.gsub(final, "d͡ʒ", "t͡ʃ")
	final = mw.ustring.gsub(final, "d͡z", "t͡s")
	final = mw.ustring.gsub(final, "b", "p")
	final = mw.ustring.gsub(final, "d", "t")
	final = mw.ustring.gsub(final, "ɡ", "k")
	final = mw.ustring.gsub(final, "ʒ", "ʃ")
	final = mw.ustring.gsub(final, "v", "f")
	final = mw.ustring.gsub(final, "z", "s")
	
	-- Final loses
	final = mw.ustring.gsub(final, "j(t͡ʃ)$", "%1")
	final = mw.ustring.gsub(final, "([ʃs])s", "%1") -- homophone plurals -xs, -çs
	
	syllables[#syllables].coda = final
	
	return syllables
end

local function to_IPA(syllables, mid_vowel_hint)
	-- Ambiguous stressed vowel
	if mw.ustring.find(syllables[syllables.stress].vowel, "[eéèoòó]") then
		if mid_vowel_hint then
			syllables[syllables.stress].vowel = mid_vowel_hint
		end
	end
	
	local syllables_IPA = {stress = syllables.stress}
	
	for key, val in ipairs(syllables) do
		syllables_IPA[key] = {onset = val.onset, vowel = val.vowel, coda = val.coda, stressed = val.stressed}
	end
	
	-- Replace letters with IPA equivalents
	for i, syll in ipairs(syllables_IPA) do
		-- Voicing of s
		if syll.onset == "s" and i > 1 and (syllables[i-1].coda == "" or syllables[i-1].coda == "i" or syllables[i-1].coda == "u") then
			syll.onset = "z"
		end
		
		if mw.ustring.find(syll.vowel, "^[eèéêëēií]$") then
			syll.onset = mw.ustring.gsub(syll.onset, "tg$", "d͡ʒ")
			syll.onset = mw.ustring.gsub(syll.onset, "[cg]$", {["c"] = "s", ["g"] = "ʒ"})
			syll.onset = mw.ustring.gsub(syll.onset, "[qg]u$", {["qu"] = "k", ["gu"] = "ɡ"})
		end
		
		syll.coda = mw.ustring.gsub(syll.coda, "ig(s?)$", "id͡ʒ%1")
		
		syll.onset = replace_context_free(syll.onset)
		syll.coda = replace_context_free(syll.coda)
		
		syll.vowel = mw.ustring.gsub(syll.vowel, ".", IPA_vowels)
	end
	
	syllables_IPA = postprocess_general(syllables_IPA)
	
	return syllables_IPA
end

local function reduction_ae(syllables)
	for i = 1, #syllables do
		local current = syllables[i]
		local previous = syllables[i - 1] or {onset = "", vowel = "", coda = ""}
		local posterior = syllables[i + 1] or {onset = "", vowel = "", coda = ""}
		
		local pre_vowel_pair = previous.vowel .. previous.coda .. current.onset .. current.vowel
		local post_vowel_pair = current.vowel .. current.coda .. posterior.onset .. posterior.vowel
		local reduction = true
		
		if current.stressed then
			reduction = false
		elseif pre_vowel_pair == "əe" then
			reduction = false
		elseif post_vowel_pair == "ea" or post_vowel_pair == "eɔ" then
			reduction = false
		elseif i < syllables.stress -1 and post_vowel_pair == "ee" then
			posterior.vowel = "ə" -- avoid əe in next loop
		elseif i > syllables.stress and post_vowel_pair == "ee" then
			reduction = false
		elseif pre_vowel_pair == "oe" or pre_vowel_pair == "ɔe" then
			reduction = false
		end
		
		if reduction then
			current.vowel = mw.ustring.gsub(current.vowel, "[ae]", "ə")
		end
	end
	return syllables
end

local function nasalAssim(onset, coda) -- tm>mm, tn>nn, pm>mm
	if string.find(coda, "d$") and string.find(onset, "m") then
		return string.gsub(coda, "d$", "m")
	elseif string.find(coda, "d$") and string.find(onset, "n") then
		return string.gsub(coda, "d$", "n")
	elseif string.find(coda, "b$") and string.find(onset, "m") then
		return string.gsub(coda, "b$", "m")
	elseif mw.ustring.find(coda, "ɡ$") and string.find(onset, "n") then
		-- velarization -gn-, -cn-
		return mw.ustring.gsub(coda, "ɡ", "ŋ")
	end
	return coda
end

-- Central
local function accentCentral(syl, variant)
	local syl1 = mw.clone(syl)
	
	-- Reduction of unstressed vowels a,e
	syl1 = reduction_ae(syl1)
	
	-- Final consonant losses
	local final = syl1[#syl1].coda
	
	final = mw.ustring.gsub(final, "^ɾ(s?)$", "%1") -- no loss with hint -rr
	final = mw.ustring.gsub(final, "mp$", "m") -- -mp/-mb
	final = mw.ustring.gsub(final, "([ln])t$", "%1") -- -lt/-nt/-ld/-nd
	final = mw.ustring.gsub(final, "[nŋ][kɡ](s?)$", "ŋ%1")
	final = mw.ustring.gsub(final, "ɾts$", "ɾs")
	final = mw.ustring.gsub(final, "jt͡ʃ$", "jt͡s") -- iodització v+igs: raigs > rajts
	
	syl1[#syl1].coda = final
	
	for i = 1, #syl1 do
		local current = syl1[i]
		local previous = syl1[i-1]
		
		if current.stressed then
			current.vowel = mw.ustring.gsub(current.vowel, "[êëô]", {["ê"] = "ɛ", ["ë"] = "ɛ", ["ô"] = "ɔ"})
		end
		
		-- Reduction of unstressed o
		if current.vowel == "o" and not (current.stressed or current.coda == "w") then
			current.vowel = mw.ustring.gsub(current.vowel, "o", "u")
		end
		
		-- ɫ + ʒ (-lj-/-lg-) > palatalized assimilation
		if current.onset == "ʒ" and previous then
			previous.coda = mw.ustring.gsub(previous.coda, "l$", "ʎ")
		end
		current.coda = mw.ustring.gsub(current.coda, "lʒ", "ʎʒ")
		
		-- nasal assimilations
		if previous then previous.coda = nasalAssim(current.onset, previous.coda) end
		
		-- v > b
		if mw.ustring.find(current.onset, "v") then
			current.onset = mw.ustring.gsub(current.onset, "v", "b")
			if i > 1 and mw.ustring.find(current.onset, "^b") then
				previous.coda = mw.ustring.gsub(previous.coda, "[ɱn]$", "m")
			end
			current.coda = mw.ustring.gsub(current.coda, "[ɱn]b", "mb")
		end
		
		-- allophones of r
		current.coda = mw.ustring.gsub(current.coda, "ɾ", "r")
		
		-- Poststressed gemination bl, gl
		if i > 1 and (current.onset == "bl" or current.onset == "ɡl") and previous.coda == "" and previous.stressed then
			previous.coda = mw.ustring.sub(current.onset, 1, 1)
		end
		
		-- Remove j before palatal obstruents
		current.coda = mw.ustring.gsub(current.coda, "j([ʃʒ])", "%1")
		current.coda = mw.ustring.gsub(current.coda, "j(t͡ʃ)", "%1")
		current.coda = mw.ustring.gsub(current.coda, "j(d͡ʒ)", "%1")
		
		if i > 1
		and (mw.ustring.find(current.onset, "^[ʃʒ]")
			or mw.ustring.find(current.onset, "^t͡ʃ")
			or mw.ustring.find(current.onset, "^d͡ʒ")
		)
		then
			previous.coda = mw.ustring.gsub(previous.coda, "j$", "")
		end
	end
	
	return syl1
end

local function join_syllables(syllables)
	syllables = mw.clone(syllables)
	
	for i, syll in ipairs(syllables) do
		syll.vowel = mw.ustring.gsub(syll.vowel, "[āēō]", {["ā"] = "a", ["ē"] = "e", ["ō"] = "o"})
		syll = syll.onset .. syll.vowel .. syll.coda
		
		if i == syllables.stress then -- primary stress
			syll = "ˈ" .. syll
		elseif syllables[i].stressed then -- secondary stress
			syll = "ˌ" .. syll
		end
		
		syllables[i] = syll
	end
	
	return "/" .. mw.ustring.gsub(table.concat(syllables, "."), "%.([ˈˌ.])", "%1") .. "/"
end

local function showIPA(word)
	word = mw.ustring.lower(mw.text.trim(word))
	local mid_vowel_hint = nil
	if word == "é" or word == "è" or word == "ê" or word == "ë" or word == "ó" or word == "ò" or word == "ô" then
		mid_vowel_hint = word
		word = mw.ustring.lower(mw.title.getCurrentTitle().text)
	end
	
	word = word_fixes(word)
	
	local syllables = split_syllables(word)
	
	if mid_vowel_hint == nil then
		if mw.ustring.find(syllables[syllables.stress].vowel, "[éêëòóô]") then
			mid_vowel_hint = mw.ustring.match(syllables[syllables.stress].vowel, "[éêëòóô]")
		elseif syllables[syllables.stress].vowel == "e" then
			mid_vowel_hint = "é"
		elseif syllables[syllables.stress].vowel == "o" then
			mid_vowel_hint = "ó"
		end
	end
	syllables = to_IPA(syllables, mid_vowel_hint)
	
	return join_syllables(accentCentral(syllables))
end

-- on debug console use: =p.debug("your_word", "your_hint")
function p.debug(word, mid_vowel_hint)
	word = word_fixes(mw.ustring.lower(word))
	
	local syllables = split_syllables(word)
	
	if mid_vowel_hint == nil then
		if mw.ustring.find(syllables[syllables.stress].vowel, "[éêëòóô]") then
			mid_vowel_hint = mw.ustring.match(syllables[syllables.stress].vowel, "[éêëòóô]")
		elseif syllables[syllables.stress].vowel == "e" then
			mid_vowel_hint = "é"
		elseif syllables[syllables.stress].vowel == "o" then
			mid_vowel_hint = "ó"
		end
	end
	
	syllables = to_IPA(syllables, mid_vowel_hint)
	
	return join_syllables(accentCentral(syllables))
end

local function format_afi(text)
	return '<span class="IPA" title="pronúncia AFI">' .. text .. '</span>'
end

function p.plantilla(frame)
	local args = frame:getParent().args
	local pagename = mw.ustring.lower(mw.title.getCurrentTitle().subpageText)
	local namespace = mw.title.getCurrentTitle().nsText
	
	local pron = {}
	if args[1] and string.find(args[1], "/") then
		pron[1] = args[1]
	end
	pron[2] = args[2]
	pron[3] = args[3]
	
	local heading = ':*<span style="font-weight: bold;">Pronúncia</span>: '
	if pron[1] == nil then
		if namespace == "Plantilla" then
			pron.gen = "/pɾuˈnun.si.ə/"
		else
			-- pronúncia automàtica
			pron[1] = showIPA(args[1] or pagename)
			if pron[1] == '' then
				pron[1] = '<span style="font-size: small";>(pendent)</span>'
				if namespace == "" then
					pron[1] = pron[1] .. '[[Categoria:Entrades en caló català amb pronúncia pendent]]'
				end
				return heading .. pron[1]
			end
		end
	end
	
	-- Formatació
	local ret = {}
	
	if pron[1] then
		table.insert(ret, format_afi(pron[1]))
		if pron[2] then
			table.insert(ret, ', ' .. format_afi(pron[2]))
			if pron[3] then
				table.insert(ret, ', ' .. format_afi(pron[3]))
			end
		end
	end
	
	return heading .. table.concat(ret)
end

return p