This module page is in beta stage.
Its interface has been stabilised, but the module page may still contain errors. Do not deploy widely until the module page has been tested.

Pronunciation module for Northern Min. See {{zh-pron}}.


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

local gsub = m_string_utils.gsub
local gsplit = mw.text.gsplit
local match = m_string_utils.match
local lower = m_string_utils.lower
local toNFC = mw.ustring.toNFC
local toNFD = mw.ustring.toNFD

local initial_ipa = {
	["b"] = "p",	["p"] = "pʰ",	["m"] = "m",
	["d"] = "t",	["t"] = "tʰ",	["n"] = "n",	["l"] = "l",
	["c"] = "t͡s",	 ["ch"] = "t͡sʰ", ["s"] = "s",
	["g"] = "k",	["k"] = "kʰ",	["ng"] = "ŋ",	["h"] = "x",
	[""] = "",
}

local final_ipa = {
	["i"] = "i", ["u"] = "u", ["ṳ"] = "y",
	["a"] = "a", ["ia"] = "ia", ["ua"] = "ua",
	["a̤"] = "ɛ", ["ie"] = "iɛ", ["uoi"] = "uɛ", ["ṳe"] = "yɛ",
	["o̤"] = "ɔ", ["io̤"] = "iɔ",
	["e"] = "e", ["e̤"] = "œ", ["o"] = "o",
	["ai"] = "ai", ["uai"] = "uai",
	["au"] = "au", ["iau"] = "iau",
	["iu"] = "iu",
	["ing"] = "iŋ", ["uing"] = "uiŋ", ["ṳing"] = "yiŋ",
	["ang"] = "aŋ", ["iang"] = "iaŋ", ["uang"] = "uaŋ",
	["o̤ng"] = "ɔŋ", ["ong"] = "ɔŋ", ["io̤ng"] = "iɔŋ", ["iong"] = "iɔŋ", ["uo̤ng"] = "uɔŋ",
	["eng"] = "eiŋ", --missing /ieiŋ/
	["e̤ng"] = "œyŋ",
	["aing"] = "aiŋ", ["uaing"] = "uaiŋ",
}

local tone_marks = "[́̂̌̿̄̆̀]"

local tone_from_mark = {
	["́"] = 1, --陰平 (acute)
	["̂"] = 3, --陽平 (circumflex)
	["̌"] = 2, --上 (caron)
	["̿"] = 3, --陰去 (double overline)
	["̄"] = 4, --陽去 (macron)
	["̆"] = 5, --陰入 (breve)
	["̀"] = 6, --陽入 (grave)
}

local tone_value = {
	[1] = "⁵⁴", --陰平
	[2] = "²¹", --上
	[3] = "³³", --陰去
	[4] = "⁵⁵", --陽去
	[5] = "²⁴", --陰入
	[6] = "⁴²", --陽入
}

--[=[
	Given a syllable, return tone value of the syllable's tone and the syllable without the tone mark
]=]
local function get_tone_value(syllable)
	if match(syllable, "̄̄") then -- check for double macron
		local corrected = gsub(syllable, "̄̄", "̿")
		error("Please change " .. syllable .. " to " .. corrected .. ".")
	end
	local tone = match(syllable, tone_marks)
	if tone then
		return tone_value[tone_from_mark[tone]], gsub(syllable, tone, "")
	else
		error("No tone detected.")
	end
end

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

function export.ipa(text)
	text = toNFD(lower(text))
	local words_ipa = {}
	for word in gsplit(text, "/") do
		word = gsub(word, " ", "-")
		local syllables_ipa = {}
		for syllable in gsplit(word, "-") do
			syllable = match(syllable, ">(.*)$") or syllable
			local initial, final, tone_value
			tone_value, syllable = get_tone_value(syllable)
			initial, final = match(syllable, "^([bpmdtnlcsk]?[gh]?)([aeioung̤]+)$")
			initial, final = toNFC(initial), toNFC(final)
			initial, final = initial_ipa[initial], final_ipa[final]
			table.insert(syllables_ipa, initial..final..tone_value)
		end
		table.insert(words_ipa, table.concat(syllables_ipa, " "))
	end
	return "/" .. table.concat(words_ipa, "/, /") .. "/"
end

return export