Module:av-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 export = {}
local stress = "ˈ"
local long = "ː"
local acute = mw.ustring.char(0x301)
local grave = mw.ustring.char(0x300)
local circumflex = mw.ustring.char(0x302)
local acute_or_grave = "[" .. acute .. grave .. "]"
local vowels = "aeɛioɔu"
local vowel = "[" .. vowels .. "]"
local vowel_or_semivowel = "[" .. vowels .. "jw]"
local not_vowel = "[^" .. vowels .. "]"
local front = "[eɛij]"
local fronted = mw.ustring.char(0x031F)
local voiced_consonant = "[bdɡlmnrv]"
local full_affricates = { ["ʦ"] = "t͡s", ["ʣ"] = "d͡z", ["ʧ"] = "t͡ʃ", ["ʤ"] = "d͡ʒ" }
-- ʦ, ʣ, ʧ, ʤ used for
-- t͡s, d͡z, t͡ʃ, d͡ʒ in body of function.
-- voiced_z must be a table of integer indices, a boolean, or nil.
function export.to_phonemic(word, voiced_z, single_character_affricates)
word = mw.ustring.lower(word):gsub("'", "")
if type(word) == 'table' then
-- Decompose combining characters: for instance, è → e + ◌̀
do_debug = word.args[4]
local decomposed = mw.ustring.toNFD(word):gsub("x", "ks"):gsub("y", "i")
word = word.args[1]
:gsub("ck", "k"):gsub("sh", "ʃ"):gsub("ng$", "ŋ")
local all_z_voiced
if type(voiced_z) == "boolean" then
all_z_voiced = voiced_z
voiced_z = nil
else
require "libraryUtil".checkTypeMulti("to_IPA", 2, voiced_z,
{ "table", "boolean", "nil" })
end
end
local orig_word = word
word = mw.ustring.lower(word or mw.title.getCurrentTitle().text)
word = mw.ustring.gsub(word, "[^abcdefgilmnoprstuv.]", "")
table.insert(debug, word)
-- Transcriptions must contain an acute or grave, to indicate stress position.
-- This does not handle phrases containing more than one stressed word.
-- Default to penultimate stress rather than throw error?
local vowel_count
if not mw.ustring.find(decomposed, acute_or_grave) then
-- Allow monosyllabic unstressed words.
vowel_count = select(2, decomposed:gsub("[aeiou]", "%1"))
if vowel_count ~= 1 then
-- Add acute accent on second-to-last vowel.
decomposed = mw.ustring.gsub(decomposed,
"(" .. vowel .. ")(" .. not_vowel .. "*[iu]?" .. vowel .. not_vowel .. "*)$",
"%1" .. acute .. "%2")
end
end
local V = "[aeiou]" -- vowel
local transcription = decomposed
local C = "[^aeiou.]" -- consonant
-- Assume that aw is English.
transcription = mw.ustring.gsub(
transcription,
"a(" .. grave .. "?)w",
{ [""] = vowel_count == 1 and "ɔ" or "o", [grave] = "ɔ"})
-- Handle è, ò.
transcription = transcription:gsub("([eo])(" .. grave .. ")",
function (vowel, accent)
return ({ e = "ɛ", o = "ɔ" })[vowel] .. accent
end) -- e or o followed by grave
-- ci, gi + vowel
-- Do ci, gi + e, é, è sometimes contain /j/?
transcription = mw.ustring.gsub(transcription,
"([cg])([cg]?)i(" .. vowel .. ")",
function (consonant, double, vowel)
local out_consonant
if consonant == "c" then
out_consonant = "ʧ"
else
out_consonant = "ʤ"
end
if double ~= "" then
if double ~= consonant then
error("Invalid sequence " .. consonant .. double .. ".")
end
out_consonant = out_consonant .. out_consonant
end
return out_consonant .. vowel
end)
--"c" & "g" before "i" and "e" and all that stuff
-- Handle gl and gn.
word = mw.ustring.gsub(word, "c([ie])" .. "t͡ʃ%1")
transcription = mw.ustring.gsub(transcription,
word = mw.ustring.gsub(word, "g([ie])", "d͡ʒ%1")
"(g[nl])(.?)()",
word = mw.ustring.gsub(word, "t([i])" .. "t͡s%1")
function (digraph, after, pos)
word = mw.ustring.gsub(word, "sc([ie])" .. "ʃ%1")
local consonant
if digraph == "gn" then
consonant = "ɲ"
-- gli is /ʎi/, or /ʎ/ before a vowel
elseif after == "i" then
consonant = "ʎ"
local following = mw.ustring.sub(transcription, pos, pos)
if following ~= "" and vowels:find(following) then
after = ""
end
end
if consonant then
return consonant .. after
end
end)
 
-- Handle other cases of c, g.
table.insert(debug, word)
transcription = mw.ustring.gsub(transcription,
"(([cg])([cg]?)(h?))(.?)",
function (consonant, first, double, second, next)
-- Don't allow the combinations cg, gc.
-- Or do something else?
if double ~= "" and double ~= first then
error("Invalid sequence " .. first .. double .. ".")
end
-- c, g is soft before e, i.
local consonant
if (next == "e" or next == "ɛ" or next == "i") and second ~= "h" then
if first == "c" then
consonant = "ʧ"
else
consonant = "ʤ"
end
else
if first == "c" then
consonant = "k"
else
consonant = "ɡ"
end
end
if double ~= "" then
consonant = consonant .. consonant
end
return consonant .. next
end)
--alphabet-to-phoneme
-- ⟨qu⟩ represents /kw/.
word = mw.ustring.gsub(word, "ch", "k")
transcription = transcription:gsub("qu", "kw")
word = mw.ustring.gsub(word, "sc","ʃ")
word = mw.ustring.gsub(word, '[cg]',
--['g']='ɡ':  U+0067 LATIN SMALL LETTER G → U+0261 LATIN SMALL LETTER SCRIPT G
{['c']='k', ['g']='ɡ'})
word = mw.ustring.gsub(word, 'n([bm])', 'm%1')
-- u or i (without accent) before another vowel is a semivowel.
word = mw.ustring.gsub(word, 'z', LatinAmerica and 'z' or 'θ') -- not the real LatAm sound
-- ci, gi + vowel, gli, qu must be dealt with beforehand.
transcription = mw.ustring.gsub(transcription,
"([iu])(" .. vowel .. ")",
function (semivowel, vowel)
if semivowel == "i" then
semivowel = "j"
else
semivowel = "w"
end
return semivowel .. vowel
end)
table.insert(debug, word)
-- sc before e, i is /ʃ/, doubled after a vowel.
transcription = transcription:gsub("sʧ", "ʃ")
--syllable division
-- ⟨z⟩ represents /t͡s/ or /d͡z/; no way to determine which.
for _ = 1, 2 do
-- For now, /t͡s/ is the default.
word = mw.ustring.gsub(word,
local before_izzare = mw.ustring.match(
"(" .. V .. ")(" .. C .. W .. "?" .. V .. ")",
transcription,
"%1.%2")
"(.-" .. vowel .. not_vowel .. "*)izza" .. acute_or_grave .. "?re$")
if before_izzare then
transcription = before_izzare
end
end
for _ = 1, 2 do
word = mw.ustring.gsub(word,
local z_index = 0
"(" .. V .. C .. ")(" .. C .. V .. ")",
transcription = mw.ustring.gsub(
"%1.%2")
transcription,
"()(z+)(.?)",
function (pos, z, after)
local length = #z
if length > 2 then
error("Too many z's in a row!")
end
z_index = z_index + 1
local voiced = voiced_z and require "Module:table".contains(voiced_z, z_index)
or all_z_voiced
if pos == 1 then
if mw.ustring.find(transcription, "^[ij]" .. acute_or_grave .. "?" .. vowel, pos + #z) then
voiced = false
elseif mw.ustring.find(transcription, "^" .. vowel .. acute_or_grave .. "?" .. vowel, pos + #z) then
voiced = true
end
-- check whether followed by two vowels
-- check onset of next syllable
else
if mw.ustring.find(after, vowel_or_semivowel) then
local before = mw.ustring.sub(transcription, pos - 2, pos - 1)
if mw.ustring.find(before, vowel_or_semivowel .. acute_or_grave .. "?$") then
if length == 1 and mw.ustring.find(after, vowel)
and mw.ustring.find(before, vowel) then
voiced = true
end
length = 2
end
if mw.ustring.sub(transcription, pos + #z, pos + #z + 1) == "i" .. circumflex then
voiced = false
end
end
end
return (voiced and "ʣ" or "ʦ"):rep(length) .. after
end)
if before_izzare then
transcription = transcription .. mw.ustring.toNFD("iʣʣàre")
end
end
for _ = 1, 2 do
word = mw.ustring.gsub(word,
"(" .. V .. C .. ")(" .. C .. C .. V .. ")",
"%1.%2")
end
word = mw.ustring.gsub(word, "([pbktdɡ])%.([lɾ])", ".%1%2")
word = mw.ustring.gsub(word, "(" .. C .. ")%.s(" .. C .. ")", "%1s.%2")
word = mw.ustring.gsub(word, "([aeo])([aeo])", "%1.%2")
word = mw.ustring.gsub(word, "([i])([i])", "%1.%2")
word = mw.ustring.gsub(word, "([u])([u])", "%1.%2")
table.insert(debug, word)
--syllables nasalized if ending with "n", voiceless consonants in syllable-final position to voiced
-- Replace acute and grave with stress mark.
local remove_accent = { ['á'] = 'a', ['é'] = 'e', ['í'] = 'i', ['ó'] = 'o', ['ú'] = 'u'}
transcription = mw.ustring.gsub(transcription,
local nasalize = { ['a'] = 'ã', ['e'] = 'ẽ', ['i'] = 'ĩ', ['o'] = 'õ', ['u'] = 'ũ' }
"(" .. vowel .. ")" .. acute_or_grave, stress .. "%1")
for i = 1, #syllables do
syllables[i] = mw.ustring.gsub(syllables[i], '[áéíóú]', remove_accent)
-- Single ⟨s⟩ between vowels is /z/.
if phonetic and mw.ustring.find(syllables[i], '[mnɲ]' .. C .. '?$') then
transcription = mw.ustring.gsub(transcription,
syllables[i] = mw.ustring.gsub(syllables[i], '[aeiou]', nasalize)
"(" .. vowel .. ")s(" .. stress .. "?" .. vowel .. ")", "%1z%2")
end
syllables[i] = mw.ustring.gsub(syllables[i], '[ptk]$', { ['p'] = 'b', ['t'] = 'd', ['k'] = 'ɡ' })
-- ⟨s⟩ immediately before a voiced consonant is always /z/
end
transcription = mw.ustring.gsub(transcription,
word = table.concat(syllables)
"s(" .. voiced_consonant .. ")", "z%1")
-- After a vowel, /ʃ ʎ ɲ/ are doubled.
-- [[w:Italian phonology]] says word-internally, [[w:Help:IPA/Italian]] says
-- after a vowel.
transcription = mw.ustring.gsub(transcription,
"(" .. vowel .. ")([ʃʎɲ])", "%1%2%2")
-- Move stress before syllable onset, and add syllable breaks.
-- This rule may need refinement.
transcription = mw.ustring.gsub(transcription,
"()(" .. not_vowel .. "?)([^" .. vowels .. stress .. "]*)(" .. stress
.. "?)(" .. vowel .. ")",
function (position, first, rest, syllable_divider, vowel)
-- beginning of word, that is, at the moment, beginning of string
if position == 1 then
return syllable_divider .. first .. rest .. vowel
end
if syllable_divider == "" then
syllable_divider = "."
end
if rest == "" then
return syllable_divider .. first .. vowel
else
return first .. syllable_divider .. rest .. vowel
end
end)
--real sound of LatAm Z
if not single_character_affricates then
word = mw.ustring.gsub(word, 'z', 's')
transcription = mw.ustring.gsub(transcription, "([ʦʣʧʤ])([%." .. stress .. "]*)([ʦʣʧʤ]*)",
--secondary stress
function (affricate1, divider, affricate2)
word = mw.ustring.gsub(word, 'ˈ(.+)ˈ', 'ˌ%1ˈ')
local full_affricate = full_affricates[affricate1]
word = mw.ustring.gsub(word, 'ˈ(.+)ˌ', 'ˌ%1ˌ')
word = mw.ustring.gsub(word, 'ˌ(.+)ˈ(.+)ˈ', 'ˌ%1ˌ%2ˈ')
if affricate2 ~= "" then
 
return mw.ustring.sub(full_affricate, 1, 1) .. divider .. full_affricate
--phonetic transcription
if phonetic then
--θ, s, f before voiced consonants
local voiced = 'mnɲbdɟɡʎ'
local r = 'ɾr'
local tovoiced = {
['θ'] = 'θ̬',
['s'] = 'z',
['f'] = 'v',
}
local function voice(sound, following)
return tovoiced[sound]..following
end
word = mw.ustring.gsub(word, '([θs])([ˈˌ]?['..voiced..r..'])', voice)
word = mw.ustring.gsub(word, '(f)([ˈˌ]?['..voiced..'])', voice)
local stop_to_fricative = {['b']='β', ['d']='ð', ['ɟ']='ʝ', ['ɡ']='ɣ'}
local fricative_to_stop = {['β']='b', ['ð']='d', ['ʝ']='ɟ', ['ɣ']='ɡ'}
--lots of allophones going on
word = mw.ustring.gsub(word, '[bdɟɡ]', stop_to_fricative)
word = mw.ustring.gsub(
word,
'()([ˈˌ]?)([βðɣʝ])',
function (pos, stress, fricative)
-- Matching the character before the fricative in the pattern
-- doesn't work because sometimes there are two fricatives in
-- a row.
local before = pos > 1 and mw.ustring.sub(word, pos - 1, pos - 1)
-- mw.log(orig_word, before, stress, fricative)
if not before or (fricative == 'ɣ' or fricative == 'β') and ('mnɲ'):find(before)
or (fricative == 'ð' or fricative == 'ʝ') and ('lʎmnɲ'):find(before) then
return stress .. fricative_to_stop[fricative]
end -- else no change
end)
word = mw.ustring.gsub(word, '[td]', {['t']='t̪', ['d']='d̪'})
--nasal assimilation before consonants
local labiodental, dentialveolar, dental, alveolopalatal, palatal, velar =
'ɱ', 'n̪', 'n̟', 'nʲ', 'ɲ', 'ŋ'
local nasal_assimilation = {
['f'] = labiodental,
['t'] = dentialveolar, ['d'] = dentialveolar,
['θ'] = dental,
['ʃ'] = alveolopalatal,
['ɟ'] = palatal, ['ʎ'] = palatal,
['k'] = velar, ['x'] = velar, ['ɡ'] = velar,
}
word = mw.ustring.gsub(
word,
'n([ˈˌ]?)(.)',
function (stress, following)
return (nasal_assimilation[following] or 'n') .. stress .. following
end)
--lateral assimilation before consonants
word = mw.ustring.gsub(
word,
'l([ˈˌ]?)(.)',
function (stress, following)
local l = 'l'
if following == 't' or following == 'd' then -- dentialveolar
l = 'l̪'
elseif following == 'θ' then -- dental
l = 'l̟'
elseif following == 'ʃ' then -- alveolopalatal
l = 'lʲ'
end
end
return l .. stress .. following
return full_affricate .. divider
end)
end)
--semivowels
word = mw.ustring.gsub(word, '([aeouãẽõũ][iïĩ])', '%1̯')
word = mw.ustring.gsub(word, '([aeioãẽĩõ][uũ])', '%1̯')
end
end
table.insert(debug, word)
transcription = mw.ustring.gsub(transcription, "[h%-" .. circumflex .. "]", "")
transcription = transcription:gsub("%.ˈ", "ˈ")
return transcription
end
 
-- Incomplete and currently not used by any templates.
function export.to_phonetic(word, voiced_z)
local phonetic = export.to_phonemic(word, voiced_z)
word = mw.ustring.gsub(word, 'h', '') --silent "h"
-- Vowels longer in stressed, open, non-word-final syllables.
word = mw.ustring.gsub(word, 'ʃ', 't͡ʃ') --fake "ch" to real "ch"
phonetic = mw.ustring.gsub(phonetic,
word = mw.ustring.gsub(word, 'ɟ', 'ɟ͡ʝ') --fake "y" to real "y"
"(" .. stress .. not_vowel .. "*" .. vowel .. ")([" .. vowels .. "%.])",
word = mw.ustring.gsub(word, 'ï', 'i') --fake "y$" to real "y$"
"%1" .. long .. "%2")
if do_debug == 'yes' then
-- /n/ before /ɡ/ or /k/ is [ŋ]
return word .. table.concat(debug, "")
phonetic = mw.ustring.gsub(phonetic,
else
"n([%.ˈ]?[ɡk])", "ŋ%1")
return word
end


 
-- Imperfect: doesn't convert geminated k, g properly.
function export.phonetic(frame)
phonetic = mw.ustring.gsub(phonetic,
return export.show(frame, false, true)
"([kg])(" .. front .. ")",
"%1" .. fronted .. "%2")
:gsub("a", "ä")
:gsub("n", "n̺") -- Converts n before a consonant, which is incorrect.
return phonetic
end
end


function export.phoneticLatinAmerica(frame)
function export.show(frame)
return export.show(frame, true, true)
local m_IPA = require "Module:IPA"
local args = require "Module:parameters".process(
frame:getParent().args,
{
-- words to transcribe
[1] = { list = true, default = mw.title.getCurrentTitle().text },
-- each parameter a series of numbers separated by commas,
-- or a boolean, indicating that a particular z is voiced or
-- that all of them are
voiced = { list = true },
})
local Array = require "Module:array"
local voiced_z = Array(args.voiced)
:map(function (param)
param = Array(mw.text.split(param, "%s*,%s*"))
:map(
function (item, i)
return tonumber(item)
or i == 1 and require "Module:yesno"(item) -- Rejects false values.
or error("Invalid input '" .. item .."' in |voiced= parameter. "
.. "Expected number or boolean.")
end)
if not param[2] and type(param[1]) == "boolean" then
param = param[1]
end
return param
end)
local transcriptions = Array(args[1])
:map(
function (word, i)
return { pron = "/" .. export.to_phonemic(word, voiced_z[i]) .. "/" }
end)
return m_IPA.format_IPA_full(
require "Module:languages".getByCode "it", transcriptions)
end
end


return export
return export

Revision as of 14:37, 9 November 2019

Documentation for this module may be created at Module:av-pron/doc

local export = {}

local stress = "ˈ"
local long = "ː"
local acute = mw.ustring.char(0x301)
local grave = mw.ustring.char(0x300)
local circumflex = mw.ustring.char(0x302)
local acute_or_grave = "[" .. acute .. grave .. "]"
local vowels = "aeɛioɔu"
local vowel = "[" .. vowels .. "]"
local vowel_or_semivowel = "[" .. vowels .. "jw]"
local not_vowel = "[^" .. vowels .. "]"
local front = "[eɛij]"
local fronted = mw.ustring.char(0x031F)
local voiced_consonant = "[bdɡlmnrv]"

local full_affricates = { ["ʦ"] = "t͡s", ["ʣ"] = "d͡z", ["ʧ"] = "t͡ʃ", ["ʤ"] = "d͡ʒ" }

-- ʦ, ʣ, ʧ, ʤ used for
-- t͡s, d͡z, t͡ʃ, d͡ʒ in body of function.

-- voiced_z must be a table of integer indices, a boolean, or nil.
function export.to_phonemic(word, voiced_z, single_character_affricates)
	word = mw.ustring.lower(word):gsub("'", "")
	
	-- Decompose combining characters: for instance, è → e + ◌̀
	local decomposed = mw.ustring.toNFD(word):gsub("x", "ks"):gsub("y", "i")
		:gsub("ck", "k"):gsub("sh", "ʃ"):gsub("ng$", "ŋ")
	local all_z_voiced
	if type(voiced_z) == "boolean" then
		all_z_voiced = voiced_z
		voiced_z = nil
	else
		require "libraryUtil".checkTypeMulti("to_IPA", 2, voiced_z,
			{ "table", "boolean", "nil" })
	end
	
	-- Transcriptions must contain an acute or grave, to indicate stress position.
	-- This does not handle phrases containing more than one stressed word.
	-- Default to penultimate stress rather than throw error?
	local vowel_count
	if not mw.ustring.find(decomposed, acute_or_grave) then
		-- Allow monosyllabic unstressed words.
		vowel_count = select(2, decomposed:gsub("[aeiou]", "%1"))
		if vowel_count ~= 1 then
			-- Add acute accent on second-to-last vowel.
			decomposed = mw.ustring.gsub(decomposed, 
				"(" .. vowel .. ")(" .. not_vowel .. "*[iu]?" .. vowel .. not_vowel .. "*)$",
				"%1" .. acute .. "%2")
		end
	end
	
	local transcription = decomposed
	
	-- Assume that aw is English.
	transcription = mw.ustring.gsub(
		transcription,
		"a(" .. grave .. "?)w",
		{ [""] = vowel_count == 1 and "ɔ" or "o", [grave] = "ɔ"})
	
	-- Handle è, ò.
	transcription = transcription:gsub("([eo])(" .. grave .. ")",
		function (vowel, accent)
			return ({ e = "ɛ", o = "ɔ" })[vowel] .. accent
		end) -- e or o followed by grave
	
	-- ci, gi + vowel
	-- Do ci, gi + e, é, è sometimes contain /j/?
	transcription = mw.ustring.gsub(transcription,
		"([cg])([cg]?)i(" .. vowel .. ")",
		function (consonant, double, vowel)
			local out_consonant
			if consonant == "c" then
				out_consonant = "ʧ"
			else
				out_consonant = "ʤ"
			end
			
			if double ~= "" then
				if double ~= consonant then
					error("Invalid sequence " .. consonant .. double .. ".")
				end
				
				out_consonant = out_consonant .. out_consonant
			end
			
			return out_consonant .. vowel
		end)
	
	-- Handle gl and gn.
	transcription = mw.ustring.gsub(transcription,
		"(g[nl])(.?)()",
		function (digraph, after, pos)
			local consonant
			if digraph == "gn" then
				consonant = "ɲ"
			
			-- gli is /ʎi/, or /ʎ/ before a vowel
			elseif after == "i" then
				consonant = "ʎ"
				
				local following = mw.ustring.sub(transcription, pos, pos)
				if following ~= "" and vowels:find(following) then
					after = ""
				end
			end
			
			if consonant then
				return consonant .. after
			end
		end)
	
	-- Handle other cases of c, g.
	transcription = mw.ustring.gsub(transcription,
		"(([cg])([cg]?)(h?))(.?)",
		function (consonant, first, double, second, next)
			-- Don't allow the combinations cg, gc.
			-- Or do something else?
			if double ~= "" and double ~= first then
				error("Invalid sequence " .. first .. double .. ".")
			end
			
			-- c, g is soft before e, i.
			local consonant
			if (next == "e" or next == "ɛ" or next == "i") and second ~= "h" then
				if first == "c" then
					consonant = "ʧ"
				else
					consonant = "ʤ"
				end
			else
				if first == "c" then
					consonant = "k"
				else
					consonant = "ɡ"
				end
			end
			
			if double ~= "" then
				consonant = consonant .. consonant
			end
			
			return consonant .. next
		end)
	
	-- ⟨qu⟩ represents /kw/.
	transcription = transcription:gsub("qu", "kw")
	
	-- u or i (without accent) before another vowel is a semivowel.
	-- ci, gi + vowel, gli, qu must be dealt with beforehand.
	transcription = mw.ustring.gsub(transcription,
		"([iu])(" .. vowel .. ")",
		function (semivowel, vowel)
			if semivowel == "i" then
				semivowel = "j"
			else
				semivowel = "w"
			end
			
			return semivowel .. vowel
		end)
	
	-- sc before e, i is /ʃ/, doubled after a vowel.
	transcription = transcription:gsub("sʧ", "ʃ")
	
	-- ⟨z⟩ represents /t͡s/ or /d͡z/; no way to determine which.
	-- For now, /t͡s/ is the default.
	local before_izzare = mw.ustring.match(
		transcription,
		"(.-" .. vowel .. not_vowel .. "*)izza" .. acute_or_grave .. "?re$")
	if before_izzare then
		transcription = before_izzare
	end
	
	local z_index = 0
	transcription = mw.ustring.gsub(
		transcription,
		"()(z+)(.?)",
		function (pos, z, after)
			local length = #z
			if length > 2 then
				error("Too many z's in a row!")
			end
			
			z_index = z_index + 1
			local voiced = voiced_z and require "Module:table".contains(voiced_z, z_index)
					or all_z_voiced
			
			if pos == 1 then
				if mw.ustring.find(transcription, "^[ij]" .. acute_or_grave .. "?" .. vowel, pos + #z) then
					voiced = false
				elseif mw.ustring.find(transcription, "^" .. vowel .. acute_or_grave .. "?" .. vowel, pos + #z) then
					voiced = true
				end
				-- check whether followed by two vowels
				-- check onset of next syllable
			else
				if mw.ustring.find(after, vowel_or_semivowel) then
					
					local before = mw.ustring.sub(transcription, pos - 2, pos - 1)
					
					if mw.ustring.find(before, vowel_or_semivowel .. acute_or_grave .. "?$") then
						if length == 1 and mw.ustring.find(after, vowel)
						and mw.ustring.find(before, vowel) then
							voiced = true
						end
						
						length = 2
					end
					
					if mw.ustring.sub(transcription, pos + #z, pos + #z + 1) == "i" .. circumflex then
						voiced = false
					end
				end
			end
			
			return (voiced and "ʣ" or "ʦ"):rep(length) .. after
		end)
	
	if before_izzare then
		transcription = transcription .. mw.ustring.toNFD("iʣʣàre")
	end
	
	-- Replace acute and grave with stress mark.
	transcription = mw.ustring.gsub(transcription,
		"(" .. vowel .. ")" .. acute_or_grave, stress .. "%1")
	
	-- Single ⟨s⟩ between vowels is /z/.
	transcription = mw.ustring.gsub(transcription,
		"(" .. vowel .. ")s(" .. stress .. "?" .. vowel .. ")", "%1z%2")
	
	-- ⟨s⟩ immediately before a voiced consonant is always /z/
	transcription = mw.ustring.gsub(transcription,
		"s(" .. voiced_consonant .. ")", "z%1")
	
	-- After a vowel, /ʃ ʎ ɲ/ are doubled.
	-- [[w:Italian phonology]] says word-internally, [[w:Help:IPA/Italian]] says
	-- after a vowel.
	transcription = mw.ustring.gsub(transcription,
		"(" .. vowel .. ")([ʃʎɲ])", "%1%2%2")
	
	-- Move stress before syllable onset, and add syllable breaks.
	-- This rule may need refinement.
	transcription = mw.ustring.gsub(transcription,
		"()(" .. not_vowel .. "?)([^" .. vowels .. stress .. "]*)(" .. stress
			.. "?)(" .. vowel .. ")",
		function (position, first, rest, syllable_divider, vowel)
			-- beginning of word, that is, at the moment, beginning of string
			if position == 1 then
				return syllable_divider .. first .. rest .. vowel
			end
			
			if syllable_divider == "" then
				syllable_divider = "."
			end
			
			if rest == "" then
				return syllable_divider .. first .. vowel
			else
				return first .. syllable_divider .. rest .. vowel
			end
		end)
	
	if not single_character_affricates then
		transcription = mw.ustring.gsub(transcription, "([ʦʣʧʤ])([%." .. stress .. "]*)([ʦʣʧʤ]*)",
			function (affricate1, divider, affricate2)
				local full_affricate = full_affricates[affricate1]
				
				if affricate2 ~= "" then
					return mw.ustring.sub(full_affricate, 1, 1) .. divider .. full_affricate
				end
				
				return full_affricate .. divider
			end)
	end
	
	transcription = mw.ustring.gsub(transcription, "[h%-" .. circumflex .. "]", "")
	transcription = transcription:gsub("%.ˈ", "ˈ")
	
	return transcription
end

-- Incomplete and currently not used by any templates.
function export.to_phonetic(word, voiced_z)
	local phonetic = export.to_phonemic(word, voiced_z)
	
	-- Vowels longer in stressed, open, non-word-final syllables.
	phonetic = mw.ustring.gsub(phonetic,
		"(" .. stress .. not_vowel .. "*" .. vowel .. ")([" .. vowels .. "%.])",
		"%1" .. long .. "%2")
	
	-- /n/ before /ɡ/ or /k/ is [ŋ]
	phonetic = mw.ustring.gsub(phonetic,
		"n([%.ˈ]?[ɡk])", "ŋ%1")

	-- Imperfect: doesn't convert geminated k, g properly.
	phonetic = mw.ustring.gsub(phonetic,
			"([kg])(" .. front .. ")",
			"%1" .. fronted .. "%2")
		:gsub("a", "ä")
		:gsub("n", "n̺") -- Converts n before a consonant, which is incorrect.
	
	return phonetic
end

function export.show(frame)
	local m_IPA = require "Module:IPA"
	
	local args = require "Module:parameters".process(
		frame:getParent().args,
		{
			-- words to transcribe
			[1] = { list = true, default = mw.title.getCurrentTitle().text },
			
			-- each parameter a series of numbers separated by commas,
			-- or a boolean, indicating that a particular z is voiced or
			-- that all of them are
			voiced = { list = true },
		})
	
	local Array = require "Module:array"
	
	local voiced_z = Array(args.voiced)
		:map(function (param)
			param = Array(mw.text.split(param, "%s*,%s*"))
				:map(
					function (item, i)
						return tonumber(item)
							or i == 1 and require "Module:yesno"(item) -- Rejects false values.
							or error("Invalid input '" .. item .."' in |voiced= parameter. "
								.. "Expected number or boolean.")
					end)
			
			if not param[2] and type(param[1]) == "boolean" then
				param = param[1]
			end
			
			return param
		end)
	
	local transcriptions = Array(args[1])
		:map(
			function (word, i)
				return { pron = "/" .. export.to_phonemic(word, voiced_z[i]) .. "/" }
			end)
	
	return m_IPA.format_IPA_full(
		require "Module:languages".getByCode "it", transcriptions)
end

return export