Módulo:generar-pron/ca
Apariencia
La documentación para este módulo puede ser creada en Módulo:generar-pron/ca/doc
local export = {}
local insert = table.insert
local concat = table.concat
local m_table = require("Módulo:tabla")
local listToSet = m_table.listToSet
local function concat_keys(tab)
local res = {}
for k, _ in pairs(tab) do
insert(res, k)
end
return concat(res)
end
local function concat_vals(tab)
local res = {}
for _, v in pairs(tab) do
insert(res, v)
end
return concat(res)
end
local m_str = require("Módulo:String")
local substr = m_str.sub
local strfind = m_str.find
local strmatch = m_str.match
local strsplit = m_str.split
local strsubn = m_str.gsub
local strsubb = m_str.gsubb
local strsubrep = m_str.gsub_rep
local strlower = m_str.lower
local strstrip = m_str.strip
local strmatchit = m_str.gmatch
local u = m_str.char
local strexplode = m_str.explode_utf8
local strnfc = m_str.toNFC
local strhtml = m_str.encode_html
-- Version of strsubn() that discards all but the first return value.
local function strsub(term, foo, bar)
local retval = strsubn(term, foo, bar)
return retval
end
local PUNTUACION = "[%(%)%[%]%{%}¡!¿?.,;:–—]"
local PUNTUACION_EXTRA = "[%(%)%[%]%{%}¡!¿?.,;:–—\"“”„‟‘’«»»«‹››‹]"
local BALEARICO = "bal"
local CENTRAL = "cen"
local VALENCIANO = "val"
local written_unaccented_vowel_l = "aeiouyAEIOUY"
local written_stressed_vowel_l = "àèéêëíòóôúýÀÈÉÊËÍÒÓÔÚÝ"
local written_accented_not_stressed_vowel_l = "ïüÏÜ"
local written_accented_vowel_l = written_stressed_vowel_l .. written_accented_not_stressed_vowel_l
local ipa_vowel_l = "ɔɛə"
local written_vowel_l = written_unaccented_vowel_l .. written_accented_vowel_l
local vowel_l = written_vowel_l .. ipa_vowel_l
local V = "[" .. vowel_l .. "]"
local written_accented_to_plain_vowel = {
["à"] = "a",
["è"] = "e",
["é"] = "e",
["ê"] = "e",
["ë"] = "e",
["í"] = "i",
["ï"] = "i",
["ò"] = "o",
["ó"] = "o",
["ô"] = "o",
["ú"] = "u",
["ü"] = "u",
["ý"] = "y",
["À"] = "A",
["È"] = "E",
["É"] = "E",
["Ê"] = "E",
["Ë"] = "E",
["Í"] = "I",
["Ï"] = "I",
["Ò"] = "O",
["Ó"] = "O",
["Ô"] = "O",
["Ú"] = "U",
["Ü"] = "U",
["Ý"] = "Y",
}
local AC = u(0x0301) -- acute = ́
local GR = u(0x0300) -- grave = ̀
local CFLEX = u(0x0302) -- circumflex = ̂
local DOTOVER = u(0x0307) -- dot over = ̇
local DIA = u(0x0308) -- diaeresis = ̈
local LINEUNDER = u(0x0331) -- lineunder = ̱
local stress_l = AC .. GR
local stress_c = "[" .. stress_l .. "]"
local ipa_stress_l = "ˈˌ"
local ipa_stress_c = "[" .. ipa_stress_l .. "]"
local sylsep_l = "%-."..ipa_stress_l -- hyphen included for syllabifying from spelling; FIXME: formerly included SYLDIV
local sylsep_c = "[" .. sylsep_l .. "]"
local tie_l = "‿'"
local tie_c = "[" .. tie_l .. "]"
local charsep_l = sylsep_l .. tie_l .. stress_l .. ipa_stress_l
local charsep_c = "[" .. charsep_l .. "]"
local wordsep_l = "# "
local wordsep_c = "[" .. wordsep_l .. "]"
local separator_l = charsep_l .. wordsep_l
local separator_c = "[" .. separator_l .. "]"
local neg_guts_of_cons = vowel_l .. separator_l
local C = "[^" .. neg_guts_of_cons .. "]" -- consonant class including h
export.mid_vowel_hints = "éèêëóòô"
export.mid_vowel_hint_c = "[" .. export.mid_vowel_hints .. "]"
local TEMP_PAREN_R = u(0xFFF1)
local TEMP_PAREN_RR = u(0xFFF2)
-- Pseudo-consonant at the edge of prefixes ending in a vowel and suffixes beginning with a vowel; FIXME: not currently
-- used.
local PSEUDOCONS = u(0xFFF3)
-- local PREFIX_MARKER = u(0xFFF4) -- marker indicating a prefix so we can convert primary to secondary accents
local valid_onsets = listToSet {
"b", "bl", "br",
"c", "cl", "cr",
"ç",
"d", "dj", "dr",
"f", "fl", "fr",
"g", "gl", "gr", "gu", "gü",
"h",
"i",
"j",
"k", "kl", "kr",
"l", "ll",
"m",
"n", "ny", "ñ",
"p", "pl", "pr",
"qu", "qü",
"r", "rr",
"s", "ss",
"t", "tg", "tj", "tr", "tx", "tz",
"u",
"v", "vl", "vr",
"w",
"x",
"ʃ", -- e.g. 'χruʃóf' respelling of [[Khrusxov]]
"χ", -- in case of respelling
"y",
"z",
}
local decompose_dotover = {
-- No composed i, u or U with DOTOVER.
["ȧ"] = "a" .. DOTOVER,
["ė"] = "e" .. DOTOVER,
["ȯ"] = "o" .. DOTOVER,
["ẏ"] = "y" .. DOTOVER,
["Ȧ"] = "A" .. DOTOVER,
["Ė"] = "E" .. DOTOVER,
["İ"] = "I" .. DOTOVER,
["Ȯ"] = "O" .. DOTOVER,
["Ẏ"] = "Y" .. DOTOVER,
}
local dotover_keys = concat_keys(decompose_dotover)
local unstressed_words = listToSet {
-- proclitic object pronouns
"em", "et", "es", "el", "la", "els", "les", "li", "ens", "us", "ho", "hi", "en",
-- enclitic object pronouns usually attach with hyphen to preceding verb but not always, cf. [[tant me fa]]
"me", "te", "se", "lo", "los", "nos", "vos", "ne",
-- contracted object pronouns and articles attached with apostrophe so no need to include
-- unstressed possessives
"mon", "ma", "mos", "mes", "ton", "ta", "tos", "tes", "son", "sa", "sos", "ses",
-- prepositions
"a", "de", "per", "amb", "ab", -- 'en' already included as proclitic object pronouns
-- prepositional contractions
"al", "als", "del", "dels", "pel", "pels",
-- articles 'el', 'la', 'els', 'les' already included as proclitic pronouns
-- personal articles
"na", -- 'en' already included above
-- indefinite articles
"un", "uns",
-- salat articles
"ets", "so", -- 'es' already included as proclitic object pronouns and 'ses', 'sa', 'sos' as possessives
-- conjunctions
"i", "o", "si", "ni", "que",
}
local pron_abc = {
["A"] = {"a"},
["a"] = {"a"},
["B"] = {"be", "be alta"},
["b"] = {"be", "be alta"},
["C"] = {"ce"},
["c"] = {"ce"},
["D"] = {"de"},
["d"] = {"de"},
["E"] = {"e"},
["e"] = {"e"},
["F"] = {"efa", "efe", "ef"},
["f"] = {"efa", "efe", "ef"},
["G"] = {"ge"},
["g"] = {"ge"},
["H"] = {"hac"},
["h"] = {"hac"},
["I"] = {"i"},
["i"] = {"i"},
["J"] = {"jota"},
["j"] = {"jota"},
["K"] = {"ca", "ka"},
["k"] = {"ca", "ka"},
["L"] = {"ela", "ele", "el"},
["l"] = {"ela", "ele", "el"},
["M"] = {"ema", "eme", "em"},
["m"] = {"ema", "eme", "em"},
["N"] = {"ena", "ene", "en"},
["n"] = {"ena", "ene", "en"},
["O"] = {"o"},
["o"] = {"o"},
["P"] = {"pe"},
["p"] = {"pe"},
["Q"] = {"cu"},
["q"] = {"cu"},
["R"] = {"erra", "erre", "er"},
["r"] = {"erra", "erre", "er"},
["S"] = {"essa", "esse", "es"},
["s"] = {"essa", "esse", "es"},
["T"] = {"te"},
["t"] = {"te"},
["U"] = {"u"},
["u"] = {"u"},
["V"] = {"ve"},
["v"] = {"ve"},
["W"] = {"doble ve", "ve doble"},
["w"] = {"doble ve", "ve doble"},
["X"] = {"ics", "xeix"},
["x"] = {"ics", "xeix"},
["Y"] = {"i grega"},
["y"] = {"i grega"},
["Z"] = {"zeta"},
["z"] = {"zeta"},
["Ç"] = {"ce trencada"},
["ç"] = {"ce trencada"},
["L·L"] = {"ela geminada", "ele geminada", "el geminada"},
["l·l"] = {"ela geminada", "ele geminada", "el geminada"},
}
local SUST = 1
local ADJ = 2
local VERB = 3
local ADV = 4
local normalizar_cg = {
["s"] = SUST,
["sust"] = SUST,
["sustantivo"] = SUST,
["adj"] = ADJ,
["adjetivo"] = ADJ,
["v"] = VERB,
["verb"] = VERB,
["verbo"] = VERB,
["adv"] = ADV,
["adverbio"] = ADV,
}
local function descomponer(text)
return strsub(text, "[" .. dotover_keys .. "]", decompose_dotover)
end
local function normalizar(texto)
texto = strlower(texto)
texto = descomponer(texto)
texto = strsubrep(texto, PUNTUACION, " | ") -- convierto lo que delimite fragmentos a los IPA foot boundaries |
texto = strsubrep(texto, PUNTUACION_EXTRA, "") -- elimino la puntuación restante que haya quedado
texto = strsubrep(texto, "[%-‐]", " ") --los guiones pasan a ser espacios (austro-húngaro, franco-italiano)
texto = strsubrep(texto, "%s*|%s*|%s*", " | ") --finalmente, elimino las barras y espacios de más
texto = strsubrep(texto, "%s+", " ")
texto = strstrip(texto, "[%s|]+")
return texto
end
local function handle_unstressed_words(words_)
local words = m_table.deepcopy(words_)
-- Check if the word at index `i` in `words` is "amb" and the following word begins with a vowel.
local function is_amb_to_join(words, i)
return i < #words and words[i] == "a" .. DOTOVER .. "mb" and strfind(words[i + 1], "^h?" .. V)
end
local saw_amb_to_join = true
-- Mark all unstressed words with DOTOVER, so that split_syllables() doesn't assign stress. We need to do this
-- before special handling for [[amb]], because [[amb]] may join to another unstressed word like [[el]], in the
-- process losing the identity of the two words. In the process, see if [[amb]] occurs before a following
-- vowel-initial word (which may begin with h-).
for i, word in ipairs(words) do
-- Put DOTOVER after the last vowel (to handle the case of [[que]]). It doesn't actually matter where we put
-- it, because split_syllables() just looks for DOTOVER anywhere in the word.
if unstressed_words[word] then
word = strsub(word, "^(.*" .. V .. ")", "%1" .. DOTOVER)
end
if is_amb_to_join(words, i) then
saw_amb_to_join = true
end
end
-- Join [[amb]] before vowel-initial word with following word.
if saw_amb_to_join then
local new_words = {}
local i = 1
while i <= #words do
if is_amb_to_join(words, i) then
insert(new_words, words[i] .. "‿" .. words[i + 1])
i = i + 2
else
insert(new_words, words[i])
i = i + 1
end
end
words = new_words
end
-- Finally, rewrite some unstressed words to get the right pronunciation. Any remaining [[amb]] not before a
-- vowel-initial word is pronounced [am] even in Valencian (where [amp]/[amb] would be expected), and [[per]] always
-- has a pronounced <r>.
local unstressed_word_replacement = {
["a" .. DOTOVER .. "mb"] = "a" .. DOTOVER .. "m",
["pe" .. DOTOVER .. "r"] = "pe" .. DOTOVER .. "rr",
}
for i, word in ipairs(words) do
word = unstressed_word_replacement[word] or word
end
return words
end
local function fix_prefixes(word)
-- Voiced s in prefix roots -fons-, -dins-, -trans-
word = strsub(word, "^enfons([aàeèéiíoòóuú])", "enfonz%1")
word = strsub(word, "^endins([aàeèéiíoòóuú])", "endinz%1")
word = strsub(word, "tr([aà])ns([aàeèéiíoòóuúbdghlmv])", "tr%1nz%2")
-- in + ex > ineks/inegz
word = strsub(word, "^inex", "in.ex")
return word
end
local function restore_diaereses(word)
-- Some structural forms do not have diaeresis per diacritic savings, let's restore it to identify hiatus
word = strsub(word, "([iu])um(s?)$", "%1üm%2") -- Latinisms (-ius is ambiguous but rare)
word = strsub(word, "([aeiou])isme(s?)$", "%1ísme%2") -- suffix -isme
word = strsub(word, "([aeiou])ist([ae]s?)$", "%1íst%2") -- suffix -ista
word = strsub(word, "([aeou])ir$", "%1ír") -- verbs -ir
word = strsub(word, "([aeou])int$", "%1ínt") -- present participle
word = strsub(word, "([aeo])ir([éà])$", "%1ïr%2") -- future
word = strsub(word, "([^gq]u)ir([éà])$", "%1ïr%2")
word = strsub(word, "([aeo])iràs$", "%1ïràs")
word = strsub(word, "([^gq]u)iràs$", "%1ïràs")
word = strsub(word, "([aeo])ir(e[mu])$", "%1ïr%2")
word = strsub(word, "([^gq]u)ir(e[mu])$", "%1ïr%2")
word = strsub(word, "([aeo])iran$", "%1ïran")
word = strsub(word, "([^gq]u)iran$", "%1ïran")
word = strsub(word, "([aeo])iria$", "%1ïria") -- conditional
word = strsub(word, "([^gq]u)iria$", "%1ïria")
word = strsub(word, "([aeo])ir(ie[sn])$", "%1ïr%2")
word = strsub(word, "([^gq]u)ir(ie[sn])$", "%1ïr%2")
return word
end
local function fix_y(word)
-- y > vowel i else consonant /j/, except ny
word = strsub(word, "ny", "ñ")
word = strsub(word, "y([^aeiouàèéêëíòóôúïü])", "i%1") -- vowel if not next to another vowel
word = strsub(word, "([^aeiouàèéêëíòóôúïü·%-%.])y", "%1i") -- excluding also syllables separators
return word
end
local function mid_vowel_fixes(word)
local function track_mid_vowel(vowel, cont)
require("Módulo:traza")("ca-vocales")
return true
end
local changed
-- final -el (not -ell) usually è but not too many cases
word, changed = strsubb(word, "e(nts?)$", "é%1")
if changed then
track_mid_vowel("e", "nt-nts")
end
word, changed = strsubb(word, "e(rs?)$", "é%1")
if changed then
track_mid_vowel("e", "r-rs")
end
word, changed = strsubb(word, "o(rs?)$", "ó%1")
if changed then
track_mid_vowel("o", "r-rs")
end
word, changed = strsubb(word, "è(s?)$", "ê%1")
if changed then
track_mid_vowel("è", "s-blank")
end
word, changed = strsubb(word, "e(s[oe]s)$", "ê%1")
if changed then
track_mid_vowel("e", "sos-sa-ses")
end
word, changed = strsubb(word, "e(sa)$", "ê%1")
if changed then
track_mid_vowel("e", "sos-sa-ses")
end
return word
end
local function word_fixes(word, dialect)
word = strsub(word, "%(rr%)", TEMP_PAREN_RR)
word = strsub(word, "%(r%)", TEMP_PAREN_R)
word = strsub(word, "%-([rs]?)", "-%1%1")
if dialect == VALENCIANO then
word = strsub(word, "%-x", "-tx")
end
word = strsub(word, "rç$", "rrs") -- silent r only in plurals -rs
word = fix_prefixes(word) -- internal pause after a prefix
word = restore_diaereses(word) -- no diaeresis saving
word = fix_y(word) -- ny > ñ else y > i vowel or consonant
word = mid_vowel_fixes(word)
-- all words in pn- (e.g. [[pneumotòrax]] and mn- (e.g. [[mnemònic]]) have silent p/m in both Central and Valencian
word = strsub(word, "^[pm]n", "n")
-- Respell ch + vowel as tx, before we remove other h's after consonants.
word = strsub(word, "ch(" .. V ..")", "tx%1")
-- Delete h after a consonant. This must happen here, before split_syllables(). We don't delete h after a vowel
-- yet because it indicates a hiatus.
word = strsub(word, "(" .. C .. ")h", "%1")
return word
end
local function split_vowels(vowels, saw_dotover, saw_lineunder)
local syllables = {{onset = "", vowel = substr(vowels, 1, 1), coda = "", separator = "", has_dotover = saw_dotover,
has_lineunder = saw_lineunder}}
vowels = substr(vowels, 2)
while vowels ~= "" do
local syll = {onset = "", vowel = "", coda = ""}
syll.onset, syll.vowel, vowels = strmatch(vowels, "^([iu]?)(.)(.-)$")
insert(syllables, syll)
end
local count = #syllables
if count >= 2 and (syllables[count].vowel == "i" or syllables[count].vowel == "u") then
syllables[count - 1].coda = syllables[count].vowel
syllables[count] = nil
end
return syllables
end
-- Split the word into syllables. Return a list of syllable objects, each of which contains fields `onset`, `vowel`,
-- `coda`, `separator` (a user-specified syllable divider that goes before the syllable; one of '·', '-' or '.') and
-- `stressed` (a boolean indicating that the syllable is stressed). In addition, the list has fields `stress` (the
-- index of the syllable with primary stress) and `is_prefix` (true if the word is a prefix, i.e. it ends in '-').
-- Normally, prefixes are treated as unstressed if a stressed syllable isn't explicitly marked, but this can be
-- overridden with `stress_prefixes`, which causes the automatic stress-assignment algorithm to run for these terms.
local function split_syllables(word, stress_prefixes, may_be_uppercase)
local syllables = {}
local saw_dotover = false
local remainder = word
local is_prefix = false
if remainder:find("%-$") then -- prefix
is_prefix = true
remainder = remainder:gsub("%-$", "")
end
local is_suffix = false
if remainder:find("^%-") then -- suffix
is_suffix = true
remainder = remainder:gsub("^%-", "")
end
while remainder ~= "" do
local consonants, vowels
-- FIXME: Using C and V below instead of the existing patterns slows things down TREMENDOUSLY.
-- Not sure why.
local vowel_list = may_be_uppercase and "aeiouàèéêëíòóôúïüAEIOUÀÈÉÊËÍÒÓÔÚÏÜ" .. DOTOVER .. LINEUNDER or
"aeiouàèéêëíòóôúïü" .. DOTOVER .. LINEUNDER
consonants, remainder = strmatch(remainder, "^([^" .. vowel_list .. "]*)(.-)$")
vowels, remainder = strmatch(remainder, "^([" .. vowel_list .. "]*)(.-)$")
local this_saw_dotover = not not strfind(vowels, DOTOVER)
if this_saw_dotover then
saw_dotover = true
vowels = vowels:gsub(DOTOVER, "")
end
local this_saw_lineunder = not not strfind(vowels, LINEUNDER)
if this_saw_lineunder then
vowels = vowels:gsub(LINEUNDER, "")
end
if vowels == "" then
if #syllables > 0 then
syllables[#syllables].coda = syllables[#syllables].coda .. consonants
else
-- word without vowels, e.g. foot boundary |
insert(syllables, {onset = consonants, vowel = "", coda = "", separator = ""})
end
else
local onset = consonants
local first_vowel = substr(vowels, 1, 1)
if (strfind(onset, "[gqGQ]$") and (first_vowel == "ü" or (first_vowel == "u" and vowels ~= "u")))
or ((onset == "" or onset == "h" or onset == "H") and #syllables == 0 and
(first_vowel == "i" or first_vowel == "I") and (vowels ~= "i" and vowels ~= "I"))
then
onset = onset .. substr(vowels, 1, 1)
vowels = substr(vowels, 2)
end
local vsyllables = split_vowels(vowels, this_saw_dotover, this_saw_lineunder)
vsyllables[1].onset = onset .. vsyllables[1].onset
for _, s in ipairs(vsyllables) do
insert(syllables, s)
end
end
end
-- Shift over consonants from the onset to the preceding coda, until the syllable onset is valid
for i = 2, #syllables do
local current = syllables[i]
local previous = syllables[i-1]
while not (current.onset == "" or valid_onsets[strsub(strsub(current.onset, tie_c .. "[hH]?$", ""), "_", "")]) do
local letter = substr(current.onset, 1, 1)
current.onset = substr(current.onset, 2)
if strfind(letter, "[·%-%.]") then -- syllable separators
current.separator = letter
break
else
previous.coda = previous.coda .. letter
if strfind(letter, tie_c) then
break
end
end
end
end
-- Detect stress
for i, syll in ipairs(syllables) do
if strfind(syll.vowel, "^[" .. written_stressed_vowel_l .. "]$") then
syll.stressed = true
-- primary stress: the last one stressed without LINEUNDER
if not syll.has_lineunder then
syllables.stress = i
end
end
end
-- Assign default stress
if not syllables.stress and not saw_dotover and (stress_prefixes or not is_prefix) then
local count = #syllables
if count == 1 then
if syllables[1].vowel ~= "" then -- vowel-less words don't get stress
syllables.stress = 1
end
else
local final = syllables[count]
-- Take account of tie symbols (apostrophes and ‿).
if strfind(final.coda, "^[s" .. tie_l .. "]*$") or (strfind(final.coda, "^" .. tie_c .. "*n" .. tie_c .. "*$") and (
final.vowel == "e" or final.vowel == "i" or final.vowel == "ï")) then
syllables.stress = count - 1
else
syllables.stress = count
end
end
if syllables.stress then
syllables[syllables.stress].stressed = true
end
end
syllables.is_prefix = is_prefix
syllables.is_suffix = is_suffix
return syllables
end
local IPA_vowels_central = {
["ê"] = "ɛ", ["ë"] = "ɛ", ["ô"] = "ɔ",
}
local IPA_vowels_balearic = {
["ê"] = "ə", ["ë"] = "ɛ", ["ô"] = "ɔ",
}
local IPA_vowels_valencian = {
["ê"] = "e", ["ë"] = "e", ["ô"] = "o",
}
local IPA_vowels = {
["à"] = "a",
["è"] = "ɛ", ["ê"] = "ɛ", ["ë"] = "ɛ", ["é"] = "e",
["í"] = "i", ["ï"] = "i",
["ò"] = "ɔ", ["ô"] = "ɔ", ["ó"] = "o",
["ú"] = "u", ["ü"] = "u",
}
local IPA_VOWEL_CLUSTER = "[" .. concat_vals(IPA_vowels) .. "]"
local function replace_context_free(cons)
cons = strsub(cons, "ŀ", "l")
cons = strsub(cons, "r", "ɾ")
cons = strsub(cons, "ɾɾ", "r")
cons = strsub(cons, "ss", "s")
cons = strsub(cons, "ll", "ʎ")
cons = strsub(cons, "ñ", "ɲ") -- hint ny > ñ
-- NOTE: We use single-character affricate symbols during processing for ease in handling, and convert them
-- to tied multi-character affricates at the end of join_syllables().
cons = strsub(cons, "[dt]j", "ʤ")
cons = strsub(cons, "tx", "ʧ")
cons = strsub(cons, "[dt]z", "ʣ")
cons = strsub(cons, "ç", "s")
cons = strsub(cons, "[cq]", "k")
cons = strsub(cons, "h", "")
cons = strsub(cons, "j", "ʒ")
-- Don't replace x -> ʃ yet so we can distinguish x from manually specified ʃ.
cons = strsub(cons, "i", "j") -- must be after j > ʒ
cons = strsub(cons, "y", "j") -- must be after j > ʒ and fix_y
cons = strsub(cons, "[uü]", "w")
cons = strsub(cons, "'", "‿")
return cons
end
-- Do context-sensitive phonological changes. Formerly this was all done syllable-by-syllable but that made the code
-- tricky (since it often had to look at adjacent syllables) and full of subtle bugs. Now we first concatenate the
-- syllables back to words and the words to the combined text and work on the text as a whole. FIXME: We should move
-- more of the work done in preprocess_word(), e.g. most of replace_context_free(), here.
local function postprocess_general(text, dialect)
local voiced = listToSet({"b", "d", "g", "m", "n", "ɲ", "l", "ʎ", "r", "ɾ", "v", "z", "ʒ", "ʣ", "ʤ"})
--local voiced_keys = concat_keys(voiced)
local voiceless = listToSet({"p", "t", "k", "f", "s", "ʃ", "ʦ", "ʧ"})
--local voiceless_keys = concat_keys(voiceless)
local voicing = {["p"] = "b", ["t"] = "d", ["k"] = "g", ["f"] = "v", ["s"] = "z", ["ʃ"] = "ʒ", ["ʦ"] = "ʤ",
["ʧ"] = "ʤ"}
--local voicing_keys = concat_keys(voicing)
local devoicing = {}
for k, v in pairs(voicing) do
devoicing[v] = k
end
--local devoicing_keys = concat_keys(devoicing)
------------------ Handle <x>
-- Handle ex- + vowel > -egz-. We handle -x- on either side of the syllable boundary. Note that this also handles
-- inex- + vowel because in fix_prefixes we respell inex- as in.ex-, which ends up at this stage as in.e.xV.
text = strsubrep(text, "([.#][eɛ]" .. stress_c .. "*)(" .. charsep_c .. "*)x(" .. charsep_c .. "*" .. V ..
")", function(e, pre, post)
-- Preserve other character separators (especially the tie character ‿).
pre = pre:gsub("%.", "")
post = post:gsub("%.", "")
return e .. pre .. "g.z" .. post
end)
-- -x- at the beginning of a coda becomes [ks], e.g. [[annex]], [[apèndix]], [[extracció]]; but not elsewhere in
-- the coda, e.g. in [[romanx]], [[ponx]]; words with [ks] in -nx such as [[esfinx]], [[linx]], [[manx]] need
-- respelling with [ks]; words ending in vowel + x like [[ídix]] need respelling with [ʃ]
text = strsub(text, "(" .. V .. stress_c .. "*)x", "%1ks")
if dialect == VALENCIANO then
-- Word-initial <x> as well as <x> after a consonant other than /j/ (including in the coda, e.g. [[ponx]])
-- becomes [t͡ʃ].
text = strsub(text, "#x", "#ʧ")
text = strsub(text, "([^" .. vowel_l .. separator_l .. "j]" .. charsep_c .. "*)x", "%1ʧ")
end
-- Other x becomes [ʃ]
text = strsub(text, "x", "ʃ")
-- Doubled ss -> s e.g. in exs-, exc(e/i)-, sc(e/i)-; FIXME: should this apply across word boundaries?
text = strsub(text, "s(" .. charsep_c .. "*)s", "%1s")
------------------ Coda consonant losses
-- In Central Catalan, coda losses happen everywhere, but otherwise they don't happen when
-- absolutely word-finally before a vowel or end of utterance (e.g. [[blanc]] has /k/ in Balearic and
-- Valencian but not [[blancs]]). Must precede consonant assimilations.
local boundary = dialect == CENTRAL and "(.)" or "([^#])"
text = strsub(text, "m[pb]" .. boundary, "m%1")
text = strsub(text, "([ln])[td]" .. boundary, "%1%2")
text = strsub(text, "[nŋ][kg]" .. boundary, "ŋ%1")
if dialect == VALENCIANO or dialect == BALEARICO then
local before_cons = "(" .. separator_c .. "*" .. C .. ")"
text = strsub(text, "m[pb]" .. before_cons, "m%1")
text = strsub(text, "([ln])[td]" .. before_cons, "%1%2")
text = strsub(text, "[nŋ][kg]" .. before_cons, "ŋ%1")
end
-- Delete /t/ between /s/ and any consonant other than /s/ or /ɾ/. Must precede voicing assimilation and
-- t + lateral/nasal assimilation.
text = strsub(text, "st(" .. sylsep_c .. "*[^" .. neg_guts_of_cons .. "sɾ])", "s%1")
------------------ Consonant assimilations
if dialect == CENTRAL then
-- v > b in onsets (not in codas, e.g. [[ovni]] [ɔ́vni] and [[hafni]] [ávni]). This needs to precede
-- assimilation of nb -> mb.
text = strsub(text, "v(" .. C .. "*" .. V ..")", "b%1")
end
-- t + lateral assimilation -> geminate across syllable boundary. We don't any more do t + nasal assimiation
-- because there are too many exceptions, e.g. [[aritmètic]], [[atmosfèric]], [[ètnia]]. Instead, we require that
-- cases where it does happen use respelling to effect this. FIXME: this doesn't always happen in -tl- either,
-- e.g. [[atlàntic]] has [əllántik] in GDLC but [adlántik] in DNV.
--
-- FIXME: Clean this up, maybe move below voicing assimilation, investigate whether it operates across words,
-- move stuff below that special-cases tll in Valencian here.
text = strsub(text, "t(" .. sylsep_c .. ")([lʎ])", "%2%1%2")
-- n + labial > labialized assimilation
text = strsub(text, "n(" .. separator_c .. "*[mbp])", "m%1")
text = strsub(text, "n(" .. separator_c .. "*[fv])", "ɱ%1")
-- n + velar > velarized assimilation
text = strsub(text, "n(" .. separator_c .. "*[kg])", "ŋ%1")
-- l/n + palatal > palatalized assimilation
text = strsub(text, "([ln])(" .. separator_c .. "*[ʎɲʃʒʧʤ])", function(ln, palatal)
ln = ({["l"] = "ʎ", ["n"] = "ɲ"})[ln]
return ln .. palatal
end)
-- ɲs > ɲʃ; FIXME: not sure the purpose of this; it doesn't apply in [[menys]] or derived terms like [[menyspreu]]
-- NOTE: Per [https://giec.iec.cat/textgramatica/codi/4.4], it does apply in these scenarios but the result is
-- somewhere between [s] and [ʃ], which is why it isn't shown in GDLC.
-- text = strsub(text, "ɲs", "%1ʃ")
------------------ Handle <r>
-- In replace_context_free(), we converted single r to ɾ and double rr to r.
if dialect == CENTRAL then
text = strsub(text, TEMP_PAREN_R, "")
text = strsub(text, TEMP_PAREN_RR, "r")
elseif dialect == BALEARICO then
text = strsub(text, TEMP_PAREN_R, "")
text = strsub(text, TEMP_PAREN_RR, "")
else
assert(dialect == VALENCIANO, ("Unrecognized dialect '%s'"):format(dialect))
text = strsub(text, TEMP_PAREN_R, "ɾ")
text = strsub(text, TEMP_PAREN_RR, "ɾ")
end
if dialect ~= VALENCIANO then
-- Coda /ɾ/ -> /r/
-- FIXME: This is inherited from the older code. Correct?
text = strsub(text, "(" .. V .. stress_c .. "*" .. C .. "*)ɾ", "%1r")
end
-- ɾ -> r word-initially or after [lns]; needs to precede voicing assimilation as <s> will be voiced to [z] before
-- /ɾ/.
text = strsub(text, "([#lns]" .. sylsep_c .. "*)ɾ", "%1r")
------------------ Voicing assimilation
-- Voicing or devoicing; we want to proceed from right to left, and due to the limitations of patterns (in
-- particular, the lack of support for alternations), it's difficult to do this cleanly using Lua patterns, so we
-- do it character by character.
local chars = strexplode(text)
-- We need to look two characters ahead in some cases, so start two characters from the end. This is safe because
-- the overall respelling ends in "##". (Similarly, as an optimization, don't check the first two characters, which
-- are always "##".)
for i = #chars - 2, 3, -1 do
-- We are looking for two consonants next to each other, possibly separated by a syllable or word divider.
-- We also handle a consonant followed by a syllable divider then a vowel, and a consonant word-finally.
-- Note that only coda consonants can change voicing, so we need to check to make sure we're in the coda.
local first = chars[i]
-- If `second` is nil, no assimilation occurs. Otherwise, `second` should be a consonant or empty string (which
-- represents a syllable or word boundary followed by a vowel or end of string), and we assimilate to that
-- consonant (empty string forces devoicing).
local second
-- If set to true, we're processing a consonant directly before a word boundary followed by a word beginning
-- with a vowel. In this context, voiceless sibilants voice. Note that we handle voicing of <s> word-internally
-- separately, in preprocess_word() [FIXME: maybe move much of the processing in preprocess_word() into this
-- function].
local word_boundary_before_vowel
if not strfind(first, C) then
-- leave `second` at nil; no assimilation
elseif chars[i + 1] == "#" then -- word boundary
if chars[i + 2] == " " then
-- chars[i + 3] should always be "#"
assert(chars[i + 3] == "#", "Word boundary followed by space but not #")
if strfind(chars[i + 4], C) then
second = chars[i + 4]
else
second = ""
word_boundary_before_vowel = true
end
else
second = ""
end
elseif strfind(chars[i + 1], sylsep_c) then -- syllable boundary
if strfind(chars[i + 2], C) then
second = chars[i + 2]
else
second = ""
end
elseif strfind(chars[i + 1], C) then
second = chars[i + 1]
else
-- followed by a vowel not across a syllable or word boundary; leave `second` as nil, no assimilation
end
if second then
-- Make sure we're in the coda. We have to look backwards until we find a vowel or syllable/word boundary.
local in_coda = false
local j = i - 1
while true do
assert(j > 0, "Missing word boundary at beginning of overall respelling")
if strfind(chars[j], "[" .. sylsep_l .. wordsep_l .. "]") then
break
elseif strfind(chars[j], V) then
in_coda = true
break
end
j = j - 1
end
if in_coda then
if word_boundary_before_vowel and strfind(first, "[zʒʣʤ]") then
-- leave alone
elseif voiced[second] and voicing[first] or word_boundary_before_vowel and strfind(first, "[sʃʦʧ]") then
chars[i] = voicing[first]
elseif (voiceless[second] or second == "") and devoicing[first] then
chars[i] = devoicing[first]
end
end
end
end
text = concat(chars)
-- gn -> ŋn e.g. [[regnar]] (including word-initial gn- e.g. [[gnòmic]], [[gneis]])
-- FIXME: This should be moved below voicing assimilation, and we need to investigate if it operates across words
-- (here I'm guessing yes).
if dialect ~= CENTRAL then
text = strsub(text, "#gn", "#n")
end
text = strsub(text, "g(" .. separator_c .. "*n)", "ŋ%1")
-- gʒ > d͡ʒ
-- FIXME: We need to investigate if it operates across words
text = strsub(text, "g(" .. sylsep_c .. "*)ʒ", "%1ʤ")
-- sʃ -> ʃ ([[desxifrar]]), zʒ -> ʒ ([[disjuntor]])
if dialect ~= VALENCIANO then
text = strsub(text, "s(" .. separator_c .. "*ʃ)", "%1")
text = strsub(text, "z(" .. separator_c .. "*ʒ)", "%1")
end
------------------ Gemination of <bl>, <gl>
if dialect ~= VALENCIANO then
-- bl -> bbl, gl -> ggl after the stress when following a vowel; to avoid this, use <b_l> or <g_l>.
-- This must follow v > b above. To force a hard ungeminated [b] or [g], use <_b> or <_g>.
text = strsub(text, "(" .. stress_c .. ")(" .. sylsep_c .. ")([bg])l", "%1%3%2%3l")
else -- Valencian; undo manually written 'bbl', 'ggl' in words like [[poblar]], [[reglament]]
text = strsub(text, "([bg])(" .. sylsep_c .. ")%1l", "%2%1l")
end
------------------ Lenition of voiced stops
-- In Central Catalan, b/d/g become fricatives (actually approximants, like in Spanish) in the onset following a
-- vowel and (except for <d>) after <l> and <ll> (cf. GDLC [[cabellblanc]] [kəβɛ̀ʎβláŋ]). This also happens across
-- word boundaries but doesn't happen after stops, nor in Central Catalan after [r], [ɾ] or [z] (and hence probably
-- not after [ʒ] either, although I can't find any examples in GDLC).
--
-- In Valencian, <b> doesn't lenite (at least formally?), but <d> and <g> do lenite after [r], [ɾ] or [z].
--
-- Balearic is like Valencian in not leniting <b>, and probably like Central Catalan otherwise.
local lenite_bdg = {["b"] = "β", ["d"] = "ð", ["g"] = "ɣ"}
if dialect == CENTRAL then
text = strsub(text, "([" .. vowel_l .. "jwlʎv]" .. separator_c .. "*[.#]" .. separator_c .. "*)([bdg])",
function(before, bdg) return before .. lenite_bdg[bdg] end)
elseif dialect == VALENCIANO then
text = strsub(text, "([" .. vowel_l .. "jwlʎvrɾzʣ]" .. separator_c .. "*[.#]" .. separator_c .. "*)([dg])",
function(before, dg) return before .. lenite_bdg[dg] end)
else
assert(dialect == BALEARICO, ("Unrecognized dialect '%s'"):format(dialect))
text = strsub(text, "([" .. vowel_l .. "jwlʎv]" .. separator_c .. "*[.#]" .. separator_c .. "*)([dg])",
function(before, dg) return before .. lenite_bdg[dg] end)
end
------------------ Vowel reduction
-- Reduction of unstressed a,e in Central and Balearic (Eastern Catalan).
if dialect ~= VALENCIANO then
-- The following rules seem to apply, based on the old code:
-- (1) Stressed a and e are never reduced.
-- (2) Unstressed e directly following ə is not reduced.
-- (3) Unstressed e directly before written <a> or before /ɔ/ is not reduced.
-- (4) Written <ee> when both vowels precede the primary stress is reduced to [əə]. (This rule preempts #2.)
-- (5) Written <ee> when both vowels follow the primary stress isn't reduced at all.
-- Rule #2 in particular seems to require that we proceed left to right, which is how the old code was
-- implemented.
-- FIXME: These rules seem overly complex and may produce incorrect results in some circumstances.
local words = strsplit(text, " ")
for j, word in ipairs(words) do
local chars = strexplode(word)
-- See above where voicing assimilation is handled. The overall respelling begins and ends in #, which we
-- can ignore. We need to look ahead three chars in some circumstances, but in all those circumstances we
-- shoudn't run off the end (and have assertions to check this).
local seen_primary_stress = false
for i = 2, #chars - 1 do
local this = chars[i]
if chars[i] == AC then
seen_primary_stress = true
end
if (this ~= "a" and this ~= "e") or strfind(chars[i + 1], stress_c) then
-- Not a/e, or a stressed vowel; continue
else
local reduction = true
local prev, prev_stress, nxt, nxt_stress
if not strfind(chars[i - 1], sylsep_c) then
prev = ""
else
prev = chars[i - 2] -- this should be non-nil as chars[i - 1] is a syllable separator (not #)
assert(prev, "Missing # at word boundary")
prev_stress = ""
if strfind(prev, stress_c) then
prev_stress = prev
prev = chars[i - 3]
-- As above; chars[i - 2] is a stress indicator (not #).
assert(prev, "Missing # at word boundary")
end
end
if not strfind(chars[i + 1], sylsep_c) then
nxt = ""
-- leave nxt at nil
else
nxt = chars[i + 2]
nxt_stress = chars[i + 3]
-- chars[i + 1] is a syllable separator, so chars[i + 2] should not be a word boundary, so
-- chars[i + 3] should exist.
assert(nxt and nxt_stress, "Syllable separator at word boundary or missing # at word boundary")
end
if this == "e" and strfind(prev, "ə") then
reduction = false
elseif this == "e" and strfind(nxt, "[aɔ]") then
reduction = false
elseif this == "e" and nxt == "e" and not strfind(nxt_stress, AC) then
-- FIXME: Check specifically for AC duplicates previous logic but is probably wrong or unnecessary.
if not seen_primary_stress then
chars[i + 2] = "ə"
else
reduction = false
end
end
if reduction then
chars[i] = "ə"
end
end
end
words[j] = concat(chars)
end
text = concat(words, " ")
end
if dialect == CENTRAL then
-- Reduction of unstressed o (not before w)
text = strsub(text, "o([^" .. stress_l .. "w])", "u%1")
elseif dialect == BALEARICO then
-- Reduction of unstressed o per vowel harmony: unstressed /o/ -> /u/ directly before stressed /i/ or /u/;
-- as a Lua pattern, o can be followed only by consonants and/or syllable separators (no vowels, stress marks
-- or word separators).
text = strsub(text, "o([^" .. vowel_l .. stress_l .. wordsep_l .. "]*[iu]" .. stress_c .. ")", "u%1")
end
-- Final losses.
text = strsub(text, "j(ʧs?#)", "%1") -- boigs /bɔt͡ʃ/
text = strsub(text, "([ʃʧs])s#", "%1#") -- homophone plurals -xs, -igs, -çs
if dialect ~= VALENCIANO then
-- Remove j before palatal obstruents
text = strsub(text, "j(" .. sylsep_c .. "*[ʃʒʧʤ])", "%1")
else -- Valencian
-- Fortition of palatal fricatives
text = strsub(text, "ʒ", "ʤ")
text = strsub(text, "(i" .. stress_c .. "*" .. sylsep_c .. ")ʣ", "%1z")
end
if dialect ~= CENTRAL then
-- No palatal gemination ʎʎ > ll or ʎ, in Valencian and Balearic.
-- FIXME: These conditions seem to be targeting specific words and should probably be fixed using respelling
-- instead.
text = strsub(text, "([bpw]a" .. stress_c .. "*)ʎ(" .. sylsep_c .. "*)ʎ", "%1l%2l")
text = strsub(text, "([mv]e" .. stress_c .. "*)ʎ(" .. sylsep_c .. "*)ʎ", "%1l%2l")
text = strsub(text, "(ti" .. stress_c .. "*)ʎ(" .. sylsep_c .. "*)ʎ", "%1l%2l")
text = strsub(text, "(m[oɔ]" .. stress_c .. "*)ʎ(" .. sylsep_c .. "*)ʎ", "%1l%2l")
text = strsub(text, "(u" .. stress_c .. "*)ʎ(" .. sylsep_c .. "*)ʎ", "%1l%2l")
text = strsub(text, "ʎ(" .. sylsep_c .. "*ʎ)", "%1")
end
---------- Convert pseudo-symbols to real ones.
-- Convert g to IPA ɡ.
text = strsub(text, "g", "ɡ")
-- Convert pseudo-afficate symbols to full affricates.
local full_affricates = { ["ʦ"] = "t͡s", ["ʣ"] = "d͡z", ["ʧ"] = "t͡ʃ", ["ʤ"] = "d͡ʒ" }
text = strsub(text, "([ʦʣʧʤ])", full_affricates)
---------- Generate IPA stress marks.
-- Convert acute and grave to IPA stress marks.
text = strsub(text, AC, "ˈ")
text = strsub(text, GR, "ˌ")
-- Move IPA stress marks to the beginning of the syllable.
text = strsubrep(text, "([#.])([^#.]*)(" .. ipa_stress_c .. ")", "%1%3%2")
-- Suppress syllable divider before IPA stress indicator.
text = strsub(text, "%.(#?" .. ipa_stress_c .. ")", "%1")
-- Make all primary stresses but the last one in a given word be secondary. May be fed by the first rule above.
-- FIXME: Currently this is handled earlier, but we might want to move it here, as is done in [[Module:pt-pronunc]].
-- text = strsubrep(text, "ˈ([^ ]+)ˈ", "ˌ%1ˈ")
-- Make primary stresses in prefixes become secondary. (FIXME: Handled earlier now.)
-- text = strsubrep(text, "ˈ([^#]*#" .. PREFIX_MARKER .. ")", "ˌ%1")
-- Remove # symbols at word/text boundaries, as well as _ (which forces separate interpretation), pseudo-consonant
-- markers (at edges of some prefixes/suffixes), and prefix markers, and recompose.
text = strsub(text, "[#_" .. PSEUDOCONS .. "]", "")
text = strnfc(text)
return text
end
local function preprocess_word(syllables, suffix_syllables, dialect, pos, orig_word)
-- Stressed vowel is ambiguous
if syllables.stress then
if strfind(syllables[syllables.stress].vowel, "[eo]") then
--[[
local marks = {["e"] = {AC, GR, CFLEX, DIA}, ["o"] = {AC, GR, CFLEX}}
local marked_vowels = {}
for _, mark in ipairs(marks[stressed_vowel]) do
insert(marked_vowels, stressed_vowel .. mark)
end
--error(("In respelling '%s', the stressed vowel '%s' is ambiguous. Please mark it with an acute, " ..
--"grave, or combined accent: %s."):format(orig_word, stressed_vowel,
--m_table.serialCommaJoin(marked_vowels, {dontTag = true, conj = "or"})))
--]]
-- en lugar de arrojar error, asumo posición cerrada que es lo más frecuente (no hay regla para decidir, lo va a tener q especificar el usuario)
syllables[syllables.stress].vowel = strsub(syllables[syllables.stress].vowel, "[eo]", {["e"] = "é", ["o"] = "ó"})
end
end
-- Final -r is ambiguous in many cases.
local final = syllables[#syllables]
-- Stressed final r after a or i in non-monosyllables is treated as (r), i.e. verbal infinitives are assumed (NOTE:
-- not always the case, e.g. there are many adjectives and nouns in -ar that should be marked as '(rr)', and
-- several loanword nouns in -ir that should be marked as 'rr'). Likewise for stressed final r or rs after é in
-- non-monosyllables (which are usually adjectives or nouns with the -er ending, but may be verbal infinitives,
-- which should be marked as 'ê(r)'). That is, it disappears other than in Valencian. All other final r and final
-- rs are considered ambiguous and need to be rewritten using rr, (rr) or (r).
if #syllables > 1 and final.stressed then
if final.coda == "r" and strfind(final.vowel, "[aàiíé]") or final.coda == "rs" and final.vowel == "é" or
final.vowel == "ó" and strfind(final.coda, "^rs?$") and strfind(final.onset, "[stdç]") then
final.coda = TEMP_PAREN_R
end
end
if strfind(final.coda, "^rs?$") or strfind(final.coda, "[^r]rs?$") then
--[[
error(("In respelling '%s', final -r by itself or in -rs is ambiguous except in the verbal endings -ar or " ..
"-ir, in the nominal or adjectival endings -er(s) and -[dtsç]or(s). In all other cases it needs to be " ..
"rewritten using one of 'rr' (pronounced everywhere), '(rr)' (pronounced everywhere but Balearic) or " ..
"'(r)' (pronounced only in Valencian). Note that adjectives in -ar usually need rewriting using '(rr)'; " ..
"nouns in -ar referring to places should be rewritten using '(r)'; and loanword nouns in -ir usually " ..
"need rewriting using 'rr'."):format(orig_word))
]]--
final.coda = strsub(final.coda, "r(s?)$", TEMP_PAREN_R.."%1") -- asumo que es (r)
end
local syllables_IPA = {stress = syllables.stress, is_prefix = syllables.is_prefix, is_suffix = syllables.is_suffix}
for key, val in ipairs(syllables) do
syllables_IPA[key] = {onset = val.onset, vowel = val.vowel, coda = val.coda, stressed = val.stressed}
end
-- Replace letters with IPA equivalents
for i, syll in ipairs(syllables_IPA) do
-- Voicing of s
if syll.onset == "s" and i > 1 and strfind(syllables[i - 1].coda, "^[iu]?$") then
syll.onset = "z"
end
if strfind(syll.vowel, "^[eèéêëií]$") then
syll.onset = strsub(syll.onset, "tg$", "ʤ")
syll.onset = strsub(syll.onset, "[cg]$", {["c"] = "s", ["g"] = "ʒ"})
syll.onset = strsub(syll.onset, "[qg]u$", {["qu"] = "k", ["gu"] = "g"})
end
syll.coda = strsub(syll.coda, "igs?$", "iʤ")
syll.onset = replace_context_free(syll.onset)
syll.coda = replace_context_free(syll.coda)
syll.vowel = strsub(syll.vowel, ".",
dialect == CENTRAL and IPA_vowels_central or
dialect == BALEARICO and IPA_vowels_balearic or
IPA_vowels_valencian
)
syll.vowel = strsub(syll.vowel, ".", IPA_vowels)
end
for _, suffix_syl in ipairs(suffix_syllables) do
insert(syllables_IPA, suffix_syl)
end
return syllables_IPA
end
local function convertir_palabra(word, dialect, pos)
local suffix_syllables = {}
local orig_word = word
if not pos or pos == ADV then
local word_before_ment, ment = strmatch(word, "^(.*)(m[eé]nt)$")
if word_before_ment and (pos == ADV or not strfind(word_before_ment, "[iï]$") and
strfind(word_before_ment, V .. ".*" .. V)) then
suffix_syllables = {{onset = "m", vowel = "e", coda = "nt", stressed = true}}
pos = ADJ
word = word_before_ment
end
end
word = word_fixes(word, dialect)
local syllables = split_syllables(word)
syllables = preprocess_word(syllables, suffix_syllables, dialect, pos, orig_word)
-- Combine syllables.
local combined = {}
local has_ment = #suffix_syllables > 0
for i, syll in ipairs(syllables) do
local ac = (i == syllables.stress and not syllables.is_prefix and not has_ment or has_ment and i == #syllables) and AC or -- primary stress
syllables[i].stressed and GR or -- secondary stress
""
insert(combined, syll.onset .. syll.vowel .. ac .. syll.coda)
end
return concat(combined, ".")
end
local function generar_pron_aux(text, dialect, pos)
local convertido = {}
local fragmentos = strsplit(text, "%s*|%s*")
local k = 1
for _,fragmento in ipairs(fragmentos) do
local palabras = strsplit(fragmento, "%s")
palabras = handle_unstressed_words(palabras)
local palabras_convertidas = {}
for _,p in ipairs(palabras) do
insert(palabras_convertidas, convertir_palabra(p, dialect, pos[k]))
k = k + 1
end
insert(convertido, concat(palabras_convertidas, " "))
end
-- Put double ## at utterance boundaries (beginning/end of string) and at foot boundaries (marked with |).
-- Note that if the string without pound signs is 'foo bar baz | bat quux', the final string will be
-- '##foo# #bar# #baz## #|# ##bat# #quux##'.
local texto_convertido = "##" .. concat(convertido, "# | #") .. "##"
texto_convertido = strsub(texto_convertido, " ", "# #")
return postprocess_general(texto_convertido, dialect)
end
local function generar_pron(text, pos)
if strfind(text, "[áìùÁÌÙ]") then
error(("Invalid accented character in respelling '%s'; use accented à í ú, not the reversed versions"):format(text))
end
text = normalizar(text)
local conv_cen = generar_pron_aux(text, CENTRAL, pos)
local conv_val = generar_pron_aux(text, VALENCIANO, pos)
local conv_bal = generar_pron_aux(text, BALEARICO, pos)
return {{"central"}, {"valenciano"}, {"baleárico"}}, {{strhtml(conv_cen)}, {strhtml(conv_val)}, {strhtml(conv_bal)}}
end
--Se obtiene el tipo de acentuación
local function determinar_acentuacion(w)
if type(w) ~= "string" then
return nil
end
local silabas = {}
for s in strmatchit(w, "[^"..sylsep_l.."]+") do
table.insert(silabas, s)
end
local L = #silabas
local sufijo = nil
if L >= 4 and silabas[L-1] == "men" and silabas[L] == "te" then
return "doble", L
elseif L == 1 then
return "monosílaba", L
else
local i = 1
for silaba in strmatchit(w, sylsep_c..'*'.."[^"..sylsep_l.."]+") do
if strfind(silaba, "ˈ") then
local idx = L - i
if idx == 0 then
return "aguda", L
elseif idx == 1 then
return "llana", L
elseif idx == 2 then
return "esdrújula", L
else
return "sobreesdrújula", L
end
break
end
i = i + 1
end
error("Se esperaba que la pronunciación de la palabra hubiera sido generada con las marcas de acentuación")
end
end
function export.procesar_pron_args(titulo, args)
local tit = titulo
local vino_ayuda, x
if #args["ayuda"] < 1 then
args["ayuda"][1] = tit
else
vino_ayuda = true
end
if #args["fone"] < 1 and #args["fono"] < 1 then
x = pron_abc[args["ayuda"][1]]
if x then
args["ayuda"] = x
args["tl"] = x
end
local A = #args["ayuda"]
local j = 1 -- indice de la ayuda
local k = 1 -- cantidad de pronunciaciones insertadas (máximo 9)
while k <= 9 and j <= A do
local cg = {}
local flags = args["ayudaextra"][j] and strsplit(args["ayudaextra"][j], ";") or {}
for _,flag in ipairs(flags) do
local z = normalizar_cg[flag]
if z then
insert(cg, z)
end
end
local pron, fone = generar_pron(args["ayuda"][j], cg)
for i,_ in ipairs(fone) do
insert(args["pron"], pron[i])
insert(args["fone"], fone[i])
if vino_ayuda then
insert(args["fgraf"], {args["ayuda"][j]})
end
k = k + 1
if k > 9 then
break
end
end
j = j + 1
end
end
local tiene_espacios = strfind(tit, "%s")
if args["fone"][1] and args["fone"][1][1] then
local rim = strsub(args["fone"][1][1], ".*%s([^%s]+)$", "%1") -- me quedo con la última palabra
rim = strsub(rim, "^.*ˈ(.-)$", "%1")
args["rima"][1] = strsub(rim, ".-".."("..IPA_VOWEL_CLUSTER..".*"..")".."$", "%1")
end
if not tiene_espacios then
if args["fone"][1] and args["fone"][1][1] then
local ls, ac = {}, {}
for _,f in ipairs(args["fone"]) do
local ace, lon = determinar_acentuacion(f[1])
ls[lon] = true
ac[ace] = true
end
for lon,_ in pairs(ls) do
insert(args["ls"], lon)
end
for ace,_ in pairs(ac) do
insert(args["ac"], ace)
end
end
end
return args
end
return export