模組解[] [] [] []

概要

編輯

{{#invoke:jptools | yalify | 參數... }}

{{#invoke:jptools | ruby | 參數... }}

用途

編輯

睇埋

編輯

校對表

編輯

Chēun Híu, Maahng Houh Yìhn

Chēun mìhn bāt gok híu,
Chyu chyu màhn tàih níuh.
Yeh lòih fūng yúh sīng,
Fā lohk jī dō síu.

Chun Hiu2, Maang6 Ho6 Yin4

Chun min4 bat gok3 hiu2,
Chue3 chue3 man4 tai4 niu5.
Ye6 loi4 fung yue5 sing,
Fa lok6 ji doh siu2.

Tsoen(上平聲:三) Hiu(上上聲:九), Maang(下去聲:二) Hou(下去聲:二) Jin(下平聲:零)

Tsoen(上平聲:三) min(下平聲:零) bat(上入聲:七) gok(中入聲:八) hiu(上上聲:九),
Tsy(上去聲:四) tsy(上去聲:四) man(下平聲:零) tai(下平聲:零) niu(下上聲:五).
Je(下去聲:二) loi(下平聲:零) fung(上平聲:三) jy(下上聲:五) sing(上平聲:三),
Fa(上平聲:三) lok(下入聲:六) dzi(上平聲:三) do(上平聲:三) siu(上上聲:九).

Tsoen(上平聲:三) Hiu(上上聲:九), Maang(下去聲:二) Hou(下去聲:二) Jin(下平聲:零)

Tsoen1 min4 bat7 gok8 hiu2,
Tsy3 tsy3 man4 tai4 niu5.
Je6 loi4 fung1 jy5 sing1,
Fa1 lok9 dzi1 do1 siu2.

Tsoen1 Hiu2, Maang6 Hou6 Jin4

Tsoen1 min4 bat7 gok8 hiu2,
Tsy3 tsy3 man4 tai4 niu5.
Je6 loi4 fung1 jy5 sing1,
Faa1 lok9 dzi1 do1 siu2.

(ㄨ˘) (˙) (ㄣˊ) (˙) (ㄈㄨㄥ) (ㄊㄨㄥˊ) (ʻ) (ㄨˇ) (ㄧˇ) (ㄩˋ) () (ㄣˆ) (ㄅㄧㄣ) (ㄍㄛˋ) (ㄍㄝˋ) (ㄅㄨㄣˇ) (ㄧˆ) (ㄉㄞˆ)
(˙) (ㄣˆ) (ㄧˇ) (ㄍㄧㄣˋ) (˙) (ㄍㄛˋ) (ㄏㄤˊ) (ㄌㄡˆ) (ㄍㄝˋ) (ㄣˊ) () (ㄥˆ) () (ㄩˆ) (˙) (ㄍㄧㄣˆ) (ㄥˊ) (ㄇㄧㄣˊ) (ㄆㄡˊ)
(ㄧ˘) (ㄉㄟˆ) (ㄥ˘) (ㄍㄛˋ) (ㄣˊ) (ㄨˆ) (ㄨㄚˆ)
(ㄫㄛ˘) (ㄉㄟˆ) (ㄧˇ) (ㄅㄧㄣ) (ㄍㄛˋ) (ㄥˊ) (ㄨˋ) (ㄧㄣ) 使 (ㄧˇ) (ㄋㄧ) (ㄍㄛˋ) (ㄏㄤˊ) (ㄌㄡˆ) (ㄍㄝˋ) (ㄣˊ) () (ㄧ˘) (ㄍㄝˋ) (ㄥˊ) (ㄆㄡˊ) (ㄊㄩ) (ㄌㄛʻ) (ㄧˊ) ()
(ㄨˆ) (ㄩㄣˋ) (ㄅㄧㄣ) (ㄍㄛˋ) (ㄍㄝˋ) (ㄅㄨㄣˇ) (ㄧˆ) (ㄉㄞˆ)

ㄨ˘ ㄧ˙ ㄨㄣˊ ㄅ˙ ㄈㄨㄥ ㄊㄨㄥˊ ㄧʻ ㄊㄨˇ ㄏㄧˇ ㄩˋ ㄤ ㄌㄣˆ ㄅㄧㄣ ㄍㄛˋ ㄍㄝˋ ㄅㄨㄣˇ ㄧˆ ㄉㄞˆ.
˙ ㄣˆ ㄊㄧˇ ㄍㄧㄣˋ ㄧ˙ ㄍㄛˋ ㄏㄤˊ ㄌㄡˆ ㄍㄝˋ ㄧㄣˊ ㄥˆ ㄩˆ ㄧ˙ ㄍㄧㄣˆ ㄥˊ ㄇㄧㄣˊ ㄆㄡˊ.
ㄧ˘ ㄉㄟˆ ㄌㄥ˘ ㄍㄛˋ ㄧㄣˊ ㄨˆ ㄨㄚˆ,
ㄫㄛ˘ ㄉㄟˆ ㄊㄧˇ ㄅㄧㄣ ㄍㄛˋ ㄋㄥˊ ㄍㄨˋ ㄧㄣ ㄧˇ ㄋㄧ ㄍㄛˋ ㄏㄤˊ ㄌㄡˆ ㄍㄝˋ ㄧㄣˊ ㄥ ㄎㄧ˘ ㄍㄝˋ ㄥˊ ㄆㄡˊ ㄊㄩ ㄌㄛʻ ㄌㄧˊ ㄚ,
ㄨˆ ㄩㄣˋ ㄅㄧㄣ ㄍㄛˋ ㄍㄝˋ ㄅㄨㄣˇ ㄧˆ ㄉㄞˆ.

(ㄧㄡ) (̣) (ㄨㄣ) (̣) (ㄈㄨ̣) (ㄊㄨ̣) (ㄧ̣) (ㄊㄡ) (ㄏㄟ) (ㄒㄩ) (ㄐㄤ) (̣) (ㄅㄧ̣) (ㄍㄛ) (ㄍㄝ) (ㄅㄨ̣) (ㄒㄧ) (ㄉㄞ)
(̣) (ㄐㄣ) (ㄊㄟ) (ㄍㄧ̣) (̣) (ㄍㄛ) (ㄏㄤ) (ㄌㄛㄨ) (ㄍㄝ) (ㄧㄣ) (ㄒㄣ) (̣) (̣) (ㄐㄩ) (̣) (ㄍㄧ̣) (̣) (ㄇㄧ̣) (ㄆㄛㄨ)
() (ㄉㄝㄧ) (̣) (ㄍㄛ) (ㄧㄣ) (ㄐㄡ) (ㄨㄚ)
(ㄫㄛ) (ㄉㄝㄧ) (ㄊㄟ) (ㄅㄧ̣) (ㄍㄛ) (ㄋㄥ) (ㄍㄡ) (ㄒㄧ̣) 使 (ㄒㄟ) (ㄋㄧ) (ㄍㄛ) (ㄏㄤ) (ㄌㄛㄨ) (ㄍㄝ) (ㄧㄣ) (̣) () (ㄍㄝ) (̣) (ㄆㄛㄨ) (ㄊㄩ̣) (ㄌㄛ̣) (ㄌㄟ) ()
(ㄐㄡ) (ㄒㄩ̣) (ㄅㄧ̣) (ㄍㄛ) (ㄍㄝ) (ㄅㄨ̣) (ㄒㄧ) (ㄉㄞ)

ㄧㄡ ㄧ̣ ㄨㄣ ㄅ̣ ㄈㄨ̣ ㄊㄨ̣ ㄧ̣ ㄊㄡ ㄏㄟ ㄒㄩ ㄐㄤ ㄌ̣ ㄅㄧ̣ ㄍㄛ ㄍㄝ ㄅㄨ̣ ㄒㄧ ㄉㄞ.
̣ ㄐㄣ ㄊㄟ ㄍㄧ̣ ㄧ̣ ㄍㄛ ㄏㄤ ㄌㄛㄨ ㄍㄝ ㄧㄣ ㄒㄣ ㄒ̣ ㄐ̣ ㄐㄩ ㄧ̣ ㄍㄧ̣ ㄑ̣ ㄇㄧ̣ ㄆㄛㄨ.
ㄧ ㄉㄝㄧ ㄌ̣ ㄍㄛ ㄧㄣ ㄐㄡ ㄨㄚ,
ㄫㄛ ㄉㄝㄧ ㄊㄟ ㄅㄧ̣ ㄍㄛ ㄋㄥ ㄍㄡ ㄒㄧ̣ ㄒㄟ ㄋㄧ ㄍㄛ ㄏㄤ ㄌㄛㄨ ㄍㄝ ㄧㄣ ㄐ̣ ㄎㄧ ㄍㄝ ㄑ̣ ㄆㄛㄨ ㄊㄩ̣ ㄌㄛ̣ ㄌㄟ ㄚ,
ㄐㄡ ㄒㄩ̣ ㄅㄧ̣ ㄍㄛ ㄍㄝ ㄅㄨ̣ ㄒㄧ ㄉㄞ.

-- vim: set sw=4 ts=4 ai sm :
---- 呢個係少眾注音模組,原版係2021年做 project 嗰陣做耶魯注音用,同粵維無關。
---- 粵維 Lua 版 2023 年由 Perl 譯咗之後,後來加入其他耶魯以外嘅其他方案。
----
---- 原程式唔會好小心咁解析粵拼,只係求其解析(用最少心機做到 project 就算,原碼只係做校對用嘅架生仔),
---- 所以模組解析粵拼都好求其,如果輸入唔係粵拼,輸出係 「未定義」⸺即係可能係亂碼、垃圾、錯嘢等等

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

local use_dotted_i_p = false;
local use_macrons_p = true;

local styles = '模組:jptools/styles.css';
local dot = mw.ustring.char(0x323);

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 remove_tags( s )
	local it;
	if s then
		it = s:gsub('<[^<>]*>', ''):gsub("'''", ''):gsub("''", '');
	end
	return it;
end

local function td_insert_attr( td, attr )
	-- I hate Lua so much
	if not td:match('|.*|') then
		td = '|' .. attr .. td;
	else
		td = td:gsub('|', '|' .. attr .. ' ', 1);
	end
	return td;
end

local jyutping_blacklist = { 'sh', 'sj' };
local mappings;
local tone_mappings = {
	['braille'] = {
		['novenary'] = {
			['suffix'] = { '', '⠁', '⠈', '⠄', '⠠', '⠂', '', '⠐', '⠄' };
		};
	};
	['hangul'] = {
		['senary'] = {
			-- 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'] = {
				'<span class=yue-hang-tone-1>',
				'<span class=yue-hang-tone-2>',
				'<span class=yue-hang-tone-3>',
				'<span class=yue-hang-tone-4>',
				'<span class=yue-hang-tone-5>',
				'<span class=yue-hang-tone-6>',
			};
			['suffix'] = { '</span>', '</span>', '</span>', '</span>', '</span>', '</span>', };
		};
	};
	['hkie_1971'] = {
		['novenary'] = {
			['suffix'] = {
				'<small>(上平聲:三)</small>', '<small>(上上聲:九)</small>', '<small>(上去聲:四)</small>',
				'<small>(下平聲:零)</small>', '<small>(下上聲:五)</small>', '<small>(下去聲:二)</small>',
				'<small>(上入聲:七)</small>', '<small>(中入聲:八)</small>', '<small>(下入聲:六)</small>',
			};
		};
	};
	['hkie_1990'] = {
		['novenary'] = {
			['suffix'] = {
				'<sup class="yue-latn-tone">1</sup>', '<sup class="yue-latn-tone">2</sup>', '<sup class="yue-latn-tone">3</sup>',
				'<sup class="yue-latn-tone">4</sup>', '<sup class="yue-latn-tone">5</sup>', '<sup class="yue-latn-tone">6</sup>',
				'<sup class="yue-latn-tone">7</sup>', '<sup class="yue-latn-tone">8</sup>', '<sup class="yue-latn-tone">9</sup>',
			};
		};
	};
	['hkie_2002'] = {
		['novenary'] = {
			['suffix'] = {
				'<sup class="yue-latn-tone">1</sup>', '<sup class="yue-latn-tone">2</sup>', '<sup class="yue-latn-tone">3</sup>',
				'<sup class="yue-latn-tone">4</sup>', '<sup class="yue-latn-tone">5</sup>', '<sup class="yue-latn-tone">6</sup>',
				'<sup class="yue-latn-tone">7</sup>', '<sup class="yue-latn-tone">8</sup>', '<sup class="yue-latn-tone">9</sup>',
			};
		};
	};
	['ipa_phonemic'] = {
		['senary'] = {
			['prefix'] = { 'ˈ', 'ˊ', 'ˉ', 'ˌ', 'ˏ', 'ˍ' };
		};
	};
	['ipa_phonetic'] = {
		['senary'] = {
			['suffix'] = { '˥', '˧˥', '˧', '˩', '˨˧', '˨' };
		};
	};
	['jyutping'] = {
		['senary'] = {
			['suffix'] = { '1', '2', '3', '4', '5', '6' };
		};
	};
	['pinyin_1960'] = {
		['senary'] = {
			['suffix'] = { '1', '2', '3', '4', '5', '6' };
		};
	};
	['pinyin_1980'] = {
		['senary'] = {
			['suffix'] = { '1', '2', '3', '4', '5', '6' };
		};
	};
	['zhuyin_1931'] = {
		['novenary'] = {
			['suffix'] = { '', 'ˇ', 'ˋ', 'ˊ', '˘', 'ˆ', '˙', '', 'ʻ' };
		};
	};
	['zhuyin_1932'] = {
		['novenary'] = {
			['suffix'] = { '', 'ˇ', 'ˋ', 'ˊ', '˘', 'ˆ', '˙', '', 'ʻ' };
		};
	};
	['zhuyin_1950'] = {
		['novenary'] = {
			['suffix'] = { '', '', '', '', '', '', '', '', '' };	-- not a mistake - tones are absolutely not marked
		};
	};
};
tone_mappings.hangul_tswong = tone_mappings.hangul;
tone_mappings.hangul_sykim = tone_mappings.hangul;
local schemes = {
	['點字'] = 'braille';
	['凸字'] = 'braille';
	['盲文'] = 'braille';
	['諺文'] = 'hangul';
	['教院'] = 'hkie';
	['國際'] = 'ipa';
	['萬國'] = 'ipa';
	['粵拼'] = 'jyutping';
	['廣拼'] = 'pinyin';
	['耶魯'] = 'yale';
	['注音'] = 'zhuyin';
	['黃錫凌'] = 'ipa_phonemic';
	['劉錫祥'] = 'sidneylau';
	['黃錫凌式'] = 'ipa_phonemic';
	['國際音標'] = 'ipa';
	['萬國音標'] = 'ipa';
	['粵語諺文'] = 'hangul';
	['教育學院'] = 'hkie';
	['廣州拼音'] = 'pinyin';
	['注音符號'] = 'zhuyin';
	['粵語注音'] = 'zhuyin';
	['廣州話拼音'] = 'pinyin';
	['粵語注音符號'] = 'zhuyin';
	['教育學院拼音方案'] = 'hkie';
	['bopomofo'] = 'zhuyin';
	['bpmf'] = 'zhuyin';
	['braille'] = 'braille';
	['hangeul'] = 'hangul';
	['hangul'] = 'hangul';
	['hkie'] = 'hkie';
	['ipa'] = 'ipa';
	['jyutping'] = 'jyutping';
	['lau'] = 'sidneylau';
	['pinyin'] = 'pinyin';
	['sidneylau'] = 'sidneylau';
	['yale'] = 'yale';
	['zhuyin'] = 'zhuyin';
};
local variants = {
	['hangul'] = {
		['金昔研'] = 'sykim';
		['黃得森'] = 'tswong';
		['sykim'] = 'sykim';
		['tswong'] = 'tswong';
	};
	['ipa'] = {
		['寛式'] = 'phonemic';
		['寬式'] = 'phonemic';
		['嚴式'] = 'phonetic';
		['黃錫凌'] = 'phonemic';
		['黃錫凌式'] = 'phonemic';
		['phonemic'] = 'phonemic';
		['phonetic'] = 'phonetic';
	};
	['hkie'] = {
		['余秉昭'] = '1971';
		['教育署'] = '1990';
		['語文教育學院'] = '1990';
		['詹伯慧'] = '2002';
		['yu'] = '1971';
		['ile'] = '1990';
		['zhan'] = '2002';
		['1971'] = '1971';
		['1990'] = '1990';
		['2002'] = '2002';
	};
	['pinyin'] = {
		['1960'] = '1960';
		['1980'] = '1980';
	};
	['zhuyin'] = {
		['民國'] = '1932';
		['中共'] = '1950';
		['人民'] = '1950';
		['趙雅庭'] = '1931';
		['1931'] = '1931';
		['1932'] = '1932';
		['1950'] = '1950';
		['1952'] = '1950';	-- source: [[廣州話拼音方案]]
	};
};
local default_scheme = 'jyutping';
local fallback_scheme = 'ipa';
local default_variants = {
	['hangul'] = 'tswong';
	['hkie'] = '2002';
	['ipa'] = 'phonemic';
	['pinyin'] = '1980';
	['zhuyin'] = '1950';
};

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
	['ng'] = mw.ustring.char(0x114c);
	['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(0x11bb);
	['d'] = mw.ustring.char(0x11ae);
	['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);
	['h'] = mw.ustring.char(0x11c2);
	['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, '^(yu)([aeiou]*)(.*)');
		if not final then
			initial_vowel, other_vowels, final = mw.ustring.match(root, '^(%a)([aeiou]*)(.*)');
		end
	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 then
		if tone.actual.senary == 1 and use_macrons_p then
			mod1 = '\204\132';	-- U+0304
		elseif tone.actual.quaternary == 2 then
			mod1 = '\204\129';	-- U+0301
		elseif tone.actual.quaternary == 1 then
			mod1 = '\204\128';	-- U+0300
		else
			mod1 = '';
		end
		if tone.actual.masculine_p then
			mod2 = 'h';
		else
			mod2 = '';
		end
	end
	if use_dotted_i_p and (not tone or tone.actual.quaternary == 3) 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
	-- XXX yu is irregular in that any diacritic is placed above the u, not the y
	-- XXX this is not explained in the article but can be deduced from the sample poem
	-- XXX and can be confirmed by searching inside the referenced book
	local part1, part2 = mw.ustring.match(initial_vowel, '^(y%a)(%a*)$');
	if part1 then
		it = it .. part1 .. mod1 .. part2 .. other_vowels .. mod2 .. final .. punctuation;
	else
		part1, part2 = mw.ustring.match(initial_vowel, '^(%a)(%a*)$');
		if part1 then
			it = it .. part1 .. mod1 .. part2 .. other_vowels .. mod2 .. final .. punctuation;
		end
	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_sidneylau (initial_consonant, root, tone, punctuation, capped_p, all_caps_p)
	local it = '';
	if initial_consonant then
		it = it .. initial_consonant;
	end
	if root then
		it = it .. root;
	end
	if tone then
		it = it .. '<sup class="yue-latn-tone">';
		if tone.actual.senary == 1 and not tone.canonical then
			-- XXX We are screwed. Sydney Lau tone 1° is Jyutping 1
			-- XXX and Sydney Lau tone 1 is a “falling” tone that is “very rare”.
			-- XXX Jyutping doesn't distinguish between the two so.
			it = it .. '1°';	-- XXX assumption here
		elseif tone.canonical and tone.canonical.senary ~= tone.actual.senary and tone.actual.senary == 1 then
			it = it .. tone.canonical.senary .. '°';
		elseif tone.canonical and tone.canonical.senary ~= tone.actual.senary and tone.actual.senary == 2 then
			it = it .. tone.canonical.senary .. '*';
		else
			it = it .. tone.actual.senary;
		end
		it = it ..  '</sup>';
	end
	if punctuation then
		it = it .. 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_jyutping (initial_consonant, root, tone, punctuation, capped_p, all_caps_p)
	local it = '';
	if initial_consonant then
		it = it .. initial_consonant;
	end
	if root then
		it = it .. root;
	end
	if tone then
		it = it .. '<span class="jpingtone">';	-- mimic jpingauto
		if tone.canonical and tone.canonical.senary ~= tone.actual.senary then
			it = it .. tone.canonical.senary .. '*';
		end
		it = it .. tone.actual.senary .. '</span>';
	end
	if punctuation then
		it = it .. 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.senary
			and tone_mapping.senary.prefix
			and tone_mapping.senary.prefix[tone.actual.senary] then

		it = it .. tone_mapping.senary.prefix[tone.actual.senary];
	elseif tone
			and tone_mapping.novenary
			and tone_mapping.novenary.prefix
			and tone_mapping.novenary.prefix[tone.actual.novenary] then

		it = it .. tone_mapping.novenary.prefix[tone.actual.novenary];
	end
	if initial_consonant then
		it = it .. initial_consonant;
	end
	if root then
		it = it .. root;
	end
	if tone and tone_mapping.senary
			and tone_mapping.senary.suffix
			and tone_mapping.senary.suffix[tone.actual.senary] then

		it = it .. tone_mapping.senary.suffix[tone.actual.senary];
	elseif tone
			and tone_mapping.novenary
			and tone_mapping.novenary.suffix
			and tone_mapping.novenary.suffix[tone.actual.novenary] then

		it = it .. tone_mapping.novenary.suffix[tone.actual.novenary];
	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_hkie (initial_consonant, root, tone, punctuation, capped_p, all_caps_p, id)
	local it = '';
	if initial_consonant then
		it = it .. initial_consonant;
	end
	if root then
		it = it .. root;
	end
	if tone then
		it = it .. tone_mappings[id].novenary.suffix[tone.actual.novenary];
	end
	if punctuation then
		it = it .. 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_hkie_1971 (initial_consonant, root, tone, punctuation, capped_p, all_caps_p)
	return reconstruct_hkie (initial_consonant, root, tone, punctuation, capped_p, all_caps_p, 'hkie_1971');
end

local function reconstruct_hkie_1990 (initial_consonant, root, tone, punctuation, capped_p, all_caps_p)
	return reconstruct_hkie (initial_consonant, root, tone, punctuation, capped_p, all_caps_p, 'hkie_1990');
end

local function reconstruct_hkie_2002 (initial_consonant, root, tone, punctuation, capped_p, all_caps_p)
	return reconstruct_hkie (initial_consonant, root, tone, punctuation, capped_p, all_caps_p, 'hkie_2002');
end

local function reconstruct_zhuyin (initial_consonant, root, tone, punctuation, id)
	if initial_consonant == 'ㄧ' and mw.ustring.match(root, '^[ㄧㄩ]') then
		initial_consonant = nil;
	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, 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
		-- don't trust the tables. look at the first jamo and check that it's not a vowel.
		-- if it's a vowel insert the zero consonant
		local det = mw.ustring.codepoint(s);
		if (det >= 0x1160 and det <= 0x11a7) or (det >= 0xd7b0 and det <= 0xd7ca) then
			s = C1[''] . s;
		end
		-- check the last consonant too. if it's a starting consonant we screwed up somewhere.
		det = mw.ustring.codepoint(mw.ustring.sub(s, -1, -1));
		if (det >= 0x1100 and det <= 0x115f) or (det >= 0xa960 and det <= 0xa97f) then
			error('Internal error: ' .. s .. ': Constructed hangul ends with starting consonant U+' .. string.format('%x', det));
		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
	return reconstruct_ipa(nil, s, tone, punctuation, id);
end

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

local function reconstruct_braille (initial_consonant, root, tone, punctuation, id)
	return reconstruct_ipa(initial_consonant, root, tone, punctuation, 'braille');
end

mappings = {
	-- 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'] = {
			[''] = C1[''];
			['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'] = C1[''];
			['h'] = 'ᄒ';	-- U+1112
			['gw'] = { 'ᄀ', 'W' };	-- U+1100
			['kw'] = { 'ᄏ', 'W' };	-- U+110F
			['w'] = { C1[''], 'W' };	-- U+110B
			['z'] = 'ᄌ';	-- U+110C
			['c'] = 'ᄎ';	-- U+110E
			['s'] = 'ᄉ';	-- U+1109
			['j'] = { C1[''], '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.h }; ['W'] = { V.waa, C2.h }; ['Y'] = { V.yaa, C2.h }; };
			['ap'] = { { V.aa, C2.p }; ['W'] = { V.waa, C2.p }; ['Y'] = { V.yaa, C2.p }; };
			['at'] = { { V.aa, C2.t }; ['W'] = { V.waa, C2.t }; ['Y'] = { V.yaa, C2.t }; };
			['ak'] = { { V.aa, C2.k }; ['W'] = { V.waa, C2.k }; ['Y'] = { V.yaa, C2.k }; };
			['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.yeo }; ['W'] = { C1.w, V.weo }; ['Y'] = { C1.y, V.yeo }; };
			['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.i }; };
			['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.i, C2.m }; };
			['in'] = { { V.i, C2.n }; ['W'] = { V.wi, C2.n }; ['Y'] = { V.i, C2.n }; };
			['ing'] = { { V.e, C2.h }; ['W'] = { V.we, C2.h }; ['Y'] = { V.e, C2.h }; };
			['ip'] = { { V.i, C2.b }; ['W'] = { V.wi, C2.b }; ['Y'] = { V.i, C2.b }; };
			['it'] = { { V.i, C2.d }; ['W'] = { V.wi, C2.d }; ['Y'] = { V.i, C2.d }; };
			['ik'] = { { V.e, C2.k }; ['W'] = { V.we, C2.k }; ['Y'] = { V.ye, C2.k }; };
			['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.k }; ['W'] = { V.wo, C2.k }; ['Y'] = { V.yo, C2.k }; };
			['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.o, C2.h }; ['W'] = { V.wo, C2.h }; ['Y'] = { V.yo, C2.h }; };
			['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.oi, C2.s }; ['Y'] = { V.yeu, C2.s }; };
			['eon'] = { { V.eu, C2.nj }; ['W'] = { V.wo, C2.nj }; ['Y'] = { V.yo, C2.nj }; };
			['oeng'] = { { V.eu, C2.ng }; ['W'] = { V.weu, C2.ng }; ['Y'] = { V.yo, C2.ng }; };
			['eot'] = { { V.eu, C2.t }; ['W'] = { V.wo, C2.t }; ['Y'] = { V.yeu, C2.t }; };
			['oet'] = { { V.eu, C2.d }; ['W'] = { V.wo, C2.d }; ['Y'] = { V.yeu, C2.d }; };
			['oek'] = { { V.eu, C2.g }; ['W'] = { V.wo, C2.g }; ['Y'] = { V.yeu, C2.g }; };
			['yu'] = { { V.yi }; ['W'] = { C1.w, V.yi }; ['Y'] = { V.yi }; };
			['yun'] = { { V.yi, C2.n }; ['W'] = { C1.w, V.yi, C2.n }; ['Y'] = { V.yi, C2.n }; };
			['yut'] = { { V.yi, C2.d }; ['W'] = { C1.w, V.yi, C2.d }; ['Y'] = { V.yi, C2.d }; };
			['m'] = 'ㅁ';
			['ng'] = 'ㅇ';	-- or ㄲ
		};
	};
	['ipa_phonemic'] = {
		['label'] = 'IPA';
		['link'] = '黃錫凌式音標';
		['prefix'] = '<span class=IPA>';
		['suffix'] = '</span>';
		['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';
			['sh'] = 'ʃ';	-- non-standard notation added to handle actual speech
			['sj'] = 'ʃ';	-- non-standard notation added to handle actual speech
			['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'] = 'ɛŋ';
			['ep'] = 'ɛp';
			['et'] = 'ɛt';
			['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';	-- FIXME non-standard, clash
			['oet'] = 'œt';
			['oek'] = 'œk';
			['yu'] = 'y';
			['yun'] = 'yn';
			['yut'] = 'yt';
			['m'] = 'm';
			['ng'] = 'ŋ';
		};
	};
	-- source: [[粵語注音符號]], with modifications
	['ipa_phonetic'] = {
		['label'] = 'IPA';
		['link'] = '國際音標';
		['sublabel'] = '嚴式';
		['prefix'] = '<span class=IPA>[ ';	-- U+200A hair space
		['suffix'] = ' ]</span>';	-- 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';
			['sh'] = 'ʃ';	-- non-standard notation added to handle actual speech
			['sj'] = 'ʃ';	-- non-standard notation added to handle actual speech
			['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'] = 'ɛ:ŋ';
			['ep'] = 'ɛp';
			['et'] = 'ɛt';
			['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̚';
			['oet'] = 'œt̚';
			['oek'] = 'œ:k̚';
			['yu'] = 'y:';
			['yun'] = 'y:n';
			['yut'] = 'yt̚';
			['m'] = 'm̩';
			['ng'] = 'ŋ̩';
		};
	};
	-- Jyutping
	['jyutping'] = {
		['label'] = '粵拼';
		['link'] = '香港語言學學會粵語拼音方案';
		['f'] = reconstruct_jyutping;
		['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'] = 'z';
			['c'] = 'c';
			['s'] = 's';
			['sh'] = 'sʲ';	-- non-standard notation added to handle actual speech
			['sj'] = 'sʲ';	-- non-standard notation added to handle actual speech
			['j'] = 'j';
		};
		['finals'] = {
			['aa'] = 'aa';
			['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'] = 'eŋ';
			['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'] = 'oe';
			['eoi'] = 'oei';
			['eon'] = 'oen';
			['oeng'] = 'oeŋ';
			['eot'] = 'eot';
			['oet'] = 'oet';
			['oek'] = 'oek';
			['yu'] = 'yu';
			['yun'] = 'yun';
			['yut'] = 'yut';
			['m'] = 'm';
			['ng'] = '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';
			['sh'] = 'sʰ';	-- non-standard notation added to handle actual speech
			['sj'] = 'sʰ';	-- non-standard notation added to handle actual speech
			['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';
			['oet'] = 'êd';	-- NOTE non-standard, clash
			['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';
			['sh'] = 'sʰ';	-- non-standard notation added to handle actual speech
			['sj'] = 'sʰ';	-- non-standard notation added to handle actual speech
			['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';
			['oet'] = 'êd';	-- NOTE non-standard, clash
			['oek'] = 'êg';
			['yu'] = 'ü';
			['yun'] = 'ün';
			['yut'] = 'üd';
			['m'] = 'm';
			['ng'] = 'ng';
		};
	};
	-- source: [[教育學院拼音方案]], [[:fr:Romanisation de l'Institut d'éducation de Hong Kong]] (i.e. my own research)
	['hkie_1971'] = {
		['label'] = '教育學院';
		['sublabel'] = '原型';
		['link'] = '教育學院拼音方案';
		['f'] = reconstruct_hkie_1971;
		['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'] = 'dz';
			['c'] = 'ts';
			['s'] = 's';
			['sh'] = 'sʲ';	-- non-standard notation added to handle actual speech
			['sj'] = 'sʲ';	-- non-standard notation added to handle actual speech
			['j'] = 'j';
		};
		['finals'] = {
			['aa'] = 'a';	-- NOTE single a
			['aai'] = 'aai';
			['aau'] = 'aau';
			['aam'] = 'aam';
			['aan'] = 'aan';
			['aang'] = 'aang';
			['aap'] = 'aap';
			['aat'] = 'aat';
			['aak'] = 'aak';
			['ai'] = "''a''i";	-- NOTE italic a per Chan (2016), ditto below
			['au'] = "''a''u";
			['am'] = "''am";
			['an'] = "''a''n";
			['ang'] = "''a''ng";
			['ap'] = "''a''p";
			['at'] = "''a''t";
			['ak'] = "''a''k";
			['e'] = 'e';
			['ei'] = 'ei';
			['eu'] = 'eu';
			['eng'] = 'eng';
			['ep'] = 'ep';
			['et'] = 'et';
			['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'] = 'oe';
			['eoi'] = 'oey';
			['eon'] = 'oen';
			['oeng'] = 'oeng';
			['eot'] = 'oet';
			['oet'] = 'oet';	-- NOTE non-standard, clash
			['oek'] = 'oek';
			['yu'] = 'y';
			['yun'] = 'yn';
			['yut'] = 'yt';
			['m'] = 'm';
			['ng'] = 'ng';
		};
	};
	['hkie_1990'] = {
		['label'] = '教育學院';
		['sublabel'] = '1990年版';
		['link'] = '教育學院拼音方案';
		['f'] = reconstruct_hkie_1990;
		['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'] = 'dz';
			['c'] = 'ts';
			['s'] = 's';
			['sh'] = 'sʲ';	-- non-standard notation added to handle actual speech
			['sj'] = 'sʲ';	-- non-standard notation added to handle actual speech
			['j'] = 'j';
		};
		['finals'] = {
			['aa'] = 'a';	-- NOTE single a per Chan (2016); confirmed with my copy of 港式廣東話詞典
			['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';
			['eu'] = 'eu';
			['eng'] = 'eng';
			['ep'] = 'ep';
			['et'] = 'et';
			['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'] = 'oe';
			['eoi'] = 'oey';
			['eon'] = 'oen';
			['oeng'] = 'oeng';
			['eot'] = 'oet';
			['oet'] = 'oet';	-- NOTE non-standard, clash
			['oek'] = 'oek';
			['yu'] = 'y';
			['yun'] = 'yn';
			['yut'] = 'yt';
			['m'] = 'm';
			['ng'] = 'ng';
		};
	};
	['hkie_2002'] = {
		['label'] = '教育學院';
		['sublabel'] = '2002年版';
		['link'] = '教育學院拼音方案';
		['f'] = reconstruct_hkie_2002;
		['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'] = 'dz';
			['c'] = 'ts';
			['s'] = 's';
			['sh'] = 'sʲ';	-- non-standard notation added to handle actual speech
			['sj'] = 'sʲ';	-- non-standard notation added to handle actual speech
			['j'] = 'j';
		};
		['finals'] = {
			['aa'] = 'aa';
			['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';
			['eu'] = 'eu';
			['eng'] = 'eng';
			['ep'] = 'ep';
			['et'] = 'et';
			['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'] = 'oe';
			['eoi'] = 'oey';
			['eon'] = 'oen';
			['oeng'] = 'oeng';
			['eot'] = 'oet';
			['oet'] = 'oet';	-- NOTE non-standard, clash
			['oek'] = 'oek';
			['yu'] = 'y';
			['yun'] = 'yn';
			['yut'] = 'yt';
			['m'] = 'm';
			['ng'] = 'ng';
		};
	};
	-- source: [[劉錫祥拼音]]
	['sidneylau'] = {
		['label'] = '劉錫祥';
		['link'] = '劉錫祥拼音';
		['f'] = reconstruct_sidneylau;
		['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';
			['sh'] = 'sʰ';	-- non-standard notation added to handle actual speech
			['sj'] = 'sʰ';	-- non-standard notation added to handle actual speech
			['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'] = 'oh';
			['oi'] = 'oi';
			['ou'] = 'o';
			['on'] = 'on';
			['ong'] = 'ong';
			['ot'] = 'ot';
			['ok'] = 'ok';
			['u'] = 'oo';
			['ui'] = 'ooi';
			['un'] = 'oon';
			['ung'] = 'ung';
			['ut'] = 'oot';
			['uk'] = 'uk';
			['oe'] = 'œ';
			['eoi'] = 'ui';
			['eon'] = 'un';
			['oeng'] = 'eung';
			['eot'] = 'ut';
			['eot'] = 'eut';	-- NOTE non-standard
			['oek'] = 'euk';
			['yu'] = 'ue';
			['yun'] = 'uen';
			['yut'] = 'uet';
			['m'] = 'm';
			['ng'] = 'ng';
		};
	};
	-- source: [[:en:Yale_romanization_of_Cantonese]]
	['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';
			['sh'] = 'sʰ';	-- non-standard notation added to handle actual speech
			['sj'] = 'sʰ';	-- non-standard notation added to handle actual speech
			['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';
			['oet'] = 'eut';	-- NOTE non-standard, clash
			['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|32px]]';
			['kw'] = '[[File:Bpmf-kw.svg|32px]]';
			['w'] = 'ㄨ';
			['z'] = 'ㄐ';
			['c'] = 'ㄑ';
			['s'] = 'ㄒ';
			--['sh'] = '???ʰ';	-- FIXME non-standard notation need to be added to handle actual speech
			--['sj'] = '???ʰ';	-- FIXME non-standard notation need to be added to handle actual speech
			['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|32px]]';
			['eoi'] = '[[File:Bpmf-oe.svg|32px]]ㄧ';
			['eon'] = '[[File:Bpmf-oe.svg|32px]]ㄋ';
			['oeng'] = '[[File:Bpmf-oe.svg|32px]]ㄫ';
			['eot'] = '[[File:Bpmf-oe.svg|32px]]ㄊ';
			['oet'] = '[[File:Bpmf-oe.svg|32px]]ㄊ';	-- NOTE non-standard, clash
			['oek'] = '[[File:Bpmf-oe.svg|32px]]ㄎ';
			['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|32px]]';
			['c'] = '[[File:Bpmf-ci.svg|32px]]';
			['s'] = '[[File:Bpmf-si.svg|32px]]';
			--['sh'] = '???ʰ';	-- FIXME non-standard notation need to be added to handle actual speech
			--['sj'] = '???ʰ';	-- FIXME non-standard notation need to be added to handle actual speech
			['j'] = 'ㄧ';
		};
		['finals'] = {
			['aa'] = 'ㄚ';
			['aai'] = 'ㄞ';
			['aau'] = 'ㄠ';
			['aam'] = '[[File:Bpmf-aam.svg|32px]]';
			['aan'] = 'ㄢ';
			['aang'] = 'ㄤ';
			['aap'] = 'ㄚ<sub>ㄆ</sub>';
			['aat'] = 'ㄚ<sub>ㄊ</sub>';
			['aak'] = 'ㄚ<sub>ㄎ</sub>';
			['ai'] = '[[File:Bpmf-aq.svg|32px]]ㄧ';
			['au'] = '[[File:Bpmf-aq.svg|32px]]ㄨ';
			['am'] = '[[File:Bpmf-aq.svg|32px]]ㆬ';
			['an'] = '[[File:Bpmf-aq.svg|32px]]ㄣ';
			['ang'] = '[[File:Bpmf-aq.svg|32px]]ㄥ';
			['ap'] = '[[File:Bpmf-aq.svg|32px]]<sub>ㄆ</sub>';
			['at'] = '[[File:Bpmf-aq.svg|32px]]<sub>ㄊ</sub>';
			['ak'] = '[[File:Bpmf-aq.svg|32px]]<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|32px]]';
			['ong'] = '[[File:Bpmf-aong.svg|32px]]';
			['ot'] = 'ㄛ<sub>ㄊ</sub>';
			['ok'] = 'ㄛ<sub>ㄎ</sub>';
			['u'] = 'ㄨ';
			['ui'] = 'ㄨㄧ';
			['un'] = 'ㄨㄣ';
			['ung'] = 'ㄨㄥ';
			['ut'] = 'ㄨ<sub>ㄊ</sub>';
			['uk'] = 'ㄨ<sub>ㄎ</sub>';
			['oe'] = '[[File:Bpmf-eu.svg|32px]]';
			['eoi'] = '[[File:Bpmf-eu.svg|32px]]ㄧ';
			['eon'] = '[[File:Bpmf-eu.svg|32px]]ㄣ';
			['oeng'] = '[[File:Bpmf-eu.svg|32px]]ㄥ';
			['eot'] = '[[File:Bpmf-eu.svg|32px]]<sub>ㄊ</sub>';
			['oet'] = '[[File:Bpmf-eu.svg|32px]]<sub>ㄊ</sub>';	-- NOTE non-standard, clash
			['oek'] = '[[File:Bpmf-eu.svg|32px]]<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|32px]]';
			['kw'] = '[[File:Bpmf-kw.svg|32px]]';
			['w'] = 'ㄨ';
			['z'] = 'ㄐ';
			['c'] = 'ㄑ';
			['s'] = 'ㄒ';
			--['sh'] = '???ʰ';	-- FIXME non-standard notation need to be added to handle actual speech
			--['sj'] = '???ʰ';	-- FIXME non-standard notation need to be added to handle actual speech
			['j'] = 'ㄧ';
		};
		['finals'] = {
			['aa'] = 'ㄚ';
			['aai'] = 'ㄞ';
			['aau'] = 'ㄠ';
			['aam'] = 'ㄚ' .. dot;	-- dot = U+323
			['aan'] = 'ㄢ';
			['aang'] = 'ㄤ';
			['aap'] = 'ㄚ' .. dot;
			['aat'] = 'ㄚ' .. dot;
			['aak'] = 'ㄚ' .. dot;
			['ai'] = 'ㄟ';
			['au'] = 'ㄡ';
			['am'] = '[[File:Bpmf-aq.svg|32px]]' .. dot;
			['an'] = 'ㄣ';
			['ang'] = 'ㄥ';
			['ap'] = '[[File:Bpmf-aq.svg|32px]]' .. dot;
			['at'] = '[[File:Bpmf-aq.svg|32px]]' .. dot;
			['ak'] = '[[File:Bpmf-aq.svg|32px]]' .. dot;
			['e'] = 'ㄝ';
			['ei'] = 'ㄝㄧ';
			['eu'] = 'ㄝㄨ';
			['em'] = 'ㄝ' .. dot;
			['en'] = 'ㄝ' .. dot;
			['eng'] = 'ㄝ' .. dot;
			['ep'] = 'ㄝ' .. dot;
			['et'] = 'ㄝ' .. dot;
			['ek'] = 'ㄝ' .. dot;
			['i'] = 'ㄧ';
			['iu'] = 'ㄧㄨ';
			['im'] = 'ㄧ' .. dot;
			['in'] = 'ㄧ' .. dot;
			['ing'] = 'ㄧ' .. dot;
			['ip'] = 'ㄧ' .. dot;
			['it'] = 'ㄧ' .. dot;
			['ik'] = 'ㄧ' .. dot;
			['o'] = 'ㄛ';
			['oi'] = 'ㄛㄧ';
			['ou'] = 'ㄛㄨ';
			['on'] = 'ㄛ' .. dot;
			['ong'] = 'ㄛ' .. dot;
			['ot'] = 'ㄛ' .. dot;
			['ok'] = 'ㄛ' .. dot;
			['u'] = 'ㄨ';
			['ui'] = 'ㄨㄧ';
			['un'] = 'ㄨ' .. dot;
			['ung'] = 'ㄨ' .. dot;
			['ut'] = 'ㄨ' .. dot;
			['uk'] = 'ㄨ' .. dot;
			['oe'] = '[[File:Bpmf-oe.svg|32px]]';
			['eoi'] = '[[File:Bpmf-oe.svg|32px]]ㄧ';
			['eon'] = '[[File:Bpmf-oe.svg|32px]]' .. dot;
			['oeng'] = '[[File:Bpmf-oe.svg|32px]]' .. dot;
			['eot'] = '[[File:Bpmf-oe.svg|32px]]' .. dot;
			['oet'] = '[[File:Bpmf-oe.svg|32px]]' .. dot;	-- NOTE non-standard, clash
			['oek'] = '[[File:Bpmf-oe.svg|32px]]' .. dot;
			['yu'] = 'ㄩ';
			['yun'] = 'ㄩ' .. dot;
			['yut'] = 'ㄩ' .. dot;
			['m'] = 'ㄇ';
			['ng'] = 'ㄫ';
		};
	};
	-- source: [[粵語點字]]
	['braille'] = {
		['label'] = '點字';
		['link'] = '粵語點字';
		['f'] = reconstruct_braille;
		['initials'] = {
			[''] = '';
			['b'] = '⠏';
			['p'] = '⠯';
			['m'] = '⠍';
			['f'] = '⠋';
			['d'] = '⠞';
			['t'] = '⠾';
			['n'] = '⠝';
			['l'] = '⠇';
			['g'] = '⠅';
			['k'] = '⠗';
			['ng'] = '⠛';
			['h'] = '⠓';
			['gw'] = '⠟';
			['kw'] = '⠻';
			['w'] = '⠺';
			['z'] = '⠉';
			['c'] = '⠭';
			['s'] = '⠎';
			--['sh'] = '???ʰ';	-- FIXME non-standard notation need to be added to handle actual speech
			['j'] = '⠚';
		};
		['finals'] = {
			['aa'] = '⠃';
			['aai'] = '⠬';
			['aau'] = '⠌';
			['aam'] = '⠜';
			['aan'] = '⠘';
			['aang'] = '⠉';
			['aap'] = '⠏';
			['aat'] = '⠞';
			['aak'] = '⠅';
			['ai'] = '⠩';
			['au'] = '⠡';
			['am'] = '⠸';
			['an'] = '⠫';
			['ang'] = '⠛';
			['ap'] = '⠢';
			['at'] = '⠔';
			['ak'] = '⠨';
			['e'] = '⠑';
			['ei'] = '⠓';
			['eng'] = '⠶';
			['ek'] = '⠺';
			['i'] = '⠊';
			['iu'] = '⠽';
			['im'] = '⠖';
			['in'] = '⠲';
			['ing'] = '⠴';
			['ip'] = '⠯';
			['it'] = '⠾';
			['ik'] = '⠗';
			['o'] = '⠕';
			['oi'] = '⠣';
			['ou'] = '⠧';
			['on'] = '⠝';
			['ong'] = '⠰';
			['ot'] = '⠋';
			['ok'] = '⠻';
			['u'] = '⠥';
			['ui'] = '⠳';
			['un'] = '⠮';
			['ung'] = '⠦';
			['ut'] = '⠵';
			['uk'] = '⠟';
			['oe'] = '⠱';
			['eoi'] = '⠚';
			['eon'] = '⠎';
			['oeng'] = '⠒';
			['eot'] = '⠭';
			['oet'] = '⠭';	-- XXX non-standard, clash; not described in article, guessing
			['oek'] = '⠪';
			['yu'] = '⠹';
			['yun'] = '⠆';
			['yut'] = '⠷';
			['m'] = '⠍';	-- XXX not described in article, guessing
			['ng'] = '⠛';	-- XXX not described in article, guessing
		};
	};
};

local function yalify_syllable (s, id)
	local it;
	if id == nil then
		id = default_scheme;
	end
	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_part1_syllabic_m = '^()(m)';
		local pat_part1_syllabic_ng = '^()(ng)';
		local pat_part1 = '^([bcdfghjklmnpstwz]*)([a-z]+)';
		local pat_tone0 = '([123456789¹²³⁴⁵⁶⁷⁸⁹₁₂₃₄₅₆₇₈₉])[%*%-]';
		local pat_tone = '([123456789¹²³⁴⁵⁶⁷⁸⁹₁₂₃₄₅₆₇₈₉])';
		local pat_part2 = '([^%a]-)$';
		local initial, final, tone0, tone, punctuation;

		s = mw.ustring.lower(s);

		-- syllabic m?
		if not tone then
			initial, final, tone0, tone, punctuation = mw.ustring.match(s, pat_part1_syllabic_m .. pat_tone0 .. pat_tone .. pat_part2);
			if not tone then
				initial, final, tone, punctuation = mw.ustring.match(s, pat_part1_syllabic_m .. pat_tone .. pat_part2);
			end
			initial = '';
		end

		-- syllabic ng?
		if not tone then
			initial, final, tone0, tone, punctuation = mw.ustring.match(s, pat_part1_syllabic_ng .. pat_tone0 .. pat_tone .. pat_part2);
			if not tone then
				initial, final, tone, punctuation = mw.ustring.match(s, pat_part1_syllabic_ng .. pat_tone .. pat_part2);
			end
			initial = '';
		end

		-- normal syllables?
		if not tone then
			initial, final, tone0, tone, punctuation = mw.ustring.match(s, pat_part1 .. pat_tone0 .. pat_tone .. pat_part2);
			if not tone then
				initial, final, tone, punctuation = mw.ustring.match(s, pat_part1 .. pat_tone .. pat_part2);
			end
		end

		-- figure out what the actual tone(s) is (are) and convert into an "object"
		-- tone value in usual 6-tone system is tone.actual.senary
		if tone then
			tone = tonumber(mw.ustring.toNFKC(tone));	-- remove superscripting and subscripting, then convert to number
			if tone0 then
				tone0 = tonumber(mw.ustring.toNFKC(tone0));
			end
			local novenary_tone = tone;
			local novenary_tone0 = tone0;
			if final:match('[ptk]$') then
				if tone == 1 then
					novenary_tone = 7;
				elseif tone == 3 then
					novenary_tone = 8;
				elseif tone == 6 then
					novenary_tone = 9;
				elseif tone == 7 then
					tone = 1;
				elseif tone == 8 then
					tone = 3;
				elseif tone == 9 then
					tone = 6;
				end
				if tone0 then
					if tone0 == 1 then
						novenary_tone0 = 7;
					elseif tone0 == 3 then
						novenary_tone0 = 8;
					elseif tone0 == 6 then
						novenary_tone0 = 9;
					elseif tone0 == 7 then
						tone0 = 1;
					elseif tone0 == 8 then
						tone0 = 3;
					elseif tone0 == 9 then
						tone0 = 6;
					end
				end
			end
			tone = {
				['actual'] = {
					['senary'] = tone;
					['quaternary'] = ((novenary_tone - 1) % 3 + 1) * (1 - math.floor((novenary_tone - 1)/6)) + math.floor((novenary_tone - 1)/6) * 4;
					['novenary'] = novenary_tone;
					['feminine_p'] = tone < 4;
					['masculine_p'] = tone > 3;
				};
			};
			if tone0 then
				tone.canonical = {
					['senary'] = tone0;
					['quaternary'] = ((novenary_tone0 - 1) % 3 + 1) * (1 - math.floor((novenary_tone0 - 1)/6)) + math.floor((novenary_tone0 - 1)/6) * 4;
					['novenary'] = novenary_tone0;
					['feminine_p'] = tone0 < 4;
					['masculine_p'] = tone0 > 3;
				};
			end
		end

		if initial then
			if initials[initial] then
				initial = initials[initial];
			end
			if finals[final] then
				final = finals[final];
			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
		--it = p.cvs({initial = initial, final = final, tone = tone});--XXXZZZ
	end
	if it then
		it = it:gsub('(%[%[File:[^%[%]]+%]%])', '<span class=img-as-char>%1</span>');
	end
	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_part1 = '[%a]+[1-9¹²³⁴⁵⁶⁷⁸⁹₁₂₃₄₅₆₇₈₉]';
	local pat_part2 = '%*[1-9¹²³⁴⁵⁶⁷⁸⁹₁₂₃₄₅₆₇₈₉]';
	local pat1 = '^(' .. pat_part1 .. ')';
	local pat2 = '^(' .. pat_part1 .. pat_part2 .. ')';
	while s and #s > 0 do
		local pre = '';
		while s and #s > 0 and not mw.ustring.match(s, pat1) 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, pat2 .. '(.*)$');
		if m then
			table.insert(it, m);
		else
			m, post = mw.ustring.match(s, pat1 .. '(.*)$');
			if m then
				table.insert(it, m);
			end
		end
		s = post;
	end
	return it;
end

local function blacklisted_p (s)
	local it = false;
	for _, det in pairs(break_into_segments(s)) do
		for i, pat in pairs(jyutping_blacklist) do
			if mw.ustring.match(det, pat) then
				it = true;
			end
		if it then break end;
		end
	if it then break end;
	end
	return it;
end

local function make_transcription( scheme, s, ruby_mode_p, ruby )
	local it = '';
	local annotation;
	if ruby_mode_p then
		for i = 1, #ruby do
			local entity = ruby[i];
			if entity.rt then
				if scheme == 'jyutping' then
					annotation = ' title="對應 IPA:' .. remove_tags(yalify(break_into_segments(entity.rt), 'ipa_phonemic')) .. '"';
				else
					annotation = ' title="對應粵拼:' .. remove_tags(entity.rt) .. '"';
				end
				it = it .. '<ruby><rb>' .. entity.rb .. '</rb><rp> (</rp><rt>';
				it = it .. '<span class=transl-yue-ruby data-type=' .. scheme .. annotation .. '>';
				it = it .. yalify(break_into_segments(entity.rt), scheme);
				it = it .. '</span>';
				it = it .. '</rt><rp>) </rp></ruby>';
			else
				it = it .. entity.rb;
			end
		end
	else
		if scheme == 'jyutping' then
			annotation = ' title="對應 IPA:' .. remove_tags(yalify(break_into_segments(s), 'ipa_phonemic')) .. '"';
		else
			annotation = ' title="對應粵拼:' .. remove_tags(s) .. '"';
		end
		it = it .. '<span class=transl-yue data-type=' .. scheme .. annotation .. '>';
		it = it ..  yalify(break_into_segments(s), scheme);
		it = it .. '</span>';
	end
	return it;
end

-- Entry point
p.ruby = function( frame )
	return p.yalify(frame, true);
end

-- Entry point
p.yalify = function (frame, ruby_mode_p)
	local parent = frame:getParent();
	local s;
	local scheme = nil;
	local variant;
	local label_p;
	local ruby, rt_mode_p;
	local vernacular, literary;
	local borrowed, approximation;
	for _, a in pairs({frame, parent}) do
		if a then
			for k, v in pairs(a.args) do
				v = v:gsub('^%s+', ''):gsub('%s+$', '');
				if type(k) == 'number' then
					if ruby_mode_p then
						if not ruby then
							ruby = {};
						end
						if rt_mode_p then
							if #v > 0 then
								ruby[#ruby].rt = v;
							end
							rt_mode_p = false;
						else
							table.insert(ruby, { ['rb'] = v; });
							rt_mode_p = true;
						end
					else
						if s then
							s = s .. ' ' .. v;
						else
							s = v;
						end
					end
				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);
				elseif k == 'vernacular' or k == 'colloquial' or k == '白' then
					vernacular = v;
				elseif k == 'literary' or k == 'formal' or k == '文' then
					literary = v;
				elseif k == 'borrowed' or k == '粵化' or k == '借' then
					borrowed = v;
				elseif k == 'approx' or k == '類似' or k == '類' or k == '似' then
					approximation = v;
				else
					error('不明參數 「' .. k .. '」');
				end
			end
		end
	end
	if not scheme then
		for _, det in pairs({s, vernacular, literary, borrowed, approximation}) do
			if det and blacklisted_p(det) then
				scheme = fallback_scheme;
			end
		if scheme then break end;
		end
		if not scheme then
			scheme = default_scheme;
		end
	end
	if scheme and not schemes[scheme] then
		if schemes[mw.ustring.lower(scheme)] then
			scheme = mw.ustring.lower(scheme);
		else
			error('唔知 「' .. scheme .. '」 係乜方案');
		end
	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
	if borrowed and (s or vernacular or literary) then
		error('唔可以同時指定粵化讀音同實際嘅粵文讀音');
	elseif approximation and (s or vernacular or literary) then
		error('唔可以同時指定類似粵讀同實際嘅粵文讀音');
	end
	local it = '';
	if label_p and approximation then
		it = it .. '類似';
	end
	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
	if ruby_mode_p and (vernacular or literary) then
		error('注音模式唔可以分文白異讀');
	elseif ruby_mode_p and borrowed then
		error('注音模式唔可以指定係借詞');
	elseif ruby_mode_p and approximation then
		error('注音模式唔可以指定係類似讀音');
	elseif ruby_mode_p then
		it = it .. make_transcription(scheme, s, ruby_mode_p, ruby);
	elseif borrowed and approximation then
		it = it .. make_transcription(scheme, approximation, ruby_mode_p, ruby);
		it = it .. ',<small>[[借詞|粵化口語音]]:</small>';
		it = it .. make_transcription(scheme, borrowed, ruby_mode_p, ruby);
	elseif borrowed then
		it = it .. '<small>[[借詞|粵化口語音]]:</small>';
		it = it .. make_transcription(scheme, borrowed, ruby_mode_p, ruby);
	elseif approximation then
		it = it .. make_transcription(scheme, approximation, ruby_mode_p, ruby);
	elseif s and literary and (not vernacular or s == vernacular) then
		it = it .. make_transcription(scheme, s, ruby_mode_p, ruby);
		it = it .. ',<small>[[文白異讀|文讀]]:</small>';
		it = it .. make_transcription(scheme, literary, ruby_mode_p, ruby);
	elseif s and vernacular and (not literary or s == literary) then
		it = it .. make_transcription(scheme, s, ruby_mode_p, ruby);
		it = it .. ',<small>[[文白異讀|白讀]]:</small>';
		it = it .. make_transcription(scheme, vernacular, ruby_mode_p, ruby);
	elseif literary and vernacular then
		it = it .. '<small>[[文白異讀|白讀]]:</small>';
		it = it .. make_transcription(scheme, vernacular, ruby_mode_p, ruby);
		it = it .. ',<small>[[文白異讀|文讀]]:</small>';
		it = it .. make_transcription(scheme, literary, ruby_mode_p, ruby);
	elseif s and vernacular and literary then
		error('標咗三種讀音,要鏟走其中一種');
	elseif s then
		it = it .. make_transcription(scheme, s, ruby_mode_p, ruby);
	else
		error('冇指定讀音');
	end
	-- request our style sheet
	it = table.concat ({ frame:extensionTag('templatestyles', '', {src=styles}), it });
	return it;
end

local function td_note (mapping, cond_p)
	local it = '';
	if mapping.label and cond_p then
		it = it .. '|' .. mapping.label;
		if mapping.sublabel then
			it = it .. ',' .. mapping.sublabel;
		end
		it = it .. '\n';
	else
		it = it .. '|\n';
	end
	return it;
end

local function td_seealso (mapping, cond_p)
	local it = '';
	if mapping.link and cond_p then
		it = it .. '|[[' .. mapping.link .. ']]\n';
	else
		it = it .. '|\n';
	end
	return it;
end

p.dump_schemes = function (frame)
	local known_schemes = {};
	local scheme_params = {};
	local broken = '冇定義,純粹楔位';
	local unk = 'style="background:#ececec;color:#2C2C2C;font-size:smaller;text-align:center" class=table-na'; -- copied from Template:Unk
	local q = require('模組:漢字/sort');
	for k, v in pairs(schemes) do	-- invert the schemes table and keep an array that can be sorted
		if known_schemes[v] then
			table.insert(known_schemes[v], k);
		else
			known_schemes[v] = { k };
		end
		table.insert(scheme_params, k);
	end
	table.sort(scheme_params, q.by_stroke_count);
	local it = '';
	it = it .. '{|class="sortable wikitable"\n';
	it = it .. '!colspan=3|方案\n';
	it = it .. '!colspan=5|版本\n';
	it = it .. '|-\n';
	it = it .. '!參數\n';
	it = it .. '!識別\n';
	it = it .. '!預設版本\n';
	it = it .. '!參數\n';
	it = it .. '!識別\n';
	it = it .. '!總識別\n';
	it = it .. '!註\n';
	it = it .. '!睇埋\n';
	it = it .. '|-\n';
	for i = 1, #scheme_params do
		local scheme_param = scheme_params[i];
		local scheme_id = schemes[scheme_param];
		local known_variants = {};
		local variant_params = {};
		if variants[scheme_id] then
			for k, v in pairs(variants[scheme_id]) do	-- invert the variants table
				if known_variants[v] then
					table.insert(known_variants[v], k);
				else
					known_variants[v] = { k };
				end
				table.insert(variant_params, k);
			end
		end
		table.sort(variant_params, q.by_stroke_count);
		local td = '|';
		if #variant_params > 1 and scheme_param == scheme_id then
			td = td .. 'rowspan=' .. #variant_params .. '|';
		end
		if scheme_param == scheme_id then
			it = it .. td_insert_attr(td_insert_attr(td, 'colspan=2 align=center'),
					'data-sort-value=' .. q.data_sort_value(scheme_param))
					.. "'''" .. scheme_param .. "'''\n";

		else
			it = it .. td_insert_attr(td, 'data-sort-value=' .. q.data_sort_value(scheme_param))
					.. scheme_param .. '\n';

			it = it .. td .. scheme_id .. '\n';
		end
		if default_variants[scheme_id] then
			it = it .. td .. default_variants[scheme_id] .. '\n';
		else
			it = it .. td_insert_attr(td, unk) .. '唔適用\n';
		end
		if scheme_param ~= scheme_id then
			if mappings[scheme_id] then
				-- to determine whether scheme_id refers to a scheme or a variant,
				-- repeat the scheme_param ~= scheme_id test with scheme_id as
				-- "scheme_param", we'll get our "scheme_id" from the schemes table
				-- just as we did at the beginning of the outer loop
				local det = schemes[scheme_id];
				if scheme_id ~= det then
					it = it .. '|colspan=5|請睇總識別碼 「' .. scheme_id .. '」\n';
				else
					it = it .. '|colspan=5|請睇方案 「' .. scheme_id .. '」\n';
				end
			else
				it = it .. '|colspan=5|請睇方案 「' .. scheme_id .. '」\n';
			end
			it = it .. '|-\n';
		elseif #variant_params == 0 then
			it = it .. '|colspan=2 ' .. unk .. '|唔適用\n';
			it = it .. '|' .. scheme_id .. '\n';
			if mappings[scheme_id] then
				it = it .. td_note(mappings[scheme_id], true);
				it = it .. td_seealso(mappings[scheme_id], true);
			else
				it = it .. '|colspan=2|' .. broken .. '\n';
			end
			it = it .. '|-\n';
		else
			for j = 1, #variant_params do
				local variant_name = variant_params[j];
				local variant_id = variants[scheme_id][variant_name];
				local id = scheme_id .. '_' .. variant_id;
				local mod1 = '';
				if variant_name == variant_id then
					if known_schemes[id] then
						mod1 = "'''";
					end
					it = it .. "|colspan=2 align=center data-sort-value="
							.. q.data_sort_value(variant_name) .. "|'''" .. variant_name .. "'''\n";

				else
					it = it .. '|data-sort-value=' .. q.data_sort_value(variant_name) .. '|'
							.. variant_name .. '\n';

					it = it .. '|' .. variant_id .. '\n';
				end
				it = it .. '|' .. mod1 .. id .. mod1 .. '\n';
				if mappings[id] then
					it = it .. td_note(mappings[id], variant_name == variant_id);
					it = it .. td_seealso(mappings[id], variant_name == variant_id);
				else
					it = it .. '|colspan=2 align=center|' .. broken .. '\n';
				end
				it = it .. '|-\n';
			end
		end
	end
	it = it .. '|}\n';
	-- request our style sheet
	it = table.concat ({ frame:extensionTag('templatestyles', '', {src=styles}), "\n", it });
	return it;
end

p.test = function ()
	assert(blacklisted_p('shep1 pat4'));
	assert(not blacklisted_p('sep1 pat4'));
end

return p;