Open main menu

Wiktionary β

This module is used for Spanish headword-line templates.

The module is always invoked the same way, by passing a single parameter to the "show" function. This parameter is the name of the part of speech, but in plural (examples given are for nouns, and for adjective forms respectively):

{{#invoke:es-headword|show|nouns}}
{{#invoke:es-headword|show|adjective forms}}

The template will, by default, accept the following parameters (specific parts of speech may accept or require others):

|head=
Override the headword display, used to add links to individual words in a multi-word term.
|cat=
Override the default category, which is the same as the provided parameter. This may sometimes be useful if you have a word that is really, say, an interjection, but you want it to show inflection as if it were an adjective. (for example, benvingut).

There is no parameter for the sort key, because this is not necessary. The sort key is automatically generated by Module:utilities, according to the normal alphabetical ordering in Spanish (encoded in Module:languages/data2).


local export = {}
local pos_functions = {}

local lang = require("Module:languages").getByCode("es")

local PAGENAME = mw.title.getCurrentTitle().text

local suffix_categories = {
	["adjectives"] = true,
	["adverbs"] = true,
	["nouns"] = true,
	["verbs"] = true,
}

local remove_stress = {
	["á"] = "a", ["é"] = "e", ["í"] = "i", ["ó"] = "o", ["ú"] = "u"
}
local add_stress = {
	["a"] = "á", ["e"] = "é", ["i"] = "í", ["o"] = "ó", ["u"] = "ú"
}

-- The main entry point.
-- This is the only function that can be invoked from a template.
function export.show(frame)
	local tracking_categories = {}
	
	local poscat = frame.args[1] or error("Part of speech has not been specified. Please pass parameter 1 to the module invocation.")
	
	local params = {
		["head"] = {list = true, default = ""},
		["suff"] = {type = "boolean"},
	}
	
	if pos_functions[poscat] then
		for key, val in pairs(pos_functions[poscat].params) do
			params[key] = val
		end
	end
	
	local args = require("Module:parameters").process(frame:getParent().args, params)
	local data = {lang = lang, pos_category = poscat, categories = {}, heads = args["head"], genders = {}, inflections = {}, categories = {}}
	
	if args["suff"] then
		data.pos_category = "suffixes"
		
		if suffix_categories[poscat] then
			local singular_poscat = poscat:gsub("s$", "")
			table.insert(data.categories, "Spanish " .. singular_poscat .. "-forming suffixes")
		else
			error("No category exists for suffixes forming " .. poscat .. ".")
		end
	end
	
	if pos_functions[poscat] then
		pos_functions[poscat].func(args, data, tracking_categories)
	end
	
	return require("Module:headword").full_headword(data) .. require("Module:utilities").format_categories(tracking_categories, lang)
end


function export.make_plural_noun(singular, gender)
	-- noun + adjective
	if singular:find(" ") then
		if singular:find(" del? .+$") then
			local preceding, prep_phrase = singular:match("^(.+)( del? .+)$")
			
			local preceding_pl = export.make_plural_noun(preceding, gender)
			return {["pl"] = preceding_pl.pl .. prep_phrase}
		else
			local words = mw.text.split(singular, " ")
			if #words == 2 then
				local noun_p = export.make_plural_noun(words[1], gender)
				local adj_p = export.adjective_forms(words[2], gender)
				
				if gender == "m" and adj_p.mp then
					return {["pl"] = noun_p.pl .. " " .. adj_p.mp}
				elseif gender == "f" and noun_p.pl and adj_p.fp then
					return {["pl"] = noun_p.pl .. " " .. adj_p.fp}
				end
			end
		end
	end
	
	-- ends in unstressed vowel or á, é, ó
	if mw.ustring.match(singular, "[aeiouáéó]$") then return {["pl"] = singular .. "s"} end
	
	-- ends in í or ú
	if mw.ustring.match(singular, "[íú]$") then return {["pl"] = singular .. "s", ["pl2"] = singular .. "es"} end
	
	-- ends in a vowel + z
	if mw.ustring.match(singular, "[aeiouáéíóú]z$") then return {["pl"] = mw.ustring.gsub(singular, "z$", "ces")} end
	
	-- ends in tz
	if mw.ustring.match(singular, "tz$") then return {["pl"] = singular} end

	local vowels = {}
	-- Replace qu before e or i with k so that the u isn't counted as a vowel.
	local modified_singular = mw.ustring.gsub(singular, "qu([ie])", "k%1")
	for i in mw.ustring.gmatch(modified_singular, "[aeiouáéíóú]") do vowels[#vowels + 1] = i end
	
	-- ends in s or x with more than 1 syllable, last syllable unstressed
	if vowels[2] and mw.ustring.match(singular, "[sx]$")
	and mw.ustring.match(vowels[#vowels], "[aeiou]") then
		return {["pl"] = singular}
	end
	
	-- ends in l, r, n, d, z, or j with 3 or more syllables, accented on third to last syllable
	if vowels[3] and mw.ustring.match(singular, "[lrndzj]$")
	and mw.ustring.match(vowels[#vowels-2], "[áéíóú]") then
		return {["pl"] = singular}
	end
	
	-- ends in a in a stressed vowel + consonant
	if mw.ustring.match(singular, "[áéíóú][^aeiouáéíóú]$") then
		return {["pl"] = mw.ustring.sub(singular, 1, -3) .. remove_stress[vowels[#vowels]] .. mw.ustring.sub(singular, -1) .. "es"}
	end
	
	-- ends in a vowel + y, l, r, n, d, j, s, x
	if mw.ustring.match(singular, "[aeiou][ylrndjsx]$") then
		--  two or more vowels: add stress mark to plural
		if vowels[2] and mw.ustring.match(singular, "n$") then
			local before_stress, after_stress = mw.ustring.match(modified_singular, "^(.*)[aeiou]([^aeiou]*[aeiou][nl])$")
			local stress = add_stress[vowels[#vowels - 1]]
			if before_stress and stress then
				return {["pl"] = (before_stress .. stress .. after_stress .. "es"):gsub("k", "qu")}
			end
		end
		
		return {["pl"] = singular .. "es"}
	end
	
	-- ends in a vowel + ch
	if mw.ustring.match(singular, "[aeiou]ch$") then return {["pl"] = singular .. "es"} end
	
	-- ends in two consonants
	if mw.ustring.match(singular, "[^aeiouáéíóú][^aeiouáéíóú]$") then return {["pl"] = singular .. "s"} end
	
	-- ends in a vowel + consonant other than l, r, n, d, z, j, s, or x
	if mw.ustring.match(singular, "[aeiou][^aeioulrndzjsx]$") then return {["pl"] = singular .. "s"} end
	
	return {}
	
end

function export.adjective_forms(singular, gender)
	local last_two, last = mw.ustring.match(singular, "(.(.))$")
	
	if mw.ustring.match(singular, "dor$") and gender == "m" then
		return {
			['ms'] = singular,
			['mp'] = singular .. 'es',
			['fs'] = singular .. 'a',
			['fp'] = singular .. 'as'
		}
	end
	
	if mw.ustring.match(singular, "dora$") and gender == "f" then
		local stem = mw.ustring.sub(singular, 1, #singular-1)
		return {
			['ms'] = stem,
			['mp'] = stem .. 'es',
			['fs'] = stem .. 'a',
			['fp'] = stem .. 'as'
		}
	end
	
	if last == "o" or last == "a" and gender == "f" then
		local stem = mw.ustring.match(singular, "^(.+)[ao]$")
		return {
			['ms'] = stem.."o",
			['mp'] = stem.."os",
			['fs'] = stem.."a",
			['fp'] = stem.."as"
		}
	end
	
	if last == "e" or mw.ustring.match(singular, "ista$") then
		local plural = singular..'s'
		return {
			['ms'] = singular,
			['mp'] = plural,
			['fs'] = singular,
			['fp'] = plural
		}
	end
	
	if last == "z" then
		local plural = mw.ustring.gsub(singular, "z$", "ces")
		return {
			['ms'] = singular,
			['mp'] = plural,
			['fs'] = singular,
			['fp'] = plural
		}
	end
	
	local function make_stem(singular)
		return mw.ustring.gsub(
			singular,
			"^(.+)(.)(.)$",
			function (before_stress, stressed_vowel, after_stress)
				return before_stress .. (remove_stress[stressed_vowel] or stressed_vowel) .. after_stress
			end)
	end
	
	if last_two == "ar" or last_two == "ón" or last_two == "ún" or last == "l" then
		local plural = make_stem(singular).."es"
		return {
			['ms'] = singular,
			['mp'] = plural,
			['fs'] = singular,
			['fp'] = plural
		}
	end
	
	if last_two == "or" then
		return {
			['ms'] = singular,
			['mp'] = singular.."es",
			['fs'] = singular,
			['fp'] = singular.."es"
		}
	end

	if last_two == "án" or last_two == "és" or last_two == "ín" then
		local stem = make_stem(singular)
		return {
			['ms'] = singular,
			['mp'] = stem.."es",
			['fs'] = stem.."a",
			['fp'] = stem.."as"
		}
	end
	
	return {}
end

-- Display information for a noun's gender
-- This is separate so that it can also be used for proper nouns
function noun_gender(args, data)
	local categories = {}
	
	local gender = args[1]
	
	if gender == "m-p" or gender == "f-p" then
		table.insert(data.categories, "Spanish pluralia tantum")
	end
	
	if gender == "mf" then
		table.insert(data.genders, "m")
		table.insert(data.genders, "f")
	else
		table.insert(data.genders, gender)
	end
 
	if #data.genders == 0 then
		table.insert(data.genders, "?")
	end
end

pos_functions["proper nouns"] = {
	params = {
		[1] = {},
		},
	func = function(args, data)
		noun_gender(args, data)
	end
}

-- Display additional inflection information for a noun
pos_functions["nouns"] = {
	params = {
		[1] = {},
		[2] = {},
		["pl2"] = {},
		["f"] = {},
		["fpl"] = {},
		["m"] = {},
		["m2"] = {},
		["mpl"] = {},
		},
	func = function(args, data, tracking_categories)
		noun_gender(args, data)
		
		-- Plural
		if data.genders[1] == "m-p" or data.genders[1] == "f-p" then
			table.insert(data.inflections, {label = "[[Appendix:Glossary#plural only|plural only]]"})
		else
			local plural = args[2]
			
			if plural == "-" then
				table.insert(data.inflections, {label = "[[Appendix:Glossary#uncountable|uncountable]]"})
				table.insert(data.categories, "Spanish uncountable nouns")
			else
				local infl_parts = {label = "plural", accel = "plural-form-of"}
				local plural2 = args["pl2"]
				
				if not plural or plural == "s" then
					plural = PAGENAME .. "s"
				elseif plural == "es" then
					plural = PAGENAME .. "es"
				end
				
				table.insert(infl_parts, plural)
				
				if plural2 then
					table.insert(infl_parts, plural2)
				end
				
				local generated_plurals = export.make_plural_noun(PAGENAME, data.genders[1])
				
				if plural and not generated_plurals.pl then
					table.insert(tracking_categories, "Spanish nouns with unpredictable plurals")
				end
				
				if plural then
					if generated_plurals.pl == plural or generated_plurals.pl2 == plural then
						table.insert(tracking_categories, "Spanish nouns with predictable plurals")
					else
						mw.log("predicted plural:", generated_plurals.pl, "actual plural:", plural)
						table.insert(tracking_categories, "Spanish nouns with unpredictable plurals")
					end
				end
				
				if plural and not mw.title.new(plural).exists then
					table.insert(tracking_categories, "Spanish nouns with missing plurals")
				end
				if plural2 and not mw.title.new(plural2).exists then
					table.insert(tracking_categories, "Spanish nouns with missing plurals")
				end
	
				table.insert(data.inflections, infl_parts)
			end
		end
		
		-- Gendered forms
		local feminine = args["f"]
		local feminine_pl = args["fpl"]
		local masculine = args["m"]
		local masculine2 = args["m2"]
		local masculine_pl = args["mpl"]
	 
		if feminine then
			table.insert(data.inflections, {label = "feminine", feminine})
			if not mw.title.new(feminine).exists then
				table.insert(tracking_categories, "Missing Spanish noun forms")
			end
		end
		
		if feminine_pl then
			table.insert(data.inflections, {label = "feminine plural", feminine_pl})
			if not mw.title.new(feminine_pl).exists then
				table.insert(tracking_categories, "Missing Spanish noun forms")
			end
		end
	 	
		if masculine then
			table.insert(data.inflections, {label = "masculine", masculine, masculine2})
			if not mw.title.new(masculine).exists then
				table.insert(tracking_categories, "Missing Spanish noun forms")
			end
		end
		
		if masculine_pl then
			table.insert(data.inflections, {label = "masculine plural", masculine_pl})
			if not mw.title.new(masculine_pl).exists then
				table.insert(tracking_categories, "Missing Spanish noun forms")
			end
		end
	end
}

function export.new_noun_headword(frame)
	local allowed_genders = {['m'] = true,
		['f'] = true,
		['m-p'] = true,
		['f-p'] = true,
		['m-f'] =  true,
		['m-f-p'] = true
	}
	local params = {
		[1] = {required=true}, --gender
		["g2"] = {}, --second gender
		["e"] = {type="boolean"}, --epicene
		[2] = {}, --plural 1 override
		["pl2"] = {}, --plural 2 override
		["pl3"] = {}, --plural 3
		["f"] = {}, --feminine form
		["m"] = {}, --masculine form
		["fpl"] = {}, --feminine plural override
		["fpl2"] = {}, --feminine plural 2 override
		["mpl"] = {}, --masculine plural override
		["mpl2"] = {}, --masculine plural 2 override
		["head"] = {},
		["json"] = {type="boolean"},
		["title"] = {}
	}
	
	local args = require("Module:parameters").process(frame:getParent().args, params)
	
	local title = args["title"] or mw.title.getCurrentTitle().text
	
	if args[1] == "mf" then
		args[1] = "m"
		args["g2"] = "f"
	elseif gender == "mfp" then
		args[1] = "m-p"
		args["g2"] = "f-p"
	end
	
	if not allowed_genders[args[1]] then error("Specified gender is not valid") end
	
	local data = {
		lang = lang,
		pos_category = "nouns",
		categories = {},
		heads = {args["head"]},
		genders = {args[1]},
		inflections = {}
	}
	
	if args.g2 then table.insert(data.genders, args.g2) end
	
	if args["e"] then
		table.insert(data.categories, "Spanish epicene nouns")
		table.insert(data.inflections, {label = "[[Appendix:Glossary#epicene|epicene]]"})
	end
	
	if args[1] ~= "m-p" and args[1] ~= "f-p" then
		if args[2] == "-" then
			table.insert(data.inflections, {label = "[[Appendix:Glossary#uncountable|uncountable]]"})
			table.insert(data.categories, "Spanish uncountable nouns")
		else
			if not args[2] then
				local plurals = export.make_plural_noun(title, args[1])
				if plurals.pl then
					args[2] = plurals.pl
				end
				if plurals.pl2 then
					args["pl2"] = plurals.pl2
				end
			end
			if args[2] then
				local infl = {label = 'plural', accel = "plural-form-of", {term=args[2]}}
				if args["pl2"] then
					table.insert(infl, {term=args["pl2"]})
					if args["pl3"] then
						table.insert(infl, {term=args["pl3"]})
					end
				end
				table.insert(data.inflections, infl)
			end
		end
	else
		table.insert(data.categories, "Spanish pluralia tantum")
		table.insert(data.inflections, {label = "[[Appendix:Glossary#plural only|plural only]]"})
	end
	
	if args[1] == "m" and args["f"] then
		if args["f"] == "1" then
			local noun_forms = export.adjective_forms(title, "m")
			if noun_forms.fs then
				args["f"] = noun_forms.fs
				if noun_forms.fp then
					args["fp"] = noun_forms.fp
				end
			end
		end
		if args["f"] ~= "1" then
			table.insert(data.inflections, {label = 'feminine', args["f"]})
			if not args['fpl'] then
				local feminine_plurals = export.make_plural_noun(args["f"], "f")
				if feminine_plurals.pl then
					args['fpl'] = feminine_plurals.pl
				end
				if feminine_plurals.pl2 then
					args["fpl2"] = feminine_plurals.pl2
				end
			end
			if args['fpl'] then
				local infl = {label = 'feminine plural', accel = "plural-form-of", {term=args['fpl']}}
				if args["fpl2"] then
					table.insert(infl, {term=args["fpl2"]})
				end
				table.insert(data.inflections, infl)
			end
		end
	end
	
	if args[1] == "f" and args["m"] then
		if args["m"] == "1" then
			local noun_forms = export.adjective_forms(title, "f")
			if noun_forms.ms then
				args["m"] = noun_forms.ms
				if noun_forms.mp then
					args["mp"] = noun_forms.mp
				end
			end
		end
		if args["m"] ~= "1" then
			table.insert(data.inflections, {label = 'masculine', args["m"]})
			if not args['mpl'] then
				local masculine_plurals = export.make_plural_noun(args["m"], "m")
				if masculine_plurals.pl then
					args['mpl'] = masculine_plurals.pl
				end
				if masculine_plurals.pl2 then
					args["mpl2"] = masculine_plurals.pl2
				end
			end
			if args['mpl'] then
				local infl = {label = 'masculine plural', accel = "plural-form-of", {term=args['mpl']}}
				if args["mpl2"] then
					table.insert(infl, {term=args["mpl2"]})
				end
				table.insert(data.inflections, infl)
			end
		end
	end
	
	if args[2] and not mw.title.new(args[2]).exists then
		table.insert(data.categories, "Missing Spanish noun forms")
	end
	if args["pl2"] and not mw.title.new(args["pl2"]).exists then
		table.insert(data.categories, "Missing Spanish noun forms")
	end
	if args["pl3"] and not mw.title.new(args["pl3"]).exists then
		table.insert(data.categories, "Missing Spanish noun forms")
	end
	if args["fpl"] and not mw.title.new(args["fpl"]).exists then
		table.insert(data.categories, "Missing Spanish noun forms")
	end
	if args["mpl"] and not mw.title.new(args["mpl"]).exists then
		table.insert(data.categories, "Missing Spanish noun forms")
	end
	if args["fpl2"] and not mw.title.new(args["fpl2"]).exists then
		table.insert(data.categories, "Missing Spanish noun forms")
	end
	if args["mpl2"] and not mw.title.new(args["mpl2"]).exists then
		table.insert(data.categories, "Missing Spanish noun forms")
	end
	
	if args["json"] then
		return require("Module:JSON").toJSON(data)
	end

	return require("Module:headword").full_headword(data)
	
end
	
return export