Xiang Chinese pronunciation module. See {{zh-pron}} and Wiktionary:About Chinese/Xiang.


local export = {}
local m_string_utils = require("Module:string utilities")

local sub = m_string_utils.sub
local find = m_string_utils.find
local match = m_string_utils.match

local initialConv = {
	["b"] = "p", ["p"] = "pʰ", ["m"] = "m", ["f"] = "ɸ", 
	["d"] = "t", ["t"] = "tʰ", ["l"] = "l", 
	["z"] = "t͡s", ["c"] = "t͡sʰ", ["s"] = "s",
	["zh"] = "ʈ͡ʂ", ["ch"] = "ʈ͡ʂʰ", ["sh"] = "ʂ", ["r"] = "ʐ", 
	["j"] = "t͡ɕ", ["q"] = "t͡ɕʰ", ["x"] = "ɕ", ["ny"] = "n̠ʲ",
	["g"] = "k", ["k"] = "kʰ", ["ng"] = "ŋ",
	["h"] = "x", [""] = ""
}

local finalConv = {
	["r"] = "z̩", ["rr"] = "ʐ̩", 
	["i"] = "i", 
	["u"] = "u", 
	["y"] = "y", 
	["a"] = "a̠", ["ia"] = "ɪ̯a̠", ["ua"] = "u̯a̠", ["ya"] = "ʏ̯a̠", 
	["o"] = "o", ["io"] = "ɪ̯o", 
	["e"] = "ɤ̞", ["ue"] = "u̯ɤ̞", 
	["ai"] = "aɪ̯", ["uai"] = "u̯aɪ̯", ["yai"] = "ʏ̯aɪ̯", 
	["ei"] = "e̞ɪ̯", ["uei"] = "u̯e̞ɪ̯", ["yei"] = "ʏ̯e̞ɪ̯", 
	["au"] = "ɒu̯", ["iau"] = "ɪ̯ɒu̯", 
	["ou"] = "ə̹u̯", ["iou"] = "ɪ̯ə̹u̯", 
	["ie"] = "ɪ̯e̞", ["ye"] = "ʏ̯e̞", 
	["onn"] = "õ", 
	["enn"] = "ə̃",
	["ienn"] = "ɪ̯ẽ", ["yenn"] = "ʏ̯ẽ",
	["en"] = "ən", ["un"] = "u̯ən", 
	["in"] = "in", 
	["yn"] = "yn", 
	["an"] = "an", ["ian"] = "ɪ̯æn", ["uan"] = "u̯an", ["yan"] = "ʏ̯æn", 
	["ong"] = "ʊŋ", ["iong"] = "ɪ̯ʊŋ", 
	["m"] = "m̩",
	["n"] = "n̩",
}

local toneConv = {
	["1"] = "³³", ["2"] = "¹³", ["3"] = "⁴¹", ["4"] = "⁴⁵", ["5"] = "²¹", ["6"] = "²⁴", ["7"] = "³",
	["5-ts"] = "²¹⁻¹¹", ["6-high"] = "²⁴⁻⁴⁴", ["6-low"] = "²⁴⁻²²", ["6*"] = "²⁴", [""] = ""
}

function export.ipa(text, style)
	if type(text) == "table" then
		text = text.args[1]
	end
	local result = {}
	for word in mw.text.gsplit(text, "/") do
		local syllables = mw.text.split(word, " ")
		local ipa, tone = {}, {}
		for index, syllable in ipairs(syllables) do
			local initial, final
			initial = syllable:match("^([bpmfdtlnzcsrjqxgkh]?([hgy]?))")
			if initial:match("^.y") and initial ~= "ny" then
				initial = sub(initial, 1, 1)
			elseif initial == "y" then
				initial = ""
			end
			final = syllable:match("^" .. initial .. "([^1-7%*]*)")
			if final == "" then
				final = initial
				initial = ""
			end
			if (find(initial, "^[zcs]h$") and find(final, "^i")) or (find(initial, "^[jqx]$") and find(final, "^[aeou]")) or (find(final, "^r$") and not (initial == "" or find(initial, "^[zcs]h?$"))) or (find(final, "^u") and not (initial == "" or find(initial, "^[bpfgk]$"))) or (find(final, "^y") and not (initial == "" or initial == "ny" or find(initial, "^[jqx]$"))) then
				error("Invalid input \"" .. syllable .. "\": initial " .. initial .. " cannot go with final " .. final .. ".")
			end
			tone[index] = syllable:match("[1-7%*]+$") or (index ~= 1 and "7" or "")
			if style == "new" then
				initial = initial:gsub("([zcs])h", "%1")
				if final:match("^i") then
					initial = initial:gsub("[zcs]$", { ["z"] = "j", ["c"] = "q", ["s"] = "x" })
				end
				final = final:gsub("(i?ong)", { ["iong"] = "in", ["ong"] = "en" })
			end
			initial = initialConv[initial] or error(("Unrecognised initial: \"%s\""):format(initial))
			if (initial == "" or match(initial, "[ʂʐ]")) and final == "r" then
				final = "rr"
			end
			final = finalConv[final] or error(("Unrecognised final: \"%s\""):format(final))
			if initial == "l" and ((final ~= "ən" and not final:match("^[iɪ]") and
				match(final, "[ŋ̃n]")) or final:match("^[iɪ].*̃")) then
				initial = "l̃"
			end
			if tone[index] == "5" and #syllables > 1 then
				tone[index] = "5-ts"
			elseif tone[index] == "6" then
				if match(tone[index-1] or "", "[126]") then
					tone[index] = "6-high"
				elseif match(tone[index-1] or "", "[345]") then
					tone[index] = "6-low"
				end
			end
			tone[index] = toneConv[tone[index]] or error(("Unrecognised tone: \"%s\""):format(tone[index]))
			ipa[index] = initial .. final .. tone[index]
		end
		table.insert(result, table.concat(ipa, " "))
	end
	return table.concat(result, "/, /")
end

function export.rom(text)
	return (text
		:gsub("/", " / ")
		:gsub("([%d-]+)", "<sup>%1</sup>"))
end

function export.stylediff(text)
	if match(text, "[zcs]h") or match(text, "[zcs]i") or match(text, "i?ong") then
		return true
	end
	return false
end

return export