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)) |
No edit summary |
||
Line 1:
-- This module is primarily maintained at:
-- https://en.wiktionary.org/wiki/Module:mh-pronunc
local MERGED_VOWELS = false
local PHONETIC_DETAILS = false
local SPECIAL_EPENTHESIS = true
local W_OFF_GLIDES = true
local export = {}
local math_max = math.max
local mw_text_gsplit = mw.text.gsplit
local mw_text_split = mw.text.split
local mw_text_trim = mw.text.trim
local mw_ustring_gsub = mw.ustring.gsub
local mw_ustring_lower = mw.ustring.lower
local string_byte = string.byte
local string_find = string.find
local string_gmatch = string.gmatch
local string_gsub = string.gsub
local string_lower = string.lower
local string_sub = string.sub
local table_concat = table.concat
local table_remove = table.remove
local ASYLL = "̯"
Line 49 ⟶ 69:
return ""
else
local atLeft =
local atRight =
if atLeft then
if atRight then
return
else
return
end
elseif atRight then
return
else
return text
Line 67 ⟶ 87:
local function parseBoolean(text)
if type(text) == "string" then
text =
if text ~= "" and text ~= "0" and
return true
end
Line 78 ⟶ 98:
chars = chars or {}
local index = 1
for ch in
chars[index] = ch
index = index + 1
Line 84 ⟶ 104:
if index <= #chars then
if shorten then
else
repeat
Line 96 ⟶ 116:
local function string_gsub2(text, pattern, subst)
return
end
Line 122 ⟶ 142:
local function ZTBL(text, sep)
local tbl = {}
for key in
tbl[key] = true
end
Line 137 ⟶ 157:
local outSeq = {}
code =
code =
for text in
text = fastTrim(text)
if text ~= "" then
local temp =
if temp ~= "" then
error("'"..code.."' contains unsupported characters: "..temp)
Line 153 ⟶ 173:
["y"] = "0", ["h"] = "0h", ["w"] = "0w"
}
text =
text =
if
error("contains misplaced underscores: "..code)
end
-- a plain {i} protected from dialect-specific reflexes
text =
-- "yi'y" and "'yiy" sequences
text =
if aposA ~= "" then
-- "dwelling upon" i
Line 197 ⟶ 217:
["0w"] = "_W"
}
text =
if
error("contains g that is not part of ng: "..code)
end
Line 220 ⟶ 240:
["'"] = ""
}
text =
-- Enforce CVC, CVCVC, CVCCVC, etc. phonotactics,
-- but allow VC, CV at affix boundaries
-- where a vowel may link to another morpheme's consonant.
temp =
if
then
error("pseudo-glides may not neighbor a consonant")
end
if
error("pseudo-glides may only be at the beginning or end"..code)
end
if
error("vowels must be separated by a consonant: "..code)
end
if
error("each consonant cluster is limited to two: "..code)
end
if
error("may not end with a consonant cluster: "..code)
end
if consonX ~= consonY then
error("may only begin with single or geminated consonant: "
Line 325 ⟶ 345:
local version = args and args.version
local map = BENDER_MAPS[
type(version) == "string" and
] or BENDER_DEFAULT
local outSeq = {}
for _, text in pairs(inSeq) do
text =
addUnique(outSeq, text)
end
Line 347 ⟶ 367:
local function toMOD(text)
text =
return text
end
Line 387 ⟶ 407:
local outSeq = {}
for _, text in pairs(inSeq) do
text =
addUnique(outSeq, text)
end
Line 427 ⟶ 447:
local function maxF1(a, b)
if b then
return VOWEL[
else
return VOWEL[
end
end
Line 436 ⟶ 456:
-- Morphemes can begin with geminated consonants, but spoken words cannot.
text =
function(conson, _, __, vowel)
if conson == "hG" then
Line 457 ⟶ 477:
-- To block this in the template argument, use "'i" instead of "i".
text = " "..text
text =
"([ jGw])( *)(h[jw])( *)i( *)(h[jw])( *)("..VI..")",
function(nonVowel, _, consonX, __, ___, consonY, ____, vowel)
Line 481 ⟶ 501:
end
)
text =
-- Restore protected {i}, we won't be checking for it anymore.
text =
return text
Line 511 ⟶ 531:
-- and no pseudo-glide, display phrase three times
-- with each of the different pseudo-glides.
if IS_VOWEL[
toPhoneticRemainder("_j"..text, config)
toPhoneticRemainder("_G"..text, config)
Line 517 ⟶ 537:
return
end
if IS_VOWEL[
toPhoneticRemainder(text.."_j", config)
toPhoneticRemainder(text.."_G", config)
Line 558 ⟶ 578:
end
toPhoneticRemainder(code, config)
addUnique(outSeq,
config.outSeq = outSeq
config.initialJ = initialJ
Line 569 ⟶ 589:
if not diphthongs then
-- {ww} always causes the second glide to surface.
text =
end
text =
text =
text =
text =
text =
-- Preserve these exceptionally stable clusters.
text =
-- Unstable consonant clusters trigger epenthesis.
-- Liquids before coronal obstruents.
text =
-- Nasals and liquids after coronal obstruents.
text =
-- Heterorganic clusters.
-- Labial consonants neighboring coronal or dorsal consonants.
text =
-- Coronal consonants neighboring labial or dorsal consonants.
text =
-- Dorsal consonants neighboring labial or coronal consonants.
text =
-- Organic speech involves certain consonant cluster assimilations.
Line 603 ⟶ 623:
-- Forward assimilation of rounded consonants.
-- There is no rounded coronal obstruent.
text =
-- Backward assimilation of remaining secondary articulations.
text =
-- Backward nasal assimilation of primary articulations.
text =
-- No longer need to protect exceptionally stable consonant clusters.
text =
-- Give a vowel height to all epenthetic vowels that still lack one.
Line 622 ⟶ 642:
-- Tag all vowels for next set of operations.
text =
-- There is no variation in the surface realizations of vowels
Line 653 ⟶ 673:
-- Represent vowels neighboring pseudo-glides as diphthongs regardless.
text =
"(_)([jGw])( *)/([aEei])(@?)( *)(.)([jGw])", subst)
text =
"(.)([jGw])( *)/([aEei])(@?)( *)(_)([jGw])", subst)
Line 728 ⟶ 748:
-- Exceptionally for the single word "rej".
text =
function(prefix, vowel, suffix)
return prefix..FRONT_VOWEL[vowel]..suffix
Line 736 ⟶ 756:
-- Vowels always claim the secondary articulation
-- of a neighboring back unrounded glide.
text =
return prefix..BACK_VOWEL[vowel]
end)
text =
return BACK_VOWEL[vowel]..suffix
end)
Line 745 ⟶ 765:
-- Unless already claimed, epenthetic vowels after a glide
-- always claim the secondary articulation to the left.
text =
function(primaryL, secondaryL, _, vowel)
return (
Line 756 ⟶ 776:
-- Unless already claimed, vowels before a glide
-- always claim the secondary articulation to the right.
text =
function(vowel, epenth, primaryR, secondaryR)
return (
Line 767 ⟶ 787:
-- For now, unless already claimed, vowels before a rounded consonant
-- claim the secondary articulation to the right.
text =
return ROUND_VOWEL[vowel]..suffix
end)
Line 773 ⟶ 793:
-- For now, unless already claimed, remaining vowels
-- claim the secondary articulation to the left.
text =
function(secondaryL, _, vowel)
return secondaryL.._..VOWEL[F1[vowel]][F2[secondaryL]]
Line 784 ⟶ 804:
return prefix..VOWEL[F1[vowel]][F2[secondaryR]]..infix..secondaryR
end
text =
-- Change certain vowels in a special environment from round to front.
text =
function(prefix, vowelL, _, conson, vowelR)
if conson ~= "hw" or F1[vowelL] ~= F1[vowelR] then
Line 794 ⟶ 814:
end
)
text =
function(prefix, vowel, suffix)
return prefix..FRONT_VOWEL[vowel]..suffix
end
)
text =
text =
-- Tag certain glide-vowel-non-glide sequences for special reflexes.
text =
text =
text =
text =
"([EeiAV7MOou]@? *h[jw] *)([aAQ] *[ptkmnNrl])", "%1/%2")
text =
"(hj *[aEei]@? *hw *)("..V.." *[ptkmnNrl])", "%1/%2")
-- Untag certain sequences, exempting them from special reflexes.
text =
-- Special reflexes.
text =
function(secondaryL, _, vowel, __, primaryR, secondaryR)
return secondaryL.._..
Line 827 ⟶ 847:
-- Temporarily cancel epenthetic {i} neighboring {yi'y}.
text =
-- {yi'y} neighboring {i} may now be demoted to {y}.
text =
text =
-- {'yiy} may now be demoted everywhere.
text =
text =
-- For the purposes of this template,
-- surface all glides pronounced in isolation.
text =
if not diphthongs then
-- Opportunistically front this vowel.
text =
"hj( *)A( *[kN]G *[kN]?G? *"..V..")", "hj%1a%2")
-- Surface certain glides.
text =
text =
text =
text =
text =
"([ptkmnNrl].).@( *)h(j *[AV7MQOou])", "%1%2H%3")
text =
text =
function(vowelL, infix, _, vowelR)
if F1[vowelL] > F1[vowelR] then
Line 860 ⟶ 880:
end
)
text =
function(vowelL, infix, _, vowelR)
if F1[vowelL] > F1[vowelR] then
Line 867 ⟶ 887:
end
)
text =
function(vowelL, infix, _, vowelR)
if F1[vowelL] < F1[vowelR] then
Line 874 ⟶ 894:
end
)
text =
function(vowel, _, secondary)
if F2[vowel] ~= F2[secondary] then
Line 884 ⟶ 904:
-- Protect word-final epenthetic vowels after non-glides
-- from next operation.
text =
-- De-epenthesize vowels if they still neighbor unsurfaced glides.
text =
text =
-- Adjust F1 of currently remaining epenthetic vowels.
Line 900 ⟶ 920:
)
text =
end
-- Delete all remaining unsurfaced glides.
text =
-- Surface realization for {yi'y}.
text =
if not diphthongs then
Line 935 ⟶ 955:
-- Turn this surfaced glide into an epenthetic vowel.
text =
end
if MERGED_VOWELS then
text = string_gsub(text, "[EO]", function(vowel)
return VOWEL[F1[vowel] + 1][F2[vowel]]
end)
end
Line 958 ⟶ 984:
index = index2
until index == 1
text =
end
Line 968 ⟶ 994:
["l"] = { ["j"] = "L" }
}
for primary in
local map = CONSON_REFLEX[primary]
if not map then
Line 981 ⟶ 1,007:
-- Tweak remaining consonants, using offsets as a guide.
text =
function(
offsetL, primaryL, secondaryL, _, primaryR, secondaryR, offsetR
Line 1,004 ⟶ 1,030:
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.
-- Assimilate primary dental or postalveolar.
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,041 ⟶ 1,078:
secondaryL = ""
end
else
secondaryL = ""
end
Line 1,066 ⟶ 1,103:
-- Elegantly connect long and epenthetic vowels across word gaps.
text =
text =
text =
text =
text =
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 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, "([kgN])w( *[aEeiAV7M])(@?)", subst)
end
if SPECIAL_EPENTHESIS then
text = string_gsub(text, "(.)@("..V..")", "%1^%2")
end
end
if SPECIAL_EPENTHESIS then
text = string_gsub(text, "(.)@", "(%1)")
text = string_gsub(text, "%)(=?)%(", "%1")
end
-- Convert remaining word gaps to liaison.
text =
text =
text =
if not PHONETIC_IPA then
Line 1,092 ⟶ 1,150:
["p"] = "p",
["b"] = "b",
["B"] = "β̞",
["t"] = "t",
["d"] = "d",
Line 1,122 ⟶ 1,181:
["u"] = "u",
["^"] = ASYLL,
["@"] =
["("] = "<small>(</small>",
[")"] = "<small>)</small>",
[":"] = "ː",
["="] = 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["Z"] = map["Z"] or (map["D"]
map["kG"] = map["kG"] or map["k"]
map["gG"] = map["gG"] or map["g"]
Line 1,138 ⟶ 1,226:
map["Hj"] = map["Hj"] or map["i"]..map["^"]
local key
for primary in
for secondary in
key = primary..secondary
map[key] = map[key] or (map[primary]..map[secondary])
end
end
for vowel in
key = vowel.."@"
map[key] = map[key] or (map[vowel]..map["@"])
Line 1,152 ⟶ 1,240:
PHONETIC_IPA = map
end
text =
addUnique(outSeq, text)
Line 1,168 ⟶ 1,256:
-- For other values, list both possible dialect reflexes where applicable.
local dialect = args and args.dialect and
if dialect == "rālik" then
dialect = "ralik"
Line 1,181 ⟶ 1,269:
-- Real-world pronunciation said to vary by sociological factors,
-- but all realizations may occur in free variation.
local modeJ = splitChars(args and args.J and
local initialJ = PHONETIC_ARG_J[modeJ[1] or ""] or "t"
local medialJ = PHONETIC_ARG_J[modeJ[2] or ""] or "s"
Line 1,210 ⟶ 1,298:
for _, str in pairs(inSeq) do
str =
str =
str =
local isRalik = dialect == "ralik"
if isRalik or dialect == "ratak" then
Line 1,241 ⟶ 1,329:
function export.bender(frame)
return
end
Line 1,249 ⟶ 1,337:
function export.parse(frame)
return
end
function export.phonemic(frame)
return
end
function export.phonetic(frame)
return
end
|