Module:IPAc2-mh
Appearance
From Wikipedia, the free encyclopedia
The above documentation is transcluded from Module:IPAc2-mh/doc. (edit | history)
Editors can experiment in this module's sandbox (create | mirror) and testcases (create) pages.
Subpages of this module.
Editors can experiment in this module's sandbox (create | mirror) and testcases (create) pages.
Subpages of this module.
-- 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. localexport={} localMERGED_VOWELS=false localPARENTHETICAL_EPENTHESIS=true localPHONETIC_DETAILS=false localW_OFF_GLIDES=true localASYLL="̯" localBREVE="̆" localCEDILLA="̧" localMACRON="̄" localTIE="͡" localTIE2="͜" localC1_="pbtdSZszkgmnNrlyYhH_" localC1="["..C1_.."]" localC2_="jGw" localC=".["..C2_.."]" localV_="aEeiAV7MQOou" localV="["..V_.."]" localVI_=V_.."I" localVI="["..VI_.."]" localS="[%s%-]+" localUTF8_CHAR="[%z1円-127円194円-244円][128円-191円]*" localEMPTY={} -- Adds elements to a sequence as if it's a set (retains unique elements only). localfunctionaddUnique(seq,value) for_,value2inpairs(seq)do ifvalue==value2then return end end seq[#seq+1]=value end -- Intended to work the same as JavaScript's Object.assign() function. localfunctionassign(target,...) localargs={...} for_,sourceinpairs(args)do iftype(source)=="table"then forkey,valueinpairs(source)do target[key]=value end end end returntarget end localfunctionfastTrim(text) returnstring.match(text,"^%s*(.-)%s*$") end localfunctionparseBoolean(text) iftype(text)=="string"then text=string.gsub(text,"[^0-9A-Za-z]","") iftext~=""andtext~="0"andstring.lower(text)~="false"then returntrue end end returnfalse end localfunctionsplitChars(text,pattern,chars,shorten) chars=charsor{} localindex=1 forchinstring.gmatch(text,patternorUTF8_CHAR)do chars[index]=ch index=index+1 end ifindex<=#charsthen ifshortenthen table.remove(chars,index) else repeat chars[index]=nil index=index+1 untilindex>#chars end end returnchars end localfunctionstring_gsub2(text,pattern,subst) returnstring.gsub(string.gsub(text,pattern,subst),pattern,subst) end localfunctiontableGet(value,key1,key2,key3) iftype(value)~="table"orkey1==nilthen returnvalue end value=value[key1] ifkey2==nilthen returnvalue end iftype(value)~="table"then returnnil end value=value[key2] ifkey3==nilthen returnvalue end iftype(value)~="table"then returnnil end returnvalue[key3] end localfunctionZTBL(text,sep) localtbl={} forkeyinmw.text.gsplit(text,sepor" ")do tbl[key]=true end returntbl end localPARSE_PSEUDO_GLIDE={ ["y"]="0", ["h"]="0h", ["w"]="0w" } localPARSE_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" } localPARSE_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", ["'"]="" } localfunctionparse(code) localoutSeq={} code=mw.ustring.gsub(code,"%s+"," ") code=string.lower(code) fortextinmw.text.gsplit(code," *,[ ,]*")do text=fastTrim(text) iftext~=""then localtemp=string.gsub(text,"[abdeghijklmnprtwy_&'%- ]","") iftemp~=""then error("'"..code.."' contains unsupported characters: "..temp) end -- 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) ifstring.find(text,"_")then error("contains misplaced underscores: "..code) end -- a plain {i} protected from dialect-specific reflexes text=string.gsub(text,"'i","I") -- "yi'y" and "'yiy" sequences text=string.gsub(text,"('?)yi('*)y",function(aposA,aposB) ifaposA~=""then -- "dwelling upon" i return"Z" elseifaposB~=""then -- "passing over lightly" i return"z" end end) -- Convert multigraphs to pseudo-X-SAMPA format. text=string.gsub(text,"[klmnr0]g?[hw]?",PARSE_C_CH_CW) ifstring.find(text,"g")then error("contains g that is not part of ng: "..code) end -- Convert remaining sequences to pseudo-X-SAMPA format. text=string.gsub(text,".",PARSE_REMAINING) -- Enforce CVC, CVCVC, CVCCVC, etc. phonotactics, -- but allow VC, CV at affix boundaries -- where a vowel may link to another morpheme's consonant. temp=string.gsub(text,"[%s%-]+","") ifstring.find(temp,"_..[jGw]")or string.find(temp,".[jGw]_.") then error("pseudo-glides may not neighbor a consonant") end ifstring.find(temp,VI.."_."..VI)then error("pseudo-glides may only be at the beginning or end"..code) end ifstring.find(temp,VI..VI)then error("vowels must be separated by a consonant: "..code) end ifstring.find(temp,".[jGw].[jGw].[jGw]")then error("each consonant cluster is limited to two: "..code) end ifstring.find(temp,".[jGw].[jGw]$")then error("may not end with a consonant cluster: "..code) end string.gsub(temp,"^(.[jGw])(.[jGw])",function(consonX,consonY) ifconsonX~=consonYthen error("may only begin with single or geminated consonant: " ..code) end end) iftext~=""then addUnique(outSeq,text) end end end returnoutSeq end localBENDER_1968={ ["pj"]="p",["pG"]="b", ["tj"]="j",["tG"]="t", ["kG"]="k",["kw"]="q", ["mj"]="m",["mG"]="ṁ", ["nj"]="n",["nG"]="ṅ",["nw"]="n̈", ["NG"]="g",["Nw"]="g̈", ["rj"]="d",["rG"]="r",["rw"]="r̈", ["lj"]="l",["lG"]="ł",["lw"]="l̈", ["yj"]="yi'y", ["Yj"]="'yiy", ["hj"]="y",["hG"]="h",["hw"]="w", ["_j"]="",["_G"]="",["_w"]="", ["a"]="a", ["E"]="e", ["e"]="&", ["i"]="i", ["I"]="i" } localBENDER_MED=assign({},BENDER_1968,{ ["mG"]="m̧", ["nG"]="ņ", ["nw"]="ņ°", ["Nw"]="g°", ["rw"]="r°", ["lG"]="ļ", ["lw"]="ļ°", ["e"]="ȩ" }) localBENDER_MOD=assign({},BENDER_MED,{ ["kw"]="kw", ["mG"]="ṃ", ["nG"]="ṇ", ["nw"]="ṇw", ["Nw"]="gw", ["rw"]="rw", ["lG"]="ḷ", ["lw"]="ḷw", ["e"]="ẹ" }) localBENDER_DEFAULT=assign({},BENDER_MOD,{ ["mG"]="m̧", ["nG"]="ņ", ["nw"]="ņw", ["lG"]="ļ", ["lw"]="ļw", ["e"]="ȩ" }) localBENDER_MAPS={ ["1968"]=BENDER_1968, ["med"]=BENDER_MED, ["mod"]=BENDER_MOD } localfunctiontoBender(inSeq,args) -- "1968" is from "Marshallese Phonology" (1968 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. localversion=argsandargs.version localmap=BENDER_MAPS[ type(version)=="string"andstring.lower(version)or"" ]orBENDER_DEFAULT localoutSeq={} for_,textinpairs(inSeq)do text=string.gsub(text,".[jGw]?",map) addUnique(outSeq,text) end returnoutSeq end localTO_MOD={ ["Ȩ"]="Ẹ",["ȩ"]="ẹ", ["Ļ"]="Ḷ",["ļ"]="ḷ", ["M̧"]="Ṃ",["m̧"]="ṃ", ["Ņ"]="Ṇ",["ņ"]="ṇ", ["N̄"]="Ñ",["n̄"]="ñ", ["O̧"]="Ọ",["o̧"]="ọ" } localfunctiontoMOD(text) text=mw.ustring.gsub(text,".["..CEDILLA..MACRON.."]?",TO_MOD) returntext end localPHONEMIC_MAP={ ["pj"]="pj",["pG"]="pɣ", ["tj"]="tj",["tG"]="tɣ", ["kG"]="k",["kw"]="kw", ["mj"]="mj",["mG"]="mɣ", ["nj"]="nj",["nG"]="nɣ",["nw"]="nw", ["NG"]="ŋ",["Nw"]="ŋw", ["rj"]="rj",["rG"]="rɣ",["rw"]="rw", ["lj"]="lj",["lG"]="lɣ",["lw"]="lw", ["hj"]="j",["hG"]="ɰ",["hw"]="w", ["_j"]="",["_G"]="",["_w"]="", ["a"]="æ", ["E"]="ɛ", ["e"]="e", ["i"]="i", ["I"]="i" } iffalsethen assign(PHONEMIC_MAP,{ ["a"]="ɐ", ["E"]="ə", ["e"]="ɘ", ["i"]="ɨ", ["I"]="ɨ" }) end assign(PHONEMIC_MAP,{ ["yj"]=PHONEMIC_MAP.hj..PHONEMIC_MAP.i..ASYLL..PHONEMIC_MAP.hj, ["Yj"]=PHONEMIC_MAP.hj..PHONEMIC_MAP.i..PHONEMIC_MAP.hj..PHONEMIC_MAP.hj }) localfunctiontoPhonemic(inSeq) localoutSeq={} for_,textinpairs(inSeq)do text=string.gsub(text,".[jGw]?",PHONEMIC_MAP) addUnique(outSeq,text) end returnoutSeq end localVOWEL={-- VOWELS[f1][f2] {"a","A","Q"}, {"E","V","O"}, {"e","7","o"}, {"i","M","u"} } localF1={} localF2_FRONT=1 localF2_BACK=2 localF2_ROUND=3 localF2={ ["j"]=F2_FRONT, ["G"]=F2_BACK, ["w"]=F2_ROUND } localFRONT_VOWEL={} localBACK_VOWEL={} localROUND_VOWEL={} forf1,rowinpairs(VOWEL)do localfront=row[F2_FRONT] localback=row[F2_BACK] localround=row[F2_ROUND] forf2,vowelinpairs(row)do F1[vowel]=f1 F2[vowel]=f2 FRONT_VOWEL[vowel]=front BACK_VOWEL[vowel]=back ROUND_VOWEL[vowel]=round end end localfunctionmaxF1(a,b,c) ifcthen returnVOWEL[math.max(F1[a],F1[b],F1[c])][F2_FRONT] elseifbthen returnVOWEL[math.max(F1[a],F1[b])][F2_FRONT] else returnFRONT_VOWEL[a] end end localfunctiontoPhoneticDialect(text,config,isRalik) -- Morphemes can begin with geminated consonants, but spoken words cannot. text=string.gsub(text,"^(.[jGw])( *)%1( *)("..VI..")", function(conson,_,__,vowel) ifconson=="hG"then ifisRalikthen return"hG"..vowel.._.."hG"..__..vowel else return"hG".._..__..vowel end else ifisRalikthen return"hj"..maxF1(vowel,"E")..conson.._..conson..__..vowel else returnconson..maxF1(vowel,"E").._..conson..__..vowel end end end ) -- Initial {yiyV-, yiwV-, wiwV-} sequences have special behavior. -- To block this in the template argument, use "'i" instead of "i". text=" "..text text=string.gsub(text, "([ jGw])( *)(h[jw])( *)i( *)(h[jw])( *)("..VI..")", function(nonVowel,_,consonX,__,___,consonY,____,vowel) ifconsonY=="hw"then -- {yiwV-, wiwV-} sequences ifisRalikthen -- Rālik {wiwV-} becomes {yiwV-}. consonX="hj" end -- {[yw]iwV-} becomes {[yw]iwwV-} in both dialects. returnnonVowel.._..consonX..__.. "I"..___..consonY..____..consonY..vowel elseifconsonX=="hj"then -- {yiyV-} sequences ifisRalikthen -- "dwelling upon" i returnnonVowel.._..__.."Yj"..___..____..vowel else -- "passing over lightly" i returnnonVowel.._..__.."yj"..___..____..vowel end end end ) text=string.sub(text,2) -- Restore protected {i}, we won't be checking for it anymore. text=string.gsub(text,"I","i") returntext end localIS_VOWEL=FRONT_VOWEL localVOWEL_REFLEX iftruethen -- [f1] localaEei={"a","E","e","i"} localAEei={"A","E","e","i"} localAV7i={"A","V","7","i"} localAV7M={"A","V","7","M"} localAV7u={"A","V","7","u"} localAOou={"A","O","o","u"} localQOou={"Q","O","o","u"} -- [F2[secondaryR]][f1] local_jv_X={aEei,AEei,QOou} localnjv_X={aEei,AV7i,QOou} localhjvtX={aEei,aEei,QOou} localhjvkX={AV7i,AV7i,QOou} local_Gv_X={AV7i,AV7M,QOou} localrGv_X={AEei,AV7M,QOou}-- not currently used localhGv_X={AV7M,AV7M,AV7M} local_wv_X={AV7u,AOou,QOou} localrwv_X={AOou,AOou,QOou} localhwv_X={AV7M,AOou,QOou} localhwvtX={AV7M,AV7M,QOou} -- [F2[secondaryL]][F2[secondaryR]][f1] local_Xv__={_jv_X,_Gv_X,_wv_X} localnXv__={njv_X,_Gv_X,hwv_X} localrXv__={_jv_X,_Gv_X,rwv_X} localhXv__={_jv_X,hGv_X,hwv_X} localhXvt_={hjvtX,hGv_X,hwvtX} localhXvk_={hjvkX,hGv_X,_wv_X} localhXvr_={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__ } localn_vX_={ ["p"]=nXv__,["t"]=nXv__,["k"]=nXv__, ["m"]=nXv__,["n"]=nXv__,["N"]=nXv__, ["r"]=nXv__,["l"]=nXv__ } localr_vX_={ ["p"]=rXv__,["t"]=rXv__,["k"]=rXv__, ["m"]=rXv__,["n"]=rXv__,["N"]=rXv__, ["r"]=rXv__,["l"]=_Xv__ } localh_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 localCONSON_REFLEX iftruethen localmap={ ["t"]={["j"]="T"}, ["n"]={["j"]="J"}, ["r"]={["j"]="R"}, ["l"]={["j"]="L"} } forprimaryinmw.text.gsplit("ptkmnNrl","")do localmap2=map[primary] ifnotmap2then map2={} map[primary]=map2 end map2["j"]=map2["j"]orprimary map2["G"]=map2["G"]orprimary map2["w"]=map2["w"]orprimary end map["T"]=map["t"] map["J"]=map["n"] map["R"]=map["r"] map["L"]=map["l"] CONSON_REFLEX=map end localVOICED_PRIMARY= {["p"]="b",["t"]="d",["T"]="D",["S"]="Z",["s"]="z",["k"]="g"} localVOICELESS_PRIMARY= {["b"]="p",["d"]="t",["D"]="T",["Z"]="S",["z"]="s",["g"]="k"} localPHONETIC_IPA iftruethen localmap={ ["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"]="j", ["G"]="ɣ", ["w"]="w", ["a"]="æ", ["E"]="ɛ", ["e"]="e", ["i"]="i", ["A"]="ɑ", ["V"]="ʌ", ["7"]="ɤ", ["M"]="ɯ", ["Q"]="ɒ", ["O"]="ɔ", ["o"]="o", ["u"]="u", ["^"]=ASYLL, ["@"]=ASYLL, ["("]="(", [")"]=")", [":"]="ː", ["="]=TIE2 } ifPHONETIC_DETAILSthen 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"]ormap["t"] map["D"]=map["D"]ormap["d"] map["S"]=map["S"]or(map["T"]..map["s"]) map["Z"]=map["Z"]or(map["D"]..map["z"]) map["kG"]=map["kG"]ormap["k"] map["gG"]=map["gG"]ormap["g"] map["J"]=map["J"]ormap["n"] map["NG"]=map["NG"]ormap["N"] map["R"]=map["R"]ormap["r"] map["L"]=map["L"]ormap["l"] map["Hj"]=map["Hj"]ormap["i"]..map["^"] localkey forprimaryinmw.text.gsplit("pbBtdTDSZszkgmnJNrRlL_","")do forsecondaryinmw.text.gsplit("jGw","")do key=primary..secondary map[key]=map[key]or(map[primary]..map[secondary]) end end forvowelinmw.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 localfunctiontoPhoneticRemainder(code,config,leftFlag,rightFlag) localtext=code localchars,subst localdiphthongs=config.diphthongs -- If the phrase begins or ends with a bare vowel -- and no pseudo-glide, display phrase up to five times -- with each of the different pseudo-glides and possible vowel reflexes. ifIS_VOWEL[string.sub(text,1,1)]then text="_j"..code toPhoneticRemainder(text,config,false,rightFlag) ifnotdiphthongsthen toPhoneticRemainder(text,config,true,rightFlag) end text="_G"..code toPhoneticRemainder(text,config,false,rightFlag) ifnotdiphthongsthen toPhoneticRemainder(text,config,true,rightFlag) end text="_w"..code toPhoneticRemainder(text,config,false,rightFlag) ifnotdiphthongsthen toPhoneticRemainder(text,config,true,rightFlag) end return end ifIS_VOWEL[string.sub(text,-1)]then text=code.."_j" toPhoneticRemainder(text,config,leftFlag,false) ifnotdiphthongsthen toPhoneticRemainder(text,config,leftFlag,true) end text=code.."_G" toPhoneticRemainder(text,config,leftFlag,false) ifnotdiphthongsthen toPhoneticRemainder(text,config,leftFlag,true) end text=code.."_w" toPhoneticRemainder(text,config,leftFlag,false) ifnotdiphthongsthen toPhoneticRemainder(text,config,leftFlag,true) end return end localinitialJ=config.initialJ localmedialJ=config.medialJ localfinalJ=config.finalJ localnoHints=config.noHints localoutSeq=config.outSeq localvoice=config.voice ifinitialJ=="x"or medialJ=="x"or finalJ=="x" then localsubSeq={} config.outSeq=subSeq ifinitialJ=="x"then config.initialJ="t" end ifmedialJ=="x"then config.medialJ="t" end iffinalJ=="x"then config.finalJ="t" end toPhoneticRemainder(code,config) ifinitialJ=="x"then config.initialJ="s" end ifmedialJ=="x"then config.medialJ="s" end iffinalJ=="x"then config.finalJ="s" end toPhoneticRemainder(code,config) addUnique(outSeq,table.concat(subSeq," ~ ")) config.outSeq=outSeq config.initialJ=initialJ config.medialJ=medialJ config.finalJ=finalJ return end -- Glides always trigger epenthesis, even neighboring other glides. text=string_gsub2(text,"([aEei])( *h)(.)( *)(h)%3( *)([aEei])", function(vowelL,_,secondary,__,primaryR,___,vowelR) ifsecondary=="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") text=string.gsub(text,"([aEei])( *)h(.)( *.[jGw])","%1%2h%3%1@%4") text=string.gsub(text,"(.[jGw])( *)h(. *)([aEei])","%1%4@%2h%3%4") text=string.gsub(text,"(.[jGw])( *[yY].)","%1i@%2") -- Preserve these exceptionally stable clusters. text=string.gsub(text,"l([jG] *)tG","l%1|tG") -- Unstable consonant clusters trigger epenthesis. -- Liquids before coronal obstruents. text=string.gsub(text,"([rl].)( *)t","%1v%2t") -- Nasals and liquids after coronal obstruents. text=string.gsub(text,"t(.)( *[nrl])","t%1v%2") -- Heterorganic clusters. -- Labial consonants neighboring coronal or dorsal consonants. text=string.gsub(text,"([pm].)( *[tnrlkN])","%1v%2") -- Coronal consonants neighboring labial or dorsal consonants. text=string.gsub(text,"([tnrl].)( *[pmkN])","%1v%2") -- Dorsal consonants neighboring labial or coronal consonants. text=string.gsub(text,"([kN].)( *[pmtnrl])","%1v%2") -- Organic speech involves certain consonant cluster assimilations. -- Forward assimilation of rounded consonants. -- There is no rounded coronal obstruent. text=string.gsub(text,"(w *[^t])[jG]","%1w") -- Backward assimilation of remaining secondary articulations. text=string.gsub(text,"[jGw]( *.)([jGw])","%2%1%2") -- Backward nasal assimilation of primary articulations. text=string.gsub(text,"[pkrl](. *)([mnN])","%2%1%2") -- No longer need to protect exceptionally stable consonant clusters. text=string.gsub(text,"|","") -- Give a vowel height to all epenthetic vowels that still lack one. text=string_gsub2(text,"(.)( *..)v( *.. *)(.)", function(vowelL,consonL,consonR,vowelR) returnvowelL..consonL.. maxF1(vowelL,vowelR,"E").."@".. consonR..vowelR end ) -- Tag all vowels for next set of operations. text=string.gsub(text,"([aEei])","/%1") -- There is no variation in the surface realizations of vowels -- between two identical secondary articulations. text=string_gsub2(text,"([jGw])( *)/([aEei])(@? *.)%1", function(secondary,_,vowel,infix) return( secondary.._..VOWEL[F1[vowel]][F2[secondary]].. infix..secondary ) end ) ifdiphthongsthen text=string_gsub2(text,"(.)([jGw])( *)/([aEei])(@?)( *)(.)([jGw])", function( primaryL,secondaryL,_,vowel,epenth,__,primaryR,secondaryR ) localf1=F1[vowel] return( primaryL..secondaryL.._.. VOWEL[f1][F2[secondaryL]]..epenth.."=".. VOWEL[f1][F2[secondaryR]]..epenth..__.. primaryR..secondaryR ) end ) else -- Vowels neighboring pseudo-glides. subst=function( primaryL,secondaryL,_,vowel,epenth, __,primaryR,secondaryR,flag ) localf2L=F2[secondaryL] localf2R=F2[secondaryR] localf2 ifflagthen f2=math.max(f2L,f2R) else f2=math.min(f2L,f2R) end return( primaryL..secondaryL.._.. VOWEL[F1[vowel]][f2]..epenth..__.. primaryR..secondaryR ) end text=string.gsub(text,"(_)([jGw])( *)/("..V..")(@?)( *)(.)([jGw])", function(a,b,c,d,e,f,g,h) returnsubst(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) returnsubst(a,b,c,d,e,f,g,h,rightFlag) end ) -- Vowels between two non-glides have the most predictable reflexes. text=string_gsub2(text, "([ptkmnNrl])(.)( *)/([aEei])(@? *)([ptkmnNrl])(.)", function( primaryL,secondaryL,_,vowel,infix,primaryR,secondaryR ) returnprimaryL..secondaryL.._.. VOWEL_REFLEX[primaryL][primaryR] [F2[secondaryL]][F2[secondaryR]][F1[vowel]].. infix..primaryR..secondaryR end ) -- Exceptionally for the single word "rej". text=string.gsub(text,"^(rG *)([V7])( *tj)$", function(prefix,vowel,suffix) returnprefix..FRONT_VOWEL[vowel]..suffix end ) -- Vowels always claim the secondary articulation -- of a neighboring back unrounded glide. text=string.gsub(text,"(hG *)/([aEei])",function(prefix,vowel) returnprefix..BACK_VOWEL[vowel] end) text=string.gsub(text,"/([aEei])(@? *hG)",function(vowel,suffix) returnBACK_VOWEL[vowel]..suffix end) -- Unless already claimed, epenthetic vowels after a glide -- always claim the secondary articulation to the left. text=string.gsub(text,"([hH])(.)( *)/([aEei])@", function(primaryL,secondaryL,_,vowel) return( primaryL..secondaryL.._.. VOWEL[F1[vowel]][F2[secondaryL]].."@" ) end ) -- Unless already claimed, vowels before a glide -- always claim the secondary articulation to the right. text=string.gsub(text,"/([aEei])(@?)( *[hHyY])(.)", function(vowel,epenth,primaryR,secondaryR) return( VOWEL[F1[vowel]][F2[secondaryR]]..epenth.. primaryR..secondaryR ) end ) -- For now, unless already claimed, vowels before a rounded consonant -- claim the secondary articulation to the right. text=string.gsub(text,"/([aEei])(@? *.w)",function(vowel,suffix) returnROUND_VOWEL[vowel]..suffix end) -- For now, unless already claimed, remaining vowels -- claim the secondary articulation to the left. text=string.gsub(text,"([jGw])( *)/([aEei])", function(secondaryL,_,vowel) returnsecondaryL.._..VOWEL[F1[vowel]][F2[secondaryL]] end ) -- Change certain vowels in a special environment from round to front. text=string_gsub2(text,"(hj *)([Oou])( *.w *"..V.." *h[jh])", function(prefix,vowel,suffix) returnprefix..FRONT_VOWEL[vowel]..suffix end ) text=string.gsub(text,"(hj *)([Oou])( *)(.w)( *)("..V..")", function(prefix,vowelL,_,conson,__,vowelR) ifconson~="hw"orF1[vowelL]~=F1[vowelR]then returnprefix..FRONT_VOWEL[vowelL].._..conson..__..vowelR end end ) text=string.gsub(text,"(hj *)([Oou])( *.w *.w)", function(prefix,vowel,suffix) returnprefix..FRONT_VOWEL[vowel]..suffix end ) text=string.gsub(text,"(a@? *hj *)Q( *.w *"..V..")","%1a%2") text=string.gsub(text,"(a@? *hj *)Q( *.w *.w)","%1a%2") -- Tag certain glide-vowel-non-glide sequences for special reflexes. text=string.gsub(text,"([HyY][jw] *)("..V.." *[ptkmnNrl])","%1/%2") 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] *h[jw] *)([aAQ] *[ptkmnNrl])","%1/%2") text=string.gsub(text,"([iMu] *hj *)([EeV7] *[kN]G)","%1/%2") text=string.gsub(text, "(hj *[aEei]@? *hw *)("..V.." *[ptkmnNrl])","%1/%2") -- Untag certain sequences, exempting them from special reflexes. text=string.gsub(text,"(hj *)/([aEei] *[knNrl]w)","%1%2") -- Special reflexes. 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) returnprefix..FRONT_VOWEL[vowel]..suffix end ) text=string.gsub(text,"^ *([Hh]w *)M( *tG)","%1u%2") end -- Temporarily cancel epenthetic {i} neighboring {yi'y}. 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. text=string.gsub(text,"(i@ *)Yj","%1hjihj") text=string.gsub(text,"Yj","hjihji@hj") -- For the purposes of this template, -- surface all glides pronounced in isolation. text=string.gsub(text,"^ *h(.) *$","H%1") ifnotdiphthongsthen -- Opportunistically front these vowels. text=string.gsub(text,"(hj *)([A7M])( *[kN]G *[kN]?G? *"..V..")", function(prefix,vowel,suffix) returnprefix..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])", function(vowelL,infix,_,vowelR) ifF1[vowelL]>F1[vowelR]then returnvowelL..infix.."Hw".._..vowelR end end ) text=string.gsub(text,"([AV7MQOou])(@? *)hj( *)([aEei])", function(vowelL,infix,_,vowelR) ifF1[vowelL]>F1[vowelR]then returnvowelL..infix.."Hj".._..vowelR end end ) text=string.gsub(text,"([aEei])(@? *)hj( *)([AV7MQOou])", function(vowelL,infix,_,vowelR) ifF1[vowelL]<F1[vowelR]then returnvowelL..infix.."Hj".._..vowelR end end ) text=string.gsub(text,"("..V..")( *)h([jw]) *$", function(vowel,_,secondary) ifF2[vowel]~=F2[secondary]then returnvowel.._.."H"..secondary end end ) -- Protect word-final epenthetic vowels after non-glides -- from the next operation. text=string.gsub(text,"([ptkmnNrl]."..V..")(@ )","%1/%2") -- De-epenthesize vowels if they still neighbor unsurfaced glides. text=string.gsub(text,"("..V..")@( *h.)","%1%2") text=string.gsub(text,"(h. *"..V..")@","%1") -- Adjust F1 of currently remaining epenthetic vowels. text=string_gsub2(text, "("..V..")( *.[jGw])(.)@( *.[jGw] *)("..V..")", function(vowelL,infixL,vowel,infixR,vowelR) return( vowelL..infixL.. VOWEL[F1[maxF1(vowelL,vowelR,"E")]][F2[vowel]].."/@".. infixR..vowelR ) end ) text=string.gsub(text,"/","") end -- Delete all remaining unsurfaced glides. text=string.gsub(text,"h.","") -- Surface realization for {yi'y}. text=string.gsub(text,"yj","i^") ifnotdiphthongsthen -- Realization for surfaced {y}. text=string_gsub2(text,"("..V.."?)(@?)( *)Hj( *)("..V.."?)", function(vowelL,epenthL,_,__,vowelR) ifvowelL~=""then ifvowelR~=""then ifvowelL==vowelRand F2[vowelL]==F2_FRONT then returnvowelL.._..__..vowelR else return( vowelL..epenthL.._.. maxF1(vowelL,vowelR,"E").."^"..__..vowelR ) end else returnvowelL.._..epenthL..maxF1(vowelL,"E").."^"..__ end else ifvowelR~=""then return_..maxF1(vowelR,"E").."^"..__..vowelR else return_.."i^"..__ end end end ) -- Flatten this epenthetic vowel and surfaced glide. text=string_gsub2(text,"([aAQ] *"..C..")E@( *)E%^( *)a","%1a%2%3a") -- Collapse this epenthetic vowel and surfaced glide into a semi-vowel. text=string.gsub(text,"([aEei])@( *)%1%^","%2%1^") end ifMERGED_VOWELSthen text=string.gsub(text,"[EO]",function(vowel) returnVOWEL[F1[vowel]+1][F2[vowel]] end) end chars=splitChars(text,".") ifnotdiphthongsthen -- Geminate long vowels. localindex=#chars repeat localch=chars[index] localindex2=index-1 ifIS_VOWEL[ch]then localch2=chars[index+1] ifch2~="@"and ch2~="^"and chars[index2]==ch then chars[index]=":" end end index=index2 untilindex==1 text=table.concat(chars,"") end -- Tweak remaining consonants, using offsets as a guide. text=string.gsub(text,"()(.)([jGw])( *)([ptkmnNrl]?)([jGw]?)()", function( offsetL,primaryL,secondaryL,_,primaryR,secondaryR,offsetR ) localisInitial=offsetL==1 localisFinal=offsetR==#chars+1 ifprimaryL=="H"or primaryL=="y" then returnprimaryL..secondaryL.._ end ifprimaryL=="_"then ifnoHintsthen -- Delete pseudo-glide. return_ end ifisInitialthen -- Show secondary articulation to the left, not the right. returnsecondaryL..primaryL.._ end returnprimaryL..secondaryL.._ end localgeminated=primaryL==primaryR ifprimaryL~="t"andprimaryR=="t"then -- /tj/ is palatalized postalveolar. -- /tɣ/ is velarized dental. -- /nj, rj, lj/ 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] ifprimaryR~=""then primaryR=CONSON_REFLEX[primaryR][secondaryR] end end ifprimaryR=="T"then ifprimaryL=="T"then primaryL=finalJ primaryR=initialJ ifprimaryL=="S"and primaryR~="s" then primaryL="T" elseif primaryL=="T"and primaryR=="s"and medialJ=="S" then primaryL="S" end else primaryR=medialJ end elseifprimaryL=="T"then ifisInitialthen primaryL=initialJ elseifisFinalthen primaryL=finalJ else primaryL=medialJ end end ifprimaryR~=""then -- Consonant cluster. -- For some reason, the {t} in {lt} and {ļt} is voiceless. ifnotgeminatedand primaryL~="l"and primaryL~="L" then primaryL=VOICED_PRIMARY[primaryL]orprimaryL primaryR=VOICED_PRIMARY[primaryR]orprimaryR end -- Display secondary articulation only once for the cluster. secondaryL="" elseif notisInitialand notisFinal then -- Medial single consonant. primaryL=VOICED_PRIMARY[primaryL]orprimaryL end ifvoice==falsethen primaryL=VOICELESS_PRIMARY[primaryL]orprimaryL primaryR=VOICELESS_PRIMARY[primaryR]orprimaryR elseifvoice==truethen primaryL=VOICED_PRIMARY[primaryL]orprimaryL primaryR=VOICED_PRIMARY[primaryR]orprimaryR end returnprimaryL..secondaryL.._..primaryR..secondaryR end ) ifnotdiphthongsthen -- Elegantly connect long and epenthetic vowels across word gaps. text=string.gsub(text,"(["..V_..":]): +","%1 : ") text=string.gsub(text,"("..V..") +%1([^%^])","%1 :%2") text=string.gsub(text,"("..V..") +%1$","%1 :") text=string.gsub(text,"("..V..")@ +%1"," %1 :") text=string.gsub(text,"("..V.."@) +"," %1 ") ifW_OFF_GLIDESthen -- Add [w] off-glides after certain consonants. subst=function(primary,_,epenth) ifepenth==""then returnprimary.."Hw".._ end end iffalseandPHONETIC_DETAILSthen text=string.gsub(text,"([pbm])(G *[aEei])(@?)", function(primary,_,epenth) ifepenth==""then returnprimary.."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 ifPARENTHETICAL_EPENTHESISthen ifnotdiphthongsthen text=string.gsub(text,"(.)@("..V..")","%1^%2") end text=string.gsub(text,"(.)@","(%1)") text=string.gsub(text,"%)(=?)%(","%1") ifnotdiphthongsandW_OFF_GLIDESthen iffalseandPHONETIC_DETAILSthen 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," +",falseand"_"or"") text=string.gsub(text,".[jGw@%^]?",PHONETIC_IPA) addUnique(outSeq,text) end localPHONETIC_ARG_J={["t"]="T",["c"]="S",["s"]="s",["x"]="x"} localfunctiontoPhonetic(inSeq,args) -- Recognize "ralik" for Rālik Chain (western dialect). -- Recognize "ratak" for Ratak Chain (eastern dialect). -- For other values, list both possible dialect reflexes where applicable. localdialect=argsandargs.dialectand mw.ustring.lower(mw.text.trim(args.dialect))or"" ifdialect=="rālik"then dialect="ralik" end -- If enabled, display full diphthong allophones for short vowels. localdiphthongs=notnot(argsandparseBoolean(args.diphthongs)) -- Argument "J" has format like "tst". -- Recognized letters are "t" = plosive, "c" = affricate, "s" = fricative. -- Letters for initial, medial and final respectively. -- Real-world pronunciation said to vary by sociological factors, -- but all realizations may occur in free variation. localmodeJ=splitChars(argsandargs.Jandstring.lower(args.J)or"tst") localinitialJ=PHONETIC_ARG_J[modeJ[1]or""]or"t" localmedialJ=PHONETIC_ARG_J[modeJ[2]or""]or"s" localfinalJ=PHONETIC_ARG_J[modeJ[3]or""]orinitialJ -- If enabled, do not display pseudo-glide hints at all. localnoHints=notnot(argsandparseBoolean(args.nohints)) -- "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. localvoice=argsandargs.voiceor"" ifvoice~=""then voice=parseBoolean(voice) end localoutSeq={} localconfig={ ["outSeq"]=outSeq, ["diphthongs"]=diphthongs, ["initialJ"]=initialJ, ["medialJ"]=medialJ, ["finalJ"]=finalJ, ["noHints"]=noHints, ["voice"]=voice } for_,strinpairs(inSeq)do str=string.gsub(str,S," ") str=string.gsub(str,"^ *","") str=string.gsub(str," *$","") localisRalik=dialect=="ralik" ifisRalikordialect=="ratak"then str=toPhoneticDialect(str,config,isRalik) toPhoneticRemainder(str,config) else localralik=toPhoneticDialect(str,config,true) localratak=toPhoneticDialect(str,config,false) -- If both dialect reflexes are the same, display only one of them. toPhoneticRemainder(ralik,config) ifralik~=ratakthen toPhoneticRemainder(ratak,config) end end end returnoutSeq end export._parse=parse export._toBender=toBender export._toMOD=toMOD export._toPhonemic=toPhonemic export._toPhonetic=toPhonetic functionexport.bender(frame) returntable.concat(toBender(parse(frame.args[1],frame.args)),", ") end functionexport.MOD(frame) returntoMOD(frame.args[1]) end functionexport.parse(frame) returntable.concat(parse(frame.args[1]),", ") end functionexport.phonemic(frame) returntable.concat(toPhonemic(parse(frame.args[1])),", ") end functionexport.phonetic(frame) returntable.concat(toPhonetic(parse(frame.args[1]),frame.args),", ") end functionexport.phoneticMED(frame) return"DEPRECATED" end functionexport.phoneticChoi(frame) return"DEPRECATED" end functionexport.phoneticWillson(frame) return"DEPRECATED" end returnexport