Module:pl-IPA

local export = {}

local m_IPA = require("Module:IPA")

local lang = require("Module:languages").getByCode("pl")

local letters2phones = { ["a"] = { ["u"] = { "a", "w" }, [false] = "a", },	["ą"] = { ["l"] = { "ɔ", "l" }, ["ł"] = { "ɔ", "w" }, [false] = "ɔ̃", },	["b"] = { ["i"] = { ["ą"] = { ["l"] = { "bʲ", "j", "ɔ", "l" }, ["ł"] = { "bʲ", "j", "ɔ", "w" }, [false] = { "bʲ", "j", "ɔ̃" } },			["ę"] = { ["l"] = { "bʲ", "j", "ɛ", "l" }, ["ł"] = { "bʲ", "j", "ɛ", "w" }, [false] = { "bʲ", "j", "ɛ̃" } },			["a"] = { "bʲ", "j", "a" }, ["e"] = { "bʲ", "j", "ɛ" }, ["i"] = { "bʲ", "i" }, ["o"] = { "bʲ", "j", "ɔ" }, ["ó"] = { "bʲ", "j", "u" }, ["u"] = { "bʲ", "j", "u" }, [false] = { "bʲ", "i" } },		[false] = "b" },	["c"] = { ["i"] = { ["ą"] = { ["l"] = { "t͡ɕ", "ɔ", "l" }, ["ł"] = { "t͡ɕ", "ɔ", "w" }, [false] = { "t͡ɕ", "ɔ̃" } },			["ę"] = { ["l"] = { "t͡ɕ", "ɛ", "l" }, ["ł"] = { "t͡ɕ", "ɛ", "w" }, [false] = { "t͡ɕ", "ɛ̃" } },			["a"] = { "t͡ɕ", "a" }, ["e"] = { "t͡ɕ", "ɛ" }, ["o"] = { "t͡ɕ", "ɔ" }, ["ó"] = { "t͡ɕ", "u" }, ["u"] = { "t͡ɕ", "u" }, ["y"] = { "t͡ɕ", "ɨ" }, [false] = { "t͡ɕ", "i" } },		["h"] = { ["i"] = { ["ą"] = { ["l"] = { "xʲ", "j", "ɔ", "l" }, ["ł"] = { "xʲ", "j", "ɔ", "w" }, [false] = { "xʲ", "j", "ɔ̃" } },				["ę"] = { ["l"] = { "xʲ", "j", "ɛ", "l" }, ["ł"] = { "xʲ", "j", "ɛ", "w" }, [false] = { "xʲ", "j", "ɛ̃" } },				["a"] = { "xʲ", "j", "a" }, ["e"] = { "xʲ", "j", "ɛ" }, ["i"] = { "xʲ", "j", "i" }, ["o"] = { "xʲ", "j", "ɔ" }, ["ó"] = { "xʲ", "j", "u" }, ["u"] = { "xʲ", "j", "u" }, [false] = { "xʲ", "i" } },			[false] = "x" },		["z"] = "t͡ʂ", [false] = "t͡s" },	["ć"] = "t͡ɕ", ["d"] = { ["i"] = { ["ą"] = { ["l"] = { "dʲ", "j", "ɔ", "l" }, ["ł"] = { "dʲ", "j", "ɔ", "w" }, [false] = { "dʲ", "j", "ɔ̃" } },			["ę"] = { ["l"] = { "dʲ", "j", "ɛ", "l" }, ["ł"] = { "dʲ", "j", "ɛ", "w" }, [false] = { "dʲ", "j", "ɛ̃" } },			["a"] = { "dʲ", "j", "a" }, ["e"] = { "dʲ", "j", "ɛ" }, ["i"] = { "dʲ", "i" }, ["o"] = { "dʲ", "j", "ɔ" }, ["ó"] = { "dʲ", "j", "u" }, ["u"] = { "dʲ", "j", "u" }, [false] = { "dʲ", "i" } },		["z"] = { ["i"] = { ["ą"] = { ["l"] = { "d͡ʑ", "ɔ", "l" }, ["ł"] = { "d͡ʑ", "ɔ", "w" }, [false] = {"d͡ʑ", "ɔ̃" } },				["ę"] = { ["l"] = { "d͡ʑ", "ɛ", "l" }, ["ł"] = { "d͡ʑ", "ɛ", "w" }, [false] = { "d͡ʑ", "ɛ̃" } },				["a"] = { "d͡ʑ", "a" }, ["e"] = { "d͡ʑ", "ɛ" }, ["o"] = { "d͡ʑ", "ɔ" }, ["ó"] = { "d͡ʑ", "u" }, ["u"] = { "d͡ʑ", "u" }, ["y"] = { "d͡ʑ", "ɨ" }, [false] = { "d͡ʑ", "i" } },			[false] = "d͡z" },		["ż"] = "d͡ʐ", ["ź"] = "d͡ʑ", [false] = "d" },	["e"] = { ["u"] = { "ɛ", "w" }, [false] = "ɛ", },	["ę"] = { ["l"] = { "ɛ", "l" }, ["ł"] = { "ɛ", "w" }, [false] = "ɛ̃", },	["f"] = { ["i"] = { ["ą"] = { ["l"] = { "fʲ", "j", "ɔ", "l" }, ["ł"] = { "fʲ", "j", "ɔ", "w" }, [false] = { "fʲ", "j", "ɔ̃" } },			["ę"] = { ["l"] = { "fʲ", "j", "ɛ", "l" }, ["ł"] = { "fʲ", "j", "ɛ", "w" }, [false] = { "fʲ", "j", "ɛ̃" } },			["a"] = { "fʲ", "j", "a" }, ["e"] = { "fʲ", "j", "ɛ" }, ["i"] = { "fʲ", "j", "i" }, ["o"] = { "fʲ", "j", "ɔ" }, ["ó"] = { "fʲ", "j", "u" }, ["u"] = { "fʲ", "j", "u" }, [false] = { "fʲ", "i" } },		[false] = "f" },	["g"] = { ["i"] = { ["ą"] = { ["l"] = { "ɡʲ", "j", "ɔ", "l" }, ["ł"] = { "ɡʲ", "j", "ɔ", "w" }, [false] = { "ɡʲ", "j", "ɔ̃" } },			["ę"] = { ["l"] = { "ɡʲ", "j", "ɛ", "l" }, ["ł"] = { "ɡʲ", "j", "ɛ", "w" }, [false] = { "ɡʲ", "j", "ɛ̃" } },			["a"] = { "ɡʲ", "j", "a" }, ["e"] = { "ɡʲ", "j", "ɛ" }, ["i"] = { "ɡʲ", "j", "i" }, ["o"] = { "ɡʲ", "j", "ɔ" }, ["ó"] = { "ɡʲ", "j", "u" }, ["u"] = { "ɡʲ", "j", "u" }, [false] = { "ɡʲ", "i" } },		[false] = "ɡ" },	["h"] = { ["i"] = { ["ą"] = { ["l"] = { "xʲ", "j", "ɔ", "l" }, ["ł"] = { "xʲ", "j", "ɔ", "w" }, [false] = { "xʲ", "j", "ɔ̃" } },			["ę"] = { ["l"] = { "xʲ", "j", "ɛ", "l" }, ["ł"] = { "xʲ", "j", "ɛ", "w" }, [false] = { "xʲ", "j", "ɛ̃" } },			["a"] = { "xʲ", "j", "a" }, ["e"] = { "xʲ", "j", "ɛ" }, ["i"] = { "xʲ", "j", "i" }, ["o"] = { "xʲ", "j", "ɔ" }, ["ó"] = { "xʲ", "j", "u" }, ["u"] = { "xʲ", "j", "u" }, [false] = { "xʲ", "i" } },		[false] = "x" },	["i"] = "i", ["j"] = "j", ["k"] = { ["i"] = { ["ą"] = { ["l"] = { "kʲ", "j", "ɔ", "l" }, ["ł"] = { "kʲ", "j", "ɔ", "w" }, [false] = { "kʲ", "j", "ɔ̃" } },			["ę"] = { ["l"] = { "kʲ", "j", "ɛ", "l" }, ["ł"] = { "kʲ", "j", "ɛ", "w" }, [false] = { "kʲ", "j", "ɛ̃" } },			["a"] = { "kʲ", "j", "a" }, ["e"] = { "kʲ", "j", "ɛ" }, ["i"] = { "kʲ", "j", "i" }, ["o"] = { "kʲ", "j", "ɔ" }, ["ó"] = { "kʲ", "j", "u" }, ["u"] = { "kʲ", "j", "u" }, [false] = { "kʲ", "i" } },		[false] = "k" },	["l"] = { ["i"] = { ["ą"] = { ["l"] = { "lʲ", "j", "ɔ", "l" }, ["ł"] = { "lʲ", "j", "ɔ", "w" }, [false] = { "lʲ", "j", "ɔ̃" } },			["ę"] = { ["l"] = { "lʲ", "j", "ɛ", "l" }, ["ł"] = { "lʲ", "j", "ɛ", "w" }, [false] = { "lʲ", "j", "ɛ̃" } },			["a"] = { "lʲ", "j", "a" }, ["e"] = { "lʲ", "j", "ɛ" }, ["i"] = { "lʲ", "j", "i" }, ["o"] = { "lʲ", "j", "ɔ" }, ["ó"] = { "lʲ", "j", "u" }, ["u"] = { "lʲ", "j", "u" }, [false] = { "lʲ", "i" } },		[false] = "l" },	["ł"] = "w", ["m"] = { ["i"] = { ["ą"] = { ["l"] = { "mʲ", "j", "ɔ", "l" }, ["ł"] = { "mʲ", "j", "ɔ", "w" }, [false] = { "mʲ", "j", "ɔ̃" } },			["ę"] = { ["l"] = { "mʲ", "j", "ɛ", "l" }, ["ł"] = { "mʲ", "j", "ɛ", "w" }, [false] = { "mʲ", "j", "ɛ̃" } },			["a"] = { "mʲ", "j", "a" }, ["e"] = { "mʲ", "j", "ɛ" }, ["i"] = { "mʲ", "j", "i" }, ["o"] = { "mʲ", "j", "ɔ" }, ["ó"] = { "mʲ", "j", "u" }, ["u"] = { "mʲ", "j", "u" }, [false] = { "mʲ", "i" } },		[false] = "m" },	["n"] = { ["k"] = { ["i"] = { ["ą"] = { ["l"] = { "ŋ", "kʲ", "j", "ɔ", "l" }, ["ł"] = { "ŋ", "kʲ", "j", "ɔ", "w" }, [false] = { "ŋ", "kʲ", "j", "ɔ̃" } },				["ę"] = { ["l"] = { "ŋ", "kʲ", "j", "ɛ", "l" }, ["ł"] = { "ŋ", "kʲ", "j", "ɛ", "w" }, [false] = { "ŋ", "kʲ", "j", "ɛ̃" } },				["a"] = { "ŋ", "kʲ", "j", "a" }, ["e"] = { "ŋ", "kʲ", "j", "ɛ" }, ["i"] = { "ŋ", "kʲ", "j", "i" }, ["o"] = { "ŋ", "kʲ", "j", "ɔ" }, ["ó"] = { "ŋ", "kʲ", "j", "u" }, ["u"] = { "ŋ", "kʲ", "j", "u" }, [false] = { "ŋ", "kʲ", "i" } },			[false] = { "ŋ", "k" } },		["g"] = { ["i"] = { ["ą"] = { ["l"] = { "ŋ", "ɡʲ", "j", "l" }, ["ł"] = { "ŋ", "ɡʲ", "j", "w" }, [false] = { "ŋ", "ɡʲ", "j", "ɔ̃" } },				["ę"] = { ["l"] = { "ŋ", "ɡʲ", "j", "ɛ", "l" }, ["ł"] = { "ŋ", "ɡʲ", "j", "ɛ", "w" }, [false] = { "ŋ", "ɡʲ", "j", "ɛ̃" } },				["a"] = { "ŋ", "ɡʲ", "j", "a" }, ["e"] = { "ŋ", "ɡʲ", "j", "ɛ" }, ["i"] = { "ŋ", "ɡʲ", "j", "i" }, ["o"] = { "ŋ", "ɡʲ", "j", "ɔ" }, ["ó"] = { "ŋ", "ɡʲ", "j", "u" }, ["u"] = { "ŋ", "ɡʲ", "j", "u" }, [false] = { "ŋ", "ɡʲ", "i" } },			[false] = { "ŋ", "ɡ" } },		["i"] = { ["ą"] = { ["l"] = { "ɲ", "ɔ", "l" }, ["ł"] = { "ɲ", "ɔ", "w" }, [false] = { "ɲ", "ɔ̃" } },			["ę"] = { ["l"] = { "ɲ", "ɛ", "l" }, ["ł"] = { "ɲ", "ɛ", "w" }, [false] = { "ɲ", "ɛ̃" } },			["a"] = { "ɲ", "a" }, ["e"] = { "ɲ", "ɛ" }, ["i"] = { "ɲ", "j", "i" }, ["o"] = { "ɲ", "ɔ" }, ["ó"] = { "ɲ", "u" }, ["u"] = { "ɲ", "u" }, [false] = { "ɲ", "i" } },		[false] = "n" },	["ń"] = "ɲ", ["o"] = { [false] = "ɔ", },	["ó"] = "u", ["p"] = { ["i"] = { -- piątek, piasek, etc.			["ą"] = { ["l"] = { "pʲ", "j", "ɔ", "l" }, ["ł"] = { "pʲ", "j", "ɔ", "w" }, [false] = { "pʲ", "j", "ɔ̃" } },			["ę"] = { ["l"] = { "pʲ", "j", "ɛ", "l" }, ["ł"] = { "pʲ", "j", "ɛ", "w" }, [false] = { "pʲ", "j", "ɛ̃" } },			["a"] = { "pʲ", "j", "a" }, ["e"] = { "pʲ", "j", "ɛ" }, ["i"] = { "pʲ", "j", "i" }, ["o"] = { "pʲ", "j", "ɔ" }, ["ó"] = { "pʲ", "j", "u" }, ["u"] = { "pʲ", "j", "u" }, [false] = { "pʲ", "i" } },		[false] = "p" },	["r"] = { ["i"] = { ["ą"] = { ["l"] = { "rʲ", "j", "ɔ", "l" }, ["ł"] = { "rʲ", "j", "ɔ", "w" }, [false] = { "rʲ", "j", "ɔ̃" } },			["ę"] = { ["l"] = { "rʲ", "j", "ɛ", "l" }, ["ł"] = { "rʲ", "j", "ɛ", "w" }, [false] = { "rʲ", "j", "ɛ̃" } },			["a"] = { "rʲ", "j", "a" }, ["e"] = { "rʲ", "j", "ɛ" }, ["i"] = { "rʲ", "j", "i" }, ["o"] = { "rʲ", "j", "ɔ" }, ["ó"] = { "rʲ", "j", "u" }, ["u"] = { "rʲ", "j", "u" }, [false] = { "rʲ", "i" } },		["z"] = "ʐ", [false] = "r" },	["q"] = { ["u"] = { "k", "v" }, [false] = false },	["s"] = { ["i"] = { ["ą"] = { ["l"] = { "ɕ", "ɔ", "l" }, ["ł"] = { "ɕ", "ɔ", "w" }, [false] = { "ɕ", "ɔ̃" } },			["ę"] = { ["l"] = { "ɕ", "ɛ", "l" }, ["ł"] = { "ɕ", "ɛ", "w" }, [false] = { "ɕ", "ɛ̃" } },			["a"] = { "ɕ", "a" }, ["e"] = { "ɕ", "ɛ" }, ["o"] = { "ɕ", "ɔ" }, ["ó"] = { "ɕ", "u" }, ["u"] = { "ɕ", "u" }, ["y"] = { "ɕ", "ɨ" }, [false] = { "ɕ", "i" } },		["z"] = "ʂ", [false] = "s", },	["ś"] = "ɕ", ["t"] = { ["i"] = { ["ą"] = { ["l"] = { "tʲ", "j", "ɔ", "l" }, ["ł"] = { "tʲ", "j", "ɔ", "w" }, [false] = { "tʲ", "j", "ɔ̃" } },			["ę"] = { ["l"] = { "tʲ", "j", "ɛ", "l" }, ["ł"] = { "tʲ", "j", "ɛ", "w" }, [false] = { "tʲ", "j", "ɛ̃" } },			["a"] = { "tʲ", "j", "a" }, ["e"] = { "tʲ", "j", "ɛ" }, ["i"] = { "tʲ", "i" }, ["o"] = { "tʲ", "j", "ɔ" }, ["ó"] = { "tʲ", "j", "u" }, ["u"] = { "tʲ", "j", "u" }, [false] = { "tʲ", "i" } },		[false] = "t" },	["u"] = "u", ["v"] = { ["i"] = { ["ą"] = { ["l"] = { "vʲ", "j", "ɔ", "l" }, ["ł"] = { "vʲ", "j", "ɔ", "w" }, [false] = { "vʲ", "j", "ɔ̃" } },			["ę"] = { ["l"] = { "vʲ", "j", "ɛ", "l" }, ["ł"] = { "vʲ", "j", "ɛ", "w" }, [false] = { "vʲ", "j", "ɛ̃" } },			["a"] = { "vʲ", "j", "a" }, ["e"] = { "vʲ", "j", "ɛ" }, ["i"] = { "vʲ", "j", "i" }, ["o"] = { "vʲ", "j", "ɔ" }, ["ó"] = { "vʲ", "j", "u" }, ["u"] = { "vʲ", "j", "u" }, [false] = { "vʲ", "i" } },		[false] = "v" },	["w"] = { ["i"] = { ["ą"] = { ["l"] = { "vʲ", "j", "ɔ", "l" }, ["ł"] = { "vʲ", "j", "ɔ", "w" }, [false] = { "vʲ", "j", "ɔ̃" } },			["ę"] = { ["l"] = { "vʲ", "j", "ɛ", "l" }, ["ł"] = { "vʲ", "j", "ɛ", "w" }, [false] = { "vʲ", "j", "ɛ̃" } },			["a"] = { "vʲ", "j", "a" }, ["e"] = { "vʲ", "j", "ɛ" }, ["i"] = { "vʲ", "j", "i" }, ["o"] = { "vʲ", "j", "ɔ" }, ["ó"] = { "vʲ", "j", "u" }, ["u"] = { "vʲ", "j", "u" }, [false] = { "vʲ", "i" } },		[false] = "v" },	["x"] = { "k", "s" }, ["y"] = "ɨ", ["z"] = { ["i"] = { ["ą"] = { ["l"] = { "ʑ", "ɔ", "l" }, ["ł"] = { "ʑ", "ɔ", "w" }, [false] = { "ʑ", "ɔ̃" } },			["ę"] = { ["l"] = { "ʑ", "ɛ", "l" }, ["ł"] = { "ʑ", "ɛ", "w" }, [false] = { "ʑ", "ɛ̃" } },			["a"] = { "ʑ", "a" }, ["e"] = { "ʑ", "ɛ" }, ["o"] = { "ʑ", "ɔ" }, ["ó"] = { "ʑ", "u" }, ["u"] = { "ʑ", "u" }, [false] = { "ʑ", "i" } },		[false] = "z" },	["ź"] = "ʑ", ["ż"] = "ʐ", ["-"] = {}, }

local valid_phone = { ["a"] = true, ["b"] = true, ["bʲ"] = true, ["d"] = true, ["dʲ"] = true, ["d͡z"] = true, ["d͡ʑ"] = true, ["d͡ʐ"] = true, ["ɛ"] = true, ["ɛ̃"] = true, ["f"] = true, ["fʲ"] = true, ["ɡ"] = true, ["ɡʲ"] = true, ["i"] = true, ["ɨ"] = true, ["j"] = true, ["k"] = true, ["kʲ"] = true, ["l"] = true, ["lʲ"] = true, ["m"] = true, ["mʲ"] = true, ["n"] = true, ["ŋ"] = true, ["ɲ"] = true, ["ɔ"] = true, ["ɔ̃"] = true, ["p"] = true, ["pʲ"] = true, ["r"] = true, ["rʲ"] = true, ["s"] = true, ["ɕ"] = true, ["ʂ"] = true, ["t"] = true, ["tʲ"] = true, ["t͡s"] = true, ["t͡ɕ"] = true, ["t͡ʂ"] = true, ["u"] = true, ["v"] = true, ["vʲ"] = true, ["w"] = true, ["w̃"] = true, ["x"] = true, ["xʲ"] = true, ["z"] = true, ["ʑ"] = true, ["ʐ"] = true, ["ɣ"] = true }

local sylmarks = { ["."] = ".", ["'"] = "ˈ", ["ˈ"] = "ˈ", [","] = "ˌ" }

local vowel = { ["a"] = true, ["ɛ"] = true, ["ɛ̃"] = true, ["i"] = true, ["ɨ"] = true, ["ɔ"] = true, ["ɔ̃"] = true, ["u"] = true }

local devoice = { ["b"] = "p", ["d"] = "t", ["d͡z"] = "t͡s", ["d͡ʑ"] = "t͡ɕ", ["d͡ʐ"] = "t͡ʂ", ["ɡ"] = "k", ["v"] = "f", ["vʲ"] = "fʲ", ["z"] = "s", ["ʑ"] = "ɕ", ["ʐ"] = "ʂ",

-- non-devoicable ["bʲ"] = "bʲ", ["dʲ"] = "dʲ", ["ɡʲ"] = "ɡʲ", ["m"] = "m", ["mʲ"] = "mʲ", ["n"] = "n", ["ɲ"] = "ɲ", ["ŋ"] = "ŋ", ["w"] = "w", ["w̃"] = "w̃", ["l"] = "l", ["lʲ"] = "lʲ", ["j"] = "j", ["r"] = "r", ["rʲ"] = "rʲ", ["tʲ"] = "tʲ", }

local voice = { ["p"] = "b", ["t"] = "d", ["t͡s"] = "d͡z", ["t͡ɕ"] = "d͡ʑ", ["t͡ʂ"] = "d͡ʐ", ["k"] = "ɡ", ["f"] = "v", ["fʲ"] = "vʲ", ["s"] = "z", ["ɕ"] = "ʑ", ["ʂ"] = "ʐ", ["x"] = "ɣ",

-- non-voicable ["bʲ"] = "bʲ", ["dʲ"] = "dʲ", ["ɡʲ"] = "ɡʲ", ["m"] = "m", ["mʲ"] = "mʲ", ["n"] = "n", ["ɲ"] = "ɲ", ["ŋ"] = "ŋ", ["w"] = "w", ["w̃"] = "w̃", ["l"] = "l", ["lʲ"] = "lʲ", ["j"] = "j", ["r"] = "r", ["rʲ"] = "rʲ", ["tʲ"] = "tʲ", }

local forward_assimilants = { ["v"] = true, ["vʲ"] = true }

local denasalized = { ["ɛ̃"] = "ɛ", ["ɔ̃"] = "ɔ", }

local nasal_map = { ["p"] = "m", ["pʲ"] = "m", ["b"] = "m", ["bʲ"] = "m", -- zębu, klępa ["k"] = "ŋ", ["kʲ"] = "ŋ", ["ɡ"] = "ŋ", ["ɡʲ"] = "ŋ", -- pąk, łęgowy ["t"] = "n", ["d"] = "n", -- wątek, piątek, mądrość

["t͡ɕ"] = "ɲ", ["d͡ʑ"] = "ɲ", ["ɕ"] = "ɲ", ["ʑ"] = "ɲ", -- pięć, pędziwiatr, łabędź -- gęsi, więzi ["t͡ʂ"] = "n", ["d͡ʐ"] = "n", -- pączek, ? -- węszyć, mężny ["t͡s"] = "n", ["d͡z"] = "n", -- wiedząc, pieniędzy }

local SPECIAL_FLAGS = { IS_RZ = "IS_RZ", }

local third_last_syllable_stress = { "łbym", "łabym", "łbyś", "łabyś", "łby", "łaby", "łoby", "liby", "łyby", }

local fourth_last_syllable_stress = { "libyśmy", "łybyśmy", "libyście", "łybyście", }

--- -- Check whether phone doesn't change due to voicing/devoicing ---@param phone string ---@return boolean local function is_neutral(phone) return (devoice[phone] and voice[phone]) and (voice[phone] == devoice[phone]) end

--- -- Check whether phone is a special character (syllable mark or word boundary) ---@param phone string ---@return boolean local function is_special(phone) return phone == " " or sylmarks[phone] end

--- -- Check whether phone is voiced ---@param phone string ---@return boolean local function is_voiced(phone) return devoice[phone] and phone ~= devoice[phone] end

--- -- Check whether phone is prone to forward assimilation ---@param phone string ---@param flags table Special flags for this phone ---@return boolean local function is_forward_assimilant(phone, flags) return forward_assimilants[phone] or (flags and flags[SPECIAL_FLAGS.IS_RZ]) end

--- -- Check whether phone cluster is a palatalized cluster ---@param cluster string ---@return boolean local function is_palatalized_cluster(cluster) return cluster:find("[ɡxkfbmprvdtl]ʲj[aɔ̃ɛɛ̃iɔu]") ~= nil end

--- -- Process special flags for grapheme and associate them with the recorded phone ---@param grapheme string ---@return table | nil local function process_special_flags(grapheme) if grapheme == "rz" then return { [SPECIAL_FLAGS.IS_RZ] = true } end end

--- -- Convert letters and graphemes to phones ---@param word string ---@return table, table> local function convert_to_phones(word) local phones = {} local flags = {} local chbuf = "" local function append_phone(phone) table.insert(phones, phone)

-- mark rz for assimilation later local grapheme_flags = process_special_flags(chbuf) if grapheme_flags then flags[#phones] = grapheme_flags end chbuf = "" end

local l2ptab = letters2phones for ch in mw.ustring.gmatch(mw.ustring.lower(word), ".") do		local value = l2ptab[ch]

if value == nil then value = l2ptab[false] if value == false then return nil elseif type(value) == "table" then for _, phone in ipairs(value) do					append_phone(phone) end else append_phone(value) end l2ptab = letters2phones value = l2ptab[ch] end

chbuf = chbuf .. ch

if type(value) == "table" then if value[false] == nil then for _, phone in ipairs(value) do					append_phone(phone) end l2ptab = letters2phones else l2ptab = value end elseif type(value) == "string" then append_phone(value) l2ptab = letters2phones else append_phone(ch) end end

if l2ptab ~= letters2phones then local value = l2ptab[false] if type(value) == "table" then for _, phone in ipairs(value) do				append_phone(phone) end else append_phone(value) end end

return phones, flags end

--- -- Simplify nasals ---@param phones table ---@return table local function simplify_nasals(phones, flags) local new_phones, new_flags = {}, {} for i, phone in ipairs(phones) do		if denasalized[phone] then local pnext = phones[i + 1] if sylmarks[pnext] then pnext = phones[i + 2] end if phone == "ɛ̃" and (not pnext or not valid_phone[pnext]) then -- denasalize word-final ę table.insert(new_phones, denasalized[phone]) new_flags[#new_phones] = flags[i] elseif nasal_map[pnext] then table.insert(new_phones, denasalized[phone]) table.insert(new_phones, nasal_map[pnext]) new_flags[#new_phones] = flags[i] else table.insert(new_phones, phone) new_flags[#new_phones] = flags[i] end else table.insert(new_phones, phone) new_flags[#new_phones] = flags[i] end end return new_phones, new_flags end

--- -- Devoice consonant phones in terminal positions ---@param phones table Target phone table to mutate local function terminal_devoice(phones) local final_phone = phones[#phones] if is_voiced(final_phone) then phones[#phones] = devoice[final_phone] end end

--- -- Process consonant cluster assimilation for single cluster ---@param cluster table Consonant cluster ---@param flags table> Flags relative to the cluster ---@param new_phones table Target phone table to mutate local function process_consonant_cluster(cluster, flags, new_phones) local determining_index = #cluster while cluster[determining_index] do		local candidate = cluster[determining_index] -- Skip forward assimilants and neutral phones to find the first voiced/devoiced consonant which decides the entire cluster if not is_forward_assimilant(candidate, flags[determining_index]) and not is_neutral(candidate) and not is_special(candidate) then break end determining_index = determining_index - 1 end

-- If the cluster ends up being comprised of just neutral phones and forward assimilants, add it as-is if determining_index == 0 then for _, consonant in ipairs(cluster) do			table.insert(new_phones, consonant) end return end

-- Transform the entire cluster, forward and back, relative to the determining consonant's voicing local determining_consonant = cluster[determining_index] local target_map = is_voiced(determining_consonant) and voice or devoice

for _, consonant in ipairs(cluster) do		local transformed = target_map[consonant] or consonant table.insert(new_phones, transformed) end end

--- -- Process consonant cluster assimilation for single cluster ---@param phones table ---@param flags table> ---@return table local function process_consonant_clusters(phones, flags) local new_phones = {} local i = 1 while i <= #phones do		local pcurr, pnext = phones[i] if not valid_phone[pcurr] or vowel[pcurr] then -- Other phone encountered, add it as-is table.insert(new_phones, pcurr) else -- Consonant cluster to process local cluster = {} -- Phone flags indexed relative to the cluster local cluster_flags = {}

-- Search forward for consonant cluster local j = i			while j <= #phones do				pnext = phones[j]

-- Break on vowel or invalid symbol and process what we have if vowel[pnext] or (not valid_phone[pnext] and not is_special(pnext)) then break end

table.insert(cluster, pnext) -- Set the cluster-relative flag for the latest processed phoneme cluster_flags[#cluster] = flags[j]

j = j + 1 end

if #cluster > 0 then if #cluster > 1 then -- Process actual consonant cluster process_consonant_cluster(cluster, cluster_flags, new_phones) -- Skip forward past the processed phones to avoid any unwanted duplication -- Offset by 1 to compensate, because i is unconditionally incremented by 1 at the very end i = j - 1 else -- The cluster is a single consonant, add it as-is table.insert(new_phones, cluster[1]) end end end i = i + 1 end return new_phones end

--- -- Join several phones together, handling table and nil values ---@vararg string | table Phones to join together ---@return string local function join_phones(...) local args = {...} local str = "" for _, syllable in ipairs(args) do		if type(syllable) == "table" then str = str .. table.concat(syllable, "") else str = str .. (syllable or "") end end return str end

--- -- Group phones into syllables ---@param phones table ---@return table local function collect_syllables(phones) local words, curword, sylmarked, sylbuf, had_vowl = {}, nil, false for i, pcurr in ipairs(phones) do		local pprev, pnext, pnnext = phones[i - 1], phones[i + 1], phones[i + 2]

if valid_phone[pcurr] then if not curword then curword, sylbuf, had_vowl, sylmarked = {}, '', false, false table.insert(words, curword) end

local same_syl = true

if vowel[pcurr] then if had_vowl then same_syl = false end had_vowl = true elseif had_vowl then if vowel[pnext] then same_syl = false elseif not vowel[pprev] and not vowel[pnext] then same_syl = false elseif vowel[pprev] and is_palatalized_cluster(join_phones(pcurr, pnext, pnnext)) then same_syl = false elseif ((pcurr == "s") and ((pnext == "t") or (pnext == "p") or (pnext == "k"))) or (pnext == "r") or (pnext == "f") or (pnext == "w") or ((pcurr == "ɡ") and (pnext == "ʐ")) or ((pcurr == "d") and ((pnext == "l") or (pnext == "w") or (pnext == "ɲ"))) or is_palatalized_cluster(join_phones(pprev, pcurr, pnext)) then -- these should belong to a common syllable same_syl = true end end

if same_syl then sylbuf = sylbuf .. pcurr else table.insert(curword, sylbuf) sylbuf, had_vowl = pcurr, vowel[pcurr] end elseif (curword or valid_phone[pnext]) and sylmarks[pcurr] then if not curword then curword, sylbuf, had_vowl = {}, '', false table.insert(words, curword) end sylmarked = true if sylbuf then table.insert(curword, sylbuf) sylbuf = '' end table.insert(curword, sylmarks[pcurr]) else if sylbuf then if #curword > 0 and not had_vowl then curword[#curword] = curword[#curword] .. sylbuf else table.insert(curword, sylbuf) end if sylmarked then words[#words] = table.concat(curword) end end curword, sylbuf = nil, nil table.insert(words, pcurr) end end if sylbuf then if #curword > 0 and not had_vowl then curword[#curword] = curword[#curword] .. sylbuf else table.insert(curword, sylbuf) end if sylmarked then words[#words] = table.concat(curword) end end

return words end

local function get_stressed_syllable(word) local stressed_syllable = 1 for i,v in ipairs(third_last_syllable_stress) do		if word:sub(-string.len(v)) == v 		then stressed_syllable = 2 end end for i,v in ipairs(fourth_last_syllable_stress) do		if word:sub(-string.len(v)) == v 		then stressed_syllable = 3 end end return stressed_syllable end

local function is_more_than_one_word(word) if string.find(word, " ") then return true else return false end end

function export.convert_to_IPA(word) local stressed_syllable = get_stressed_syllable(word) local more_than_one_word = is_more_than_one_word(word) local phones, flags = convert_to_phones(word) phones, flags = simplify_nasals(phones, flags) terminal_devoice(phones) phones = process_consonant_clusters(phones, flags) local words = collect_syllables(phones) -- mark syllable breaks and stress for i, word in ipairs(words) do		if type(word) == "table" then -- unless already marked if not ((word[2] == ".") or (word[2] == "ˈ") or (word[2] == "ˌ")) then for j, syl in ipairs(word) do					if not more_than_one_word then if #word < stressed_syllable+1 then stressed_syllable = #word-1 end end if #word > 1 then if j == (#word - stressed_syllable) then word[j] = "ˈ" .. syl elseif j ~= 1 then word[j] = "." .. syl end end end end words[i] = table.concat(word) end end for i, word in ipairs(words) do		-- get rid of /ʲ/ words[i] = mw.ustring.gsub(words[i], "ʲ([ij])", "%1") words[i] = mw.ustring.gsub(words[i], "ʲ", "j") -- replace /ɔ̃/ and /ɛ̃/ with /ɔw̃/ and /ɛw̃/ words[i] = mw.ustring.gsub(words[i], "ɛ̃", "ɛw̃") words[i] = mw.ustring.gsub(words[i], "ɔ̃", "ɔw̃") replace /n/ with /w̃/ before /s, z, ʂ, ʐ, ɕ, ʑ/ (currently turned off) -- words[i] = mw.ustring.gsub(words[i], "n([szʂʐɕʑ])", "w̃%1") -- words[i] = mw.ustring.gsub(words[i], "n([ˈˌ.])([szʂʐɕʑ])", "w̃%1%2") end

return table.concat(words) end

function export.show(frame) local page_title = mw.title.getCurrentTitle.text

local args = require("Module:parameters").process(frame:getParent.args, {		[1] = { list = true },		["qual"] = { list = true, allow_holes = true },		["n"] = { list = true, allow_holes = true },	})

local Array = require("Module:array")

local words if next(args[1]) ~= nil then words = args[1] else words = { page_title } end

local transcriptions = Array(words):map(function(word, i)		local qualifiers = { args.qual[i] }		return {			pron = "/" .. export.convert_to_IPA(word) .. "/",			qualifiers = #qualifiers > 0 and qualifiers or nil,			note = args.n[i]		}	end)

return m_IPA.format_IPA_full { lang = lang, items = transcriptions } end

return export