Module:sandbox/nan-pron
Appearance
- This module sandbox lacks a documentation subpage. Please create it.
- Useful links: root page • root page’s subpages • links • transclusions • testcases • sandbox
local export = {}
local data = mw.loadData("Module:nan-pron/data")
local m_str_utils = require("Module:string utilities")
local find = m_str_utils.find
local gsplit = m_str_utils.gsplit
local gsub = m_str_utils.gsub
local sub = m_str_utils.sub
local match = m_str_utils.match
local len = m_str_utils.len
local lower = m_str_utils.lower
local split = m_str_utils.split
local toNFC = mw.ustring.toNFC
local toNFD = mw.ustring.toNFD
-- We use this table to encode digraphs and other multi-byte sequences.
-- Capitalization is encoded as a preceding "^", e.g. Tân → ^ta5n
-- Special treatments for digraphs: the diacritic that goes in between is
-- moved to the end, e.g. ńg → G2, ó͘ → O2, ó̤ → o_2, ṳ́ → u_2
local encoding = {
ph = "P", th = "T", kh = "K", ng = "G", ch = "c", chh = "C", sh = "S",
["ɛ"] = "E", ["ⁿ"] = "N",
["\204\129"] = "2", -- á
["\204\128"] = "3", -- à
["\204\130"] = "5", -- â
["\204\131"] = "6", -- ã (POJ T6)
["\204\140"] = "&", -- ǎ (TL T6)
["\204\132"] = "7", -- ā
["\204\141"] = "8", -- a̍
["\204\134"] = "9", -- ă (POJ T9)
["\204\139"] = "0", -- a̋ (TL T9)
["\204\164"] = "_",
["o\205\152"] = "O", -- o͘
}
local decoding = {}
for key,val in pairs(encoding) do
decoding[val] = key
end
-- e.g. Tân → ^ta5n
function export.do_encode(text)
text = toNFD(text)
:gsub("[A-Z]",function(c) return "^"..c:lower() end)
:gsub("chh",encoding)
:gsub("ⁿ",encoding)
:gsub("[ptkcs\201-\204][h\128-\164]",encoding)
:gsub("n([%d&]?)g","G%1")
:gsub("o([%d&]?)\205\152","O%1")
return text
end
-- e.g. ^ta5n → Tân
function export.do_decode(text)
text = text:gsub("G([%d&]?)","n%1g")
:gsub("[PTKcCSENO0-9&_]",decoding)
:gsub("%^(.)",string.upper)
return toNFC(text)
end
-- simpler version that ignores numbers or uppercase
local function decode(text)
return toNFC(text:gsub("[GPTKcCSENO_]",decoding))
end
-- convert to an internal representation that uses the encoding above
-- and also place the tone at the end
-- and splits 4 and 8 into 4A 4B 8A 8B
-- e.g. Khóng-chú → ^KoG2-cu2
function export.poj_to_internal(text, check_diminutive)
if text:find("^",1,true) then
error("Hokkien: Invalid character found.")
end
text = export.do_encode(text)
local checked_category = {p="A",t="A",k="A",h="B"}
text = text:gsub("[^%- ,.#]+",function(syl)
if check_diminutive and syl == "a2" then
error("The diminutive should be specified with 仔.")
end
if syl == "仔" then
return syl
end
local tone = syl:match("[23456&7890]")
local detone = syl:gsub("[%d&]","")
local category = checked_category[detone:sub(-1,-1)]
if not tone then
tone = category and ("4"..category) or "1"
elseif tone == "8" then
tone = "8" .. category
else
tone = ({["&"]="6T",["0"]="9T"})[tone] or tone
end
if detone:find("^",2,true) then
error("Only the first letter in a syllable may be capitalised.")
end
return detone..tone
end)
return text
end
local tone_placement_order = "aOoEeuiymG"
local tone_placement_exceptions = {
ioa=2, ioaN=2,
oa=1, oaN=1, oeh=2, oehN=2
}
-- convert the internal representation as described above back to POJ
-- this function determines the canonical tone placement
function export.internal_to_poj(text)
text = text:gsub("[^%^%- ,.#]+",function(syl)
local i,f,t = syl:match("^([pPmbtTnlcCjskKGghfdSrwy]?)([iou]?[aeiouEOymG][_aeiour]*h?[mnGptkN]?)(%dT?)[AB]?$")
if not i then
if syl == "仔" then
return syl
end
error("Syllable decomposition failed: " .. decode(syl))
end
if t == "1" or t == "4" then
return i..f
else
t = ({["6T"]="&", ["9T"]="0"})[t] or t
end
-- tone placement rule: a>O>o>E>e>u>i>y>m>G, exceptions specified above
local idx = tone_placement_exceptions[f]
if not idx then
for j=1,#tone_placement_order do
idx = f:find(tone_placement_order:sub(j,j),1,true)
if idx then break end
end
end
if not idx then
error("Tone placement failed: " .. decode(syl))
end
return i..f:sub(1,idx)..t..f:sub(idx+1)
end)
return export.do_decode(text)
end
function export.poj_check_invalid(text)
if not text then
return nil
end
local title = mw.title.getCurrentTitle().text
local check_diminutive = (title:find("子") or title:find("仔")) and title ~= "明仔早"
local reading = mw.text.split(text, "/", true)
local internal,loc = {},{}
for i=1,#reading do
local colon = reading[i]:find(':',1,true)
if colon then
loc[i], reading[i] = reading[i]:sub(1,colon-1), reading[i]:sub(colon+1)
end
internal[i] = export.poj_to_internal(reading[i], check_diminutive)
local normalized = export.internal_to_poj(internal[i])
if reading[i] ~= normalized then
error("Invalid POJ input \"" .. reading[i] .. "\": please change it to \"" .. normalized .. "\"")
end
end
return reading, internal, loc
end
local function check_canonical_POJ(text)
for syl in text:gmatch("[^%^%- ,.#]+") do
local i,f,t = syl:match("^([pPmbtTnlcCjskKGghfdSrwy]?)([iou]?[aeiouEOymG][_aeiour]*h?[mnGptkN]?)(%d)[ABT]?$")
if not data.poj.canonical_final[decode(f)] or t=="6" or t=="9" then
return false
end
end
return true
end
function export.poj_display_one(reading, internal)
reading = reading:gsub("仔", "á"):gsub("#", "")
if check_canonical_POJ(internal) then
reading = "[[" .. reading .. "#Hokkien|" .. reading .. "]]"
end
return reading
end
function export.poj_display(readings, internals)
for i = 1, #readings do
readings[i] = export.poj_display_one(readings[i], internals[i])
end
return table.concat(readings, " / ")
end
local tl_conv = {
O="oo", u_="ir", o_="er", E="ee", hN="nnh",
c="ts", C="tsh",
["6"]="\204\140", -- ã → ǎ
["9"]="\204\139", -- ă → a̋
}
local tl_tone_placement_exceptions = {
ere=3
}
function export.internal_to_tl(text)
text = text:gsub("仔","a2")
:gsub("#","")
:gsub("e([kG])","i%1") -- eng/ek → ing/ik
:gsub("o([aeE])","u%1") -- oa/oe/oɛ → ua/ue/uɛ
:gsub("(h?)N", "nn%1") -- (h)ⁿ → nn(h)
:gsub("[EOuo]_?", tl_conv) -- ɛ/o͘/ṳ/o̤ → ee/oo/ir/er
-- place tones
:gsub("[^%^%- ,.#]+",function(syl)
local i,f,t = syl:match("^([pPmbtTnlcCjskKGghfdSrwy]?)([aeiouymG][a-zA-Z]*)(%d)[ABT]?$")
if not i then
error("Syllable decomposition failed: " .. decode(syl))
end
if t == "1" or t == "4" then -- no tone diacritic needed
return i..f
end
t = tl_conv[t] or t
-- tone placement rule: if i or u occurs before other vowel, put on the other vowel
local idx = tl_tone_placement_exceptions[f]
or f:match("^[iu]?u?()[aeiouy]")
or f:match("^()[mG]")
if not idx then
error("Tone placement failed: " .. decode(syl))
end
return i..f:sub(1,idx)..t..f:sub(idx+1)
end)
:gsub("[cC]", tl_conv) -- ch → ts
return export.do_decode(text)
end
function export.poj_check_syllable(initial, final, loc)
if not ((validInitials[initial] or moreValidInitials[loc][initial]) and (validFinals[final] or moreValidFinals[loc][final])) then
--error("The syllable " .. initial .. "+" .. final .. " does not appear to be a valid " .. loc .. " POJ syllable.")
return "[[Category:Hokkien terms needing pronunciation attention|" .. loc_code[loc] .. "]]"
end
return nil
end
function export.internal_to_psdb(text)
-- TODO
return text
end
function export.internal_to_IPA(text,loc)
-- TODO
return 'IPA '..text.." "..data.IPA.tone[loc]["5"]
end
local default_location_list = { "Xiamen", "Quanzhou", "Zhangzhou", "Taiwan" }
local default_IPA_location_list = { "Xiamen", "Quanzhou", "Zhangzhou", "Taipei", "Kaohsiung" }
local loc_overrides = {
['Taiwan'] = { 'Taipei', 'Kaohsiung' },
['Xiamen-d'] = { 'Xiamen-d' },
['Mainland'] = { 'Xiamen', 'Quanzhou', 'Zhangzhou' }
}
-- returns a list of locations for the header, and a list of locations for the IPA
-- these generally coïncide except for the overrides defined above, or
-- when it is tagged with -d (dated) etc.
function export.parse_locations(loc)
if not loc then
return default_location_list, default_IPA_location_list
end
local locations, IPA_locations = {},{}
for location_abbrev in mw.text.gsplit(loc, ",", true) do
if data.loc.invalid_code_hint[location_abbrev] then
error("Invalid Hokkien location code: " .. location_abbrev .. ", maybe you meant: " .. data.loc.invalid_code_hint[location_abbrev])
end
local loc_name = data.loc.code[location_abbrev]
if not loc_name then
error("The region label '" .. location_abbrev .. "' cannot be found. Please see [[Template:zh-pron#Parameters]].")
end
table.insert(locations, loc_name)
loc_name = gsub(loc_name, '^Taiwan%-[vdr]?([tk]?)$', { ['t'] = 'Taipei', ['k'] = 'Kaohsiung', [''] = 'Taiwan' })
if loc_overrides[loc_name] then
for _,IPA_loc in ipairs(loc_overrides[loc_name]) do
table.insert(IPA_locations, IPA_loc)
end
else
loc_name = gsub(loc_name, '%-d$', '')
table.insert(IPA_locations, loc_name)
end
end
return locations, IPA_locations
end
-- used by [[T:zh-pron]] and [[T:nan-hbl-pronunc]]
function export.generate_all(text)
local nan_pronunc
if type(text) == "table" then
text, nan_pronunc = text.args[1], text.args[2]
end
local output_text = {}
local prefix = (nan_pronunc and nan_pronunc ~= "") and "\n" or "\n*"
local function fmt(item, content) --formatting
table.insert(output_text,
prefix .. data.boilerplate[item].leading
.. (content or "") .. data.boilerplate[item].trailing)
end
-- e.g. poj={"koe","ke"}, internal={"koe1","ke1"}, loc={"xm,qz","zz"}
local poj,internal,loc = export.poj_check_invalid(text)
local locations, IPA_locations, display, tl, psdb, ipa
local backwards_compatibility = not text:find(":",1,true)
for i=1,#poj do
locations, IPA_locations = export.parse_locations(loc[i])
fmt("POJ", export.poj_display_one(poj[i],internal[i]))
fmt("TL", export.internal_to_tl(internal[i]))
if not find(text, "%-%-") then
psdb = export.internal_to_psdb(internal[i])
ipa = {} -- store the generated ipa AND which lects have which ipa
for _,IPA_location in ipairs(IPA_locations) do
if data.loc.IPA_available[IPA_location] then
local generated_IPA = export.internal_to_IPA(internal[i],IPA_location)
if not ipa[generated_IPA] then
table.insert(ipa, generated_IPA)
ipa[generated_IPA] = {}
end
IPA_location = IPA_location:gsub("%-d$","")
table.insert(ipa[generated_IPA], IPA_location)
end
end
end
mw.logObject({poj=poj[i],int=internal[i],loc=loc[i],loca=locations,ipaloc=IPA_locations,dis=display,tl=tl,psdb=psdb,ipa=ipa})
end
return table.concat(output_text)
--[==[
if not find(text, ":") then
table.insert(output_text, fmt("LV2")
.. fmt("POJ", export.poj_display(poj,internal))
.. fmt("TL", export.poj_to_tl_conv(text)))
if not find(text, "%-%-") then
local psdb_hash = export.poj_to_psdb_conv(text)
if not find(psdb_hash, "error") then
table.insert(output_text, formatting.PSDB.leading .. psdb_hash .. formatting.PSDB.trailing)
end
for _, IPA_location in ipairs(IPA_available_list) do
IPA_location = IPA_location == "Taiwan" and { "Taipei", "Kaohsiung" } or { IPA_location }
for _, location in ipairs(IPA_location) do
table.insert(output_text, formatting.IPA.leading .. location_link[location] .. formatting.IPA.trailing)
local reading_IPA_hash = {}
for poj_reading in gsplit(text, "/", true) do
table.insert(reading_IPA_hash, export.generate_IPA(poj_reading, location))
end
table.insert(output_text, table.concat(reading_IPA_hash, ", "))
if #reading_IPA_hash > 1 then
table.insert(output_text, string.format("[[Category:Hokkien terms needing pronunciation attention|%d]]", #reading_IPA_hash))
end
end
end
end
else
for i, poj_reading in ipairs(all_readings) do
table.insert(output_text, formatting.LV_two.leading)
local location_hash = {}
for _, location_name in ipairs(locations[i]) do
table.insert(location_hash, location_link[location_name])
end
table.insert(output_text, ": " .. table.concat(location_hash, ", ") .. formatting.LV_two.trailing)
table.insert(output_text, formatting.POJ.leading .. export.poj_display(poj_reading) .. formatting.POJ.trailing ..
formatting.TL.leading .. export.poj_to_tl_conv(poj_reading) .. formatting.TL.trailing)
if not find(poj_reading, "%-%-") then
local psdb_hash = export.poj_to_psdb_conv(poj_reading)
if not find(psdb_hash, "error") then
table.insert(output_text, formatting.PSDB.leading .. psdb_hash .. formatting.PSDB.trailing)
end
local IPA_readings = {}
for j, location_name in ipairs(locations[i]) do
location_name = gsub(location_name, '^Taiwan%-[vdr]?([tk]?)$', { ['t'] = 'Taipei', ['k'] = 'Kaohsiung', [''] = 'Taiwan' })
loc = {
['Taiwan'] = { 'Taipei', 'Kaohsiung' },
['Xiamen-d'] = { 'Xiamen-d' },
['Mainland'] = { 'Xiamen', 'Quanzhou', 'Zhangzhou' }
}
location_name = loc[location_name] or { gsub(location_name, '%-d$', '') }
for k, location in ipairs(location_name) do
local loc = gsub(location, '%-d$', '')
if IPA_available[loc] then
local poj_to_ipa = export.generate_IPA(poj_reading, location)
if IPA_readings[poj_to_ipa] then
table.insert(IPA_readings[poj_to_ipa][2], location_link[loc])
else
IPA_readings[poj_to_ipa] = { j + (k/10), { location_link[loc] } }
end
end
end
end
for reading, reading_info in pairs(IPA_readings) do
table.insert(output_text, formatting.IPA.leading .. table.concat(reading_info[2], ", ") ..
formatting.IPA.trailing .. reading)
end
end
end
end
]==]
end
function export.generate_IPA(text, location)
-- (Wyang) I can't seem to find an example where 'triple' is used.. The code is below:
--if match(p[i], "%(") then
-- p[i] = gsub(p[i], "[%(%)]", "")
-- triple[i] = true
--end
--if triple[i] then
-- if tone[i] == "一" then
-- ipa[i] = (initial[i] .. final[i] .. "一至七 " .. initial[i] .. final[i] .. "一至七 " .. initial[i] .. final[i] .. (i == #tone and "一" or "一至七"))
-- elseif tone[i] == "二" then
-- ipa[i] = (initial[i] .. final[i] .. "二至一 " .. initial[i] .. final[i] .. "二至一 " .. initial[i] .. final[i] .. (i == #tone and "二" or "二至一"))
-- elseif tone[i] == "三" then
-- ipa[i] = (initial[i] .. final[i] .. "三至二 " .. initial[i] .. final[i] .. "三至二 " .. initial[i] .. final[i] .. (i == #tone and "三" or "三至二"))
-- elseif tone[i] == "四A" then
-- ipa[i] = (initial[i] .. final[i] .. "四至八 " .. initial[i] .. final[i] .. "四至八 " .. initial[i] .. final[i] .. (i == #tone and "四" or "四至八"))
-- elseif tone[i] == "四B" then
-- final[i] = gsub(final[i], "ʔ", "(ʔ)")
-- ipa[i] = (initial[i] .. final[i] .. "四至二 " .. initial[i] .. final[i] .. "四至二 " .. initial[i] .. final[i] .. (i == #tone and "四" or "四至二"))
-- elseif tone[i] == "五" then
-- if loc == "Quanzhou" or loc == "Taipei" then
-- ipa[i] = (initial[i] .. final[i] .. "五 " .. initial[i] .. final[i] .. "五至三 " .. initial[i] .. final[i] .. (i == #tone and "五" or "五至三"))
-- else
-- ipa[i] = (initial[i] .. final[i] .. "五 " .. initial[i] .. final[i] .. "五至七 " .. initial[i] .. final[i] .. (i == #tone and "五" or "五至七"))
-- end
-- elseif tone[i] == "七" then
-- ipa[i] = (initial[i] .. final[i] .. "七至一 " .. initial[i] .. final[i] .. "七至三 " .. initial[i] .. final[i] .. (i == #tone and "七" or "七至三"))
-- elseif tone[i] == "八A" then
-- ipa[i] = (initial[i] .. final[i] .. "八至四 " .. initial[i] .. final[i] .. "八至四 " .. initial[i] .. final[i] .. (i == #tone and "八" or "八至四"))
-- elseif tone[i] == "八B" then
-- final[i] = gsub(final[i], "ʔ", "(ʔ)")
-- ipa[i] = (initial[i] .. final[i] .. "八至五 " .. initial[i] .. final[i] .. "八至三 " .. initial[i] .. final[i] .. (i == #tone and "八" or "八至三"))
-- end
--end
if type(text) == "table" then text, location = text.args[1], text.args["loc"] end
local tone_from_mark = {
[""] = "1",
["́"] = "2",
["̀"] = "3",
["p"] = "4A", ["t"] = "4A", ["k"] = "4A",
["h"] = "4B",
["̂"] = "5",
["̌"] = "6",
["̄"] = "7",
["̍p"] = "8A", ["̍t"] = "8A", ["̍k"] = "8A",
["̍h"] = "8B",
["̋"] = "9",
["̆"] = "9",
}
local initial_ipa = {
["p"] = "p", ["ph"] = "pʰ", ["m"] = "m", ["b"] = "b", ["f"] = "f",
["t"] = "t", ["th"] = "tʰ", ["n"] = "n", ["l"] = "l", ["d"] = "d",
["ch"] = "t͡s", ["chh"] = "t͡sʰ", ["j"] = "d͡z", ["s"] = "s", ["sh"] = "ʃ",
["k"] = "k", ["kh"] = "kʰ", ["ng"] = "ŋ", ["g"] = "ɡ",
["h"] = "h", ["r"] = "ɹ", ["w"] = "w", ["y"] = "j", [""] = "",
["Kaohsiung-j"] = "z",
}
local final_ipa = {
["a"] = "a", ["ah"] = "aʔ", ["ahⁿ"] = "ãʔ",
["ai"] = "ai", ["aih"] = "aiʔ", ["aiⁿ"] = "ãi", ["aihⁿ"] = "ãiʔ",
["ak"] = "ak̚", ["am"] = "am", ["an"] = "an", ["aⁿ"] = "ã",
["ang"] = "aŋ", ["ap"] = "ap̚", ["at"] = "at̚",
["au"] = "au", ["auh"] = "auʔ", ["auhⁿ"] = "ãuʔ", ["auⁿ"] = "ãu",
["e"] = "e", ["ee"] = "ɛ", ["eeh"] = "ɛʔ",
["eek"] = "ɛk̚", ["eeng"] = "ɛŋ",
["eh"] = "eʔ", ["ehⁿ"] = "ẽʔ", ["ei"] = "ei", ["ek"] = "iɪk̚",
["em"] = "ɛm", ["en"] = "ɛn", ["eⁿ"] = "ẽ",
["eng"] = "iɪŋ", ["eoi"] = "ɵy", ["er"] = "ə",
["erh"] = "əʔ", ["erm"] = "əm", ["ern"] = "ən",
["ert"] = "ət̚", ["erk"] = "ək̚", ["et"] = "ɛt̚", ["ep"] = "ep̚",
["eu"] = "eu", ["euⁿ"] = "ẽu",
["i"] = "i", ["ia"] = "ia", ["iah"] = "iaʔ",
["iahⁿ"] = "iãʔ", ["iak"] = "iak̚",
["iam"] = "iam", ["ian"] = "iɛn", ["iaⁿ"] = "iã",
["iang"] = "iaŋ", ["iap"] = "iap̚", ["iat"] = "iɛt̚",
["iau"] = "iau", ["iauh"] = "iauʔ", ["iauhⁿ"] = "iãuʔ", ["iauⁿ"] = "iãu",
["ie"] = "ie", ["iee"] = "iɛ", ["ieⁿ"] = "iɛ̃",
["ih"] = "iʔ", ["ihⁿ"] = "ĩʔ",
["im"] = "im", ["in"] = "in", ["iⁿ"] = "ĩ", ["ing"] = "iŋ",
["io"] = "io", ["ioa"] = "iua", ["ioaⁿ"] = "iuã", ["ioh"] = "ioʔ", ["io͘h"] = "iɔʔ", ["io͘"] = "iɔ",
["iop"] = "iop", ["iok"] = "iɔk̚", ["ioⁿ"] = "iɔ̃", ["iom"] = "iom", ["iong"] = "iɔŋ",
["ip"] = "ip̚", ["ir"] = "ɯ", ["irh"] = "ɯʔ", ["irn"] = "ən", ["it"] = "it̚",
["iu"] = "iu", ["iua"] = "iua", ["iuh"] = "iuʔ", ["iuⁿ"] = "iũ", ["iuaⁿ"] = "iuã", ["iuhⁿ"] = "iũʔ",
["ie"] = "ie", ["iee"] = "iɛ", ["ieeⁿ"] = "iɛ̃",
["m"] = "m̩", ["mh"] = "m̩ʔ",
["ng"] = "ŋ̍", ["ngh"] = "ŋ̍ʔ",
["o"] = "o", ["o͘"] = "ɔ", ["oa"] = "ua", ["oah"] = "uaʔ", ["oahⁿ"] = "uãʔ", ["oai"] = "uai",
["oaih"] = "uaiʔ", ["oaihⁿ"] = "uãiʔ", ["oaiⁿ"] = "uãi", ["oan"] = "uan", ["oaⁿ"] = "uã",
["oang"] = "uaŋ", ["oat"] = "uat̚", ["oak"] = "uak̚",
["oe"] = "ue", ["oeh"] = "ueʔ", ["oehⁿ"] = "uẽʔ", ["oeⁿ"] = "uẽ", ["oee"] = "uɛ",
["oh"] = "oʔ", ["o͘h"] = "ɔʔ", ["ohⁿ"] = "ɔ̃ʔ", ["oi"] = "ɔi", ["oiⁿ"] = "ɔ̃i",
["ok"] = "ɔk̚", ["om"] = "ɔm", ["oⁿ"] = "ɔ̃", ["ong"] = "ɔŋ", ["op"] = "ɔp̚",
["ot"] = "ɔt̚", ["ou"] = "ou",
["u"] = "u", ["uⁿ"] = "ũ", ["uh"] = "uʔ", ["uhⁿ"] = "ũʔ",
["ui"] = "ui", ["uiⁿ"] = "uĩ", ["uih"] = "uiʔ", ["uihⁿ"] = "uĩʔ",
["uk"] = "ok̚", ["um"] = "om",
["un"] = "un", ["ung"] = "oŋ", ["ut"] = "ut̚",
["y"] = "y", ["yn"] = "yn",
["Tong'an-iai"] = "iai",
["Zhangpu-e"] = "iei",
["Zhangpu-eⁿ"] = "ɛ̃", ["Zhangzhou-eⁿ"] = "ɛ̃", ["Penang-eⁿ"] = "ɛ̃",
["Zhangpu-ehⁿ"] = "ɛ̃ʔ", ["Zhangzhou-ehⁿ"] = "ɛ̃ʔ",
["Kaohsiung-o"] = "ɤ", ["Kaohsiung-io"] = "iɤ",
["Kaohsiung-oh"] = "ɤʔ", ["Kaohsiung-ioh"] = "iɤʔ",
["Tainan-o"] = "ɤ", ["Tainan-io"] = "iɤ",
["Tainan-oh"] = "ɤʔ", ["Tainan-ioh"] = "iɤʔ",
["Zhangpu-oe"] = "uɛ",
["Zhangpu-oeⁿ"] = "uɛ̃",
["Zhangpu-oeh"] = "uɛʔ",
["Zhangpu-eng"] = "ɛŋ", ["Zhangpu-ek"] = "ɛk̚",
["Singapore-eng"] = "eŋ", ["Singapore-ek"] = "ek̚",
["Penang-eng"] = "eŋ", ["Penang-ek"] = "ek̚", ["Penang-ik"] = "ik̚",
["Zhangpu-o͘"] = "ɔu", ["Zhangpu-oⁿ"] = "ɔ̃u",
["Changtai-eng"] = "eŋ", ["Changtai-ek"] = "ek̚",
["Changtai-o"] = "ɔ",
["Changtai-io"] = "iɔ",
["Changtai-oh"] = "ɔʔ",
["Changtai-ioh"] = "iɔʔ",
["Changtai-o͘"] = "eu", ["Changtai-o͘ⁿ"] = "ẽu",
["Hui'an-eng"] = "eŋ", ["Hui'an-ek"] = "ek̚",
["Hui'an-en"] = "en", ["Hui'an-et"] = "et̚",
["Hui'an-em"] = "em",
["Longyan-ong"] = "oŋ", ["Longyan-ok"] = "ok̚",
["Longyan-iong"] = "ioŋ", ["Longyan-iok"] = "iok̚",
["Longyan-oⁿ"] = "õ", ["Longyan-ioⁿ"] = "iõ",
["Longyan-oeⁿ"] = "uɛ̃",
["Longyan-ir"] = "z̩",
["Lukang-ir"] = "ɨ",
["Klang-eng"] = "eŋ", ["Klang-ek"] = "ek̚",
}
local tone_sandhi = { }
-- (Wyang) I'm not sure about the 'Xd' ones, when tone X is followed by the diminutive 仔.
tone_sandhi["Xiamen"] = {
["1"] = "7", ["2"] = "1", ["3"] = "2", ["4A"] = "8A", ["4B"] = "2",
["5"] = "7", ["7"] = "3", ["8A"] = "4A", ["8B"] = "3",
}
tone_sandhi["Xiamen-d"] = tone_sandhi["Xiamen"]
tone_sandhi["Tong'an"] = { -- 2 and 4 are special cases
["1"] = "7", ["3"] = "10",
["5"] = "9", ["7"] = "9", ["8A"] = "11", ["8B"] = "11",
}
tone_sandhi["Quanzhou"] = {
["1"] = "1", ["2"] = "5", ["3"] = "2", ["4A"] = "8A", ["4B"] = "4B",
["5"] = "6", ["6"] = "6", ["7"] = "6", ["8A"] = "S", ["8B"] = "S",
}
tone_sandhi["Jinjiang"] = {
["1"] = "1", ["2"] = "5", ["3"] = "2", ["4A"] = "8A", ["4B"] = "4B",
["5"] = "S1", ["6"] = "S1", ["7"] = "S1", ["8A"] = "S2", ["8B"] = "S2",
}
tone_sandhi["Nan'an"] = {
["1"] = "1", ["2"] = "5", ["3"] = "2", ["4A"] = "4A", ["4B"] = "4B",
["5"] = "6", ["6"] = "6", ["7"] = "6", ["8A"] = "S", ["8B"] = "S",
}
tone_sandhi["Hui'an"] = {
["1"] = "1", ["2"] = "5", ["3"] = "2", ["4A"] = "5", ["4B"] = "4B",
["5"] = "6", ["6"] = "6", ["7"] = "6", ["8A"] = "S", ["8B"] = "S",
}
tone_sandhi["Yongchun"] = {
["1"] = "7", ["2"] = "1", ["3"] = "S1", ["4A"] = "8B", ["4B"] = "8B",
["5"] = "7", ["7"] = "3", ["8A"] = "S2", ["8B"] = "S2",
}
tone_sandhi["Zhangzhou"] = {
["1"] = "7", ["2"] = "1", ["3"] = "2", ["4A"] = "S", ["4B"] = "2",
["5"] = "7", ["7"] = "3", ["8A"] = "3", ["8B"] = "3",
["4Bd"] = "1", ["8Bd"] = "7",
}
tone_sandhi["Changtai"] = {
["1"] = "7", ["2"] = "1", ["3"] = "2", ["4A"] = "8A", ["4B"] = "2",
["5"] = "7", ["7"] = "3", ["8A"] = "4A", ["8B"] = "3",
}
tone_sandhi["Zhangpu"] = {
["1"] = "7", ["2"] = "1", ["3"] = "2", ["4A"] = "4A", ["4B"] = "4B",
["5"] = "7", ["7"] = "3", ["8A"] = "7", ["8B"] = "7",
}
tone_sandhi["Longyan"] = {
["1"] = "1",
["5"] = "5", ["6"] = "5", ["8A"] = "2", ["8B"] = "2",
}
tone_sandhi["Taipei"] = {
["1"] = "7", ["2"] = "1", ["3"] = "2", ["4A"] = "8A", ["4B"] = "2",
["5"] = "3", ["7"] = "3", ["8A"] = "4A", ["8B"] = "3", ["9"] = "9",
["3d"] = "1", ["4Bd"] = "1", ["5d"] = "7", ["7d"] = "7", ["8Bd"] = "7",
}
tone_sandhi["Kaohsiung"] = {
["1"] = "7", ["2"] = "1", ["3"] = "2", ["4A"] = "8A", ["4B"] = "2",
["5"] = "7", ["7"] = "3", ["8A"] = "4A", ["8B"] = "3", ["9"] = "9",
["3d"] = "1", ["4Bd"] = "1", ["5d"] = "7", ["7d"] = "7", ["8Bd"] = "7",
}
tone_sandhi["Hongmaogang"] = {
["1"] = "1", ["2"] = "1", ["3"] = "2", ["4A"] = "8A", ["4B"] = "2",
["5"] = "3", ["6"] = "3", ["7"] = "3", ["8A"] = "S", ["8B"] = "S",
}
tone_sandhi["Dalinpu"] = {
["1"] = "1", ["2"] = "1", ["3"] = "2", ["4A"] = "8A", ["4B"] = "2",
["5"] = "3", ["6"] = "3", ["7"] = "3", ["8A"] = "S", ["8B"] = "S",
}
tone_sandhi["Tainan"] = {
["1"] = "7", ["2"] = "S1", ["3"] = "2", ["4A"] = "8A", ["4B"] = "2",
["5"] = "7", ["7"] = "S2", ["8A"] = "S3", ["8B"] = "S3", ["9"] = "9",
}
tone_sandhi["Lukang"] = {
["1"] = "1", ["2"] = "8A", ["3"] = "S1", ["4A"] = "4A", ["4B"] = "S1",
["5"] = "S2", ["6"] = "S2", ["7"] = "S2", ["8A"] = "S3", ["8B"] = "S3", ["9"] = "9",
}
tone_sandhi["Yilan"] = {
["1"] = "7", ["2"] = "S", ["3"] = "2", ["4A"] = "8A", ["4B"] = "2",
["5"] = "7", ["7"] = "3", ["8A"] = "4A", ["8B"] = "4B", ["9"] = "9",
}
tone_sandhi["Kinmen"] = { -- 3 and 4B are special cases
["1"] = "7", ["2"] = "5", ["4A"] = "8A",
["5"] = "3", ["7"] = "3", ["8A"] = "4A", ["8B"] = "3"
}
tone_sandhi["Singapore"] = { --Xiamen/Zhangzhou-like
["1"] = "7", ["2"] = "5", ["3"] = "2", ["4A"] = "8As", ["4B"] = "2",
["5"] = "3", ["7"] = "3", ["8A"] = "3", ["8B"] = "3"
}
tone_sandhi["Penang"] = {
["1"] = "7", ["2"] = "1", ["3"] = "1", ["4A"] = "8A", ["4B"] = "8B",
["5"] = "7", ["6"] = "6", ["7"] = "3", ["8A"] = "4A", ["8B"] = "4B", ["9"] = "9"
}
tone_sandhi["Klang"] = {
["1"] = "1", ["2"] = "S2", ["3"] = "S3", ["4A"] = "S3", ["4B"] = "S3",
["5"] = "S1", ["7"] = "S1", ["8A"] = "8B", ["8B"] = "8B",
}
tone_sandhi["Philippines"] = tone_sandhi["Jinjiang"]
local tone_value = { }
tone_value["Xiamen"] = {
["1"] = "44", ["2"] = "53", ["3"] = "21", ["4A"] = "32", ["4B"] = "32",
["5"] = "24", ["7"] = "22", ["8A"] = "4", ["8B"] = "4",
}
tone_value["Xiamen-d"] = tone_value["Xiamen"]
tone_value["Tong'an"] = {
["1"] = "44", ["2"] = "31", ["3"] = "112", ["4A"] = "32", ["4B"] = "32",
["5"] = "24", ["7"] = "22", ["8A"] = "53", ["8B"] = "53",
["9"] = "11", ["10"] = "42", ["11"] = "1", ["12"] = "4" --sandhi-only tones
}
tone_value["Quanzhou"] = {
["1"] = "33", ["2"] = "554", ["3"] = "41", ["4A"] = "5", ["4B"] = "5",
["5"] = "24", ["6"] = "22", ["7"] = "41", ["8A"] = "24", ["8B"] = "24",
["S"] = "2", --sandhi-only
}
tone_value["Jinjiang"] = {
["1"] = "33", ["2"] = "554", ["3"] = "41", ["4A"] = "5", ["4B"] = "5",
["5"] = "24", ["6"] = "33", ["7"] = "41", ["8A"] = "24", ["8B"] = "24",
["S1"] = "22", ["S2"] = "2", --sandhi-only
}
tone_value["Nan'an"] = {
["1"] = "33", ["2"] = "554", ["3"] = "31", ["4A"] = "5", ["4B"] = "5",
["5"] = "24", ["6"] = "22", ["7"] = "31", ["8A"] = "23", ["8B"] = "23",
["S"] = "2", --sandhi-only
}
tone_value["Hui'an"] = {
["1"] = "33", ["2"] = "54", ["3"] = "21", ["4A"] = "4", ["4B"] = "4",
["5"] = "24", ["6"] = "22", ["7"] = "21", ["8A"] = "23", ["8B"] = "23",
["S"] = "2", --sandhi-only
}
tone_value["Yongchun"] = {
["1"] = "44", ["2"] = "53", ["3"] = "21", ["4A"] = "32", ["4B"] = "32",
["5"] = "24", ["7"] = "22", ["8A"] = "24", ["8B"] = "4",
["S1"] = "53", ["S2"] = "21", --sandhi-only
}
tone_value["Zhangzhou"] = {
["1"] = "44", ["2"] = "53", ["3"] = "21", ["4A"] = "32", ["4B"] = "32",
["5"] = "13", ["7"] = "22", ["8A"] = "121", ["8B"] = "121",
["S"] = "5", --sandhi-only
}
tone_value["Changtai"] = {
["1"] = "44", ["2"] = "53", ["3"] = "21", ["4A"] = "32", ["4B"] = "32",
["5"] = "24", ["7"] = "22", ["8A"] = "3", ["8B"] = "3",
}
tone_value["Zhangpu"] = {
["1"] = "55", ["2"] = "53", ["3"] = "11", ["4A"] = "32", ["4B"] = "32",
["5"] = "213", ["7"] = "33", ["8A"] = "14", ["8B"] = "14",
}
tone_value["Longyan"] = {
["1"] = "334", ["2"] = "21", ["3"] = "213", ["4A"] = "5", ["4B"] = "5",
["5"] = "11", ["6"] = "53", ["7"] = "55", ["8A"] = "32", ["8B"] = "32",
["S"] = "34", --sandhi-only
}
tone_value["Taipei"] = {
["1"] = "44", ["2"] = "53", ["3"] = "11", ["4A"] = "32", ["4B"] = "32",
["5"] = "24", ["7"] = "33", ["8A"] = "4", ["8B"] = "4", ["9"] = "35"
}
tone_value["Kaohsiung"] = {
["1"] = "44", ["2"] = "41", ["3"] = "21", ["4A"] = "32", ["4B"] = "32",
["5"] = "23", ["7"] = "33", ["8A"] = "4", ["8B"] = "4", ["9"] = "35"
}
tone_value["Hongmaogang"] = {
["1"] = "33", ["2"] = "51", ["3"] = "11", ["4A"] = "31", ["4B"] = "31",
["5"] = "13", ["6"] = "31", ["7"] = "11", ["8A"] = "5", ["8B"] = "5",
["S"] = "1", --sandhi-only
}
tone_value["Dalinpu"] = {
["1"] = "33", ["2"] = "51", ["3"] = "11", ["4A"] = "31", ["4B"] = "31",
["5"] = "13", ["6"] = "31", ["7"] = "11", ["8A"] = "5", ["8B"] = "5",
["S"] = "1", --sandhi-only
}
tone_value["Tainan"] = {
["1"] = "44", ["2"] = "53", ["3"] = "21", ["4A"] = "32", ["4B"] = "32",
["5"] = "24", ["7"] = "33", ["8A"] = "4", ["8B"] = "4", ["9"] = "35",
["S1"] = "55", ["S2"] = "11", ["S3"] = "1", --sandhi-only
}
tone_value["Lukang"] = {
["1"] = "33", ["2"] = "55", ["3"] = "31", ["4A"] = "5", ["4B"] = "5",
["5"] = "24", ["6"] = "33", ["7"] = "31", ["8A"] = "35", ["8B"] = "35", ["9"] = "35",
["S1"] = "53", ["S2"] = "22", ["S3"] = "2", --sandhi-only
}
tone_value["Yilan"] = {
["1"] = "44", ["2"] = "53", ["3"] = "21", ["4A"] = "2", ["4B"] = "2",
["5"] = "24", ["7"] = "33", ["8A"] = "5", ["8B"] = "5", ["9"] = "35",
["S"] = "55", --sandhi-only
}
tone_value["Kinmen"] = {
["1"] = "44", ["2"] = "53", ["3"] = "12", ["4A"] = "32", ["4B"] = "32",
["5"] = "24", ["7"] = "22", ["8A"] = "54", ["8B"] = "54"
}
tone_value["Singapore"] = { --Xiamen/Zhangzhou-like
["1"] = "44", ["2"] = "42", ["3"] = "21", ["4A"] = "32", ["4B"] = "32",
["5"] = "24", ["7"] = "22", ["8A"] = "43", ["8B"] = "43", ["8As"] = "4"
}
tone_value["Penang"] = {
["1"] = "33", ["2"] = "445", ["3"] = "21", ["4A"] = "3", ["4B"] = "3",
["5"] = "23", ["6"] = "55", ["7"] = "21", ["8A"] = "4", ["8B"] = "4", ["9"] = "5"
}
tone_value["Klang"] = {
["1"] = "33", ["2"] = "53", ["3"] = "31", ["4A"] = "53", ["4B"] = "53",
["5"] = "24", ["7"] = "31", ["8A"] = "3", ["8B"] = "3",
["S1"] = "22", ["S2"] = "34", ["S3"] = "54", --sandhi-only
}
tone_value["Philippines"] = tone_value["Jinjiang"]
local function get_sandhi_from_post(location, current, post)
if post then
if location == "Tong'an" then
if current == "2" then
if find(post, "^[15]$") or find(post, "^8[AB]$") then
return "7"
else
return "5"
end
elseif find(current, "^4[AB]$") then
if post == "2" then
return "10"
else
return "12"
end
end
elseif location == "Kinmen" then
if current == "3" or current == "4B" then
if find(post, "^[12]$") or find(post, "^4[AB]$") then
return "1"
else
return "2"
end
end
elseif location == "Longyan" then
if current == "2" then
if post == "2" or post == "5" then
return "3"
else
return "2"
end
elseif current == "3" then
if post == "2" or post == "5" then
return "3"
else
return "2"
end
elseif find(current, "^4[AB]$") then
if post == "2" or post == "5" then
return current
else
return "S"
end
elseif current == "7" then
if post == "2" or post == "5" then
return "7"
else
return "1"
end
end
end
end
end
local function get_tone(text)
local tone = gsub(text, "^[^" .. poj_tone_marks .. "ptkh]+([" .. poj_tone_marks .. "]?)[^" .. poj_tone_marks .. "ptkh]*([ptkh]?)ⁿ?", function(tone_symbol, coda)
return tone_from_mark[tone_symbol .. coda] end)
return tone
end
local function nasalize(final)
if find(final, "^mh?$") or find(final, "^ngh?$") then return final end
if find(final, "o͘h?$") then
final = gsub(final, "͘", "")
elseif find(final, "oh?$") then
error("Invalid POJ: nasal initial cannot go with -" .. final)
elseif find(final, "eeh?$") then
final = gsub(final, "ee", "e")
end
return final .. "ⁿ"
end
local formatting = {
leading = "<span class=\"IPA\">/",
trailing = "/</span>"
}
local tone_superscript = { ["1"] = "¹", ["2"] = "²", ["3"] = "³", ["4"] = "⁴", ["5"] = "⁵", ["-"] = "⁻" }
local word_result = {}
local attention = {}
if location ~= 'Xiamen-d' then
location = gsub(location, '%-d$', '')
end
text = gsub(text, " ", "-")
text = gsub(text, ",", "#")
text = gsub(text, "%-?%.%.%.%-?", "#")
text = gsub(text, "#$", "")
text = gsub(text, "#%-?", " ")
text = toNFD(lower(text))
for word in gsplit(text, " ", true) do
local initial, final, tone, diminutive, sandhi, result = {}, {}, {}, {}, {}, {}
local syllables = split(word, "-", true)
syllables.length = #syllables
for index, syllable in ipairs(syllables) do
if syllable == "仔" then
syllable = "a".."́"
diminutive[index] = true
end
local original_syllable = syllable
syllable = gsub(syllable, "[" .. poj_tone_marks .. "]", "")
if not find(syllable, "[aeiouy]") then
final[index] = match(syllable, "^[ckmnpst]?h?h?(ngh?)$") or match(syllable, "^h?(mh?)$")
initial[index] = syllable ~= final[index] and sub(syllable, 1, len(syllable) - len(final[index])) or "" --original code: "ʔ"
else
initial[index] = match(syllable, "^[bcdfgjklmnprstwy]?[gh]?h?")
final[index] = sub(syllable, len(initial[index]) + 1, -1)
end
tone[index] = get_tone(sub(original_syllable, len(initial[index]) + 1, -1))
local nasal_initial = match(initial[index], "^[mn]g?$")
if nasal_initial then
if find(final[index], "ⁿ") then
error("Too much nasality in POJ. " .. original_syllable .. " should be " .. gsub(original_syllable, "ⁿ", ""))
end
if location ~= "Penang" and location ~= "Philippines" and location ~= "Singapore" then --exception for Penang, Philippines and Singapore
final[index] = nasalize(final[index])
end
end
if location == "Longyan" and find(final[index], "h$") then
final[index] = gsub(final[index], "h", "")
end
local nasal_final = match(final[index], "^[mn]") or match(final[index], "ⁿ")
local not_nasal_initial = match(initial[index], "^[blg]$")
if ((nasal_initial and not nasal_final) or (not_nasal_initial and nasal_final)) and (location ~= "Penang" and location ~= "Philippines" and location ~= "Singapore") then --exception for Penang, Philippines and Singapore
error("POJ error: nasality of initial and final not synchronized.")
end
table.insert(attention, export.poj_check_syllable(initial[index], final[index], location))
initial[index] = initial_ipa[location .. "-" .. initial[index]] or initial_ipa[initial[index]]
final[index] = final_ipa[location .. "-" .. final[index]] or final_ipa[final[index]]
or error("Cannot recognise " .. final[index] .. ".")
if index < syllables.length then
final[index] = gsub(final[index], "ʔ", "(ʔ)")
end
end
for index = 1, syllables.length do
sandhi[index] = tone_value[location][tone[index]]
local sandhi_hash = get_sandhi_from_post(location, tone[index], tone[index+1])
or tone_sandhi[location][tone[index]..(diminutive[index+1] and "d" or "")]
or tone_sandhi[location][tone[index]]
if index < syllables.length and tone_value[location][sandhi_hash] ~= tone_value[location][tone[index]] then
sandhi[index] = sandhi[index] .. "-" .. tone_value[location][sandhi_hash]
end
table.insert(result, initial[index] .. final[index] .. sandhi[index])
end
table.insert(word_result, table.concat(result, " "))
end
return (gsub(formatting.leading .. table.concat(word_result, " ") ..
formatting.trailing, "[12345%-]", tone_superscript)) .. table.concat(attention)
end
local psdb_initial = {
["p"] = "'p", ["ph"] = "ph", ["b"] = "'b",
["t"] = "'d", ["th"] = "'t",
["k"] = "'k", ["kh"] = "'q", ["g"] = "'g",
["chi"] = "c", ["ch"] = "z",
["chhi"] = "ch", ["chh"] = "zh",
["si"] = "s", ["s"] = "s",
["j"] = "j",
["l"] = "l", ["h"] = "'h",
["m"] = "m", ["n"] = "n", ["ng"] = "ng",
[""] = "'"
}
local function psdb_final(text)
local basic_psdb = {
--single vowel tone 12357
["a1"] = "af", ["a2"] = "ar", ["a3"] = "ax", ["a5"] = "aa", ["a7"] = "a",
["i1"] = "y", ["i2"] = "ie", ["i3"] = "ix", ["i5"] = "ii", ["i7"] = "i",
["u1"] = "w", ["u2"] = "uo", ["u3"] = "ux", ["u5"] = "uu", ["u7"] = "u",
["e1"] = "ef", ["e2"] = "ea", ["e3"] = "ex", ["e5"] = "ee", ["e7"] = "e",
["oo1"] = "of", ["oo2"] = "or", ["oo3"] = "ox", ["oo5"] = "oo", ["oo7"] = "o",
["o1"] = "oy", ["o2"] = "oir", ["o3"] = "oix", ["o5"] = "ooi", ["o7"] = "oi",
["ng1"] = "'ngf", ["ng2"] = "'ngr", ["ng3"] = "'ngx", ["ng5"] = "'ngg", ["ng7"] = "'ng",
["m1"] = "'mf", ["m2"] = "'mr", ["m3"] = "'mx", ["m5"] = "'mm", ["m7"] = "'m",
--double vowel tone 12357
["ai1"] = "ay", ["ai2"] = "ae", ["ai3"] = "aix", ["ai5"] = "aai", ["ai7"] = "ai",
["au1"] = "aw", ["au2"] = "ao", ["au3"] = "aux", ["au5"] = "aau", ["au7"] = "au",
["ia1"] = "iaf", ["ia2"] = "iar", ["ia3"] = "iax", ["ia5"] = "iaa", ["ia7"] = "ia",
["iau1"] = "iaw", ["iau2"] = "iao", ["iau3"] = "iaux", ["iau5"] = "iaau", ["iau7"] = "iau",
["io1"] = "ioy", ["io2"] = "ioir", ["io3"] = "ioix", ["io5"] = "iooi", ["io7"] = "ioi",
["iu1"] = "iw", ["iu2"] = "iuo", ["iu3"] = "iux", ["iu5"] = "iuu", ["iu7"] = "iu",
["oa1"] = "oaf", ["oa2"] = "oar", ["oa3"] = "oax", ["oa5"] = "oaa", ["oa7"] = "oa",
["oai1"] = "oay", ["oai2"] = "oae", ["oai3"] = "oaix", ["oai5"] = "oaai", ["oai7"] = "oai",
["oe1"] = "oef", ["oe2"] = "oea", ["oe3"] = "oex", ["oe5"] = "oee", ["oe7"] = "oe",
["ui1"] = "uy", ["ui2"] = "uie", ["ui3"] = "uix", ["ui5"] = "uii", ["ui7"] = "ui",
--nasal vowel tone 12357
--nasal ending tone 12357
["ian1"] = "iefn", ["ian2"] = "iern", ["ian3"] = "iexn", ["ian5"] = "ieen", ["ian7"] = "ien",
["iong1"] = "iofng", ["iong2"] = "iorng", ["iong3"] = "ioxng", ["iong5"] = "ioong", ["iong7"] = "iong",
--stopped single vowel tone 48
["op4"] = "ob", ["op8"] = "op",
["ot4"] = "od", ["ot8"] = "ot",
["ok4"] = "og", ["ok8"] = "ok",
--stopped double vowel tone 48
["iop4"] = "iob", ["iop8"] = "iop",
["iot4"] = "iod", ["iot8"] = "iot",
["iok4"] = "iog", ["iok8"] = "iok",
}
text = gsub(text, "[一二三四五六七八]", {["一"] = "1", ["二"] = "2", ["三"] = "3", ["四"] = "4", ["五"] = "5", ["六"] = "6", ["七"] = "7", ["八"] = "8"})
if find(text, "ⁿ[12357]$") then
local basic = gsub(text, "ⁿ", "")
basic = gsub(basic, "^o([12357])$", "oo%1")
if basic_psdb[basic] then
return "v" .. basic_psdb[basic]
end
elseif find(text, ".[mn]g?[12357]$") and not find(text, "^ian[12357]$") and not find(text, "^iong[12357]$") then
local basic = gsub(text, "[mn]g?([12357])$", "%1")
local ending = match(text, "([mn]g?)[12357]$")
basic = gsub(basic, "^o([12357])$", "oo%1")
if basic_psdb[basic] then
return basic_psdb[basic] .. ending
end
elseif find(text, "[ptkh]ⁿ?4$") and not find(text, "^i?o[ptk]4$") then
local basic = gsub(text, "[ptkh](ⁿ?)4$", "%1") .. "7"
local ending = match(text, "([ptkh])ⁿ?4$")
ending = gsub(ending, "[ptkh]",{p = "b", t = "d", k = "g", h = "q"})
if find(basic, "ⁿ") then
basic = gsub(basic, "ⁿ", "")
basic = gsub(basic, "^o([12357])$", "oo%1")
if basic_psdb[basic] then
return "v" .. basic_psdb[basic] .. ending
end
else
if basic_psdb[basic] then
return basic_psdb[basic] .. ending
end
end
elseif find(text, "[ptkh]ⁿ?8$") and not find(text, "^i?o[ptk]8$") then
local basic = gsub(text, "[ptkh](ⁿ?)8$", "%1") .. "7"
local ending = match(text, "([ptkh])ⁿ?8$")
if find(basic, "ⁿ") then
basic = gsub(basic, "ⁿ", "")
basic = gsub(basic, "^o([12357])$", "oo%1")
if basic_psdb[basic] then
return "v" .. basic_psdb[basic] .. ending
end
else
if basic_psdb[basic] then
return basic_psdb[basic] .. ending
end
end
else
return basic_psdb[text]
end
end
function export.poj_to_psdb_conv(text)
if type(text) == "table" then text = text.args[1] end
local readings = split(lower(text), "/", true)
for i = 1, #readings do
-- will ignore # boundary marker
local parts = split(gsub(readings[i], "#", ""), " ", true)
for j = 1, #parts do
local initial = {}
local final = {}
local psdb = {}
local tone = {}
local tonesandhi = {}
local neutral = {}
parts[j] = gsub(parts[j], "%-%-", "-0")
local p = split(parts[j], "-", true)
local ar = {}
local triple = {}
for i, item in ipairs(p) do
if find(item, "仔") then
item = gsub(item, "仔", "á")
ar[i] = true
end
if find(item, "%(") then
item = gsub(item, "[%(%)]", "")
triple[i] = true
end
if find(item, "^0") then
item = gsub(item, "0", "")
neutral[i] = true
end
item = gsub(item, "ớ", "óo")
item = gsub(item, "ờ", "òo")
item = gsub(item, "ơ̂", "ôo")
item = gsub(item, "ơ̄", "ōo")
item = gsub(item, "ơ̍", "o̍o")
item = gsub(item, "ơ", "oo")
item = gsub(item, "͘", "o")
item = gsub(item, "[̍̂̄̀]",{["̍"] = "捌", ["̂"] = "伍", ["̄"] = "柒", ["̀"] = "叁"})
if find(item, "[aeiou][捌]?[ptkh]") or find(item, "[^aeiou][mn][捌]?g?[ptkh]") then
if find(item, "捌") then
tone[i] = "八"
else
tone[i] = "四"
end
elseif find(item, "[áíúéóḿń貳]") then
tone[i] = "二"
elseif find(item, "[àìùèòǹ叁]") then
tone[i] = "三"
elseif find(item, "[âîûêô伍]") then
tone[i] = "五"
elseif find(item, "[āīūēō柒]") then
tone[i] = "七"
else
tone[i] = "一"
end
item = gsub(item, "[áíúéóḿńàìùèòǹâîûêôāīūēō貳叁伍柒捌]",{["á"] = "a", ["í"] = "i", ["ú"] = "u", ["é"] = "e", ["ó"] = "o", ["ḿ"] = "m", ["ń"] = "n", ["貳"] = "", ["à"] = "a", ["ì"] = "i", ["ù"] = "u", ["è"] = "e", ["ò"] = "o", ["ǹ"] = "n", ["叁"] = "", ["â"] = "a", ["î"] = "i", ["û"] = "u", ["ê"] = "e", ["ô"] = "o", ["伍"] = "", ["ā"] = "a", ["ī"] = "i", ["ū"] = "u", ["ē"] = "e", ["ō"] = "o", ["柒"] = "", ["捌"] = ""})
if sub(item,1,3) == "chh" then
initial[i] = "chh"
final[i] = sub(item,4,-1)
elseif sub(item,1,1) == "m" then
if sub(item,2,2) == "h" then
initial[i] = ""
final[i] = "mh"
elseif sub(item,2,2) == "" then
initial[i] = ""
final[i] = "m"
else
initial[i] = "m"
final[i] = sub(item,2,-1)
end
elseif sub(item,1,2) == "ng" then
if sub(item,3,3) == "h" then
initial[i] = ""
final[i] = "ngh"
elseif sub(item,3,3) == "" then
initial[i] = ""
final[i] = "ng"
else
initial[i] = "ng"
final[i] = sub(item,3,-1)
end
elseif find(item, "^[ptkc]h") then
initial[i] = sub(item,1,2)
final[i] = sub(item,3,-1)
elseif find(item, "^[npbtkgjshl]") then
initial[i] = sub(item,1,1)
final[i] = sub(item,2,-1)
else
initial[i] = ""
final[i] = item
end
if find(initial[i], "^chh?$") or initial[i] == "s" then
if find(final[i], "^i") then
initial[i] = initial[i] .. "i"
end
end
p[i] = item
end
for i = 1, #p do
if tone[i] == "一" then
tonesandhi[i] = "七"
elseif tone[i] == "二" then
tonesandhi[i] = "一"
elseif tone[i] == "三" then
tonesandhi[i] = ar[i+1] and "一" or "二"
elseif tone[i] == "四" then
tonesandhi[i] = "八"
elseif tone[i] == "五" then
tonesandhi[i] = "七"
elseif tone[i] == "七" then
tonesandhi[i] = ar[i+1] and "七" or "三"
elseif tone[i] == "八" then
tonesandhi[i] = "四"
end
if triple[i] then
local tonesandhi1 = nil
if tone[i] == "五" then
tonesandhi1 = "五"
elseif tone[i] == "七" then
tonesandhi1 = "一"
end
psdb[i] = (psdb_initial[initial[i]] or "error")
..(psdb_final(final[i]..(tonesandhi1 or tonesandhi[i])) or "error")
..psdb_initial[initial[i]]
..psdb_final(final[i]..tonesandhi[i])
..psdb_initial[initial[i]]
..psdb_final(final[i]..(i == #tone and tone[i] or tonesandhi[i]))
else
psdb[i] = (psdb_initial[initial[i]] or "error")
..(psdb_final(final[i]..(i == #tone and tone[i] or tonesandhi[i])) or "error")
end
if neutral[i] then
psdb[i] = "~" .. (psdb_initial[initial[i]] or "error")
..(psdb_final(final[i].."七") or "error")
end --psdb[i] = p[i]
end
parts[j] = table.concat(psdb, "")
end
readings[i] = table.concat(parts, " ")
readings[i] = gsub(readings[i], "'+", "'")
readings[i] = gsub(readings[i], "^'", "")
readings[i] = gsub(readings[i], "([^a-z])'", "%1")
readings[i] = gsub(readings[i], "([^pbdtkqgczsjlmnhaeiou])'([aeiouwy])", "%1%2")
readings[i] = gsub(readings[i], "([^aeiouwy])'([ptkbdqmn])", "%1%2")
readings[i] = gsub(readings[i], "([^aeiouwyn])'g", "%1g")
readings[i] = gsub(readings[i], "([^aeiouwypcz])'h", "%1h")
end
return (gsub(table.concat(readings, ", "),'/([^ ])',' / %1'))
end
function export.pengim_check_syllable(initial, final, loc)
local validInitials = {
["b"] = 1, ["p"] = 1, ["m"] = 1, ["bh"] = 1,
["d"] = 1, ["t"] = 1, ["n"] = 1, ["l"] = 1,
["g"] = 1, ["k"] = 1, ["ng"] = 1, ["gh"] = 1, ["h"] = 1,
["z"] = 1, ["c"] = 1, ["s"] = 1, ["r"] = 1, [""] = 1,
}
local validFinals = {
["a"] = 1, ["i"] = 1, ["u"] = 1, ["ê"] = 1, ["o"] = 1,
["ah"] = 1, ["ih"] = 1, ["uh"] = 1, ["êh"] = 1, ["oh"] = 1,
["oi"] = 1, ["ai"] = 1, ["ou"] = 1, ["au"] = 1,
["ia"] = 1, ["iu"] = 1, ["uê"] = 1, ["ua"] = 1, ["ui"] = 1, ["uai"] = 1,
["oih"] = 1, ["auh"] = 1, ["uêh"] = 1, ["uah"] = 1,
["ang"] = 1, ["êng"] = 1, ["ong"] = 1,
["iang"] = 1, ["iong"] = 1, ["ung"] = 1, ["uang"] = 1,
["ag"] = 1, ["êg"] = 1, ["og"] = 1, ["iag"] = 1,
["iog"] = 1, ["ug"] = 1, ["uag"] = 1,
["an"] = 1, ["in"] = 1, ["ên"] = 1,
["oun"] = 1, ["ain"] = 1, ["aun"] = 1,
["iun"] = 1, ["ian"] = 1, ["uên"] = 1, ["uin"] = 1, ["uan"] = 1, ["uain"] = 1,
["ng"] = 1, ["m"] = 1,
}
local moreValidFinals = {
["Chaozhou"] = {
["e"] = 1, ["iê"] = 1, ["iou"] = 1, ["iêh"] = 1,
["eng"] = 1, ["am"] = 1, ["ing"] = 1, ["iêng"] = 1,
["im"] = 1, ["iêm"] = 1, ["uêng"] = 1, ["uam"] = 1,
["eg"] = 1, ["ab"] = 1, ["ig"] = 1, ["iêg"] = 1,
["ib"] = 1, ["iêb"] = 1, ["uêg"] = 1, ["uab"] = 1,
["oin"] = 1, ["iên"] = 1,
},
["Shantou"] = {
["e"] = 1, ["io"] = 1, ["iau"] = 1, ["ioh"] = 1,
["eng"] = 1, ["am"] = 1, ["ing"] = 1,
["im"] = 1, ["iam"] = 1, ["uam"] = 1,
["eg"] = 1, ["ab"] = 1, ["ig"] = 1,
["ib"] = 1, ["iab"] = 1, ["uab"] = 1,
["oin"] = 1, ["ion"] = 1,
},
["Chenghai"] = {
["e"] = 1, ["iê"] = 1, ["iou"] = 1, ["iêh"] = 1,
["eng"] = 1, ["ing"] = 1,
["eg"] = 1, ["ig"] = 1,
["oin"] = 1, ["iên"] = 1,
},
["Raoping"] = {
["e"] = 1, ["io"] = 1, ["iau"] = 1, ["ioh"] = 1,
["eng"] = 1, ["am"] = 1, ["ing"] = 1,
["im"] = 1, ["iam"] = 1, ["uam"] = 1,
["eg"] = 1, ["ab"] = 1, ["ig"] = 1,
["ib"] = 1, ["iab"] = 1, ["uab"] = 1,
["oin"] = 1, ["ion"] = 1,
},
["Jieyang"] = {
["e"] = 1, ["io"] = 1, ["iau"] = 1, ["ioh"] = 1,
["am"] = 1, ["im"] = 1, ["iam"] = 1, ["uêng"] = 1,
["uam"] = 1, ["ab"] = 1, ["ib"] = 1, ["iab"] = 1,
["uêg"] = 1, ["uab"] = 1, ["ion"] = 1,
},
["Chaoyang"] = {
["io"] = 1, ["iau"] = 1, ["ioh"] = 1,
["am"] = 1, ["ing"] = 1, ["im"] = 1, ["iam"] = 1,
["uêng"] = 1, ["uam"] = 1, ["ab"] = 1, ["ig"] = 1,
["ib"] = 1, ["iab"] = 1, ["uêg"] = 1, ["uab"] = 1,
["ion"] = 1,
},
}
if not (validInitials[initial] and (validFinals[final] or moreValidFinals[loc][final])) then
return "[[Category:Teochew terms needing pronunciation attention|" .. loc .. "]]"
end
return nil
end
local pengim_to_ipa_two_letters_above = {
["gh"] = "ɡ", ["bh"] = "β", ["ng"] = "ŋ",
["ao"] = "au",
}
local pengim_to_ipa_one_letter = {
--initials
["m"] = "m", ["n"] = "n",
["b"] = "p", ["d"] = "t", ["g"] = "k",
["p"] = "pʰ", ["t"] = "tʰ", ["k"] = "kʰ",
["s"] = "s", ["h"] = "h",
["r"] = "d͡z",
["z"] = "t͡s",
["c"] = "t͡sʰ",
["l"] = "l",
--vowels
["a"] = "a",
["ê"] = "e",
["e"] = "ɯ",
["i"] = "i",
["o"] = "o",
["u"] = "u",
--tones
["1"] = "³³⁻²³",
["2"] = "⁵²⁻³⁵",
["2"] = "⁵²⁻²¹",
["3"] = "²¹³⁻⁵⁵",
["4"] = "²⁻⁴",
["5"] = "⁵⁵⁻¹¹",
["6"] = "³⁵⁻¹¹",
["7"] = "¹¹",
["8"] = "⁴⁻²",
}
local pengim_tone_value = {}
pengim_tone_value["Chaozhou"] = {
["1"] = "33", ["2"] = "53", ["3"] = "213", ["4"] = "2",
["5"] = "55", ["6"] = "35", ["7"] = "11", ["8"] = "5"
}
pengim_tone_value["Shantou"] = pengim_tone_value["Chaozhou"]
pengim_tone_value["Chenghai"] = pengim_tone_value["Chaozhou"]
pengim_tone_value["Jieyang"] = pengim_tone_value["Chaozhou"]
pengim_tone_value["Chaoyang"] = {
["1"] = "33", ["2"] = "53", ["3"] = "31", ["4"] = "2",
["5"] = "55", ["6"] = "313", ["7"] = "11", ["8"] = "5"
}
local pengim_tone_sandhi = {}
pengim_tone_sandhi["Chaozhou"] = {
["1"] = "23",
["2A"] = "23", ["2B"] = "35",
["3A"] = "31", ["3B"] = "53",
["4A"] = "3", ["4B"] = "5",
["5"] = "11", ["6"] = "21", ["7"] = "", ["8"] = "2"
}
pengim_tone_sandhi["Shantou"] = {
["1"] = "23",
["2A"] = "35", ["2B"] = "35",
["3A"] = "55", ["3B"] = "55",
["4A"] = "5", ["4B"] = "5",
["5"] = "11", ["6"] = "21", ["7"] = "", ["8"] = "2"
}
pengim_tone_sandhi["Chenghai"] = pengim_tone_value["Chaozhou"]
pengim_tone_sandhi["Jieyang"] = pengim_tone_value["Chaozhou"]
pengim_tone_sandhi["Chaoyang"] = {
["1"] = "23",
["2A"] = "31", ["2B"] = "31",
["3A"] = "55", ["3B"] = "55",
["4A"] = "5", ["4B"] = "5",
["5"] = "11", ["6"] = "33", ["7"] = "33", ["8"] = "2"
}
local pengim_to_ipa_fix = {
["β"] = "b",
["p([²⁴⁻]+[ -/])"] = "p̚%1",
["k([²⁴⁻]+[ -/])"] = "k̚%1",
["h([²⁴⁻]+[ -/])"] = "ʔ%1",
}
local pengim_to_ipa_nasal = {
["a"] = "ã",
["e"] = "ẽ", -- ê
["ɯ"] = "ɯ̃", -- e
["i"] = "ĩ",
["o"] = "õ",
["u"] = "ũ",
["n"] = "",
}
function export.pengim_to_ipa_conv(text)
local result
for key, val in pairs(pengim_to_ipa_two_letters_above) do
text = gsub(text, key, val)
end
text = gsub(text, "([234]) ([^12345678 ]+)2$", "%1 %22")
local function verbose_function(char) return pengim_to_ipa_one_letter[char] or char end
-- This should work, but it doesn't convert the tone number in "diên1":
-- result = gsub(text, ".", pengim_to_ipa_one_letter)
result = gsub(text, ".", verbose_function)
result = result .. "/"
for key, val in pairs(pengim_to_ipa_fix) do
result = gsub(result, key, val)
end
result = gsub(result, "[aeiouɯ]+nʔ?[¹²³⁴⁵⁻]+[ -/]", function (a)
return gsub(a, ".", pengim_to_ipa_nasal)
end)
result = gsub(result, "(⁻[¹²³⁴⁵]+)/", function(a) return (a ~= "⁻²¹" and "/" or a .. "/") end)
result = gsub(result, ",", "#")
result = gsub(result, "(⁻[¹²³⁴⁵]+)#", function(a) return (a ~= "⁻²¹" and "" or a) end)
result = gsub(result, "#", "")
result = gsub(result, "/$", "")
result = gsub(result, "/", "/, /")
return "/" .. result .. "/"
end
function export.pengim_display(text)
text = gsub(text, "([1-8])/", "%1 / ")
text = gsub(text, "[1-8]+", "<sup>%0</sup>") -- note: originally [1-8-]+ but it seems like websites have the final tone within parentheses, if at all
return (gsub(text, "#", ""))
end
function export.pengim_to_pojlike_conv(text)
-- kind of based on MTR (http://www.ispeakmin.com/bbs/viewthread.php?tid=2784)
text = gsub(text, "#", "")
local words = split(text, "/", true)
local tone_marks = {
['1'] = '',
['2'] = '́',
['3'] = '̀',
['4'] = '',
['5'] = '̂',
['6'] = '̆', -- this is a breve; MTR: breve; current hokkien dialect convention: hacek; missionary: tilde or breve??
['7'] = '̄',
['8'] = '̍'
}
local function get_tone_mark(a, num) return tone_marks[num] .. a end
local function convert_final(x,c,t) -- convert final -g and -b (but not -ng)
if c=='b' then c='p'
elseif c=='g' then c='k' end
return x..c..t
end
local cons_correspondences = { ['bh']='b', ['gh']='g', ['b']='p', ['d']='t',
['g']='k', ['p']='ph', ['t']='th', ['k']='kh', ['z']='ts', ['c']='tsh',
['r']='j' }
local function nasalization(n,h,t) return h..'ⁿ'..t end
for i, word in ipairs(words) do
local syllables = split(word, " ", true)
for i, syllable in ipairs(syllables) do
syllable = gsub(syllable, '^[bdgptkzcr]h?', cons_correspondences)
syllable = gsub(syllable, '([^n])([bg])(%d)', convert_final)
syllable = gsub(syllable, '[êe]', { ['ê']='e', ['e']='ṳ' } )
syllable = gsub(syllable, 'ao', 'au' )
syllable = gsub(syllable, '(n)(h?)(%d)', nasalization)
if find(syllable, 'uai') then
syllable = gsub(syllable, 'uai', 'ua符i')
elseif find(syllable, '[aiueoṳ][aiueoṳ]') then
syllable = gsub(syllable, '([aouṳ])i', '%1符i') -- ?i
syllable = gsub(syllable, 'i([aoeuṳ])', 'i%1符') -- i?
syllable = gsub(syllable, '([ao])([uṳ])', '%1符%2') -- ?u
syllable = gsub(syllable, '([uṳ])([ae])', '%1%2符') -- u?
elseif find(syllable, '[^aiueoṳ][aiueoṳ]') or find(syllable, '^[aiueoṳ]') then
syllable = gsub(syllable, '([aiueoṳ])', '%1符')
elseif find(syllable, 'ngh?%d') then
syllable = gsub(syllable, 'ng(h?)(%d)', 'n符g%1%2')
elseif find(syllable, '[^aiueoṳ]h?%d') then
syllable = gsub(syllable, '([^aiueoṳ])(h?)(%d)', '%1符%2%3')
end
syllable = gsub(syllable, '符(.*)(%d)', get_tone_mark)
syllables[i] = syllable
end
words[i] = table.concat(syllables, ' ')
end
return toNFC(table.concat(words, ' / '))
end
return export