Module:User:Erutuon/mh-pronunc

-- This is still a work in progress.

local export = {}

local concat = table.concat local find = mw.ustring.find local gsplit = mw.text.gsplit local gsub = mw.ustring.gsub local insert = table.insert local lower = mw.ustring.lower local split = mw.text.split local trim = mw.text.trim

local LP = "%(" local RP = "%)"

local LQ = LP.."?" local RQ = RP.."?"

local ASYLL = "̯" local ASYLLTIE = "᷼" local DENT = "̪" local DEVO = "̥" local DEVO2 = "̊" local DOWN = "̞" local RETR = "̠" local TIE = "͡" local TIE2 = "͜" local UNREL = "̚" local UP = "̝"

local C2 = "[ʲˠʷ]" local NG1 = "[^ɦ_]" local NG = NG1..C2 local C = "."..C2 local V_ = "æɛeiɑʌɤɯɒɔouï" local V = "["..V_.."]" local NV = "[^"..V_.."]" local S = "[%s%-]*"

local function gsub2(text, patt, subst) text = gsub(text, patt, subst) text = gsub(text, patt, subst) return text end

local function gsubx(text, patt, subst) local oldText repeat oldText = text text = gsub(text, patt, subst) until text == oldText return text end

local function insertUnique(seq, value) for _, value2 in pairs(seq) do		if value == value2 then return end end insert(seq, value) end

local function parseBoolean(text) local z = false if text then text = trim(text) if text ~= "" and text ~= "0" and lower(text) ~= "false" then z = true end end return z end

local function parse(code) local seq, subst, temp code = trim(code) seq = {} for text in gsplit(code, "%s*,[%s,]*") do		text = trim(text) if text ~= "" then text = " "..lower(text).." " temp = gsub(text, "[abdeghijklmnprtwy_&'%-%s]", "") if temp ~= "" then error("'"..code.."' contains unsupported characters: "..temp) end -- recognize "y_", "h_", "w_", "_y", "_h", "_w" as pseudo-glides subst = { ["h"] = "0ˠ", ["w"] = "0ʷ", ["y"] = "0ʲ" } text = gsub(text, "_*([hwy])_+", subst) text = gsub(text, "_+([hwy])", subst) if find(text, "_") then error("contains misplaced underscores: "..code) end text = gsub(text, "0", "_") -- recognize "ng", but not plain "g" -- "ngw" is a special sequence text = gsub(text, "ngw?", {				["ng"] = "ŋˠ",				["ngw"] = "ŋʷ"			}) if find(text, "g") then error("contains g that is not part of ng: "..code) end -- "kw", "lh", "lw", "mh", "nh", "nw", "rw" are special sequences -- recognize both these and plain "k", "l", "m", "n", "r" -- but "kh", "mw", "rh" are not special sequences text = gsub(text, "[klmnr][hw]?", {				["k"] = "kˠ",				["kh"] = "kˠh", -- N\A				["kw"] = "kʷ",				["l"] = "lʲ",				["lh"] = "lˠ",				["lw"] = "lʷ",				["m"] = "mʲ",				["mh"] = "mˠ",				["mw"] = "mʲw", -- N\A				["n"] = "nʲ",				["nh"] = "nˠ",				["nw"] = "nʷ",				["r"] = "rˠ",				["rh"] = "rˠh", -- N\A				["rw"] = "rʷ"			}) -- "passing over lightly" text = gsub(text, "yi'+y", "ĭʲ") -- "dwelling upon" text = gsub(text, "'+yiy", "īʲ") -- a plain /i/ protected from dialect-specific reflexes text = gsub(text, "'+i", "ï") -- convert remaining sequences to internal format text = gsub(text, "[abdehijptwy&']", {				["a"] = "æ",				["b"] = "pˠ",				["d"] = "rʲ",				["e"] = "ɛ",				["&"] = "e",				["h"] = "ɦˠ",				["i"] = "i",				["j"] = "tʲ",				["p"] = "pʲ",				["t"] = "tˠ",				["w"] = "ɦʷ",				["y"] = "ɦʲ",				["'"] = ""			}) -- treat initial /ɦˠɦˠ/ as a special consonant text = gsub(text, "("..NV..")ɦˠɦˠ("..V..")", "%1ɣˠ%2") -- enforce /CVC/, /CVCVC/, /CVCCVC/ phonotactics -- but allow /(_)VC/, /CV(_)/ at affix boundaries -- where a vowel may link to another morpheme's consonant temp = gsub(text, S, "") if find(temp, "_."..C) or find(temp, C.."_") then error("pseudo-glides may not neighbor a consonant") end if find(temp, V.."_."..V) then error("pseudo-glides may only be at the beginning or end"..code) end if find(temp, V..V) then error("vowels must be separated by a consonant: "..code) end if find(temp, C..C..C) then error("consonant clusters are limited to two: "..code) end if find(temp, C..C.."$") then error("may not end with a consonant cluster: "..code) end gsub(temp, "^("..C..")("..C..")", function(a, b)				if a ~= b then					error( "may only begin with single or geminated consonant: " ..code )				end				return ""			end) text = gsub(text, "%s+", " ") text = trim(text) if text ~= "" then insertUnique(seq, text) end end end return seq end

local function toBender(items, args) -- "1969" is from "Spoken Marshallese" (1969 by Byron W. Bender) -- "med" is from the Marshallese-English Dictionary (1976) -- "mod" is from the Marshallese-English Online Dictionary -- "default" is the same as "mod" but with cedillas beneath consonants local version = args and args.version ~= "" and lower(args.version) or "default" local consSubst = { ["pʲ"] = "p", ["pˠ"] = "b", ["tʲ"] = "j", ["tˠ"] = "t", ["kˠ"] = "k", ["kʷ"] = ({ ["1969"] = "q", ["med"] = "q" })[version] or "kʷ", ["mʲ"] = "m", ["mˠ"] = ({ ["1969"] = "ṁ", ["mod"] = "ṃ" })[version] or "m̧", ["nʲ"] = "n", ["nˠ"] = ({ ["1969"] = "ṅ", ["mod"] = "ṇ" })[version] or "ņ", ["nʷ"] = ({			["1969"] = "n̈", ["med"] = "ņ°", ["mod"] = "ṇʷ"		})[version] or "ņʷ", ["ŋˠ"] = "g", ["ŋʷ"] = ({ ["1969"] = "g̈", ["med"] = "g°" })[version] or "gʷ", ["rʲ"] = "d", ["rˠ"] = "r", ["rʷ"] = ({ ["1969"] = "r̈", ["med"] = "r°" })[version] or "rʷ", ["lʲ"] = "l", ["lˠ"] = ({ ["1969"] = "ƚ", ["mod"] = "ḷ" })[version] or "ļ", ["lʷ"] = ({			["1969"] = "l̈", ["med"] = "ļ°", ["mod"] = "ḷʷ"		})[version] or "ļʷ", ["ĭʲ"] = "yi'y", ["īʲ"] = "'yiy", ["ɣˠ"] = "hh", ["ɦʲ"] = "y", ["ɦˠ"] = "h", ["ɦʷ"] = "w", ["_ʲ"] = "", ["_ˠ"] = "", ["_ʷ"] = "" }	local vowelSubst = { ["æ"] = "a", ["ɛ"] = "e", ["e"] = ({ ["1969"] = "&", ["mod"] = "ẹ" })[version] or "ȩ", ["i"] = "i", ["ï"] = "i" }	local seq = {} for _, text in pairs(items) do		text = gsub(text, C, consSubst) text = gsub(text, V, vowelSubst) insertUnique(seq, text) end return seq end

local function toPhonemic(items) local seq = {} for _, text in pairs(items) do		text = gsub(text, C, {			["kˠ"] = "k",			["ŋˠ"] = "ŋ",			["ĭʲ"] = "ji̯j",			["īʲ"] = "jijj",			["ɣˠ"] = "ɰɰ",			["ɦʲ"] = "j",			["ɦˠ"] = "ɰ",			["ɦʷ"] = "w",			["_ʲ"] = "",			["_ˠ"] = "",			["_ʷ"] = ""		}) text = gsub(text, "ï", "i") insertUnique(seq, text) end return seq end

local function toPhonetic(items, args) -- if enabled, display any palatalized coronal sibilant allophones -- as alveolopalatal sibilants local alvPal = args and parseBoolean(args.alvpal) -- recognize "ralik" for Rālik Chain (western dialect) -- recognize "ratak" for Ratak Chain (eastern dialect) -- for "any", list both possible dialect reflexes where applicable local dialect = args and args.dialect and lower(args.dialect) or "any" if dialect == "rālik" then dialect = "ralik" end -- argument "J" has format like "tstt" -- recognized letters are "t" = plosive, "c" = affricate, "s" = fricative -- letters for initial, medial, final and geminate respectively -- real-world pronunciation said to vary by sociological factors -- but all realizations may occur in free variation local modeJ = split(args.J and lower(args.J) or "tstt", "") local voicelessJ = { ["t"] = "t", ["c"] = "ʦ", ["s"] = "s" } local voicedJ   = { ["t"] = "d", ["c"] = "ʣ", ["s"] = "z" } local initialJ = voicelessJ[modeJ[1] or ""] or "t" local medialJ = voicedJ[modeJ[2] or ""] or "t" local finalJ = voicelessJ[modeJ[3] or ""] or initialJ local geminateJ = voicelessJ[modeJ[4] or ""] or initialJ -- if enabled, do not display pseudo-glide hints at all local noHints = args and parseBoolean(args.nohints) -- if enabled, vowels between two non-glide consonants -- will be radically simplified resembling newer Marshallese orthography local radSimp = args and parseBoolean(args.radsimp) -- false will display all obstruent allophones as voiceless -- true will display all obstruent allophones as voiced -- empty string or absent by default will display -- only medial obstruent allophones as semi-voiced local voice = args and args.voice or "" local seq = {} local function forItem(text) local map, map2, patt, patt2, subst text = gsub(text, S, "") function forDialect(text, dialect) -- morphemes can begin with geminated consonants, -- but spoken words cannot text = gsub(text, "^("..C..")%1("..V..")", function(a, b)				-- the prosthetic vowel is never more open than /ɛ/				local c = b				if c == "æ" then					c = "ɛ"				end				if dialect == "ralik" then					-- Rālik /CCV-/ becomes /jVCCV-/					return "ɦʲ"..c..a..a..b				else					-- Ratak /CCV-/ becomes /CVCV-/					return a..c..a..b				end			end) -- initial /jijV-, jiwV-, wiwV-/ sequences have special behavior -- to block this in the template argument, use "'i" instead of "i" text = " "..text text = gsub(				text, "([ʲˠʷ ])(ɦ[ʲʷ])i(ɦ[ʲʷ])("..V..")",				function(a, b, c, d)					local result					if c == "ɦʷ" then						-- /jiwV-, wiwV-/ sequences						if dialect == "ralik" then							-- Rālik /wiwV-/ becomes /jiwV-/							b = "ɦʲ"						end						-- /[jw]iwV-/ becomes /[jw]iwwV-/ in both dialects						result = b.."ïɦʷɦʷ"					elseif b == "ɦʲ" then						-- /jijV-/ sequences						if dialect == "ralik" then							-- "dwelling upon"							result = "īʲ"						else							-- "passing over lightly"							result = "ĭʲ"						end					else						-- no change for /wijV-/ sequences						result = b.."ï"..c					end					return a..result..d				end			) text = trim(text) -- Rālik /ɰɰV-/ becomes /ɰVɰV-/ if dialect == "ralik" then -- Rālik /ɰɰV-/ becomes /ɰVɰ-/ text = gsub(text, "ɣˠ("..V..")", "ɦˠ%1ɦˠ") else -- Ratak /ɰɰV-/ becomes /ɰV-/ text = gsub(text, "ɣ", "ɦ") end return text end if dialect == "ralik" or dialect == "ratak" then text = forDialect(text, dialect) else local ralik = forDialect(text, "ralik") local ratak = forDialect(text, "ratak") -- if both dialect reflexes are the same, display only one of them if ralik == ratak then text = ralik else forItem(ralik) forItem(ratak) return end end -- if the phrase begins or ends with a bare vowel and no pseudo-glide, -- display phrase three times with each of the different pseudo-glides if find(text, "^"..V) then forItem("_ʲ"..text) forItem("_ˠ"..text) forItem("_ʷ"..text) return end if find(text, V.."$") then forItem(text.."_ʲ") forItem(text.."_ˠ") forItem(text.."_ʷ") return end -- restore protected /i/, we won't be hiding it anymore text = gsub(text, "ï", "i") -- expand "dwelling upon" i, we won't be checking for it anymore text = gsub(text, "īʲ", "ɦʲiɦʲɦʲ") -- forward assimilation of rounded consonant clusters subst = "%1ʷ%2%ʷ" text = gsub(text, "([kŋ])ʷ([kŋ]).", subst) text = gsub(text, "([nrl])ʷ([nrl]).", subst) -- experimental if true then -- the references are vague on			-- how this actually assimilates -- there is no /tʷ/ in Marshallese -- but /tˠ/ is at least still heavy text = gsub(text, "nʷtʲ", "nʷtˠ") end -- backward assimilation of remaining secondary articulations subst = "%1%3%2%3" text = gsub(text, "([pm]).([pm])(.)", subst) text = gsub(text, "([tn]).(t)(.)", subst) text = gsub(text, "([kŋ]).([kŋ])(.)", subst) text = gsub(text, "([nrl]).([nrl])(.)", subst) -- backward nasal assimilation of consonant clusters subst = "%2%1%2" text = gsub(text, "p(.)(m)", subst) text = gsub(text, "[rl](.)(n)", subst) text = gsub(text, "k(.)(ŋ)", subst) -- insert epenthetic vowels within unstable consonant clusters subst = "%1V%2" -- rhotics before coronal obstruents text = gsub(text, "(r.)(t)", subst) -- laterals before velarized coronal obstruents are stable -- but laterals before palatalized coronal obstruents are not text = gsub(text, "(l.)(tʲ)", subst) -- obstruents or non-coronal nasals before coronal nasals or liquids text = gsub(text, "([ptkmŋ].)([nrl])", subst) -- labials before coronals or dorsals text = gsub(text, "([pm].)([tkŋ])", subst) -- coronals before dorsals or labials text = gsub(text, "([tnrl].)([pkmŋ])", subst) -- dorsals before labials or coronals text = gsub(text, "([kŋ].)([ptm])", subst) -- give those epenthetic vowels a transitional height map = { ["æ"] = { ["æ"] = "æ", ["ɛ"] = "æ", ["e"] = "ɛ", ["i"] = "ɛ" }, ["ɛ"] = { ["æ"] = "ɛ", ["ɛ"] = "ɛ", ["e"] = "ɛ", ["i"] = "e" }, ["e"] = { ["æ"] = "ɛ", ["ɛ"] = "e", ["e"] = "e", ["i"] = "e" }, ["i"] = { ["æ"] = "e", ["ɛ"] = "e", ["e"] = "i", ["i"] = "i" } }		text = gsub2(text, "(.)(..)V(..)(.)", function(a, b, c, d)			if not (map[a] and map[a][d]) then				error("no data for "..d.." after "..a)			end			return a..b.."("..map[a][d]..")"..c..d		end) -- clusters with glides have epenthetic vowels, too -- but their height is that of the glide's other vowel -- forming epenthetic long vowels text = gsub(text, "("..V..")ĭʲ("..NG..")", "%1ɦʲi%2") text = gsub(text, "("..NG..")ĭʲ("..V..")", "%1iɦʲ%2") text = gsub(text, "("..V..")(ɦ.)("..C..")", "%1%2%1%3") text = gsub(text, "("..C..")(ɦ.)("..V..")", "%1%3%2%3") local allophones = { ["ʲ"] = { ["æ"] = "æ", ["ɛ"] = "ɛ", ["e"] = "e", ["i"] = "i" }, ["ˠ"] = { ["æ"] = "ɑ", ["ɛ"] = "ʌ", ["e"] = "ɤ", ["i"] = "ɯ" }, ["ʷ"] = { ["æ"] = "ɒ", ["ɛ"] = "ɔ", ["e"] = "o", ["i"] = "u" } }		local front, back, rounded = "ʲ", "ˠ", "ʷ" local glide = "ɦ" local low, mid_low, mid_high, high = "æ", "ɛ", "e", "i" -- match consonant, articulation, vowel, -- and grab next consonant and articulation, -- excluding them from pattern so that matches don't overlap text = gsub(text, "(.)("..C2..")("..LQ..")("..V..")("..RQ..")",			function(start, C1, A1, LQ, V, RQ, finish)				local C2, A2 = mw.ustring.match(text, "(.)(.)", finish)				local final_articulation				if A1 == A2 then					final_articulation = A1				elseif (C1 == glide) ~= (C2 == glide) then					final_articulation = C1 == glide and A1 or A2				else					-- incorporate some orthographic biases					-- from Marshallese language					local front_vs_back = A1 == front and A2 == back or A2 == front and A1 == back					-- a over ā					if front_vs_back and V == low then						final_articulation = back					-- i over ū					elseif front_vs_back and V == high then						final_articulation = front					-- ō over e					elseif front_vs_back and (V == mid_low or V == mid_high) then						final_articulation = back					elseif A1 == front and (A2 == back or A2 == rounded) then						final_articulation = A2					elseif A2 == front and (A1 == back or A1 == rounded) then final_articulation = A1					elseif A1 == back and A2 == rounded then final_articulation = A2					elseif A2 == back and A1 == rounded then final_articulation = A1					end end local final_V = final_articulation and allophones[final_articulation][V] or allophones[A1][V]..allophones[A2][V] mw.log(text, start, C1, A1, LQ, V, RQ, C2, A2, final_V) return C1..A1..LQ..final_V..RQ			end)		text = trim(text)		-- tie short diphthongs		text = gsub(text, "("..V..")("..V..")", "%1"..TIE.."%2")		-- delete glide phonemes, now that we're done coloring vowels		text = gsub(text, "ɦ.", "")		-- experimental		if true then			-- convert double consonants to geminates			text = gsub(text, "("..C..")%1", "%1ː")		end		-- convert certain medial obstruent consonants to voiced allophones		map = {			["pʲ"] = "bʲ",			["pˠ"] = "bˠ",			["tʲ"] = medialJ.."ʲ",			["tˠ"] = "dˠ",			["kˠ"] = "ɡˠ",			["kʷ"] = "ɡʷ"		}		subst = function(a, b, c)			return a..map[b]..c		end		text = gsub2(text, "("..V..RQ..")([ptk].)("..LQ..V..")", subst)		-- experimental		if true then			-- after nasals, too			text = gsub(text, "([mnŋ].)([ptk].)("..V..")", subst)		end		-- convert /tʲ/ to preferred allophones per J argument		text = gsub(text, "tʲː?", { ["tʲ"] = initialJ.."ʲ", ["tʲː"] = geminateJ.."ʲː" })		patt = "[tʦs]ʲ"		text = gsub(text, patt..patt, finalJ.."ʲ"..initialJ.."ʲ")		text = gsub(text, patt.."$", finalJ.."ʲ")		-- display full voicing or full devoicing per voice argument		if voice ~= "" then			if parseBoolean(voice) then				-- display all consonants as voiced in this mode				text = gsub(text, "[ptʦsk]", { ["p"] = "b", ["t"] = "d", ["ʦ"] = "ʣ", ["s"] = "z", ["k"] = "ɡ" })			else				-- display all consonants as voiceless in this mode				text = gsub(text, "[bdʣzɡ]", { ["b"] = "p", ["d"] = "t", ["ʣ"] = "ʦ", ["z"] = "s", ["ɡ"] = "k" })			end		end		-- experimental		if true then			-- convert hard glides /j/ to semivowels			-- æɛeiɑʌɤɯɒɔou			text = gsub(text, "jʲ([ɯu])", "i̯%1")			text = gsub(text, "([ɯu])jʲ", "%1i̯")			text = gsub(text, "jʲ([ɤo])", "e̯%1")			text = gsub(text, "([ɤo])jʲ", "%1e̯")			text = gsub(text, "jʲ([ɑʌɒɔ])", "ɛ̯%1")			text = gsub(text, "([ɑʌɒɔ])jʲ", "%1ɛ̯")		end		-- experimental		if true then			-- convert repeated vowels to geminates			text = gsubx(text, "("..V..")(ː*)%1", "%1%2ː")		end		-- display affricates, if any, as tied consonants		text = gsub(text, "[ʦʣ]", { ["ʦ"] = "t"..TIE.."s", ["ʣ"] = "d"..TIE.."z" })		if alvPal then			text = gsub(text, "[sz]ʲ", { ["sʲ"] = "ɕ", ["zʲ"] = "ʑ" })		end		-- display final stops as unreleased		text = gsub(text, "([ptk]"..C2..")$", "%1"..UNREL)		-- (mostly) final consonant presentation forms		text = gsub(text, C..UNREL.."?", { -- dorsal obstruents and nasals are postvelar ["kˠ"] = "k"..RETR, ["kʷ"] = "k"..RETR.."ʷ", ["ɡˠ"] = "ɡ"..RETR, ["ɡʷ"] = "ɡ"..RETR.."ʷ", ["ŋˠ"] = "ŋ"..RETR, ["ŋʷ"] = "ŋ"..RETR.."ʷ", -- these are "dark-R-colored," e.g. retroflex ["nˠ"] = "ɳˠ", ["nʷ"] = "ɳʷ", -- these are dental according to Bender (1969) ["tˠ"] = "t"..DENT.."ˠ", ["dˠ"] = "d"..DENT.."ˠ", ["nʲ"] = "n"..DENT.."ʲ", ["rʲ"] = "r"..DENT.."ʲ", ["lʲ"] = "l"..DENT.."ʲ", -- dark L			--["lˠ"] = "ɫ", --["lʷ"] = "ɫʷ", -- "passing over lightly" ["ĭʲ"] = "i"..ASYLL, -- on-glides ["jʲ"] = "j", ["wʷ"] = "w", -- unreleased stops ["pʲ"..UNREL] = "p"..UNREL.."ʲ", ["pˠ"..UNREL] = "p"..UNREL.."ˠ", ["tʲ"..UNREL] = "t"..UNREL.."ʲ", ["tˠ"..UNREL] = "t"..DENT..UNREL.."ˠ", ["kˠ"..UNREL] = "k"..RETR..UNREL, ["kʷ"..UNREL] = "k"..RETR..UNREL.."ʷ" })		-- experimental		if true then			if voice == "" then				-- voiced allophones are actually semi-voiced				text = gsub(text, "[bdzʑɡ]", { ["b"] = "b"..DEVO, ["d"] = "d"..DEVO, ["z"] = "z"..DEVO, ["ʑ"] = "ʑ"..DEVO2, ["ɡ"] = "ɡ"..DEVO2 })				-- these display better				text = gsub(text, DEVO..DENT, DENT..DEVO)			end		end		-- experimental		if false then			-- deparenthesize epenthetic vowels, but make them asyllabic			text = gsub(text, LP.."("..V..")"..RP, "%1"..ASYLL.."")			text = gsub( text, LP.."("..V..")"..TIE.."("..V..")"..RP, "%1"..ASYLL..TIE.."%2"..ASYLL.."" )		end		-- experimental		if false then			-- raised allophones?			text = gsub(text, "[æɛeɒɔo]", "%1"..UP)		end		-- experimental		if false then			-- alternate between above and below ties to improve presentation			text = gsub( text, TIE.."([^"..TIE.."]*)"..TIE.."([^"..TIE.."]*)", TIE.."%1"..TIE2.."%2" )		end		-- experimental		if false then			-- tying vowels from below is more elegant when there are ascenders			text = gsub(text, "("..V..")"..TIE.."("..V..")", "%1"..TIE2.."%2")		end		-- experimental		if true then			-- Wiktionary tends to prefer more diacritics			-- Wikipedia tends to prefer fewer diacritics			-- strip some diacritics that don't affect meaning			text = gsub( text, "["..DENT..DEVO..DEVO2..DOWN..RETR..UNREL..UP.."]", "" )		end		if noHints then			text = gsub(text, "_.", "")		else			-- convert pseudo-glides to hints for attached affixes			text = gsub(text, "^_(.)", "%1‿")			text = gsub(text, "_(.)$", "‿%1")		end		-- workaround: where is the trailing space being introduced?		--text = trim(text)		insertUnique(seq, text)	end	for _, item in pairs(items) do		forItem(item)	end	return seq end

export._parse = parse export._toBender = toBender export._toPhonemic = toPhonemic export._toPhonetic = toPhonetic

function export.bender(frame) return concat(toBender(parse(frame.args[1], frame.args)), ", ") end

function export.parse(frame) return concat(parse(frame.args[1]), ", ") end

function export.phonemic(frame) return concat(toPhonemic(parse(frame.args[1])), ", ") end

function export.phonetic(frame) return concat(toPhonetic(parse(frame.args[1]), frame.args), ", ") end

function export.phoneticMED(frame) return "DEPRECATED" end

function export.phoneticChoi(frame) return "DEPRECATED" end

function export.phoneticWillson(frame) return "DEPRECATED" end

return export