模組:Sandbox/Al12si/zz/Date validation

模組解[]
-- vim: set sw=4 ts=4 ai sm :
---- 粵拼轉耶魯模組
----
---- Perl 原碼	2021年	原作者	[[User:Al12si]]
---- Lua  模組	2023年	翻譯	[[User:Al12si]]
----
---- 原程式唔會好小心咁解析粵拼,只係求其解析(用最少心機做到 project 就算,原碼只係做校對用嘅架生仔),
---- 所以模組解析粵拼都好求其,如果輸入唔係粵拼,輸出係 「未定義」⸺即係可能係亂碼、垃圾、錯嘢等等

require ('strict');
local p = {};

local use_dotted_i_p = false;
local use_macrons_p = true;

if true then
	local z = require('模組:書名');
	p.cvs = z.cvs;
end

local function interpret_boolean( s )
	return s:match('^%s*true%s*$')
		or s:match('^%s*yes%s*$')
		or s:match('^%s*0*1%s*$');
end

local function make_hangul_yam( s, adj )
	if not adj then
		adj = 0;
	end
	adj = adj + 0.2;
	return '<span style="position:relative"><span style="position:absolute;display:inline-block;margin-top:' .. adj .. 'em">'
			.. s .. '</span>';
end

local function make_hangul_yoeng( s, adj )
	if not adj then
		adj = 0;
	end
	adj = -1.2 - adj;
	return '<span style="position:relative"><span style="position:absolute;display:inline-block;margin-top:' .. adj .. 'em">'
			.. s .. '</span>';
end

local mappings;
local tone_mappings = {
	['hangul'] = {
		-- FIXME: can't understand the description, doing own thing based on description in PowerPoint
		-- PowerPoint says originally, ˊsœŋ was represented by 2 dots (on left of hangul), ˉhœy by 1 dot
		-- need way to distinguish between 2,3 and 5,6 so 2,3 will go below and 4,5,6 will go below
		['prefix'] = {
			'',
			make_hangul_yam(' ̤'),		-- two dots below (U+2004 [1/3 em space], U+324)
			make_hangul_yam(' ̣'),		-- one dot below (U+2004, U+323)
			make_hangul_yoeng('。', -0.2),	-- ring above ("zero dots", like the Arabic system)
			make_hangul_yoeng(' ̤'),		-- two dots above
			make_hangul_yoeng(' ̣'),		-- one dot above
		};
		['suffix'] = {
			'',
			'</span>',
			'</span>',
			'</span>',
			'</span>',
			'</span>',
		};
	};
	['ipa_phonemic'] = {
		['prefix'] = { 'ˈ', 'ˇ', 'ˉ', 'ˌ', 'ˬ', 'ˍ' };
	};
	['ipa_phonetic'] = {
		['suffix'] = { '˥', '˧˥', '˧', '˩', '˨˧', '˨' };
	};
	['pinyin_1960'] = {
		['suffix'] = { '1', '2', '3', '4', '5', '6' };
	};
	['pinyin_1980'] = {
		['suffix'] = { '1', '2', '3', '4', '5', '6' };
	};
	['zhuyin_1931'] = {
		['suffix'] = { '', 'ˇ', 'ˋ', 'ˊ', '˘', 'ˆ', '˙', '', 'ʻ' };
	};
	['zhuyin_1932'] = {
		['suffix'] = { '', 'ˇ', 'ˋ', 'ˊ', '˘', 'ˆ', '˙', '', 'ʻ' };
	};
	['zhuyin_1950'] = {
		['suffix'] = { '', 'ˇ', 'ˋ', 'ˊ', '˘', 'ˆ', '˙', '', 'ʻ' };
	};
};
tone_mappings.hangul_tswong = tone_mappings.hangul;
tone_mappings.hangul_sykim = tone_mappings.hangul;
tone_mappings.hangul_test = tone_mappings.hangul;
local schemes = {
	['諺文'] = 'hangul';
	['國際'] = 'ipa';
	['萬國'] = 'ipa';
	['拼音'] = 'pinyin';
	['耶魯'] = 'yale';
	['注音'] = 'zhuyin';
	['黃錫凌'] = 'ipa_phonemic';
	['黃錫凌式'] = 'ipa_phonemic';
	['國際音標'] = 'ipa';
	['萬國音標'] = 'ipa';
	['粵語諺文'] = 'hangul';
	['廣州拼音'] = 'pinyin';
	['注音符號'] = 'zhuyin';
	['粵語注音'] = 'zhuyin';
	['廣州話拼音'] = 'pinyin';
	['粵語注音符號'] = 'zhuyin';
	['bopomofo'] = 'zhuyin';
	['bpmf'] = 'zhuyin';
	['hangeul'] = 'hangul';
	['hangul'] = 'hangul';
	['ipa'] = 'ipa';
	['pinyin'] = 'pinyin';
	['yale'] = 'yale';
	['zhuyin'] = 'zhuyin';
};
local variants = {
	['hangul'] = {
		['金昔研'] = 'sykim';
		['黃得森'] = 'tswong';
		['sykim'] = 'sykim';
		['test'] = 'test';
		['tswong'] = 'tswong';
	};
	['ipa'] = {
		['寛式'] = 'phonemic';
		['寬式'] = 'phonemic';
		['嚴式'] = 'phonetic';
		['黃錫凌'] = 'phonemic';
		['黃錫凌式'] = 'phonemic';
		['phonemic'] = 'phonemic';
		['phonetic'] = 'phonetic';
	};
	['pinyin'] = {
		['1960'] = '1960';
		['1980'] = '1980';
	};
	['zhuyin'] = {
		['民國'] = '1932';
		['中共'] = '1950';
		['人民'] = '1950';
		['趙雅庭'] = '1931';
		['1931'] = '1931';
		['1932'] = '1932';
		['1950'] = '1950';
		['1952'] = '1950';	-- source: [[廣州話拼音方案]]
	};
};
local default_variants = {
	['hangul'] = 'tswong';
	['ipa'] = 'phonemic';
	['pinyin'] = '1980';
	['zhuyin'] = '1932';
};

local C1 = {
	[''] = mw.ustring.char(0x110b);
	['ʾ'] = mw.ustring.char(0x1159);
	['bb'] = mw.ustring.char(0x1108);
	['f'] = mw.ustring.char(0x1157);	-- per [[:en:Hangul]], f is 112b or 1157, but not composable so unusable
	['w'] = mw.ustring.char(0x111d);	-- per [[:en:Hangul]]
	['y'] = mw.ustring.char(0x1147);	-- per [[:en:Hangul]]
};
local C2 = {
	['l'] = mw.ustring.char(0x11af);	-- think of ł = /w/
	['m'] = mw.ustring.char(0x11b7);
	['n'] = mw.ustring.char(0x11ab);
	['ng'] = mw.ustring.char(0x11bc);
	['b'] = mw.ustring.char(0x1107);
	['d'] = mw.ustring.char(0x1103);
	['g'] = mw.ustring.char(0x11a8);	-- think of Ger g > Eng y... I'm out of ideas
	['p'] = mw.ustring.char(0x11c1);
	['t'] = mw.ustring.char(0x11c0);
	['k'] = mw.ustring.char(0x11bf);
	['j'] = mw.ustring.char(0x11bd);
	['z'] = mw.ustring.char(0x11eb);
	['lb'] = mw.ustring.char(0x11b2);
	['lg'] = mw.ustring.char(0x11b0);
	['lh'] = mw.ustring.char(0x11b6);
	['ll'] = mw.ustring.char(0x11d0);
	['lm'] = mw.ustring.char(0x11b1);
	['ln'] = mw.ustring.char(0x11cd);
	['lp'] = mw.ustring.char(0x11b5);
	['lt'] = mw.ustring.char(0x11b4);
	['nc'] = mw.ustring.char(0xd7cc);
	['ngs'] = mw.ustring.char(0x11f1);
	['nj'] = mw.ustring.char(0x11ac);
	['ns'] = mw.ustring.char(0x11c7);
	['s'] = mw.ustring.char(0x11ba);
	['sk'] = mw.ustring.char(0x11e7);
	['sm'] = mw.ustring.char(0xd7ea);
	['sp'] = mw.ustring.char(0x11ea);
	['st'] = mw.ustring.char(0x11e8);
};
local V = {
	['aa'] = mw.ustring.char(0x1161);
	['aai'] = mw.ustring.char(0x1162);
	['ə'] = mw.ustring.char(0x119e);
	['ae'] = mw.ustring.char(0x1162);	-- per [[:en:Hangul]], this is /ɛ/
	['e'] = mw.ustring.char(0x1166);
	['əi'] = mw.ustring.char(0x11a1);
	['ei'] = mw.ustring.char(0x1164);	-- per [[:en:Hangul]], this is /e/
	['eo'] = mw.ustring.char(0x1165);
	['eo_u'] = mw.ustring.char(0x117b);
	['eu'] = mw.ustring.char(0x1173);
	['i'] = mw.ustring.char(0x1175);
	['o'] = mw.ustring.char(0x1169);
	['oi'] = mw.ustring.char(0x116c);	-- called oe; per [[:en:Hangul]], this should be /œ/, which doesn't work
	['u'] = mw.ustring.char(0x116e);
	['waa'] = mw.ustring.char(0x116a);
	['waai'] = mw.ustring.char(0x118A);
	['wai'] = '(WAI)';
	['wa'] = mw.ustring.char(0x118A);
	['wae'] = mw.ustring.char(0x116b);	-- per [[:en:Hangul]], this is /wɛ/
	['we'] = mw.ustring.char(0x1170);	-- per [[:en:Hangul]], this is /we/
	['weo'] = mw.ustring.char(0x116f);
	['weu'] = '(WEU)';
	['wi'] = mw.ustring.char(0x1171);	-- this is ui; per [[:en:Hangul]], this should be /y/, which doesn't work
	['wo'] = mw.ustring.char(0x1182);	-- XXX o + o
	['woi'] = mw.ustring.char(0xd7b1);	-- XXX o + o + i
	['wu'] = mw.ustring.char(0x118d);
	['wyu'] = '(WYU)';
	['yaa'] = mw.ustring.char(0x1163);
	['yaai'] = mw.ustring.char(0x1164);
	['yə'] = mw.ustring.char(0x119d);
	['yae'] = mw.ustring.char(0x1164);	-- per [[:en:Hangul]], this is /jɛ/
	['yeo'] = mw.ustring.char(0x1167);
	['yeo_u'] = mw.ustring.char(0x117e);
	['yo'] = mw.ustring.char(0x116d);
	['yoi'] = mw.ustring.char(0x1188);
	['ye'] = mw.ustring.char(0x1168);	-- per [[:en:Hangul]], this is /je/
	['yeu'] = mw.ustring.char(0x119c);
	['yi'] = mw.ustring.char(0x1174);
	['y_i'] = mw.ustring.char(0xd7c4);
	['yu'] = mw.ustring.char(0x1172);
};

local function reconstruct_yale (initial_consonant, root, tone, punctuation, capped_p, all_caps_p)
	local it = '';
	if initial_consonant == nil then	-- I hate Lua
		initial_consonant = '';
	end
	if root == nil then					-- I hate Lua
		root = '';
	end
	if punctuation == nil then			-- I hate Lua
		punctuation = '';
	end
	local initial_vowel, other_vowels, final = mw.ustring.match(root, '^(ng)([aeiou]*)(.*)');
	if not final then
		initial_vowel, other_vowels, final = mw.ustring.match(root, '^(%a)([aeiou]*)(.*)');
	end
	if initial_vowel == nil then		-- I hate Lua
		initial_vowel = '';
	end
	if other_vowels == nil then			-- I hate Lua
		other_vowels = '';
	end
	if final == nil then				-- I hate Lua
		final = '';
	end
	local mod1, mod2;
	if tone == 1 and use_macrons_p then
		mod1 = '\204\132';	-- U+0304
	elseif tone == 2 or tone == 5 then
		mod1 = '\204\129';	-- U+0301
	elseif tone == 1 or tone == 4 then
		mod1 = '\204\128';	-- U+0300
	else
		mod1 = '';
	end
	if tone and tone > 3 then
		mod2 = 'h';
	else
		mod2 = '';
	end
	if use_dotted_i_p and (not tone or tone == 3 or tone == 6) and all_caps_p then
		initial_vowel = mw.ustring.gsub(initial_vowel, 'i', 'i̇');	-- append U+0307 I hate Lua
	end
	if not (initial_consonant == 'y' and initial_vowel:sub(1, 1) == 'y') then
		it = it .. initial_consonant;
	end
	local part1, part2 = mw.ustring.match(initial_vowel, '^(%a)(%a*)$');
	if part1 then
		it = it .. part1 .. mod1 .. part2 .. other_vowels .. mod2 .. final .. punctuation;
	end
	if capped_p then
		it = mw.ustring.upper(mw.ustring.sub(it, 1, 1)) .. mw.ustring.sub(it, 2);
	elseif all_caps_p then
		it = mw.ustring.upper(it);
	end
	return it;
end

local function reconstruct_ipa (initial_consonant, root, tone, punctuation, id)
	local it = '';
	local mapping = mappings[id];
	local tone_mapping = tone_mappings[id];
	if tone and tone_mapping.prefix and tone_mapping.prefix[tone] then
		it = it .. tone_mapping.prefix[tone];
	end
	if initial_consonant then
		it = it .. initial_consonant;
	end
	if root then
		it = it .. root;
	end
	if tone and tone_mapping.suffix and tone_mapping.suffix[tone] then
		it = it .. tone_mapping.suffix[tone];
	end
	if punctuation and not mapping.ignore_punctuation_p then
		it = it .. punctuation;
	end
	return it;
end

local function reconstruct_ipa_phonemic (initial_consonant, root, tone, punctuation, capped_p, all_caps_p)
	return reconstruct_ipa(initial_consonant, root, tone, punctuation, 'ipa_phonemic');
end

local function reconstruct_ipa_phonetic (initial_consonant, root, tone, punctuation, capped_p, all_caps_p)
	return reconstruct_ipa(initial_consonant, root, tone, punctuation, 'ipa_phonetic');
end

local function reconstruct_pinyin (initial_consonant, root, tone, punctuation, id)
	local it = reconstruct_ipa(initial_consonant, root, tone, punctuation, id);
	it = mw.ustring.gsub(it, 'z([iü])', 'j%1');
	it = mw.ustring.gsub(it, 'c([iü])', 'q%1');
	it = mw.ustring.gsub(it, 's([iü])', 'x%1');
	it = mw.ustring.gsub(it, '([jqx])ü', '%1u');
	return it;
end

local function reconstruct_pinyin_1960 (initial_consonant, root, tone, punctuation, capped_p, all_caps_p)
	return reconstruct_pinyin (initial_consonant, root, tone, punctuation, 'pinyin_1960');
end

local function reconstruct_pinyin_1980 (initial_consonant, root, tone, punctuation, capped_p, all_caps_p)
	return reconstruct_pinyin (initial_consonant, root, tone, punctuation, 'pinyin_1980');
end

local function reconstruct_zhuyin (initial_consonant, root, tone, punctuation, id)
	if root and tone and mw.ustring.match(root, '[ㄆㄊㄎ]') then	-- XXX stupid hack
		if tone == 1 then
			tone = 7
		elseif tone == 3 then
			tone = 8
		elseif tone == 6 then
			tone = 9
		end
	end
	return reconstruct_ipa(initial_consonant, root, tone, punctuation, id);
end

local function reconstruct_zhuyin_1931 (initial_consonant, root, tone, punctuation, capped_p, all_caps_p)
	return reconstruct_zhuyin (initial_consonant, root, tone, punctuation, 'zhuyin_1931');
end

local function reconstruct_zhuyin_1932 (initial_consonant, root, tone, punctuation, capped_p, all_caps_p)
	return reconstruct_zhuyin (initial_consonant, root, tone, punctuation, 'zhuyin_1932');
end

local function reconstruct_zhuyin_1950 (initial_consonant, root, tone, punctuation, capped_p, all_caps_p)
	return reconstruct_zhuyin (initial_consonant, root, tone, punctuation, 'zhuyin_1950');
end

local function reconstruct_hangul (initial_consonant, root, tone, punctuation, id)
	local onset, glide, n, s;
	if type(initial_consonant) == 'table' then
		onset = initial_consonant[1];
		glide = initial_consonant[2];
	else
		onset = initial_consonant;
	end
	if type(root) == 'table' then
		local i = glide;
		if not i then
			i = 1;
		end
		if root[i] then
			if root[i][1] then
				s = table.concat(root[i]);
			else
				error('Internal error: Unexpected value ' .. p.cvs(root[i]) .. ' for i=' .. p.cvs(i) .. ' (root=' .. p.cvs(root) .. ')');
			end
		else
			s = 'ERROR(no root for i=' .. p.cvs(i) .. ')';
		end
		if onset and onset ~= '' then
			s = onset .. s;
		end
	elseif (onset
			and onset ~= ''
			and onset ~= mappings[id].initials['']
			and onset ~= C1['']
			and onset ~= C1['ʾ']) or glide then

		error('Internal error: ' .. root .. ': Not expecting onset but onset=' .. p.cvs(onset) .. ', glide=' .. p.cvs(glide));
	else
		s = root;
	end
	if n and not s then
		s = mw.ustring.char(0xac00 + n);
	end
	return reconstruct_ipa(nil, s, tone, punctuation, 'hangul_tswong');
end

local function reconstruct_hangul_test (initial_consonant, root, tone, punctuation, id)
	return reconstruct_hangul(initial_consonant, root, tone, punctuation, 'hangul_test');
end

local function reconstruct_hangul_tswong (initial_consonant, root, tone, punctuation, id)
	return reconstruct_hangul(initial_consonant, root, tone, punctuation, 'hangul_tswong');
end

mappings = {
	['hangul_test'] = {
		['label'] = '諺文<sup>測試版</sup>';
		['link'] = '粵語諺文';
		['f'] = reconstruct_hangul_test;
		['ignore_spaces_p'] = true,
		['initials'] = {
			[''] = 'ᅙ';	-- U+1159
			['b'] = 'ᄇ';	-- U+1107
			['p'] = 'ᄑ';	-- U+1111
			['m'] = 'ᄆ';	-- U+1106
			['f'] = C1.bb;	-- U+112B per [[:en:Hangul]] but unusable because not supported at all
			['d'] = 'ᄃ';	-- U+1103
			['t'] = 'ᄐ';	-- U+1110
			['n'] = 'ᄂ';	-- U+1102
			['l'] = 'ᄅ';	-- U+1105
			['g'] = 'ᄀ';	-- U+1100
			['k'] = 'ᄏ';	-- U+110F
			['ng'] = 'ᅌ';	-- U+114C
			['h'] = 'ᄒ';	-- U+1112
			['gw'] = { 'ᄀ', 'W' };	-- U+1100
			['kw'] = { 'ᄏ', 'W' };	-- U+110F
			['w'] = { C1[''], 'W' };
			['z'] = 'ᄌ';	-- U+110C
			['c'] = 'ᄎ';	-- U+110E
			['s'] = 'ᄉ';	-- U+1109
			['j'] = { C1[''], 'Y' };
		};
		['finals'] = { -- per [[:en:Hangul]], in principle œ = oi, y = ui, clearly unworkable unless -i can be a consonant letter
			['aa'] = { { V.aa }; ['W'] = { V.waa }; ['Y'] = { V.yaa }; };
			['aai'] = { { V.aa, C2.j }; ['W'] = { V.waa, C2.j }; ['Y'] = { V.yaa, C2.j }; };
			['aau'] = { { V.aa, C2.l }; ['W'] = { V.waa, C2.l }; ['Y'] = { V.yaa, C2.l }; };
			['aam'] = { { V.aa, C2.m }; ['W'] = { V.waa, C2.m }; ['Y'] = { V.yaa, C2.m }; };
			['aan'] = { { V.aa, C2.n }; ['W'] = { V.waa, C2.n }; ['Y'] = { V.yaa, C2.n }; };
			['aang'] = { { V.aa, C2.ng }; ['W'] = { V.waa, C2.ng }; ['Y'] = { V.yaa, C2.ng }; };
			['aap'] = { { V.aa, C2.p }; ['W'] = { V.waa, C2.p }; ['Y'] = { V.yaa, C2.p }; };
			['aat'] = { { V.aa, C2.t }; ['W'] = { V.waa, C2.t }; ['Y'] = { V.yaa, C2.t }; };
			['aak'] = { { V.aa, C2.k }; ['W'] = { V.waa, C2.k }; ['Y'] = { V.yaa, C2.k }; };
			['ai'] = { { V.eo, C2.j }; ['W'] = { V.weo, C2.j }; ['Y'] = { V.yeo, C2.j }; };
			['au'] = { { V.eo, C2.l }; ['W'] = { V.weo, C2.l }; ['Y'] = { V.yeo, C2.l }; };
			['am'] = { { V.eo, C2.m }; ['W'] = { V.weo, C2.m }; ['Y'] = { V.yeo, C2.m }; };
			['an'] = { { V.eo, C2.n }; ['W'] = { V.weo, C2.n }; ['Y'] = { V.yeo, C2.n }; };
			['ang'] = { { V.eo, C2.ng }; ['W'] = { V.weo, C2.ng }; ['Y'] = { V.yeo, C2.ng }; };
			['ap'] = { { V.eo, C2.p }; ['W'] = { V.weo, C2.p }; ['Y'] = { V.yeo, C2.p }; };
			['at'] = { { V.eo, C2.t }; ['W'] = { V.weo, C2.t }; ['Y'] = { V.yeo, C2.t }; };
			['ak'] = { { V.eo, C2.k }; ['W'] = { V.weo, C2.k }; ['Y'] = { V.yeo, C2.k }; };
			['e'] = { { V.ae }; ['W'] = { V.wae }; ['Y'] = { V.yae }; };
			['ei'] = { { V.ae, C2.j }; ['W'] = { V.wae, C2.j }; ['Y'] = { V.yae, C2.j }; };
			['eu'] = { { V.ae, C2.l }; ['W'] = { V.wae, C2.l }; ['Y'] = { V.yae, C2.l }; };
			['em'] = { { V.ae, C2.m }; ['W'] = { V.wae, C2.m }; ['Y'] = { V.yae, C2.m }; };
			['en'] = { { V.ae, C2.n }; ['W'] = { V.wae, C2.n }; ['Y'] = { V.yae, C2.n }; };
			['eng'] = { { V.ae, C2.ng }; ['W'] = { V.wae, C2.ng }; ['Y'] = { V.yae, C2.ng }; };
			['ep'] = { { V.ae, C2.p }; ['W'] = { V.wae, C2.p }; ['Y'] = { V.yae, C2.p }; };
			['ek'] = { { V.ae, C2.k }; ['W'] = { V.wae, C2.k }; ['Y'] = { V.yae, C2.k }; };
			['i'] = { { V.i }; ['W'] = { V.wi }; ['Y'] = { V.yi }; };
			['iu'] = { { V.i, C2.l }; ['W'] = { V.wi, C2.l }; ['Y'] = { V.yi, C2.l }; };
			['im'] = { { V.i, C2.m }; ['W'] = { V.wi, C2.m }; ['Y'] = { V.yi, C2.m }; };
			['in'] = { { V.i, C2.n }; ['W'] = { V.wi, C2.n }; ['Y'] = { V.yi, C2.n }; };
			['ing'] = { { V.i, C2.ng }; ['W'] = { V.wi, C2.ng }; ['Y'] = { V.yi, C2.ng }; };
			['ip'] = { { V.i, C2.p }; ['W'] = { V.wi, C2.p }; ['Y'] = { V.yi, C2.p }; };
			['it'] = { { V.i, C2.t }; ['W'] = { V.wi, C2.t }; ['Y'] = { V.yi, C2.t }; };
			['ik'] = { { V.i, C2.k }; ['W'] = { V.wi, C2.k }; ['Y'] = { V.yi, C2.k }; };
			['o'] = { { V.o }; ['W'] = { V.wo }; ['Y'] = { V.yo }; };
			['oi'] = { { V.o, C2.j }; ['W'] = { V.wo, C2.j }; ['Y'] = { V.yo, C2.j }; };
			['ou'] = { { V.o, C2.l }; ['W'] = { V.wo, C2.l }; ['Y'] = { V.yo, C2.l }; };
			['on'] = { { V.o, C2.n }; ['W'] = { V.wo, C2.n }; ['Y'] = { V.yo, C2.n }; };
			['ong'] = { { V.o, C2.ng }; ['W'] = { V.wo, C2.ng }; ['Y'] = { V.yo, C2.ng }; };
			['op'] = { { V.o, C2.p }; ['W'] = { V.wo, C2.p }; ['Y'] = { V.yo, C2.p }; };
			['ot'] = { { V.o, C2.t }; ['W'] = { V.wo, C2.t }; ['Y'] = { V.yo, C2.t }; };
			['ok'] = { { V.o, C2.k }; ['W'] = { V.wo, C2.k }; ['Y'] = { V.yo, C2.k }; };
			['u'] = { { V.u }; ['W'] = { V.wu }; ['Y'] = { C1.y, V.u }; };
			['ui'] = { { V.u, C2.j }; ['W'] = { V.wu, C2.j }; ['Y'] = { C1.y, V.u, C2.j }; };
			['un'] = { { V.u, C2.n }; ['W'] = { V.wu, C2.n }; ['Y'] = { C1.y, V.u, C2.n }; };
			['ung'] = { { V.u, C2.ng }; ['W'] = { V.wu, C2.ng }; ['Y'] = { C1.y, V.u, C2.ng }; };
			['up'] = { { V.u, C2.p }; ['W'] = { V.wu, C2.p }; ['Y'] = { C1.y, V.u, C2.p }; };
			['ut'] = { { V.u, C2.t }; ['W'] = { V.wu, C2.t }; ['Y'] = { C1.y, V.u, C2.t }; };
			['uk'] = { { V.u, C2.k }; ['W'] = { V.wu, C2.k }; ['Y'] = { C1.y, V.u, C2.k }; };
			['oe'] = { { V.oi }; ['W'] = { C1.w, V.oi }; ['Y'] = { C1.y, V.oi }; };
			['eoi'] = { { V.oi, C2.j }; ['W'] = { C1.w, V.oi, C2.j }; ['Y'] = { V.yoi, C2.j }; };
			['eon'] = { { V.oi, C2.n }; ['W'] = { C1.w, V.oi, C2.n }; ['Y'] = { V.yoi, C2.n }; };
			['oeng'] = { { V.oi, C2.ng }; ['W'] = { C1.w, V.oi, C2.ng }; ['Y'] = { V.yoi, C2.ng }; };
			['eot'] = { { V.oi, C2.t }; ['W'] = { C1.w, V.oi, C2.t }; ['Y'] = { V.yoi, C2.t }; };
			['oek'] = { { V.oi, C2.k }; ['W'] = { C1.w, V.oi, C2.k }; ['Y'] = { V.yoi, C2.k }; };
			['yu'] = { { V.wi }; ['W'] = { C1.w, V.wi }; ['Y'] = { V.wi }; };
			['yun'] = { { V.wi, C2.n }; ['W'] = { C1.w, V.wi, C2.n }; ['Y'] = { V.wi, C2.n }; };
			['yut'] = { { V.wi, C2.t }; ['W'] = { C1.w, V.wi, C2.t }; ['Y'] = { V.wi, C2.t }; };
			['m'] = 'ㅁ';
			['ng'] = 'ㅇ';	-- or ㄲ
		};
	};
	-- source: [[粵語諺文]]
	-- any form of precomposing would not work, so we'll transform it into some kind of internal representation
	-- then attempt to reconstruct the actual hangul in reconstruct_hangul_tswong
	['hangul_tswong'] = {
		['label'] = '諺文';
		['link'] = '粵語諺文#黃得森方案';
		['sublabel'] = '黃得森式';
		['f'] = reconstruct_hangul_tswong;
		['ignore_spaces_p'] = true,
		['initials'] = {
			[''] = '';--mw.ustring.char(0x110b);
			['b'] = 'ᄇ';	-- U+1107
			['p'] = 'ᄑ';	-- U+1111
			['m'] = 'ᄆ';	-- U+1106
			['f'] = mw.ustring.char(0x1108);
			['d'] = 'ᄃ';	-- U+1103
			['t'] = 'ᄐ';	-- U+1110
			['n'] = 'ᄂ';	-- U+1102
			['l'] = 'ᄅ';	-- U+1105
			['g'] = 'ᄀ';	-- U+1100
			['k'] = 'ᄏ';	-- U+110F
			['ng'] = 'ᅌ';	-- U+114C
			['h'] = 'ᄒ';	-- U+1112
			['gw'] = { 'ᄀ', 'W' };	-- U+1100
			['kw'] = { 'ᄏ', 'W' };	-- U+110F
			['w'] = { 'ᄋ', 'W' };	-- U+110B
			['z'] = 'ᄌ';	-- U+110C
			['c'] = 'ᄎ';	-- U+110E
			['s'] = 'ᄉ';	-- U+1109
			['j'] = { 'ᄋ', 'Y' };	-- U+110B
		};
		['finals'] = {
			['aa'] = { { V.aa }; ['W'] = { V.waa }; ['Y'] = { V.yaa }; };
			['aai'] = { { V.ae }; ['W'] = { V.wae }; ['Y'] = { V.yae }; };
			['aau'] = { { V.eo }; ['W'] = { V.weo }; ['Y'] = { V.yeo }; };
			['aam'] = { { V.aa, C2.m }; ['W'] = { V.waa, C2.m }; ['Y'] = { V.yaa, C2.m }; };
			['aan'] = { { V.aa, C2.n }; ['W'] = { V.waa, C2.n }; ['Y'] = { V.yaa, C2.n }; };
			['aang'] = { { V.aa, C2.ng }; ['W'] = { V.waa, C2.ng }; ['Y'] = { V.yaa, C2.ng }; };
			['aap'] = { { V.aa, C2.b }; ['W'] = { V.waa, C2.b }; ['Y'] = { V.yaa, C2.b }; };
			['aat'] = { { V.aa, C2.d }; ['W'] = { V.waa, C2.d }; ['Y'] = { V.yaa, C2.d }; };
			['aak'] = { { V.aa, C2.g }; ['W'] = { V.waa, C2.g }; ['Y'] = { V.yaa, C2.g }; };
			['ai'] = { { V.ae, C2.s }; ['W'] = { V.wae, C2.s }; ['Y'] = { V.yae, C2.s }; };
			['au'] = { { V.aa, C2.s }; ['W'] = { V.waa, C2.s }; ['Y'] = { V.yaa, C2.s }; };
			['am'] = { { V.aa, C2.lm }; ['W'] = { V.waa, C2.lm }; ['Y'] = { V.yaa, C2.lm }; };
			['an'] = { { V.aa, C2.nj }; ['W'] = { V.waa, C2.nj }; ['Y'] = { V.yaa, C2.nj }; };
			['ang'] = { { V.aa, C2.ngs }; ['W'] = { V.waa, C2.ngs }; ['Y'] = { V.yaa, C2.ngs }; };
			['ap'] = { { V.aa, C2.sp }; ['W'] = { V.waa, C2.sp }; ['Y'] = { V.yaa, C2.sp }; };
			['at'] = { { V.aa, C2.st }; ['W'] = { V.waa, C2.st }; ['Y'] = { V.yaa, C2.st }; };
			['ak'] = { { V.aa, C2.sk }; ['W'] = { V.waa, C2.sk }; ['Y'] = { V.yaa, C2.sk }; };
			['e'] = { { V.e }; ['W'] = { V.we }; ['Y'] = { V.ye }; };
			['ei'] = { { V.e, C2.s }; ['W'] = { V.we, C2.s }; ['Y'] = { V.ye, C2.s }; };
			['eu'] = { { V.eo, C2.l }; ['W'] = { V.weo, C2.l }; ['Y'] = { V.yeo, C2.l }; };
			['em'] = { { V.e, C2.m }; ['W'] = { V.we, C2.m }; ['Y'] = { V.ye, C2.m }; };
			['en'] = { { V.e, C2.n }; ['W'] = { V.we, C2.n }; ['Y'] = { V.ye, C2.n }; };
			['eng'] = { { V.e, C2.ng }; ['W'] = { V.we, C2.ng }; ['Y'] = { V.ye, C2.ng }; };
			['ep'] = { { V.e, C2.b }; ['W'] = { V.we, C2.b }; ['Y'] = { V.ye, C2.b }; };
			['ek'] = { { V.e, C2.g }; ['W'] = { V.we, C2.g }; ['Y'] = { V.ye, C2.g }; };
			['i'] = { { V.i }; ['W'] = { V.wi }; ['Y'] = { V.yi }; };
			['iu'] = { { V.yu }; ['W'] = { C1.w, V.yu }; ['Y'] = { C1.y, V.yu }; };
			['im'] = { { V.i, C2.m }; ['W'] = { V.wi, C2.m }; ['Y'] = { V.yi, C2.m }; };
			['in'] = { { V.i, C2.n }; ['W'] = { V.wi, C2.n }; ['Y'] = { V.yi, C2.n }; };
			['ing'] = { { V.i, C2.ng }; ['W'] = { V.wi, C2.ng }; ['Y'] = { V.yi, C2.ng }; };
			['ip'] = { { V.i, C2.b }; ['W'] = { V.wi, C2.b }; ['Y'] = { V.yi, C2.b }; };
			['it'] = { { V.i, C2.d }; ['W'] = { V.wi, C2.d }; ['Y'] = { V.yi, C2.d }; };
			['ik'] = { { V.i, C2.g }; ['W'] = { V.wi, C2.g }; ['Y'] = { V.yi, C2.g }; };
			['o'] = { { V.o }; ['W'] = { V.wo }; ['Y'] = { V.yo }; };
			['oi'] = { { V.oi }; ['W'] = { V.woi }; ['Y'] = { V.yoi }; };
			['ou'] = { { V.o, C2.s }; ['W'] = { V.wo, C2.s }; ['Y'] = { V.yo, C2.s }; };
			['on'] = { { V.o, C2.n }; ['W'] = { V.wo, C2.n }; ['Y'] = { V.yo, C2.n }; };
			['ong'] = { { V.o, C2.ng }; ['W'] = { V.wo, C2.ng }; ['Y'] = { V.yo, C2.ng }; };
			['op'] = { { V.o, C2.b }; ['W'] = { V.wo, C2.b }; ['Y'] = { V.yo, C2.b }; };
			['ot'] = { { V.o, C2.d }; ['W'] = { V.wo, C2.d }; ['Y'] = { V.yo, C2.d }; };
			['ok'] = { { V.o, C2.g }; ['W'] = { V.wo, C2.g }; ['Y'] = { V.yo, C2.g }; };
			['u'] = { { V.u }; ['W'] = { V.wu }; ['Y'] = { C1.y, V.u }; };
			['ui'] = { { V.wi }; ['W'] = { V.wi }; ['Y'] = { C1.y, V.wi }; };
			['un'] = { { V.u, C2.n }; ['W'] = { V.wu, C2.n }; ['Y'] = { C1.y, V.u, C2.n }; };
			['ung'] = { { V.u, C2.ng }; ['W'] = { V.wu, C2.ng }; ['Y'] = { C1.y, V.u, C2.ng }; };
			['up'] = { { V.u, C2.b }; ['W'] = { V.wu, C2.b }; ['Y'] = { C1.y, V.u, C2.b }; };
			['ut'] = { { V.u, C2.d }; ['W'] = { V.wu, C2.d }; ['Y'] = { C1.y, V.u, C2.d }; };
			['uk'] = { { V.u, C2.g }; ['W'] = { V.wu, C2.g }; ['Y'] = { C1.y, V.u, C2.g }; };
			['oe'] = { { V.eu }; ['W'] = { V.weu }; ['Y'] = { V.yoi }; };
			['eoi'] = { { V.eu, C2.s }; ['W'] = { V.weu, C2.s }; ['Y'] = { V.yoi, C2.s }; };
			['eon'] = { { V.eu, C2.ns }; ['W'] = { V.weu, C2.ns }; ['Y'] = { V.yoi, C2.ns }; };
			['oeng'] = { { V.eu, C2.ngs }; ['W'] = { V.weu, C2.ngs }; ['Y'] = { V.yoi, C2.ngs }; };
			['oet'] = { { V.eu, C2.t }; ['W'] = { V.weu, C2.t }; ['Y'] = { V.yoi, C2.t }; };
			['oek'] = { { V.eu, C2.k }; ['W'] = { V.weu, C2.k }; ['Y'] = { V.yoi, C2.k }; };
			['yu'] = { { V.yi }; ['W'] = { C1.w, V.yi }; ['Y'] = { V.yi }; };
			['yun'] = { { V.yu, C2.n }; ['W'] = { C1.w, V.yi, C2.n }; ['Y'] = { V.yi, C2.n }; };
			['yut'] = { { V.yu, C2.d }; ['W'] = { C1.w, V.yi, C2.d }; ['Y'] = { V.yi, C2.d }; };
			['m'] = 'ㅁ';
			['ng'] = 'ㅇ';	-- or ㄲ
		};
	};
	['ipa_phonemic'] = {
		['label'] = 'IPA';
		['link'] = '黃錫凌式音標';
		['f'] = reconstruct_ipa_phonemic;
		['initials'] = {
			[''] = '';
			['b'] = 'b';
			['p'] = 'p';
			['m'] = 'm';
			['f'] = 'f';
			['d'] = 'd';
			['t'] = 't';
			['n'] = 'n';
			['l'] = 'l';
			['g'] = 'ɡ';
			['k'] = 'k';
			['ng'] = 'ŋ';
			['h'] = 'h';
			['gw'] = 'ɡw';
			['kw'] = 'kw';
			['w'] = 'w';
			['z'] = 'dz';
			['c'] = 'ts';
			['s'] = 's';
			['j'] = 'j';
		};
		['finals'] = {
			['aa'] = 'a';
			['aai'] = 'ai';
			['aau'] = 'au';
			['aam'] = 'am';
			['aan'] = 'an';
			['aang'] = 'aŋ';
			['aap'] = 'ap';
			['aat'] = 'at';
			['aak'] = 'ak';
			['ai'] = 'ɐi';
			['au'] = 'ɐu';
			['am'] = 'ɐm';
			['an'] = 'ɐn';
			['ang'] = 'ɐŋ';
			['ap'] = 'ɐp';
			['at'] = 'ɐt';
			['ak'] = 'ɐk';
			['e'] = 'ɛ';
			['ei'] = 'ei';
			['eng'] = 'ɛŋ';
			['ek'] = 'ɛk';
			['i'] = 'i';
			['iu'] = 'iu';
			['im'] = 'im';
			['in'] = 'in';
			['ing'] = 'iŋ';
			['ip'] = 'ip';
			['it'] = 'it';
			['ik'] = 'ik';
			['o'] = 'ɔ';
			['oi'] = 'ɔi';
			['ou'] = 'ou';
			['on'] = 'ɔn';
			['ong'] = 'ɔŋ';
			['ot'] = 'ɔt';
			['ok'] = 'ɔk';
			['u'] = 'u';
			['ui'] = 'ui';
			['un'] = 'un';
			['ung'] = 'uŋ';
			['ut'] = 'ut';
			['uk'] = 'uk';
			['oe'] = 'œ';
			['eoi'] = 'œy';
			['eon'] = 'œn';
			['oeng'] = 'œŋ';
			['eot'] = 'œt';
			['oek'] = 'œk';
			['yu'] = 'y';
			['yun'] = 'yn';
			['yut'] = 'yt';
			['m'] = 'm';
			['ng'] = 'ŋ';
		};
	};
	-- source: [[粵語注音符號]], with modifications
	['ipa_phonetic'] = {
		['label'] = 'IPA';
		['link'] = '國際音標';
		['sublabel'] = '嚴式';
		['prefix'] = '[ ';	-- U+200A hair space
		['suffix'] = ' ]';	-- ditto
		['f'] = reconstruct_ipa_phonetic;
		['ignore_punctuation_p'] = true,
		['initials'] = {
			[''] = 'ˀ';
			['b'] = 'p';
			['p'] = 'pʰ';
			['m'] = 'm';
			['f'] = 'f';
			['d'] = 't';
			['t'] = 'tʰ';
			['n'] = 'n';
			['l'] = 'l';
			['g'] = 'k';
			['k'] = 'kʰ';
			['ng'] = 'ŋ';
			['h'] = 'h';
			['gw'] = 'kʷ';
			['kw'] = 'kʷʰ';
			['w'] = 'w';
			['z'] = 't͡s';
			['c'] = 't͡sʰ';
			['s'] = 's';
			['j'] = 'j';
		};
		['finals'] = {
			['aa'] = 'a';
			['aai'] = 'a:i';
			['aau'] = 'a:u';
			['aam'] = 'a:m';
			['aan'] = 'a:n';
			['aang'] = 'a:ŋ';
			['aap'] = 'a:p̚';
			['aat'] = 'a:t̚';
			['aak'] = 'a:k̚';
			['ai'] = 'ɐi';
			['au'] = 'ɐu';
			['am'] = 'ɐm';
			['an'] = 'ɐn';
			['ang'] = 'ɐŋ';
			['ap'] = 'ɐp̚';
			['at'] = 'ɐt̚';
			['ak'] = 'ɐk̚';
			['e'] = 'ɛ:';
			['ei'] = 'ei';
			['eng'] = 'ɛ:ŋ';
			['ek'] = 'ɛk̚';
			['i'] = 'i:';
			['iu'] = 'i:u';
			['im'] = 'i:m';
			['in'] = 'i:n';
			['ing'] = 'ɪŋ';
			['ip'] = 'i:p̚';
			['it'] = 'i:t̚';
			['ik'] = 'ɪk̚';
			['o'] = 'ɔ';
			['oi'] = 'ɔi';
			['ou'] = 'ou';
			['on'] = 'ɔn';
			['ong'] = 'ɔŋ';
			['ot'] = 'ɔt̚';
			['ok'] = 'ɔk̚';
			['u'] = 'u';
			['ui'] = 'ui';
			['un'] = 'un';
			['ung'] = 'uŋ';
			['ut'] = 'ut̚';
			['uk'] = 'uk̚';
			['oe'] = 'œ:';
			['eoi'] = 'ɵy';
			['eon'] = 'ɵn';
			['oeng'] = 'œ:ŋ';
			['eot'] = 'ɵt̚';
			['oek'] = 'œ:k̚';
			['yu'] = 'y:';
			['yun'] = 'y:n';
			['yut'] = 'yt̚';
			['m'] = 'm̩';
			['ng'] = 'ŋ̩';
		};
	};
	-- source: [[廣州話拼音方案]]
	['pinyin_1960'] = {
		['label'] = '廣州話拼音';
		['link'] = '廣州話拼音方案';
		['sublabel'] = '1960年版';
		['f'] = reconstruct_pinyin_1960;
		['initials'] = {
			[''] = '';
			['b'] = 'b';
			['p'] = 'p';
			['m'] = 'm';
			['f'] = 'f';
			['d'] = 'd';
			['t'] = 't';
			['n'] = 'n';
			['l'] = 'l';
			['g'] = 'g';
			['k'] = 'k';
			['ng'] = 'ng';
			['h'] = 'h';
			['gw'] = 'gu';
			['kw'] = 'ku';
			['w'] = 'w';
			['z'] = 'z';
			['c'] = 'c';
			['s'] = 's';
			['j'] = 'y';
		};
		['finals'] = {
			['aa'] = 'a';
			['aai'] = 'ai';
			['aau'] = 'ao';
			['aam'] = 'am';
			['aan'] = 'an';
			['aang'] = 'ang';
			['aap'] = 'ab';
			['aat'] = 'ad';
			['aak'] = 'ag';
			['ai'] = 'ei';
			['au'] = 'ou';
			['am'] = 'em';
			['an'] = 'en';
			['ang'] = 'eng';
			['ap'] = 'eb';
			['at'] = 'ed';
			['ak'] = 'eg';
			['e'] = 'é';
			['ei'] = 'éi';
			['eng'] = 'éng';
			['ek'] = 'ég';
			['i'] = 'i';
			['iu'] = 'iu';
			['im'] = 'im';
			['in'] = 'in';
			['ing'] = 'ing';
			['ip'] = 'ib';
			['it'] = 'id';
			['ik'] = 'ig';
			['o'] = 'o';
			['oi'] = 'oi';
			['ou'] = 'ô';
			['on'] = 'on';
			['ong'] = 'ông';
			['ot'] = 'od';
			['ok'] = 'og';
			['u'] = 'u';
			['ui'] = 'ui';
			['un'] = 'un';
			['ung'] = 'ong';
			['ut'] = 'ud';
			['uk'] = 'ug';
			['oe'] = 'ê';
			['eoi'] = 'êu';
			['eon'] = 'ên';
			['oeng'] = 'êng';
			['eot'] = 'êd';
			['oek'] = 'êg';
			['yu'] = 'ü';
			['yun'] = 'ün';
			['yut'] = 'üd';
			['m'] = 'm’';	-- XXX not supported
			['ng'] = 'ng’';	-- XXX not supported
		};
	};
	-- source: [[廣州話拼音方案]]
	['pinyin_1980'] = {
		['label'] = '廣州話拼音';
		['link'] = '廣州話拼音方案';
		['f'] = reconstruct_pinyin_1980;
		['initials'] = {
			[''] = '';
			['b'] = 'b';
			['p'] = 'p';
			['m'] = 'm';
			['f'] = 'f';
			['d'] = 'd';
			['t'] = 't';
			['n'] = 'n';
			['l'] = 'l';
			['g'] = 'g';
			['k'] = 'k';
			['ng'] = 'ng';
			['h'] = 'h';
			['gw'] = 'gu';
			['kw'] = 'ku';
			['w'] = 'w';
			['z'] = 'z';
			['c'] = 'c';
			['s'] = 's';
			['j'] = 'y';
		};
		['finals'] = {
			['aa'] = 'a';
			['aai'] = 'ai';
			['aau'] = 'ao';
			['aam'] = 'am';
			['aan'] = 'an';
			['aang'] = 'ang';
			['aap'] = 'ab';
			['aat'] = 'ad';
			['aak'] = 'ag';
			['ai'] = 'ei';
			['au'] = 'eo';
			['am'] = 'em';
			['an'] = 'en';
			['ang'] = 'eng';
			['ap'] = 'eb';
			['at'] = 'ed';
			['ak'] = 'eg';
			['e'] = 'é';
			['ei'] = 'éi';
			['eng'] = 'éng';
			['ek'] = 'ég';
			['i'] = 'i';
			['iu'] = 'iu';
			['im'] = 'im';
			['in'] = 'in';
			['ing'] = 'ing';
			['ip'] = 'ib';
			['it'] = 'id';
			['ik'] = 'ig';
			['o'] = 'o';
			['oi'] = 'oi';
			['ou'] = 'ou';
			['on'] = 'on';
			['ong'] = 'ong';
			['ot'] = 'od';
			['ok'] = 'og';
			['u'] = 'u';
			['ui'] = 'ui';
			['un'] = 'un';
			['ung'] = 'ung';
			['ut'] = 'ud';
			['uk'] = 'ug';
			['oe'] = 'ê';
			['eoi'] = 'êu';
			['eon'] = 'ên';
			['oeng'] = 'êng';
			['eot'] = 'êd';
			['oek'] = 'êg';
			['yu'] = 'ü';
			['yun'] = 'ün';
			['yut'] = 'üd';
			['m'] = 'm';
			['ng'] = 'ng';
		};
	};
	['yale'] = {
		['label'] = '耶魯';
		['link'] = '耶魯粵語拼音';
		['f'] = reconstruct_yale;
		['initials'] = {
			[''] = '';
			['b'] = 'b';
			['p'] = 'p';
			['m'] = 'm';
			['f'] = 'f';
			['d'] = 'd';
			['t'] = 't';
			['n'] = 'n';
			['l'] = 'l';
			['g'] = 'g';
			['k'] = 'k';
			['ng'] = 'ng';
			['h'] = 'h';
			['gw'] = 'gw';
			['kw'] = 'kw';
			['w'] = 'w';
			['z'] = 'j';
			['c'] = 'ch';
			['s'] = 's';
			['j'] = 'y';
		};
		['finals'] = {
			['aa'] = 'a';
			['aai'] = 'aai';
			['aau'] = 'aau';
			['aam'] = 'aam';
			['aan'] = 'aan';
			['aang'] = 'aang';
			['aap'] = 'aap';
			['aat'] = 'aat';
			['aak'] = 'aak';
			['ai'] = 'ai';
			['au'] = 'au';
			['am'] = 'am';
			['an'] = 'an';
			['ang'] = 'ang';
			['ap'] = 'ap';
			['at'] = 'at';
			['ak'] = 'ak';
			['e'] = 'e';
			['ei'] = 'ei';
			['eng'] = 'eng';
			['ek'] = 'ek';
			['i'] = 'i';
			['iu'] = 'iu';
			['im'] = 'im';
			['in'] = 'in';
			['ing'] = 'ing';
			['ip'] = 'ip';
			['it'] = 'it';
			['ik'] = 'ik';
			['o'] = 'o';
			['oi'] = 'oi';
			['ou'] = 'ou';
			['on'] = 'on';
			['ong'] = 'ong';
			['ot'] = 'ot';
			['ok'] = 'ok';
			['u'] = 'u';
			['ui'] = 'ui';
			['un'] = 'un';
			['ung'] = 'ung';
			['ut'] = 'ut';
			['uk'] = 'uk';
			['oe'] = 'eu';
			['eoi'] = 'eui';
			['eon'] = 'eun';
			['oeng'] = 'eung';
			['eot'] = 'eut';
			['oek'] = 'euk';
			['yu'] = 'yu';
			['yun'] = 'yun';
			['yut'] = 'yut';
			['m'] = 'm';
			['ng'] = 'ng';
		};
	};
	-- source: [[粵語注音符號]]
	['zhuyin_1931'] = {
		['label'] = '注音';
		['link'] = '粵語注音符號#民國政府時期';
		['sublabel'] = '原型';
		['f'] = reconstruct_zhuyin_1931;
		['initials'] = {
			[''] = '';
			['b'] = 'ㄅ';
			['p'] = 'ㄆ';
			['m'] = 'ㄇ';
			['f'] = 'ㄈ';
			['d'] = 'ㄉ';
			['t'] = 'ㄊ';
			['n'] = 'ㄋ';
			['l'] = 'ㄌ';
			['g'] = 'ㄍ';
			['k'] = 'ㄎ';
			['ng'] = 'ㄫ';
			['h'] = 'ㄏ';
			['gw'] = '[[File:Bpmf-gw.svg|16px]]';
			['kw'] = '[[File:Bpmf-kw.svg|16px]]';
			['w'] = 'ㄨ';
			['z'] = 'ㄐ';
			['c'] = 'ㄑ';
			['s'] = 'ㄒ';
			['j'] = 'ㄧ';
		};
		['finals'] = {
			['aa'] = 'ㄚ';
			['aai'] = 'ㄞ';
			['aau'] = 'ㄠ';
			['aam'] = 'ㄚㄇ';
			['aan'] = 'ㄢ';
			['aang'] = 'ㄤ';
			['aap'] = 'ㄚㄆ';
			['aat'] = 'ㄚㄊ';
			['aak'] = 'ㄚㄎ';
			['ai'] = 'ㄟ';
			['au'] = 'ㄡ';
			['am'] = 'ㄜㄇ';
			['an'] = 'ㄣ';
			['ang'] = 'ㄥ';
			['ap'] = 'ㄜㄆ';
			['at'] = 'ㄜㄊ';
			['ak'] = 'ㄜㄎ';
			['e'] = 'ㄝ';
			['ei'] = 'ㄝㄧ';
			['eu'] = 'ㄝㄨ';
			['em'] = 'ㄝㄇ';
			['en'] = 'ㄝㄋ';
			['eng'] = 'ㄝㄫ';
			['ep'] = 'ㄝㄆ';
			['et'] = 'ㄝㄊ';
			['ek'] = 'ㄝㄎ';
			['i'] = 'ㄧ';
			['iu'] = 'ㄧㄨ';
			['im'] = 'ㄧㄇ';
			['in'] = 'ㄧㄋ';
			['ing'] = 'ㄧㄫ';
			['ip'] = 'ㄧㄆ';
			['it'] = 'ㄧㄊ';
			['ik'] = 'ㄧㄎ';
			['o'] = 'ㄛ';
			['oi'] = 'ㄛㄧ';
			['ou'] = 'ㄛㄨ';
			['on'] = 'ㄛㄋ';
			['ong'] = 'ㄛㄫ';
			['ot'] = 'ㄛㄊ';
			['ok'] = 'ㄛㄎ';
			['u'] = 'ㄨ';
			['ui'] = 'ㄨㄧ';
			['un'] = 'ㄨㄋ';
			['ung'] = 'ㄨㄫ';
			['ut'] = 'ㄨㄊ';
			['uk'] = 'ㄨㄎ';
			['oe'] = '[[File:Bpmf-oe.svg|16px]]';
			['eoi'] = '[[File:Bpmf-oe.svg|16px]]ㄧ';
			['eon'] = '[[File:Bpmf-oe.svg|16px]]ㄋ';
			['oeng'] = '[[File:Bpmf-oe.svg|16px]]ㄫ';
			['eot'] = '[[File:Bpmf-oe.svg|16px]]ㄊ';
			['oek'] = '[[File:Bpmf-oe.svg|16px]]ㄎ';
			['yu'] = 'ㄩ';
			['yun'] = 'ㄩㄋ';
			['yut'] = 'ㄩㄊ';
			['m'] = 'ㄇ';
			['ng'] = 'ㄫ';
		};
	};
	-- source: [[粵語注音符號]]
	['zhuyin_1932'] = {
		['label'] = '注音';
		['link'] = '粵語注音符號#民國政府時期';
		['sublabel'] = '1932年版';
		['f'] = reconstruct_zhuyin_1932;
		['initials'] = {
			[''] = '';
			['b'] = 'ㄅ';
			['p'] = 'ㄆ';
			['m'] = 'ㄇ';
			['f'] = 'ㄈ';
			['d'] = 'ㄉ';
			['t'] = 'ㄊ';
			['n'] = 'ㄋ';
			['l'] = 'ㄌ';
			['g'] = 'ㄍ';
			['k'] = 'ㄎ';
			['ng'] = 'ㄫ';
			['h'] = 'ㄏ';
			['gw'] = 'ㄍㄨ';
			['kw'] = 'ㄎㄨ';
			['w'] = 'ㄨ';
			['z'] = '[[File:Bpmf-zi.svg|16px]]';
			['c'] = '[[File:Bpmf-ci.svg|16px]]';
			['s'] = '[[File:Bpmf-si.svg|16px]]';
			['j'] = 'ㄧ';
		};
		['finals'] = {
			['aa'] = 'ㄚ';
			['aai'] = 'ㄞ';
			['aau'] = 'ㄠ';
			['aam'] = '[[File:Bpmf-aam.svg|16px]]';
			['aan'] = 'ㄢ';
			['aang'] = 'ㄤ';
			['aap'] = 'ㄚ<sub>ㄆ</sub>';
			['aat'] = 'ㄚ<sub>ㄊ</sub>';
			['aak'] = 'ㄚ<sub>ㄎ</sub>';
			['ai'] = '[[File:Bpmf-aq.svg|16px]]ㄧ';
			['au'] = '[[File:Bpmf-aq.svg|16px]]ㄨ';
			['am'] = '[[File:Bpmf-aq.svg|16px]]ㆬ';
			['an'] = '[[File:Bpmf-aq.svg|16px]]ㄣ';
			['ang'] = '[[File:Bpmf-aq.svg|16px]]ㄥ';
			['ap'] = '[[File:Bpmf-aq.svg|16px]]<sub>ㄆ</sub>';
			['at'] = '[[File:Bpmf-aq.svg|16px]]<sub>ㄊ</sub>';
			['ak'] = '[[File:Bpmf-aq.svg|16px]]<sub>ㄎ</sub>';
			['e'] = 'ㄝ';
			['ei'] = 'ㄟ';
			['eu'] = 'ㄝㄨ';
			['em'] = 'ㄝㆬ';
			['en'] = 'ㄝㄣ';
			['eng'] = 'ㄝㄥ';
			['ep'] = 'ㄝ<sub>ㄆ</sub>';
			['et'] = 'ㄝ<sub>ㄊ</sub>';
			['ek'] = 'ㄝ<sub>ㄎ</sub>';
			['i'] = 'ㄧ';
			['iu'] = 'ㄧㄨ';
			['im'] = 'ㄧㆬ';
			['in'] = 'ㄧㄣ';
			['ing'] = 'ㄧㄥ';
			['ip'] = 'ㄧ<sub>ㄆ</sub>';
			['it'] = 'ㄧ<sub>ㄊ</sub>';
			['ik'] = 'ㄧ<sub>ㄎ</sub>';
			['o'] = 'ㄛ';
			['oi'] = 'ㄛㄧ';
			['ou'] = 'ㄡ';
			['on'] = '[[File:Bpmf-on.svg|16px]]';
			['ong'] = '[[File:Bpmf-aong.svg|16px]]';
			['ot'] = 'ㄛ<sub>ㄊ</sub>';
			['ok'] = 'ㄛ<sub>ㄎ</sub>';
			['u'] = 'ㄨ';
			['ui'] = 'ㄨㄧ';
			['un'] = 'ㄨㄣ';
			['ung'] = 'ㄨㄥ';
			['ut'] = 'ㄨ<sub>ㄊ</sub>';
			['uk'] = 'ㄨ<sub>ㄎ</sub>';
			['oe'] = '[[File:Bpmf-eu.svg|16px]]';
			['eoi'] = '[[File:Bpmf-eu.svg|16px]]ㄧ';
			['eon'] = '[[File:Bpmf-eu.svg|16px]]ㄣ';
			['oeng'] = '[[File:Bpmf-eu.svg|16px]]ㄥ';
			['eot'] = '[[File:Bpmf-eu.svg|16px]]<sub>ㄊ</sub>';
			['oek'] = '[[File:Bpmf-eu.svg|16px]]<sub>ㄎ</sub>';
			['yu'] = 'ㄩ';
			['yun'] = 'ㄩㄣ';
			['yut'] = 'ㄩ<sub>ㄊ</sub>';
			['m'] = 'ㆬ';
			['ng'] = 'ㆭ';
		};
	};
	-- source: [[粵語注音符號]]
	['zhuyin_1950'] = {
		['label'] = '注音';
		['link'] = '粵語注音符號#人民政府時期';
		['sublabel'] = '1950年版';
		['f'] = reconstruct_zhuyin_1950;
		['initials'] = {
			[''] = '';
			['b'] = 'ㄅ';
			['p'] = 'ㄆ';
			['m'] = 'ㄇ';
			['f'] = 'ㄈ';
			['d'] = 'ㄉ';
			['t'] = 'ㄊ';
			['n'] = 'ㄋ';
			['l'] = 'ㄌ';
			['g'] = 'ㄍ';
			['k'] = 'ㄎ';
			['ng'] = 'ㄫ';
			['h'] = 'ㄏ';
			['gw'] = '[[File:Bpmf-gw.svg|16px]]';
			['kw'] = '[[File:Bpmf-kw.svg|16px]]';
			['w'] = 'ㄨ';
			['z'] = 'ㄐ';
			['c'] = 'ㄑ';
			['s'] = 'ㄒ';
			['j'] = 'ㄧ';
		};
		['finals'] = {
			['aa'] = 'ㄚ';
			['aai'] = 'ㄞ';
			['aau'] = 'ㄠ';
			['aam'] = 'ㄚㄇ̣';	-- dot = U+323
			['aan'] = 'ㄢ';
			['aang'] = 'ㄤ';
			['aap'] = 'ㄚㄆ̣';
			['aat'] = 'ㄚㄊ̣';
			['aak'] = 'ㄚㄎ̣';
			['ai'] = 'ㄟ';
			['au'] = 'ㄡ';
			['am'] = '[[File:Bpmf-aq.svg|16px]]ㄇ̣';
			['an'] = '[[File:Bpmf-aq.svg|16px]]ㄣ̣';
			['ang'] = '[[File:Bpmf-aq.svg|16px]]ㄥ̣';
			['ap'] = '[[File:Bpmf-aq.svg|16px]]ㄆ̣';
			['at'] = '[[File:Bpmf-aq.svg|16px]]ㄊ̣';
			['ak'] = '[[File:Bpmf-aq.svg|16px]]ㄎ̣';
			['e'] = 'ㄝ';
			['ei'] = 'ㄝㄧ';
			['eu'] = 'ㄝㄨ';
			['em'] = 'ㄝㄇ̣';
			['en'] = 'ㄝㄋ̣';
			['eng'] = 'ㄝㄫ̣';
			['ep'] = 'ㄝㄆ̣';
			['et'] = 'ㄝㄊ̣';
			['ek'] = 'ㄝㄎ̣';
			['i'] = 'ㄧ';
			['iu'] = 'ㄧㄨ';
			['im'] = 'ㄧㄇ̣';
			['in'] = 'ㄧㄋ̣';
			['ing'] = 'ㄧㄫ̣';
			['ip'] = 'ㄧㄆ̣';
			['it'] = 'ㄧㄊ̣';
			['ik'] = 'ㄧㄎ̣';
			['o'] = 'ㄛ';
			['oi'] = 'ㄛㄧ';
			['ou'] = 'ㄛㄨ';
			['on'] = 'ㄛㄋ̣';
			['ong'] = 'ㄛㄫ̣';
			['ot'] = 'ㄛㄊ̣';
			['ok'] = 'ㄛㄎ̣';
			['u'] = 'ㄨ';
			['ui'] = 'ㄨㄧ';
			['un'] = 'ㄨㄋ̣';
			['ung'] = 'ㄨㄫ̣';
			['ut'] = 'ㄨㄊ̣';
			['uk'] = 'ㄨㄎ̣';
			['oe'] = '[[File:Bpmf-oe.svg|16px]]';
			['eoi'] = '[[File:Bpmf-oe.svg|16px]]ㄧ';
			['eon'] = '[[File:Bpmf-oe.svg|16px]]ㄋ̣';
			['oeng'] = '[[File:Bpmf-oe.svg|16px]]ㄫ̣';
			['eot'] = '[[File:Bpmf-oe.svg|16px]]ㄊ̣';
			['oek'] = '[[File:Bpmf-oe.svg|16px]]ㄎ̣';
			['yu'] = 'ㄩ';
			['yun'] = 'ㄩㄋ̣';
			['yut'] = 'ㄩㄊ̣';
			['m'] = 'ㄇ';
			['ng'] = 'ㄫ';
		};
	};
};

local function yalify_syllable (s, id)
	local it;
	local mapping = mappings[id];
	local initials = mapping.initials;
	local finals = mapping.finals;
	local f = mapping.f;
	if s then
		local all_caps_p = s == mw.ustring.upper(s);
		local capped_p = s == (mw.ustring.upper(mw.ustring.sub(s, 1, 1))
							.. mw.ustring.lower(mw.ustring.sub(s, 2)));
		local pat_avoid1 = '^ng%d';	-- I hate Lua
		local pat_avoid2 = '^ng$';
		local pat_part1 = '^([bcdfghjklmnpstwz]*)([a-z]+)';
		local pat_part2 = '([^%a]*)$';
		local initial, final, tone, punctuation;
		local s1 = mw.ustring.lower(s);
		for i, pat_tone in pairs({	'[1¹₁7⁷₇]', '[2²₂]', '[3³₃8⁸₈]',
									'[4⁴₄]', '[5⁵₈]', '[6⁶₆9⁹₉]'	}) do
			if 			not mw.ustring.match(s, pat_avoid1)
					and not mw.ustring.match(s, pat_avoid2) then
				initial, final, punctuation = mw.ustring.match(s1,
							pat_part1 .. pat_tone .. pat_part2);
				if initial then
					if initials[initial] then
						initial = initials[initial];
					end
					if finals[final] then
						final = finals[final];
					end
					tone = i;
				end
		if tone then break end;
			end
		end
		if tone then
			it = f(initial, final, tone, punctuation, capped_p, all_caps_p);
		elseif not mapping.ignore_spaces_p or mw.ustring.match(s, '%S') then
			it = s;
		else
			it = '';
		end
	end
	it = it:gsub('(%[%[File:[^%[%]]+%]%])', '<span style="height:1em;vertical-align:text-bottom">%1</span>');
	return it;
end

local function yalify (segments, id)
	local it = '';
	if not id then
		id = 'yale';
	end
	local mapping = mappings[id];
	if mapping.prefix then
		it = it .. mapping.prefix;
	end
	for _, s in pairs(segments) do
		it = it .. yalify_syllable(s, id);
	end
	if mapping.suffix then
		it = it .. mapping.suffix;
	end
	return it;
end

local function break_into_segments (s)	-- I hate Lua
	local it = {};
	local pat = '^([%a]+[1-9¹²³⁴⁵⁶⁷⁸⁹₁₂₃₄₅₆₇₈₉])';
	while s and #s > 0 do
		local pre = '';
		while s and #s > 0 and not mw.ustring.match(s, pat) do
			pre = pre .. mw.ustring.sub(s, 1, 1);
			s = mw.ustring.sub(s, 2);
		end
		if #pre > 0 then
			table.insert(it, pre);
		end
		local m, post = mw.ustring.match(s, pat .. '(.*)$');
		if m then
			table.insert(it, m);
		end
		s = post;
	end
	return it;
end

-- Entry point for template
p.yalify2 = function( frame )
	return p.yalify(frame:getParent());
end

-- Entry point for direct #invoke
p.yalify = function (frame)
	local parent = frame.args[1] or frame.args.version;
	local s = '';
	local scheme = 'yale';
	local variant;
	local label_p;
	for k, v in pairs(frame.args) do
		if type(k) == 'number' then
			if #s > 0 then
				s = s .. ' ';
			end
			s = s .. v;
		elseif k == 'type' or k == '方案' then
			scheme = v;
		elseif k == 'variant' or k == '版本' then
			variant = v;
		elseif k == 'label' or k == '標題' then
			label_p = interpret_boolean(v);
		else
			error('不明參數 「' .. k .. '」');
		end
	end
	if variant and not scheme then
		scheme = 'yale';
	end;
	if scheme and not schemes[scheme] then
		error('唔知 「' .. scheme .. '」 係乜方案');
	end
	if scheme and variant then
		if not (variants[schemes[scheme]] and variants[schemes[scheme]][variant]) then
			error('「' .. scheme .. '」 方案冇版本叫 「' .. variant .. '」');
		end
		variant = variants[schemes[scheme]][variant];
	elseif scheme and default_variants[schemes[scheme]] then
		variant = default_variants[schemes[scheme]];
	end
	if scheme then
		scheme = schemes[scheme];
		if variant then
			scheme = scheme .. '_' .. variant;
		end
		if not mappings[scheme] then
			error('Internal error: ' .. scheme .. ': No such scheme in mappings table');
		end
	end
	local it = '';
	if label_p and mappings[scheme].label then
		local prefix, suffix;
		if mappings[scheme].link then
			prefix = '[[' .. mappings[scheme].link .. '|';
			suffix = ']]';
		end
		it = it .. prefix .. mappings[scheme].label .. suffix;
		if mappings[scheme].sublabel then
			it = it .. '<sup>' .. mappings[scheme].sublabel .. '</sup>';
		end
		it = it .. ':';
	end
	it = it ..  yalify(break_into_segments(s), scheme);
	return it;
end

p.test = function ()
	assert(yalify_syllable(nil) == nil);	-- make sure it does not crash
	assert(mw.ustring.toNFC(yalify_syllable_real('j', 'eu', 2, '', false, false)) == mw.ustring.toNFC('jéu'));
	assert(mw.ustring.toNFC(yalify_syllable('zoe2')) == mw.ustring.toNFC('jéu'));
	assert(z.cvs(break_into_segments('seoi1 zoe2')) == '[(seoi1) ( ) (zoe2)]');
	assert(mw.ustring.toNFC(yalify({'seoi1', 'zoe2'})) == mw.ustring.toNFC('sēuijéu'));
	assert(mw.ustring.toNFC(yalify({'seoi1', ' ', 'zoe2'})) == mw.ustring.toNFC('sēui jéu'));
	error(p.cvs(yalify({'Seoi1', ' ', 'zoe2'})));
	assert(mw.ustring.toNFC(yalify({'Seoi1', ' ', 'zoe2'})) == mw.ustring.toNFC('Sēui jéu'));
	assert(mw.ustring.toNFC(yalify({'SEOI1', ' ', 'zoe2'})) == mw.ustring.toNFC('SĒUI jéu'));
end

return p;