text: mapolder

title: mapolder


text:
Module:form of

local export = {}

export.force_cat = false -- for testing; set to true to display categories even on non-mainspace pages

local m_links = require("Module:links") local m_string_utils = require("Module:string utilities") local m_table = require("Module:table") local parse_utilities_module = "Module:parse utilities" local labels_module = "Module:labels" local utilities_module = "Module:utilities" export.form_of_pos_module = "Module:form of/pos" export.form_of_functions_module = "Module:form of/functions" export.form_of_cats_module = "Module:form of/cats" export.form_of_lang_data_module_prefix = "Module:form of/lang-data/" export.form_of_data_module = "Module:form of/data" export.form_of_data2_module = "Module:form of/data2"

local ulen = m_string_utils.len local rsubn = m_string_utils.gsub local rmatch = m_string_utils.match local rsplit = m_string_utils.split

export.TAG_TYPE = 1 export.GLOSSARY = 2 export.SHORTCUTS = 3 export.WIKIDATA = 4

export.APPENDIX = true export.WP = false export.WIKT = 0

--[==[ Set listing the languages with lang-specific tags. If a language isn't listed here, the tags for that language won't be recognized. ]==] export.langs_with_lang_specific_tags = { ["en"] = true, ["got"] = true, ["lt"] = true, ["lv"] = true, ["nl"] = true, ["pi"] = true, ["sw"] = true, ["ttj"] = true, }

--[==[ intro:

This module implements the underlying processing of form of, inflection of and specific variants such as past participle of and alternative spelling of. Most of the logic in this file is to handle tags in inflection of. Other related files:

tags, their shortcuts, the category they belong to (tense-aspect, case, gender, voice-valence, etc.), the appropriate glossary link and the wikidata ID. format as Module:form of/data. tags for the language with code LANGCODE, in the same format as Module:form of/data. Language-specific tags override general tags. appropriate combinations of tags are seen for a given language. categorization. FIXME: This should be unified with the parts of speech listed in Module:links. They are contained in this module because data-only modules can't contain code. The functions in this file are of two types: one such handler, for handling multipart person tags such as `1//2//3`. Currently no such functions exist.
 * Module:form of/templates contains the majority of the logic that implements the templates themselves.
 * Module:form of/data is a data-only file containing information on the more common inflection tags, listing the
 * Module:form of/data2 is a data-only file containing information on the less common inflection tags, in the same
 * Module:form of/lang-data/LANGCODE is a data-only file containing information on the language-specific inflection
 * Module:form of/cats is a data-only file listing the language-specific categories that are added when the
 * Module:form of/pos is a data-only file listing the recognized parts of speech and their abbreviations, used for
 * Module:form of/functions contains functions for use with Module:form of/data and Module:form of/cats.
 * Display handlers allow for customization of the display of multipart tags (see below). Currently there is only
 * Cat functions allow for more complex categorization logic, and are referred to by name in Module:form of/cats.

The following terminology is used in conjunction with inflection of:

are `masculine`, `nominative`, or `first-person`. Tags may be abbreviated, e.g. `m` for `masculine`, `nom` for `nominative`, or `1` for `first-person`. Such abbreviations are called aliases, and some tags have multiple equivalent aliases (e.g. `p` or `pl` for `plural`). The full, non-abbreviated form of a tag is called its canonical form. as its canonical form, and it normally functions as a link to a glossary entry explaining the tag. Usually the link is to an entry in Appendix:Glossary, but sometimes the tag is linked to an individual dictionary entry or to a  Wikipedia entry. Occasionally, the display text differs from the canonical form of the tag. An example is the tag `comparative case`, which has the display text read as simply `comparative`. Normally, tags referring to cases don't have the word "case" in them, but in this case the tag `comparative` was already used as an alias for the tag `comparative degree`, so the tag was named `comparative case` to avoid clashing. A similar situation occurs with `adverbial case` vs. the grammar tag `adverbial` (as in `adverbial participle`). which can be expanded to canonical-form tags as `first-person|singular|present|indicative`. `1|s|pres|ind|;|2|s|imp`, which specifies two tag sets, `1|s|pres|ind` as above and `2|s|imp` (in canonical form, `second-person|singular|imperative`). Multiple tag sets specified in a single call to inflection of are specified in this fashion. Conjoined tag sets can also occur in list-tag shortcuts. the case of syncretism, when the same form applies to multiple inflections. Examples are the Spanish present subjunctive, where the first-person and third-person singular have the same form (e.g. from  ), or Latin third-declension adjectives, where the dative and ablative plural of all genders have the same form (e.g. from ). These would be expressed respectively as `1//3|s|pres|sub` and `dat//abl|m//f//n|p`, where the use of the multipart tag compactly encodes the syncretism and avoids the need to individually list out all of the inflections. Multipart tags currently display as a list separated by a slash, e.g. dative/ablative or masculine/feminine/neuter where each individual word is linked appropriately. As a special case, multipart tags involving persons display specially; for example, the multipart tag `1//2//3` displays as first-, second- and third-person, with the word "person" occurring only once. individual tags. The tags within the tag set are joined by a colon, e.g. `1:s//3:p`, which is displayed as first-person singular and third-person plural, e.g. for use with the form  of the verb , which uses the tag set `1:s//3:p|impf|actv|indc|unaugmented` to express the syncretism between the first singular and third plural forms of the imperfect active indicative unaugmented conjugation. Two-level multipart tags should be used sparingly; if in doubt, list out the inflections separately. [FIXME: Make two-level multipart tags obsolete.] Aliases are a particular type of shortcut whose expansion is a single non-multipart tag. multipart tag `1//2//3`. Only the most common such combinations exist as shortcuts. the shortcut `1s` expands to `1|s` (first-person singular). Only the most common such combinations exist as shortcuts. sets). For example, the English language-specific shortcut `ed-form` expands to `spast|;|past|part`, expressing the common syncretism between simple past and past participle in English (and in this case, `spast` is itself a list shortcut that expands to `simple|past`). ]==]
 * A tag is a single grammatical item, as specified in a single numbered parameter of inflection of. Examples
 * The display form of a tag is the way it's displayed to the user. Usually the displayed text of the tag is the same
 * A tag set is an ordered list of tags, which together express a single inflection, for example, `1|s|pres|ind`,
 * A conjoined tag set is a tag set that consists of multiple individual tag sets separated by a semicolon, e.g.
 * A multipart tag is a tag that embeds multiple tags within it, such as `f//n` or `nom//acc//voc`. These are used in
 * A two-level multipart tag is a special type of multipart tag that joins two or more tag sets instead of joining
 * A shortcut is a tag that expands to any type of tag described above, or to any type of tag set described above.
 * A multipart shortcut is a shortcut that expands into a multipart tag, for example `123`, which expands to the
 * A list shortcut is a special type of shortcut that expands to a list of tags instead of a single tag. For example,
 * A conjoined shortcut is a special type of list shortcut that consists of a conjoined tag set (multiple logical tag

-- version of rsubn that discards all but the first return value local function rsub(term, foo, bar) local retval = rsubn(term, foo, bar) return retval end

local function normalize_index(list, index) if index < 0 then return #list + index + 1 end return index end

-- FIXME, consider moving to Module:table -- Return true if the list `tags1`, treated as a set, is a subset of the list `tags2`, also treated as a set. local function is_subset(tags1, tags2) tags1 = m_table.listToSet(tags1) tags2 = m_table.listToSet(tags2) for tag, _ in pairs(tags1) do		if not tags2[tag] then return false end end return true end

-- FIXME, move to Module:table local function slice(list, i, j)	--checkType("slice", 1, list, "table") --checkType("slice", 2, i, "number", true) --checkType("slice", 3, j, "number", true) if i == nil then i = 1 else i = normalize_index(list, i)	end j = normalize_index(list, j or -1)

local retval = {} local k = 0 for index = i, j do		k = k + 1 retval[k] = list[index] end return retval end

-- Add tracking category for PAGE when called from or -- similar TEMPLATE. The tracking category linked to is -- Tracking/inflection of/PAGE. local function track(page) require("Module:debug/track")("inflection of/" ..		-- avoid including links in pages (may cause error)		page:gsub("%[", "("):gsub("%]", ")"):gsub("|", "!")) end

local function wrap_in_span(text, classes) return (" %s "):format(classes, text) end

--[==[ Lowest-level implementation of form-of templates, including the general form of as well as those that deal with inflection tags, such as the general inflection of, semi-specific variants such as participle of, and specific variants such as past participle of. `data` contains all the information controlling the display, with the following fields:

directly to {full_link} in Module:links. Each object should have at minimum a `.lang` field containing the language of the lemma and a `.term` field containing the lemma itself. Each object is formatted using {full_link} and then if there are more than one, they are joined using {serialCommaJoin} in Module:table. Alternatively, `.lemmas` can be a string, which is displayed directly, or omitted, to show no lemma links and omit the connecting text. themselves forms of another lemma (the base lemma), e.g. a comparative, superlative or participle. Each object is of  the form { { paramobj = PARAM_OBJ, lemmas = {LEMMA_OBJ, LEMMA_OBJ, ...} }} where PARAM_OBJ describes the properties of the base lemma parameter (i.e. the relationship between the intermediate and base lemmas) and LEMMA_OBJ is an  object suitable to be passed to {full_link} in Module:links. PARAM_OBJ is of the format { { param = "PARAM", tags = {"TAG", "TAG", ...} } where PARAM is the name of the parameter to inflection of  etc. that holds the base lemma(s) of the specified relationship and the tags describe the relationship, such as   { {"comd"}} or { {"past", "part"}}. for the tag text and lemma links, and additionally {"form-of-definition-link"} specifically for the lemma links. (FIXME: Should separate out the lemma links into their own field.) ]==] function export.format_form_of(data) if type(data) ~= "table" then error("Internal error: First argument must now be a table of arguments") end local text_classes = data.text_classes or "form-of-definition use-with-mention" local lemma_classes = data.text_classes or "form-of-definition-link" local parts = {} local function ins(text) table.insert(parts, text) end ins("") ins(data.text) if data.text ~= "" and data.lemmas then ins(" ") end if data.lemmas then if type(data.lemmas) == "string" then ins(wrap_in_span(data.lemmas, lemma_classes)) else local formatted_terms = {} for _, lemma in ipairs(data.lemmas) do				table.insert(formatted_terms, wrap_in_span( m_links.full_link(lemma, data.lemma_face), lemma_classes ))			end ins(m_table.serialCommaJoin(formatted_terms)) end end if data.enclitics and #data.enclitics > 0 then -- The outer parens need to be outside of the text_classes span so they show in upright instead of italic, or -- they will clash with upright parens generated by link annotations such as transliterations and pos=. ins(" ") local formatted_terms = {} for _, enclitic in ipairs(data.enclitics) do -- FIXME, should we have separate clitic face and/or classes? table.insert(formatted_terms, wrap_in_span( m_links.full_link(enclitic, data.lemma_face, nil, "show qualifiers"), lemma_classes ))		end ins(" (")		ins(wrap_in_span("with enclitic" .. (#data.enclitics > 1 and "s" or "") .. " ", text_classes))		ins(m_table.serialCommaJoin(formatted_terms))		ins(")") ins("") end if data.base_lemmas and #data.base_lemmas > 0 then for _, base_lemma in ipairs(data.base_lemmas) do			ins(", the ") ins(export.tagged_inflections {				lang = base_lemma.lemmas[1].lang,				tags = base_lemma.paramobj.tags,				lemmas = base_lemma.lemmas,				lemma_face = data.lemma_face,				no_format_categories = true,				nocat = true,				text_classes = data.text_classes,			}) ins("") end end -- FIXME, should posttext go before enclitics? If so we need to have separate handling for the -- final colon when there are multiple tag sets in tagged_inflections. if data.posttext then ins(data.posttext) end ins(" ") return table.concat(parts) end
 * `.text`: Text to insert before the lemmas. Wrapped in the value of `.text_classes`, or its default; see below.
 * `.lemmas`: List of objects describing the lemma(s) of which the term in question is a non-lemma form. These are passed
 * `.lemma_face`: "Face" to use when displaying the lemma objects. Usually should be set to {"term"}.
 * `.enclitics`: List of enclitics to display after the lemmas, in parens.
 * `.base_lemmas`: List of base lemmas to display after the lemmas, in the case where the lemmas in `.lemmas` are
 * `.text_classes`: CSS classes used to wrap the tag text and lemma links. Default is {"form-of-definition use-with-mention"}
 * `.posttext`: Additional text to display after the lemma links.

--[==[ Return true if `tag` contains an internal link or HTML. ]==] function export.is_link_or_html(tag) return tag:find("[[", nil, true) or tag:find("|", nil, true) or tag:find("<", nil, true) end

--[==[ Look up a tag (either a shortcut of any sort of a canonical long-form tag) and return its expansion. The expansion will be a string unless the shortcut is a list-tag shortcut such as `1s`; in that case, the expansion will be a list. The caller must handle both cases. Only one level of expansion happens; hence, `acc` expands to {"accusative"}, `1s` expands to { {"1", "s"}} (not to { {"first", "singular"}}) and `123` expands to {"1//2//3"}. The expansion will be the same as the passed-in tag in the following circumstances:

 * 1) The tag is `;` (this is special-cased, and no lookup is done).
 * 2) The tag is a multipart tag such as `nom//acc` (this is special-cased, and no lookup is done).
 * 3) The tag contains a raw link (this is special-cased, and no lookup is done).
 * 4) The tag contains HTML (this is special-cased, and no lookup is done).
 * 5) The tag is already a canonical long-form tag.
 * 6) The tag is unrecognized.

This function first looks up in the lang-specific data module Module:form of/lang-data/LANGCODE, then in Module:form of/data (which includes more common non-lang-specific tags) and finally (only if the tag is not recognized as a shortcut or canonical tag, and is not of types 1-4 above) in Module:form of/data2.

If the expansion is a string and is different from the tag, track it if `do_track` is true. ]==] function export.lookup_shortcut(tag, lang, do_track) -- If there is HTML or a link in the tag, return it directly; don't try -- to look it up, which will fail. if tag == ";" or tag:find("//", nil, true) or export.is_link_or_html(tag) then return tag end local expansion local langcode = lang and lang:getCode if langcode and export.langs_with_lang_specific_tags[langcode] then local langdata = mw.loadData(export.form_of_lang_data_module_prefix .. langcode) -- If this is a canonical long-form tag, just return it, and don't check for shortcuts. This is an -- optimization; see below. if langdata.tags[tag] then return tag end expansion = langdata.shortcuts[tag] end if not expansion and lang then -- If the lang we're dealing with is an etym-only lang, try again with the corresponding full language. local full_langcode = lang:getFullCode if full_langcode ~= langcode and export.langs_with_lang_specific_tags[full_langcode] then local langdata = mw.loadData(export.form_of_lang_data_module_prefix .. full_langcode) -- If this is a canonical long-form tag, just return it, and don't check for shortcuts. This is an -- optimization; see below. if langdata.tags[tag] then return tag end expansion = langdata.shortcuts[tag] end end if not expansion then local m_data = mw.loadData(export.form_of_data_module) -- If this is a canonical long-form tag, just return it, and don't check for shortcuts (which will cause		-- Module:form of/data2 to be loaded, because there won't be a shortcut entry in Module:form of/data --		-- or, for that matter, in Module:form of/data2). This is an optimization; the code will still work without -- it, but will use up more memory. if m_data.tags[tag] then return tag end expansion = m_data.shortcuts[tag] end if not expansion then local m_data2 = mw.loadData(export.form_of_data2_module) expansion = m_data2.shortcuts[tag] end if not expansion then return tag end -- Maybe track the expansion if it's not the same as the raw tag. if do_track and expansion ~= tag and type(expansion) == "string" then track("tag/" .. tag) end return expansion end

--[==[ Look up a normalized/canonicalized tag and return the data object associated with it. If the tag isn't found, return nil. This first looks up in the lang-specific data module Module:form of/lang-data/LANGCODE, then in Module:form of/data (which includes more common non-lang-specific tags) and then finally in Module:form of/data2. ]==] function export.lookup_tag(tag, lang) local langcode = lang and lang:getCode if langcode and export.langs_with_lang_specific_tags[langcode] then local langdata = mw.loadData(export.form_of_lang_data_module_prefix .. langcode) if langdata.tags[tag] then return langdata.tags[tag] end end local full_langcode = lang and lang:getFullCode if full_langcode and full_langcode ~= langcode and export.langs_with_lang_specific_tags[full_langcode] then -- If the lang we're dealing with is an etym-only lang, try again with the corresponding full language. local langdata = mw.loadData(export.form_of_lang_data_module_prefix .. full_langcode) if langdata.tags[tag] then return langdata.tags[tag] end end local m_data = mw.loadData(export.form_of_data_module) local tagobj = m_data.tags[tag] if tagobj then return tagobj end local m_data2 = mw.loadData(export.form_of_data2_module) local tagobj2 = m_data2.tags[tag] if tagobj2 then return tagobj2 end return nil end

-- Normalize a single tag, which may be a shortcut but should not be a multipart tag, a multipart shortcut or a list -- shortcut. local function normalize_single_tag(tag, lang, do_track) local expansion = export.lookup_shortcut(tag, lang, do_track) if type(expansion) ~= "string" then error("Tag '" .. tag .. "' is a list shortcut, which is not allowed here") end tag = expansion if not export.lookup_tag(tag, lang) and do_track then -- If after all expansions and normalizations we don't recognize the canonical tag, track it. track("unknown") track("unknown/" .. tag) end return tag end

--[=[ Normalize a component of a multipart tag. This should not have any // in it, but may join multiple individual tags with a colon, and may be a single list-tag shortcut, which is treated as if colon-separated. The return value may be a list of tags. ]=] local function normalize_multipart_component(tag, lang, do_track) -- If there is HTML or a link in the tag, don't try to split on colon. A colon may legitimately occur in either one, -- and we don't want these things parsed. Note that we don't do this check before splitting on //, which we don't	-- expect to occur in links or HTML; see comment in normalize_tag. if export.is_link_or_html(tag) then return tag end local components = rsplit(tag, ":", true) if #components == 1 then -- We allow list-tag shortcuts inside of multipart tags, e.g. -- '1s//3p'. Check for this now. tag = export.lookup_shortcut(tag, lang, do_track) if type(tag) == "table" then -- Temporary tracking as we will disallow this. track("list-tag-inside-of-multipart") -- We found a list-tag shortcut; treat as if colon-separated. components = tag else return normalize_single_tag(tag, lang, do_track) end end local normtags = {} -- Temporary tracking as we will disallow this. track("two-level-multipart") for _, component in ipairs(components) do		if do_track then -- There are multiple components; track each of the individual -- raw tags. track("tag/" .. component) end table.insert(normtags, normalize_single_tag(component, lang, do_track)) end

return normtags end

--[=[ Normalize a single tag. The return value may be a list (in the case of multipart tags), which will contain nested lists in the case of two-level multipart tags. ]=] local function normalize_tag(tag, lang, do_track) -- We don't check for links or HTML before splitting on //, which we don't expect to occur in links or HTML. Doing -- it this way allows for a tag like '//' to function correctly (the template calls	-- will be expanded before we process the tag, and will contain links and HTML). The only check we do is for a URL, -- which shouldn't normally occur, but might if the user tries to put an external link into the tag. URL's with // -- normally have the sequence ://, which should never normally occur when // and : are used in their normal ways. if tag:find("://", nil, true) then return tag end local split_tags = rsplit(tag, "//", true) if #split_tags == 1 then local retval = normalize_multipart_component(tag, lang, do_track) if type(retval) == "table" then -- The user gave a tag like '1:s', i.e. with colon but without //. Allow this, but we need to return a -- nested list. return {retval} end return retval end local normtags = {} for _, single_tag in ipairs(split_tags) do		if do_track then -- If the tag was a multipart tag, track each of individual raw tags. track("tag/" .. single_tag) end table.insert(normtags, normalize_multipart_component(single_tag, lang, do_track)) end return normtags end

--[==[ Normalize a tag set (a list of tags) into its canonical-form tags. The return value is a list of normalized tag sets (a list because of there may be conjoined shortcuts among the input tags). A normalized tag set is a list of tag elements, where each element is either a string (the canonical form of a tag), a list of such strings (in the case of multipart tags) or a list of lists of such strings (in the case of two-level multipart tags). For example, the multipart tag `nom//acc//voc` will be represented in canonical form as { {"nominative", "accusative", "vocative"}}, and the two-level multipart tag `1:s//3:p` will be represented as { {{"first-person", "singular"}, {"third-person", "plural"}}}.

Example 1:

{normalize_tag_set({"nom//acc//voc", "n", "p"})} = { }

Example 2:

{normalize_tag_set({"ed-form"}, ENGLISH)} = { {{"simple", "past"}, {"past", "participle"}}}

Example 3:

{normalize_tag_set({"archaic", "ed-form"}, ENGLISH)} = { {{"archaic", "simple", "past"}, {"archaic", "past", "participle"}}} ]==] function export.normalize_tag_set(tag_set, lang, do_track) -- We track usage of shortcuts, normalized forms and (in the case of multipart tags or list tags) intermediate -- forms. For example, if the tags 1s|mn|gen|indefinite are passed in, we track the following: -- Tracking/inflection of/tag/1s -- Tracking/inflection of/tag/1 -- Tracking/inflection of/tag/s -- Tracking/inflection of/tag/first-person -- Tracking/inflection of/tag/singular -- Tracking/inflection of/tag/mn -- Tracking/inflection of/tag/m//n -- Tracking/inflection of/tag/m -- Tracking/inflection of/tag/n -- Tracking/inflection of/tag/masculine -- Tracking/inflection of/tag/neuter -- Tracking/inflection of/tag/gen -- Tracking/inflection of/tag/genitive -- Tracking/inflection of/tag/indefinite local output_tag_set = {} local saw_semicolon = false

for _, tag in ipairs(tag_set) do		if do_track then -- Track the raw tag. track("tag/" .. tag) end -- Expand the tag, which may generate a new tag (either a fully canonicalized tag, a multipart tag, or a list		-- of tags). tag = export.lookup_shortcut(tag, lang, do_track) if type(tag) == "table" then saw_semicolon = m_table.contains(tag, ";") if saw_semicolon then -- If we saw a conjoined shortcut, we need to use a more general algorithm that can expand a single -- tag set into multiple. break end

for _, t in ipairs(tag) do				if do_track then -- If the tag expands to a list of raw tags, track each of those. track("tag/" .. t)				end table.insert(output_tag_set, normalize_tag(t, lang, do_track)) end else table.insert(output_tag_set, normalize_tag(tag, lang, do_track)) end end

if not saw_semicolon then return {output_tag_set} end

-- Use a more general algorithm that handles conjoined shortcuts. local output_tag_set = {} for i, tag in ipairs(tag_set) do		if do_track then -- Track the raw tag. track("tag/" .. tag) end -- Expand the tag, which may generate a new tag (either a fully canonicalized tag, a multipart tag, or a list		-- of tags). tag = export.lookup_shortcut(tag, lang, do_track) if type(tag) == "table" then local output_tag_sets = {} local shortcut_tag_sets = export.split_tag_set(tag) local normalized_shortcut_tag_sets = {} for _, shortcut_tag_set in ipairs(shortcut_tag_sets) do				m_table.extendList(normalized_shortcut_tag_sets,					export.normalize_tag_set(shortcut_tag_set, lang, do_track)) end local after_tags = slice(tag_set, i + 1) local normalized_after_tags_sets = export.normalize_tag_set(after_tags, lang, do_track) for _, normalized_shortcut_tag_set in ipairs(normalized_shortcut_tag_sets) do				for _, normalized_after_tags_set in ipairs(normalized_after_tags_sets) do					table.insert(output_tag_sets, m_table.append(output_tag_set, normalized_shortcut_tag_set, normalized_after_tags_set)) end end return output_tag_sets else table.insert(output_tag_set, normalize_tag(tag, lang, do_track)) end end

error("Internal error: Should not get here") end

function export.combine_multipart_tags(tag_set) for i, tag in ipairs(tag_set) do		if type(tag) == "table" then for j, subtag in ipairs(tag) do				if type(subtag) == "table" then tag[j] = table.concat(subtag, ":") end end tag_set[i] = table.concat(tag, "//") end end

return tag_set end

function export.normalize_tags(tags, lang, recombine_multitags, do_track) local tag_sets = export.normalize_tag_set(tags, lang, do_track) if recombine_multitags then for i, tag_set in ipairs(tag_sets) do			tag_sets[i] = export.combine_multipart_tags(tag_set) end return export.combine_tag_sets(tag_sets) end return tag_sets end

--[==[ Split a tag set containing two-level multipart tags into one or more tag sets not containing such tags. Single-level multipart tags are left alone. (If we need to, a slight modification of the following code will also split single-level multipart tags.) This assumes that multipart tags are represented as lists and two-level multipart tags are represented as lists of lists, as is output by {normalize_tag_set}. NOTE: We have to be careful to properly handle imbalanced two-level multipart tags such as `def:s//p` (or the reverse, `s//def:p`). ]==] function export.split_two_level_multipart_tag_set(tag_set) for i, tag in ipairs(tag_set) do		if type(tag) == "table" then -- We saw a multipart tag. Check if any of the parts are two-level. local saw_two_level_tag = false for _, first_level_tag in ipairs(tag) do				if type(first_level_tag) == "table" then saw_two_level_tag = true break end end if saw_two_level_tag then -- We found a two-level multipart tag. -- (1) Extract the preceding tags. local pre_tags = slice(tag_set, 1, i - 1) -- (2) Extract the following tags. local post_tags = slice(tag_set, i + 1) -- (3) Loop over each tag set alternant in the two-level multipart tag. -- For each alternant, form the tag set consisting of pre_tags + alternant + post_tags, -- and recursively split that tag set. local resulting_tag_sets = {} for _, first_level_tag_set in ipairs(tag) do					local expanded_tag_set = {} m_table.extendList(expanded_tag_set, pre_tags) -- The second level may have a string or a list. if type(first_level_tag_set) == "table" then m_table.extendList(expanded_tag_set, first_level_tag_set) else table.insert(expanded_tag_set, first_level_tag_set) end m_table.extendList(expanded_tag_set, post_tags) m_table.extendList(resulting_tag_sets, export.split_two_level_multipart_tag_set(expanded_tag_set)) end return resulting_tag_sets end end end

return {tag_set} end

--[==[ Split a tag set that may consist of multiple semicolon-separated tag sets into the component tag sets. ]==] function export.split_tag_set(tag_set) local split_tag_sets = {} local cur_tag_set = {} for _, tag in ipairs(tag_set) do		if tag == ";" then if #cur_tag_set > 0 then table.insert(split_tag_sets, cur_tag_set) end cur_tag_set = {} else table.insert(cur_tag_set, tag) end end if #cur_tag_set > 0 then table.insert(split_tag_sets, cur_tag_set) end return split_tag_sets end

export.split_tags_into_tag_sets = export.split_tag_set

--[==[ Combine multiple tag sets in a tag set group into a simple tag set, with logical tag sets separated by semicolons. This is the opposite of {split_tag_set}. ]==] function export.combine_tag_sets(tag_sets) if #tag_sets == 1 then return tag_sets[1] end local combined_tag_set = {} for _, tag_set in ipairs(tag_sets) do		if #combined_tag_set > 0 then table.insert(combined_tag_set, ";") end m_table.extendList(combined_tag_set, tag_set) end return tags end

local tag_set_param_mods = { lb = { item_dest = "labels", convert = function(arg, parse_err) return rsplit(arg, "//", true) end, } }

--[==[ Parse tag set properties from a tag set (list of tags). Currently no per-tag properties are recognized, and the only per-tag-set property recognized is `` for specifing label(s) for the tag set. Per-tag-set properties must be attached to the last tag. ]==] function export.parse_tag_set_properties(tag_set) local function generate_tag_set_obj(last_tag) tag_set[#tag_set] = last_tag return {tags = tag_set} end local last_tag = tag_set[#tag_set] -- Check for inline modifier, e.g. מרים. But exclude HTML entry with, , or -- similar in it, caused by wrapping an argument in, 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. , 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. if last_tag:find("<") and not last_tag:find("^[^<]*<[a-z]*[^a-z:]") then return require(parse_utilities_module).parse_inline_modifiers(last_tag, {			param_mods = tag_set_param_mods,			generate_obj = generate_tag_set_obj,		}) else return generate_tag_set_obj(last_tag) end end

function export.normalize_pos(pos) if not pos then return nil end return mw.loadData(export.form_of_pos_module)[pos] or pos end

-- Return the display form of a single canonical-form tag. The value -- passed in must be a string (i.e. it cannot be a list describing a -- multipart tag). To handle multipart tags, use get_tag_display_form. local function get_single_tag_display_form(normtag, lang) local data = export.lookup_tag(normtag, lang) local display = normtag

-- If the tag has a special display form, use it	if data and data.display then display = data.display end

-- If there is a nonempty glossary index, then show a link to it	local glossary = data and data[export.GLOSSARY] if glossary ~= nil then if glossary == export.WIKT then display = "" .. display .. "" elseif glossary == export.WP then display = "" .. display .. "" elseif glossary == export.APPENDIX then display = "" .. display .. "" elseif type(glossary) ~= "string" then error(("Internal error: Wrong type %s for glossary value %s for tag %s"):format( type(glossary), mw.dumpObject(glossary), normtag)) else local link = rmatch(glossary, "^wikt:(.*)") if link then display = "" .. display .. "" end if not link then link = rmatch(glossary, "^w:(.*)") if link then display = "" .. display .. "" end end if not link then display = "" .. display .. "" end end end return display end

--[==[ Turn a canonicalized tag spec (which describes a single, possibly multipart tag) into the displayed form. The tag spec may be a string (a canonical-form tag); a list of canonical-form tags (in the case of a simple multipart tag); or a list of mixed canonical-form tags and lists of such tags (in the case of a two-level multipart tag). `joiner` indicates how to join the parts of a multipart tag, and can be either {"and"} ("foo and bar", or "foo, bar and baz" for 3 or more), {"slash"} ("foo/bar"), {"en-dash"} ("foo–bar") or {nil}, which uses the global default found in {multipart_join_strategy} in Module:form of/functions. (NOTE: The global default is {"slash"} and this seems unlikely to change.) ]==] function export.get_tag_display_form(tagspec, lang, joiner) if type(tagspec) == "string" then return get_single_tag_display_form(tagspec, lang) end -- We have a multipart tag. See if there's a display handler to display them specially. for _, handler in ipairs(require(export.form_of_functions_module).display_handlers) do		local displayval = handler(tagspec, joiner) if displayval then return displayval end end -- No display handler. local displayed_tags = {} for _, first_level_tag in ipairs(tagspec) do		if type(first_level_tag) == "string" then table.insert(displayed_tags, get_single_tag_display_form(first_level_tag, lang)) else -- A first-level element of a two-level multipart tag. Currently we just separate the individual components -- with spaces, but other ways are possible, e.g. using an underscore, colon, parens or braces. local components = {} for _, component in ipairs(first_level_tag) do				table.insert(components, get_single_tag_display_form(component, lang)) end table.insert(displayed_tags, table.concat(components, " ")) end end return require(export.form_of_functions_module).join_multiparts(displayed_tags, joiner) end

--[==[ Given a normalized tag set (i.e. as output by {normalize_tag_set}; all tags are in canonical form, multipart tags are represented as lists, and two-level multipart tags as lists of lists), convert to displayed form (a string). See {get_tag_display_form} for the meaning of `joiner`. ]==] function export.get_tag_set_display_form(normalized_tag_set, lang, joiner) local parts = {}

for _, tagspec in ipairs(normalized_tag_set) do		local to_insert = export.get_tag_display_form(tagspec, lang, joiner) -- Maybe insert a space before inserting the display form of the tag. We insert a space if		-- (a) we're not the first tag; and -- (b) the tag we're about to insert doesn't have the "no_space_on_left" property; and -- (c) the preceding tag doesn't have the "no_space_on_right" property. -- NOTE: We depend here on the fact that -- (1) all tags with either of the above properties set have the same display form as canonical form, and -- (2) all tags with either of the above properties set are single-character tags. -- The second property is an optimization to avoid looking up display forms resulting from multipart tags, -- which won't be found and which will trigger loading of Module:form of/data2. If multichar punctuation is -- added in the future, it's ok to change the == 1 below to <= 2 or <= 3. --		-- If the first property above fails to hold in the future, we need to track the canonical form of each tag -- (including the previous one) as well as the display form. This would also avoid the need for the == 1 check. if #parts > 0 then local most_recent_tagobj = ulen(parts[#parts]) == 1 and export.lookup_tag(parts[#parts], lang) local to_insert_tagobj = ulen(to_insert) == 1 and export.lookup_tag(to_insert, lang) if (				(not most_recent_tagobj or not most_recent_tagobj.no_space_on_right) and				(not to_insert_tagobj or not to_insert_tagobj.no_space_on_left)			) then table.insert(parts, " ") end end table.insert(parts, to_insert) end

return table.concat(parts) end

--[==[ Given a normalized tag set (i.e. as output by {normalize_tag_set}; all tags are in canonical form, multipart tags are represented as lists, and two-level multipart tags as lists of lists), fetch the associated categories and labels. Return two values, a list of categories and a list of labels. `lang` is the language of term represented by the tag set, and `POS` is the user-provided part of speech (which may be {nil}). ]==] function export.fetch_categories_and_labels(normalized_tag_set, lang, POS, pagename, lemmas) local m_cats = mw.loadData(export.form_of_cats_module) local categories = {} local labels = {}

POS = export.normalize_pos(POS) -- First split any two-level multipart tags into multiple sets, to make our life easier. for _, tag_set in ipairs(export.split_two_level_multipart_tag_set(normalized_tag_set)) do		-- Call a named function, either from the lang-specific data in -- Module:form of/lang-specific/LANGCODE/functions or in Module:form of/functions. local function call_named_function(name, funtype) local data = { pagename = pagename or mw.title.getCurrentTitle.subpageText, lemmas = lemmas, tag_set = normalized_tag_set, lang = lang, POS = POS }			local modules_tried = {} local function try_lang_specific_module(langcode) if export.langs_with_lang_specific_tags[langcode] then local lang_specific_module = export.form_of_lang_data_module_prefix .. langcode .. "/functions" local langdata = require(utilities_module).safe_require(lang_specific_module) if langdata then table.insert(modules_tried, lang_specific_module) if langdata.cat_functions then local fn = langdata.cat_functions[name] if fn then return fn(data), true end end end end return nil, false end -- First try lang-specific. local langcode = lang and lang:getCode if langcode then local retval, found_it = try_lang_specific_module(langcode) if found_it then return retval end end -- If the lang we're dealing with is an etym-only lang, try again with the corresponding full language. local full_langcode = lang and lang:getFullCode if full_langcode and full_langcode ~= langcode then local retval, found_it = try_lang_specific_module(full_langcode) if found_it then return retval end end -- Try lang-independent. table.insert(modules_tried, export.form_of_functions_module) local fn = require(export.form_of_functions_module).cat_functions[name] if fn then return fn(data) end for i, modname in ipairs(modules_tried) do modules_tried[i] = "" .. modname .. "" end error(("No %s function named '%s' in %s"):format(funtype, name, lang_specific_part, m_table.serialCommaJoin(modules_tried, {conj = "or", dontTag = true}))) end

-- Given a tag from the current tag set (which may be a list in case of a multipart tag), -- and a tag from a categorization spec, check that the two match. -- (1) If both are strings, we just check for equality. -- (2) If the spec tag is a string and the tag set tag is a list (i.e. it originates from a		-- multipart tag), we check that the spec tag is in the list. This is because we want to treat -- multipart tags in user-specified tag sets as if the user had specified multiple tag sets. -- For example, if the user said "1//3|s|pres|ind" and the categorization spec says {"has", "1"}, -- we want this to match, because "1//3|s|pres|ind" should be treated equivalently to two tag -- sets "1|s|pres|ind" and "3|s|pres|ind", and the former matches the categorization spec. -- (3) If the spec tag is a list (i.e. it originates from a multipart tag), we check that the -- tag set tag is also a list and is a superset of the spec tag. For example, if the categorization -- spec says {"has", "1//3"}, then the tag set tag must be a multipart tag that has both "1" and "3" -- in it. "1//3" works, as does "1//2//3". local function tag_set_tag_matches_spec_tag(tag_set_tag, spec_tag) if type(spec_tag) == "table" then if type(tag_set_tag) == "table" and is_subset(spec_tag, tag_set_tag) then return true end elseif type(tag_set_tag) == "table" then if m_table.contains(tag_set_tag, spec_tag) then return true end elseif tag_set_tag == spec_tag then return true end return false end

-- Check that the current tag set matches the given spec tag. This means that any of the tags -- in the current tag set match, according to tag_set_tag_matches_spec_tag; see above. If the -- current tag set contains only string tags (i.e. no multipart tags), and the spec tag is a -- string (i.e. not a multipart tag), this boils down to list containment, but it gets more -- complex when multipart tags are present. local function tag_set_matches_spec_tag(spec_tag) spec_tag = normalize_tag(spec_tag, lang) for _, tag_set_tag in ipairs(tag_set) do				if tag_set_tag_matches_spec_tag(tag_set_tag, spec_tag) then return true end end return false end

-- Check whether the given spec matches the current tag set. Two values are returned: -- (1) whether the spec matches the tag set; (2) the index of the category to add if -- the spec matches. local function check_condition(spec) if type(spec) == "boolean" then return spec elseif type(spec) ~= "table" then error("Wrong type of condition " .. spec .. ": " .. type(spec)) end local predicate = spec[1] if predicate == "has" then return tag_set_matches_spec_tag(spec[2]), 3 elseif predicate == "hasall" then for _, tag in ipairs(spec[2]) do					if not tag_set_matches_spec_tag(tag) then return false, 3 end end return true, 3 elseif predicate == "hasany" then for _, tag in ipairs(spec[2]) do					if tag_set_matches_spec_tag(tag) then return true, 3 end end return false, 3 elseif predicate == "tags=" then local normalized_spec_tag_sets = export.normalize_tag_set(spec[2], lang) if #normalized_spec_tag_sets > 1 then error("Internal error: No support for conjoined shortcuts in category/label specs in "						.. "Module:form of/cats when processing spec tag set " .. table.concat(spec[2], "|")) end local normalized_spec_tag_set = normalized_spec_tag_sets[1] -- Check for and disallow two-level multipart tags in the specs. FIXME: Remove this when we remove -- support for two-level multipart tags. for _, tag in ipairs(normalized_spec_tag_set) do					if type(tag) == "table" then for _, subtag in ipairs(tag) do							if type(subtag) == "table" then error("Internal error: No support for two-level multipart tags in category/label specs"									.. "Module:form of/cats when processing spec tag set "									.. table.concat(spec[2], "|")) end end end end -- Allow tags to be in different orders, and multipart tags to be in different orders. To handle this, -- we first check that both tag set tags and spec tags have the same length. If so, we sort the -- multipart tags in the tag set tags and spec tags, and then check that all tags in the spec tags are -- in the tag set tags. if #tag_set ~= #normalized_spec_tag_set then return false, 3 end local tag_set_tags = m_table.deepcopy(tag_set) for i=1,#tag_set_tags do					if type(tag_set_tags[i]) == "table" then table.sort(tag_set_tags[i]) end if type(normalized_spec_tag_set[i]) == "table" then table.sort(normalized_spec_tag_set[i]) end end for i=1,#tag_set_tags do					if not m_table.contains(tag_set_tags, normalized_spec_tag_set[i]) then return false, 3 end end return true, 3 elseif predicate == "p=" then return POS == export.normalize_pos(spec[2]), 3 elseif predicate == "pany" then for _, specpos in ipairs(spec[2]) do					if POS == export.normalize_pos(specpos) then return true, 3 end end return false, 3 elseif predicate == "pexists" then return POS ~= nil, 2 elseif predicate == "not" then local condval = check_condition(spec[2]) return not condval, 3 elseif predicate == "and" then local condval = check_condition(spec[2]) if condval then condval = check_condition(spec[3]) end return condval, 4 elseif predicate == "or" then local condval = check_condition(spec[2]) if not condval then condval = check_condition(spec[3]) end return condval, 4 elseif predicate == "call" then return fn(call_named_function(spec[2], "condition")), 3 else error("Unrecognized predicate: " .. predicate) end end

-- Process a given spec. This checks any conditions in the spec against the -- tag set, and insert any resulting categories into `categories`. Return value -- is true if the outermost condition evaluated to true and a category was inserted -- (this is used in {"cond" ...} conditions, which stop when a subcondition evaluates		-- to true). local function process_spec(spec) if not spec then return false elseif type(spec) == "string" then -- A category. Substitute POS request with user-specified part of speech or default. spec = rsub(spec, "<>", function(default)					return POS or export.normalize_pos(default)				end) table.insert(categories, lang:getFullName .. " " .. spec) return true elseif type(spec) == "table" and spec.labels then -- A label spec. for _, label in ipairs(spec.labels) do					m_table.insertIfNot(labels, label) end return true elseif type(spec) ~= "table" then error("Wrong type of specification " .. spec .. ": " .. type(spec)) end local predicate = spec[1] if predicate == "multi" then -- WARNING! #spec doesn't work for objects loaded from loadData for i, sp in ipairs(spec) do					if i > 1 then process_spec(sp) end end return true elseif predicate == "cond" then -- WARNING! #spec doesn't work for objects loaded from loadData for i, sp in ipairs(spec) do					if i > 1 and process_spec(sp) then return true end end return false elseif predicate == "call" then return process_spec(call_named_function(spec[2], "spec")) else local condval, ifspec = check_condition(spec) if condval then process_spec(spec[ifspec]) return true else process_spec(spec[ifspec + 1]) -- FIXME: Are we sure this is correct? return false end end end

local langcode = lang:getCode local langspecs = m_cats[langcode] if langspecs then for _, spec in ipairs(langspecs) do				process_spec(spec) end end local full_code = lang:getFullCode if full_code ~= langcode then local langspecs = m_cats[full_code] if langspecs then for _, spec in ipairs(langspecs) do					process_spec(spec) end end end if full_code ~= "und" then local langspecs = m_cats["und"] if langspecs then for _, spec in ipairs(langspecs) do					process_spec(spec) end end end end

return categories, labels end

--[==[ Implementation of templates that display inflection tags, such as the general inflection of, semi-specific variants such as participle of, and specific variants such as past participle of. `data` contains all the information controlling the display, with the following fields:

labels, and for displaying categories and labels. can be indicated by a {";"} as one of the tags, and tag-set properties may be attached to the last tag of a tag set. The tags themselves may come directly from the user (as in inflection of); come partly from the user (as in participle of, which adds the tag `part` to user-specified inflection tags); or be entirely specified by the template (as in past participle of). per-tag-set properties. Each element of the list is an object of the form { {tags = {"TAG", "TAG", ...}, labels = {"LABEL", "LABEL", ...}}. If `.tag_sets` is specified, `.tags` should not be given and vice-versa. Specifying `.tag_sets` in place of tags allowed per-tag set labels to be specified; otherwise, there is no advantage. Module:pt-gl-inflections uses this functionality to supply labels like {"Brazil"} and {"Portugal"} associated with specific tag sets. non-lemma form. These are passed directly to {full_link} in Module:links. Each object should have at minimum a `.lang` field containing the language of the lemma and a `.term` field containing the lemma itself. Each object is formatted using {full_link} and then if there are more than one, they are joined using {serialCommaJoin} in  Module:table. Alternatively, `.lemmas` can be a string, which is displayed directly. If omitted entirely, no lemma links are shown and the connecting "of" is also omitted. {"term"}. inflection of. {"form-of-definition use-with-mention"} for the tag text, {"form-of-definition-link"} for the lemma links. (FIXME: Should separate out the lemma links into their own field.) `.joiner`: Override the joiner (normally a slash) used to join multipart tags. You should normally not specify this.
 * `.lang`: (required) Language to use when looking up language-specific inflection tags, categories and
 * `.tags`: (required unless `.tag_sets` is given) List of non-canonicalized inflection tags. Multiple tag sets
 * `.tag_sets`: (required unless `.tags` is given) List of non-canonicalized tag sets and associated
 * `.lemmas`: (recommended) List of objects describing the lemma(s) of which the term in question is a
 * `.lemma_face`: (recommended) "Face" to use when displaying the lemma objects. Usually should be set to
 * `.POS`: (recommended) Categorizing part-of-speech tag. Comes from the p or POS argument of
 * `.pagename`: Page name of "current" page or nil to use the actual page title; for testing purposes.
 * `.enclitics`: List of enclitics to display after the lemmas, in parens.
 * `.no_format_categories`: If true, don't format the categories derived from the inflection tags; just return them.
 * `.sort`: Sort key for formatted categories. Ignored when `.no_format_categories` = {true}.
 * `.nocat`: Suppress computation of categories (even if `.no_format_categories` is not given).
 * `.notext`: Disable display of all tag text and `inflection of` text. (FIXME: Maybe not implemented correctly.)
 * `.capfirst`: Capitalize the first word displayed.
 * `.pretext`: Additional text to display before the inflection tags, but after any top-level labels.
 * `.posttext`: Additional text to display after the lemma links.
 * `.text_classes`: CSS classes used to wrap the tag text and lemma links. Default is

A typical call might look like this (for ): { local lang = require("Module:languages").getByCode("es")

local lemma_obj = { lang = lang, term = "amar", }

return m_form_of.tagged_inflections({		lang = lang, tags = {"1", "s", "pres", "ind"}, lemmas = {lemma_obj}, lemma_face = "term", POS = "verb"	}) }

Normally, one value is returned, the formatted text, which has appended to it the formatted categories derived from the tag-set-related categories generated by the specs in [Module:form of/cats]]. To suppress this, set `data.no_format_categories` = {true}, in which case two values are returned, the formatted text without any formatted categories appended and a list of the categories to be formatted.

NOTE: There are two sets of categories that may be generated: (1) categories derived directly from the tag sets, as specified in Module:form of/cats; (2) categories derived from tag-set labels, either (a) set explicitly by the caller in `data.tag_sets`, (b) specified by the user using `` attached to the last tag in a tag set, or (c) specified in Module:form of/cats. The second type (label-related categories) are currently not returned in the second return value of {tagged_inflections}, and are currently inserted into the output text even if `data.no_format_categories` is set to {true}; but they can be suppressed by setting `data.nocat` = {true} (which also suppresses the first type of categories, those derived directly from tag sets, even if `data.no_format_categories` is set to {true}). ]==] function export.tagged_inflections(data) if not data.tags and not data.tag_sets then error("First argument must be a table of arguments, and `.tags` or `.tag_sets` must be specified") end if data.tags and data.tag_sets then error("Both `.tags` and `.tag_sets` cannot be specified") end local tag_sets = data.tag_sets if not tag_sets then tag_sets = export.split_tag_set(data.tags) for i, tag_set in ipairs(tag_sets) do			tag_sets[i] = export.parse_tag_set_properties(tag_set) end end

local inflections = {} local categories = {} for _, tag_set in ipairs(tag_sets) do		local normalized_tag_sets = export.normalize_tag_set(tag_set.tags, data.lang, "do-track")

for _, normalized_tag_set in ipairs(normalized_tag_sets) do			local cur_infl = {} local this_categories, this_labels = export.fetch_categories_and_labels(normalized_tag_set, data.lang,				data.POS, data.pagename, type(data.lemmas) == "table" and data.lemmas or nil) if not data.nocat then m_table.extendList(categories, this_categories) end local cur_infl = export.get_tag_set_display_form(normalized_tag_set, data.lang, data.joiner) if #cur_infl > 0 then if tag_set.labels then this_labels = m_table.append(tag_set.labels, this_labels) end table.insert(inflections, {infl_text = cur_infl, labels = this_labels}) end end end

local overall_labels, need_per_tag_set_labels for _, inflection in ipairs(inflections) do		if overall_labels == nil then overall_labels = inflection.labels elseif not m_table.deepEquals(overall_labels, inflection.labels) then need_per_tag_set_labels = true overall_labels = nil break end end

if not need_per_tag_set_labels then for _, inflection in ipairs(inflections) do			inflection.labels = nil end end

local format_data = m_table.shallowcopy(data)

local function format_labels(labels, notext) if labels and #labels > 0 then return require(labels_module).show_labels { labels = labels, lang = data.lang, sort = data.sort, nocat = data.nocat } .. (notext and (data.pretext or "") == "" and "" or " ") else return "" end end

local of_text = data.lemmas and " of" or "" local formatted_text if #inflections == 1 then if need_per_tag_set_labels then error("Internal error: need_per_tag_set_labels should not be set with one inflection") end format_data.text = format_labels(overall_labels, data.notext) .. (data.pretext or "") .. (data.notext and "" or			((data.capfirst and require("Module:string utilities").ucfirst(inflections[1].infl_text) or inflections[1].infl_text) .. of_text)) formatted_text = export.format_form_of(format_data) else format_data.text = format_labels(overall_labels, data.notext) .. (data.pretext or "") .. (data.notext and "" or			((data.capfirst and "Inflection" or "inflection") .. of_text)) format_data.posttext = (data.posttext or "") .. ":"		local link = export.format_form_of(format_data) local text_classes = data.text_classes or "form-of-definition use-with-mention" for i, inflection in ipairs(inflections) do inflections[i] = "\n## " .. format_labels(inflection.labels, false) .. "" .. inflection.infl_text .. " "		end formatted_text = link .. table.concat(inflections) end

if not data.no_format_categories then if #categories > 0 then formatted_text = formatted_text .. require("Module:utilities").format_categories(categories, data.lang,				data.sort, nil, export.force_cat) end return formatted_text end return formatted_text, categories end

--[==[ Given a tag set, return a flattened list all Wikidata ID's of all tags in the tag set. FIXME: Only used in a debugging function in Module:se-verbs; move there. ]==] function export.to_Wikidata_IDs(tag_set, lang, skip_tags_without_ids) local ret = {}

local function get_wikidata_id(tag) local data = export.lookup_tag(tag, lang)

if not data or not data[export.WIKIDATA] then if not skip_tags_without_ids then error('The tag "' .. tag .. '" does not have a Wikidata ID defined in the form-of data modules') else return nil end else return ("Q%s"):format(data[export.WIKIDATA]) end end

local normalized_tag_sets = export.normalize_tag_set(tag_set, lang) for _, tag_set in ipairs(normalized_tag_sets) do		for _, tag in ipairs(tag_set) do			if type(tag) == "table" then for _, subtag in ipairs(tag) do					if type(subtag) == "table" then -- two-level multipart tag; FIXME: delete support for this for _, subsubtag in ipairs(subtag) do							table.insert(ret, get_wikidata_id(subsubtag)) end else table.insert(ret, get_wikidata_id(subtag)) end end else table.insert(ret, get_wikidata_id(tag)) end end end

return ret end

function export.dump_form_of_data(frame) local data = { data = require(export.form_of_data_module), data2 = require(export.form_of_data2_module) }	return require("Module:JSON").toJSON(data) end

function export.finalize_tag_data(tags, shortcuts) local function process_shortcut(name, shortcut) -- If the shortcut is already in the list, then there is a duplicate. if shortcuts[shortcut] then error("The shortcut \"" .. shortcut .. "\" (for the inflection tag \"" .. name .. "\") conflicts with an existing shortcut for the tag \"" .. shortcuts[shortcut] .. "\".") elseif tags[shortcut] then error("The shortcut \"" .. shortcut .. "\" (for the inflection tag \"" .. name .. "\") conflicts with an existing tag with that name.") end

shortcuts[shortcut] = name end for name, data in pairs(tags) do		local data_shortcuts = data[export.SHORTCUTS] if data_shortcuts then if type(data_shortcuts) == "string" then process_shortcut(name, data_shortcuts) else for _, shortcut in ipairs(data_shortcuts) do					process_shortcut(name, shortcut) end end end end end

return export

license: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/

title: Module:form of


text:
Հուբեյ

Proper noun

 * 1) Hubei

title: Հուբեյ


text:
nuorempi konstaapeli

Noun

 * 1) constable, police constable lowest rank in the Finnish police force, below vanhempi konstaapeli

Usage notes
For a listing of Finnish police ranks see usage notes under.

title: nuorempi konstaapeli


text:
タッチパッド

Etymology
Borrowed from.

Noun

 * 1)  a touchpad

title: タッチパッド


text:
Malacca cane

Etymology
From the former importance of as a port for the rattan trade and  in its senses both as a reed-like plant and as a walking stick.

Noun

 * , a species of thick rattan climbing palm native to Southeast Asia; its material; closely similar species and their material.
 * 1)  A walking stick made of C. scipionum or similar material with a rich but mottled brown color.
 * 1)  A walking stick made of C. scipionum or similar material with a rich but mottled brown color.
 * 1)  A walking stick made of C. scipionum or similar material with a rich but mottled brown color.

title: Malacca cane


text: burug

title: burug


text: トゥールーズ

title: トゥールーズ


text:
トルマリン

Etymology

 * see 🇨🇬.

Noun

 * 1) tourmaline

title: トルマリン


text:
バベルの塔

Proper noun

 * 1)  the Tower of Babel

title: バベルの塔


text:
phantom tumour

Noun

 * 1) A swelling, especially of the abdomen, due to muscular spasm, accumulation of flatus, etc., simulating an actual tumour in appearance, but disappearing upon the administration of an anaesthetic.

title: phantom tumour


text:
Template:Apidae Hypernyms

 * Apocrita – suborder; Apoidea – superfamily; Apidae – family

title: Template:Apidae Hypernyms


text:
Template:Apidae Hypernyms/documentation

This is a typing shortcut intended for use under the Hypernyms header of taxa under Apidae. It can also be used with subst:.

title: Template:Apidae Hypernyms/documentation


text:
spray condenser

Noun

 * 1)  An injection condenser in which the steam is condensed by a spray of water which mingles with it.

title: spray condenser


text:
therva

Etymology
From ; cognate with Old English þearf, Old High German darba, Old Icelandic þörf, Gothic. Compare thurvan.

Noun

 * 1) need, distress

title: therva


text: ĉevaliĉo

title: ĉevaliĉo


text:
User:Wolf Hoog

Hi!

I'm rather new here. So please excuse mistakes.

My mother tongue is German and as an author I like to concern myself with words.

You find more information about me in german on http://hoog.at and some projects in english on http://en.hoog.at.

title: User:Wolf Hoog


text:
User talk:Wolf Hoog

Equinox ◑ 01:23, 15 December 2013 (UTC)
 * Thanks! :-) --Wolf Hoog (talk) 01:30, 15 December 2013 (UTC)

Lizard King
Who uses this? Is it capitalized? Mglovesfun (talk) 22:51, 29 December 2013 (UTC)

Hi! Sorry for the late answer! Had many other things to do. I don't know. Jim Morrison for example. Initially I just stumbled upon the interesting homophony of "Lizard King" and "lizardling" and wanted to share this in the wikipedia article. But one of the deleting-fetishists there forced me to spin this of to wiktionary. I hope, people here prefer to help make things better rather than just deleting. For me the information is more important than cantankerousness & smugness. ;-) --Wolf Hoog (talk) 15:24, 18 January 2014 (UTC)

 * Homophones have to sound identical, like rose and rows. Lizard King and lizardling do not sound identical because one has a K sound where the other has an L sound. Equinox ◑ 16:53, 18 January 2014 (UTC)
 * In music, movies and normal life I never hear people speak this correct. And in German homophones often differ in much more than this one letter. So what's the right term if two words sound just nearly identical?

 * I suppose you'd just call them "near-homophones". Even with the various lazy ways to pronounce English (e.g. glottal stop for t in British English, or dropping a final g as in "sleepin'") I can't imagine that any native speaker would pronounce Lizard King and lizardling identically. Equinox ◑ 03:51, 19 January 2014 (UTC)

 * If "curse" is a homophone of "course" than "Lizard King" is a homophone of "lizardling".
 * I just wanted to help here but if some people here are just addicted to clear and everyone else just want to excuse them, it's no wonder when athors are getting less and less.--Wolf Hoog (talk) 00:16, 2 August 2014 (UTC)

 * But "curse" is not a homophone of "course". You're really weird. Equinox ◑ 00:52, 2 August 2014 (UTC)

title: User talk:Wolf Hoog


text:
thurft

Etymology
From ; cognate with 🇨🇬, 🇨🇬 (🇨🇬), 🇨🇬. Compare, and.

Noun

 * 1) need, lack, necessity

title: thurft


text: thurfti

title: thurfti


text: Category:Old Saxon noun forms

title: Category:Old Saxon noun forms


text: thervo

title: thervo


text:
Ancylaini

Etymology
, an irregular formation to avoid conflict with (from ), a tribe of limpets

Hypernyms

 * ; - subfamily

Hyponyms

 * - type genus, - other genus

title: Ancylaini


text: tharvo

title: tharvo


text: Template:osx-decl-noun-a-f-v

title: Template:osx-decl-noun-a-f-v


text:
姉妹都市

Noun

 * 1) sister city

title: 姉妹都市


text: Category:Japanese terms spelled with 妹 read as まい


text: Category:Japanese terms spelled with 姉 read as し


text:
Template:Hylidae Hypernyms

 * Lissamphibia - subclass; Anura - order; Neobatrachia - suborder; Hylidae - family

title: Template:Hylidae Hypernyms


text:
Template:Hylidae Hypernyms/documentation

This is a typing shortcut intended for use under the Hypernyms header of taxa under Hylidae. It can also be used with subst:.


text:
気管

Noun

 * 1) trachea

title: 気管


text:
kinghead

Noun

 * , a form of ragweed.

title: kinghead


text:
mnijeti

Etymology
, from.

Verb

 * 1)  to think

title: mnijeti


text:
貿易風

Noun

 * 1) trade wind

Noun

 * 1) a trade wind

title: 貿易風


text:
商標名

Noun

 * 1) brand name, trade name name used to identify a commercial product or service

title: 商標名


text:
Template talk:Nihongo

Template:Nihongo
This template has been broken, for how long I don't know, but it was only used on two entries. It appears that the only purpose of this template was to stick a little superscript question mark ? which was linked to wikipedia:Help:Japanese. That sort of feature is common on Wikipedia but not here as far as I know. Haplogy (話) 06:52, 12 November 2013 (UTC)
 * It's an exact copy of the version that was on Wikipedia at the time. It also has two subtemplates: and . It takes three parameters, and uses some css classes, so there may be more to it than just the ?- perhaps to display English with the Japanese translation and romaji. At any rate, we're not Wikipedia and we don't need to use their formatting- so delete. Chuck Entz (talk) 07:26, 12 November 2013 (UTC)
 * Since 02:51, 15 April 2008 apparently. Delete, not needed, use or  or something. Mglovesfun (talk) 13:00, 12 November 2013 (UTC)
 * Deleted. Haplogy (話) 03:32, 15 December 2013 (UTC)

title: Template talk:Nihongo


text: svagaj

title: svagaj


text: svagan

title: svagan


text: svagajn

title: svagajn


text: reĝimoj

title: reĝimoj


text: reĝimon

title: reĝimon


text:
pokape

Adverb

 * 1) per capita

title: pokape


text: funebroj

title: funebroj


text: funebron

title: funebron


text: reĝimojn

title: reĝimojn


text: funebrojn

title: funebrojn


text: gratefullest

title: gratefullest


text: Template:imperfective form of/documentation


text: anotha'

title: anotha'


text:
ロードコーン

Etymology
.

Noun

 * 1) traffic cone

title: ロードコーン


text:
gulaŝo

Noun

 * 1) goulash

title: gulaŝo


text: gulaŝon

title: gulaŝon


text: anotha

title: anotha


text:
sieĝo

Etymology
From and, from , from. Compare 🇨🇬. .

Noun

 * 1) siege

title: sieĝo


text: sieĝoj

title: sieĝoj


text: sieĝon

title: sieĝon


text: sieĝojn

title: sieĝojn


text:
vilao

Noun

 * 1) villa

title: vilao


text: vilaoj

title: vilaoj


text: vilaon

title: vilaon


text: vilaojn

title: vilaojn


text:
先駆者

Noun

 * 1) trailblazer
 * 2) forerunner

title: 先駆者


text:
leoninely

Adverb

 * 1) In a leonine manner.

Translations

 * Finnish: leijonamainen

title: leoninely


text:
foiro

Etymology
..

Noun

 * 1) fair, market

title: foiro


text: foiroj

title: foiroj


text: foiron

title: foiron


text: foirojn

title: foirojn


text:
lipstick tree

Etymology
From, from the resemblance of the open fruit pods of the plant to red lips, or from the fact that the consistency of the arils surrounding its seeds is similar to that of lipstick (see the 1942 quotation).

Noun

 * 1) The shrub, which is native to Mexico and northern South America; the arils covering its seeds are a source of the orange-red colourant annatto, and the ground seeds are used in traditional Caribbean, Central American, and South American cuisine.

title: lipstick tree


text: lipstick trees

title: lipstick trees


text: lastatempaj

title: lastatempaj


text:
Thread:User talk:CodeCat/Redundant categories

Is this really necessary?


text: lastatempan

title: lastatempan


text:
Thread:User talk:CodeCat/Redundant categories/reply

Currently there are a number of form-of templates that add entries to a "noun form of" category, while this isn't needed because this category should be added by the headword template. I'm fixing these now. The "nocat=1" is a way to keep track of which instances of the templates have been confirmed to not need the category.


text: lastatempajn

title: lastatempajn


text:
Savanto

Etymology
From, present participle of.

Proper noun

 * 1) Savior

title: Savanto


text: Savanton

title: Savanton


text:
下肢

Noun

 * 1) lower limbs

Noun

 * 1) lower limbs, legs

title: 下肢


text:
Thread:User talk:CodeCat/Redundant categories/reply (2)

Ok that makes sense. So you're planning on removing the categorization in form-of templates after this is all done, and then running the bot to remove the nocats again?


text: non-believers

title: non-believers


text:
nekredanto

Etymology
From.

Noun

 * 1) nonbeliever

title: nekredanto


text: nekredantoj

title: nekredantoj


text: nekredanton

title: nekredanton


text: nekredantojn

title: nekredantojn


text:
照準

Noun

 * 1) one's aim, a sight at something

Verb

 * 1) set one's sights, aim, train (a gun etc.)

title: 照準


text:
研修生

Noun

 * 1) trainee

title: 研修生


text:
Thread:User talk:CodeCat/Redundant categories/reply (3)

Yes. I'm doing it in a two-stage process so that I can be certain that entries aren't accidentally orphaned after the category is removed from the template. is only added if the script "sees" another template on the page that already adds the same category, like. That way it becomes a process of elimination, cutting down the numbers gradually in steps until nothing is left. Once that's done, then the categories can be removed from the templates, and the  parameter can be removed too.

I won't remove it on all form-of templates. The ones I'm working on are the templates for 8 "Indo-European cases". These are probably our oldest grammatical form-of templates so they probably reflect older practices that are not done anymore. The category is one of those, since most other grammatical form-of templates don't add categories either. This brings them in line with the rest.


text:
Nord-Irlando

Etymology
From.

Proper noun

 * 1) Northern Ireland

title: Nord-Irlando


text: Nord-Irlandon

title: Nord-Irlandon


text: paresthesiae

title: paresthesiae


text:
補助輪

Noun

 * 1) training wheel

title: 補助輪


text:
Thread:User talk:CodeCat/Redundant categories/reply (4)

[//en.wiktionary.org/w/index.php?title=aeterni&oldid=15633791 Here] is an example of how these categories can cause problems. Notice how it's an adjective form but it's added to the "noun forms" category anyway.


text: staboj

title: staboj


text: stabon

title: stabon


text: stabojn

title: stabojn


text:
stabano

Noun

 * 1) staff member, employee

title: stabano


text: stabanoj

title: stabanoj


text: stabanon

title: stabanon


text: stabanojn

title: stabanojn


text:
トランシーバー

Etymology
From.

Noun

 * 1) transceiver

title: トランシーバー


text:
vivipova

Adjective

 * 1) viable

title: vivipova


text: vivipovaj

title: vivipovaj


text: vivipovan

title: vivipovan


text: vivipovajn

title: vivipovajn


text: Template:fi-decl-vanhempi-paperi

title: Template:fi-decl-vanhempi-paperi


text:
vanhempi konstaapeli

Noun

 * 1) senior constable, constable, police constable second lowest rank in the Finnish police force, above nuorempi konstaapeli and below ylikonstaapeli

Usage notes
For a listing of Finnish police ranks see.

title: vanhempi konstaapeli


text:
User:Moonspell Bloodlines

Haíls, meins namo ist Moonspell Bloodlines. Ik im gutan liubands in Amerika.

Hi, my name is Moonspell Bloodlines. I am a goth living in America.

Classical Latin
These will be Classical Latin terms I'll add once I get the Oxford Latin Dictionary. I will only put Classical terms in my pages, I hate sermo vulgaris (Vulgar Latin). I will also have a section made for my constructed language "Neo-Classical" Latin, my modernized version of Classical Latin.

User:Moonspell Bloodlines/Classical Latin nouns:

User:Moonspell Bloodlines/Classical Latin verbs:

Gothic
This will be my Gothic neologism section, because i like Gothic and i want it to be popular again. There's a hand full of people who speak Gothic and/or know of it.

Gothic subjects:

Gothic nouns:

Gothic verbs:

Gothic names:

Neo-Classical Latin
User:Moonspell Bloodlines/Neo-Classical Latin nouns:

title: User:Moonspell Bloodlines


text:
Schwirrvogel

Noun

 * 1)  hummingbird

title: Schwirrvogel


text: bilos

title: bilos


text: Schwirrvogels

title: Schwirrvogels


text:
User talk:Moonspell Bloodlines

Equinox ◑ 05:35, 15 December 2013 (UTC)

hi
Thanks for welcoming me here, appreciate it :)

Moonspell Bloodlines (talk) 05:44, 15 December 2013 (UTC)Moonspell Bloodlines

Your Gothic words
I noticed that you created a page with some made-up Gothic words. The Germanic descendant of would be  or similar, although I presume that the consonant cluster would be simplified to something like. This would then be in Gothic, and it would be an i-stem. 20:59, 15 December 2013 (UTC)

thanks for the tip
I think the Gothic word would be ugkns because the proto-germanic nk* became Gothic gk. For example, *ugkns, *ugkuns, *unns possibly. I try to have fun with Gothic, like what you did with Proto-Germanic. Moonspell Bloodlines (talk) 21:17, 15 December 2013 (UTC)
 * Gothic gk is just a way of spelling nk that it got from Ancient Greek, which has words like.
 * As for having fun, that's all well and good, but the main purpose for user pages is to help in the task of editing the dictionary. We tend to give a lot more leeway to someone like CodeCat, who's making monumental contributions on a daily basis, than to someone who hasn't contributed anything to the dictionary beyond a single edit that had to be deleted because we don't allow made-up terms in the main dictionary. Just a word of warning: I have no intention of deleting your subpages, but there are others who might, if that's all you're ever going to work on (see WT:USER). Chuck Entz (talk) 05:47, 16 December 2013 (UTC)
 * I understand, and i know that there is thousands of Gothic words not yet with entries, but they do have romanizations. I've been trying to look at what those words are, but the net doesn't really have any information on them, what can we do to improve that? Moonspell Bloodlines (talk) 06:21, 16 December 2013 (UTC)

Translation
Hello, could you translate Automatic refresh (Latin: Renovatio automatica) to Gothic language? Thanks! :) -XQV- (talk) 14:28, 21 October 2016 (UTC)

title: User talk:Moonspell Bloodlines


text: Schwirrvögel

title: Schwirrvögel


text:
rikosylikonstaapeli

Noun

 * 1) detective sergeant ; a detective rank in Finnish police, equal in seniority to.

title: rikosylikonstaapeli


text:
minster house

Noun

 * 1) The official house in which the canons of a cathedral live in common or in rotation.

title: minster house


text: Schwirrvögeln

title: Schwirrvögeln


text:
User talk:Mborrus

Your new entries are pretty good for first tries, but they're missing headwords and definitions (see WT:ELE) and we don't do redirects like that (see WT:REDIR). Words like that are especially tricky because they're eye dialect alternative forms for another, and slang is usually dealt with by putting the template in front of the definition. Chuck Entz (talk) 05:55, 15 December 2013 (UTC)

title: User talk:Mborrus


text: Nägeln

title: Nägeln


text: Шивэр

title: Шивэр


text:
τζάουλ

Etymology
Transliteration of the name of the physicist James Joule

Noun

 * 1)  joule

title: τζάουλ


text: έργου

title: έργου


text: έργων

title: έργων


text:
έργα

Noun

 * 1) roadworks

title: έργα


text:
User:Moonspell Bloodlines/peitsa

Etymology
From Italian.

Noun
peitsa

 * 1) pizza

title: User:Moonspell Bloodlines/peitsa


text: κάποια

title: κάποια


text: κάποιες

title: κάποιες


text: κάποιοι

title: κάποιοι


text: κάποιους

title: κάποιους


text:
κάποιων

Alternative forms

 * — Used orally to differentiate between and.

title: κάποιων


text: καποιανών

title: καποιανών


text: κάποιο

title: κάποιο


text: κάποιου

title: κάποιου


text: κάποιε

title: κάποιε


text: κάποιας

title: κάποιας


text: καποιανού

title: καποιανού


text: καποιανής

title: καποιανής


text:
κάποιον

Pronunciation

 * — usage: is used orally for differentiation.

Pronoun

 * Είδα κάποιον να έρχεται I saw someone coming.
 * Είδα κάποιον να έρχεται I saw someone coming.

title: κάποιον


text:
Bixa

Etymology
From, from.

Hyponyms

 * (lipstick tree) - type species; - selected other species

title: Bixa


text:
Bixaceae

Etymology
From.

Usage notes
Previously classified in order Violales, genus is sometimes placed in its own family,.

title: Bixaceae


text:
Appendix:Serbo-Croatian pronunciation

See Serbo-Croatian phonology at Wikipedia for a thorough look at the sounds of Serbo-Croatian.

title: Appendix:Serbo-Croatian pronunciation


text:
ylietsivä

Etymology
From.

Noun

 * 1) detective sergeant ; a detective rank in Finnish police, equal in seniority to.

title: ylietsivä


text:
Appendix:Bulgarian pronunciation

See Bulgarian phonology at Wikipedia for a thorough look at the sounds of Bulgarian.

title: Appendix:Bulgarian pronunciation


text:
Appendix:Russian pronunciation

See at Wikipedia for a thorough look at the sounds of Russian.

Consonants
Basic consonants as in the alphabet:

Palatalisation
Palatalised ("soft") consonants: bʲ, pʲ, vʲ, fʲ, gʲ, kʲ dʲ, tʲ, zʲ, sʲ, lʲ, mʲ, nʲ, rʲ, xʲ, (t͡sʲ). [j], [t͡ɕ] and [ɕː] are always palatalised ("soft"), [ʐ] and [ʂ] are almost always unpalatalised ("hard").

Palatalisation for other consonants typically occurs: TO DO
 * 1) In front of vowels: "е", "ё", "и", "ю", "я" and in front of "ь" ("soft sign"), see also note on "ь" as a "separation sign".
 * E.g.: вре́мя [ˈvrʲemʲə], ребёнок [rʲɪˈbʲɵnək], пита́ние [pʲɪˈtanʲɪjə], колю́чка [kɐˈlʲʉt͡ɕkə], пря́ник [ˈprʲænʲɪk], день [dʲenʲ]
 * 1) Many loanwords are NOT palatalised in front of "е" (= э), e.g., "тест" (= тэст) is pronounced [tɛst]. ("е" is then pronounced [ɛ] in stressed positions, [ɨ] in unstressed positions)

Vowels
Basic vowels as in the alphabet:

NOTES:
 * The difference between "palatal" and "non-palatal" vowels is actually their effect on a preceding consonant. Palatal vowels palatalize a preceding consonant, while non-palatal vowels don't. If no consonant directly precedes (i.e. either at the beginning of a word, following a vowel or following a soft or hard sign), a is pronounced before the palatal vowels, e.g.,  is pronounced . This, however, is omitted in some situations: (1) with palatal и (stressed or unstressed), except when following the soft sign ь (hence  is pronounced , and  is pronounced ); (2) optionally with other palatal vowels when unstressed and following a vowel (hence  is pronounced ).
 * The two pronunciations of я and ю are related to what follows: If a palatalized consonant follows, the second-listed, more-fronted pronunciations are used; otherwise, the first-listed, less-fronted pronunciations  are used. When я is unstressed, no such difference exists but it still does with unstressed ю.
 * Unstressed о and а are always pronounced when word-initial and when directly following another unstressed о or а, regardless of the position of the stress.
 * Unstressed э when word-initial and when following a vowel is pronounced, exactly as for и in the same position.

title: Appendix:Russian pronunciation


text:
Appendix:Georgian pronunciation

See Georgian phonology at Wikipedia for a thorough look at the sounds of Georgian.

title: Appendix:Georgian pronunciation


text:
Appendix:Lithuanian pronunciation

See Lithuanian phonology at Wikipedia for a thorough look at the sounds of Lithuanian.

title: Appendix:Lithuanian pronunciation


text:
Appendix:Finnish pronunciation

This page details the pronunciation of Standard Finnish (Standard Spoken Finnish, ), which is, unless otherwise specified, the spoken variety used to document Finnish pronunciations on the English Wiktionary. It is the standard prestige variety used in e.g. formal discussions, newscasts and official speeches, and is the primary variety taught in schools.

Key
Legend:
 * IPA: The phoneme expressed in the IPA (International Phonetic Alphabet). If the symbol is a link, it points to the relevant Wikipedia article.
 * Examples: A Finnish word containing the phoneme, both as short (ungeminated for consonants) and long (geminated for consonants), as available. Each word is accompanied by an audio clip.
 * English approximation: A rough approximation of the sound in an English word. This is only a rough approximation and should not be taken as an accurate representation of the sound.
 * Explanation: An explanation of the sound.
 * Notes: Additional notes in the form of footnotes.

Consonants
All consonants except and  may appear geminated. Geminated is however very rare.

Plosives
Standard Finnish native plosives are unvoiced and unaspirated:, ,.

An additional 'native' can be found as the weak grade of  (under consonant gradation), and thus only occurs between vowels, either independently or as part of  (thus, native  is never geminated). This originates as a spelling pronunciation by Swedish speakers learning Finnish during the 19th century, as it was spelled (earlier also ) in an attempt to represent, its pronunciation in the Southwest Finnish literary standard at the time. In other dialects, it may be realized as, , or lost, only leaving behind e.g. a glide. Even in Standard Finnish, the consonant may in rapid speech become an.

Recent borrowings can also contain the phonemes, and , which are pronounced as voiced plosives, although in monolingual Finnish speech they may only be partially voiced, if at all. However, minimal pairs do exist.

For (potential) glottal stops, see final gemination below.

Fricatives
Native words only have two fricatives: and. The pronunciation of is highly variable. It is often devoiced and somewhat retracted, but may be voiced between two vowels in fast speech, and may even become a "sh-sound" after rounded vowels.

Two more fricatives can be found in loanwords: and. The latter is often pronounced as if it were if there is no risk of confusion.

Finnish is not a fricative.

Rhotic
The single Finnish rhotic is a rolled R, but if ungeminated, may be realized as a tap between two vowels in rapid speech.

Lateral
Finnish is always a bright L, never a dark (velarized) L.

Nasals
Finnish has three nasals:, and. While the first two are common and can appear in any position, in native words is only found either as part of   or   between two vowels. In loanwords, it may appear as a short even before other consonants or at the end of a word.

Approximants
and are approximants in Finnish. The latter is spelled, but is not a fricative.

Vowels
All vowels may appear short or long, and there is very little if any difference in quality between the short and long variants of the same vowel.

is, a decidedly back vowel. , and  are,  and  respectively, all front vowels. Finnish, and  are mid vowels.

Standard Finnish has 18 diphthongs which are listed in the tables above.

Final gemination

 * See also:  on Wikipedia

Some Finnish words or word roots feature final gemination (also called boundary lengthening, Finnish: ), which is usually marked with (Finnish: ; see also the entry for this symbol). This feature (argued to be morphophonetic) originates from the loss of some final consonants (primarily or ) always follows a vowel, and its realization depends on what follows it:

 * If is followed by a consonant, the consonant becomes geminated if it isn't already.
 * If is followed by a vowel or the end of the utterance, it usually manifests as a , which may or may not be geminated, and may even be completely omitted in rapid speech.

This feature is not indicated in the Finnish orthography, but results in (albeit marginal). It can at times even surface within words due to clitics  →  (respelled ), even though final gemination does not affect possessive suffixes. However, there are cases in which final gemination has in effect become grammaticalized, such as the partitive singular of →, in which the geminated consonant is spelled with gemination.

In standard Finnish, final gemination occurs primarily in the following cases:

 * nominals:
 * (the nominative singular forms of) nominals belonging to type 48 (""); for some speakers, may be missing in compounds if not the final component, more often in rapid speech
 * allative singular and plural forms of nominals
 * verbs:
 * first infinitive forms (dictionary forms)
 * note that the third-person singular present indicative does not have final gemination even if it is a homograph (i.e. spelled identically)
 * connegative forms of verbs (except for conditional, in all persons, and imperative, in all other persons than the second-person singular)
 * second-person singular imperative forms of verbs (identical with the indicative connegative)
 * adverbs:
 * the adverbial cases prolative and sublative
 * adverbs derived with the suffixes, and
 * certain individual adverbs and adpositions
 * the third-person possessive suffix

The following features may or may not have final gemination depending on the idiolect (speaker and variety):

 * nominals:
 * comitative forms of adjectives (i.e. when not followed by a possessive suffix)
 * verbs:
 * past passive participles
 * conditional connegative
 * the numeral
 * the pronoun

Furthermore, in some dialects, final gemination is completely absent.

Stress
Stress occurs on the first syllable of any given word. In compounds, secondary stress occurs at the beginning of each component word, which may result in stress falling on consecutive syllables if a component is only a single syllable.

Secondary stress also occurs roughly on odd syllables, although it may skip up to two light syllables if a heavy syllable follows (e.g. → o.me.na.nam.me). This kind of secondary stress is weaker than the phonemic secondary stress found in compound words, as described above. The placement of these secondary stresses can be described formulaically, as the Finnish pronunciations on Wiktionary often do. However, they are not phonemic and can depend on the speaker and context. Songs and poems, in particular, may take significant liberties.

Some suffixes may also incur secondary stress automatically, in which case they may 'steal' it from an immediately preceding syllable. Syllables containing (the start of) certain inflectional suffixes, including possessive suffixes, may also avoid secondary stress, by e.g. shifting it to the preceding syllable, even if it is a light syllable.

Sandhi
Finnish is rich in sandhi phenomena. Besides the final gemination listed above, there are instances of assimilation: is generally realized as if it were  and  as.

title: Appendix:Finnish pronunciation


text:
бяцхан

Etymology
From.

Adjective

 * 1) diminutive, elvish, little, measly, teeny, tiny, wee, miniature, piccaninny, pygmy

title: бяцхан


text:
Appendix:Faroese pronunciation

See at Wikipedia for a thorough look at the sounds of Faroese.

title: Appendix:Faroese pronunciation


text:
Appendix:Old English pronunciation

See at Wikipedia for a thorough look at the sounds of Old English.

title: Appendix:Old English pronunciation


text:
Appendix:Danish pronunciation

See at Wikipedia for a thorough look at the sounds of Danish and w:Help:IPA for Danish for the standard used in Wiktionary entries.

title: Appendix:Danish pronunciation


text:
Appendix:Armenian pronunciation

See at Wikipedia for a thorough look at the sounds of Armenian.

title: Appendix:Armenian pronunciation


text:
Appendix:Mandarin pronunciation

See at Wikipedia for a thorough look at the sounds of Mandarin.

See

 * Old National Pronunciation

title: Appendix:Mandarin pronunciation


text:
Appendix:Romanian pronunciation

See the Wikipedia article for a thorough look at the sounds of Romanian, or the help page  for the symbols used in phonetic transcriptions of Romanian words.

title: Appendix:Romanian pronunciation


text:
Appendix:Icelandic pronunciation

See at Wikipedia for a thorough look at the sounds of Icelandic.

title: Appendix:Icelandic pronunciation


text:
Appendix:Korean pronunciation

See at Wikipedia for a thorough look at the sounds of Korean.

This page uses the (the official South Korean Korean language romanization system) unless noted otherwise.

Overall Korean phonemically has these vowels and diphthongs and consonants: Only seven consonants are allowed at the end of a phonemic syllable: ㄱ /k/, ㄴ /n/, ㄷ /t/, ㄹ /l/, ㅁ /m/, ㅂ /p/, ㅇ /ŋ/; all other consonants and clusters assimilate into these ones. Inside a word each one of these consonants can be followed by another consonant, allowing for 95 consonant clusters: All other clusters assimilate into these ones. The clusters ㄱㄲ /kk͈/, ㄱㅋ /kkʰ/, ㄷㄸ /tt͈/, ㄷㅆ /ts͈/, ㄷㅉ /tt͈ɕ/, ㄷㅊ /ttɕʰ/, ㄷㅌ /ttʰ/, ㅂㅃ /pp͈/, ㅂㅍ /ppʰ/, ㄴㄴ /nn/, ㄹㄹ /ll/ and ㅁㅁ /mm/ can be considered as double consonants ㄱㄲ /k͈ː/, ㄱㅋ /kːʰ/, ㄷㄸ /t͈ː/, ㄷㅆ /s͈ː/, ㄷㅉ /t͈ɕː/, ㄷㅊ /tɕːʰ/, ㄷㅌ /tːʰ/, ㅂㅃ /p͈ː/, ㅂㅍ /pːʰ/, ㄴㄴ /nː/, ㄹㄹ /lː/ and ㅁㅁ /mː/.
 * 1) ㄱ /k/, ㄲ /k͈/, ㄴ /n/, ㄷ /t/, ㄸ /t͈/, ㄹ /l/, ㅁ /m/, ㅂ /p/, ㅃ /p͈/, ㅅ /s/, ㅆ /s͈/, ㅇ /ŋ/, ㅈ /tɕ/, ㅉ /t͈ɕ/, ㅊ /tɕʰ/, ㅋ /kʰ/, ㅌ /tʰ/, ㅍ /pʰ/, ㅎ /h/ (all of which, except for ㅇ /ŋ/, are allowed at the beginning of a phonemic syllable)
 * 2) ㅏ /a/, ㅐ /ɛ/, ㅑ /ja/, ㅒ /jɛ/, ㅓ /ʌ/, ㅔ /e/, ㅕ /jʌ/, ㅖ /je/, ㅗ /o/, ㅘ /wa/, ㅙ /wɛ/, ㅚ /ø/, ㅛ /jo/, ㅜ /u/, ㅝ /wʌ/, ㅞ /we/, ㅟ /y/, ㅠ /ju/, ㅡ /ɯ/, ㅢ /ɰi/, ㅣ /i/
 * 1) ㄱㄲ /kk͈/, ㄱㄸ /kt͈/, ㄱㅃ /kp͈/, ㄱㅆ /ks͈/, ㄱㅉ /kt͈ɕ/, ㄱㅊ /ktɕʰ/, ㄱㅋ /kkʰ/, ㄱㅌ /ktʰ/, ㄱㅍ /kpʰ/
 * 2) ㄷㄲ /tk͈/, ㄷㄸ /tt͈/, ㄷㅃ /tp͈/, ㄷㅆ /ts͈/, ㄷㅉ /tt͈ɕ/, ㄷㅊ /ttɕʰ/, ㄷㅋ /tkʰ/, ㄷㅌ /ttʰ/, ㄷㅍ /tpʰ/
 * 3) ㅂㄲ /pk͈/, ㅂㄸ /pt͈/, ㅂㅃ /pp͈/, ㅂㅆ /ps͈/, ㅂㅉ /pt͈ɕ/, ㅂㅊ /ptɕʰ/, ㅂㅋ /pkʰ/, ㅂㅌ /ptʰ/, ㅂㅍ /ppʰ/
 * 4) ㅇㄱ /ŋk/, ㅇㄲ /ŋk͈/, ㅇㄴ /ŋn/, ㅇㄷ /ŋt/, ㅇㄸ /ŋt͈/, ㅇㅁ /ŋm/, ㅇㅂ /ŋp/, ㅇㅃ /ŋp͈/, ㅇㅅ /ŋs/, ㅇㅆ /ŋs͈/, ㅇㅈ /ŋtɕ/, ㅇㅉ /ŋt͈ɕ/, ㅇㅊ /ŋtɕʰ/, ㅇㅋ /ŋkʰ/, ㅇㅌ /ŋtʰ/, ㅇㅍ /ŋpʰ/, ㅇㅎ /ŋh/
 * 5) ㄴㄱ /nk/, ㄴㄲ /nk͈/, ㄴㄴ /nn/, ㄴㄷ /nt/, ㄴㄸ /nt͈/, ㄴㅁ /nm/, ㄴㅂ /np/, ㄴㅃ /np͈/, ㄴㅅ /ns/, ㄴㅆ /ns͈/, ㄴㅈ /ntɕ/, ㄴㅉ /nt͈ɕ/, ㄴㅊ /ntɕʰ/, ㄴㅋ /nkʰ/, ㄴㅌ /ntʰ/, ㄴㅍ /npʰ/, ㄴㅎ /nh/
 * 6) ㄹㄱ /lk/, ㄹㄲ /lk͈/, ㄹㄹ /ll/, ㄹㄷ /lt/, ㄹㄸ /lt͈/, ㄹㅁ /lm/, ㄹㅂ /lp/, ㄹㅃ /lp͈/, ㄹㅅ /ls/, ㄹㅆ /ls͈/, ㄹㅈ /ltɕ/, ㄹㅉ /lt͈ɕ/, ㄹㅊ /ltɕʰ/, ㄹㅋ /lkʰ/, ㄹㅌ /ltʰ/, ㄹㅍ /lpʰ/, ㄹㅎ /lh/
 * 7) ㅁㄱ /mk/, ㅁㄲ /mk͈/, ㅁㄴ /mn/, ㅁㄷ /mt/, ㅁㄸ /mt͈/, ㅁㅁ /mm/, ㅁㅂ /mp/, ㅁㅃ /mp͈/, ㅁㅅ /ms/, ㅁㅆ /ms͈/, ㅁㅈ /mtɕ/, ㅁㅉ /mt͈ɕ/, ㅁㅊ /mtɕʰ/, ㅁㅋ /mkʰ/, ㅁㅌ /mtʰ/, ㅁㅍ /mpʰ/, ㅁㅎ /mh/

title: Appendix:Korean pronunciation


text:
Appendix:Turkish pronunciation

See at Wikipedia for a thorough look at the sounds of Turkish.

title: Appendix:Turkish pronunciation


text:
Talk:sockdolager

Obsolete
I’ve marked this word “obsolete”, as per “sockdolager” thread at Wordreference.com – I’ve only seen this once, in a consciously archaic usage.
 * —Nils von Barth (nbarth) (talk) 07:32, 15 December 2013 (UTC)

title: Talk:sockdolager


text:
Appendix:Persian pronunciation

View for more general information. Also see and.

Read foot notes for the pronunciation information below for additional pronunciation information.

Schwa and Epenthetic short vowels

 * In Dari and Tajik, particularly in colloquial speech, a short vowel in an unstressed syllable may be produced as a schwa or even as another short vowel. This occurs randomly and causes unstressed short vowels in these varieties to be unstable.
 * In Dari frequently is realized as  or sometimes as  after the accented/stressed syllable. Since the accented syllable is typically the last syllable of the lemma form, this only commonly occurs in inflicted forms.
 * Example:  ->.
 * In Dari and Tajik, syllabic consonants are forbidden in the coda, being replaced by -[əC].
 * Example: and  vs  and.
 * In colloquial Dari an epenthetic short vowel may be randomly inserted in consonant clusters. In rare instances, short vowel may also be deleted between two open syllables.
 * Dari does not forbid multi-consonant onsets like other dialects and as such epenthetic vowels do not always appear here. Though multi-consonant onsets are rare and only happen word initially.

Dialectal consonants
Also see

title: Appendix:Persian pronunciation


text:
Appendix:Czech pronunciation

See at Wikipedia for a thorough look at the sounds of Czech.

cs:Wikislovník:Výslovnost/čeština fr:Annexe:Prononciation/tchèque

title: Appendix:Czech pronunciation


text:
Appendix:Malay pronunciation

The charts below show the way in which the represents  ( and ) pronunciations in Wiktionary entries. For a guide to adding IPA characters to Wiktionary entries, see and.

See at Wikipedia for a more thorough look at the sounds of Malay.

title: Appendix:Malay pronunciation


text:
Appendix:Adyghe pronunciation

See at Wikipedia for a thorough look at the sounds of Adyghe.

title: Appendix:Adyghe pronunciation


text:
Appendix:Japanese pronunciation

See at Wikipedia for a thorough look at the sounds of Japanese. For a comprehensive table of IPA characters describing the pronunciation of the Japanese language, see Wikipedia's corresponding IPA page.

title: Appendix:Japanese pronunciation


text:
Appendix:Hokkien pronunciation

See at Wikipedia for a thorough look at the sounds of Hokkien.

title: Appendix:Hokkien pronunciation


text:
Appendix:Catalan pronunciation

See at Wikipedia for a thorough look at the sounds of Catalan.

title: Appendix:Catalan pronunciation


text:
Appendix:Sicilian pronunciation

See at Wikipedia for some information on the sounds of Sicilian.

title: Appendix:Sicilian pronunciation


text:
Appendix:Italian pronunciation

See at Wikipedia for a thorough look at the sounds of Italian. In addition, the has some useful English approximations.

title: Appendix:Italian pronunciation


text:
пастаяць

Etymology
From. Compare 🇨🇬, 🇨🇬.

Verb

 * 1)  to stand

title: пастаяць


text:
дараа

Noun

 * 1) obstruction, encumbrance

Postposition

 * 1) after, afterwards
 * 2) following

title: дараа


text:
Appendix:Bashkir pronunciation

See at Wikipedia for a look at the sounds of Bashkir.

title: Appendix:Bashkir pronunciation