Module:alternative forms: difference between revisions

From Wiktionary, the free dictionary
Jump to navigation Jump to search
Content deleted Content added
use em-dash rather than en-dash to separate terms from dialect tags; more correct in terms of punctuation
fix support for references in {{alt}} items
(21 intermediate revisions by 3 users not shown)
Line 1: Line 1:
local export = {}
local export = {}
local m_links = require("Module:links")
local m_languages = require("Module:languages")


local rsplit = mw.text.split
local labels_module = "Module:labels"
local links_module = "Module:links"

local parameter_utilities_module = "Module:parameter utilities"
-- See if the language's dialectal data module has a label corresponding to the dialect argument.
local parameters_module = "Module:parameters"
function export.getLabel(dialect, dialect_data)
local parse_utilities_module = "Module:parse utilities"
local data = dialect_data[dialect] or ( dialect_data.labels and dialect_data.labels[dialect] )
local pron_qualifier_module = "Module:pron qualifier"
local alias_of = ( dialect_data.aliases and dialect_data.aliases[dialect] )
if not data then
if alias_of then
data = dialect_data[alias_of] or ( dialect_data.labels and dialect_data.labels[alias_of] )
end
end
if data then
local display = data.display or dialect
if data.appendix then
dialect = '[[Appendix:' .. data.appendix .. '|' .. display .. ']]'
else
local target = data.link
dialect = target and '[[w:'.. target .. '|' .. display .. ']]' or display
end
end
return dialect
end

function export.make_dialects(raw, lang)
local dialect_page = 'Module:'.. lang:getCode() ..':Dialects'
local dialect_info
if raw[1] then
dialect_info = mw.title.new(dialect_page).exists and mw.loadData(dialect_page) or false
end

local dialects = {}

for _, dialect in ipairs(raw) do
table.insert(dialects, dialect_info and export.getLabel(dialect, dialect_info) or dialect)
end

return dialects
end


local function track(page)
local function track(page)
Line 46: Line 12:
end
end


-- Per-param modifiers, which can be specified either as separate parameters (e.g. t2=, pos3=) or as inline modifiers
-- <t:...>, <pos:...>, etc. The key is the name fo the parameter (e.g. "t", "pos") and the value is a table with
-- elements as follows:
-- * `extra_specs`: An optional table of extra key-value pairs to add to the spec used for parsing the parameter
-- when specified as a separate parameter (e.g. {type = "boolean"} for a Boolean parameter, or
-- {alias_of = "t"} for the "gloss" parameter, which is aliased to "t"), on top of the default, which
-- is {list = true, allow_holes = true}.
-- * `convert`: An optional function to convert the raw argument into the form passed to [[Module:links]].
-- This function takes three parameters: (1) `arg` (the raw argument); (2) `inline` (true if we're
-- processing an inline modifier, false otherwise); (4) `termno` (the logical index of the term being
-- processed, starting from 1).
-- * `item_dest`: The name of the key used when storing the parameter's value into the processed `term` or `termobj`
-- object. Normally the same as the parameter's name. Different in the case of "t", where we store the
-- gloss in "gloss", and "g", where we store the genders in "genders".
-- * `outer`: If true, store the value into `termobj` rather than `term`.
local param_mods = {
local param_mods = {
alt = {},
t = {
t = {
-- [[Module:links]] expects the gloss in "gloss".
-- We need to store the t1=/t2= param and the <t:...> inline modifier into the "gloss" key of the parsed term,
-- because that is what [[Module:links]] expects.
item_dest = "gloss",
item_dest = "gloss",
},
},
gloss = {
gloss = {
alias_of = "t",
-- The `extra_specs` handles the fact that "gloss" is an alias of "t".
extra_specs = {alias_of = "t"},
},
},
tr = {},
tr = {},
ts = {},
ts = {},
g = {
g = {
-- [[Module:links]] expects the genders in "genders".
-- We need to store the g1=/g2= param and the <g:...> inline modifier into the "genders" key of the parsed term,
-- because that is what [[Module:links]] expects.
item_dest = "genders",
item_dest = "genders",
sublist = true,
convert = function(arg, inline, termno)
return rsplit(arg, ",")
end,
},
},
pos = {},
lit = {},
id = {},
id = {},
alt = {},
sc = {
separate_no_index = true,
q = {
outer = true,
type = "script",
},
},
qq = {
outer = true,
},
lit = {},
pos = {},
}
}


--[==[
local function get_valid_prefixes()
Main function for displaying alternative forms. Extracted out from the template-callable function so this can be
local valid_prefixes = {}
called by other modules (in particular, [[Module:descendants tree]]). `show_labels_after_terms` no longer has any
for param_mod, _ in pairs(param_mods) do
meaning. `allow_self_link` causes terms the same as the pagename to be shown normally; otherwise they are displayed
table.insert(valid_prefixes, param_mod)
unlinked.
end
]==]
table.sort(valid_prefixes)
function export.display_alternative_forms(parent_args, pagename, show_labels_after_terms, allow_self_link)
return valid_prefixes
end

-- Main function for displaying alternative forms. Extracted out from the template-callable function so this can be
-- called by other modules (in particular, [[Module:descendants tree]]).
function export.display_alternative_forms(parent_args, pagename, include_dialect_tags, allow_self_link)
local list_with_holes = { list = true, allow_holes = true }
local params = {
local params = {
[1] = { required = true, default = "und" },
[1] = {required = true, type = "language", etym_lang = true, default = "en"},
[2] = list_with_holes,
[2] = {list = true, allow_holes = true},
["sc"] = {},
}
}


local m_param_utils = require(parameter_utilities_module)
for param_mod, param_mod_spec in pairs(param_mods) do
m_param_utils.augment_param_mods_with_pron_qualifiers(param_mods, {
if not param_mod_spec.extra_specs then
{param = "q", separate_no_index = false},
params[param_mod] = list_with_holes
{param = "l", separate_no_index = false, require_index = true},
else
"ref",
local param_spec = mw.clone(list_with_holes)
})
for k, v in pairs(param_mod_spec.extra_specs) do
m_param_utils.augment_params_with_modifiers(params, param_mods)
param_spec[k] = v
end
params[param_mod] = param_spec
end
end


local args = require("Module:parameters").process(parent_args, params)
local args = require("Module:parameters").process(parent_args, params)
local lang = m_languages.getByCode(args[1], 1)
local sc = args["sc"] and require("Module:scripts").getByCode(args["sc"], "sc") or nil


local rawDialects = {}
local lang = args[1]
local items = {}


local items = m_param_utils.process_list_arguments {
-- Find the maximum index among any of the list parameters.
args = args,
local maxmaxindex = 0
param_mods = param_mods,
for k, v in pairs(args) do
termarg = 2,
if type(v) == "table" and v.maxindex and v.maxindex > maxmaxindex then
parse_lang_prefix = true,
maxmaxindex = v.maxindex
track_module = "alter",
end
lang = lang,
end
sc = args.sc.default,
stop_when = function(data)
return not data.any_param_at_index
end,
}


if maxmaxindex == 0 then
if not items[1] then
error("No items found!")
error("Either a positional parameter, alt parameter, id parameter, tr parameter, or ts parameter is required.")
end
end


local raw_labels = {}
-- Is set to true if there is a term (entry link, alt text, transliteration, transcription) at the previous index.
local prev = false
local use_semicolon = false
local put


-- Extract the labels and make sure none are blank or omitted.
local termno = 0
local last_item_index = items[#items].orig_index
for i = 1, maxmaxindex do
if last_item_index < args[2].maxindex then
-- If the previous term parameter was empty and we're not on the first term parameter,
for i = last_item_index + 2, args[2].maxindex do
-- this term parameter and any others contain dialect or other labels.
if i > 1 and not prev then
if not args[2][i] then
-- Indices in i start at 1 but parameters start at 2 to add 1 to shown index.
rawDialects = {unpack(args[2], i, maxmaxindex)}
error("Missing/blank item not allowed in [[Template:alt]] labels, but saw such an item in parameter "
break
.. (i + 1))
end
table.insert(raw_labels, args[2][i])
end
end
end


-- Make sure there aren't property parameters after the last item (i.e. corresponding to labels).
local term = args[2][i]
for k, v in pairs(args) do

-- Look for named list parameters. We check:
if term ~= ";" then
-- (1) key is a string (excludes the term param, which is a number);
termno = termno + 1
-- (2) value is a table, i.e. a list;

-- (3) v.maxindex is set (i.e. allow_holes was used);
-- Compute whether any of the separate indexed params exist for this index.
-- (4) v.maxindex is past the index of the last term.
local any_param_at_index = term ~= nil
if type(k) == "string" and type(v) == "table" and v.maxindex and v.maxindex > last_item_index then
for k, v in pairs(args) do
local set_values = {}
-- Look for named list parameters. We check:
for i = last_item_index + 1, v.maxindex do
-- (1) key is a string (excludes 2=, a numbered rather than named list param, because it needs to
if v[i] then
-- be indexed using `i` instead of `termno`);
table.insert(set_values, i)
-- (2) value is a table (1= and sc= are converted into strings or nil rather than lists);
-- (3) the value has an entry at index `termno` (the current logical index).
if type(k) == "string" and type(v) == "table" and v[termno] then
any_param_at_index = true
-- Tracking for use of any specific indexed parameter. FIXME: Do we still need this?
-- FIXME: If we don't need it, remove the call to track() below, add `break` below, and wrap
-- the `for` loop in `if not any_param_at_index then` for efficiency purposes.
-- break
-- [[Special:WhatLinksHere/Template:tracking/alter/alt]]
-- [[Special:WhatLinksHere/Template:tracking/alter/id]]
-- [[Special:WhatLinksHere/Template:tracking/alter/tr]]
-- [etc.]
track(k)
end
end
end
end
error(("Extraneous values for %s= (set at position%s %s)"):format(k, #set_values > 1 and "s" or "",

table.concat(set_values, ",")))
-- If any of the params used for formatting this term is present, create a term and add it to the list.
if any_param_at_index then
-- Initialize the `termobj` object and the `term` object passed to full_link() in [[Module:links]].
local termobj = {
joiner = i > 1 and (args[2][i - 1] == ";" and "; " or ", ") or "",
term = {
lang = lang,
sc = sc,
term = term,
},
}

-- Parse all the term-specific parameters and store in `term` or `termobj`.
for param_mod, param_mod_spec in pairs(param_mods) do
local dest = param_mod_spec.item_dest or param_mod
local arg = args[param_mod][termno]
if arg then
if param_mod_spec.convert then
arg = param_mod_spec.convert(arg, false, termno)
end
local obj = param_mod_spec.outer and termobj or termobj.term
obj[dest] = arg
end
end

-- Check for inline modifier, e.g. מרים<tr:Miryem>. But exclude HTML entry with <span ...>, <i ...>, <br/>
-- or similar in it, caused by wrapping an argument in {{l|...}}, {{af|...}} or similar. Basically, all tags
-- of the sort we parse here should consist of a less-than sign, plus letters, plus a colon, e.g. <tr:...>,
-- so if we see a tag on the outer level that isn't in this format, we don't try to parse it. The
-- restriction to the outer level is to allow generated HTML inside of e.g. qualifier tags, such as
-- foo<q:similar to {{m|fr|bar}}>.
if term and term:find("<") and not term:find("^[^<]*<[a-z]*[^a-z:]") then
if not put then
put = require("Module:parse utilities")
end
local run = put.parse_balanced_segment_run(term, "<", ">")
local function parse_err(msg)
-- Add 1 before first term index starts at 2.
error(msg .. ": " .. (i + 1) .. "=" .. table.concat(run))
end
termobj.term.term = run[1] ~= "" and run[1] or nil

for j = 2, #run - 1, 2 do
if run[j + 1] ~= "" then
parse_err("Extraneous text '" .. run[j + 1] .. "' after modifier")
end
local modtext = run[j]:match("^<(.*)>$")
if not modtext then
parse_err("Internal error: Modifier '" .. modtext .. "' isn't surrounded by angle brackets")
end
local prefix, arg = modtext:match("^([a-z]+):(.*)$")
if not prefix then
parse_err(("Modifier %s lacks a prefix, should begin with one of %s followed by a colon"):format(
run[j], table.concat(get_valid_prefixes(), ",")))
end
local param_mod_spec = param_mods[prefix]
if not param_mod_spec then
parse_err(("Unrecognized prefix '%s' in modifier %s, should be one of %s"):format(
prefix, run[j], table.concat(get_valid_prefixes(), ",")))
end
local dest = param_mod_spec.item_dest or prefix
local obj = param_mod_spec.outer and termobj or termobj.term
if obj[dest] then
parse_err("Modifier '" .. prefix .. "' occurs twice, second occurrence " .. run[j])
end
if param_mod_spec.convert then
arg = param_mod_spec.convert(arg, true, termno)
end
obj[dest] = arg
end
end

-- FIXME: Either we should have a general mechanism in `param_mods` for default values, or (better) modify
-- [[Module:links]] so it can handle nil for .genders.
termobj.term.genders = termobj.term.genders or {}

-- If the displayed term (from .term or .alt) has an embedded comma, use a semicolon to join the terms.
local term_text = termobj.term.term or termobj.term.alt
if not use_semicolon and term_text then
if term_text:find(",", 1, true) then
use_semicolon = true
end
end

-- If the to-be-linked term is the same as the pagename, display it unlinked.
if not allow_self_link and termobj.term.term and (lang:makeEntryName(termobj.term.term)) == pagename then
track("term is pagename")
termobj.term.alt = termobj.term.alt or termobj.term.term
termobj.term.term = nil
end

table.insert(items, termobj)
prev = true
else
if math.max(args.alt.maxindex, args.id.maxindex, args.tr.maxindex, args.ts.maxindex) >= termno then
track("too few terms")
end

prev = false
end
end
end
end
end


if not allow_self_link then
-- The template must have either items or dialect labels.
-- If the to-be-linked term is the same as the pagename, display it unlinked.
if items[1] == nil and rawDialects[1] == nil then error("No terms found!") end
for _, item in ipairs(items) do

if not item.term and (lang:makeEntryName(item.term)) == pagename then
-- If any term had an embedded comma, override all joiners to be semicolons.
track("term is pagename")
if use_semicolon then
for i, item in ipairs(items) do
item.alt = item.alt or item.term
if i > 1 then
item.term = nil
item.joiner = "; "
end
end
end
end
end
end


local labels
-- Format all the items, including joiners, pre-qualifiers and post-qualifiers.
if #raw_labels > 0 then
for i, item in ipairs(items) do
labels = require(labels_module).process_raw_labels { labels = raw_labels, lang = lang, nocat = true }
local preq_text = item.q and require("Module:qualifier").format_qualifier(item.q) .. " " or ""
end
items[i] = item.joiner .. preq_text .. m_links.full_link(item.term, nil, allow_self_link)

.. (item.qq and " " .. require("Module:qualifier").format_qualifier(item.qq) or "")
local parts = {}
local function ins(part)
table.insert(parts, part)
end
end


-- Construct the final output.
-- Construct the final output.
if include_dialect_tags then
-- If there are dialect or similar tags, construct them now and append to final output.
local dialects = export.make_dialects(rawDialects, lang)
if #dialects > 0 then
local dialect_label
if lang:hasTranslit() then
dialect_label = " &mdash; ''" .. table.concat(dialects, ", ") .. "''"
else
dialect_label = " (''" .. table.concat(dialects, ", ") .. "'')"
end


-- First the items, including separators, left and right regular qualifiers and left and right per-item labels.
-- Fixes the problem of '' being added to '' at the end of last dialect parameter
for _, item in ipairs(items) do
dialect_label = mw.ustring.gsub(dialect_label, "''''", "")
ins(item.separator)
table.insert(items, dialect_label)
local text = require(links_module).full_link(item, nil, allow_self_link)
if item.q and item.q[1] or item.qq and item.qq[1] or item.l and item.l[1] or item.ll and item.ll[1]
or item.refs and item.refs[1] then
text = require(pron_qualifier_module).format_qualifiers {
lang = item.lang,
text = text,
q = item.q,
qq = item.qq,
l = item.l,
ll = item.ll,
refs = item.refs,
}
end
end
ins(text)
end
end


-- If there are labels, construct them now and append to final output.
return table.concat(items)
if labels then
if lang:hasTranslit() then
ins(" &mdash; " .. require(labels_module).format_processed_labels {
labels = labels, lang = lang
})
else
ins(" " .. require(labels_module).format_processed_labels {
labels = labels, lang = lang, open = "(", close = ")"
})
end
end

return table.concat(parts)
end
end


--[==[
-- Template-callable function for displaying alternative forms.
Template-callable function for displaying alternative forms.
]==]
function export.create(frame)
function export.create(frame)
local parent_args = frame:getParent().args
local parent_args = frame:getParent().args
local title = mw.title.getCurrentTitle()
local title = mw.title.getCurrentTitle()
local PAGENAME = title.text
local PAGENAME = title.text
return export.display_alternative_forms(parent_args, title, "include dialect tags")
return export.display_alternative_forms(parent_args, title)
end

function export.categorize(frame)
local content = {}

local title = mw.title.getCurrentTitle()
local titletext = title.text
local namespace = title.nsText
local subpagename = title.subpageText

-- subpagename ~= titletext if it is a documentation page
if namespace == "Module" and subpagename == titletext then
local langCode = mw.ustring.match(titletext, "^([^:]+):")
local lang = m_languages.getByCode(langCode) or error('"' .. langCode .. '" is not a valid language code.')
content.canonicalName = lang:getCanonicalName()

local categories =
[=[
[[Category:<canonicalName> modules|dialects]]
[[Category:Dialectal data modules|<canonicalName>]]
]=]

categories = mw.ustring.gsub(categories, "<([^>]+)>", content)
return categories
end
end
end



Revision as of 19:16, 16 June 2024

This module is called by {{alter}}, the template that is used in Alternative forms sections to link the alternative forms of a term and to show which dialect or spelling system they belong to.

The dialect labels used after an empty parameter are now the same as those used by {{lb}}, and can be found in the labels data modules, e.g. Module:labels/data/lang/en for English and Module:labels/data/lang/grc for Ancient Greek.

Testcases

ὑμεῖς (humeîs)
  • Lua error at line 52: attempt to call field 'augment_param_mods_with_pron_qualifiers' (a nil value)
  • Lua error at line 52: attempt to call field 'augment_param_mods_with_pron_qualifiers' (a nil value)
  • Lua error at line 52: attempt to call field 'augment_param_mods_with_pron_qualifiers' (a nil value)
(using aliases of labels)
  • Lua error at line 52: attempt to call field 'augment_param_mods_with_pron_qualifiers' (a nil value)
  • Lua error at line 52: attempt to call field 'augment_param_mods_with_pron_qualifiers' (a nil value)
  • Lua error at line 52: attempt to call field 'augment_param_mods_with_pron_qualifiers' (a nil value)
honor
  • Lua error at line 52: attempt to call field 'augment_param_mods_with_pron_qualifiers' (a nil value)
werweissen
  • Lua error at line 52: attempt to call field 'augment_param_mods_with_pron_qualifiers' (a nil value)
items with commas
  • Lua error at line 52: attempt to call field 'augment_param_mods_with_pron_qualifiers' (a nil value)
term param missing
  • Lua error at line 52: attempt to call field 'augment_param_mods_with_pron_qualifiers' (a nil value)
  • Lua error at line 52: attempt to call field 'augment_param_mods_with_pron_qualifiers' (a nil value)

See also


local export = {}

local labels_module = "Module:labels"
local links_module = "Module:links"
local parameter_utilities_module = "Module:parameter utilities"
local parameters_module = "Module:parameters"
local parse_utilities_module = "Module:parse utilities"
local pron_qualifier_module = "Module:pron qualifier"

local function track(page)
	require("Module:debug/track")("alter/" .. page)
end

local param_mods = {
	alt = {},
	t = {
		-- [[Module:links]] expects the gloss in "gloss".
		item_dest = "gloss",
	},
	gloss = {
		alias_of = "t",
	},
	tr = {},
	ts = {},
	g = {
		-- [[Module:links]] expects the genders in "genders".
		item_dest = "genders",
		sublist = true,
	},
	pos = {},
	lit = {},
	id = {},
	sc = {
		separate_no_index = true,
		type = "script",
	},
}

--[==[
Main function for displaying alternative forms. Extracted out from the template-callable function so this can be
called by other modules (in particular, [[Module:descendants tree]]). `show_labels_after_terms` no longer has any
meaning. `allow_self_link` causes terms the same as the pagename to be shown normally; otherwise they are displayed
unlinked.
]==]
function export.display_alternative_forms(parent_args, pagename, show_labels_after_terms, allow_self_link)
	local params = {
		[1] = {required = true, type = "language", etym_lang = true, default = "en"},
		[2] = {list = true, allow_holes = true},
	}

	local m_param_utils = require(parameter_utilities_module)
	m_param_utils.augment_param_mods_with_pron_qualifiers(param_mods, {
		{param = "q", separate_no_index = false},
		{param = "l", separate_no_index = false, require_index = true},
		"ref",
	})
	m_param_utils.augment_params_with_modifiers(params, param_mods)

	local args = require("Module:parameters").process(parent_args, params)

	local lang = args[1]

	local items = m_param_utils.process_list_arguments {
		args = args,
		param_mods = param_mods,
		termarg = 2,
		parse_lang_prefix = true,
		track_module = "alter",
		lang = lang,
		sc = args.sc.default,
		stop_when = function(data)
			return not data.any_param_at_index
		end,
	}

	if not items[1] then
		error("No items found!")
	end

	local raw_labels = {}

	-- Extract the labels and make sure none are blank or omitted.
	local last_item_index = items[#items].orig_index
	if last_item_index < args[2].maxindex then
		for i = last_item_index + 2, args[2].maxindex do
			if not args[2][i] then
				-- Indices in i start at 1 but parameters start at 2 to add 1 to shown index.
				error("Missing/blank item not allowed in [[Template:alt]] labels, but saw such an item in parameter "
					.. (i + 1))
			end
			table.insert(raw_labels, args[2][i])
		end
	end

	-- Make sure there aren't property parameters after the last item (i.e. corresponding to labels).
	for k, v in pairs(args) do
		-- Look for named list parameters. We check:
		-- (1) key is a string (excludes the term param, which is a number);
		-- (2) value is a table, i.e. a list;
		-- (3) v.maxindex is set (i.e. allow_holes was used);
		-- (4) v.maxindex is past the index of the last term.
		if type(k) == "string" and type(v) == "table" and v.maxindex and v.maxindex > last_item_index then
			local set_values = {}
			for i = last_item_index + 1, v.maxindex do
				if v[i] then
					table.insert(set_values, i)
				end
			end
			error(("Extraneous values for %s= (set at position%s %s)"):format(k, #set_values > 1 and "s" or "",
				table.concat(set_values, ",")))
		end
	end

	if not allow_self_link then
		-- If the to-be-linked term is the same as the pagename, display it unlinked.
		for _, item in ipairs(items) do
			if not item.term and (lang:makeEntryName(item.term)) == pagename then
				track("term is pagename")
				item.alt = item.alt or item.term
				item.term = nil
			end
		end
	end

	local labels
	if #raw_labels > 0 then
		labels = require(labels_module).process_raw_labels { labels = raw_labels, lang = lang, nocat = true }
	end

	local parts = {}
	local function ins(part)
		table.insert(parts, part)
	end

	-- Construct the final output.

	-- First the items, including separators, left and right regular qualifiers and left and right per-item labels.
	for _, item in ipairs(items) do
		ins(item.separator)
		local text = require(links_module).full_link(item, nil, allow_self_link)
		if item.q and item.q[1] or item.qq and item.qq[1] or item.l and item.l[1] or item.ll and item.ll[1]
			or item.refs and item.refs[1] then
			text = require(pron_qualifier_module).format_qualifiers {
				lang = item.lang,
				text = text,
				q = item.q,
				qq = item.qq,
				l = item.l,
				ll = item.ll,
				refs = item.refs,
			}
		end
		ins(text)
	end

	-- If there are labels, construct them now and append to final output.
	if labels then
		if lang:hasTranslit() then
			ins(" &mdash; " .. require(labels_module).format_processed_labels {
				labels = labels, lang = lang
			})
		else
			ins(" " .. require(labels_module).format_processed_labels {
				labels = labels, lang = lang, open = "(", close = ")"
			})
		end
	end

	return table.concat(parts)
end

--[==[
Template-callable function for displaying alternative forms.
]==]
function export.create(frame)
	local parent_args = frame:getParent().args
	local title = mw.title.getCurrentTitle()
	local PAGENAME = title.text
	return export.display_alternative_forms(parent_args, title)
end

return export