Module:sk-noun
Appearance
- This module lacks a documentation subpage. Please create it.
- Useful links: subpage list • links • transclusions • testcases • sandbox
--[=[
This module contains functions for creating inflection tables for Slovak
nouns.
]=]--
local export = {}
-- Within this module, declensions are the functions that do the actual
-- declension by creating the forms of a basic noun.
-- They are defined further down.
--local lemma = "Bratislava"
local declensions = {}
local lang = require("Module:languages").getByCode("sk")
local m_table = require("Module:table")
local m_links = require("Module:links")
local m_adj = require("Module:sk-adjective")
local allowed_genders = {
"m-pr", "m-anml", "m-in", "f", "n"
}
local allowed_genders_set = m_table.listToSet(allowed_genders)
local hard_consonants = {"d", "t", "n", "l", "h", "ch", "k", "g"}
local soft_consonants = {"ď", "ť", "ň", "ľ", "ž", "š", "č", "dž", "c", "dz", "j"}
local ambiguous_consonants = {"b", "m", "p", "r", "s", "v", "z", "f"}
local hard_consonants_set = m_table.listToSet(hard_consonants)
local soft_consonants_set = m_table.listToSet(soft_consonants)
local ambiguous_consonants_set = m_table.listToSet(ambiguous_consonants)
local consonants = m_table.append(hard_consonants, soft_consonants, ambiguous_consonants)
local consonants_set = m_table.listToSet(consonants)
local short_vowels = {"a", "e", "i", "o", "u", "y", "ä", "ö", "ü"}
local long_vowels = {"á", "é", "í", "ó", "ú", "ý", "ő", "ű"}
local diphthongs = {"ia", "ie", "iu", "ô"}
local short_vowels_set = m_table.listToSet(short_vowels)
local long_vowels_set = m_table.listToSet(long_vowels)
local diphthongs_set = m_table.listToSet(diphthongs)
local vowels = m_table.append(short_vowels, long_vowels)
local vowels_set = m_table.listToSet(vowels)
local obstruents = {"p", "t", "k", "b", "d", "g", "f", "s", "š", "ch", "z", "ž", "č", "dž"} --"v" removed??
local sonorants = {"m", "n", "l", "r", "ʋ", "v"} --"v" added??
local obstruents_set = m_table.listToSet(obstruents)
local sonorants_set = m_table.listToSet(sonorants)
local bigraphs = {"ch", "dz", "dž"}
local bigraphs_set = m_table.listToSet(bigraphs)
local prepositions = {"bez", "bezo", "cez", "cezo", "do", "k", "ku", "medzi",
"na", "nad", "nado", "o", "do", "okrem", "po", "pod", "podo", "pre", "pred",
"predo", "pri", "proti", "naproti", "oproti", "s", "so", "skrz", "u", "v",
"vo", "z", "zo", "za"} -- Add more prepositions as needed
local prepositions_set = m_table.listToSet(prepositions)
local conjunctions = {"a"} -- Add more conjunctions as needed
local conjunctions_set = m_table.listToSet(conjunctions)
-- The main entry point.
-- This is the only function that can be invoked from a template.
function export.show(frame)
local args = frame:getParent().args
NAMESPACE = mw.title.getCurrentTitle().nsText
if NAMESPACE == "" then
PAGENAME = mw.title.getCurrentTitle().text
else
if args["pagename"] then
PAGENAME = args["pagename"] -- TEST: lemma
else
error("Pagename not specified")
end
end
GENDER = check_gender(args[1])
NUMBER = args["n"]
TYPE = args["t"]
GEN_ENDING = args["g"]
PLURAL = args["pl"]
PLURAL2 = args["pl2"]
PRONUNCIATION = args["p"]
local genitive = args[2]
local category = ""
-- Check if PAGENAME is a multiword expression
if not mw.ustring.find(PAGENAME, " ") then
-- Single-word expression, handle as usual
PATTERN = determine_pattern(PAGENAME, genitive)
-- Category
if NAMESPACE == "" then
if PATTERN == "indeclinable" then
category = "[[Category:Slovak indeclinable nouns|" .. PAGENAME .. "]]"
elseif PATTERN == "irregular" then
category = "[[Category:Slovak irregular nouns|" .. PAGENAME .. "]]"
elseif PATTERN == "adjective" then
category = "[[Category:Slovak adjectival nouns|" .. PAGENAME .. "]]"
else
category = "[[Category:Slovak terms with declension " .. PATTERN .. "|" .. PAGENAME .. "]]"
end
if GENDER == "m-anml" then
category = category .. "[[Category:Slovak terms with declension chlap|" .. PAGENAME .. "]]"
end
if PAGENAME == "cól" then
category = category .. "[[Category:Slovak terms with declension dub|" .. PAGENAME .. "]]"
end
end
local forms, title
-- Find out if there are more genitive stems and if so, process them
if genitive then
if mw.ustring.find(genitive, "/") then
forms, title = decline_with_more_stems(genitive)
else
forms, title = declensions[PATTERN](PAGENAME, genitive)
normalize_forms(forms)
end
else
forms, title = declensions[PATTERN](PAGENAME, genitive)
normalize_forms(forms)
end
specified_by_user(forms, args)
return make_table(forms, title) .. category
end
-- Multiword expressions
-- Category
if NAMESPACE == "" then
category = "[[Category:Slovak multiword terms|" .. PAGENAME .. "]]"
end
-- Split into words
local units, gen_units = split_into_units(PAGENAME, genitive)
-- Process each unit to generate forms
local combined_forms = generate_combined_forms(units, gen_units)
-- Add forms specified by the user
specified_by_user(combined_forms, args)
-- Generate the final title and table
local title = "Declension of ''" .. PAGENAME .. "'' (multiword term)"
return make_table(combined_forms, title) .. category
end
--[=[
Declension functions
]=]--
declensions["chlap"] = function(word, genitive)
local forms = {}
local syllable_count, syllables = split_into_syllables(word)
local mobile_removed = false -- Tracks if a mobile vowel was removed
local words_with_mobile = {"kmotor", "švagor", "svokor", "lotor",
"obor", "Uhor", "pochábeľ", "väzeň", "učeň",
"posol", "diabol", "blázon", "Turek", "fanúšek", "center",
"hypochonder", "Kafer", "neger", "gangster",
"kapor", "zubor", "bobor", "orol", "pes"
}
local surn_ech_always_declined = {"Čech", "Plech", "Pech", "Štech", "Vojtech"}
-- Determine the stem by removing mobile vowels if applicable
local stem = word
if (matches_any(word, words_with_mobile)
or ends_with_any(word, {"ec", "ček", "íček", "ok"}))
and not (TYPE == "surn"
and (ends_with_any(word, {"[aeiouyáéíóúýäöüőű]ček", "[aeiouyáéíóúýäöüőű]čok"})
or syllable_count == 1))
and not matches_any(word, {"otrok"})
then
stem = remove_mobile_vowel(word)
mobile_removed = true -- Mark mobile vowel as removed
end
-- Adjust specific stems based on predefined mappings
stem = ({["švec"] = "ševc", ["žnec"] = "ženc"})[word] or stem
-- Remove certain endings for words ending with "us", "os", "es" and "o"
if ends_with(word, "[uoea]s") then
stem = mw.ustring.sub(word, 1, -3)
end
if ends_with(word, "o") then
stem = remove_last_char(word)
end
-- Override if a specific argument is given
if genitive and NUMBER ~= "pl" then
stem = remove_last_char(genitive)
end
-- Title for the declension table
local title = "Declension of ''" .. word .. "'' (pattern " .. pattern_link("chlap") .. ")"
if TYPE == "surn" then
if ends_with(word, "a[iy]") then
title = "Declension of ''" .. word .. "'' (pattern " .. pattern_link("chlap") .. " or " .. pattern_link("kuli") .. ")"
elseif ends_with_any(word, {"ých", "ech"}) and not matches_any(word, surn_ech_always_declined) then
title = "Declension of ''" .. word .. "'' (pattern " .. pattern_link("chlap") .. " or indeclinable)"
end
end
-- Helper variables for analyzing the last and penultimate characters
local ultimate, penultimate = get_last_letters(stem)
local first = get_first_char(word)
-- Determine alternative dative and locative for some nouns
if matches_any(word, {"človek", "pán", "čert", "diabol", "vlk", "pes",
"Boh", "boh", "otec", "syn", "duch", "Kristus", "don", "šor"})
then
set_forms(forms, {"dat_sg2", "loc_sg2"}, {stem .. "u", stem .. "u"})
end
local nom_pl = "i"
if PLURAL then
if mw.ustring.find(PLURAL, "/") then
local endings = {}
for part in mw.ustring.gmatch(PLURAL, "[^/]+") do
table.insert(endings, part)
end
nom_pl = endings[1]
for i = 2, 4 do
if endings[i] then
set_forms(forms, {"nom_pl" .. i}, {append_ending(stem, endings[i])})
else
break
end
end
else
nom_pl = PLURAL
end
elseif (ends_with_any(word, {"fil", "fób", "graf", "nóm"})
and not matches_any(word, {"xylograf", "typograf"}))
or matches_any(word, {"cynik", "epik", "klasik", "stoik",
"Brit", "Búr", "Gal", "Ír", "Dór", "Italik", "Ión", "Nór",
"cár", "lodivod", "odkundes", "starík", "muž", "svat", "apoštol",
"augur", "bán", "bogomil", "buržuj", "don", "druid", "eféb",
"famulus", "héros", "man", "posol"
})
then
set_forms(forms, {"nom_pl2"}, {append_ending(stem, "ovia")})
elseif (ends_with_any(word, {"[^c]h", "g", "ček", "ek", "ik", "čik", "ok", "o", "duch"}) and word ~= "pes")
or matches_any(word, {"otec", "syn", "ujec", "strýc", "svák", "svokor", "tesť", "švagor",
"zať", "kmotor", "ded", "kmeť", "pobratim", "otčim", "zlosyn", "princ",
"Bask", "Frank", "Ind", "Ink", "Kurd", "Dák", "Čudo", "Fríz", "Azték",
"Asýr", "Etrusk", "Kazach", "Kašub", "odľud", "člen", "zved", "hňup", "cicerone"})
or (ends_with(word, "ch") and TYPE == "loan")
or (ends_with_any(word, {"us", "es"}) and genitive ~= word .. "a")
or matches_any(TYPE, {"comp-deverb", "surn", "name"})
or PLURAL == "ovia"
then
nom_pl = "ovia"
if TYPE == "surn" then
append_second_plural(forms, stem .. "ovci", stem .. "ovcov", stem .. "ovcom",
stem .. "ovcov", stem .. "ovcoch", stem .. "ovcami")
end
elseif ends_with(word, "teľ")
or (ends_with(word, "an") and is_capital(first))
or matches_any(word, {"občan", "dedinčan", "mešťan", "zeman", "ostrovan",
"krajan", "dolňan", "horňan", "južan", "severan", "rodič", "dedič", "brat", "hosť",
"manžel", "exmanžel", "sused", "žid", "Žid", "kresťan", "nekresťan", "pohan"})
then
nom_pl = "ia"
if word == "pohan" then
set_forms(forms, {"nom_pl2"}, {append_ending(stem, "i")})
end
end
-- Determine instrumental plural ending
local ins_pl = "mi" -- Default instrumental plural ending is "mi"
if ultimate == "m"
or mobile_removed
or ends_with_any(word, {"o", "ius"})
or matches_any(word, {"hosť", "tesť", "Kopt"}) --obstruents_set[penultimate] and obstruents_set[ultimate]
then
ins_pl = "ami"
end
-- Populate forms table with singular and plural endings
append_endings(forms, word, stem,
"a", "ovi", "a", "ovi", "om",
nom_pl, append_ending(stem, "ov"), "om", "ov", "och", ins_pl
)
-- Surnames ending with -ých, -ech, -ai, -ay
if TYPE == "surn" then
if ends_with_any(word, {"ých", "ech"}) and not matches_any(word, surn_ech_always_declined) then
append_alternative_singular(forms, word, word, word, word, word)
append_alternative_plural(forms, word, word, word, word, word, word)
elseif ends_with(word, "a[iy]") then
append_alternative_singular(forms, word .. "ho", word .. "mu", word .. "ho", word .. "m", word .. "m")
set_forms(forms, {"ins_pl2"}, {word .. "ami"})
end
end
-- Exceptions
if word == "človek" then
set_forms(forms,
{"nom_pl", "gen_pl", "dat_pl", "acc_pl", "loc_pl", "ins_pl"},
{"ľudia", "ľudí", "ľuďom", "ľudí", "ľuďoch", "ľuďmi"}
)
elseif word == "héros" then
set_forms(forms,
{"gen_sg", "dat_sg", "acc_sg", "loc_sg", "ins_sg",
"nom_pl", "gen_pl", "dat_pl", "acc_pl", "loc_pl", "ins_pl",
"gen_sg2", "dat_sg2", "acc_sg2", "loc_sg2", "ins_sg2",
"nom_pl2", "gen_pl2", "dat_pl2", "acc_pl2", "loc_pl2", "ins_pl2"},
{"hérosa", "hérosovi", "hérosa", "hérosovi", "hérosom",
"hérosi", "hérosov", "hérosom", "hérosov", "hérosoch", "hérosmi",
"héroa", "héroovi", "héroa", "héroovi", "héroom",
"héroovia", "héroov", "héroom", "héroov", "hérooch", "héroami"}
)
end
-- Vocative
if matches_any(word, {"syn", "sváko", "švagor", "kmotor", "brat", "synko",
"synáčik", "pán", "chlapec", "priateľ", "majster", "Boh", "boh", "Pán", "Syn",
"Duch", "Ježiš", "Ježiško", "Kristus", "Spasiteľ", "Vykupiteľ", "Stvoriteľ",
"Premožiteľ", "Kráľ", "Hospodin", "baránok", "Baránok", "tešiteľ", "Tešiteľ",
"hriešnik", "protivník", "kresťan", "otec", "Otec", "chlap", "človek"})
then
voc_stem = stem
if word == "synáčik" then voc_stem = "synáčk" end
if word == "pán" then voc_stem = "pan" end
if word == "Pán" then voc_stem = "Pan" end
if ends_with_any(word, {"teľ", "o"})
or matches_any(word, {"syn", "Syn", "Duch", "Ježiš", "Kráľ",
"hriešnik", "synáčik", "baránok", "Baránok"})
then
set_forms(forms, {"voc_sg"}, {append_ending(voc_stem, "u")})
else
if ends_with_any(voc_stem, {"k", "c"}) then
voc_stem = remove_last_char(voc_stem) .. "č"
elseif ends_with(voc_stem, "h") then
voc_stem = remove_last_char(voc_stem) .. "ž"
end
set_forms(forms, {"voc_sg"}, {append_ending(voc_stem, "e")})
end
if not matches_any(word, {"človek", "pán", "brat"}) then
set_forms(forms, {"voc_sg2"}, {word})
elseif word == "brat" then
set_forms(forms, {"voc_sg2", "voc_sg3"}, {"bratu", "brat"})
end
end
return forms, title -- Return forms table and title for declension
end
declensions["hrdina"] = function(word, genitive)
local forms = {}
local title = "Declension of ''" .. word .. "'' (pattern " .. pattern_link("hrdina") .. ")"
local stem = remove_suffix(word, "a")
if ends_with(word, "[oeě]") then
stem = remove_last_char(word)
if ends_with(word, "ě") and ends_with(stem, "[dtnl]") then
stem = soften_last_consonant(stem)
end
end
local nom_pl_ending = "ovia"
if ends_with_any(word, {"ista", "ita"})
or (ends_with(word, "ta") and TYPE == "loan") then
nom_pl_ending = "i"
end
local ins_pl_ending = "ami"
if ends_with(word, "[aeiouyáéíóúýäöüőű]ta") and TYPE == "loan" then
ins_pl_ending = "i"
end
append_endings(forms, word, stem,
"u", "ovi", "u", "ovi", "om",
nom_pl_ending, stem .. "ov", "om", "ov", "och", ins_pl_ending
)
if TYPE == "surn" then
append_second_plural(forms, stem .. "ovci", stem .. "ovcov", stem .. "ovcom",
stem .. "ovcov", stem .. "ovcoch", stem .. "ovcami")
end
if word == "buržoa" then
set_forms(forms,
{"dat_sg", "loc_sg", "ins_sg",
"nom_pl", "gen_pl", "dat_pl", "acc_pl", "loc_pl"},
{"buržuovi", "buržuovi", "buržuom",
"buržuovia", "buržuov", "buržuom", "buržuov", "buržuoch"}
)
end
return forms, title
end
declensions["kuli"] = function(word, genitive)
local forms = {}
local stem = word
local title = "Declension of ''" .. word .. "'' (pattern " .. pattern_link("kuli") .. ")"
append_endings(forms, word, stem,
"ho", "mu", "ho", "m", "m",
"ovia", stem .. "ov", "om", "ov", "och", "ami"
)
if TYPE == "surn" then
append_second_plural(forms, stem .. "ovci", stem .. "ovcov", stem .. "ovcom",
stem .. "ovcov", stem .. "ovcoch", stem .. "ovcami")
end
return forms, title
end
declensions["dub"] = function(word, genitive)
local forms = {}
local mobile_removed = false -- Tracks if a mobile vowel was removed
local words_with_mobile = {"priemysel", "počet", "ocot", "bahor", "chrbát",
"ker", "cukor", "uhor", "vietor", "víchor", "vôdor", "kotol",
"kapor", "zubor", "bobor", "orol", "pes", "mozog", "uhol"}
local syllable_count, syllables = split_into_syllables(word)
-- Determine the stem by removing mobile vowels if applicable
local stem = word
if (matches_any(word, words_with_mobile)
or (ends_with_any(word, {"íček", "ýček", "ok"}) and syllable_count ~= 1))
and not matches_any(word, {"polrok", "nárok", "zákrok", "rozkrok"})
then
stem = remove_mobile_vowel(word)
mobile_removed = true -- Mark mobile vowel as removed
end
-- Adjust specific stems based on predefined mappings
stem = ({["mráz"] = "mraz", ["vietor"] = "vetr", ["kôl"] = "kol",
["stôl"] = "stol", ["vôl"] = "vol", ["chlieb"] = "chleb",
["ensemble"] = "ensembl"})[word] or stem
-- Remove certain endings for words ending with "us", "os", "es" and "o"
if (ends_with(word, "[uoe]s") and TYPE == "loan") or ends_with(word, "izmus") then
stem = mw.ustring.sub(word, 1, -3)
end
if ends_with(word, "o") then
stem = remove_last_char(word)
end
-- Pluralia tantum
if NUMBER == "pl" then
stem = remove_suffix(word, "y")
end
-- Override if a specific argument is given
if genitive and NUMBER ~= "pl" then
stem = remove_last_char(genitive)
if stem == remove_last_char(remove_last_char(word)) .. get_last_char(word) then
mobile_removed = true
elseif stem == word then
mobile_removed = false
end
end
-- Set "mobile_removed" if genitive was set by the user
if ends_with(word, "e[lr]") and ends_with(stem, "[^e][lr]") then
mobile_removed = true
end
-- Title for the declension table
local title = "Declension of ''" .. word .. "'' (pattern " .. pattern_link("dub") .. ")"
if GENDER == "m-anml" then
if matches_any(word, {"vták", "vlk", "pes"}) then
title = "Declension of ''" .. word .. "'' (patterns " .. pattern_link("chlap") .. " <small>(singular, plural 1)</small> and " .. pattern_link("dub") .. " <small>(plural 2)</small>)"
elseif PLURAL2 then
title = "Declension of ''" .. word .. "'' (patterns " .. pattern_link("chlap") .. " <small>(singular, plural 2)</small> and " .. pattern_link("dub") .. " <small>(plural 1)</small>)"
else
title = "Declension of ''" .. word .. "'' (patterns " .. pattern_link("chlap") .. " <small>(singular)</small> and " .. pattern_link("dub") .. " <small>(plural)</small>)"
end
end
-- Helper variables for analyzing the last and penultimate characters
local ultimate, penultimate = get_last_letters(stem)
local first = get_first_char(word)
-- Determine genitive singular ending
local gen_sg = "a"
if (matches_any(TYPE, {"abstr", "comp-deverb", "material", "collect", "loan"})
or matches_any(GEN_ENDING, {"u", "u/a"})
or ends_with_any(word, {"izmus", "x"})
or (ends_with(word, "m") and not is_capital(first))
or (ends_with(word, "z") and is_capital(first)))
and not matches_any(GEN_ENDING, {"a", "a/u"})
then
gen_sg = "u"
end
-- Override genitive singular ending if a specific argument is given
if genitive then
gen_sg = get_last_char(genitive)
end
-- Determine locative singular ending based on specific suffixes and patterns
local loc_sg = "e"
if ends_with_any(word, {"k", "g", "h", "ch", "ius", "eus"}) then
loc_sg = "u"
elseif (ends_with(word, "ér")
or (ends_with(word, "ál") and not matches_any(word, {"bál", "paškál", "paušál", "šál", "škandál", "žurnál"}))
or matches_any(word, {"Alžír", "klavír", "apríl", "jún", "júl"}))
then
loc_sg = "i"
end
-- Determine genitive plural ending
local gen_pl = stem .. "ov" -- Default genitive plural ending is "ov"
-- Adjust genitive plural for specific place names and patterns
if is_capital(first) and (ends_with_any(word, {"any", "íky", "áky", "iaky"})
or matches_any(word, {"Krompachy", "Kubachy", "Veľbachy", "Spišské Vlachy",
"Žabokreky", "Čechy", "Brezolupy", "Bruty", "Rakoľuby",
"Rakúsy", "Prusy", "Veľaty", "Piešťany", "Bieščary"}))
then
local gen_stem = remove_last_char(word) -- Remove last character to get the base stem
local syllable_count, syllables = split_into_syllables(gen_stem)
local last_syllable = syllables[1]
local penultimate_syllable = syllables[2] or ""
-- Lengthen the vowel if the penultimate syllable is short, otherwise use unchanged stem
if is_long(penultimate_syllable) then
gen_pl = gen_stem
else
gen_pl = remove_suffix(gen_stem, last_syllable) .. lengthen_vowel(last_syllable)
end
end
-- Special cases for genitive plural of specific words
if word == "čas" then
gen_pl = "čias"
elseif word == "raz" then
gen_pl = "ráz"
end
-- Determine instrumental plural ending
local ins_pl = "mi" -- Default instrumental plural ending is "mi"
if ultimate == "m"
or mobile_removed
or ends_with_any(word, {"o", "ius", "eus"})
or (penultimate == "m" and consonants_set[ultimate])
then
ins_pl = "ami"
end
-- Doublets for instrumental plural
if matches_any(word, {"schod", "schody", "fúz", "fúzy", "roh", "rohy",
"paroh", "parohy", "čary", "orech"})
then
set_forms(forms, {"ins_pl2"}, {stem .. "ami"})
elseif matches_any(word, {"zub", "dol", "most", "prst", "piest", "necht"}) then
ins_pl = "ami"
set_forms(forms, {"ins_pl2"}, {stem .. "mi"})
end
-- Populate forms table with singular and plural endings
append_endings(forms, word, stem,
gen_sg, "u", nil, loc_sg, "om",
"y", gen_pl, "om", nil, "och", ins_pl
)
-- Alternative genitive ending
if GEN_ENDING == "a/u" then
set_forms(forms, {"gen_sg2"}, {stem .. "u"})
elseif GEN_ENDING == "u/a" then
set_forms(forms, {"gen_sg2"}, {stem .. "a"})
end
-- Exceptions
if matches_any(word, {"sen", "dol"}) then
set_forms(forms, {"loc_pl2"}, {stem .. "ách"})
elseif word == "čas" then
set_forms(forms, {"gen_pl2"}, {"časov"})
end
-- Animal nouns
if GENDER == "m-anml" then
append_animal_singular(forms, stem)
if matches_any(word, {"vták", "vlk", "pes"}) or PLURAL2 then
local forms2, title2 = declensions["chlap"](word, stem .. gen_sg)
append_second_plural(forms, forms2["nom_pl"], forms2["gen_pl"], forms2["dat_pl"],
forms2["acc_pl"], forms2["loc_pl"], forms2["ins_pl"])
if matches_any(word, {"vták", "vlk", "pes"}) then
switch_plural_forms(forms) --changes the prefered forms
-- Determine alternative dative and locative for some nouns
if matches_any(word, {"vlk", "pes"}) then
set_forms(forms, {"dat_sg2", "loc_sg2"}, {stem .. "u", stem .. "u"})
end
end
end
end
return forms, title -- Return forms table and title for declension
end
declensions["stroj"] = function(word, genitive)
local forms = {}
local mobile_removed = false -- Tracks if a mobile vowel was removed
local words_with_no_mobile = {"kúpeľ", "červeň", "kameň", "jačmeň", "koreň",
"prameň", "prsteň", "jeleň"}
local first = get_first_char(word)
-- Determine the stem by removing mobile vowels if applicable
local stem = word
if (ends_with_any(word, {"ec", "iec", "[^ti]eľ", "oľ", "[^i]eň", "eť", "[^i]er", "el"})
or (ends_with(word, "ac") and (is_capital(first) or word == "desaterac")))
and not matches_any(word, words_with_no_mobile)
then
stem = remove_mobile_vowel(word)
mobile_removed = true -- Mark mobile vowel as removed
end
-- Adjust specific stems based on predefined mappings
stem = ({["dážď"] = "dažď", ["kôš"] = "koš", ["nôž"] = "nož",
["kôň"] = "koň", ["timbre"] = "timbr"})[word] or stem
-- Pluralia tantum
if NUMBER == "pl" then
stem = remove_suffix(word, "e")
end
-- Override if a specific argument is given
if genitive and NUMBER ~= "pl" then
stem = remove_last_char(genitive)
if stem == remove_last_char(remove_last_char(word)) .. get_last_char(word) then
mobile_removed = true
elseif stem == word then
mobile_removed = false
end
end
-- Set "mobile_removed" if genitive was set by the user
if ends_with(word, "e[lr]") and ends_with(stem, "[^e][lr]") then
mobile_removed = true
end
-- Title for the declension table
local title = "Declension of ''" .. word .. "'' (pattern " .. pattern_link("stroj") .. ")"
if GENDER == "m-anml" then
if PLURAL2 then
title = "Declension of ''" .. word .. "'' (patterns " .. pattern_link("chlap") .. " <small>(singular, plural 2)</small> and " .. pattern_link("stroj") .. " <small>(plural 1)</small>)"
else
title = "Declension of ''" .. word .. "'' (patterns " .. pattern_link("chlap") .. " <small>(singular)</small> and " .. pattern_link("stroj") .. " <small>(plural)</small>)"
end
elseif word == "cól" then
title = "Declension of ''" .. word .. "'' (pattern " .. pattern_link("stroj") .. " or " .. pattern_link("dub") .. ")"
end
-- Helper variables for analyzing the last and penultimate characters
local ultimate, penultimate = get_last_letters(stem)
local first = get_first_char(word)
-- Determine genitive singular ending
local gen_sg = "a"
if ((matches_any(TYPE, {"abstr", "material"})
or matches_any(GEN_ENDING, {"u", "u/a"}))
and not matches_any(word, {"jačmeň", "kameň", "olej", "loj", "vývoj", "rozvoj"}))
and not matches_any(GEN_ENDING, {"a", "a/u"})
then
gen_sg = "u"
end
-- Override genitive singular ending if a specific argument is given
if genitive then
gen_sg = get_last_char(genitive)
end
-- Determine genitive plural ending
local gen_pl = stem .. "ov" -- Default genitive plural ending is "ov"
-- Adjust genitive plural for specific place names and patterns
if is_capital(first) and (ends_with_any(word, {"áre", "iare"})
or word == "Tlmače")
then
local gen_stem = remove_last_char(word) -- Remove last character to get the base stem
local syllable_count, syllables = split_into_syllables(gen_stem)
local last_syllable = syllables[1]
local penultimate_syllable = syllables[2] or ""
-- Lengthen the vowel if the penultimate syllable is short, otherwise use unchanged stem
if is_long(penultimate_syllable) then
gen_pl = gen_stem
else
gen_pl = remove_suffix(gen_stem, last_syllable) .. lengthen_vowel(last_syllable)
end
end
-- Special cases for genitive plural of specific words
if word == "Ladce" then
gen_pl = "Ladiec"
elseif word == "Vráble" then
gen_pl = "Vrábeľ"
elseif word == "peniaze" then
gen_pl = "peňazí"
elseif matches_any(word, {"deň", "kôň", "groš"}) then
gen_pl = append_ending(stem, "í")
end
-- Determine instrumental plural ending
local ins_pl = "mi" -- Default instrumental plural ending is "mi"
if mobile_removed
or (obstruents_set[penultimate] and obstruents_set[ultimate])
then
ins_pl = "ami"
end
-- Populate forms table with singular and plural endings
append_endings(forms, word, stem,
gen_sg, "u", nil, "i", "om",
"e", gen_pl, "om", nil, "och", ins_pl
)
-- Alternative genitive ending
if GEN_ENDING == "a/u" then
set_forms(forms, {"gen_sg2"}, {stem .. "u"})
elseif GEN_ENDING == "u/a" then
set_forms(forms, {"gen_sg2"}, {stem .. "a"})
end
-- Exceptions
if matches_any(word, {"deň", "poldeň"}) then
set_forms(forms,
{"nom_pl", "acc_pl", "loc_sg2"},
{append_ending(stem, "i"), append_ending(stem, "i"), append_ending(stem, "e")}
)
elseif word == "ďateľ" then
set_forms(forms,
{"gen_sg2", "dat_sg2", "loc_sg2", "ins_sg2",
"nom_pl2", "gen_pl2", "dat_pl2", "acc_pl2", "loc_pl2", "ins_pl2"},
{"ďatľa", "ďatľovi", "ďatľovi", "ďatľom",
"ďatle", "ďatľov", "ďatľom", "ďatle", "ďatľoch", "ďatľami"}
)
elseif word == "cól" then
set_forms(forms,
{"loc_sg2", "nom_pl2", "acc_pl2"},
{"cóle", "cóly", "cóly"}
)
end
-- Animal nouns
if GENDER == "m-anml" then
append_animal_singular(forms, stem)
if PLURAL2 then
local forms2, title2 = declensions["chlap"](word, stem .. gen_sg)
append_second_plural(forms, forms2["nom_pl"], forms2["gen_pl"], forms2["dat_pl"],
forms2["acc_pl"], forms2["loc_pl"], forms2["ins_pl"])
end
end
return forms, title -- Return forms table and title for declension
end
declensions["žena"] = function(word, genitive)
local forms = {}
local stem = remove_suffix(word, "a")
local title = "Declension of ''" .. word .. "'' (pattern " .. pattern_link("žena") .. ")"
if ends_with(word, "ea") then
title = "Declension of ''" .. word .. "'' (pattern " .. pattern_link("žena") .. ", loanword ending with ''-ea'')"
end
-- Pluralia tantum or genitive
if NUMBER == "pl" then
stem = remove_suffix(word, "y")
end
if genitive and NUMBER ~= "pl" then
stem = remove_suffix(genitive, "y")
end
-- Split into syllables
local gen_pl = ""
local syllable_count, syllables = split_into_syllables(stem)
local word_syllable_count, word_syllables = split_into_syllables(word)
-- Get the last and second-to-last syllables for rule checks
local last_syllable = syllables[1]
local penultimate_syllable = syllables[2] or ""
local word_penultimate_syllable = word_syllables[2] or ""
local ultimate, penultimate = get_last_letters(stem)
-- 1. Add a vowel between the last two consonants of a cluster at the end of the stem
if ends_with_consonant_cluster(last_syllable) or word_syllable_count == 1 then
if (ultimate == "k" and not matches_any(word, {"banka"}))
or (ultimate == "b" and (TYPE == "der" or not sonorants_set[penultimate]))
or sonorants_set[ultimate]
or matches_any(word, {"farba", "karta", "buchta", "astma"})
or (obstruents_set[penultimate] and ultimate == "v")
then
if penultimate ~= "j" and (word_syllable_count == 1 or is_short(word_penultimate_syllable)) then
gen_pl = harden_last_consonant(remove_suffix(stem, ultimate)) .. "ie" .. ultimate
if matches_any(word, {"handra", "perla", "metla", "slivka",
"tehla", "vidly", "karta", "kvapka", "stovka", "doska"})
then
gen_pl = remove_suffix(stem, ultimate) .. "á" .. ultimate
set_forms(forms, {"gen_pl2"}, {harden_last_consonant(remove_suffix(stem, ultimate)) .. "ie" .. ultimate})
end
if matches_any(word, {"jamka", "kvapka"}) then
set_forms(forms, {"gen_pl2"}, {remove_suffix(stem, ultimate) .. "ô" .. ultimate})
end
if matches_any(word, {"astma"}) then
set_forms(forms, {"gen_pl2"}, {stem .. "í"})
end
elseif obstruents_set[ultimate] and (is_long(word_penultimate_syllable) or penultimate == "j") then
gen_pl = remove_suffix(stem, ultimate) .. "o" .. ultimate
elseif (sonorants_set[ultimate] or ultimate == "v") and (is_long(word_penultimate_syllable) or penultimate == "j") then
gen_pl = remove_suffix(stem, ultimate) .. "e" .. ultimate
if penultimate ~= "j" then
set_forms(forms, {"gen_pl2"}, {remove_suffix(stem, ultimate) .. "ie" .. ultimate})
end
end
end
end
-- 2. Add "í" if stem ends with a vowel or a soft consonant
if gen_pl == "" and (vowels_set[ultimate]
or matches_any(word, {"medaila", "pera", "kanva", "panva", "skepsa", "nuansa"}))
then
gen_pl = stem .. "í"
if matches_any(word, {"nuansa"}) then
set_forms(forms, {"gen_pl2"}, {remove_suffix(stem, last_syllable) .. lengthen_vowel(last_syllable)})
end
end
-- 3. Return the stem with no change if conditions match
if gen_pl == "" and (is_long(penultimate_syllable)
or ends_with_any(stem, {"tvor", "ov"})
or mw.ustring.find(last_syllable, "jo")
or (TYPE == "loan" and matches_any(get_vowel(last_syllable), {"e", "o"}))) then
gen_pl = stem
end
-- 4. Logic to use the lengthened vowel if conditions match
if gen_pl == "" and (not (ends_with_consonant_cluster(last_syllable) or vowels_set[ultimate])
or (ends_with_consonant_cluster(last_syllable) and sonorants_set[penultimate] and obstruents_set[ultimate])
or (ends_with_consonant_cluster(last_syllable) and matches_any(penultimate, {"c", "s", "z", "š", "ž", "p"}) and matches_any(ultimate, {"t", "d"})))
and not matches_any(word, {"šachta", "mzda"}) then
gen_pl = remove_suffix(stem, last_syllable) .. lengthen_vowel(last_syllable)
if mw.ustring.find(stem, "au[bcčdďfghjklľmnňpqrsštťvwxzž]$") then
gen_pl = remove_suffix(stem, "a" .. last_syllable) .. "á" .. last_syllable
end
end
-- 5. long -á- instead of -ia- for some loanwords
if matches_any(word, {"pyžama", "šachta"}) then
gen_pl = (word == "pyžama") and "pyžám" or "šácht"
if word == "šachta" then
set_forms(forms, {"gen_pl2"}, {"šachiet"})
end
end
-- 6. Exceptions
if word == "mzda" then
gen_pl = "miezd"
end
if gen_pl == "" then gen_pl = stem end
local dat_pl, loc_pl
if is_long(word_penultimate_syllable) then
dat_pl, loc_pl = "am", "ach"
else
dat_pl, loc_pl = "ám", "ách"
end
local dat_sg, loc_sg
if ultimate == "e" then
dat_sg, loc_sg = "i", "i"
else
dat_sg, loc_sg = "e", "e"
end
append_endings(forms, word, stem,
"y", dat_sg, "u", loc_sg, "ou",
"y", gen_pl, dat_pl, nil, loc_pl, "ami"
)
if matches_any(word, {"zora", "žiara", "žiabra"}) then
set_forms(forms,
{"nom_pl", "acc_pl"},
{stem .. "e", stem .. "e"}
)
end
return forms, title
end
declensions["gazdiná"] = function(word, genitive)
local forms = {}
local stem = remove_suffix(word, "á")
local title = "Declension of ''" .. word .. "'' (pattern " .. pattern_link("gazdiná") .. ")"
local syllable_count, syllables = split_into_syllables(stem)
local last_syllable = syllables[1]
local gen_pl = ""
if ends_with_consonant_cluster(last_syllable) then
gen_pl = mw.ustring.sub(stem, 1, -2) .. "ien"
else
gen_pl = remove_suffix(stem, last_syllable) .. lengthen_vowel(last_syllable)
end
append_endings(forms, word, stem,
"ej", "ej", "ú", "ej", "ou",
"é", gen_pl, "ám", nil, "ách", "ami"
)
return forms, title
end
declensions["ulica"] = function(word, genitive)
local forms = {}
local stem = remove_suffix(word, "a")
local title = "Declension of ''" .. word .. "'' (pattern " .. pattern_link("ulica") .. ")"
-- Pluralia tantum
if NUMBER == "pl" then
stem = remove_suffix(word, "e")
if ends_with_any(stem, {"d", "t", "n", "l"}) then
stem = soften_last_consonant(stem)
end
end
-- Split into syllables
local gen_pl = ""
local syllable_count, syllables = split_into_syllables(stem)
local word_syllable_count, word_syllables = split_into_syllables(word)
-- Get the last and second-to-last syllables for rule checks
local last_syllable = syllables[1]
local penultimate_syllable = syllables[2] or ""
local word_penultimate_syllable = word_syllables[2] or ""
local ultimate, penultimate = get_last_letters(stem)
local first = get_first_char(word)
-- 1. Add a vowel between the last two consonants of a cluster at the end of the stem
if ends_with_consonant_cluster(last_syllable) then
if (is_capital(first) and ends_with(word, "ce"))
or matches_any(word, {"dverce", "ovca", "fakľa", "drumbľa", "husle", "jasle",
"kachle", "hrable", "šabľa", "mašľa", "žemľa", "ríbezľa", "čerešňa", "sukňa", "višňa"})
then
gen_pl = harden_last_consonant(remove_suffix(stem, ultimate)) .. "ie" .. ultimate
if matches_any(word, {"drumbľa", "husle", "jasle", "kachle", "hrable",
"šabľa", "mašľa", "žemľa", "ríbezľa", "čerešňa", "sukňa", "višňa"}) then
set_forms(forms, {"gen_pl2"}, {append_ending(stem, "í")})
end
end
end
-- 2. Add "í" if stem ends with a vowel or a soft consonant
if gen_pl == "" and (matches_any(ultimate, {"dz", "dž", "ž", "ť", "ď", "j", "i", "y"})
or ends_with_any(word, {"nca", "oľa", "ôľa", "aľa", "ša", "ča"})
or matches_any(word, {"liace", "pasca", "páľa", "trúbeľa", "mrľa", "konope", "večera", "rozopra"})
or (matches_any(ultimate, {"ľ"}) and consonants_set[penultimate])
or (matches_any(ultimate, {"ň"}) and penultimate ~= "y"))
and not matches_any(word, {"papuča", "priča", "hrča", "paprča",
"garniža", "fakľa", "skriňa", "sviňa", "abatiša", "čaša", "fľaša",
"Krkonoše", "ríša", "skrýša"})
then
gen_pl = append_ending(stem, "í")
end
-- 3. Logic to use the lengthened vowel if conditions match
if gen_pl == "" and not ends_with_consonant_cluster(last_syllable) and not vowels_set[ultimate]
and ((ends_with(word, "ca") and TYPE ~= "der")
or ends_with_any(word, {"yňa", "uľa"})
or matches_any(word, {"papuča", "priča", "hrča", "chvíľa", "míľa", "košeľa", "nedeľa",
"guľa", "hoľa", "homoľa", "skriňa", "sviňa", "abatiša", "čaša", "fľaša",
"Krkonoše", "ríša", "skrýša", "garniža", "Hybe", "dvere", "kuša", "moruša"}))
then
if is_long(penultimate_syllable) then
gen_pl = stem
else
gen_pl = remove_suffix(stem, last_syllable) .. lengthen_vowel(last_syllable)
if matches_any(word, {"homoľa", "kuša", "moruša", "hrča", "paprča",
"guľa", "hoľa"})
then
set_forms(forms, {"gen_pl2"}, {append_ending(stem, "í")})
end
end
end
if gen_pl == "" then gen_pl = stem end
local dat_pl, loc_pl
if ends_with_any(stem, {"i", "y"}) or word == "pizza" then
dat_pl, loc_pl = "ám", "ách"
elseif is_long(word_penultimate_syllable) or ends_with(stem, "j") then
dat_pl, loc_pl = "am", "ach"
else
dat_pl, loc_pl = "iam", "iach"
end
append_endings(forms, word, stem,
"e", "i", "u", "i", "ou",
"e", gen_pl, dat_pl, nil, loc_pl, "ami"
)
if word == "rozopra" then
set_forms(forms,
{"dat_pl2", "loc_pl2"},
{"rozoprám", "rozoprách"}
)
elseif word == "dvere" then
set_forms(forms,
{"dat_pl", "loc_pl", "gen_pl2", "ins_pl2"},
{"dverám", "dverách", "dverí", "dvermi"}
)
end
return forms, title
end
declensions["irregular"] = function(word, genitive)
local forms = {}
local title = "Declension of ''" .. word .. "'' (irregular)"
if matches_any(word, {"mať", "mater", "mati"}) then
local stem = "mater"
append_endings(forms, word, stem,
"e", "i", "", "i", "ou",
"e", stem .. "í", "iam", nil, "iach", "ami"
)
local accusative = (word == "mater") and {"mater", "mať"} or {"mať", "mater"}
set_forms(forms, {"acc_sg", "acc_sg2"}, accusative)
elseif ends_with(word, "pani") then
local stem = remove_suffix(word, "ni") .. "ň"
append_endings(forms, word, stem,
"ej", "ej", "iu", "ej", "ou",
"ie", stem .. "í", "iam", nil, "iach", "iami"
)
end
return forms, title
end
declensions["dlaň"] = function(word, genitive)
local forms = {}
local words_with_mobile_vowel = {"faloš", "osuheľ", "siheľ", "myseľ"}
local stem = (matches_any(word, words_with_mobile_vowel) or ends_with(word, "eň"))
and remove_mobile_vowel(word) or word
local title = "Declension of ''" .. word .. "'' (pattern " .. pattern_link("dlaň") .. ")"
local syllable_count, syllables = split_into_syllables(word)
local last_syllable = syllables[1]
local penultimate_syllable = syllables[2] or ""
local last_char = get_last_char(word)
local dat_pl, loc_pl
if is_long(penultimate_syllable) or last_char == "j" then
dat_pl, loc_pl = "am", "ach"
elseif word == "kader" then
dat_pl, loc_pl = "ám", "ách"
else
dat_pl, loc_pl = "iam", "iach"
end
append_endings(forms, word, stem,
"e", "i", nil, "i", "ou",
"e", append_ending(stem, "í"), dat_pl, nil, loc_pl, "ami"
)
if matches_any(word, {"myseľ", "tvár", "hneď", "raž"}) then
set_forms(forms, {"gen_sg2"}, {append_ending(stem, "i")})
end
return forms, title
end
declensions["kosť"] = function(word, genitive)
local forms = {}
local words_with_mobile_vowel = {"voš", "lož", "Ves", "cirkev", "reďkev"}
local stem = (matches_any(word, words_with_mobile_vowel))
and remove_mobile_vowel(word) or word
if word == "česť" then stem = "cť" end
local title = "Declension of ''" .. word .. "'' (pattern " .. pattern_link("kosť") .. ")"
append_endings(forms, word, stem,
"i", "i", nil, "i", "ou",
"i", append_ending(stem, "í"), "iam", nil, "iach", "ami"
)
if word == "hrsť" then
set_forms(forms,
{"nom_pl", "acc_pl"},
{"hrste", "hrste"}
)
elseif word == "lesť" then
append_alternative_singular(forms, "ľsti", "ľsti", nil, "ľsti", "ľsťou")
append_alternative_plural(forms, "ľsti", "ľstí", "ľstiam", nil, "ľstiach", "ľsťami")
elseif matches_any(word, {"cirkev", "reďkev"}) then
set_forms(forms,
{"dat_pl", "loc_pl"},
{stem .. "ám", stem .. "ách"}
)
end
return forms, title
end
declensions["mesto"] = function(word, genitive)
local forms = {}
local ultimate, penultimate = get_last_letters(word)
local special_type = ""
local stem = remove_suffix(word, "o")
if penultimate == "u" and ultimate == "m" then
stem = remove_suffix(word, "um")
special_type = ", of Latin origin ending with ''-um''"
elseif penultimate == "o" and ultimate == "n" then
stem = remove_suffix(word, "on")
special_type = ", of Greek origin ending with ''-on''"
elseif penultimate == "m" and ultimate == "ä" then
stem = remove_suffix(word, "ä") .. "en"
special_type = ", archaic type ending with ''-mä''"
end
-- Pluralia tantum
if NUMBER == "pl" then
stem = remove_suffix(word, ultimate)
end
local title = "Declension of ''" .. word .. "'' (pattern " .. pattern_link("mesto") .. special_type .. ")"
local stem_ultimate, stem_penultimate = get_last_letters(stem)
local loc_sg = "e"
if vowels[stem_ultimate] or matches_any(stem_ultimate, {"k", "g", "ch", "h"}) then
loc_sg = "u"
elseif matches_any(word, {"vnútro", "nebo"}) then
loc_sg = "i"
end
-- Split into syllables
local gen_pl = ""
local syllable_count, syllables = split_into_syllables(stem)
local word_syllable_count, word_syllables = split_into_syllables(word)
-- Get the last and second-to-last syllables for rule checks
local last_syllable = syllables[1]
local penultimate_syllable = syllables[2] or ""
local word_penultimate_syllable = word_syllables[2] or ""
-- 1. Add a vowel between the last two consonants of a cluster at the end of the stem
if ends_with_consonant_cluster(last_syllable) then
if matches_any(word, {"jedlo", "predjedlo", "jutro", "vrecko", "brvno"}) then
if matches_any(word, {"jedlo", "predjedlo", "jutro"}) then
gen_pl = remove_suffix(stem, stem_ultimate) .. "á" .. stem_ultimate
else
gen_pl = remove_suffix(stem, stem_ultimate) .. "ie" .. stem_ultimate
set_forms(forms, {"gen_pl2"}, {remove_suffix(stem, stem_ultimate) .. "á" .. stem_ultimate})
end
elseif sonorants_set[stem_ultimate] or ends_with_any(last_syllable, {"stv", "ctv", "íčk", "ečk", "očk"}) or TYPE == "dim" then
if stem_penultimate ~= "j" and (word_syllable_count == 1 or ends_with_any(last_syllable, {"stv", "ctv"}) or is_short(word_penultimate_syllable)) then
gen_pl = remove_suffix(stem, stem_ultimate) .. "ie" .. stem_ultimate
elseif obstruents_set[stem_ultimate] and (is_long(word_penultimate_syllable) or stem_penultimate == "j") then
gen_pl = remove_suffix(stem, stem_ultimate) .. "o" .. stem_ultimate
elseif sonorants_set[stem_ultimate] and (is_long(word_penultimate_syllable) or stem_penultimate == "j") then
gen_pl = remove_suffix(stem, stem_ultimate) .. "e" .. stem_ultimate
if stem_penultimate ~= "j" then
set_forms(forms, {"gen_pl2"}, {remove_suffix(stem, stem_ultimate) .. "ie" .. stem_ultimate})
end
end
end
end
-- 2. Add "í" if stem ends with a vowel or a soft consonant
if gen_pl == "" and (vowels_set[stem_ultimate] or soft_consonants_set[stem_ultimate]) then
gen_pl = stem .. "í"
end
-- 3. Return the stem with no change if conditions match
if gen_pl == "" and (is_long(penultimate_syllable)
or ends_with_any(stem, {"vojsk", "ov"})
or (TYPE == "loan" and matches_any(get_vowel(last_syllable), {"e", "o"}))) then
gen_pl = stem
end
-- 4. Logic to use the lengthened vowel if conditions match
if gen_pl == "" and (not (ends_with_consonant_cluster(last_syllable) or vowels_set[stem_ultimate])
or (ends_with_consonant_cluster(last_syllable) and obstruents_set[stem_ultimate])) then
gen_pl = remove_suffix(stem, last_syllable) .. lengthen_vowel(last_syllable)
end
local nom_pl = "á"
if NUMBER == "pl" then
nom_pl = ultimate
end
if gen_pl == "" then gen_pl = stem end
if is_long(word_penultimate_syllable) or word == "jojo" then
append_endings(forms, word, stem,
"a", "u", nil, loc_sg, "om",
"a", gen_pl, "am", nil, "ach", "ami"
)
else
append_endings(forms, word, stem,
"a", "u", nil, loc_sg, "om",
nom_pl, gen_pl, "ám", nil, "ách", "ami"
)
end
if matches_any(word, {"oko", "ucho"}) then
local pl_stem = (word == "oko") and "oč" or "uš"
append_second_plural(forms, pl_stem .. "i", pl_stem .. "í", pl_stem .. "iam",
nil, pl_stem .. "iach", pl_stem .. "ami")
switch_plural_forms(forms) --changes the prefered forms
set_forms(forms,
{"gen_pl2"},
{pl_stem .. "ú"}
)
elseif word == "nebo" then
set_forms(forms,
{"nom_pl", "nom_pl2", "gen_pl", "dat_pl", "dat_pl2", "acc_pl",
"acc_pl2", "loc_pl", "loc_pl2", "ins_pl"},
{"nebesá", "nebesia", "nebies", "nebesám", "nebesiam", "nebesá",
"nebesia", "nebesách", "nebesiach", "nebesami"}
)
end
return forms, title
end
declensions["srdce"] = function(word, genitive)
local forms = {}
local stem = remove_suffix(word, "e")
if ends_with(word, "ě") then
stem = remove_suffix(word, "ě")
end
local title = "Declension of ''" .. word .. "'' (pattern " .. pattern_link("srdce") .. ")"
if NUMBER == "pl" then
if ends_with(word, "ia") then
stem = remove_suffix(word, "ia")
else
stem = remove_suffix(word, "a")
end
end
if ends_with(stem, "[dtnl]") then
stem = soften_last_consonant(stem)
end
-- Split into syllables
local gen_pl = ""
local syllable_count, syllables = split_into_syllables(stem)
local word_syllable_count, word_syllables = split_into_syllables(word)
-- Get the last and second-to-last syllables for rule checks
local last_syllable = syllables[1]
local penultimate_syllable = syllables[2] or ""
local word_penultimate_syllable = word_syllables[2] or ""
local last_char = get_last_char(stem)
local stem_ultimate, stem_penultimate = get_last_letters(stem)
-- 1 & 2. Lengthen the last syllable's vowel if conditions apply,
-- otherwise leave the stem unchanged if penultimate syllable is long
if not ends_with_consonant_cluster(last_syllable)
or (ends_with(word, "ce") and matches_any(get_vowel(word_penultimate_syllable), {"r", "l"}) and TYPE ~= "dim")
or ends_with(word, "ište") then
if is_long(penultimate_syllable) then
gen_pl = stem -- If penultimate syllable of the stem is long, use the stem unchanged
else
-- Otherwise, lengthen the last syllable's vowel
gen_pl = remove_suffix(stem, last_syllable) .. lengthen_vowel(last_syllable)
end
end
-- 3. Add a vowel between the last two consonants if the conditions apply
if gen_pl == "" and (word == "citoslovce" or word == "vajce"
or ends_with(word, "ce")) then
if stem_penultimate ~= "j" and is_short(word_penultimate_syllable) then
gen_pl = remove_suffix(stem, stem_ultimate) .. "ie" .. stem_ultimate
elseif is_long(word_penultimate_syllable) or stem_penultimate == "j" then
gen_pl = remove_suffix(stem, stem_ultimate) .. "e" .. stem_ultimate
end
end
-- 4. Add "í" to the stem for specific words
if matches_any(word, {"more", "oje", "pole", "lože"}) then
gen_pl = harden_last_consonant(stem) .. "í"
end
-- Fallback: if none of the rules apply, return the original stem
if gen_pl == "" then gen_pl = stem end
if is_long(word_penultimate_syllable) or ends_with(stem, "j") then
append_endings(forms, word, stem,
"a", "u", nil, "i", "om",
"a", gen_pl, "am", nil, "ach", "ami"
)
else
append_endings(forms, word, stem,
"a", "u", nil, "i", "om",
"ia", gen_pl, "iam", nil, "iach", "ami"
)
end
return forms, title
end
declensions["vysvedčenie"] = function(word, genitive)
local forms = {}
local stem = remove_suffix(word, "ie")
if ends_with(word, "í") then
stem = remove_suffix(word, "í")
end
local title = "Declension of ''" .. word .. "'' (pattern " .. pattern_link("vysvedčenie") .. ")"
append_endings(forms, word, stem,
"ia", "iu", nil, "í", "ím",
"ia", stem .. "í", "iam", nil, "iach", "iami"
)
if word == "storočie" then
append_second_plural(forms, "stáročia", "stáročí", "stáročiam",
nil, "stáročiach", "stáročiami")
end
return forms, title
end
declensions["dievča"] = function(word, genitive)
local forms = {}
local stem = remove_suffix(word, "a")
if ends_with(word, "ä") then
stem = remove_suffix(word, "ä")
end
local ending = get_last_char(word)
local title = "Declension of ''" .. word .. "'' (pattern " .. pattern_link("dievča") .. ")"
local gen_pl = ""
local syllable_count, syllables = split_into_syllables(word)
local last_syllable = syllables[1]
local penultimate_syllable = syllables[2] or ""
if is_long(penultimate_syllable) then
gen_pl = stem .. ending .. "t"
else
gen_pl = append_ending(stem, "iat")
end
append_endings(forms, word, stem,
ending .. "ťa", ending .. "ťu", nil, ending .. "ti", ending .. "ťom",
ending .. "tá", gen_pl, ending .. "tám", nil, ending .. "tách", ending .. "tami"
)
local pl_stem = harden_last_consonant(stem)
append_second_plural(forms, pl_stem .. "ence", pl_stem .. "eniec", pl_stem .. "encom",
nil, pl_stem .. "encoch", pl_stem .. "encami")
if matches_any(word, {"kura", "strídža", "drumblence", "gajdence", "deťúrence"}) then
switch_plural_forms(forms) --changes the prefered forms
unset_alt_forms(forms)
elseif matches_any(word, {"páža", "knieža", "kurča", "plánča", "pôrča",
"zviera", "pachoľa", "mláďa", "dúpä", "chlápä", "žieňa", "nemluvňa"}) then
unset_alt_forms(forms)
elseif matches_any(word, {"prasa", "teľa", "šteňa"}) then
local pl_stem = (word == "šteňa") and "šten" or stem
append_second_plural(forms, pl_stem .. "ce", append_ending(pl_stem, "iec"),
pl_stem .. "com", nil, pl_stem .. "coch", pl_stem .. "cami")
switch_plural_forms(forms) --changes the prefered forms
elseif word == "dieťa" then
set_forms(forms,
{"nom_pl", "gen_pl", "dat_pl", "acc_pl", "loc_pl", "ins_pl"},
{"deti", "detí", "deťom", "deti", "deťoch", "deťmi"}
)
unset_alt_forms(forms)
end
return forms, title
end
declensions["adjective"] = function(word, genitive)
local forms = {}
local title = "Declension of ''" .. word .. "'' (adjective declension)"
local ultimate, penultimate = get_last_letters(word)
local lemma_form = word
if NUMBER == "pl" then
if ends_with(word, "é") then
lemma_form = remove_suffix(word, "é") .. "ý"
elseif ends_with(word, "ie") then
lemma_form = remove_suffix(word, "ie") .. "í"
elseif ends_with_any(word, {"ove", "ine"}) then
lemma_form = remove_suffix(word, "e")
elseif ends_with(word, "e") then
lemma_form = remove_suffix(word, "e") .. "y"
elseif ends_with(word, "í") and not soft_consonants_set[penultimate] then
lemma_form = remove_suffix(word, "í") .. "ý"
elseif ends_with(word, "i") and not soft_consonants_set[penultimate] then
lemma_form = remove_suffix(word, "i") .. "y"
end
else
if GENDER == "f" or GENDER == "n" then
if ends_with_any(word, {"á", "é"}) then
lemma_form = remove_last_char(word) .. "ý"
elseif ends_with_any(word, {"ia", "ie"}) then
lemma_form = remove_suffix(remove_last_char(word), "i") .. "í"
elseif ends_with_any(word, {"ova", "ovo", "ina", "ino"}) then
lemma_form = remove_last_char(word)
elseif ends_with_any(word, {"a", "e"}) then
lemma_form = remove_last_char(word) .. "y"
end
end
end
local forms_raw = m_adj.do_generate_forms({pagename=lemma_form}, "adjective").forms
set_forms(forms,
{"nom_sg"},
{forms_raw["nom_" .. get_first_char(GENDER)][1].form}
)
-- Singular
if GENDER == "f" then
set_forms(forms,
{"gen_sg", "dat_sg", "loc_sg", "ins_sg"},
{
forms_raw["gen_f"][1].form, forms_raw["dat_f"][1].form,
forms_raw["loc_f"][1].form, forms_raw["ins_f"][1].form
}
)
else
set_forms(forms,
{"gen_sg", "dat_sg", "loc_sg", "ins_sg"},
{
forms_raw["gen_mn"][1].form, forms_raw["dat_mn"][1].form,
forms_raw["loc_mn"][1].form, forms_raw["ins_mn"][1].form
}
)
end
-- special accusative singular
if GENDER == "m-pr" or GENDER == "m-anml" then
set_forms(forms,
{"acc_sg"},
{forms_raw["acc_m_an"][1].form}
)
elseif GENDER == "m-in" then
set_forms(forms,
{"acc_sg"},
{forms_raw["acc_m_in"][1].form}
)
else
set_forms(forms,
{"acc_sg"},
{forms_raw["acc_" .. get_first_char(GENDER)][1].form}
)
end
-- Plural
set_forms(forms,
{"gen_pl", "dat_pl", "loc_pl", "ins_pl"},
{
forms_raw["gen_p"][1].form, forms_raw["dat_p"][1].form,
forms_raw["loc_p"][1].form, forms_raw["ins_p"][1].form
}
)
if GENDER == "m-pr" then
set_forms(forms,
{"nom_pl", "acc_sg", "acc_pl"},
{forms_raw["nom_mp_an"][1].form, forms_raw["acc_m_an"][1].form, forms_raw["acc_mp_an"][1].form}
)
else
set_forms(forms,
{"nom_pl", "acc_pl"},
{forms_raw["nom_fnp"][1].form, forms_raw["acc_fnp"][1].form}
)
end
return forms, title
end
declensions["indeclinable"] = function(word, genitive)
local forms = {}
local title = "Declension of ''" .. word .. "'' (indeclinable)"
local cases = {"nom", "gen", "dat", "acc", "loc", "ins"} -- List of cases
local numbers = {"sg", "pl"}
for _, case in ipairs(cases) do
for _, number in ipairs(numbers) do
local form_key = case .. "_" .. number
forms[form_key] = word
end
end
-- Surnames ending with -ů, -eje, -oje and -ovie
if TYPE == "surn" and ends_with_any(word, {"ů", "eje", "e", "oje", "ovie"}) then
append_second_plural(forms, word .. "ovci", word .. "ovcov", word .. "ovcom",
word .. "ovcov", word .. "ovcoch", word .. "ovcami")
end
return forms, title
end
--[=[
Partial declination functions
]=]--
function check_gender(gender)
if gender == nil then
return error("No gender entered. Please pass one of these values as parameter 1: m-pr, m-anml, m-in, f, n.")
elseif allowed_genders_set[gender] then
return gender
else
return error("Unknown gender. Please pass one of these values as parameter 1: m-pr, m-anml, m-in, f, n.")
end
end
function determine_pattern(word, genitive)
local pattern
local phon_word = (PRONUNCIATION) and PRONUNCIATION or word
local ultimate, penultimate = get_last_letters(phon_word)
local first = get_first_char(phon_word)
local dlan_endings = m_table.listToSet({"ň", "č", "ž", "ľ", "ď", "j", "š", "m", "z", "dz", "x"})
local dlan_exceptions_neg = m_table.listToSet({"reč", "seč", "lož", "beľ", "soľ", "mlaď", "meď", "myš", "voš"})
local dlan_exceptions_pos = m_table.listToSet({"obec", "pec", "čelusť", "kysť", "päsť", "Provence"})
local dlan_end_with_r_t = m_table.listToSet({"kader", "neter", "šír", "tvár",
"činovať", "drobäť", "droboť", "hať", "hrochoť", "Hrochoť", "hrsť", "inovať", "labuť",
"niť", "obeť", "paruť", "pažiť", "pečať", "perepúť", "perleť", "peruť", "pípeť", "plť",
"postať", "prť", "púť", "sieť", "sihoť", "stať", "štvrť", "trať", "úvrať", "vňať",
"violeť", "záhať", "žlť"})
local stroj_exceptions_pos = {"timbre", "cól", "gáfor", "hámor", "kôpor",
"kufor", "Pôtor", "šiator", "pedál", "sandál", "kanál", "peniaz", "daniel"}
local stroj_exceptions_neg = {"nesvár", "nešvár", "pár", "suchopár", "svár", "ker"}
if matches_any(ultimate, {"r", "l"}) and penultimate == "e" and not ends_with(phon_word, "ier") and genitive == nil then
genitive = phon_word .. "a"
end
if mw.ustring.find(phon_word, " ") then
return "indeclinable"
end
if NUMBER == "pl" then
if matches_any(ultimate, {"é", "i", "í"}) or (ultimate == "e" and ends_with(genitive, "ch")) then
pattern = "adjective"
elseif GENDER == "m-in" then
if ultimate == "y" then
pattern = "dub"
else
pattern = "stroj"
end
elseif GENDER == "f" then
if ultimate == "y" then
pattern = "žena"
else
pattern = "ulica"
end
elseif GENDER == "n" then
if ultimate == "á" then
pattern = "mesto"
elseif penultimate == "i" and ultimate == "a" then
pattern = "srdce"
else
if soft_consonants_set[penultimate] then
pattern = "srdce"
else
pattern = "mesto"
end
end
end
else
if GENDER == "m-pr" then
if (matches_any(ultimate, {"y", "ý", "i", "í"}) and not ends_with_any(genitive, {"[yií]ho", "a"})
and not (TYPE == "surn" and matches_any(penultimate .. ultimate, {"ay", "ai"})))
or (matches_any(penultimate .. ultimate, {"ov", "in"}) and ends_with_any(genitive, {"ovho", "inho"}))
then
pattern = "adjective"
elseif ultimate == "a"
or (TYPE == "surn" and matches_any(ultimate, {"e", "ě"}) and (penultimate .. ultimate == "ně" or ends_with(genitive, "u")))
then
pattern = "hrdina"
elseif (matches_any(ultimate, {"i", "í", "y", "e", "é", "ä"})
or (TYPE == "name" and matches_any(ultimate, {"ü", "ö", "ő"}))
or matches_any(phon_word, {"Hrabě", "Poupě"}))
and not (TYPE == "surn" and (ends_with_any(phon_word, {"ay", "ai", "eje", "oje", "ovie"}) or ends_with(genitive, "a")))
then
pattern = "kuli"
elseif TYPE == "surn" and ends_with_any(phon_word, {"ů", "eje", "oje", "ovie"}) then
pattern = "indeclinable"
else
pattern = "chlap"
end
elseif GENDER == "m-in" or GENDER == "m-anml" then
if matches_any(ultimate, {"y", "ý", "i", "í"})
or (matches_any(penultimate .. ultimate, {"ov", "in"}) and ends_with(genitive, "ho"))
then
pattern = "adjective"
elseif (soft_consonants_set[ultimate]
or (matches_any(ultimate, {"r", "l"}) and penultimate == "e"
and (ends_with(genitive, "[^e][rl]a")))
or ends_with_any(phon_word, {"ár", "iar", "ier"})
or matches_any(phon_word, stroj_exceptions_pos))
and not matches_any(phon_word, stroj_exceptions_neg)
then
pattern = "stroj"
else
pattern = "dub"
end
elseif GENDER == "f" then
if matches_any(phon_word, {"gazdiná", "švagriná", "testiná", "ujčiná", "stryná",
"kňažná", "kráľovná", "cisárovná", "cárovná", "šľachtičná", "princezná"
}) then
pattern = "gazdiná"
elseif ultimate == "a" and not ends_with(genitive, "ej") then
if soft_consonants_set[penultimate] or matches_any(penultimate, {"i", "y"}) or matches_any(phon_word, {"rozopra", "konopa", "večera"}) then
pattern = "ulica"
else
pattern = "žena"
end
elseif matches_any(ultimate, {"a", "á"})
or (matches_any(penultimate .. ultimate, {"ova", "ina"}) and ends_with(genitive, "ej"))
then
pattern = "adjective"
elseif ends_with(phon_word, "pani") or matches_any(phon_word, {"Mať", "mať", "mater", "mati"}) then
pattern = "irregular"
else
if not dlan_exceptions_neg[phon_word]
and (dlan_endings[ultimate] or dlan_exceptions_pos[phon_word]
or dlan_end_with_r_t[phon_word] or (penultimate == "š" and ultimate == "ť" and not is_capital(first)))
then
pattern = "dlaň"
else
pattern = "kosť"
end
end
if genitive then
local g_ultimate = get_last_char(genitive)
if g_ultimate == "y" then
pattern = "žena"
elseif g_ultimate == "i" then
pattern = "kosť"
end
end
elseif GENDER == "n" then
if (ultimate == "o" or (penultimate == "u" and ultimate == "m")
or (penultimate == "o" and ultimate == "n")
or (penultimate == "m" and ultimate == "ä"))
and not ends_with(genitive, "ho")
then
pattern = "mesto"
elseif (penultimate == "i" and ultimate == "e")
or ultimate == "í"
then
pattern = "vysvedčenie"
elseif (ultimate == "e" or ultimate == "ě")
and not ends_with(genitive, "ho")
then
pattern = "srdce"
elseif matches_any(ultimate, {"a", "ä"}) then
pattern = "dievča"
elseif matches_any(ultimate, {"e", "é"})
or matches_any(penultimate .. ultimate, {"ovo", "ino"})
then
pattern = "adjective"
else
pattern = "indeclinable"
end
end
end
return pattern
end
function append_ending(stem1, ending)
if matches_any(get_first_char(ending), {"e", "i", "é", "í"}) then
return harden_last_consonant(stem1) .. ending
else
return stem1 .. ending
end
end
function append_endings(forms, word, stem, end2, end3, end4, end5, end6, end7, gen_pl, end9, end10, end11, end12)
forms["nom_sg"] = word
forms["gen_sg"] = append_ending(stem, end2)
forms["dat_sg"] = append_ending(stem, end3)
if GENDER == "m-pr"
or (GENDER == "f"
and (ends_with_any(word, {"pani", "a", "á"})
or matches_any(word, {"Mať", "mať", "mater", "mati"})))
then
forms["acc_sg"] = append_ending(stem, end4)
else
forms["acc_sg"] = word
end
forms["loc_sg"] = append_ending(stem, end5)
forms["ins_sg"] = append_ending(stem, end6)
local nom_pl = stem
if ends_with_any(stem, {"k", "ch"}) and matches_any(GENDER, {"m-pr", "m-anml"}) and end7 == "i" then
if ends_with(stem, "k") then
nom_pl = remove_suffix(stem, "k") .. "c"
else
nom_pl = remove_suffix(stem, "ch") .. "s"
end
end
forms["nom_pl"] = append_ending(nom_pl, end7)
forms["gen_pl"] = gen_pl
forms["dat_pl"] = append_ending(stem, end9)
if GENDER == "m-pr"
or end10
or (GENDER == "f"
and (ends_with_any(word, {"pani"})
or matches_any(word, {"Mať", "mať", "mater", "mati"})))
then
forms["acc_pl"] = append_ending(stem, end10)
else
forms["acc_pl"] = forms["nom_pl"]
end
forms["loc_pl"] = append_ending(stem, end11)
forms["ins_pl"] = append_ending(stem, end12)
end
function set_forms(forms, indices, values)
for i = 1, #indices do
forms[indices[i]] = values[i]
end
end
function switch_plural_forms(forms)
local indices = {"nom_pl", "gen_pl", "dat_pl", "acc_pl", "loc_pl", "ins_pl"}
for _, index in ipairs(indices) do
forms[index], forms[index .. "_alt"] = forms[index .. "_alt"], forms[index]
end
end
function unset_alt_forms(forms)
for key in pairs(forms) do
if mw.ustring.find(key, "_alt$") then
forms[key] = nil
end
end
end
function append_alternative_singular(forms, form2, form3, form4, form5, form6)
forms["gen_sg2"] = (forms["gen_sg"] ~= form2) and form2
forms["dat_sg2"] = (forms["dat_sg"] ~= form3) and form3
if matches_any(GENDER, {"m-pr", "m-anml"})
or (GENDER == "f"
and (ends_with_any(word, {"pani", "a", "á"})
or matches_any(word, {"Mať", "mať", "mater", "mati"})))
then
forms["acc_sg2"] = (forms["acc_sg"] ~= form4) and form4
end
forms["loc_sg2"] = (forms["loc_sg"] ~= form5) and form5
forms["ins_sg2"] = (forms["ins_sg"] ~= form6) and form6
end
function append_alternative_plural(forms, form1, form2, form3, form4, form5, form6)
forms["nom_pl2"] = (forms["nom_pl"] ~= form2) and form1
forms["gen_pl2"] = (forms["gen_pl"] ~= form2) and form2
forms["dat_pl2"] = (forms["dat_pl"] ~= form2) and form3
if form4 ~= nil and forms["acc_pl"] ~= form4 then
forms["acc_pl2"] = form4
elseif forms["acc_pl"] ~= form1 then
forms["acc_pl2"] = form1
end
forms["loc_pl2"] = (forms["loc_pl"] ~= form2) and form5
forms["ins_pl2"] = (forms["ins_pl"] ~= form2) and form6
end
function append_second_plural(forms, form1, form2, form3, form4, form5, form6)
forms["nom_pl_alt"] = form1
forms["gen_pl_alt"] = form2
forms["dat_pl_alt"] = form3
if form4 ~= nil then
forms["acc_pl_alt"] = form4
else
forms["acc_pl_alt"] = forms["nom_pl_alt"]
end
forms["loc_pl_alt"] = form5
forms["ins_pl_alt"] = form6
end
function append_animal_singular(forms, stem)
set_forms(forms,
{"gen_sg", "dat_sg", "acc_sg", "loc_sg"},
{
append_ending(stem, "a"),
append_ending(stem, "ovi"),
append_ending(stem, "a"),
append_ending(stem, "ovi")
}
)
end
function get_last_char(str)
local last = mw.ustring.sub(str, -1, -1)
return last
end
function get_first_char(str)
local first = mw.ustring.sub(str, 1, 1)
return first
end
function remove_last_char(str)
local stem = mw.ustring.sub(str, 1, -2)
return stem
end
function is_capital(str)
return mw.ustring.find(str, "[A-Z]")
end
function get_last_letters(word)
local ultimate = get_last_char(word)
local penultimate = get_last_char(remove_last_char(word))
local antepenultimate = get_last_char(remove_last_char(remove_last_char(word)))
if (penultimate == "c" and ultimate == "h")
or (penultimate == "d" and (ultimate == "z" or ultimate == "ž"))
then
ultimate = penultimate .. ultimate
penultimate = antepenultimate
antepenultimate = get_last_char(remove_last_char(remove_last_char(remove_last_char(word))))
end
if (antepenultimate == "c" and penultimate == "h")
or (antepenultimate == "d" and (penultimate == "z" or penultimate == "ž"))
then
penultimate = antepenultimate .. penultimate
end
if penultimate == "v" and consonants_set[ultimate] and not matches_any(ultimate, {"r", "l", "ŕ", "ĺ"}) then
penultimate = "ʋ"
end
return ultimate, penultimate
end
function ends_with(word, suffix)
-- Check if suffix matches the end of word
word = (word ~= nil) and word or ""
return mw.ustring.find(word, suffix .. "$") ~= nil
end
function ends_with_any(word, suffixes)
for _, suffix in ipairs(suffixes) do
if ends_with(word, suffix) then
return true -- Return true if any suffix matches
end
end
return false -- Return false if no suffix matches
end
function matches_any(value, list)
for _, item in ipairs(list) do
if value == item then
return true
end
end
return false
end
function get_vowel(syllable)
-- Define patterns for diphthongs and vowels
local diphthong_pattern = "ia|ie|iu|ô"
local vowel_pattern = "[aeiouyáéíóúýäöőüű]"
local syllabic_consonant_pattern = "[rl]"
-- Check for a diphthong first
local diphthong = mw.ustring.match(syllable, diphthong_pattern)
if diphthong then
return diphthong
end
-- Check for a single vowel
local vowel = mw.ustring.match(syllable, vowel_pattern)
if vowel then
return vowel
end
-- Check for a syllabic consonant if no vowel is found
local syllabic_consonant = mw.ustring.match(syllable, syllabic_consonant_pattern)
if syllabic_consonant then
return syllabic_consonant
end
return nil -- Return nil if no vowel or syllabic consonant is found
end
-- Function to split a word into characters and bigraphs
function split_into_letter_units(word)
local units = {}
local i = 1
local length = mw.ustring.len(word)
while i <= length do
local two_char = mw.ustring.sub(word, i, i + 1)
-- Check if the two-character sequence is a bigraph or diphthong
if bigraphs_set[two_char] or diphthongs_set[two_char] then
table.insert(units, two_char)
i = i + 2
else
-- If not a bigraph/diphthong, treat it as a single character
table.insert(units, mw.ustring.sub(word, i, i))
i = i + 1
end
end
return units
end
-- check if a unit is a vowel or syllabic element
function is_vowel(unit, prev_unit, next_unit)
if diphthongs_set[unit] then return true end
if vowels_set[unit] then return true end
-- Check if 'r' or 'l' are syllabic (preceded and followed by consonants)
if matches_any(unit, {"r", "ŕ", "l", "ĺ"})
and prev_unit and next_unit
and not (vowels_set[prev_unit] or vowels_set[next_unit]
or diphthongs_set[prev_unit] or diphthongs_set[next_unit])
then
return true
end
return false
end
-- Function to split a word into syllables according to Slovak rules
function split_into_syllables(word)
local units = split_into_letter_units(word)
local syllables = {}
local current_syllable = ""
local i = 1
local length = #units
local first_vowel_found = false -- Flag to indicate when the first vowel has been encountered
-- Iterate over the units in the word
while i <= length do
local unit = units[i]
local next_unit = i < length and units[i + 1] or nil
local previous_unit = i > 1 and units[i - 1] or nil
local is_current_vowel = is_vowel(unit, previous_unit, next_unit)
-- If we haven't encountered the first vowel, keep adding to the first syllable
if not first_vowel_found then
current_syllable = current_syllable .. unit
if is_current_vowel then
first_vowel_found = true -- Mark that the first vowel has been found
end
else
if is_current_vowel then
-- If a vowel is encountered after the first vowel has been found, finalize the current syllable
if current_syllable ~= "" and is_vowel(previous_unit, nil, nil) then
table.insert(syllables, current_syllable)
current_syllable = ""
end
current_syllable = current_syllable .. unit
else
-- Handling consonants between vowels
local consonant_cluster = unit
-- Collect any consecutive consonants into a cluster
local j = i + 1
while j <= length and not is_vowel(units[j], units[j - 1], units[j + 1]) do
consonant_cluster = consonant_cluster .. units[j]
j = j + 1
end
local consonant_count = mw.ustring.len(consonant_cluster)
if next_unit and is_vowel(next_unit, unit, units[j]) then
-- Apply syllable rules based on the number of consonants in the cluster
if consonant_count == 1 then
-- Rule 3: Single consonant goes to the next syllable
table.insert(syllables, current_syllable) -- End the current syllable without the consonant
current_syllable = consonant_cluster -- Start the next syllable with the consonant
elseif consonant_count == 2 then
-- Rule 4: Two consonants split between syllables
current_syllable = current_syllable .. mw.ustring.sub(consonant_cluster, 1, 1)
table.insert(syllables, current_syllable)
current_syllable = mw.ustring.sub(consonant_cluster, 2, 2)
else
-- Rule 5: Three or more consonants - first goes with current syllable, rest with next
current_syllable = current_syllable .. mw.ustring.sub(consonant_cluster, 1, 1)
table.insert(syllables, current_syllable)
current_syllable = mw.ustring.sub(consonant_cluster, 2)
end
i = j - 1 -- Adjust the index to skip the processed consonants
else
current_syllable = current_syllable .. unit
end
end
end
i = i + 1
end
-- Add any remaining characters as the final syllable
if #current_syllable > 0 then
table.insert(syllables, current_syllable)
end
-- Reverse the syllables array for the requested output order
local reversed_syllables = {}
for j = #syllables, 1, -1 do
reversed_syllables[#reversed_syllables + 1] = syllables[j]
end
-- Return the count of syllables and the reversed syllable array
return #syllables, reversed_syllables
end
function is_long(syllable)
return mw.ustring.find(syllable, "[áéíóúýôĺŕ]") ~= nil or mw.ustring.find(syllable, "i[aeu]") ~= nil
end
function is_short(syllable)
return not is_long(syllable)
end
local function get_last_consonant_before_vowel(syllable)
local vowel = get_vowel(syllable)
-- If there is no vowel in the syllable, return false
if not vowel then
return false
end
-- Find the position of the vowel in the syllable
local vowel_pos = mw.ustring.find(syllable, vowel)
-- Loop backwards from the position of the vowel to find the last consonant
for i = vowel_pos - 1, 1, -1 do
local char = mw.ustring.sub(syllable, i, i)
if not mw.ustring.find(char, "[aeiouáéíóúýôä]") then
return char -- Return the last consonant before the vowel
end
end
return false -- Return false if no consonant is found before the vowel
end
-- Function to lengthen the last vowel in a syllable if it’s not already long
function lengthen_vowel(syllable)
if is_long(syllable) then
return syllable -- Return as-is if the syllable is already long
end
local lengthening_map = {
["a"] = "á", ["i"] = "í", ["y"] = "ý", ["u"] = "ú",
["ä"] = "ia", ["e"] = "ie", ["o"] = "ô"
}
local cons_map = {
["ď"] = "d", ["ť"] = "t", ["ň"] = "n", ["ľ"] = "l"
}
-- Check for regular vowels first and replace if found
for vowel, long_vowel in pairs(lengthening_map) do
if mw.ustring.find(syllable, vowel) then
-- if there is a soft consonant before "a", it becomes "ia" instead of "á"
local last_cons = get_last_consonant_before_vowel(syllable) or ""
if soft_consonants_set[last_cons] and last_cons ~= "j" and vowel == "a" then
long_vowel = "ia"
end
-- if a or ä changes into "ia", the previous consonant should be written as hard
if matches_any(vowel, {"ä", "a"}) and matches_any(last_cons, {"ď", "ť", "ň", "ľ"}) then
syllable = mw.ustring.gsub(syllable, last_cons .. vowel, cons_map[last_cons] .. vowel, 1)
end
syllable = mw.ustring.gsub(syllable, vowel, long_vowel, 1)
return syllable -- Return immediately after replacing a regular vowel
end
end
-- Only replace "r" and "l" if no other vowels were found
if not mw.ustring.find(syllable, "[aeiouyáéíóúýôä]") then
syllable = mw.ustring.gsub(syllable, "r", "ŕ")
syllable = mw.ustring.gsub(syllable, "l", "ĺ")
end
return syllable
end
-- Helper function to determine if the syllable ends with a consonant cluster
function ends_with_consonant_cluster(last_syllable)
local last_char = get_last_char(last_syllable)
local second_last_char = mw.ustring.sub(last_syllable, -2, -2)
local third_last_char = mw.ustring.sub(last_syllable, -3, -3)
local last_two_chars = mw.ustring.sub(last_syllable, -2)
-- Check if the syllable contains any regular vowels
local has_vowel = mw.ustring.find(last_syllable, "[aeiouyáéíóúýôä]")
if has_vowel then
-- If the last two characters form a digraph, check the third-last character for a cluster
if bigraphs_set[last_two_chars] then
return not vowels_set[third_last_char]
else
-- If no digraph, just check the last two characters
return not (vowels_set[last_char] or vowels_set[second_last_char]
or diphthongs_set[last_char] or diphthongs_set[second_last_char])
end
else
-- No regular vowel; treat `r` or `l` as syllabic if either is in the last two characters
if matches_any(last_char, {'ŕ', 'ĺ'}) or matches_any(second_last_char, {'ŕ', 'ĺ'}) then
return false
elseif mw.ustring.find(last_syllable, "[bcčdďfghjkľmnňpqsštťvwxzž][rl][bcčdďfghjkľmnňpqysštťvwxzž][rl]$") then
return true
else
-- No syllabic `r` or `l`, so both characters are treated as consonants
return not vowels_set[last_char] and not vowels_set[second_last_char]
end
end
end
function remove_mobile_vowel(word)
local units = split_into_letter_units(word) -- Split word into units
for i = #units, 1, -1 do
local unit = units[i]
if matches_any(unit, {"e", "ie", "o", "i", "á"}) then
-- Remove the mobile vowel unit and reassemble the word
table.remove(units, i)
return table.concat(units)
end
end
return word -- Return the original word if no mobile vowel is found
end
function soften_last_consonant(str)
local consonants_t = { ["c"] = "č", ["d"] = "ď", ["l"] = "ľ", ["n"] = "ň",
["s"] = "š", ["t"] = "ť", ["z"] = "ž" }
local last_char = get_last_char(str)
-- Check if the last character is in consonants_t and replace if needed
return consonants_t[last_char] and remove_last_char(str) .. consonants_t[last_char] or str
end
function harden_last_consonant(str)
local soft_to_hard = { ["ď"] = "d", ["ť"] = "t", ["ň"] = "n", ["ľ"] = "l" }
local last_char = get_last_char(str)
-- Check if the last character is a soft consonant and replace if needed
return soft_to_hard[last_char] and remove_last_char(str) .. soft_to_hard[last_char] or str
end
function remove_suffix(form, suffix)
if mw.ustring.find(form, suffix .. "$") then
local length = mw.ustring.len(suffix)
return mw.ustring.sub(form, 1, -length-1)
end
return form
end
function pattern_link(pattern)
return "''[[Appendix:Slovak declension pattern " .. pattern .. "|" .. pattern .. "]]''"
end
function decline_with_more_stems(genitive)
local genitives = {}
for part in mw.ustring.gmatch(genitive, "[^/]+") do
table.insert(genitives, part)
end
local forms, title = declensions[PATTERN](PAGENAME, genitives[1])
normalize_forms(forms)
local forms2, title2 = declensions[PATTERN](PAGENAME, genitives[2])
normalize_forms(forms2)
append_alternative_singular(forms, forms2["gen_sg"], forms2["dat_sg"], forms2["acc_sg"],
forms2["loc_sg"], forms2["ins_sg"])
append_alternative_plural(forms, forms2["nom_pl"], forms2["gen_pl"], forms2["dat_pl"],
forms2["acc_pl"], forms2["loc_pl"], forms2["ins_pl"])
return forms, title
end
function split_into_units(expression, genitive)
-- Split the original expression into words
local words = {}
for word in mw.ustring.gmatch(expression, "%S+") do
table.insert(words, word)
end
-- Split the genitive phrase into words, if provided
local gen_words = {}
if genitive then
for word in mw.ustring.gmatch(genitive, "%S+") do
table.insert(gen_words, word)
end
-- Check if the genitive phrase has the same structure as the original
if #gen_words ~= #words then
error("Genitive phrase must have the same number of words as the original expression.")
end
end
-- Combine words into units, grouping prepositional phrases for both expressions
local units = {}
local gen_units = {}
local i = 1
while i <= #words do
if prepositions_set[words[i]] and words[i + 1] then
-- Combine preposition with the following words as one unit
local phrase = words[i]
local gen_phrase = genitive and gen_words[i] or nil
i = i + 1
while i <= #words do
phrase = phrase .. " " .. words[i]
if genitive then
gen_phrase = gen_phrase .. " " .. gen_words[i]
end
if i == #words then
table.insert(units, phrase)
if genitive then table.insert(gen_units, gen_phrase) end
end
i = i + 1
end
else
-- Add standalone word as a unit
table.insert(units, words[i])
if genitive then
table.insert(gen_units, gen_words[i])
end
i = i + 1
end
end
-- Return both units and genitive units
return units, gen_units
end
function generate_combined_forms(units, gen_units)
-- Process each unit to generate forms
local all_forms = {}
local patterns = ""
for i = 1, #units do
local unit = units[i]
local gen_unit = gen_units[i]
if conjunctions_set[unit] or unit == gen_unit then
-- Conjunctions are indeclinable
local forms = declensions["indeclinable"](unit, gen_unit)
table.insert(all_forms, forms)
else
-- Decline each unit using determine_pattern and declensions
local pattern = determine_pattern(unit, gen_unit)
local forms, title = declensions[pattern](unit, gen_unit)
normalize_forms(forms)
table.insert(all_forms, forms)
end
end
-- Combine forms into a single forms table
local combined_forms = {}
local cases = {"nom", "gen", "dat", "acc", "loc", "ins"}
local numbers = {"sg", "pl"}
-- check if vocative and plural 2 forms are necessary
for _, forms in ipairs(all_forms) do
if forms["voc_sg"] and #cases == 6 then
table.insert(cases, "voc")
end
if forms["nom_pl_alt"] and #numbers == 2 then
table.insert(numbers, "pl_alt")
end
end
for _, case in ipairs(cases) do
for _, number in ipairs(numbers) do
local form_key = case .. "_" .. number
local combined_form, highest_index = {}, 1
-- Gather primary and numbered alternative forms (e.g., gen_sg2, gen_sg3)
for _, forms in ipairs(all_forms) do
local primary_form = forms[form_key] or forms["nom_" .. number] or forms[case .. "_pl"]
table.insert(combined_form, primary_form)
-- Check the highest alternative index
for alt_index = 2, 4 do
local alt_key = form_key .. alt_index
if forms[alt_key] then
highest_index = alt_index
end
end
end
-- Combine primary forms
combined_forms[form_key] = mw.ustring.gsub(table.concat(combined_form, " "), "^%s+", "")
-- Combine alternative forms if present
if highest_index > 1 then
for alt_index = 2, highest_index do
-- Check and add numbered alternative forms
local alt_key = form_key .. alt_index
alt_form = {}
for _, forms in ipairs(all_forms) do
local primary_form = forms[form_key] or forms["nom_" .. number]
if forms[alt_key] then
table.insert(alt_form, forms[alt_key])
else
table.insert(alt_form, primary_form)
end
end
combined_forms[alt_key] = mw.ustring.gsub(table.concat(alt_form, " "), "^%s+", "")
end
end
end
end
return combined_forms
end
function normalize_forms(forms)
-- Step 1: Remove forms based on NUMBER
if NUMBER == "sg" then
for key in pairs(forms) do
if mw.ustring.find(key, "_pl") then
forms[key] = nil -- Remove plural forms if "n" is "sg"
end
end
elseif NUMBER == "pl" then
for key in pairs(forms) do
if mw.ustring.find(key, "_sg") then
forms[key] = nil -- Remove singular forms if "n" is "pl"
end
end
end
-- Step 2: Ensure all mandatory indices are set
local cases = {"nom", "gen", "dat", "acc", "loc", "ins"}
local numbers = {"sg", "pl"}
for _, case in ipairs(cases) do
for _, number in ipairs(numbers) do
local form_key = case .. "_" .. number
if not forms[form_key] then
forms[form_key] = "—" -- Set missing forms to —
end
end
end
-- Step 3: Create missing _pl_alt indices if at least one exists
local has_alt_plural = false
for key in pairs(forms) do
if mw.ustring.find(key, "_pl_alt") then
has_alt_plural = true
break
end
end
if has_alt_plural then
for _, case in ipairs(cases) do
local form_key_alt = case .. "_pl_alt"
if not forms[form_key_alt] then
forms[form_key_alt] = "—" -- Create missing _pl_alt forms
end
end
end
-- Step 4: Create vocative plural forms if voc_sg exists
if forms["voc_sg"] then
forms["voc_pl"] = forms["nom_pl"] -- Set voc_pl to nom_pl
if has_alt_plural then
forms["voc_pl_alt"] = forms["nom_pl_alt"] -- Set voc_pl_alt to nom_pl_alt if it exists
end
end
end
function specified_by_user(forms, args)
local cases = {nom = true, gen = true, dat = true, acc = true, voc = true, loc = true, ins = true}
local numbers = {sg = true, pl = true}
for key, value in pairs(args) do
-- Match the pattern "<case>_<number><optional digit>" using mw.ustring.match
local case, number, optional_digit = mw.ustring.match(key, "^(%a%a%a)_(%a%a)(%d?)$")
-- Check if it matches the cases and numbers you want
if case and cases[case] and number and numbers[number] then
forms[key] = make_link(value, case .. "|" .. get_first_char(number))
end
end
end
function make_link(link, accel_form)
local new_link = link
-- If link is not empty, valid, and not "—", create the full link
if link ~= "" and link and link ~= "—" then
new_link = m_links.full_link({lang = lang, term = link, accel = {form = accel_form}})
end
return new_link
end
function make_table_header(title)
local header = [=[<div class="NavFrame" style="max-width:50em">
<div class="NavHead">]=] .. title .. [=[</div>
<div class="NavContent">
<table style="text-align:center" class="inflection-table">]=]
return header
end
function make_simple_row(forms, case)
local case_code = mw.ustring.sub(case, 1, 3)
local row = "<tr><td style=\"background:#eff7ff\">'''" .. case .. "'''</td>"
-- Singular
if NUMBER ~= "pl" then
row = row .. "<td><span lang=\"sk\">" .. make_link(forms[case_code .. "_sg"], case_code .. "|s") .. "</span>"
-- Loop to check and display secondary singular forms in the same cell
for i = 2, 4 do
local form_key = case_code .. "_sg" .. i
if not forms[form_key] then
break -- Exit loop if form doesn't exist
end
row = row .. ",<br /><span lang=\"sk\">" .. make_link(forms[form_key], case_code .. "|s") .. "</span>"
end
row = row .. "</td>"
end
-- Plural
if NUMBER ~= "sg" then
-- Primary plural form
row = row .. "<td><span lang=\"sk\">" .. make_link(forms[case_code .. "_pl"], case_code .. "|p") .. "</span>"
-- Loop to check and display secondary plural forms in the same cell
for i = 2, 4 do
local form_key = case_code .. "_pl" .. i
if not forms[form_key] then
break -- Exit loop if form doesn't exist
end
row = row .. ",<br /><span lang=\"sk\">" .. make_link(forms[form_key], case_code .. "|p") .. "</span>"
end
row = row .. "</td>"
-- Check for alternative plural and add it in a separate cell
if forms[case_code .. "_pl_alt"] then
row = row .. "<td><span lang=\"sk\">" .. make_link(forms[case_code .. "_pl_alt"], case_code .. "|p") .. "</span></td>"
end
end
row = row .. "</tr>"
return row
end
function make_table_header2(forms)
local tr_open = "<tr><th style=\"background:#d9ebff; width: 10em;\"></th>"
local singular = "<th style=\"background:#d9ebff\">singular</th>"
local plural = "<th style=\"background:#d9ebff\">plural</th>"
local plural1 = "<th style=\"background:#d9ebff\">plural 1</th>"
local plural2 = "<th style=\"background:#d9ebff\">plural 2</th>"
local tr_close = "</tr>"
local alt_plural = false
if forms["nom_pl_alt"] or forms["gen_pl_alt"] or forms["dat_pl_alt"]
or forms["acc_pl_alt"] or forms["loc_pl_alt"] or forms["ins_pl_alt"]
then
alt_plural = true
end
local header
if NUMBER == "sg" then
header = tr_open .. singular .. tr_close
elseif NUMBER == "pl" then
if alt_plural then
header = tr_open .. plural1 .. plural2 .. tr_close
else
header = tr_open .. plural .. tr_close
end
elseif alt_plural then
header = tr_open .. singular .. plural1 .. plural2 .. tr_close
else
header = tr_open .. singular .. plural .. tr_close
end
return header
end
function make_table_footer()
return "</table></div></div>"
end
-- Make the table
function make_table(forms, title)
for key, form in pairs(forms) do
-- check for empty strings and nil's
if form == "" or not form then
forms[key] = "—"
end
end
local final = make_table_header(title)
final = final .. make_table_header2(forms)
final = final .. make_simple_row(forms, "nominative")
final = final .. make_simple_row(forms, "genitive")
final = final .. make_simple_row(forms, "dative")
final = final .. make_simple_row(forms, "accusative")
if forms["voc_sg"] then
final = final .. make_simple_row(forms, "vocative")
end
final = final .. make_simple_row(forms, "locative")
final = final .. make_simple_row(forms, "instrumental")
final = final .. make_table_footer()
return final
end
return export