Content deleted Content added
m Protected "Module:IPAc2-mh": match protection on Template:IPAc2-mh per WP:RFPP ([Edit=Require autoconfirmed or confirmed access] (indefinite) [Move=Require autoconfirmed or confirmed access] (indefinite)) |
[w] off-glide tweak |
||
(13 intermediate revisions by 2 users not shown) | |||
Line 1:
-- This module is primarily maintained at:
-- https://en.wiktionary.org/wiki/Module:mh-pronunc
-- Please direct all technical queries and contributions there.
-- The version of this script on Wikipedia is only a mirror.
local export = {}
local MERGED_VOWELS = false
local PARENTHETICAL_EPENTHESIS = true
local PHONETIC_DETAILS = false
local W_OFF_GLIDES = true
local ASYLL = "̯"
Line 13 ⟶ 20:
local C1_ = "pbtdSZszkgmnNrlyYhH_"
local C1 = "["..C1_.."]"
local
local C = ".["..C2_.."]"
local V_ = "aEeiAV7MQOou"
local V = "["..V_.."]"
Line 24 ⟶ 32:
local EMPTY = {}
-- Adds elements to a sequence as if it's a set (retains unique elements only).
local function addUnique(seq, value)
for _, value2 in pairs(seq) do
Line 33 ⟶ 42:
end
-- Intended to work the same as JavaScript's Object.assign() function.
local function assign(target, ...)
local args = { ... }
Line 46 ⟶ 56:
local function fastTrim(text)
return string.match(text, "^%s*(.-)%s*$")
end
Line 130 ⟶ 124:
local PARSE_PSEUDO_GLIDE = {
["y"] = "0",
["h"] = "0h",
["w"] = "0w"
}
local PARSE_C_CH_CW = {
["k"] = "kG",
["kh"] = "kGh", -- N\A
["kw"] = "kW",
["l"] = "lJ",
["lh"] = "lG",
["lw"] = "lW",
["m"] = "mJ",
["mh"] = "mG",
["mw"] = "mJw", -- N\A
["n"] = "nJ",
["nh"] = "nG",
["nw"] = "nW",
["ng"] = "NG",
["ngh"] = "NGh", -- N\A
["ngw"] = "NW",
["r"] = "rG",
["rh"] = "rGh", -- N\A
["rw"] = "rW",
["0"] = "_J",
["0h"] = "_G",
["0w"] = "_W"
}
local PARSE_REMAINING = {
["b"] = "pG",
["d"] = "rj",
["e"] = "E",
["&"] = "e",
["h"] = "hG",
["j"] = "tj",
["J"] = "j",
["p"] = "pj",
["t"] = "tG",
["w"] = "hw",
["W"] = "w",
["y"] = "hj",
["z"] = "yj",
["Z"] = "Yj",
["'"] = ""
}
local function parse(code)
Line 150 ⟶ 188:
-- Recognize "y_", "h_", "w_", "_y", "_h", "_w" as pseudo-glides.
text = string.gsub(text, "_*([hwy])_+", PARSE_PSEUDO_GLIDE)
text = string.gsub(text, "_+([hwy])", PARSE_PSEUDO_GLIDE)
Line 174 ⟶ 209:
-- Convert multigraphs to pseudo-X-SAMPA format.
text = string.gsub(text, "[klmnr0]g?[hw]?", PARSE_C_CH_CW)
if string.find(text, "g") then
Line 203 ⟶ 215:
-- Convert remaining sequences to pseudo-X-SAMPA format.
text = string.gsub(text, ".", PARSE_REMAINING)
Line 277 ⟶ 272:
["hj"] = "y", ["hG"] = "h", ["hw"] = "w",
["_j"] = "", ["_G"] = "", ["_w"] = "",
["a"] = "a",
["E"] = "e",
["e"] = "&",
["i"] = "i",
["I"] = "i"
}
local BENDER_MED = assign({}, BENDER_1968, {
Line 291 ⟶ 286:
["lG"] = "ļ",
["lw"] = "ļ°",
["e"] = "ȩ"
})
local BENDER_MOD = assign({}, BENDER_MED, {
Line 302 ⟶ 297:
["lG"] = "ḷ",
["lw"] = "ḷʷ",
["e"] = "ẹ"
})
local BENDER_DEFAULT = assign({}, BENDER_MOD, {
Line 310 ⟶ 305:
["lG"] = "ļ",
["lw"] = "ļʷ",
["e"] = "ȩ"
})
local BENDER_MAPS = {
["1968"] = BENDER_1968,
["med"] = BENDER_MED,
["mod"] = BENDER_MOD
}
Line 364 ⟶ 359:
["hj"] = "j", ["hG"] = "ɰ", ["hw"] = "w",
["_j"] = "", ["_G"] = "", ["_w"] = "",
["a"] = "æ",
["E"] = "ɛ",
["e"] = "e",
["i"] = "i",
["I"] = "i"
}
if false then
Line 406 ⟶ 401:
local F2_BACK = 2
local F2_ROUND = 3
local F2 = {
["j"] = F2_FRONT, ["G"] = F2_BACK, ["w"] = F2_ROUND } local FRONT_VOWEL = {}
Line 425 ⟶ 424:
end
local function maxF1(a, b, c)
if
return VOWEL[math.max(
elseif b then
return VOWEL[math.max(F1[a], F1[b])][F2_FRONT]
else
return
end
end
Line 446 ⟶ 447:
else
if isRalik then
return "hj"..maxF1(vowel, "E")..conson.._..conson..__..vowel
else
return conson..maxF1(vowel, "E").._..conson..__..vowel
end
end
Line 493 ⟶ 494:
local IS_VOWEL = FRONT_VOWEL
local VOWEL_REFLEX
if true then
-- [f1]
local aEei = { "a", "E", "e", "i" }
local AEei = { "A", "E", "e", "i" }
local AV7i = { "A", "V", "7", "i" }
local AV7M = { "A", "V", "7", "M" }
local AV7u = { "A", "V", "7", "u" }
local AOou = { "A", "O", "o", "u" }
local QOou = { "Q", "O", "o", "u" }
-- [F2[secondaryR]][f1]
local _jv_X = { aEei, AEei, QOou }
local njv_X = { aEei, AV7i, QOou }
local hjvtX = { aEei, aEei, QOou }
local hjvkX = { AV7i, AV7i, QOou }
local _Gv_X = { AV7i, AV7M, QOou }
local rGv_X = { AEei, AV7M, QOou } -- not currently used
local hGv_X = { AV7M, AV7M, AV7M }
local _wv_X = { AV7u, AOou, QOou }
local rwv_X = { AOou, AOou, QOou }
local hwv_X = { AV7M, AOou, QOou }
local hwvtX = { AV7M, AV7M, QOou }
-- [F2[secondaryL]][F2[secondaryR]][f1]
local _Xv__ = { _jv_X, _Gv_X, _wv_X }
local nXv__ = { njv_X, _Gv_X, hwv_X }
local rXv__ = { _jv_X, _Gv_X, rwv_X }
local hXv__ = { _jv_X, hGv_X, hwv_X }
local hXvt_ = { hjvtX, hGv_X, hwvtX }
local hXvk_ = { hjvkX, hGv_X, _wv_X }
local hXvr_ = { hjvtX, hGv_X, hwv_X }
-- [primaryR][F2[secondaryL]][F2[secondaryR]][f1]
local __vX_ = {
["p"] = _Xv__, ["t"] = _Xv__, ["k"] = _Xv__,
["m"] = _Xv__, ["n"] = _Xv__, ["N"] = _Xv__,
["r"] = _Xv__, ["l"] = _Xv__
}
local n_vX_ = {
["p"] = nXv__, ["t"] = nXv__, ["k"] = nXv__,
["m"] = nXv__, ["n"] = nXv__, ["N"] = nXv__,
["r"] = nXv__, ["l"] = nXv__
}
local r_vX_ = {
["p"] = rXv__, ["t"] = rXv__, ["k"] = rXv__,
["m"] = rXv__, ["n"] = rXv__, ["N"] = rXv__,
["r"] = rXv__, ["l"] = _Xv__
}
local h_vX_ = {
["p"] = hXv__, ["t"] = hXvt_, ["k"] = hXvk_,
["m"] = hXv__, ["n"] = hXv__, ["N"] = hXvk_,
["r"] = hXvr_, ["l"] = hXv__
}
-- [primaryL][primaryR][F2[secondaryL]][F2[secondaryR]][f1]
VOWEL_REFLEX = {
["p"] = __vX_, ["t"] = __vX_, ["k"] = __vX_,
["m"] = __vX_, ["n"] = n_vX_, ["N"] = n_vX_,
["r"] = r_vX_, ["l"] = n_vX_, ["h"] = h_vX_
}
end
local CONSON_REFLEX
if true then
local map = {
["t"] = { ["j"] = "T" },
["n"] = { ["j"] = "J" },
["r"] = { ["j"] = "R" },
["l"] = { ["j"] = "L" }
}
for primary in mw.text.gsplit("ptkmnNrl", "") do
local map2 = map[primary]
if not map2 then
map2 = {}
map[primary] = map2
end
map2["j"] = map2["j"] or primary
map2["G"] = map2["G"] or primary
map2["w"] = map2["w"] or primary
end
map["T"] = map["t"]
map["J"] = map["n"]
map["R"] = map["r"]
map["L"] = map["l"]
CONSON_REFLEX = map
end
local VOICED_PRIMARY =
Line 499 ⟶ 584:
{ ["b"]="p", ["d"]="t", ["D"]="T", ["Z"]="S", ["z"]="s", ["g"]="k" }
local PHONETIC_IPA
if true then
local map = {
["p"] = "p",
["b"] = "b",
["B"] = "β̞",
["t"] = "t",
["d"] = "d",
["s"] = "s",
["z"] = "z",
["k"] = "k",
["g"] = "ɡ",
["m"] = "m",
["n"] = "n",
["N"] = "ŋ",
["r"] = "r",
["l"] = "l",
["Hj"] = "j",
["HG"] = "ʔ",
["Hw"] = "w",
["_"] = "‿",
["j"] = "ʲ",
["G"] = "ˠ",
["w"] = "ʷ",
["a"] = "æ",
["E"] = "ɛ",
["e"] = "e",
["i"] = "i",
["A"] = "ɑ",
["V"] = "ʌ",
["7"] = "ɤ",
["M"] = "ɯ",
["Q"] = "ɒ",
["O"] = "ɔ",
["o"] = "o",
["u"] = "u",
["^"] = ASYLL,
["@"] = ASYLL,
["("] = "(",
[")"] = ")",
[":"] = "ː",
["="] = TIE2
}
if PHONETIC_DETAILS then
assign(map, {
["t"] = "t̪",
["T"] = "t̠",
["d"] = "d̪",
["D"] = "d̠",
["s"] = "s̠",
["z"] = "z̠",
["k"] = "k̠",
["g"] = "ɡ̠",
["n"] = "n̠",
["J"] = "n̪",
["N"] = "ŋ̠",
["r"] = "r̠",
["R"] = "r̪",
["l"] = "l̠",
["L"] = "l̪",
["a"] = "æ̝",
["E"] = "ɛ̝",
["E@"] = "e"..map["@"],
["E^"] = "e"..map["^"],
["Q"] = "ɒ̝",
["O"] = "ɔ̝",
["O@"] = "o"..map["@"],
["O^"] = "o"..map["^"]
})
end
map["T"] = map["T"] or map["t"]
map["D"] = map["D"] or map["d"]
map["S"] = map["S"] or (map["T"]..map["s"])
map["Z"] = map["Z"] or (map["D"]..map["z"])
map["kG"] = map["kG"] or map["k"]
map["gG"] = map["gG"] or map["g"]
map["J"] = map["J"] or map["n"]
map["NG"] = map["NG"] or map["N"]
map["R"] = map["R"] or map["r"]
map["L"] = map["L"] or map["l"]
map["Hj"] = map["Hj"] or map["i"]..map["^"]
local key
for primary in mw.text.gsplit("pbBtdTDSZszkgmnJNrRlL_", "") do
for secondary in mw.text.gsplit("jGw", "") do
key = primary..secondary
map[key] = map[key] or (map[primary]..map[secondary])
end
end
for vowel in mw.text.gsplit(V_, "") do
key = vowel.."@"
map[key] = map[key] or (map[vowel]..map["@"])
key = vowel.."^"
map[key] = map[key] or (map[vowel]..map["^"])
end
PHONETIC_IPA = map
end
local function toPhoneticRemainder(code, config, leftFlag, rightFlag)
local text = code
local chars, subst
local diphthongs = config.diphthongs
-- If the phrase begins or ends with a bare vowel
-- and no pseudo-glide, display phrase
-- with each of the different pseudo-glides and possible vowel reflexes.
if IS_VOWEL[string.sub(text, 1, 1)] then
text = "_j"..code
toPhoneticRemainder(
if not diphthongs then
toPhoneticRemainder(text, config, true, rightFlag)
end
text = "_G"..code
toPhoneticRemainder(text, config, false, rightFlag)
if not diphthongs then
toPhoneticRemainder(text, config, true, rightFlag)
end
text = "_w"..code
toPhoneticRemainder(text, config, false, rightFlag)
if not diphthongs then
toPhoneticRemainder(text, config, true, rightFlag)
end
return
end
if IS_VOWEL[string.sub(text, -1)] then
toPhoneticRemainder(text
if not diphthongs then
toPhoneticRemainder(text, config, leftFlag, true)
end
text = code.."_G"
toPhoneticRemainder(text, config, leftFlag, false)
if not diphthongs then
toPhoneticRemainder(text, config, leftFlag, true)
end
text = code.."_w"
toPhoneticRemainder(text, config, leftFlag, false)
if not diphthongs then
toPhoneticRemainder(text, config, leftFlag, true)
end
return
end
local initialJ = config.initialJ
local medialJ = config.medialJ
Line 567 ⟶ 770:
-- Glides always trigger epenthesis, even neighboring other glides.
text = string_gsub2(text, "([aEei])( *h)(.)( *)(h)%3( *)([aEei])",
function(vowelL, _, secondary, __, primaryR, ___, vowelR)
if secondary == "w" then
primaryR = "H"
end
return (
vowelL.._..secondary..
maxF1(vowelL, vowelR).."@"..
__..primaryR..secondary..___..vowelR
)
end
)
text = string.gsub(text, "([aEei])( *)hG( *.[jGw])", "%1%2hG%1@%3")
text = string.gsub(text, "(.[jGw])( *)hG( *)([aEei])", "%1%4@%2hG%3%4")
Line 617 ⟶ 828:
text = string_gsub2(text, "(.)( *..)v( *.. *)(.)",
function(vowelL, consonL, consonR, vowelR)
return vowelL..consonL..
maxF1(vowelL, vowelR, "E").."@".. consonR..vowelR end
)
Line 628 ⟶ 841:
text = string_gsub2(text, "([jGw])( *)/([aEei])(@? *.)%1",
function(secondary, _, vowel, infix)
return (
secondary.._..VOWEL[F1[vowel]][F2[secondary]].. infix..secondary
)
end
)
if diphthongs then
text = string_gsub2(text, "(.)([jGw])( *)/([aEei])(@?)( *)(.)([jGw])",
function(
primaryL, secondaryL, _, vowel, epenth, __, primaryR, secondaryR
)
local f1 = F1[vowel]
return (
primaryL..secondaryL.._..
VOWEL[f1][F2[secondaryL]]..epenth.."="..
VOWEL[f1][F2[secondaryR]]..epenth..__..
primaryR..secondaryR
)
end
)
else
--
primaryL, secondaryL, _, vowel, epenth,
__, primaryR, secondaryR, flag
)
local f2L = F2[secondaryL]
local f2R = F2[secondaryR]
if flag then
f2 = math.max(f2L, f2R)
else
f2 = math.min(f2L, f2R)
end
return (
primaryL..secondaryL.._..
primaryR..secondaryR
)
end
text = string.gsub(text, "(_)([jGw])( *)/("..V..")(@?)( *)(.)([jGw])",
function(a, b, c, d, e, f, g, h)
return subst(a, b, c, d, e, f, g, h, leftFlag)
end
)
text = string.gsub(text, "(.)([jGw])( *)/("..V..")(@?)( *)(_)([jGw])",
function(a, b, c, d, e, f, g, h)
return subst(a, b, c, d, e, f, g, h, rightFlag)
end
)
-- Vowels between two non-glides have the most predictable reflexes.
Line 778 ⟶ 960:
end
)
-- Change certain vowels in a special environment from round to front.
text =
function(prefix,
return prefix..FRONT_VOWEL[vowel]..suffix
end
)
text = string.gsub(text, "(hj *)([Oou])( *)(.w)( *)("..V..")",
function(prefix, vowelL, _, conson, __, vowelR)
if conson ~= "hw" or F1[vowelL] ~= F1[vowelR] then
return prefix..FRONT_VOWEL[vowelL].._..conson..__..vowelR
end
end
)
text = string.gsub(text, "(
function(prefix, vowel, suffix)
return prefix..FRONT_VOWEL[vowel]..suffix
Line 803 ⟶ 983:
-- Tag certain glide-vowel-non-glide sequences for special reflexes.
text = string.gsub(text, "(
text = string.gsub(text, "^ *(h[jw] *)("..V.." *[ptkmnNrl])", "%1/%2")
text = string.gsub(text, "(@ *h[jw] *)("..V.." *[ptkmnNrl])", "%1/%2")
text = string.gsub(text,
"([EeiAV7MOou]
text = string.gsub(text, "([iMu] *hj *)([EeV7] *[kN]G)", "%1/%2")
text = string.gsub(text,
"(hj *[aEei]@? *hw *)("..V.." *[ptkmnNrl])", "%1/%2")
Line 817 ⟶ 998:
text = string.gsub(text, "([jw])( *)/("..V..")( *)(.)([jGw])",
function(secondaryL, _, vowel, __, primaryR, secondaryR)
return (
secondaryL.._.. VOWEL_REFLEX["h"][primaryR]
[F2[secondaryL]][F2[secondaryR]][F1[vowel]]..
__..primaryR..secondaryR
)
end
)
-- Exceptional phrase-initial reflex.
text = string.gsub(text, "^ *([Hh]j *)([V7])( *[kN]G)",
function(prefix, vowel, suffix)
return prefix..FRONT_VOWEL[vowel]..suffix
end
)
text = string.gsub(text, "^ *([Hh]w *)M( *tG)", "%1u%2")
end
Line 829 ⟶ 1,020:
text = string.gsub(text, "i@( *yj)", "%1")
-- {yi'y} neighboring {i} may now be demoted to {y}.
text = string.gsub(text, "([iMu]@? *)yj", "%1hj")
text = string.gsub(text, "yj( *[iMu])", "hj%1")
-- {'yiy} may now be demoted everywhere.
Line 837 ⟶ 1,028:
-- For the purposes of this template,
-- surface all glides pronounced in isolation.
text = string.gsub(text, "^ *h(.) *$", "H%1")
if not diphthongs then
-- Opportunistically front
text = string.gsub(text, "(hj *)([A7M])( *[kN]G *[kN]?G? *"..V..")",
function(prefix, vowel, suffix)
return prefix..FRONT_VOWEL[vowel]..suffix
end
)
-- Surface certain glides.
text = string.gsub(text, "^ *h(w *[Oou])", "H%1")
text = string.gsub(text, "h(w *[aEeiAV7M])", "H%1")
text = string.gsub(text, "^ *h(j *[AV7MQOou])", "H%1")
text = string.gsub(text, "([ptkmnNrl]..@ *)h(w *[Oou])", "%1H%2")
text = string.gsub(text, "([ptkmnNrl]..@ *)h(j *"..V..")", "%1H%2")
text = string.gsub(text, "([AV7MQOou]@? *)h(j *[AV7MQOou])", "%1H%2")
text = string.gsub(text, "([aEeiAV7M])(@? *)hw( *)([QOou])",
Line 874 ⟶ 1,067:
end
)
text = string.gsub(text, "("..V..")( *)h([jw]) *$",
function(vowel, _, secondary)
if F2[vowel] ~= F2[secondary] then
Line 883 ⟶ 1,076:
-- Protect word-final epenthetic vowels after non-glides
-- from the next operation.
text = string.gsub(text, "([ptkmnNrl]
-- De-epenthesize vowels if they still neighbor unsurfaced glides.
Line 894 ⟶ 1,087:
"("..V..")( *.[jGw])(.)@( *.[jGw] *)("..V..")",
function(vowelL, infixL, vowel, infixR, vowelR)
return (
vowelL..infixL.. VOWEL[F1[maxF1(vowelL, vowelR, "E")]][F2[vowel]].."/@"..
infixR..vowelR
)
end
)
Line 917 ⟶ 1,112:
if vowelL ~= "" then
if vowelR ~= "" then
F2[vowelL
then
return vowelL.._..__..vowelR
else
return (
vowelL..epenthL.._..
maxF1(vowelL, vowelR, "E").."^"..__..vowelR
)
end
else
return vowelL.._..epenthL..maxF1(vowelL, "E").."^"..__
end
else
if vowelR ~= "" then
return _..maxF1(vowelR, "E").."^"..__..vowelR
else
return _.."i^"..__
Line 934 ⟶ 1,135:
)
--
text =
-- Collapse this epenthetic vowel and surfaced glide into a semi-vowel.
text = string.gsub(text, "([aEei])@( *)%1%^", "%2%1^")
end
if MERGED_VOWELS then
text = string.gsub(text, "[EO]", function(vowel)
return VOWEL[F1[vowel] + 1][F2[vowel]]
end)
end
Line 959 ⟶ 1,169:
until index == 1
text = table.concat(chars, "")
end
Line 1,004 ⟶ 1,195:
end
local geminated = primaryL == primaryR
if primaryL ~=
-- /tʲ/ is palatalized postalveolar.
-- /tˠ/ is velarized dental.
-- /nʲ, rʲ, lʲ/ are palatalized dental.
-- /nˠ, rˠ, lˠ/ are velarized postalveolar.
-- Regressively assimilate primary dental or postalveolar.
-- None of this will be visible unless PHONETIC_DETAILS == true.
primaryL = CONSON_REFLEX[primaryL]
[secondaryL == "j" and "G" or "j"]
primaryR = CONSON_REFLEX[primaryR][secondaryR]
else
primaryL = CONSON_REFLEX[primaryL][secondaryL]
if primaryR ~= "" then
primaryR = CONSON_REFLEX[primaryR][secondaryR]
end
end
if primaryR == "T" then
Line 1,012 ⟶ 1,215:
primaryL = finalJ
primaryR = initialJ
if primaryL == "S" and
primaryR ~= "s"
then
primaryL = "T"
elseif
primaryL == "T" and
primaryR == "s" and
medialJ == "S"
then
primaryL = "S"
end
else
primaryR = medialJ
Line 1,026 ⟶ 1,240:
if primaryR ~= "" then
-- Consonant cluster.
-- For some reason, the {t} in {lt} and {ļt} is voiceless.
if not geminated and
primaryL ~= "l" and
Line 1,033 ⟶ 1,248:
primaryR = VOICED_PRIMARY[primaryR] or primaryR
end
-- Display secondary articulation only once for the cluster.
elseif
not isInitial and
Line 1,071 ⟶ 1,276:
text = string.gsub(text, "("..V..")@ +%1", " %1 :")
text = string.gsub(text, "("..V.."@) +", " %1 ")
if W_OFF_GLIDES then
-- Add [w] off-glides after certain consonants.
subst = function(primary, _, epenth)
if epenth == "" then
return primary.."Hw".._
end
end
if false and PHONETIC_DETAILS then
text = string.gsub(text, "([pbm])(G *[aEei])(@?)",
function(primary, _, epenth)
if epenth == "" then
return primary.."B".._
end
end
)
else
text = string.gsub(text, "([pbm])G( *[aEei])(@?)", subst)
end
text = string.gsub(text, "([kgnNrl])w( *[aEeiAV7M])(@?)", subst)
-- Remove [w] off-glides after certain consonants
-- when they occur after rounded vowels.
text = string.gsub(text, "([QOou] *[nrl]? *[nrl])Hw", "%1w")
text = string.gsub(text, "([QOou] *[kgN]? *N)Hw( *M)", "%1w%2")
end
end
if PARENTHETICAL_EPENTHESIS then
if not diphthongs then
end
text = string.gsub(text, "(.)@", "(%1)")
text = string.gsub(text, "%)(=?)%(", "%1")
if not diphthongs and W_OFF_GLIDES then
if false and PHONETIC_DETAILS then
text = string.gsub(text, "([pbm]G%()([aEei])", "%1BG%2")
else
text = string.gsub(text, "([pbm]G%()([aEei])", "%1Hw%2")
end
text = string.gsub(text, "([kgnNrl]w%()([aEeiAV7M])", "%1Hw%2")
text = string.gsub(text, "([QOou] *[nrl]w%()Hw", "%1")
text = string.gsub(text, "([QOou] *Nw%()HwM", "%1M")
end
end
-- Convert remaining word gaps to liaison.
text = fastTrim(text)
text = string.gsub(text, " +", false and "_" or "")
text = string.gsub(text, ".[jGw@%^]?", PHONETIC_IPA)
|