Module:gender and number
This module shows gender/number annotations, such as m (masculine), n pl (neuter plural) or f anim du (feminine animate dual). "Gender" is something of a misnomer, as the available annotation categories include other similar lexical characteristics such as animacy and verb aspect (though the former is sometimes also considered a part of word gender). These annotations are attached to nouns, pronouns and other parts of speech that have such lexical categories, depending on the particular language. The annotations occur as parameters e.g. to headword templates such as {{head}}
, {{es-noun}}
(for Spanish nouns), {{fr-proper noun}}
(for French proper nouns) and {{ru-verb}}
(for Russian verbs); to translation templates such as {{t}}
, {{t+}}
and {{t-check}}
; to definition templates such as {{demonym-noun}}
; and to dedicated gender/number templates such as {{g}}
.
Gender/number specifications
[jhyuk]A given annotation is described using a gender/number specification. Each such specification is a hyphen-separated list of gender/number codes, where a given code describes a single value of a particular category (e.g. m for "masculine", an for "animate", p for "plural"). Often, templates accept a list of gender/number specifications rather than just a single one, since some terms belong to multiple possible gender/number categories. Depending on the template in question, these specifications will be given using separate parameters or a single comma-separated parameter.
When displaying a specification, each code in the specification is converted into the appropriate display form. The different codes within the specification are then added together, separated by spaces.
Some examples:
List | Result |
---|---|
{"m"} | m |
{"m-p"} | m pl |
{"m-an-p"} | m anim pl |
{"mfbysense-p"} | m pl or f pl by sense |
{"?-p"} | ? pl |
{"?!-an-s"} | gender unattested anim sg |
{"f-d", "m-p"} | f du or m pl |
{"m-p", "f-p"} | m pl or f pl |
{"m", "f", "p"} | m or f or pl |
The source of truth for the current set of recognized codes is Module:gender and number/data.
The following table gives:
- the currently recognized codes;
- the type category they belong to (in a given spec, only one code in a given type category can be present);
- how the codes display (hover over the display form to see an explanation of what the code means);
- which category (if any) that the term will be assigned to if the code in question is in the term's headword gender/number spec (where
POS
is replaced by the pluralized part of speech of the term).
Script error: No such module "gender and number doc".
Certain gender/number codes are combination codes that are more or less equivalent to individually specifying two or more codes of a given type category in separate gender/number specs. Some combination codes categorize and display additionally. The following table gives these combination codes:
Script error: No such module "gender and number doc".
In certain type categories, if more than one code of that category is given (necessarily in different gender/number specifications), the term is added to a special category. The following table gives these multi-code categories:
Script error: No such module "gender and number doc".
Noun classes
[jhyuk]Specifications that begin with "c" (but not "c" itself) are treated specially. They are considered noun classes, and the part immediately after the "c" is simply treated as some kind of name for a noun class; usually this will be a number. Noun classes do not have sub-parts, so they will not contain hyphens. When more than one specification is given, they must all be noun classes, and they are displayed separated with a forward slash instead, and preceded by class.
Examples:
List | Result |
---|---|
{"c1"} | class 1 |
{"c1", "c2"} | class 1/2 |
{"c1a", "c2a"} | class 1a/2a |
Usage
[jhyuk]The module can be used from another module by importing it and calling the exported format_list
function. It requires one parameter, which must be a table of zero or more strings. It will then return a string containing the result. For example:
local gen = require("Module:gender and number")
local example1 = gen.format_list({"m"})
local example2 = gen.format_list({"m", "f"})
local example3 = gen.format_list({"m-p"})
WARNING: The list passed in will be overwritten.
It can also be invoked from a template. The function show_list
is used for this. It works the same way as the format_list
function, but the specifications are passed as parameters to the module invocation, like so:
*{{#invoke:gender and number|show_list|m}} *{{#invoke:gender and number|show_list|m|f}} *{{#invoke:gender and number|show_list|m-p}}
- m
- m or f
- m pl
There is no limit to the number of parameters that can be given this way. The module will process all of its parameters until it finds one that is empty. This means that the following will display only "m" and not "m or n":
{{#invoke:gender and number|show_list|m||n}}
--[=[
This module creates standardised displays for gender and number.
It converts a gender specification into Wiki/HTML format.
A gender/number specification consists of one or more gender/number elements, separated by hyphens.
Examples are: "n" (neuter gender), "f-p" (feminine plural), "m-an-p" (masculine animate plural),
"pf" (perfective aspect). Each gender/number element has the following properties:
1. A code, as used in the spec, e.g. "f" for feminine, "p" for plural".
2. A type, e.g. "gender", "number" or "animacy". Each element in a given spec must be of a different type.
3. A display form, which in turn consists of a display code and a tooltip gloss. The display code
may not be the same as the spec code, e.g. the spec code "an" has display code "anim" and tooltip
gloss "animate".
4. A category into which lemmas of the right part of speech are placed if they have a gender/number
spec containing the given element. For example, a noun with gender/number spec "m-an-p" is placed
into the categories "LANG masculine nouns", "LANG animate nouns" and "LANG pluralia tantum".
]=]--
local export = {}
local data = mw.loadData("Module:gender and number/data")
-- Version of format_list that can be invoked from a template.
function export.show_list(frame)
local args = frame.args
local lang = args["lang"]; if lang == "" then lang = nil end
local list = {}
local i = 1
while args[i] and args[i] ~= "" do
table.insert(list, args[i])
i = i + 1
end
return export.format_list(list, lang)
end
-- Older entry point; equivalent to format_genders() except that it formats the
-- categories and returns them appended to the formatted gender text rather than
-- returning the formatted text and categories separately.
function export.format_list(specs, lang, pos_for_cat, sort_key)
require("Module:debug/track")("gender and number/old-format-list")
local text, cats = export.format_genders(specs, lang, pos_for_cat)
if #cats == 0 then
return text
end
return text .. require("Module:utilities").format_categories(cats, lang, sort_key)
end
-- Format one or more gender/number specifications. Each spec is either a string, e.g. "f-p", or a table of the form
-- {spec = "SPEC", qualifiers = {"QUALIFIER", "QUALIFIER", ...}} where `.spec` is a gender/number spec such as "f-p"
-- and `.qualifiers` is a list of qualifiers to display before the formatted gender/number spec. `.spec` must be present
-- but `.qualifiers` may be omitted.
--
-- The function returns two values:
-- (a) the formatted text;
-- (b) a list of the categories to add.
-- If `lang` (which should be a language object) and `pos_for_cat` (which should be a plural part of speech) are given,
-- gender categories such as "German masculine nouns" or "Russian imperfective verbs" are added to the categories.
-- Otherwise, if only `lang` is given, the only category that may be returned is "Requests for gender in LANG entries".
-- If both are omitted, the returned list is empty.
function export.format_genders(specs, lang, pos_for_cat)
local formatted_specs = {}
local categories = {}
local seen_types = {}
local category_text = ""
local all_is_nounclass = nil
local function do_gender_spec(spec, parts)
local types = {}
local codes = data.codes
for key, code in ipairs(parts) do
-- Is this code valid?
if not codes[code] then
error('The tag "' .. code .. '" in the gender specification "' .. spec.spec .. '" is not valid.')
end
-- Check for multiple genders/numbers/animacies in a single spec.
local typ = codes[code].type
if typ ~= "other" and types[typ] then
error('The gender specification "' .. spec.spec .. '" contains multiple tags of type "' .. typ .. '".')
end
types[typ] = true
if key == 1 and spec.qualifiers and #spec.qualifiers > 0 then
parts[key] = require("Module:qualifier").format_qualifier(spec.qualifiers) .. " " .. codes[code].display
else
parts[key] = codes[code].display
end
-- Generate categories if called for.
if lang and pos_for_cat then
local cat = codes[code].cat
if cat then
table.insert(categories, lang:getCanonicalName() .. " " .. cat)
end
if seen_types[typ] and seen_types[typ] ~= code then
cat = data.multicode_cats[typ]
if cat then
table.insert(categories, lang:getCanonicalName() .. " " .. cat)
end
end
seen_types[typ] = code
end
end
-- Add the processed codes together with non-breaking spaces
if #parts == 1 then
return parts[1]
end
return table.concat(parts, " ")
end
for _, spec in ipairs(specs) do
if type(spec) ~= "table" then
spec = {spec = spec}
end
local is_nounclass
-- If the specification starts with cX, then it is a noun class specification.
if spec.spec:find("^[1-9]") or spec.spec:find("^c[^-]") then
is_nounclass = true
local code = spec.spec:gsub("^c", "")
local text
if code == "?" then
text = '<abbr class="noun-class" title="noun class missing">?</abbr>'
else
text = '<abbr class="noun-class" title="noun class ' .. code .. '">' .. code .. "</abbr>"
if lang and pos_for_cat then
table.insert(categories, lang:getCanonicalName() .. " class " .. code .. " POS")
end
end
local text_with_qual
if spec.qualifiers and #spec.qualifiers > 0 then
text_with_qual = require("Module:qualifier").format_qualifier(spec.qualifiers) .. " " .. text
else
text_with_qual = text
end
table.insert(formatted_specs, text_with_qual)
else
-- Split the parts and iterate over each part, converting it into its display form
local parts = mw.text.split(spec.spec, "%-")
local combined_codes = data.combinations
if lang then
-- Do some additional gender checks if a language was given
-- Is this an incomplete gender?
for _, code in ipairs(parts) do
if code == "?" then
table.insert(categories, "Requests for " .. (pos_for_cat == "verbs" and "aspect" or "gender") ..
" in " .. lang:getCanonicalName() .. " entries")
end
end
-- Check if the specification is valid
--elseif langinfo.genders then
-- local valid_genders = {}
-- for _, g in ipairs(langinfo.genders) do valid_genders[g] = true end
--
-- if not valid_genders[spec.spec] then
-- local valid_string = {}
-- for i, g in ipairs(langinfo.genders) do valid_string[i] = g end
-- error('The gender specification "' .. spec.spec .. '" is not valid for ' .. langinfo.names[1] .. ". Valid are: " .. table.concat(valid_string, ", "))
-- end
--end
end
local has_combined = false
for _, code in ipairs(parts) do
if combined_codes[code] then
has_combined = true
break
end
end
if not has_combined then
if #formatted_specs > 0 then
table.insert(formatted_specs, "or")
end
table.insert(formatted_specs, do_gender_spec(spec, parts))
else
-- This logic is to handle combined gender specs like 'mf' and 'mfbysense'.
local all_parts = {{}}
local extra_cats
local extra_displays
for i, code in ipairs(parts) do
if combined_codes[code] then
local new_all_parts = {}
for _, one_parts in ipairs(all_parts) do
for _, one_code in ipairs(combined_codes[code].codes) do
local new_combined_parts = mw.clone(one_parts)
table.insert(new_combined_parts, one_code)
table.insert(new_all_parts, new_combined_parts)
end
end
all_parts = new_all_parts
if lang and pos_for_cat then
local extra_cat = combined_codes[code].cat
if extra_cat then
if not extra_cats then
extra_cats = {}
end
table.insert(extra_cats, lang:getCanonicalName() .. " " .. extra_cat)
end
end
local extra_display = combined_codes[code].display
if extra_display then
if not extra_displays then
extra_displays = {}
end
table.insert(extra_displays, extra_display)
end
else
for _, one_parts in ipairs(all_parts) do
table.insert(one_parts, code)
end
end
end
for _, parts in ipairs(all_parts) do
if #formatted_specs > 0 then
table.insert(formatted_specs, "or")
end
table.insert(formatted_specs, do_gender_spec(spec, parts))
end
if extra_cats then
for _, cat in ipairs(extra_cats) do
table.insert(categories, cat)
end
end
if extra_displays then
for _, display in ipairs(extra_displays) do
table.insert(formatted_specs, display)
end
end
end
is_nounclass = false
end
-- Ensure that the specifications are either all noun classes, or none are.
if all_is_nounclass == nil then
all_is_nounclass = is_nounclass
elseif all_is_nounclass ~= is_nounclass then
error("Noun classes and genders cannot be mixed. Please use either one or the other.")
end
end
if lang and pos_for_cat then
for i, cat in ipairs(categories) do
categories[i] = cat:gsub("POS", pos_for_cat)
end
end
if all_is_nounclass then
-- Add the processed codes together with slashes
return '<span class="gender">class ' .. table.concat(formatted_specs, "/") .. "</span>", categories
else
-- Add the processed codes together with spaces
return '<span class="gender">' .. table.concat(formatted_specs, " ") .. "</span>", categories
end
end
return export