Powers {{sa-IPA}}
.
local export = {}
local u = require("Module:string/char")
local gsub = mw.ustring.gsub
local match = mw.ustring.match
local ACUTE = u(0x0301)
local COARTIC = u(0x0361)
local DENTAL = u(0x032A)
local FLAP = u(0x0306)
local NORELEASE = u(0x031A)
local SYLLABIC = u(0x0329)
local NASAL = u(0x0303)
local m_IPA = require("Module:IPA")
local lang = require("Module:languages").getByCode("sa")
local m_a = require("Module:accent qualifier")
local consonants = {
= "k", = "ɡ", = "kʰ", = "ɡʱ", = "ŋ",
= "t͡ɕ", = "d͡ʑ", = "t͡ɕʰ", = "d͡ʑʱ", = "ɲ",
= "t", = "d", = "tʰ", = "dʱ", = "n",
= "ʈ", = "ɖ", = "ʈʰ", = "ɖʱ", = "ɳ",
= "p", = "b", = "pʰ", = "bʱ", = "m",
= "j", = "ɾ", = "l", = "ʋ", = "ɭ̆", -- = "ɭ̆ʱ",
= "ɕ", = "ʂ", = "s", = "ɦ",
}
local diacritics = {
= "ɑː", = "i", = "iː", = "u", = "uː", = "r̩", = "r̩ː",
= "l̩", = "l̩ː", = "ɐj", = "ɑːj", = "ɐw", = "ɑːw", = "",
}
local vowel_list = {
= true, = true, = true, = true, = true, = true, = true, = true,
= true, = true, = true, = true, = true, = true,
}
local stop_list = {
= true, = true, = true, = true,
= true, = true, = true, = true,
= true, = true, = true, = true,
= true, = true, = true, = true,
= true, = true, = true, = true,
}
local consonant_sonority = {
-- voiceless stops and affricates
= 1, = 1,
= 1, = 1,
= 1, = 1,
= 1, = 1,
= 1, = 1,
-- voiceless fricatives
= 2, = 2, = 2, = 2, = 2, = 2,
-- voiced stops and affricates
= 3, = 3,
= 3, = 3,
= 3, = 3,
= 3, = 3,
= 3, = 3,
-- voiced fricatives
= 4,
-- nasals
= 5, = 5, = 5, = 5, = 5, = 5, = 5,
-- flaps
= 6,
-- laterals
= 7, = 7, = 7,
-- glides
= 8, = 8,
}
local tt = {
-- vowels
= "ɐ", = "ɑː", = "i", = "iː", = "u", = "uː", = "r̩", = "r̩ː",
= "l̩", = "l̩ː", = "ɐj", = "ɑːj", = "ɐw", = "ɑːw",
-- visarga
= "h",
-- chandrabindu
= "m̐",
-- anusvara
= "ṃ",
-- avagraha
= "",
--Vedic extensions
= "x", = "ɸ",
}
local accent_vowel = {
= "ɐ́", = "ɑ́ː", = "í", = "íː", = "ú", = "úː", = "ŕ̩", = "ŕ̩ː",
= "ĺ̩", = "ĺ̩ː", = "ɐ́j", = "ɑ́ːj", = "ɐ́w", = "ɑ́ːw",
}
local function shift_to_codas(syllables)
-- shift codas to previous syllable using the Weerasinghe-Wasala-Gamage method
local to_move = 0
for i, syll in ipairs(syllables) do
if i == 1 then
-- no need to shift to coda if in the first syllable
elseif #syll < 3 then
-- coda movement only needed for onset clusters of 2 or more
elseif #syll == 3 then
-- V.CCV => VC.CV
to_move = 1
elseif #syll == 4 then
if syll == "ɾ" or syll == "j" or (stop_list] and stop_list]) then
-- V.CCrV or V.CCyV => VC.CrV or VC.CyV
-- if the first two consonants are stops, VC.CCV
to_move = 1
else
-- V.CCCV => VCC.CV
to_move = 2
end
else
-- 4 consonants or more
if syll == "ɾ" or syll == "j" then
to_move = #syll - 3
else
-- find index of consonant of least sonority
to_move = #syll - 1
local min_son = consonant_sonority]
for i = (#syll - 1), 1, -1 do
if consonant_sonority] < min_son then
to_move = i
min_son = consonant_sonority]
end
end
end
end
while to_move > 0 do
table.insert(syllables, table.remove(syllables, 1))
to_move = to_move - 1
end
end
return syllables
end
local function syllabify(remainder, accent)
local syllables = {}
local syll = {}
while #remainder > 0 do
local phoneme = table.remove(remainder, 1)
if vowel_list then
table.insert(syll, phoneme)
table.insert(syllables, syll)
syll = {}
else
table.insert(syll, phoneme)
end
end
-- store whatever consonants remain
local final_cons = syll
-- Vedic pitch accent
if accent ~= nil and accent <= #syllables then
syll = syllables
syllables = accent_vowel]
end
syllables = shift_to_codas(syllables)
-- it is not necessary to include 'l' in the pattern for short vowels as it doesn't occur as a vowel in syllable coda and as consonantal 'l' would then be erroneously included
local short_vowel_patt = "^" .. SYLLABIC .. "?" .. ACUTE .. "?$"
-- Classic stress accent
local num_sylls = #syllables
if num_sylls == 2 then
table.insert(syllables, 1, 'ˈ')
elseif num_sylls == 3 then
-- if the final segment of the second syllable is not a short vowel, stress the second syllable
if match(syllables], short_vowel_patt) == nil then
table.insert(syllables, 1, 'ˈ')
-- else stress the third
else
table.insert(syllables, 1, 'ˈ')
end
elseif num_sylls >= 4 then
if match(syllables], short_vowel_patt) == nil then
table.insert(syllables, 1, 'ˈ')
elseif match(syllables], short_vowel_patt) == nil then
table.insert(syllables, 1, 'ˈ')
else
table.insert(syllables, 1, 'ˈ')
end
end
-- If there are phonemes left, then the word ends in a consonant
-- Add them to the last syllable
for _, phoneme in ipairs(final_cons) do
table.insert(syllables, phoneme)
end
for i, _ in ipairs(syllables) do
syllables = table.concat(syllables, "")
end
return table.concat(syllables, ".")
end
local anu_to_nasals = {
= "ŋ", = "ŋ",
= "ɲ", = "ɲ",
= "n", = "n",
= "ɳ", = "ɳ",
= "m", = "m",
}
local function anusvara(text)
text = gsub(text, "ṃ$", "m")
text = gsub(
text,
"ṃ(?)()(?)(?)",
function(div, cons, mark, fric)
return anu_to_nasals .. div .. cons .. mark .. fric
end
)
text = gsub(
text,
"()(" .. SYLLABIC .. "?)(" .. ACUTE .. "?)(ː?)(?)ṃ",
"%1%2" .. NASAL .. "%3%4%5"
)
text = gsub(text, "ṃ", "ɴ")
return text
end
local function convert_word(word, accent)
local chars = {}
local t = {}
gsub(word, ".", function(c) table.insert(chars, c) end)
for i, c in ipairs(chars) do
if consonants then
table.insert(t, consonants)
if not diacritics] then
table.insert(t, "ɐ")
end
elseif c == "्" then
-- do nothing
elseif diacritics then
table.insert(t, diacritics)
elseif tt then
table.insert(t, tt)
end
end
word = syllabify(t, accent)
word = gsub(word, "%.ˈ", "ˈ")
-- correction for ळ्ह = ɭ̆ʱ
word = gsub(word, "ɭ̆(?)ɦ", "%1ɭ̆ʱ")
-- chandrabindu
word = gsub(
word,
"()(" .. SYLLABIC .. "?)(" .. ACUTE .. "?)(ː?)(?)m̐",
"%1%2" .. NASAL .. "%3%4%5"
)
return word
end
local function convert_words(words, accents)
local result = {}
local word_num = 1
for word in mw.text.gsplit(words, " ") do
table.insert(result, convert_word(word, accents))
word_num = word_num + 1
end
text = table.concat(result, " ")
return text
end
local function phon_procs(text)
-- Anusvāra
text = anusvara(text)
return text
end
local function abhinidhana_phonemic(text)
--de-aspirate and de-affricate before stops
text = gsub(
text,
"()(" .. DENTAL .. "?)?(?)()",
"%1%2%3%4"
)
text = gsub(
text,
"()" .. COARTIC .. "?(?)()",
"%1%2%3"
)
return text
end
local function abhinidhana_phonetic(text)
text = gsub(
text,
"()(" .. DENTAL .. "?)(?)()",
"%1%2" .. NORELEASE .. "%3%4"
)
return text
end
local superscript = {
= "ɐ̆",
= "ɑ̆",
= "ĕ",
= "ŏ",
= "ĭ",
= "ŭ",
}
local function make_dialects(text)
local dialects = {}
text = abhinidhana_phonemic(text)
-- Rigvedic Sanskrit
local rig_phnm = text
rig_phnm = gsub(rig_phnm, "^ˈ", "")
rig_phnm = gsub(rig_phnm, "ˈ", ".")
rig_phnm = gsub(rig_phnm, " %.", " ")
local rig_phnt = abhinidhana_phonetic(rig_phnm)
-- visarga alternation
rig_phnt = gsub(rig_phnt, "h(?)()", "ɸ%1%2")
rig_phnt = gsub(rig_phnt, "h(?)()", "x%1%2")
-- nasalized semivowels
rig_phnt = gsub(
rig_phnt,
"()(" .. DENTAL .. "?)(?)()(?)(ʱ?)",
"%4%5" .. NASAL .. "%3%4%5%6"
)
dialects = {
label = "Vedic",
phonemic = rig_phnm,
phonetic = rig_phnt,
}
-- Classical Sanskrit
local cla_phnm = text
cla_phnm = gsub(cla_phnm, "", { = "e", = "o", = "i", = "u", = "", = "r"})
cla_phnm = gsub(cla_phnm, "ɐ(" .. NASAL .. "?)j", "e%1ː")
cla_phnm = gsub(cla_phnm, "ɐ(" .. NASAL .. "?)w", "o%1ː")
cla_phnm = gsub(cla_phnm, "ɑ(" .. NASAL .. "?)ː()", "ɑ%1%2")
-- Add dental diacritic to t, d, tʰ, dʱ, n, l, s.
cla_phnm = gsub(
cla_phnm,
"(" .. COARTIC .. "??)(?)",
function(base_consonant, aspiration)
if base_consonant == "t" or base_consonant == "d" then
return base_consonant .. DENTAL .. aspiration
end
end
)
cla_phnm = gsub(cla_phnm, NASAL .. "?()", "%1" .. DENTAL)
local cla_phnt = abhinidhana_phonetic(cla_phnm)
-- cla_pron = gsub(cla_pron, "r̩(" .. NASAL .. "?)(" .. ACUTE .. "?)(ː?)", "ɾi%1%2%3")
-- cla_pron = gsub(cla_pron, "l̩(" .. NASAL .. "?)(" .. ACUTE .. "?)(ː?)", "l̪i%1%2%3")
cla_phnt = gsub(
cla_phnt,
"()(" .. NASAL .. "?)(ː?)(?)h$",
function (vow, nas, length, glide)
return vow .. nas .. length .. glide .. "h" .. superscript
end
)
cla_phnt = gsub(
cla_phnt,
"()(" .. NASAL .. "?)(ː?)(?)h ",
function (vow, nas, length, glide)
return vow .. nas .. length .. glide .. "h" .. superscript .. " "
end
)
dialects = {
label = "Classical Sanskrit",
phonemic = cla_phnm,
phonetic = cla_phnt,
}
return dialects
end
local function make_table(dialects, novedic, noclassical)
local dial_types = {'rig', 'cla'}
if novedic then
table.remove(dial_types, 1)
end
if noclassical then
table.remove(dial_types, 2)
end
if #dial_types == 1 then
local dial = dial_types
local IPA_args = {{pron = '/' .. dialects.phonemic .. '/'}}
if dialects.phonemic ~= dialects.phonetic then
table.insert(IPA_args, {pron = '.phonetic .. ']'})
end
return table.concat{
'\n* ',
m_a.format_qualifiers(lang, {dialects.label}),
' ',
m_IPA.format_IPA_full { lang = lang, items = IPA_args },
}
else
local inline_args = {{pron = '/' .. dialects.cla.phonemic .. '/'}}
if dialects.cla.phonemic ~= dialects.cla.phonetic then
table.insert(inline_args, {pron = ''})
end
local full = {}
for _, dial in ipairs(dial_types) do
local full_args = {{pron = '/' .. dialects.phonemic .. '/'}}
if dialects.phonemic ~= dialects.phonetic then
table.insert(full_args, {pron = '.phonetic ..']'})
end
table.insert(full, table.concat{
'\n* ',
m_a.format_qualifiers(lang, {dialects.label}),
' ',
m_IPA.format_IPA_full { lang = lang, items = full_args },
})
end
return table.concat(full, "")
end
end
function export.show(frame)
local params = {
= {alias_of = 'w'},
w = {default = mw.title.getCurrentTitle().text},
a = {list = true, allow_holes = true, type = 'number'},
novedic = {type = 'boolean'},
noclassical = {type = 'boolean'}
}
local args = require("Module:parameters").process(frame:getParent().args, params)
local text = convert_words(args.w, args.a)
text = phon_procs(text)
local dialects = make_dialects(text)
return make_table(dialects, args.novedic, args.noclassical)
end
return export