ဝိက်ရှေန်နရဳ
mnwwiktionary
https://mnw.wiktionary.org/wiki/%E1%80%9D%E1%80%AD%E1%80%80%E1%80%BA%E1%80%9B%E1%80%BE%E1%80%B1%E1%80%94%E1%80%BA%E1%80%94%E1%80%9B%E1%80%B3:%E1%80%99%E1%80%AF%E1%80%80%E1%80%BA%E1%80%9C%E1%80%AD%E1%80%80%E1%80%BA%E1%80%90%E1%80%99%E1%80%BA
MediaWiki 1.47.0-wmf.5
case-sensitive
မဳဒဳယာ
တၟေင်
ဓရီုကျာ
ညးလွပ်
ညးလွပ် ဓရီုကျာ
ဝိက်ရှေန်နရဳ
ဝိက်ရှေန်နရဳ ဓရီုကျာ
ဝှာင်
ဝှာင် ဓရီုကျာ
မဳဒဳယာဝဳကဳ
မဳဒဳယာဝဳကဳ ဓရီုကျာ
ထာမ်ပလိက်
ထာမ်ပလိက် ဓရီုကျာ
ရီု
ရီု ဓရီုကျာ
ကဏ္ဍ
ကဏ္ဍ ဓရီုကျာ
အဆက်လက္ကရဴ
အဆက်လက္ကရဴ ဓရီုကျာ
ကာရန်
ကာရန် ဓရီုကျာ
အဘိဓာန်
အဘိဓာန် ဓရီုကျာ
ဗီုပြၚ်သိုၚ်တၟိ
ဗီုပြၚ်သိုၚ်တၟိ ဓရီုကျာ
TimedText
TimedText talk
မဝ်ဂျူ
မဝ်ဂျူ ဓရီုကျာ
Event
Event talk
မဝ်ဂျူ:languages
828
651
396543
396041
2026-06-07T17:06:39Z
咽頭べさ
33
396543
Scribunto
text/plain
--[==[ intro:
This module implements fetching of language-specific information and processing text in a given language.
===Types of languages===
There are two types of languages: full languages and etymology-only languages. The essential difference is that only
full languages appear in L2 headings in vocabulary entries, and hence categories like [[:Category:French nouns]] exist
only for full languages. Etymology-only languages have either a full language or another etymology-only language as
their parent (in the parent-child inheritance sense), and for etymology-only languages with another etymology-only
language as their parent, a full language can always be derived by following the parent links upwards. For example,
"Canadian French", code `fr-CA`, is an etymology-only language whose parent is the full language "French", code `fr`.
An example of an etymology-only language with another etymology-only parent is "Northumbrian Old English", code
`ang-nor`, which has "Anglian Old English", code `ang-ang` as its parent; this is an etymology-only language whose
parent is "Old English", code `ang`, which is a full language. (This is because Northumbrian Old English is considered
a variety of Anglian Old English.) Sometimes the parent is the "Undetermined" language, code `und`; this is the case,
for example, for "substrate" languages such as "Pre-Greek", code `qsb-grc`, and "the BMAC substrate", code `qsb-bma`.
It is important to distinguish language ''parents'' from language ''ancestors''. The parent-child relationship is one
of containment, i.e. if X is a child of Y, X is considered a variety of Y. On the other hand, the ancestor-descendant
relationship is one of descent in time. For example, "Classical Latin", code `la-cla`, and "Late Latin", code `la-lat`,
are both etymology-only languages with "Latin", code `la`, as their parents, because both of the former are varieties
of Latin. However, Late Latin does *NOT* have Classical Latin as its parent because Late Latin is *not* a variety of
Classical Latin; rather, it is a descendant. There is in fact a separate `ancestors` field that is used to express the
ancestor-descendant relationship, and Late Latin's ancestor is given as Classical Latin. It is also important to note
that sometimes an etymology-only language is actually the conceptual ancestor of its parent language. This happens,
for example, with "Old Italian" (code `roa-oit`), which is an etymology-only variant of full language "Italian" (code
`it`), and with "Old Latin" (code `itc-ola`), which is an etymology-only variant of Latin. In both cases, the full
language has the etymology-only variant listed as an ancestor. This allows a Latin term to inherit from Old Latin
using the {{tl|inh}} template (where in this template, "inheritance" refers to ancestral inheritance, i.e. inheritance
in time, rather than in the parent-child sense); likewise for Italian and Old Italian.
Full languages come in three subtypes:
* {regular}: This indicates a full language that is attested according to [[WT:CFI]] and therefore permitted in the
main namespace. There may also be reconstructed terms for the language, which are placed in the
{Reconstruction} namespace and must be prefixed with * to indicate a reconstruction. Most full languages
are natural (not constructed) languages, but a few constructed languages (e.g. Esperanto and Volapük,
among others) are also allowed in the mainspace and considered regular languages.
* {reconstructed}: This language is not attested according to [[WT:CFI]], and therefore is allowed only in the
{Reconstruction} namespace. All terms in this language are reconstructed, and must be prefixed with
*. Languages such as Proto-Indo-European and Proto-Germanic are in this category.
* {appendix-constructed}: This language is attested but does not meet the additional requirements set out for
constructed languages ([[WT:CFI#Constructed languages]]). Its entries must therefore be in
the Appendix namespace, but they are not reconstructed and therefore should not have *
prefixed in links. Most constructed languages are of this subtype.
Both full languages and etymology-only languages have a {Language} object associated with them, which is fetched using
the {getByCode} function in [[Module:languages]] to convert a language code to a {Language} object. Depending on the
options supplied to this function, etymology-only languages may or may not be accepted, and family codes may be
accepted (returning a {Family} object as described in [[Module:families]]). There are also separate {getByCanonicalName}
functions in [[Module:languages]] and [[Module:etymology languages]] to convert a language's canonical name to a
{Language} object (depending on whether the canonical name refers to a full or etymology-only language).
===Textual representations===
Textual strings belonging to a given language come in several different ''text variants'':
# The ''input text'' is what the user supplies in wikitext, in the parameters to {{tl|m}}, {{tl|l}}, {{tl|ux}},
{{tl|t}}, {{tl|lang}} and the like.
# The ''corrected input text'' is the input text with some corrections and/or normalizations applied, such as
bad-character replacements for certain languages, like replacing `l` or `1` to [[palochka]] in some languages written
in Cyrillic. (FIXME: This currently goes under the name ''display text'' but that will be repurposed below. Also,
[[User:Surjection]] suggests renaming this to ''normalized input text'', but "normalized" is used in a different sense
in [[Module:usex]].)
# The ''display text'' is the text in the form as it will be displayed to the user. This is what appears in headwords,
in usexes, in displayed internal links, etc. This can include accent marks that are removed to form the stripped
display text (see below), as well as embedded bracketed links that are variously processed further. The display text
is generated from the corrected input text by applying language-specific transformations; for most languages, there
will be no such transformations. The general reason for having a difference between input and display text is to allow
for extra information in the input text that is not displayed to the user but is sent to the transliteration module.
Note that having different display and input text is only supported currently through special-casing but will be
generalized. Examples of transformations are: (1) Removing the {{cd|^}} that is used in certain East Asian (and
possibly other unicameral) languages to indicate capitalization of the transliteration (which is currently
special-cased); (2) for Korean, removing or otherwise processing hyphens (which is currently special-cased); (3) for
Arabic, removing a ''sukūn'' diacritic placed over a ''tāʔ marbūṭa'' (like this: ةْ) to indicate that the
''tāʔ marbūṭa'' is pronounced and transliterated as /t/ instead of being silent [NOTE, NOT IMPLEMENTED YET]; (4) for
Thai and Khmer, converting space-separated words to bracketed words and resolving respelling substitutions such as
`[กรีน/กฺรีน]`, which indicate how to transliterate given words [NOTE, NOT IMPLEMENTED YET except in language-specific
templates like {{tl|th-usex}}].
## The ''right-resolved display text'' is the result of removing brackets around one-part embedded links and resolving
two-part embedded links into their right-hand components (i.e. converting two-part links into the displayed form).
The process of right-resolution is what happens when you call {{cd|remove_links()}} in [[Module:links]] on some text.
When applied to the display text, it produces exactly what the user sees, without any link markup.
# The ''stripped display text'' is the result of applying diacritic-stripping to the display text.
## The ''left-resolved stripped display text'' [NEED BETTER NAME] is the result of applying left-resolution to the
stripped display text, i.e. similar to right-resolution but resolving two-part embedded links into their left-hand
components (i.e. the linked-to page). If the display text refers to a single page, the resulting of applying
diacritic stripping and left-resolution produces the ''logical pagename''.
# The ''physical pagename text'' is the result of converting the stripped display text into physical page links. If the
stripped display text contains embedded links, the left side of those links is converted into physical page links;
otherwise, the entire text is considered a pagename and converted in the same fashion. The conversion does three
things: (1) converts characters not allowed in pagenames into their "unsupported title" representation, e.g.
{{cd|Unsupported titles/`gt`}} in place of the logical name {{cd|>}}; (2) handles certain special-cased
unsupported-title logical pagenames, such as {{cd|Unsupported titles/Space}} in place of {{cd|[space]}} and
{{cd|Unsupported titles/Ancient Greek dish}} in place of a very long Greek name for a gourmet dish as found in
Aristophanes; (3) converts "mammoth" pagenames such as [[a]] into their appropriate split component, e.g.
[[a/languages A to L]].
# The ''source translit text'' is the text as supplied to the language-specific {{cd|transliterate()}} method. The form
of the source translit text may need to be language-specific, e.g Thai and Khmer will need the corrected input text,
whereas other languages may need to work off the display text. [FIXME: It's still unclear to me how embedded bracketed
links are handled in the existing code.] In general, embedded links need to be right-resolved (see above), but when
this happens is unclear to me [FIXME]. Some languages have a chop-up-and-paste-together scheme that sends parts of the
text through the transliterate mechanism, and for others (those listed with "cont" in {{cd|substitution}} in
[[Module:languages/data]]) they receive the full input text, but preprocessed in certain ways. (The wisdom of this is
still unclear to me.)
# The ''transliterated text'' (or ''transliteration'') is the result of transliterating the source translit text. Unlike
for all the other text variants except the transcribed text, it is always in the Latin script.
# The ''transcribed text'' (or ''transcription'') is the result of transcribing the source translit text, where
"transcription" here means a close approximation to the phonetic form of the language in languages (e.g. Akkadian,
Sumerian, Ancient Egyptian, maybe Tibetan) that have a wide difference between the written letters and spoken form.
Unlike for all the other text variants other than the transliterated text, it is always in the Latin script.
Currently, the transcribed text is always supplied manually be the user; there is no such thing as a
{{cd|transcribe()}} method on language objects.
# The ''sort key'' is the text used in sort keys for determining the placing of pages in categories they belong to. The
sort key is generated from the pagename or a specified ''sort base'' by lowercasing, doing language-specific
transformations and then uppercasing the result. If the sort base is supplied and is generated from input text, it
needs to be converted to display text, have embedded links removed through right-resolution and have
diacritic-stripping applied.
# There are other text variants that occur in usexes (specifically, there are normalized variants of several of the
above text variants), but we can skip them for now.
The following methods exist on {Language} objects to convert between different text variants:
# {correctInputText} (currently called {makeDisplayText}): This converts input text to corrected input text.
# {stripDiacritics}: This converts to stripped display text. [FIXME: This needs some rethinking. In particular,
{stripDiacritics} is sometimes called on input text, corrected input text or display text (in various paths inside of
[[Module:links]], and, in the case of input text, usually from other modules). We need to make sure we don't try to
convert input text to display text twice, but at the same time we need to support calling it directly on input text
since so many modules do this. This means we need to add a parameter indicating whether the passed-in text is input,
corrected input, or display text; if the former two, we call {correctInputText} ourselves.]
# {logicalToPhysical}: This converts logical pagenames to physical pagenames.
# {transliterate}: This appears to convert input text with embedded brackets removed into a transliteration.
[FIXME: This needs some rethinking. In particular, it calls {processDisplayText} on its input, which won't work
for Thai and Khmer, so we may need language-specific flags indicating whether to pass the input text directly to the
language transliterate method. In addition, I'm not sure how embedded links are handled in the existing translit code;
a lot of callers remove the links themselves before calling {transliterate()}, which I assume is wrong.]
# {makeSortKey}: This converts display text (?) to a sort key. [FIXME: Clarify this.]
]==]
local export = {}
local debug_track_module = "Module:debug/track"
local etymology_languages_data_module = "Module:etymology languages/data"
local families_module = "Module:families"
local headword_page_module = "Module:headword/page"
local json_module = "Module:JSON"
local language_like_module = "Module:language-like"
local languages_data_module = "Module:languages/data"
local languages_data_patterns_module = "Module:languages/data/patterns"
local links_data_module = "Module:links/data"
local load_module = "Module:load"
local scripts_module = "Module:scripts"
local scripts_data_module = "Module:scripts/data"
local string_encode_entities_module = "Module:string/encode entities"
local string_pattern_escape_module = "Module:string/patternEscape"
local string_replacement_escape_module = "Module:string/replacementEscape"
local string_utilities_module = "Module:string utilities"
local table_module = "Module:table"
local utilities_module = "Module:utilities"
local wikimedia_languages_module = "Module:wikimedia languages"
local mw = mw
local string = string
local table = table
local char = string.char
local concat = table.concat
local find = string.find
local floor = math.floor
local get_by_code -- Defined below.
local get_data_module_name -- Defined below.
local get_extra_data_module_name -- Defined below.
local getmetatable = getmetatable
local gmatch = string.gmatch
local gsub = string.gsub
local insert = table.insert
local ipairs = ipairs
local is_known_language_tag = mw.language.isKnownLanguageTag
local make_object -- Defined below.
local match = string.match
local next = next
local pairs = pairs
local remove = table.remove
local require = require
local select = select
local setmetatable = setmetatable
local sub = string.sub
local type = type
local unstrip = mw.text.unstrip
-- Loaded as needed by findBestScript.
local Hans_chars
local Hant_chars
local function check_object(...)
check_object = require(utilities_module).check_object
return check_object(...)
end
local function debug_track(...)
debug_track = require(debug_track_module)
return debug_track(...)
end
local function decode_entities(...)
decode_entities = require(string_utilities_module).decode_entities
return decode_entities(...)
end
local function decode_uri(...)
decode_uri = require(string_utilities_module).decode_uri
return decode_uri(...)
end
local function deep_copy(...)
deep_copy = require(table_module).deepCopy
return deep_copy(...)
end
local function encode_entities(...)
encode_entities = require(string_encode_entities_module)
return encode_entities(...)
end
local function get_L2_sort_key(...)
get_L2_sort_key = require(headword_page_module).get_L2_sort_key
return get_L2_sort_key(...)
end
local function get_script(...)
get_script = require(scripts_module).getByCode
return get_script(...)
end
local function find_best_script_without_lang(...)
find_best_script_without_lang = require(scripts_module).findBestScriptWithoutLang
return find_best_script_without_lang(...)
end
local function get_family(...)
get_family = require(families_module).getByCode
return get_family(...)
end
local function get_plaintext(...)
get_plaintext = require(utilities_module).get_plaintext
return get_plaintext(...)
end
local function get_wikimedia_lang(...)
get_wikimedia_lang = require(wikimedia_languages_module).getByCode
return get_wikimedia_lang(...)
end
local function keys_to_list(...)
keys_to_list = require(table_module).keysToList
return keys_to_list(...)
end
local function list_to_set(...)
list_to_set = require(table_module).listToSet
return list_to_set(...)
end
local function load_data(...)
load_data = require(load_module).load_data
return load_data(...)
end
local function make_family_object(...)
make_family_object = require(families_module).makeObject
return make_family_object(...)
end
local function pattern_escape(...)
pattern_escape = require(string_pattern_escape_module)
return pattern_escape(...)
end
local function replacement_escape(...)
replacement_escape = require(string_replacement_escape_module)
return replacement_escape(...)
end
local function safe_require(...)
safe_require = require(load_module).safe_require
return safe_require(...)
end
local function shallow_copy(...)
shallow_copy = require(table_module).shallowCopy
return shallow_copy(...)
end
local function split(...)
split = require(string_utilities_module).split
return split(...)
end
local function to_json(...)
to_json = require(json_module).toJSON
return to_json(...)
end
local function u(...)
u = require(string_utilities_module).char
return u(...)
end
local function ugsub(...)
ugsub = require(string_utilities_module).gsub
return ugsub(...)
end
local function ulen(...)
ulen = require(string_utilities_module).len
return ulen(...)
end
local function ulower(...)
ulower = require(string_utilities_module).lower
return ulower(...)
end
local function umatch(...)
umatch = require(string_utilities_module).match
return umatch(...)
end
local function uupper(...)
uupper = require(string_utilities_module).upper
return uupper(...)
end
local function track(page)
debug_track("languages/" .. page)
return true
end
local function normalize_code(code)
return load_data(languages_data_module).aliases[code] or code
end
local function check_inputs(self, check, default, ...)
local n = select("#", ...)
if n == 0 then
return false
end
local ret = check(self, (...))
if ret ~= nil then
return ret
elseif n > 1 then
local inputs = {...}
for i = 2, n do
ret = check(self, inputs[i])
if ret ~= nil then
return ret
end
end
end
return default
end
local function make_link(self, target, display)
local prefix, main
if self:getFamilyCode() == "qfa-sub" then
prefix, main = display:match("^(the )(.*)")
if not prefix then
prefix, main = display:match("^(a )(.*)")
end
end
return (prefix or "") .. "[[" .. target .. "|" .. (main or display) .. "]]"
end
-- Convert risky characters to HTML entities, which minimizes interference once returned (e.g. for "sms:a", "<!-- -->" etc.).
local function escape_risky_characters(text)
-- Spacing characters in isolation generally need to be escaped in order to be properly processed by the MediaWiki software.
if umatch(text, "^%s*$") then
return encode_entities(text, text)
end
return encode_entities(text, "!#%&*+/:;<=>?@[\\]_{|}")
end
-- Temporarily convert various formatting characters to PUA to prevent them from being disrupted by the substitution process.
local function doTempSubstitutions(text, subbedChars, keepCarets, noTrim)
-- Clone so that we don't insert any extra patterns into the table in package.loaded. For some reason, using require seems to keep memory use down; probably because the table is always cloned.
local patterns = shallow_copy(require(languages_data_patterns_module))
if keepCarets then
insert(patterns, "((\\+)%^)")
insert(patterns, "((%^))")
end
-- Ensure any whitespace at the beginning and end is temp substituted, to prevent it from being accidentally trimmed. We only want to trim any final spaces added during the substitution process (e.g. by a module), which means we only do this during the first round of temp substitutions.
if not noTrim then
insert(patterns, "^([\128-\191\244]*(%s+))")
insert(patterns, "((%s+)[\128-\191\244]*)$")
end
-- Pre-substitution, of "[[" and "]]", which makes pattern matching more accurate.
text = gsub(text, "%f[%[]%[%[", "\1"):gsub("%f[%]]%]%]", "\2")
local i = #subbedChars
for _, pattern in ipairs(patterns) do
-- Patterns ending in \0 stand are for things like "[[" or "]]"), so the inserted PUA are treated as breaks between terms by modules that scrape info from pages.
local term_divider
pattern = gsub(pattern, "%z$", function(divider)
term_divider = divider == "\0"
return ""
end)
text = gsub(text, pattern, function(...)
local m = {...}
local m1New = m[1]
for k = 2, #m do
local n = i + k - 1
subbedChars[n] = m[k]
local byte2 = floor(n / 4096) % 64 + (term_divider and 128 or 136)
local byte3 = floor(n / 64) % 64 + 128
local byte4 = n % 64 + 128
m1New = gsub(m1New, pattern_escape(m[k]), "\244" .. char(byte2) .. char(byte3) .. char(byte4), 1)
end
i = i + #m - 1
return m1New
end)
end
text = gsub(text, "\1", "%[%["):gsub("\2", "%]%]")
return text, subbedChars
end
-- Reinsert any formatting that was temporarily substituted.
local function undoTempSubstitutions(text, subbedChars)
for i = 1, #subbedChars do
local byte2 = floor(i / 4096) % 64 + 128
local byte3 = floor(i / 64) % 64 + 128
local byte4 = i % 64 + 128
text = gsub(text, "\244[" .. char(byte2) .. char(byte2+8) .. "]" .. char(byte3) .. char(byte4),
replacement_escape(subbedChars[i]))
end
text = gsub(text, "\1", "%[%["):gsub("\2", "%]%]")
return text
end
-- Check if the raw text is an unsupported title, and if so return that. Otherwise, remove HTML entities. We do the pre-conversion to avoid loading the unsupported title list unnecessarily.
local function checkNoEntities(self, text)
local textNoEnc = decode_entities(text)
if textNoEnc ~= text and load_data(links_data_module).unsupported_titles[text] then
return text
else
return textNoEnc
end
end
-- If no script object is provided (or if it's invalid or None), get one.
local function checkScript(text, self, sc)
if not check_object("script", true, sc) or sc:getCode() == "None" then
return self:findBestScript(text)
end
return sc
end
local function normalize(text, sc)
text = sc:fixDiscouragedSequences(text)
return sc:toFixedNFD(text)
end
-- Subfunction of iterateSectionSubstitutions(). Process an individual chunk of text according to the specifications in
-- `substitution_data`. The input parameters are all as in the documentation of iterateSectionSubstitutions() except for
-- `recursed`, which is set to true if we called ourselves recursively to process a script-specific setting or
-- script-wide fallback. Returns two values: the processed text and the actual substitution data used to do the
-- substitutions (same as the `actual_substitution_data` return value to iterateSectionSubstitutions()).
local function doSubstitutions(self, text, sc, substitution_data, data_field, function_name, recursed)
-- BE CAREFUL in this function because the value at any level can be `false`, which causes no processing to be done
-- and blocks any further fallback processing.
local actual_substitution_data = substitution_data
-- If there are language-specific substitutes given in the data module, use those.
if type(substitution_data) == "table" then
-- If a script is specified, run this function with the script-specific data before continuing.
local sc_code = sc:getCode()
local has_substitution_data = false
if substitution_data[sc_code] ~= nil then
has_substitution_data = true
if substitution_data[sc_code] then
text, actual_substitution_data = doSubstitutions(self, text, sc, substitution_data[sc_code], data_field,
function_name, true)
end
-- Hant, Hans and Hani are usually treated the same, so add a special case to avoid having to specify each one
-- separately.
elseif sc_code:match("^Han") and substitution_data.Hani ~= nil then
has_substitution_data = true
if substitution_data.Hani then
text, actual_substitution_data = doSubstitutions(self, text, sc, substitution_data.Hani, data_field,
function_name, true)
end
-- Substitution data with key 1 in the outer table may be given as a fallback.
elseif substitution_data[1] ~= nil then
has_substitution_data = true
if substitution_data[1] then
text, actual_substitution_data = doSubstitutions(self, text, sc, substitution_data[1], data_field,
function_name, true)
end
end
-- Iterate over all strings in the "from" subtable, and gsub with the corresponding string in "to". We work with
-- the NFD decomposed forms, as this simplifies many substitutions.
if substitution_data.from then
has_substitution_data = true
for i, from in ipairs(substitution_data.from) do
-- Normalize each loop, to ensure multi-stage substitutions work correctly.
text = sc:toFixedNFD(text)
text = ugsub(text, sc:toFixedNFD(from), substitution_data.to[i] or "")
end
end
if substitution_data.remove_diacritics then
has_substitution_data = true
text = sc:toFixedNFD(text)
-- Convert exceptions to PUA.
local remove_exceptions, substitutes = substitution_data.remove_exceptions
if remove_exceptions then
substitutes = {}
local i = 0
for _, exception in ipairs(remove_exceptions) do
exception = sc:toFixedNFD(exception)
text = ugsub(text, exception, function(m)
i = i + 1
local subst = u(0x80000 + i)
substitutes[subst] = m
return subst
end)
end
end
-- Strip diacritics.
text = ugsub(text, "[" .. substitution_data.remove_diacritics .. "]", "")
-- Convert exceptions back.
if remove_exceptions then
text = text:gsub("\242[\128-\191]*", substitutes)
end
end
if not has_substitution_data and sc._data[data_field] then
-- If language-specific sort key (etc.) is nil, fall back to script-wide sort key (etc.).
text, actual_substitution_data = doSubstitutions(self, text, sc, sc._data[data_field], data_field,
function_name, true)
end
elseif type(substitution_data) == "string" then
-- If there is a dedicated function module, use that.
local module = safe_require("Module:" .. substitution_data)
if module then
-- TODO: translit functions should take objects, not codes.
-- TODO: translit functions should be called with form NFD.
if function_name == "tr" then
if not module[function_name] then
error(("Internal error: Module [[%s]] has no function named 'tr'"):format(substitution_data))
end
text = module[function_name](text, self._code, sc:getCode())
elseif function_name == "stripDiacritics" then
-- FIXME, get rid of this arm after renaming makeEntryName -> stripDiacritics.
if module[function_name] then
text = module[function_name](sc:toFixedNFD(text), self, sc)
elseif module.makeEntryName then
text = module.makeEntryName(sc:toFixedNFD(text), self, sc)
else
error(("Internal error: Module [[%s]] has no function named 'stripDiacritics' or 'makeEntryName'"
):format(substitution_data))
end
else
if not module[function_name] then
error(("Internal error: Module [[%s]] has no function named '%s'"):format(
substitution_data, function_name))
end
text = module[function_name](sc:toFixedNFD(text), self, sc)
end
else
error("Substitution data '" .. substitution_data .. "' does not match an existing module.")
end
elseif substitution_data == nil and sc._data[data_field] then
-- If language-specific sort key (etc.) is nil, fall back to script-wide sort key (etc.).
text, actual_substitution_data = doSubstitutions(self, text, sc, sc._data[data_field], data_field,
function_name, true)
end
-- Don't normalize to NFC if this is the inner loop or if a module returned nil.
if recursed or not text then
return text, actual_substitution_data
end
-- Fix any discouraged sequences created during the substitution process, and normalize into the final form.
return sc:toFixedNFC(sc:fixDiscouragedSequences(text)), actual_substitution_data
end
-- Split the text into sections, based on the presence of temporarily substituted formatting characters, then iterate
-- over each section to apply substitutions (e.g. transliteration or diacritic stripping). This avoids putting PUA
-- characters through language-specific modules, which may be unequipped for them. This function is passed the following
-- values:
-- * `self` (the Language object);
-- * `text` (the text to process);
-- * `sc` (the script of the text, which must be specified; callers should call checkScript() as needed to autodetect the
-- script of the text if not given explicitly by the user);
-- * `subbedChars` (an array of the same length as the text, indicating which characters have been substituted and by
-- what, or {nil} if no substitutions are to happen);
-- * `keepCarets` (DOCUMENT ME);
-- * `substitution_data` (the data indicating which substitutions to apply, taken directly from `data_field` in the
-- language's data structure in a submodule of [[Module:languages/data]]);
-- * `data_field` (the data field from which `substitution_data` was fetched, such as "sort_key" or "strip_diacritics");
-- * `function_name` (the name of the function to call to do the substitution, in case `substitution_data` specifies a
-- module to do the substitution);
-- * `notrim` (don't trim whitespace at the edges of `text`; set when computing the sort key, because whitespace at the
-- beginning of a sort key is significant and causes the resulting page to be sorted at the beginning of the category
-- it's in).
-- Returns three values:
-- (1) the processed text;
-- (2) the value of `subbedChars` that was passed in, possibly modified with additional character substitutions; will be
-- {nil} if {nil} was passed in;
-- (3) the actual substitution data that was used to apply substitutions to `text`; this may be different from the value
-- of `substitution_data` passed in if that value recursively specified script-specific substitutions or if no
-- substitution data could be found in the language-specific data (e.g. {nil} was passed in or a structure was passed
-- in that had no setting for the script given in `sc`), but a script-wide fallback value was set; currently it is
-- only used by makeSortKey().
local function iterateSectionSubstitutions(self, text, sc, subbedChars, keepCarets, substitution_data, data_field,
function_name, notrim)
local sections
-- See [[Module:languages/data]].
if not find(text, "\244") or load_data(languages_data_module).substitution[self._code] == "cont" then
sections = {text}
else
sections = split(text, "\244[\128-\143][\128-\191]*", true)
end
local actual_substitution_data
for _, section in ipairs(sections) do
-- Don't bother processing empty strings or whitespace (which may also not be handled well by dedicated
-- modules).
if gsub(section, "%s+", "") ~= "" then
local sub, this_actual_substitution_data = doSubstitutions(self, section, sc, substitution_data, data_field,
function_name)
actual_substitution_data = this_actual_substitution_data
-- Second round of temporary substitutions, in case any formatting was added by the main substitution
-- process. However, don't do this if the section contains formatting already (as it would have had to have
-- been escaped to reach this stage, and therefore should be given as raw text).
if sub and subbedChars then
local noSub
for _, pattern in ipairs(require(languages_data_patterns_module)) do
if match(section, pattern .. "%z?") then
noSub = true
end
end
if not noSub then
sub, subbedChars = doTempSubstitutions(sub, subbedChars, keepCarets, true)
end
end
if not sub then
text = sub
break
end
text = sub and gsub(text, pattern_escape(section), replacement_escape(sub), 1) or text
end
end
if not notrim then
-- Trim, unless there are only spacing characters, while ignoring any final formatting characters.
-- Do not trim sort keys because spaces at the beginning are significant.
text = text and text:gsub("^([\128-\191\244]*)%s+(%S)", "%1%2"):gsub("(%S)%s+([\128-\191\244]*)$", "%1%2") or
nil
end
return text, subbedChars, actual_substitution_data
end
-- Process carets (and any escapes). Default to simple removal, if no pattern/replacement is given.
local function processCarets(text, pattern, repl)
local rep
repeat
text, rep = gsub(text, "\\\\(\\*^)", "\3%1")
until rep == 0
return (text:gsub("\\^", "\4")
:gsub(pattern or "%^", repl or "")
:gsub("\3", "\\")
:gsub("\4", "^"))
end
-- Remove carets if they are used to capitalize parts of transliterations (unless they have been escaped).
local function removeCarets(text, sc)
if not sc:hasCapitalization() and sc:isTransliterated() and text:find("^", 1, true) then
return processCarets(text)
else
return text
end
end
local Language = {}
--[==[Returns the language code of the language. Example: {{code|lua|"fr"}} for French.]==]
function Language:getCode()
return self._code
end
--[==[Returns the canonical name of the language. This is the name used to represent that language on Wiktionary, and is guaranteed to be unique to that language alone. Example: {{code|lua|"French"}} for French.]==]
function Language:getCanonicalName()
local name = self._name
if name == nil then
name = self._data[1]
self._name = name
end
return name
end
--[==[
Return the display form of the language. The display form of a language, family or script is the form it takes when
appearing as the <code><var>source</var></code> in categories such as <code>English terms derived from
<var>source</var></code> or <code>English given names from <var>source</var></code>, and is also the displayed text
in {makeCategoryLink()} links. For full and etymology-only languages, this is the same as the canonical name, but
for families, it reads <code>"<var>name</var> languages"</code> (e.g. {"Indo-Iranian languages"}), and for scripts,
it reads <code>"<var>name</var> script"</code> (e.g. {"Arabic script"}).
]==]
function Language:getDisplayForm()
local form = self._displayForm
if form == nil then
form = self:getCanonicalName()
-- Add article and " substrate" to substrates that lack them.
if self:getFamilyCode() == "qfa-sub" then
if not (sub(form, 1, 4) == "the " or sub(form, 1, 2) == "a ") then
form = "a " .. form
end
if not match(form, " [Ss]ubstrate") then
form = form .. " substrate"
end
end
self._displayForm = form
end
return form
end
--[==[Returns the value which should be used in the HTML lang= attribute for tagged text in the language.]==]
function Language:getHTMLAttribute(sc, region)
local code = self._code
if not find(code, "-", 1, true) then
return code .. "-" .. sc:getCode() .. (region and "-" .. region or "")
end
local parent = self:getParent()
region = region or match(code, "%f[%u][%u-]+%f[%U]")
if parent then
return parent:getHTMLAttribute(sc, region)
end
-- TODO: ISO family codes can also be used.
return "mis-" .. sc:getCode() .. (region and "-" .. region or "")
end
--[==[Returns a table of the aliases that the language is known by, excluding the canonical name. Aliases are synonyms for the language in question. The names are not guaranteed to be unique, in that sometimes more than one language is known by the same name. Example: {{code|lua|{"High German", "New High German", "Deutsch"} }} for [[:Category:German language|German]].]==]
function Language:getAliases()
self:loadInExtraData()
return require(language_like_module).getAliases(self)
end
--[==[
Return a table of the known subvarieties of a given language, excluding subvarieties that have been given
explicit etymology-only language codes. The names are not guaranteed to be unique, in that sometimes a given name
refers to a subvariety of more than one language. Example: {{code|lua|{"Southern Aymara", "Central Aymara"} }} for
[[:Category:Aymara language|Aymara]]. Note that the returned value can have nested tables in it, when a subvariety
goes by more than one name. Example: {{code|lua|{"North Azerbaijani", "South Azerbaijani", {"Afshar", "Afshari",
"Afshar Azerbaijani", "Afchar"}, {"Qashqa'i", "Qashqai", "Kashkay"}, "Sonqor"} }} for
[[:Category:Azerbaijani language|Azerbaijani]]. Here, for example, Afshar, Afshari, Afshar Azerbaijani and Afchar
all refer to the same subvariety, whose preferred name is Afshar (the one listed first). To avoid a return value
with nested tables in it, specify a non-{{code|lua|nil}} value for the <code>flatten</code> parameter; in that case,
the return value would be {{code|lua|{"North Azerbaijani", "South Azerbaijani", "Afshar", "Afshari",
"Afshar Azerbaijani", "Afchar", "Qashqa'i", "Qashqai", "Kashkay", "Sonqor"} }}.
]==]
function Language:getVarieties(flatten)
self:loadInExtraData()
return require(language_like_module).getVarieties(self, flatten)
end
--[==[Returns a table of the "other names" that the language is known by, which are listed in the <code>otherNames</code> field. It should be noted that the <code>otherNames</code> field itself is deprecated, and entries listed there should eventually be moved to either <code>aliases</code> or <code>varieties</code>.]==]
function Language:getOtherNames() -- To be eventually removed, once there are no more uses of the `otherNames` field.
self:loadInExtraData()
return require(language_like_module).getOtherNames(self)
end
--[==[
Return a combined table of the canonical name, aliases, varieties and other names of a given language.]==]
function Language:getAllNames()
self:loadInExtraData()
return require(language_like_module).getAllNames(self)
end
--[==[Returns a table of types as a lookup table (with the types as keys).
The possible types are
* {language}: This is a language, either full or etymology-only.
* {full}: This is a "full" (not etymology-only) language, i.e. the union of {regular}, {reconstructed} and
{appendix-constructed}. Note that the types {full} and {etymology-only} also exist for families, so if you
want to check specifically for a full language and you have an object that might be a family, you should
use {{lua|hasType("language", "full")}} and not simply {{lua|hasType("full")}}.
* {etymology-only}: This is an etymology-only (not full) language, whose parent is another etymology-only
language or a full language. Note that the types {full} and {etymology-only} also exist for
families, so if you want to check specifically for an etymology-only language and you have an
object that might be a family, you should use {{lua|hasType("language", "etymology-only")}}
and not simply {{lua|hasType("etymology-only")}}.
* {regular}: This indicates a full language that is attested according to [[WT:CFI]] and therefore permitted
in the main namespace. There may also be reconstructed terms for the language, which are placed in
the {Reconstruction} namespace and must be prefixed with * to indicate a reconstruction. Most full
languages are natural (not constructed) languages, but a few constructed languages (e.g. Esperanto
and Volapük, among others) are also allowed in the mainspace and considered regular languages.
* {reconstructed}: This language is not attested according to [[WT:CFI]], and therefore is allowed only in the
{Reconstruction} namespace. All terms in this language are reconstructed, and must be prefixed
with *. Languages such as Proto-Indo-European and Proto-Germanic are in this category.
* {appendix-constructed}: This language is attested but does not meet the additional requirements set out for
constructed languages ([[WT:CFI#Constructed languages]]). Its entries must therefore
be in the Appendix namespace, but they are not reconstructed and therefore should
not have * prefixed in links.
]==]
function Language:getTypes()
local types = self._types
if types == nil then
types = {language = true}
if self:getFullCode() == self._code then
types.full = true
else
types["etymology-only"] = true
end
for t in gmatch(self._data.type, "[^,]+") do
types[t] = true
end
self._types = types
end
return types
end
--[==[Given a list of types as strings, returns true if the language has all of them.]==]
function Language:hasType(...)
Language.hasType = require(language_like_module).hasType
return self:hasType(...)
end
--[==[Returns a table containing <code>WikimediaLanguage</code> objects (see [[Module:wikimedia languages]]), which represent languages and their codes as they are used in Wikimedia projects for interwiki linking and such. More than one object may be returned, as a single Wiktionary language may correspond to multiple Wikimedia languages. For example, Wiktionary's single code <code>sh</code> (Serbo-Croatian) maps to four Wikimedia codes: <code>sh</code> (Serbo-Croatian), <code>bs</code> (Bosnian), <code>hr</code> (Croatian) and <code>sr</code> (Serbian).
The code for the Wikimedia language is retrieved from the <code>wikimedia_codes</code> property in the data modules. If that property is not present, the code of the current language is used. If none of the available codes is actually a valid Wikimedia code, an empty table is returned.]==]
function Language:getWikimediaLanguages()
local wm_langs = self._wikimediaLanguageObjects
if wm_langs == nil then
local codes = self:getWikimediaLanguageCodes()
wm_langs = {}
for i = 1, #codes do
wm_langs[i] = get_wikimedia_lang(codes[i])
end
self._wikimediaLanguageObjects = wm_langs
end
return wm_langs
end
function Language:getWikimediaLanguageCodes()
local wm_langs = self._wikimediaLanguageCodes
if wm_langs == nil then
wm_langs = self._data.wikimedia_codes
if wm_langs then
wm_langs = split(wm_langs, ",", true, true)
else
local code = self._code
if is_known_language_tag(code) then
wm_langs = {code}
else
-- Inherit, but only if no codes are specified in the data *and*
-- the language code isn't a valid Wikimedia language code.
local parent = self:getParent()
wm_langs = parent and parent:getWikimediaLanguageCodes() or {}
end
end
self._wikimediaLanguageCodes = wm_langs
end
return wm_langs
end
--[==[
Returns the name of the Wikipedia article for the language. `project` specifies the language and project to retrieve
the article from, defaulting to {"enwiki"} for the English Wikipedia. Normally if specified it should be the project
code for a specific-language Wikipedia e.g. "zhwiki" for the Chinese Wikipedia, but it can be any project, including
non-Wikipedia ones. If the project is the English Wikipedia and the property {wikipedia_article} is present in the data
module it will be used first. In all other cases, a sitelink will be generated from {:getWikidataItem} (if set). The
resulting value (or lack of value) is cached so that subsequent calls are fast. If no value could be determined, and
`noCategoryFallback` is {false}, {:getCategoryName} is used as fallback; otherwise, {nil} is returned. Note that if
`noCategoryFallback` is {nil} or omitted, it defaults to {false} if the project is the English Wikipedia, otherwise
to {true}. In other words, under normal circumstances, if the English Wikipedia article couldn't be retrieved, the
return value will fall back to a link to the language's category, but this won't normally happen for any other project.
]==]
function Language:getWikipediaArticle(noCategoryFallback, project)
Language.getWikipediaArticle = require(language_like_module).getWikipediaArticle
return self:getWikipediaArticle(noCategoryFallback, project)
end
function Language:makeWikipediaLink()
return make_link(self, "w:" .. self:getWikipediaArticle(), self:getCanonicalName())
end
--[==[Returns the name of the Wikimedia Commons category page for the language.]==]
function Language:getCommonsCategory()
Language.getCommonsCategory = require(language_like_module).getCommonsCategory
return self:getCommonsCategory()
end
--[==[Returns the Wikidata item id for the language or <code>nil</code>. This corresponds to the the second field in the data modules.]==]
function Language:getWikidataItem()
Language.getWikidataItem = require(language_like_module).getWikidataItem
return self:getWikidataItem()
end
--[==[Returns a table of <code>Script</code> objects for all scripts that the language is written in. See [[Module:scripts]].]==]
function Language:getScripts()
local scripts = self._scriptObjects
if scripts == nil then
local codes = self:getScriptCodes()
if codes[1] == "All" then
scripts = load_data(scripts_data_module)
else
scripts = {}
for i = 1, #codes do
scripts[i] = get_script(codes[i])
end
end
self._scriptObjects = scripts
end
return scripts
end
--[==[Returns the table of script codes in the language's data file.]==]
function Language:getScriptCodes()
local scripts = self._scriptCodes
if scripts == nil then
scripts = self._data[4]
if scripts then
local codes, n = {}, 0
for code in gmatch(scripts, "[^,]+") do
n = n + 1
-- Special handling of "Hants", which represents "Hani", "Hant" and "Hans" collectively.
if code == "Hants" then
codes[n] = "Hani"
codes[n + 1] = "Hant"
codes[n + 2] = "Hans"
n = n + 2
else
codes[n] = code
end
end
scripts = codes
else
scripts = {"None"}
end
self._scriptCodes = scripts
end
return scripts
end
--[==[Given some text, this function iterates through the scripts of a given language and tries to find the script that best matches the text. It returns a {{code|lua|Script}} object representing the script. If no match is found at all, it returns the {{code|lua|None}} script object.]==]
function Language:findBestScript(text, forceDetect)
if not text or text == "" or text == "-" then
return get_script("None")
end
-- Differs from table returned by getScriptCodes, as Hants is not normalized into its constituents.
local codes = self._bestScriptCodes
if codes == nil then
codes = self._data[4]
codes = codes and split(codes, ",", true, true) or {"None"}
self._bestScriptCodes = codes
end
local first_sc = codes[1]
if first_sc == "All" then
return find_best_script_without_lang(text)
end
local codes_len = #codes
if not (forceDetect or first_sc == "Hants" or codes_len > 1) then
first_sc = get_script(first_sc)
local charset = first_sc.characters
return charset and umatch(text, "[" .. charset .. "]") and first_sc or get_script("None")
end
-- Remove all formatting characters.
text = get_plaintext(text)
-- Remove all spaces and any ASCII punctuation. Some non-ASCII punctuation is script-specific, so can't be removed.
text = ugsub(text, "[%s!\"#%%&'()*,%-./:;?@[\\%]_{}]+", "")
if #text == 0 then
return get_script("None")
end
-- Try to match every script against the text,
-- and return the one with the most matching characters.
local bestcount, bestscript, length = 0
for i = 1, codes_len do
local sc = codes[i]
-- Special case for "Hants", which is a special code that represents whichever of "Hant" or "Hans" best matches, or "Hani" if they match equally. This avoids having to list all three. In addition, "Hants" will be treated as the best match if there is at least one matching character, under the assumption that a Han script is desirable in terms that contain a mix of Han and other scripts (not counting those which use Jpan or Kore).
if sc == "Hants" then
local Hani = get_script("Hani")
if not Hant_chars then
Hant_chars = load_data("Module:zh/data/ts")
Hans_chars = load_data("Module:zh/data/st")
end
local t, s, found = 0, 0
-- This is faster than using mw.ustring.gmatch directly.
for ch in gmatch((ugsub(text, "[" .. Hani.characters .. "]", "\255%0")), "\255(.[\128-\191]*)") do
found = true
if Hant_chars[ch] then
t = t + 1
if Hans_chars[ch] then
s = s + 1
end
elseif Hans_chars[ch] then
s = s + 1
else
t, s = t + 1, s + 1
end
end
if found then
if t == s then
return Hani
end
return get_script(t > s and "Hant" or "Hans")
end
else
sc = get_script(sc)
if not length then
length = ulen(text)
end
-- Count characters by removing everything in the script's charset and comparing to the original length.
local charset = sc.characters
local count = charset and length - ulen((ugsub(text, "[" .. charset .. "]+", ""))) or 0
if count >= length then
return sc
elseif count > bestcount then
bestcount = count
bestscript = sc
end
end
end
-- Return best matching script, or otherwise None.
return bestscript or get_script("None")
end
--[==[Returns a <code>Family</code> object for the language family that the language belongs to. See [[Module:families]].]==]
function Language:getFamily()
local family = self._familyObject
if family == nil then
family = self:getFamilyCode()
-- If the value is nil, it's cached as false.
family = family and get_family(family) or false
self._familyObject = family
end
return family or nil
end
--[==[Returns the family code in the language's data file.]==]
function Language:getFamilyCode()
local family = self._familyCode
if family == nil then
-- If the value is nil, it's cached as false.
family = self._data[3] or false
self._familyCode = family
end
return family or nil
end
function Language:getFamilyName()
local family = self._familyName
if family == nil then
family = self:getFamily()
-- If the value is nil, it's cached as false.
family = family and family:getCanonicalName() or false
self._familyName = family
end
return family or nil
end
do
local function check_family(self, family)
if type(family) == "table" then
family = family:getCode()
end
if self:getFamilyCode() == family then
return true
end
local self_family = self:getFamily()
if self_family:inFamily(family) then
return true
-- If the family isn't a real family (e.g. creoles) check any ancestors.
elseif self_family:inFamily("qfa-not") then
local ancestors = self:getAncestors()
for _, ancestor in ipairs(ancestors) do
if ancestor:inFamily(family) then
return true
end
end
end
end
--[==[Check whether the language belongs to `family` (which can be a family code or object). A list of objects can be given in place of `family`; in that case, return true if the language belongs to any of the specified families. Note that some languages (in particular, certain creoles) can have multiple immediate ancestors potentially belonging to different families; in that case, return true if the language belongs to any of the specified families.]==]
function Language:inFamily(...)
if self:getFamilyCode() == nil then
return false
end
return check_inputs(self, check_family, false, ...)
end
end
function Language:getParent()
local parent = self._parentObject
if parent == nil then
parent = self:getParentCode()
-- If the value is nil, it's cached as false.
parent = parent and get_by_code(parent, nil, true, true) or false
self._parentObject = parent
end
return parent or nil
end
function Language:getParentCode()
local parent = self._parentCode
if parent == nil then
-- If the value is nil, it's cached as false.
parent = self._data.parent or false
self._parentCode = parent
end
return parent or nil
end
function Language:getParentName()
local parent = self._parentName
if parent == nil then
parent = self:getParent()
-- If the value is nil, it's cached as false.
parent = parent and parent:getCanonicalName() or false
self._parentName = parent
end
return parent or nil
end
function Language:getParentChain()
local chain = self._parentChain
if chain == nil then
chain = {}
local parent, n = self:getParent(), 0
while parent do
n = n + 1
chain[n] = parent
parent = parent:getParent()
end
self._parentChain = chain
end
return chain
end
do
local function check_lang(self, lang)
for _, parent in ipairs(self:getParentChain()) do
if (type(lang) == "string" and lang or lang:getCode()) == parent:getCode() then
return true
end
end
end
function Language:hasParent(...)
return check_inputs(self, check_lang, false, ...)
end
end
--[==[
If the language is etymology-only, this iterates through parents until a full language or family is found, and the
corresponding object is returned. If the language is a full language, then it simply returns itself.
]==]
function Language:getFull()
local full = self._fullObject
if full == nil then
full = self:getFullCode()
full = full == self._code and self or get_by_code(full)
self._fullObject = full
end
return full
end
--[==[
If the language is an etymology-only language, this iterates through parents until a full language or family is
found, and the corresponding code is returned. If the language is a full language, then it simply returns the
language code.
]==]
function Language:getFullCode()
return self._fullCode or self._code
end
--[==[
If the language is an etymology-only language, this iterates through parents until a full language or family is
found, and the corresponding canonical name is returned. If the language is a full language, then it simply returns
the canonical name of the language.
]==]
function Language:getFullName()
local full = self._fullName
if full == nil then
full = self:getFull():getCanonicalName()
self._fullName = full
end
return full
end
--[==[Returns a table of <code class="nf">Language</code> objects for all languages that this language is directly descended from. Generally this is only a single language, but creoles, pidgins and mixed languages can have multiple ancestors.]==]
function Language:getAncestors()
local ancestors = self._ancestorObjects
if ancestors == nil then
ancestors = {}
local ancestor_codes = self:getAncestorCodes()
if #ancestor_codes > 0 then
for _, ancestor in ipairs(ancestor_codes) do
insert(ancestors, get_by_code(ancestor, nil, true))
end
else
local fam = self:getFamily()
local protoLang = fam and fam:getProtoLanguage() or nil
-- For the cases where the current language is the proto-language
-- of its family, or an etymology-only language that is ancestral to that
-- proto-language, we need to step up a level higher right from the
-- start.
if protoLang and (
protoLang:getCode() == self._code or
(self:hasType("etymology-only") and protoLang:hasAncestor(self))
) then
fam = fam:getFamily()
protoLang = fam and fam:getProtoLanguage() or nil
end
while not protoLang and not (not fam or fam:getCode() == "qfa-not") do
fam = fam:getFamily()
protoLang = fam and fam:getProtoLanguage() or nil
end
insert(ancestors, protoLang)
end
self._ancestorObjects = ancestors
end
return ancestors
end
do
-- Avoid a language being its own ancestor via class inheritance. We only need to check for this if the language has inherited an ancestor table from its parent, because we never want to drop ancestors that have been explicitly set in the data.
-- Recursively iterate over ancestors until we either find self or run out. If self is found, return true.
local function check_ancestor(self, lang)
local codes = lang:getAncestorCodes()
if not codes then
return nil
end
for i = 1, #codes do
local code = codes[i]
if code == self._code then
return true
end
local anc = get_by_code(code, nil, true)
if check_ancestor(self, anc) then
return true
end
end
end
--[==[Returns a table of <code class="nf">Language</code> codes for all languages that this language is directly descended from. Generally this is only a single language, but creoles, pidgins and mixed languages can have multiple ancestors.]==]
function Language:getAncestorCodes()
if self._ancestorCodes then
return self._ancestorCodes
end
local data = self._data
local codes = data.ancestors
if codes == nil then
codes = {}
self._ancestorCodes = codes
return codes
end
codes = split(codes, ",", true, true)
self._ancestorCodes = codes
-- If there are no codes or the ancestors weren't inherited data, there's nothing left to check.
if #codes == 0 or self:getData(false, "raw").ancestors ~= nil then
return codes
end
local i, code = 1
while i <= #codes do
code = codes[i]
if check_ancestor(self, self) then
remove(codes, i)
else
i = i + 1
end
end
return codes
end
end
--[==[Given a list of language objects or codes, returns true if at least one of them is an ancestor. This includes any etymology-only children of that ancestor. If the language's ancestor(s) are etymology-only languages, it will also return true for those language parent(s) (e.g. if Vulgar Latin is the ancestor, it will also return true for its parent, Latin). However, a parent is excluded from this if the ancestor is also ancestral to that parent (e.g. if Classical Persian is the ancestor, Persian would return false, because Classical Persian is also ancestral to Persian).]==]
function Language:hasAncestor(...)
local function iterateOverAncestorTree(node, func, parent_check)
local ancestors = node:getAncestors()
local ancestorsParents = {}
for _, ancestor in ipairs(ancestors) do
-- When checking the parents of the other language, and the ancestor is also a parent, skip to the next ancestor, so that we exclude any etymology-only children of that parent that are not directly related (see below).
local ret = (parent_check or not node:hasParent(ancestor)) and
func(ancestor) or iterateOverAncestorTree(ancestor, func, parent_check)
if ret then
return ret
end
end
-- Check the parents of any ancestors. We don't do this if checking the parents of the other language, so that we exclude any etymology-only children of those parents that are not directly related (e.g. if the ancestor is Vulgar Latin and we are checking New Latin, we want it to return false because they are on different ancestral branches. As such, if we're already checking the parent of New Latin (Latin) we don't want to compare it to the parent of the ancestor (Latin), as this would be a false positive; it should be one or the other).
if not parent_check then
return nil
end
for _, ancestor in ipairs(ancestors) do
local ancestorParents = ancestor:getParentChain()
for _, ancestorParent in ipairs(ancestorParents) do
if ancestorParent:getCode() == self._code or ancestorParent:hasAncestor(ancestor) then
break
else
insert(ancestorsParents, ancestorParent)
end
end
end
for _, ancestorParent in ipairs(ancestorsParents) do
local ret = func(ancestorParent)
if ret then
return ret
end
end
end
local function do_iteration(otherlang, parent_check)
-- otherlang can't be self
if (type(otherlang) == "string" and otherlang or otherlang:getCode()) == self._code then
return false
end
repeat
if iterateOverAncestorTree(
self,
function(ancestor)
return ancestor:getCode() == (type(otherlang) == "string" and otherlang or otherlang:getCode())
end,
parent_check
) then
return true
elseif type(otherlang) == "string" then
otherlang = get_by_code(otherlang, nil, true)
end
otherlang = otherlang:getParent()
parent_check = false
until not otherlang
end
local parent_check = true
for _, otherlang in ipairs{...} do
local ret = do_iteration(otherlang, parent_check)
if ret then
return true
end
end
return false
end
do
local function construct_node(lang, memo)
local branch, ancestors = {lang = lang:getCode()}
memo[lang:getCode()] = branch
for _, ancestor in ipairs(lang:getAncestors()) do
if ancestors == nil then
ancestors = {}
end
insert(ancestors, memo[ancestor:getCode()] or construct_node(ancestor, memo))
end
branch.ancestors = ancestors
return branch
end
function Language:getAncestorChain()
local chain = self._ancestorChain
if chain == nil then
chain = construct_node(self, {})
self._ancestorChain = chain
end
return chain
end
end
function Language:getAncestorChainOld()
local chain = self._ancestorChain
if chain == nil then
chain = {}
local step = self
while true do
local ancestors = step:getAncestors()
step = #ancestors == 1 and ancestors[1] or nil
if not step then
break
end
insert(chain, step)
end
self._ancestorChain = chain
end
return chain
end
local function fetch_descendants(self, fmt)
local descendants, family = {}, self:getFamily()
-- Iterate over all three datasets.
for _, data in ipairs{
require("Module:languages/code to canonical name"),
require("Module:etymology languages/code to canonical name"),
require("Module:families/code to canonical name"),
} do
for code in pairs(data) do
local lang = get_by_code(code, nil, true, true)
-- Test for a descendant. Earlier tests weed out most candidates, while the more intensive tests are only used sparingly.
if (
code ~= self._code and -- Not self.
lang:inFamily(family) and -- In the same family.
(
family:getProtoLanguageCode() == self._code or -- Self is the protolanguage.
self:hasDescendant(lang) or -- Full hasDescendant check.
(lang:getFullCode() == self._code and not self:hasAncestor(lang)) -- Etymology-only child which isn't an ancestor.
)
) then
if fmt == "object" then
insert(descendants, lang)
elseif fmt == "code" then
insert(descendants, code)
elseif fmt == "name" then
insert(descendants, lang:getCanonicalName())
end
end
end
end
return descendants
end
function Language:getDescendants()
local descendants = self._descendantObjects
if descendants == nil then
descendants = fetch_descendants(self, "object")
self._descendantObjects = descendants
end
return descendants
end
function Language:getDescendantCodes()
local descendants = self._descendantCodes
if descendants == nil then
descendants = fetch_descendants(self, "code")
self._descendantCodes = descendants
end
return descendants
end
function Language:getDescendantNames()
local descendants = self._descendantNames
if descendants == nil then
descendants = fetch_descendants(self, "name")
self._descendantNames = descendants
end
return descendants
end
do
local function check_lang(self, lang)
if type(lang) == "string" then
lang = get_by_code(lang, nil, true)
end
if lang:hasAncestor(self) then
return true
end
end
function Language:hasDescendant(...)
return check_inputs(self, check_lang, false, ...)
end
end
local function fetch_children(self, fmt)
local m_etym_data = require(etymology_languages_data_module)
local self_code, children = self._code, {}
for code, lang in pairs(m_etym_data) do
local _lang = lang
repeat
local parent = _lang.parent
if parent == self_code then
if fmt == "object" then
insert(children, get_by_code(code, nil, true))
elseif fmt == "code" then
insert(children, code)
elseif fmt == "name" then
insert(children, lang[1])
end
break
end
_lang = m_etym_data[parent]
until not _lang
end
return children
end
function Language:getChildren()
local children = self._childObjects
if children == nil then
children = fetch_children(self, "object")
self._childObjects = children
end
return children
end
function Language:getChildrenCodes()
local children = self._childCodes
if children == nil then
children = fetch_children(self, "code")
self._childCodes = children
end
return children
end
function Language:getChildrenNames()
local children = self._childNames
if children == nil then
children = fetch_children(self, "name")
self._childNames = children
end
return children
end
function Language:hasChild(...)
local lang = ...
if not lang then
return false
elseif type(lang) == "string" then
lang = get_by_code(lang, nil, true)
end
if lang:hasParent(self) then
return true
end
return self:hasChild(select(2, ...))
end
--[==[Returns the name of the main category of that language. Example: {{code|lua|"French language"}} for French, whose category is at [[:Category:French language]]. Unless optional argument <code>nocap</code> is given, the language name at the beginning of the returned value will be capitalized. This capitalization is correct for category names, but not if the language name is lowercase and the returned value of this function is used in the middle of a sentence.]==]
function Language:getCategoryName(nocap)
local name = self._categoryName
if name == nil then
name = self:getCanonicalName()
-- If a substrate, omit any leading article.
if self:getFamilyCode() == "qfa-sub" then
name = name:gsub("^the ", ""):gsub("^a ", "")
end
-- Only add " language" if a full language.
if self:hasType("full") then
-- Unless the canonical name already ends with "language", "lect" or their derivatives, add " language".
if not (match(name, "^ဘာသာ") or match(name, "^ဘာသာ")) then
name = "ဘာသာ" .. name
end
end
self._categoryName = name
end
if nocap then
return name
end
return mw.getContentLanguage():ucfirst(name)
end
--[==[Creates a link to the category; the link text is the canonical name.]==]
function Language:makeCategoryLink()
return make_link(self, ":Category:" .. self:getCategoryName(), self:getDisplayForm())
end
function Language:getStandardCharacters(sc)
local standard_chars = self._data.standard_chars
if type(standard_chars) ~= "table" then
return standard_chars
elseif sc and type(sc) ~= "string" then
check_object("script", nil, sc)
sc = sc:getCode()
end
if (not sc) or sc == "None" then
local scripts = {}
for _, script in pairs(standard_chars) do
insert(scripts, script)
end
return concat(scripts)
end
if standard_chars[sc] then
return standard_chars[sc] .. (standard_chars[1] or "")
end
end
--[==[
Strip diacritics from display text `text` (in a language-specific fashion), which is in the script `sc`. If `sc` is
omitted or {nil}, the script is autodetected. This also strips certain punctuation characters from the end and (in the
case of Spanish upside-down question mark and exclamation points) from the beginning; strips any whitespace at the
end of the text or between the text and final stripped punctuation characters; and applies some language-specific
Unicode normalizations to replace discouraged characters with their prescribed alternatives. Return the stripped text.
]==]
function Language:stripDiacritics(text, sc)
if (not text) or text == "" then
return text
end
sc = checkScript(text, self, sc)
text = normalize(text, sc)
-- FIXME, rename makeEntryName to stripDiacritics and get rid of second and third return values
-- everywhere
text, _, _ = iterateSectionSubstitutions(self, text, sc, nil, nil,
self._data.strip_diacritics or self._data.entry_name, "strip_diacritics", "stripDiacritics")
text = umatch(text, "^[¿¡]?(.-[^%s%p].-)%s*[؟?!;՛՜ ՞ ՟?!︖︕।॥။၊་།]?$") or text
return text
end
--[==[
Convert a ''logical'' pagename (the pagename as it appears to the user, after diacritics and punctuation have been
stripped) to a ''physical'' pagename (the pagename as it appears in the MediaWiki database). Reasons for a difference
between the two are (a) unsupported titles such as `[ ]` (with square brackets in them), `#` (pound/hash sign) and
`¯\_(ツ)_/¯` (with underscores), as well as overly long titles of various sorts; (b) "mammoth" pages that are split into
parts (e.g. `a`, which is split into physical pagenames `a/languages A to L` and `a/languages M to Z`). For almost all
purposes, you should work with logical and not physical pagenames. But there are certain use cases that require physical
pagenames, such as checking the existence of a page or retrieving a page's contents.
`pagename` is the logical pagename to be converted. `is_reconstructed_or_appendix` indicates whether the page is in the
`Reconstruction` or `Appendix` namespaces. If it is omitted or has the value {nil}, the pagename is checked for an
initial asterisk, and if found, the page is assumed to be a `Reconstruction` page. Setting a value of `false` or `true`
to `is_reconstructed_or_appendix` disables this check and allows for mainspace pagenames that begin with an asterisk.
]==]
function Language:logicalToPhysical(pagename, is_reconstructed_or_appendix)
-- FIXME: This probably shouldn't happen but it happens when makeEntryName() receives nil.
if pagename == nil then
track("nil-passed-to-logicalToPhysical")
return nil
end
local initial_asterisk
if is_reconstructed_or_appendix == nil then
local pagename_minus_initial_asterisk
initial_asterisk, pagename_minus_initial_asterisk = pagename:match("^(%*)(.*)$")
if pagename_minus_initial_asterisk then
is_reconstructed_or_appendix = true
pagename = pagename_minus_initial_asterisk
elseif self:hasType("appendix-constructed") then
is_reconstructed_or_appendix = true
end
end
if not is_reconstructed_or_appendix then
-- Check if the pagename is a listed unsupported title.
local unsupportedTitles = load_data(links_data_module).unsupported_titles
if unsupportedTitles[pagename] then
return "Unsupported titles/" .. unsupportedTitles[pagename]
end
end
-- Set `unsupported` as true if certain conditions are met.
local unsupported
-- Check if there's an unsupported character. \239\191\189 is the replacement character U+FFFD, which can't be typed
-- directly here due to an abuse filter. Unix-style dot-slash notation is also unsupported, as it is used for
-- relative paths in links, as are 3 or more consecutive tildes. Note: match is faster with magic
-- characters/charsets; find is faster with plaintext.
if (
match(pagename, "[#<>%[%]_{|}]") or
find(pagename, "\239\191\189") or
match(pagename, "%f[^%z/]%.%.?%f[%z/]") or
find(pagename, "~~~")
) then
unsupported = true
-- If it looks like an interwiki link.
elseif find(pagename, ":") then
local prefix = gsub(pagename, "^:*(.-):.*", ulower)
if (
load_data("Module:data/namespaces")[prefix] or
load_data("Module:data/interwikis")[prefix]
) then
unsupported = true
end
end
-- Escape unsupported characters so they can be used in titles. ` is used as a delimiter for this, so a raw use of
-- it in an unsupported title is also escaped here to prevent interference; this is only done with unsupported
-- titles, though, so inclusion won't in itself mean a title is treated as unsupported (which is why it's excluded
-- from the earlier test).
if unsupported then
-- FIXME: This conversion needs to be different for reconstructed pages with unsupported characters. There
-- aren't any currently, but if there ever are, we need to fix this e.g. to put them in something like
-- Reconstruction:Proto-Indo-European/Unsupported titles/`lowbar``num`.
local unsupported_characters = load_data(links_data_module).unsupported_characters
pagename = pagename:gsub("[#<>%[%]_`{|}\239]\191?\189?", unsupported_characters)
:gsub("%f[^%z/]%.%.?%f[%z/]", function(m)
return (gsub(m, "%.", "`period`"))
end)
:gsub("~~~+", function(m)
return (gsub(m, "~", "`tilde`"))
end)
pagename = "Unsupported titles/" .. pagename
elseif not is_reconstructed_or_appendix then
-- Check if this is a mammoth page. If so, which subpage should we link to?
local m_links_data = load_data(links_data_module)
local mammoth_page_type = m_links_data.mammoth_pages[pagename]
if mammoth_page_type then
local canonical_name = self:getFullName()
if canonical_name ~= "ဘာသာပံၚ်ကောံ" and canonical_name ~= "မန်" then
local this_subpage
local L2_sort_key = get_L2_sort_key(canonical_name)
for _, subpage_spec in ipairs(m_links_data.mammoth_page_subpage_types[mammoth_page_type]) do
-- unpack() fails utterly on data loaded using mw.loadData() even if offsets are given
local subpage, pattern = subpage_spec[1], subpage_spec[2]
if pattern == true or L2_sort_key:match(pattern) then
this_subpage = subpage
break
end
end
if not this_subpage then
error(("Internal error: Bad data in mammoth_page_subpage_pages in [[Module:links/data]] for mammoth page %s, type %s; last entry didn't have 'true' in it"):format(
pagename, mammoth_page_type))
end
pagename = pagename .. "/" .. this_subpage
end
end
end
return (initial_asterisk or "") .. pagename
end
--[==[
Strip the diacritics from a display pagename and convert the resulting logical pagename into a physical pagename.
This allows you, for example, to retrieve the contents of the page or check its existence. WARNING: This is deprecated
and will be going away. It is a simple composition of `self:stripDiacritics` and `self:logicalToPhysical`; most callers
only want the former, and if you need both, call them both yourself.
`text` and `sc` are as in `self:stripDiacritics`, and `is_reconstructed_or_appendix` is as in `self:logicalToPhysical`.
]==]
function Language:makeEntryName(text, sc, is_reconstructed_or_appendix)
return self:logicalToPhysical(self:stripDiacritics(text, sc), is_reconstructed_or_appendix)
end
--[==[Generates alternative forms using a specified method, and returns them as a table. If no method is specified, returns a table containing only the input term.]==]
function Language:generateForms(text, sc)
local generate_forms = self._data.generate_forms
if generate_forms == nil then
return {text}
end
sc = checkScript(text, self, sc)
return require("Module:" .. self._data.generate_forms).generateForms(text, self, sc)
end
--[==[Creates a sort key for the given stripped text, following the rules appropriate for the language. This removes
diacritical marks from the stripped text if they are not considered significant for sorting, and may perform some other
changes. Any initial hyphen is also removed, and anything in parentheses is removed as well.
The <code>sort_key</code> setting for each language in the data modules defines the replacements made by this function, or it gives the name of the module that takes the stripped text and returns a sortkey.]==]
function Language:makeSortKey(text, sc)
if (not text) or text == "" then
return text
end
if match(text, "<[^<>]+>") then
track("track HTML tag")
end
-- Remove directional characters, bold, italics, soft hyphens, strip markers and HTML tags.
-- FIXME: Partly duplicated with remove_formatting() in [[Module:links]].
text = ugsub(text, "[\194\173\226\128\170-\226\128\174\226\129\166-\226\129\169]", "")
text = text:gsub("('*)'''(.-'*)'''", "%1%2"):gsub("('*)''(.-'*)''", "%1%2")
text = gsub(unstrip(text), "<[^<>]+>", "")
text = decode_uri(text, "PATH")
text = checkNoEntities(self, text)
-- Remove initial hyphens and * unless the term only consists of spacing + punctuation characters.
text = ugsub(text, "^([-]*)[-־ـ᠊*]+([-]*)(.*[^%s%p].*)", "%1%2%3")
sc = checkScript(text, self, sc)
text = normalize(text, sc)
text = removeCarets(text, sc)
-- For languages with dotted dotless i, ensure that "İ" is sorted as "i", and "I" is sorted as "ı".
if self:hasDottedDotlessI() then
text = gsub(text, "I\204\135", "i") -- decomposed "İ"
:gsub("I", "ı")
text = sc:toFixedNFD(text)
end
-- Convert to lowercase, make the sortkey, then convert to uppercase. Where the language has dotted dotless i, it is
-- usually not necessary to convert "i" to "İ" and "ı" to "I" first, because "I" will always be interpreted as
-- conventional "I" (not dotless "İ") by any sorting algorithms, which will have been taken into account by the
-- sortkey substitutions themselves. However, if no sortkey substitutions have been specified, then conversion is
-- necessary so as to prevent "i" and "ı" both being sorted as "I".
--
-- An exception is made for scripts that (sometimes) sort by scraping page content, as that means they are sensitive
-- to changes in capitalization (as it changes the target page).
if not sc:sortByScraping() then
text = ulower(text)
end
local actual_substitution_data
-- Don't trim whitespace here because it's significant at the beginning of a sort key or sort base.
text, _, actual_substitution_data = iterateSectionSubstitutions(self, text, sc, nil, nil, self._data.sort_key,
"sort_key", "makeSortKey", "notrim")
if not sc:sortByScraping() then
if self:hasDottedDotlessI() and not actual_substitution_data then
text = text:gsub("ı", "I"):gsub("i", "İ")
text = sc:toFixedNFC(text)
end
text = uupper(text)
end
-- Remove parentheses, as long as they are either preceded or followed by something.
text = gsub(text, "(.)[()]+", "%1"):gsub("[()]+(.)", "%1")
text = escape_risky_characters(text)
return text
end
--[==[Create the form used as as a basis for display text and transliteration. FIXME: Rename to correctInputText().]==]
local function processDisplayText(text, self, sc, keepCarets, keepPrefixes)
local subbedChars = {}
text, subbedChars = doTempSubstitutions(text, subbedChars, keepCarets)
text = decode_uri(text, "PATH")
text = checkNoEntities(self, text)
sc = checkScript(text, self, sc)
text = normalize(text, sc)
text, subbedChars = iterateSectionSubstitutions(self, text, sc, subbedChars, keepCarets, self._data.display_text,
"display_text", "makeDisplayText")
text = removeCarets(text, sc)
-- Remove any interwiki link prefixes (unless they have been escaped or this has been disabled).
if find(text, ":") and not keepPrefixes then
local rep
repeat
text, rep = gsub(text, "\\\\(\\*:)", "\3%1")
until rep == 0
text = gsub(text, "\\:", "\4")
while true do
local prefix = gsub(text, "^(.-):.+", function(m1)
return (gsub(m1, "\244[\128-\191]*", ""))
end)
-- Check if the prefix is an interwiki, though ignore capitalised Wiktionary:, which is a namespace.
if not prefix or prefix == text or prefix == "ဝိတ်ရှေန်နရဳ"
or not (load_data("Module:data/interwikis")[ulower(prefix)] or prefix == "") then
break
end
text = gsub(text, "^(.-):(.*)", function(m1, m2)
local ret = {}
for subbedChar in gmatch(m1, "\244[\128-\191]*") do
insert(ret, subbedChar)
end
return concat(ret) .. m2
end)
end
text = gsub(text, "\3", "\\"):gsub("\4", ":")
end
return text, subbedChars
end
--[==[Make the display text (i.e. what is displayed on the page).]==]
function Language:makeDisplayText(text, sc, keepPrefixes)
if not text or text == "" then
return text
end
local subbedChars
text, subbedChars = processDisplayText(text, self, sc, nil, keepPrefixes)
text = escape_risky_characters(text)
return undoTempSubstitutions(text, subbedChars)
end
--[==[Transliterates the text from the given script into the Latin script (see
[[Wiktionary:Transliteration and romanization]]). The language must have the <code>translit</code> property for this to
work; if it is not present, {{code|lua|nil}} is returned.
The <code>sc</code> parameter is handled by the transliteration module, and how it is handled is specific to that
module. Some transliteration modules may tolerate {{code|lua|nil}} as the script, others require it to be one of the
possible scripts that the module can transliterate, and will throw an error if it's not one of them. For this reason,
the <code>sc</code> parameter should always be provided when writing non-language-specific code.
The <code>module_override</code> parameter is used to override the default module that is used to provide the
transliteration. This is useful in cases where you need to demonstrate a particular module in use, but there is no
default module yet, or you want to demonstrate an alternative version of a transliteration module before making it
official. It should not be used in real modules or templates, only for testing. All uses of this parameter are tracked
by [[Wiktionary:Tracking/languages/module_override]].
'''Known bugs''':
* This function assumes {tr(s1) .. tr(s2) == tr(s1 .. s2)}. When this assertion fails, wikitext markups like <nowiki>'''</nowiki> can cause wrong transliterations.
* HTML entities like <code>&apos;</code>, often used to escape wikitext markups, do not work.
]==]
function Language:transliterate(text, sc, module_override)
-- If there is no text, or the language doesn't have transliteration data and there's no override, return nil.
if not text or text == "" or text == "-" then
return text
end
-- If the script is not transliteratable (and no override is given), return nil.
sc = checkScript(text, self, sc)
if not (sc:isTransliterated() or module_override) then
-- temporary tracking to see if/when this gets triggered
track("non-transliterable")
track("non-transliterable/" .. self._code)
track("non-transliterable/" .. sc:getCode())
track("non-transliterable/" .. sc:getCode() .. "/" .. self._code)
return nil
end
-- Remove any strip markers.
text = unstrip(text)
-- Do not process the formatting into PUA characters for certain languages.
local processed = load_data(languages_data_module).substitution[self._code] ~= "none"
-- Get the display text with the keepCarets flag set.
local subbedChars
if processed then
text, subbedChars = processDisplayText(text, self, sc, true)
end
-- Transliterate (using the module override if applicable).
text, subbedChars = iterateSectionSubstitutions(self, text, sc, subbedChars, true, module_override or
self._data.translit, "translit", "tr")
if not text then
return nil
end
-- Incomplete transliterations return nil.
local charset = sc.characters
if charset and umatch(text, "[" .. charset .. "]") then
-- Remove any characters in Latin, which includes Latin characters also included in other scripts (as these are
-- false positives), as well as any PUA substitutions. Anything remaining should only be script code "None"
-- (e.g. numerals).
local check_text = ugsub(text, "[" .. get_script("Latn").characters .. "-]+", "")
-- Set none_is_last_resort_only flag, so that any non-None chars will cause a script other than "None" to be
-- returned.
if find_best_script_without_lang(check_text, true):getCode() ~= "None" then
return nil
end
end
if processed then
text = escape_risky_characters(text)
text = undoTempSubstitutions(text, subbedChars)
end
-- If the script does not use capitalization, then capitalize any letters of the transliteration which are
-- immediately preceded by a caret (and remove the caret).
if text and not sc:hasCapitalization() and text:find("^", 1, true) then
text = processCarets(text, "%^([\128-\191\244]*%*?)([^\128-\191\244][\128-\191]*)", function(m1, m2)
return m1 .. uupper(m2)
end)
end
-- Track module overrides.
if module_override ~= nil then
track("module_override")
end
return text
end
do
local function handle_language_spec(self, spec, sc)
local ret = self["_" .. spec]
if ret == nil then
ret = self._data[spec]
if type(ret) == "string" then
ret = list_to_set(split(ret, ",", true, true))
end
self["_" .. spec] = ret
end
if type(ret) == "table" then
ret = ret[sc:getCode()]
end
return not not ret
end
function Language:overrideManualTranslit(sc)
return handle_language_spec(self, "override_translit", sc)
end
function Language:link_tr(sc)
return handle_language_spec(self, "link_tr", sc)
end
end
--[==[Returns {{code|lua|true}} if the language has a transliteration module, or {{code|lua|false}} if it doesn't.]==]
function Language:hasTranslit()
return not not self._data.translit
end
--[==[Returns {{code|lua|true}} if the language uses the letters I/ı and İ/i, or {{code|lua|false}} if it doesn't.]==]
function Language:hasDottedDotlessI()
return not not self._data.dotted_dotless_i
end
function Language:toJSON(opts)
local strip_diacritics, strip_diacritics_patterns, strip_diacritics_remove_diacritics = self._data.strip_diacritics
if strip_diacritics then
if strip_diacritics.from then
strip_diacritics_patterns = {}
for i, from in ipairs(strip_diacritics.from) do
insert(strip_diacritics_patterns, {from = from, to = strip_diacritics.to[i] or ""})
end
end
strip_diacritics_remove_diacritics = strip_diacritics.remove_diacritics
end
-- mainCode should only end up non-nil if dontCanonicalizeAliases is passed to make_object().
-- props should either contain zero-argument functions to compute the value, or the value itself.
local props = {
ancestors = function() return self:getAncestorCodes() end,
canonicalName = function() return self:getCanonicalName() end,
categoryName = function() return self:getCategoryName("nocap") end,
code = self._code,
mainCode = self._mainCode,
parent = function() return self:getParentCode() end,
full = function() return self:getFullCode() end,
stripDiacriticsPatterns = strip_diacritics_patterns,
stripDiacriticsRemoveDiacritics = strip_diacritics_remove_diacritics,
family = function() return self:getFamilyCode() end,
aliases = function() return self:getAliases() end,
varieties = function() return self:getVarieties() end,
otherNames = function() return self:getOtherNames() end,
scripts = function() return self:getScriptCodes() end,
type = function() return keys_to_list(self:getTypes()) end,
wikimediaLanguages = function() return self:getWikimediaLanguageCodes() end,
wikidataItem = function() return self:getWikidataItem() end,
wikipediaArticle = function() return self:getWikipediaArticle(true) end,
}
local ret = {}
for prop, val in pairs(props) do
if not opts.skip_fields or not opts.skip_fields[prop] then
if type(val) == "function" then
ret[prop] = val()
else
ret[prop] = val
end
end
end
-- Use `deep_copy` when returning a table, so that there are no editing restrictions imposed by `mw.loadData`.
return opts and opts.lua_table and deep_copy(ret) or to_json(ret, opts)
end
function export.getDataModuleName(code)
local letter = match(code, "^(%l)%l%l?$")
return "Module:" .. (
letter == nil and "languages/data/exceptional" or
#code == 2 and "languages/data/2" or
"languages/data/3/" .. letter
)
end
get_data_module_name = export.getDataModuleName
function export.getExtraDataModuleName(code)
return get_data_module_name(code) .. "/extra"
end
get_extra_data_module_name = export.getExtraDataModuleName
do
local function make_stack(data)
local key_types = {
[2] = "unique",
aliases = "unique",
otherNames = "unique",
type = "append",
varieties = "unique",
wikipedia_article = "unique",
wikimedia_codes = "unique"
}
local function __index(self, k)
local stack, key_type = getmetatable(self), key_types[k]
-- Data that isn't inherited from the parent.
if key_type == "unique" then
local v = stack[stack[make_stack]][k]
if v == nil then
local layer = stack[0]
if layer then -- Could be false if there's no extra data.
v = layer[k]
end
end
return v
-- Data that is appended by each generation.
elseif key_type == "append" then
local parts, offset, n = {}, 0, stack[make_stack]
for i = 1, n do
local part = stack[i][k]
if part == nil then
offset = offset + 1
else
parts[i - offset] = part
end
end
return offset ~= n and concat(parts, ",") or nil
end
local n = stack[make_stack]
while true do
local layer = stack[n]
if not layer then -- Could be false if there's no extra data.
return nil
end
local v = layer[k]
if v ~= nil then
return v
end
n = n - 1
end
end
local function __newindex()
error("table is read-only")
end
local function __pairs(self)
-- Iterate down the stack, caching keys to avoid duplicate returns.
local stack, seen = getmetatable(self), {}
local n = stack[make_stack]
local iter, state, k, v = pairs(stack[n])
return function()
repeat
repeat
k = iter(state, k)
if k == nil then
n = n - 1
local layer = stack[n]
if not layer then -- Could be false if there's no extra data.
return nil
end
iter, state, k = pairs(layer)
end
until not (k == nil or seen[k])
-- Get the value via a lookup, as the one returned by the
-- iterator will be the raw value from the current layer,
-- which may not be the one __index will return for that
-- key. Also memoize the key in `seen` (even if the lookup
-- returns nil) so that it doesn't get looked up again.
-- TODO: store values in `self`, avoiding the need to create
-- the `seen` table. The iterator will need to iterate over
-- `self` with `next` first to find these on future loops.
v, seen[k] = self[k], true
until v ~= nil
return k, v
end
end
local __ipairs = require(table_module).indexIpairs
function make_stack(data)
local stack = {
data,
[make_stack] = 1, -- stores the length and acts as a sentinel to confirm a given metatable is a stack.
__index = __index,
__newindex = __newindex,
__pairs = __pairs,
__ipairs = __ipairs,
}
stack.__metatable = stack
return setmetatable({}, stack), stack
end
return make_stack(data)
end
local function get_stack(data)
local stack = getmetatable(data)
return stack and type(stack) == "table" and stack[make_stack] and stack or nil
end
--[==[
<span style="color: var(--wikt-palette-red,#BA0000)">This function is not for use in entries or other content pages.</span>
Returns a blob of data about the language. The format of this blob is undocumented, and perhaps unstable; it's intended for things like the module's own unit-tests, which are "close friends" with the module and will be kept up-to-date as the format changes. If `extra` is set, any extra data in the relevant `/extra` module will be included. (Note that it will be included anyway if it has already been loaded into the language object.) If `raw` is set, then the returned data will not contain any data inherited from parent objects.
-- Do NOT use these methods!
-- All uses should be pre-approved on the talk page!
]==]
function Language:getData(extra, raw)
if extra then
self:loadInExtraData()
end
local data = self._data
-- If raw is not set, just return the data.
if not raw then
return data
end
local stack = get_stack(data)
-- If there isn't a stack or its length is 1, return the data. Extra data (if any) will be included, as it's stored at key 0 and doesn't affect the reported length.
if stack == nil then
return data
end
local n = stack[make_stack]
if n == 1 then
return data
end
local extra = stack[0]
-- If there isn't any extra data, return the top layer of the stack.
if extra == nil then
return stack[n]
end
-- If there is, return a new stack which has the top layer at key 1 and the extra data at key 0.
data, stack = make_stack(stack[n])
stack[0] = extra
return data
end
function Language:loadInExtraData()
-- Only full languages have extra data.
if not self:hasType("language", "full") then
return
end
local data = self._data
-- If there's no stack, create one.
local stack = get_stack(self._data)
if stack == nil then
data, stack = make_stack(data)
-- If already loaded, return.
elseif stack[0] ~= nil then
return
end
self._data = data
-- Load extra data from the relevant module and add it to the stack at key 0, so that the __index and __pairs metamethods will pick it up, since they iterate down the stack until they run out of layers.
local code = self._code
local modulename = get_extra_data_module_name(code)
-- No data cached as false.
stack[0] = modulename and load_data(modulename)[code] or false
end
--[==[Returns the name of the module containing the language's data. Currently, this is always [[Module:scripts/data]].]==]
function Language:getDataModuleName()
local name = self._dataModuleName
if name == nil then
name = self:hasType("etymology-only") and etymology_languages_data_module or
get_data_module_name(self._mainCode or self._code)
self._dataModuleName = name
end
return name
end
--[==[Returns the name of the module containing the language's data. Currently, this is always [[Module:scripts/data]].]==]
function Language:getExtraDataModuleName()
local name = self._extraDataModuleName
if name == nil then
name = not self:hasType("etymology-only") and get_extra_data_module_name(self._mainCode or self._code) or false
self._extraDataModuleName = name
end
return name or nil
end
function export.makeObject(code, data, dontCanonicalizeAliases)
local data_type = type(data)
if data_type ~= "table" then
error(("bad argument #2 to 'makeObject' (table expected, got %s)"):format(data_type))
end
-- Convert any aliases.
local input_code = code
code = normalize_code(code)
input_code = dontCanonicalizeAliases and input_code or code
local parent
if data.parent then
parent = get_by_code(data.parent, nil, true, true)
else
parent = Language
end
parent.__index = parent
local lang = {_code = input_code}
-- This can only happen if dontCanonicalizeAliases is passed to make_object().
if code ~= input_code then
lang._mainCode = code
end
local parent_data = parent._data
if parent_data == nil then
-- Full code is the same as the code.
lang._fullCode = parent._code or code
else
-- Copy full code.
lang._fullCode = parent._fullCode
local stack = get_stack(parent_data)
if stack == nil then
parent_data, stack = make_stack(parent_data)
end
-- Insert the input data as the new top layer of the stack.
local n = stack[make_stack] + 1
data, stack[n], stack[make_stack] = parent_data, data, n
end
lang._data = data
return setmetatable(lang, parent)
end
make_object = export.makeObject
end
--[==[Finds the language whose code matches the one provided. If it exists, it returns a <code class="nf">Language</code> object representing the language. Otherwise, it returns {{code|lua|nil}}, unless <code class="n">paramForError</code> is given, in which case an error is generated. If <code class="n">paramForError</code> is {{code|lua|true}}, a generic error message mentioning the bad code is generated; otherwise <code class="n">paramForError</code> should be a string or number specifying the parameter that the code came from, and this parameter will be mentioned in the error message along with the bad code. If <code class="n">allowEtymLang</code> is specified, etymology-only language codes are allowed and looked up along with normal language codes. If <code class="n">allowFamily</code> is specified, language family codes are allowed and looked up along with normal language codes.]==]
function export.getByCode(code, paramForError, allowEtymLang, allowFamily)
-- Track uses of paramForError, ultimately so it can be removed, as error-handling should be done by [[Module:parameters]], not here.
if paramForError ~= nil then
track("paramForError")
end
if type(code) ~= "string" then
local typ
if not code then
typ = "nil"
elseif check_object("language", true, code) then
typ = "a language object"
elseif check_object("family", true, code) then
typ = "a family object"
else
typ = "a " .. type(code)
end
error("The function getByCode expects a string as its first argument, but received " .. typ .. ".")
end
local m_data = load_data(languages_data_module)
if m_data.aliases[code] or m_data.track[code] then
track(code)
end
local norm_code = normalize_code(code)
-- Get the data, checking for etymology-only languages if allowEtymLang is set.
local data = load_data(get_data_module_name(norm_code))[norm_code] or
allowEtymLang and load_data(etymology_languages_data_module)[norm_code]
-- If no data was found and allowFamily is set, check the family data. If the main family data was found, make the object with [[Module:families]] instead, as family objects have different methods. However, if it's an etymology-only family, use make_object in this module (which handles object inheritance), and the family-specific methods will be inherited from the parent object.
if data == nil and allowFamily then
data = load_data("Module:families/data")[norm_code]
if data ~= nil then
if data.parent == nil then
return make_family_object(norm_code, data)
elseif not allowEtymLang then
data = nil
end
end
end
local retval = code and data and make_object(code, data)
if not retval and paramForError then
require("Module:languages/errorGetBy").code(code, paramForError, allowEtymLang, allowFamily)
end
return retval
end
get_by_code = export.getByCode
--[==[Finds the language whose canonical name (the name used to represent that language on Wiktionary) or other name matches the one provided. If it exists, it returns a <code class="nf">Language</code> object representing the language. Otherwise, it returns {{code|lua|nil}}, unless <code class="n">paramForError</code> is given, in which case an error is generated. If <code class="n">allowEtymLang</code> is specified, etymology-only language codes are allowed and looked up along with normal language codes. If <code class="n">allowFamily</code> is specified, language family codes are allowed and looked up along with normal language codes.
The canonical name of languages should always be unique (it is an error for two languages on Wiktionary to share the same canonical name), so this is guaranteed to give at most one result.
This function is powered by [[Module:languages/canonical names]], which contains a pre-generated mapping of full-language canonical names to codes. It is generated by going through the [[:Category:Language data modules]] for full languages. When <code class="n">allowEtymLang</code> is specified for the above function, [[Module:etymology languages/canonical names]] may also be used, and when <code class="n">allowFamily</code> is specified for the above function, [[Module:families/canonical names]] may also be used.]==]
function export.getByCanonicalName(name, errorIfInvalid, allowEtymLang, allowFamily)
local byName = load_data("Module:languages/canonical names")
local code = byName and byName[name]
if not code and allowEtymLang then
byName = load_data("Module:etymology languages/canonical names")
code = byName and byName[name] or
byName[gsub(name, " [Ss]ubstrate$", "")] or
byName[gsub(name, "^a ", "")] or
byName[gsub(name, "^a ", ""):gsub(" [Ss]ubstrate$", "")] or
-- For etymology families like "ira-pro".
-- FIXME: This is not ideal, as it allows " languages" to be appended to any etymology-only language, too.
byName[match(name, "^ဘာသာ(.*)$")]
end
if not code and allowFamily then
byName = load_data("Module:families/canonical names")
code = byName[name] or byName[match(name, "^ဘာသာ(.*)$")]
end
local retval = code and get_by_code(code, errorIfInvalid, allowEtymLang, allowFamily)
if not retval and errorIfInvalid then
require("Module:languages/errorGetBy").canonicalName(name, allowEtymLang, allowFamily)
end
return retval
end
--[==[Used by [[Module:languages/data/2]] (et al.) and [[Module:etymology languages/data]], [[Module:families/data]], [[Module:scripts/data]] and [[Module:writing systems/data]] to finalize the data into the format that is actually returned.]==]
function export.finalizeData(data, main_type, variety)
local fields = {"type"}
if main_type == "language" then
insert(fields, 4) -- script codes
insert(fields, "ancestors")
insert(fields, "link_tr")
insert(fields, "override_translit")
insert(fields, "wikimedia_codes")
elseif main_type == "script" then
insert(fields, 3) -- writing system codes
end -- Families and writing systems have no extra fields to process.
local fields_len = #fields
for _, entity in next, data do
if variety then
-- Move parent from 3 to "parent" and family from "family" to 3. These are different for the sake of convenience, since very few varieties have the family specified, whereas all of them have a parent.
entity.parent, entity[3], entity.family = entity[3], entity.family
-- Give the type "regular" iff not a variety and no other types are assigned.
elseif not (entity.type or entity.parent) then
entity.type = "regular"
end
for i = 1, fields_len do
local key = fields[i]
local field = entity[key]
if field and type(field) == "string" then
entity[key] = gsub(field, "%s*,%s*", ",")
end
end
end
return data
end
--[==[For backwards compatibility only; modules should require the error themselves.]==]
function export.err(lang_code, param, code_desc, template_tag, not_real_lang)
return require("Module:languages/error")(lang_code, param, code_desc, template_tag, not_real_lang)
end
return export
hifrcctqpwkp7bgqb8uxce19qc1hc2i
ထာမ်ပလိက်:maintenance box
10
692
396534
157397
2026-06-07T16:21:57Z
咽頭べさ
33
396534
wikitext
text/x-wiki
<templatestyles src="Module:message box/styles.css" /><div class="noprint maintenance-box maintenance-box-{{{1|blue}}}">
{|
| rowspan="2" class="maintenance-box-image-cell" | {{{image|[[File:Codex icon Notice blue.svg|40px|alt=Notice]]}}}
! style="text-align: left;" | {{{title|}}}
|-
| {{{text|}}}
|}</div><noinclude>{{documentation}}</noinclude>
pn4fc374opf897cbzurxrblzw75otms
မဝ်ဂျူ:headword/data
828
735
396589
395417
2026-06-07T21:04:17Z
咽頭べさ
33
396589
Scribunto
text/plain
local headword_page_module = "Module:headword/page"
local list_to_set = require("Module:table").listToSet
local data = {}
------ 1. Lists which are converted into sets. ------
--[==[ var:
Large pages where we disable label tracking, red link checking and similar.
]==]
data.large_pages = list_to_set {
-- pages that consistently hit timeouts
"a",
-- pages that sometimes hit timeouts
"A",
"baba",
"de",
"e",
"i",
"lima",
"o",
"u",
"и",
"山",
"子",
"月",
"一",
"人",
}
--[==[ var:
Map from singular to plural, and from plural to itself, for recognized parts of speech with irregular plurals. Most of
these are invariable plurals, e.g. `kanji` is its own plural; but we also have `mora` plural `morae`.
]==]
data.irregular_plurals = list_to_set({
"cmavo",
"cmene",
"fu'ivla",
"gismu",
"Han tu",
"hanja",
"hanzi",
"jyutping",
"kana",
"kanji",
"lujvo",
"phrasebook",
"pinyin",
"rafsi",
}, function(_, item)
return item
end)
local irregular_plurals = data.irregular_plurals
-- Irregular non-zero plurals AND any regular plurals where the singular ends in "s",
-- because the module assumes that inputs ending in "s" are plurals. The singular and
-- plural both need to be added, as the module will generate a default plural if
-- the input doesn't match a key in this table.
for sg, pl in next, {
mora = "morae"
} do
irregular_plurals[sg], irregular_plurals[pl] = pl, pl
end
--[==[ var:
Recognized lemmas. If the part of speech in {{tl|head}} is set to one of these or its singular equivalent, the category
'LANG lemmas' will automatically be added. If the part of speech is not a singular or plural lemma or non-lemma form and
is not an abbreviation that expands to a recognized lemma or non-lemma form, the page will be added to various tracking
categories:
* [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos]]
* [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos/LANG]]
* [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos/pos/POS]]
* [[Special:WhatLinksHere/Wiktionary:Tracking/headword/unrecognized pos/pos/POS/LANG]]
]==]
data.lemmas = list_to_set{
"အက္ခရ်ဂၠေံ",
"ဝေါဟာဂၠေံ",
"နာမဝိသေသန",
"အာက်နဝ်မာဲနဝ်",
"adpositions",
"ဝါကျ",
"အဳမိုဂျဳ",
"ကြိယာဝိသေသန",
"အဆက်စုတ်လက္ကရဴ",
"ambipositions",
"လိက်ပရေၚ်",
"circumfixes",
"ကြိယာဗီုပြၚ်သိုၚ်တၟိ",
"circumpositions",
"နာမ်ပါ်ကၞာတ်",
"သဳမာဝဝ်",
"ခၠာတ်သတေသဳမာဝဝ်",
"cmene",
"combining forms",
"သမ္ဗန္ဓ",
"counters",
"ဖျေံလဝ်သန္နိဋ္ဌာန်",
"ခရက်သမ္တီလဝ်ဓမံက်ထ္ၜးရမျာၚ်",
"digraphs",
"equative adjectives",
"fu'ivla",
"gismu",
"Han characters",
"Han tu",
"ဟာန်ဂျာ",
"ဟာန်သဳ",
"ideophones",
"idioms",
"စန်",
"initialisms",
"iteration marks",
"interfixes",
"အာမေဍိက်",
"kana",
"kanji",
"အက္ခရ်",
"ligatures",
"logograms",
"lujvo",
"morae",
"morphemes",
"non-constituents",
"နာမ်",
"ဂၞန်သၚ်္ချာ",
"သၚ်္ကေတဂၞန်သၚ်္ချာ",
"ဂၞန်သၚ်္ချာ",
"ပစ္စဲ",
"မအရေဝ်",
"ကဆံၚ်အကာဲအရာ",
"ဝါကျကဆံၚ်အကာဲအရာ",
"predicatives",
"အဆက်ဂတ",
"ဝါကျဝိဘတ်",
"ဝိဘတ်",
"ပေါရာဏာံပေါရာဒါံ",
"pronominal adverbs",
"သဗ္ဗနာမ်",
"နာမ်မကိတ်ညဳ",
"punctuation marks",
"မသ",
"တံရိုဟ်",
"တံမအရေဝ်",
"အဆက်လက္ကရဴ",
"ဝဏ္ဏ",
"သၚ်္ကေတ",
"ကြိယာ",
"ကၞာတ်အမှိက်",
}
--[==[ var:
Recognized non-lemma forms. If the part of speech in {{tl|head}} is set to one of these or its singular equivalent, the
category 'LANG non-lemma forms' will automatically be added. If the part of speech is not a singular or plural lemma or
non-lemma form and is not an abbreviation that expands to a recognized lemma or non-lemma form, the page will be added
to various tracking categories; see the documentation of `data.lemmas`.
]==]
data.nonlemmas = list_to_set{
"ဗီုပြၚ်လုပ်ကၠောန်စွံလဝ်နကဵုမစိုပ်တရဴ",
"လုပ်ကၠောန်စွံလဝ်နကဵုမစိုပ်တရဴ",
"ခ္ဍံက်လိက်ဗၠေတ်",
"နာမ်ဗီုပြၚ်သိုၚ်တၟိ",
"adjectival participles",
"ဗီုပြၚ်နာမဝိသေသန",
"နာမဝိသေသနဗီုပြၚ်ဣတ္တိလိၚ်",
"နာမဝိသေသနဗီုပြၚ်ကိုန်ဗဟုဝစ်",
"ဗီုပြၚ်ကြိယာဝိသေသန",
"adverbial participles",
"agent participles",
"article forms",
"circumfix forms",
"combined forms",
"ဗီုပြၚ်ပတုပ်ရံၚ်နာမဝိသေသန",
"နာမဝိသေသနပတုပ်ရံၚ်",
"ဗီုပြၚ်ပတုပ်ရံၚ်ကြိယာဝိသေသန",
"ကြိယာဝိသေသနပတုပ်ရံၚ်",
"ပွမထညောံ",
"contractions",
"converbs",
"determiner comparative forms",
"ဗီုပြၚ်ဖျေံလဝ်သန္နိဋ္ဌာန်",
"determiner superlative forms",
"နာမ်မလဟုတ်စှ်ေ",
"elative adjectives",
"equative adjective forms",
"equative adjectives",
"future participles",
"ဗီုပြၚ်ကြိယာမဒှ်နာမ်",
"infinitive forms",
"infinitives",
"interjection forms",
"ယျဝဳဖေန်",
"kanji readings",
"negative participles",
"nominal participles",
"သဗ္ဗနာမ်ဝိဘတ်",
"ဗီုပြၚ်ရုပ်နာမ်",
"နာမ်ဗီုပြၚ်ၜါလ္ပာ်",
"ဗီုပြၚ်နာမ်",
"ဗီုပြၚ်နာမ်ပဝ်ကာယ်လ်",
"နာမ်ဗီုပြၚ်ကိုန်ဗဟုဝစ်",
"နာမ်ဗီုပြၚ်ပိုၚ်ပြဳ",
"နာမ်ဗီုပြၚ်ကိုန်ဨကဝုစ်",
"ဗီုပြၚ်ဂၞန်သၚ်္ချာ",
"လုပ်ကၠောန်စွံလဝ်",
"ဗီုပြၚ်လုပ်ကၠောန်စွံလဝ်",
"ဗီုပြၚ်ကၞာတ်အမှိက်",
"လုပ်ကၠောန်စွံလဝ်ဟွံတဝ်စၞေဟ်",
"လုပ်ကၠောန်စွံလဝ်နကဵုအတိက်ပြဟ်ပြေဟ်",
"လုပ်ကၠောန်စွံလဝ်နကဵုအတိက်",
"ဗီုပြၚ်လုပ်ကၠောန်စွံလဝ်နကဵုအတိက်",
"လုပ်ကၠောန်စွံလဝ်ဗီုပြၚ်ဟွံတဝ်စၞေဟ်နူအတိက်",
"လုပ်ကၠောန်စွံလဝ်မက္ဍိုပ်ပေၚ်ပြဟ်ပြေဟ်",
"လုပ်ကၠောန်စွံလဝ်ဗီုပြၚ်မက္ဍိုပ်ပေၚ်",
"လုပ်ကၠောန်စွံလဝ်မက္ဍိုပ်ပေၚ်ဟွံတဝ်စၞေဟ်",
"ဖေန်အိန်",
"ကိုန်ဗဟုဝစ်",
"ဗီုပြၚ်ပသ္ၚောဲထောံ",
"ဗီုပြၚ်မုက်နာမ်",
"ဝိဘတ်ပသ္ၚောဲထောံလဝ်",
"ဗီုပြၚ်ဝိဘတ်",
"ဝိဘတ်ဗီုပြၚ်သဗ္ဗနာမ်",
"လုပ်ကၠောန်စွံလဝ်ပစ္စုပ္ပန်ပြဟ်ပြေဟ်",
"လုပ်ကၠောန်စွံလဝ်ပစ္စုပ္ပန်",
"လုပ်ကၠောန်စွံလဝ်ပစ္စုပ္ပန်ဗီုပြၚ်ဟွံတဝ်စၞေဟ်",
"ဗီုပြၚ်သဗ္ဗနာမ်",
"သဗ္ဗနာမ်ဗီုပြၚ်ပိုၚ်ပြဳ",
"ဗီုပြၚ်နာမ်မကိတ်ညဳ",
"နာမ်မကိတ်ညဳဗီုပြၚ်ကိုန်ဗဟုဝစ်",
"rafsi",
"ဗီုအက္ခရ်ရောမ",
"ဗီုပြၚ်တံရိုဟ်",
"ကိုန်ဨကဝုစ်",
"ဗီုပြၚ်အဆက်လက္ကရဴ",
"ဗီုပြၚ်သဒ္ဒာနာမဝိသေသန",
"သဒ္ဒာနာမဝိသေသန",
"ဗီုပြၚ်သဒ္ဒာကြိယာဝိသေသန",
"သဒ္ဒာကြိယာဝိသေသန",
"ဗီုပြၚ်ကြိယာ",
"နာမ်ဝါစာ",
"နာမ်အပြံၚ်အသၠာဲ",
"ကြိယာအပြံၚ်အသၠာဲ",
}
--[==[ var:
List of languages that will not have links to separate parts of the headword.
]==]
data.no_multiword_links = list_to_set{
"zh",
}
--[==[ var:
List of languages that will not have `LANG multiword terms` categories added. There are various reasons why languages
are in this list: (a) words are written without spaces between them; (b) syllables are written with spaces between them;
(c) variant reconstructions are notated with a tilde surrounded by spaces; (d) the language is a sign language, where
pagenames are multiword descriptions of the gesture(s) required to make an individual sign; (e) some other weirdnesses.
]==]
data.no_multiword_cat = list_to_set{
-------- Languages without spaces between words (sometimes spaces between phrases) --------
"blt", -- Tai Dam
"ja", -- Japanese
"khb", -- Lü
"km", -- Khmer
"lo", -- Lao
"mnw", -- Mon
"my", -- Burmese
"nan", -- Min Nan (some words in Latin script; hyphens between syllables)
"nan-hbl", -- Hokkien (some words in Latin script; hyphens between syllables)
"nod", -- Northern Thai
"ojp", -- Old Japanese
"shn", -- Shan
"sou", -- Southern Thai
"tdd", -- Tai Nüa
"th", -- Thai
"tts", -- Isan
"twh", -- Tai Dón
"txg", -- Tangut
"zh", -- Chinese (all varieties with Chinese characters)
"zkt", -- Khitan
-------- Languages with spaces between syllables --------
"ahk", -- Akha
"aou", -- A'ou
"atb", -- Zaiwa
"byk", -- Biao
"cdy", -- Chadong
--"duu", -- Drung; not sure
--"hmx-pro", -- Proto-Hmong-Mien
--"hnj", -- Green Hmong; not sure
"huq", -- Tsat
"ium", -- Iu Mien
--"lis", -- Lisu; not sure
"mtq", -- Muong
--"mww", -- White Hmong; not sure
"onb", -- Lingao
--"sit-gkh", -- Gokhy; not sure
--"swi", -- Sui; not sure
"tbq-lol-pro", -- Proto-Loloish
"tdh", -- Thulung
"ukk", -- Muak Sa-aak
"vi", -- Vietnamese
"yig", -- Wusa Nasu
"zng", -- Mang
-------- Languages with ~ with surrounding spaces used to separate variants --------
"mkh-ban-pro", -- Proto-Bahnaric
"sit-pro", -- Proto-Sino-Tibetan; listed above
-------- Other weirdnesses --------
"mul", -- Translingual; gestures, Morse code, etc.
"aot", -- Atong (India); bullet is a letter
-------- All sign languages --------
"ads",
"aed",
"aen",
"afg",
"ase",
"asf",
"asp",
"asq",
"asw",
"bfi",
"bfk",
"bog",
"bqn",
"bqy",
"bvl",
"bzs",
"cds",
"csc",
"csd",
"cse",
"csf",
"csg",
"csl",
"csn",
"csq",
"csr",
"doq",
"dse",
"dsl",
"ecs",
"esl",
"esn",
"eso",
"eth",
"fcs",
"fse",
"fsl",
"fss",
"gds",
"gse",
"gsg",
"gsm",
"gss",
"gus",
"hab",
"haf",
"hds",
"hks",
"hos",
"hps",
"hsh",
"hsl",
"icl",
"iks",
"ils",
"inl",
"ins",
"ise",
"isg",
"isr",
"jcs",
"jhs",
"jls",
"jos",
"jsl",
"jus",
"kgi",
"kvk",
"lbs",
"lls",
"lsl",
"lso",
"lsp",
"lst",
"lsy",
"lws",
"mdl",
"mfs",
"mre",
"msd",
"msr",
"mzc",
"mzg",
"mzy",
"nbs",
"ncs",
"nsi",
"nsl",
"nsp",
"nsr",
"nzs",
"okl",
"pgz",
"pks",
"prl",
"prz",
"psc",
"psd",
"psg",
"psl",
"pso",
"psp",
"psr",
"pys",
"rms",
"rsl",
"rsm",
"sdl",
"sfb",
"sfs",
"sgg",
"sgx",
"slf",
"sls",
"sqk",
"sqs",
"ssp",
"ssr",
"svk",
"swl",
"syy",
"tse",
"tsm",
"tsq",
"tss",
"tsy",
"tza",
"ugn",
"ugy",
"ukl",
"uks",
"vgt",
"vsi",
"vsl",
"vsv",
"xki",
"xml",
"xms",
"ygs",
"ysl",
"zib",
"zsl",
}
--[==[ var:
List of languages where a hyphen is not considered a word separator for the `LANG multiword terms` category. There are
numerous reasons why languages are in this list; by each language should be listed the reason for inclusion.
]==]
data.hyphen_not_multiword_sep = list_to_set{
"akk", -- Akkadian; hyphens between syllables
"akl", -- Aklanon; hyphens for mid-word glottal stops
"ber-pro", -- Proto-Berber; morphemes separated by hyphens
"ceb", -- Cebuano; hyphens for mid-word glottal stops
"cnk", -- Khumi Chin; hyphens used in single words
"cpi", -- Chinese Pidgin English; Chinese-derived words with hyphens between syllables
"de", -- German; too many false positives
"esx-esk-pro", -- hyphen used to separate morphemes
"fi", -- Finnish; hyphen used to separate components in compound words if the final and initial vowels match, respectively
"gd", -- Scottish Gaelic; too many false positives like [[a-chianaibh]], [[a-nìos]], [[an-dè]] and other adverbs in a- and an-
"hil", -- Hiligaynon; hyphens for mid-word glottal stops
"hnn", -- Hanunoo; too many false positives
"ilo", -- Ilocano; hyphens for mid-word glottal stops
"kne", -- Kankanaey; hyphens for mid-word glottal stops
"lcp", -- Western Lawa; dash as syllable joiner
"lwl", -- Eastern Lawa; dash as syllable joiner
"mfa", -- Pattani Malay in Thai script; dash as syllable joiner
"mkh-vie-pro", -- Proto-Vietic; morphemes separated by hyphens
"msb", -- Masbatenyo; too many false positives
"tl", -- Tagalog; too many false positives
"war", -- Waray-Waray; too many false positives
"yo", -- Yoruba; hyphens used to show lengthened nasal vowels
}
--[==[ var:
List of languages that will not have `LANG masculine nouns` and similar categories added. Generally, these languages are
lacking gender but use the gender field for other purposes. (This is a massive hack and should be changed.)
]==]
data.no_gender_cat = list_to_set{
-- Languages without gender but which use the gender field for other purposes
"ja",
"th",
}
--[==[ var:
List of languages where [[Module:headword]] should not attempt to generate a transliteration even if the term is written
in a non-Latin script. FIXME: Notate reasons why each language is in this list.
]==]
data.notranslit = list_to_set{
"ams",
"az",
"bbc",
"bug",
"cdo",
"cia",
"cjm",
"cjy",
"cmn",
"cnp",
"cpi",
"cpx",
"csp",
"czh",
"czo",
"gan",
"hak",
"hnm",
"hsn",
"ja",
"kzg",
"lad",
"ltc",
"luh",
"lzh",
"mnp",
"ms",
"mul",
"mvi",
"nan",
"nan-dat",
"nan-hbl",
"nan-hlh",
"nan-lnx",
"nan-tws",
"nan-zhe",
"nan-zsh",
"och",
"oj",
"okn",
"ryn",
"rys",
"ryu",
"sh",
"sjc",
"tgt",
"th",
"tkn",
"tly",
"txg",
"und",
"vi",
"wuu",
"xug",
"yoi",
"yox",
"yue",
"za",
"zh",
"zhx-sic",
"zhx-tai",
}
--[==[ var:
List of languages that will default to `sccat` being true, i.e. categories like `LANG POS in SCRIPT script` will
automatically be generated. This can be overridden using {{para|sccat|0}} in {{tl|head}} or setting `sccat` to
`false` in Lua.
]==]
data.default_sccat = list_to_set{
"inc-apa",
"inc-ash",
"kfr",
"ks",
"mr",
"mwr",
"inc-oaw",
"inc-ohi",
"omr",
"inc-opa",
"phr",
"pi",
"pra",
"sa",
"skr",
"sd",
}
--[==[ var:
List of script codes for which a script-tagged display title will be added.
]==]
data.toBeTagged = list_to_set{
"Ahom",
"Arab",
"fa-Arab",
"glk-Arab",
"kk-Arab",
"ks-Arab",
"ku-Arab",
"mzn-Arab",
"ms-Arab",
"ota-Arab",
"pa-Arab",
"ps-Arab",
"sd-Arab",
"tt-Arab",
"ug-Arab",
"ur-Arab",
"Armi",
"Armn",
"Avst",
"Bali",
"Bamu",
"Batk",
"Beng",
"as-Beng",
"Bopo",
"Brah",
"Brai",
"Bugi",
"Buhd",
"Cakm",
"Cans",
"Cari",
"Cham",
"Cher",
"Copt",
"Cprt",
"Cyrl",
"Cyrs",
"Deva",
"Dsrt",
"Egyd",
"Egyp",
"Ethi",
"Geok",
"Geor",
"Glag",
"Goth",
"Grek",
"Polyt",
"polytonic",
"Gujr",
"Guru",
"Hang",
"Hani",
"Hano",
"Hebr",
"Hira",
"Hluw",
"Ital",
"Java",
"Kali",
"Kana",
"Khar",
"Khmr",
"Knda",
"Kthi",
"Lana",
"Laoo",
"Latn",
"Latf",
"Latg",
"Latnx",
"Latinx",
"pjt-Latn",
"Lepc",
"Limb",
"Linb",
"Lisu",
"Lyci",
"Lydi",
"Mand",
"Mani",
"Marc",
"Merc",
"Mero",
"Mlym",
"Mong",
"mnc-Mong",
"sjo-Mong",
"xwo-Mong",
"Mtei",
"Mymr",
"Narb",
"Nkoo",
"Nshu",
"Ogam",
"Olck",
"Orkh",
"Orya",
"Osma",
"Ougr",
"Palm",
"Phag",
"Phli",
"Phlv",
"Phnx",
"Plrd",
"Prti",
"Rjng",
"Runr",
"Samr",
"Sarb",
"Saur",
"Sgnw",
"Shaw",
"Shrd",
"Sinh",
"Sora",
"Sund",
"Sylo",
"Syrc",
"Tagb",
"Tale",
"Talu",
"Taml",
"Tang",
"Tavt",
"Telu",
"Tfng",
"Tglg",
"Thaa",
"Thai",
"Tibt",
"Ugar",
"Vaii",
"Xpeo",
"Xsux",
"Yiii",
"Zmth",
"Zsym",
"Ipach",
"Music",
"Rumin",
}
--[==[ var:
Parts of speech which will not be categorised in categories like `English terms spelled with É` if the term is the
character in question (e.g. the letter entry for English [[é]]). This contrasts with entries like the French adjective
[[m̂]], which is a one-letter word spelled with the letter.
]==]
data.pos_not_spelled_with_self = list_to_set{
"diacritical marks",
"Han characters",
"Han tu",
"hanja",
"hanzi",
"iteration marks",
"kana",
"kanji",
"letters",
"ligatures",
"logograms",
"morae",
"numeral symbols",
"numerals",
"punctuation marks",
"syllables",
"symbols",
}
------ 2. Lists not converted into sets. ------
--[==[ var:
Recognized aliases for parts of speech (param 2=). Key is the short form and value is the canonical singular (not
pluralized) form. It is singular so the same table can be used in [[Module:form of]] for the {{para|p}}/{{para|POS}}
param and [[Module:links]] for the pos= param. Note that any part of speech, abbreviated or not, can be suffixed with
`f` to generate the corresponding non-lemma form part of speech, such as `adjf`, `af` or `adjectivef` for
`adjective form`, and `nounf` or `nf` for `noun form`. This expansion happens even when it does not make sense for the
given part of speech (e.g. `pclf` expands to `particle form` and `symf` expands to `symbol form`), and currently also,
at least in [[Module:headword]] (but not [[Module:links]]), even if the part before the `f` is not a recognized part of
speech or abbreviation (hence `nerf` expands to `ner form`).
]==]
data.pos_aliases = {
a = "နာမဝိသေသန",
adj = "နာမဝိသေသန",
adv = "ကြိယာဝိသေသန",
art = "ပစ္စဲ",
det = "determiner",
cnum = "cardinal number",
conj = "သမ္ဗန္ဓ",
conv = "converb",
int = "အာမေဍိက်",
interj = "အာမေဍိက်",
intj = "အာမေဍိက်",
interjections = "အာမေဍိက်",
interjection = "အာမေဍိက်",
n = "နာမ်",
nf = "ဗီုပြၚ်နာမ်",
nounf = "ဗီုပြၚ်နာမ်",
num = "ဂၞန်သၚ်္ချာ",
part = "လုပ်ကၠောန်စွံလဝ်",
pcl = "လုပ်ကၠောန်စွံလဝ်",
phr = "ဝါကျ",
pn = "နာမ်မကိတ်ညဳ",
postp = "ကဆံၚ်အကာဲအရာ",
pre = "ဝိဘတ်",
prep = "ဝိဘတ်",
pro = "သဗ္ဗနာမ်",
pron = "သဗ္ဗနာမ်",
prop = "နာမ်မကိတ်ညဳ",
proper = "နာမ်မကိတ်ညဳ",
onum = "ordinal number",
rom = "ဗီုအက္ခရ်ရောမ",
romanization = "ဗီုအက္ခရ်ရောမ",
romanizations = "ဗီုအက္ခရ်ရောမ",
v = "ကြိယာ",
vb = "ကြိယာ",
vi = "intransitive verb",
vt = "transitive verb",
vti = "transitive and intransitive verb",
["diminutive noun"] = "နာမ်မလဟုတ်စှ်ေ",
["diminutive nouns"] = "နာမ်မလဟုတ်စှ်ေ",
noun = "နာမ်",
nouns = "နာမ်",
["noun form"] = "ဗီုပြၚ်နာမ်",
["noun forms"] = "ဗီုပြၚ်နာမ်",
verb = "ကြိယာ",
verbs = "ကြိယာ",
["verb form"] = "ဗီုပြၚ်ကြိယာ",
["verb forms"] = "ဗီုပြၚ်ကြိယာ",
verbf = "ဗီုပြၚ်ကြိယာ",
adjective = "နာမဝိသေသန",
adjectives = "နာမဝိသေသန",
adjf = "ဗီုပြၚ်နာမဝိသေသန",
adjectivef = "ဗီုပြၚ်နာမဝိသေသန",
["adjective form"] = "ဗီုပြၚ်နာမဝိသေသန",
["adjective forms"] = "ဗီုပြၚ်နာမဝိသေသန",
adverb = "ကြိယာဝိသေသန",
adverbs = "ကြိယာဝိသေသန",
["adverb form"] = "ဗီုပြၚ်ကြိယာဝိသေသန",
["adverb forms"] = "ဗီုပြၚ်ကြိယာဝိသေသန",
interjection = "အာမေဍိက်",
["interjection form"] = "ဗီုပြၚ်အာမေဍိက်",
["interjection forms"] = "ဗီုပြၚ်အာမေဍိက်",
pronoun = "သဗ္ဗနာမ်",
pronouns = "သဗ္ဗနာမ်",
["pronoun form"] = "ဗီုပြၚ်သဗ္ဗနာမ်",
["pronoun forms"] = "ဗီုပြၚ်သဗ္ဗနာမ်",
preposition = "ဝိဘတ်",
["preposition form"] = "ဗီုပြၚ်ဝိဘတ်",
["preposition forms"] = "ဗီုပြၚ်ဝိဘတ်",
suffix = "အဆက်လက္ကရဴ",
["suffix form"] = "ဗီုပြၚ်အဆက်လက္ကရဴ",
["suffix forms"] = "ဗီုပြၚ်အဆက်လက္ကရဴ",
phrase = "ဝါကျ",
["phrase form"] = "ဗီုပြၚ်ဝါကျ",
["phrase forms"] = "ဗီုပြၚ်ဝါကျ",
numeral = "ဂၞန်သၚ်္ချာ",
numerals = "ဂၞန်သၚ်္ချာ",
["numeral form"] = "ဗီုပြၚ်ဂၞန်သၚ်္ချာ",
["numeral forms"] = "ဗီုပြၚ်ဂၞန်သၚ်္ချာ",
proverb = "ပေါရာဏာံပေါရာဒါံ",
conjunction = "သမ္ဗန္ဓ",
conjunctions = "သမ္ဗန္ဓ",
["conjunction form"] = "ဗီုပြၚ်သမ္ဗန္ဓ",
["conjunction forms"] = "ဗီုပြၚ်သမ္ဗန္ဓ",
contraction = "ပွမထညောံ",
["contraction form"] = "ဗီုပြၚ်ပွမထညောံ",
["contraction forms"] = "ဗီုပြၚ်ပွမထညောံ",
article = "ပစ္စဲ",
articles = "ပစ္စဲ",
["article form"] = "ဗီုပြၚ်ပစ္စဲ",
["article forms"] = "ဗီုပြၚ်ပစ္စဲ",
root = "တံရိုဟ်",
["root form"] = "ဗီုပြၚ်တံရိုဟ်",
["root forms"] = "ဗီုပြၚ်တံရိုဟ်",
prefix = "အဆက်ဂတ",
["prefix form"] = "ဗီုပြၚ်အဆက်ဂတ",
["prefix forms"] = "ဗီုပြၚ်အဆက်ဂတ",
particle = "ကၞာတ်အမှိက်",
classifier = "နာမ်ပါ်ကၞာတ်",
determiner = "ဖျေံလဝ်သန္နိဋ္ဌာန်",
determiners = "ဖျေံလဝ်သန္နိဋ္ဌာန်",
["mutated noun"] = "နာမ်အပြံၚ်အသၠာဲ",
["mutated verb"] = "ကြိယာအပြံၚ်အသၠာဲ",
["determiner form"] = "ဗီုပြၚ်ဖျေံလဝ်သန္နိဋ္ဌာန်",
["determiner forms"] = "ဗီုပြၚ်ဖျေံလဝ်သန္နိဋ္ဌာန်",
["reconstructed determiner"] = "ဖျေံလဝ်သန္နိဋ္ဌာန်နကဵုဗီုပြၚ်သိုၚ်တၟိ",
["reconstructed verb"] = "ကြိယာဗီုပြၚ်သိုၚ်တၟိ",
["reconstructed noun"] = "နာမ်ဗီုပြၚ်သိုၚ်တၟိ",
counter = "ရိုဟ်သၠုဲ",
postposition = "ကဆံၚ်",
misspelling = "ခ္ဍံက်လိက်ဗၠေတ်",
final = "အဆံၚ်လက္ကရဴ",
["verb finals"] = "ကြိယာအဆံၚ်လက္ကရဴ",
["transitive inanimate verb finals"] = "ကြိယာအပြံၚ်အလှာဲအဆံၚ်လက္ကရဴမသ္ကုဟၟဲကဵုလမျီုနကဵုဘာသာ",
affix = "အဆက်စုတ်လက္ကရဴ",
["affix form"] = "ဗီုပြၚ်အဆက်စုတ်လက္ကရဴ",
["affix forms"] = "ဗီုပြၚ်အဆက်စုတ်လက္ကရဴ",
["diacritical mark"] = "ခရက်သမ္တီလဝ်ဓမံက်ထ္ၜးရမျာၚ်",
postposition = "ကဆံၚ်အကာဲအရာ",
["postposition form"] = "ဗီုပြၚ်ကဆံၚ်အကာဲအရာ",
symbol = "သၚ်္ကေတ",
symbols = "သၚ်္ကေတ",
infix = "စန်",
letter = "အက္ခရ်",
hanzi = "ဟာန်သဳ",
["prepositional phrase"] = "ဝါကျဝိဘတ်",
["prepositional pronoun"] = "သဗ္ဗနာမ်ဝိဘတ်",
stem = "တံမအရေဝ်",
participle = "လုပ်ကၠောန်စွံလဝ်",
participles = "လုပ်ကၠောန်စွံလဝ်",
["past participle"] = "လုပ်ကၠောန်စွံလဝ်နကဵုအတိက်",
["past participle form"] = "ဗီုပြၚ်လုပ်ကၠောန်စွံလဝ်နကဵုအတိက်",
["proper noun"] = "နာမ်မကိတ်ညဳ",
["proper noun form"] = "ဗီုပြၚ်နာမ်မကိတ်ညဳ",
["participle form"] = "ဗီုပြၚ်လုပ်ကၠောန်စွံလဝ်",
["comparative adjective"] = "နာမဝိသေသနပတုပ်ရံၚ်",
["superlative adjective"] = "သဒ္ဒာနာမဝိသေသန",
["numeral symbol"] = "သၚ်္ကေတဂၞန်သၚ်္ချာ",
["particle forms"] = "ဗီုပြၚ်ကၞာတ်အမှိက်",
["particle form"] = "ဗီုပြၚ်ကၞာတ်အမှိက်",
["present participles"] = "လုပ်ကၠောန်စွံလဝ်ပစ္စုပ္ပန်",
["present participle"] = "လုပ်ကၠောန်စွံလဝ်ပစ္စုပ္ပန်",
["alternative form"] = "ဗီုပြၚ်တၞဟ်ခြာ",
singulative = "ကိုန်ဨကဝုစ်",
singulatives = "ကိုန်ဨကဝုစ်",
gerund = "ဗီုပြၚ်ကြိယာမဒှ်နာမ်",
gerunds = "ဗီုပြၚ်ကြိယာမဒှ်နာမ်",
}
--[==[ var:
Map of parts of speech for which categories like `German masculine nouns` or `Russian imperfective verbs` will be
generated if the headword is of the appropriate gender/number. The map is used to canonicalize parts of speech for
categorization purposes; specifically, proper nouns categorizes like nouns.
]==]
data.pos_for_gender_number_cat = {
["နာမ်"] = "နာမ်",
["နာမ်မကိတ်ညဳ"] = "နာမ်",
["အဆက်လက္ကရဴ"] = "အဆက်လက္ကရဴ",
-- We include verbs because impf and pf are valid "genders".
["ကြိယာ"] = "ကြိယာ",
}
--[==[ var:
Lower limit for a "long" word in a particular language. Used to categorize terms into e.g.
[[:Category:Long English words]] automatically. Languages with no mapping here do not get categorized.
]==]
data.long_word_thresholds = {
["af"] = 20,
["bg"] = 20,
["cy"] = 25,
["de"] = 20,
["en"] = 25,
["es"] = 20,
["fr"] = 20,
["ka"] = 20,
["sv"] = 20,
["tl"] = 25,
}
------ 3. Page-wide processing (so that it only needs to be done once per page). ------
data.page = require(headword_page_module).process_page()
-- Set some page properties directly on `data` for ease of use.
data.pagename = data.page.pagename
data.encoded_pagename = data.page.encoded_pagename
return data
rasiinok2kqrclvo5hptyjkwqt0lage
မဳဒဳယာဝဳကဳ:Common.css
8
2085
396505
11994
2026-06-07T14:37:12Z
咽頭べさ
33
396505
css
text/css
/* CSS placed here will be applied to all skins */
/*This CSS will help to automatically replace a user's wrong using character with the correct one from င် to ၚ် */
/*body
{
<input type=”ၚ်” placeholder=”replaced” id=”ၚ်” onkeyup=”submit()”/>
}*/
pdtg1or7ikwf9gm0rj5vbq91lc1crhs
မဳဒဳယာဝဳကဳ:Common.js
8
2087
396500
11042
2026-06-07T14:04:19Z
咽頭べさ
33
396500
javascript
text/javascript
/**
* Keep code in MediaWiki:Common.js to a minimum as it is unconditionally
* loaded for all users on every wiki page. If possible create a gadget that is
* enabled by default instead of adding it here (since gadgets are fully
* optimized ResourceLoader modules with possibility to add dependencies etc.)
*
* Since Common.js isn't a gadget, there is no place to declare its
* dependencies, so we have to lazy load them with mw.loader.using on demand and
* then execute the rest in the callback. In most cases these dependencies will
* be loaded (or loading) already and the callback will not be delayed. In case a
* dependency hasn't arrived yet it'll make sure those are loaded before this.
*/
"use strict";
// [[Category:Wiktionary scripts]] <nowiki>
/*jshint shadow:true, undef:true, latedef:true, unused:true, esversion:3 */
/*global window, jQuery, mw, importScript, importStylesheet, $ */
/** [[WT:PREFS]] v2.0 **/
// Note: copied into Mobile.js - please keep in sync
try {
(function () {
var prefs;
try {
prefs = window.localStorage.getItem("AGprefs");
} catch (e) {
prefs = jQuery.cookie("AGprefs");
}
prefs = prefs && JSON.parse(prefs);
if (mw.config.get("wgUserGroups").indexOf("autoconfirmed") !== -1) return;
if (mw.config.get("wgUserGroups").indexOf("user") === -1) {
// XXX: [[Wiktionary:Preferences/V2]] is just a temporary page
mw.loader.using(["mediawiki.util"], function () {
mw.util.addPortletLink(
mw.config.get("skin") === "vector-2022"
? "p-vector-user-menu-overflow"
: "p-personal",
mw.util.getUrl("Wiktionary:Preferences for users without an account"),
"Preferences",
"pt-agprefs",
"Personalise Wiktionary (settings are kept per-browser).",
"",
document.getElementById("pt-createaccount-2") ||
document.getElementById("pt-createaccount")
);
});
if (
mw.config.get("wgAction") === "ဗီုပြၚ်လညာတ်" &&
mw.config.get("wgPageName") ===
"Wiktionary:Preferences_for_users_without_an_account"
) {
mw.loader.load("ext.gadget.AGprefs"); // [[MediaWiki:Gadget-AGprefs.js]]
}
}
if (!prefs) return;
mw.loader.state(
"wiktionary_this_gadget_has_been_disabled_by_the_user",
"missing"
);
for (var key in prefs.modules) {
if (prefs.modules[key]) {
mw.loader.load([key]);
} else {
// unavoidable race condition. to prevent it, every enabled-by-default gadget should have "site" as a dependency
if (mw.loader.getState(key) !== "ready") {
mw.loader.moduleRegistry[key].dependencies.push(
"wiktionary_this_gadget_has_been_disabled_by_the_user"
);
mw.loader.state(key, "missing");
} else {
// XXX
mw.log.warn(
key +
" could not be disabled; make sure it has 'site' declared as a dependency"
);
}
}
}
for (var key in prefs.sheets) {
importStylesheet("MediaWiki:Gadget-" + key);
}
for (var key in prefs.scripts) {
importScript("MediaWiki:Gadget-" + key);
}
if (mw.config.get("wgUserGroups").indexOf("ညးလွပ်") !== -1)
mw.loader.using(["mediawiki.api"], function () {
var changes = [];
for (var key in prefs.gadgets)
changes.push(
"gadget-" + key + "=" + (prefs.gadgets[key] ? "1" : "0")
);
new mw.Api()
.postWithToken("options", {
action: "options",
change: changes.join("|")
})
.then(function () {
jQuery.cookie("AGprefs", null);
try {
window.localStorage.removeItem("AGprefs");
} catch (e) {
/* */
}
mw.notify(
jQuery(
'<b>Your <a href="/wiki/Wiktionary:Preferences/V2">per-browser preferences</a> have been migrated</b><br/><br/>' +
'From now on, you should use your <a href="/wiki/Special:Preferences">user preferences page</a>. ' +
"Preferences will no longer apply after you log out."
)
);
});
});
})();
} catch (e) {
mw.log.warn(e);
}
mw.loader.using("mediawiki.util").done(function () {
/** &withmodule= query parameter **/
if (mw.util.getParamValue("withmodule"))
mw.loader.load(mw.util.getParamValue("withmodule").split(","));
/** &preloadtext= and &preloadminor= **/
if (mw.config.get("wgAction") === "ပလေဝ်ဒါန်")
jQuery(document).ready(function () {
var wpTextbox1 = document.getElementById("wpTextbox1");
var wpMinoredit = document.getElementById("wpMinoredit");
if (!wpTextbox1) return;
var preloadtext = mw.util.getParamValue("preloadtext");
var preloadminor = mw.util.getParamValue("preloadminor");
if (preloadtext && !wpTextbox1.value) wpTextbox1.value = preloadtext;
if (preloadminor !== null && wpMinoredit)
wpMinoredit.checked = !/^(0|false|no|)$/i.test(preloadminor);
});
});
// == "Did you mean" auto redirect ==
/**
* This will redirect in 3 seconds if a link enclosed in id="did-you-mean"
* is found, and add the text "Auto-redirected from X" under the top header
* if a rdfrom is passed in the get parameters.
* Pages with wynn ([[ƿ]]) will be redirected immediately.
**/
$.when(mw.loader.using(["ညးလွပ်", "mediawiki.util"]), $.ready).done(function () {
if (window.disableAutoRedirect) return;
var rdFromValue = mw.util.getParamValue("rdfrom");
if (rdFromValue) {
$("#siteSub").after(
$("<div>")
.attr("id", "contentSub")
.append(document.createTextNode("(Auto-redirected from "))
.append(
$("<a>", {
href: mw.util.getUrl(rdFromValue, { redirect: "no" }),
addClass: "new"
}).text(rdFromValue)
)
.append(document.createTextNode(")"))
);
} else {
// Redirect as quickly as possible from [[ƿ]] title to [[w]] title.
var pageTitle = mw.config.get("wgTitle");
var isWynnTitle = /ƿ/i.test(pageTitle);
var didYouMean = $("#did-you-mean a").html();
var target = didYouMean
? didYouMean
: isWynnTitle
? $("#go-to-search-page a").html()
: null;
var timeout = isWynnTitle ? 0 : 3000;
window.setTimeout(function () {
var canRedirect = mw.util.getParamValue("redirect") != "no";
var action = mw.config.get("wgAction");
if (
target &&
target !== pageTitle &&
canRedirect &&
!window.disableAutoRedirect &&
(action == "ဗီုပြၚ်လညာတ်" || (isWynnTitle && action == "ပလေဝ်ဒါန်")) &&
mw.config.get("wgArticleId") === 0 &&
mw.config.get("wgNamespaceNumber") === 0 &&
!/Redirected from/.test(jQuery("#contentSub").html())
) {
window.location = mw.util.getUrl(target, { rdfrom: pageTitle });
}
}, timeout);
}
});
/* == [[WT:FEED]] == */
// used to be [[User:Conrad.Irwin/feedback.js]]
$.when(mw.loader.using("mediawiki.util"), $.ready).done(function () {
var fb_comment_url = mw.util.getUrl("Wiktionary:Feedback", {
action: "edit",
section: "new",
preload: "Wiktionary:Feedback/preload",
editintro: "Wiktionary:Feedback/intro",
preloadtitle: "[[:" + mw.config.get("wgPageName") + "]]"
});
var fb_comment = "If you have time, leave us a note.";
if (mw.config.get("skin") === "vector-2022") {
var vectorMenuContent = $("<a>")
.attr("href", fb_comment_url)
.text(fb_comment)
.appendTo($("<li>").addClass("mw-list-item"))
.parent()
.appendTo($("<ul>").addClass("vector-menu-content-list"))
.parent()
.appendTo($("<div>").addClass("vector-menu-content"))
.parent();
var vectorMenuHeading = $("<div>")
.addClass("vector-menu-heading")
.text("Feedback");
var vectorMenu = $("<div>")
.addClass("vector-menu mw-portlet mw-portlet-navigation")
.attr("id", "p-feedback")
.append(vectorMenuHeading)
.append(vectorMenuContent);
vectorMenu.appendTo($("#vector-main-menu"));
} else {
$("<a>")
.attr("href", fb_comment_url)
.text(fb_comment)
.appendTo($("<p>").css("font-size", "80%"))
.parent()
.appendTo($("<div>").addClass("body"))
.parent()
.before($("<h3>Feedback</h3>"))
.appendTo($("<div>").addClass("portal expanded").attr("id", "p-feedback"))
.parent()
.appendTo($("#mw-panel"));
}
});
// Various fixes for the trees generated by [[Module:family tree]].
$(function () {
// Show the top toggle.
$(".familytree-toptoggle").css("display", "");
// Initialize the text of the toggles.
$(".familytree")
.get()
.forEach(function (tree) {
var isCollapsed = tree.classList.contains("mw-collapsed");
var toggles = $(tree).find(".familytree-toggle");
if (toggles[0]) {
var customToggleClass = Array.prototype.filter.call(
toggles[0].classList,
function (className) {
return className.indexOf("mw-customtoggle") === 0;
}
)[0];
if (customToggleClass) {
var toggledElement = $(
"#" +
customToggleClass.replace(
"mw-customtoggle",
"mw-customcollapsible"
)
);
if (toggledElement) {
toggles.text(
toggledElement.data(isCollapsed ? "expandtext" : "collapsetext")
);
}
}
}
});
// Change the text in the custom toggles generated by [[Module:family tree]]
// when they are clicked.
$(".mw-collapsible").on(
"beforeExpand.mw-collapsible beforeCollapse.mw-collapsible",
function (event) {
if (this.id.indexOf("mw-customcollapsible") === 0) {
var toggle = $(
"." + this.id.replace("mw-customcollapsible", "mw-customtoggle")
);
if (event.type === "beforeExpand") {
toggle.text(this.dataset.collapsetext);
} else {
// beforeCollapse
toggle.text(this.dataset.expandtext);
}
event.stopPropagation();
}
}
);
});
// </nowiki>
lgzfa5n74xszfr03lytrc81yq9xprkx
မဳဒဳယာဝဳကဳ:Vector.css
8
2088
396502
84292
2026-06-07T14:28:32Z
咽頭べさ
33
396502
css
text/css
/* CSS placed here will affect users of the Vector (legacy) skin. */
/*<nowiki>*/
/* Move the top portlet links 0.5 em upwards. */
body.skin-vector-legacy #left-navigation {margin-top: 2em;}
body.skin-vector-legacy #right-navigation {margin-top: 2em;}
body.skin-vector-legacy #mw-head-base {height: 4.5em;}
/* [[Wiktionary:Beer_parlour/2024/September#Heading level 4 appearance]]:
* h3 is 120% 1.2em, and h5 is 100%. make h4 110% = 1.1em.
* increase h3 to 125% to compensate, since otherwise h3 and h4 are too similar. */
@media screen {
.vector-body .mw-heading3, .vector-body h3 {
font-size: 1.25em;
}
.vector-body .mw-heading4, .vector-body h4 {
font-size: 1.1em;
}
}
/*</nowiki>*/
cvin5belj2wa8vwzsvrxyvvywyxmxxs
မဳဒဳယာဝဳကဳ:Monobook.css
8
2089
396499
6023
2026-06-07T13:50:53Z
咽頭べさ
33
396499
css
text/css
/* See also [[Special:Mypage/monobook.css]], [[MediaWiki:Common.css]], [[Wiktionary:Customizing your monobook]] */
/* This page should be for monobook skin only, use [[MediaWiki:Common.css]] for general content styles. */
.ns-0 #ca-talk, .ns-1 #ca-talk, .ns-114 #ca-talk, .ns-10 #ca-talk, .ns-11 #ca-talk {
margin-right: 0.3em;
}
.ns-0 #ca-nstab-citations, .ns-1 #ca-nstab-citations, .ns-114 #ca-nstab-citations, .ns-10 #ca-nstab-documentation, .ns-11 #ca-nstab-documentation {
margin-right: 1.6em;
}
/* Remove bullets from the (by language) sections of the sidebar */
li#n-recentchangesbylanguage, li#n-randompagebylanguage {
list-style: none;
display: inline;
}
/* small margin between "See also" and TOC */
.disambig-see-also,
.disambig-see-also-2 {
margin-bottom:0.3em;
}
/* display redirects in Special:Allpages in black */
.allpagesredirect a {
color:#000;
}
.allpagesredirect a:visited {
color:#000;
}
/* subpage link bigger, clearer */
.subpages {
font-size:127%;
margin-top:0.5em;
margin-bottom:0.5em;
}
139ug1otrthocw00dug8snldb0x4iwe
မဳဒဳယာဝဳကဳ:Gadget-EditTools.css
8
2105
396498
6099
2026-06-07T13:49:22Z
咽頭べさ
33
396498
css
text/css
/* CSS borrowed from Wikipedia's [[w:MediaWiki:Gadget-charinsert-styles.css]]. */
.charinserter {
/*background-color: #f9f9f9;*/
/*border: 1px solid #ddd;*/
padding: 1px 4px;
}
bksa5383zgxhkcuw7l53pphz0jvw3mm
မဳဒဳယာဝဳကဳ:Gadget-Edittools.js
8
2106
396582
6100
2026-06-07T20:14:54Z
咽頭べさ
33
396582
javascript
text/javascript
// <nowiki>
// implicit dependencies: mediawiki.cookie, jquery.textselection
(function charInsertIIFE () {
"use strict";
function CharInsertCookieLegacy(key) {
this.key = key;
}
CharInsertCookieLegacy.prototype = {
get: function () {
return parseInt(mw.cookie.get(this.key), 10) || 0;
},
set: function (value) {
if (typeof value === 'number')
value = value.toString(10);
if (!(typeof value === 'string' && !isNaN(parseInt(value, 10))))
throw new TypeError("Expected string or number");
return mw.cookie.set(this.key, value);
},
};
function CharInsertCookie(key) {
this.key = key;
}
CharInsertCookie.prototype = {
get: function () {
return mw.cookie.get(this.key);
},
set: function (value) {
if (typeof value !== 'string')
throw new TypeError("Expected string");
return mw.cookie.set(this.key, value);
},
};
var charInsertCookieLegacy = new CharInsertCookieLegacy('edittoolscharsubset');
var charInsertCookie = new CharInsertCookie('edittoolscharsubsetname');
/* ===applyCharinserts=== */
/* handle <span class="charinsert"> like <charinsert> */
function applyCharinserts() {
var textbox = $('#wpTextbox1');
textbox.encapsulate = function (left, right) {
return this.textSelection(
'encapsulateSelection', {
pre: left.replace(/^ */, '')
.replace(/\u0640(.)/g, '$1'), // remove ARABIC TATWEEL before Arabic diacritic
peri: '',
post: right
}
);
};
// Must set insertArgs in the element for which this is an event handler.
function clickFunction () {
textbox.encapsulate.apply(textbox, this.insertArgs);
return false;
}
function getInsertArg(string, start, end) {
string = string.substring(start, end);
return string !== ''
? string.replace(/\x22/g, '"')
: string;
}
function makeInsertArgs(string) {
var index = string.indexOf('+');
if (index === -1)
index = string.length;
var left = getInsertArg(string, 0, index);
var right = getInsertArg(string, index + 1);
return [ left, right ];
}
function makeCharInserter(string) {
var charInserter = document.createElement('a');
charInserter.onclick = clickFunction;
charInserter.href = '#';
charInserter.classList.add("charinserter");
var insertArgs = makeInsertArgs(string);
charInserter.insertArgs = insertArgs.map(function(arg) {
return arg
// No-break space ( ) must become an ASCII space in the inserted
// text, but it must be displayed as a no-break space so that combining
// characters are clickable.
.replace(/\xA0/g, ' ')
// Dotted circle can be used as a seat for diacritics
// but shouldn't be inserted into the text box.
.replace(/^\u25CC(.)/, '$1');
});
var visibleText = insertArgs.join('');
charInserter.appendChild(document.createTextNode(visibleText));
return charInserter;
}
function charInsertify(parent) {
if (parent.charInsertified)
return;
parent.charInsertified = true;
// Go through all child nodes of parent.
for (var childNode = parent.firstChild;
childNode !== null;
childNode = childNode.nextSibling) {
if (childNode.nodeType === 1) { // Element node
charInsertify(childNode);
} else if (childNode.nodeType === 3) { // Text node
// There is a newline when the wikitext has a line break
// followed by a character entity reference such as  .
// Remove the newline because we don't want to see multiple spaces.
childNode.nodeValue = childNode.nodeValue
.replace(/^\n+/, '');
var strings = childNode.nodeValue
// Split text content on ASCII whitespace characters
// and en space (U+2002) character references.
.split(/[ \f\n\r\t\v\u2002]+/g)
.filter(function(string) { return string !== ''; });
var addedNew = false;
for (var i = 0; i < strings.length; ++i) {
if (i > 0)
parent.insertBefore(document.createTextNode(' '), childNode);
parent.insertBefore(makeCharInserter(strings[i]), childNode);
addedNew = true;
}
if (addedNew)
parent.removeChild(childNode);
}
}
}
var charInsertSpans = document.querySelectorAll('#editpage-specialchars .charinsert');
Array.prototype.forEach.call(charInsertSpans, charInsertify);
}
function makeCharSubsetMenuText(slug) {
return decodeURIComponent(slug
.replace(/\.([0-9A-F][0-9A-F])/g, '%$1')
.replace(/_/g, '%20'));
}
/* ===addCharSubsetMenu=== */
/* add menu for selecting subsets of secial characters */
function addCharSubsetMenu() {
var edittools = $('#editpage-specialchars');
if (edittools.length === 0) return;
var menu = $('<select>')
.attr("id", 'charSubsetControl').css("display", "inline")
.on("change", function() {
chooseCharSubset($(this).val());
});
var sections = edittools.find('p');
if (sections.length === 0) return;
sections.each(function() {
var slug = ($(this).attr("id") || '').replace(/^edittools-/, '');
$('<option>').text(makeCharSubsetMenuText(slug)).val(slug).appendTo(menu);
});
/* default subset from cookie */
var sectionIndex = charInsertCookieLegacy.get();
var sectionName = charInsertCookie.get();
if (!sectionName) {
sectionName = menu.children()[sectionIndex].value;
}
/* update dropdown control to value of cookie */
menu.val(sectionName);
/* display the subset indicated by the cookie */
chooseCharSubset(sectionName);
edittools.prepend(menu);
}
/* ===chooseCharSubsetMenu=== */
/* select subsection of special characters */
function chooseCharSubset(sectionSlug) {
var sections = $('#editpage-specialchars').find('p');
var idToShow = 'edittools-' + sectionSlug;
for (var i = 0; i < sections.length; i++) {
var style = sections[i].style;
style.display = sections[i].id == idToShow ? 'inline' : 'none';
}
charInsertCookie.set(sectionSlug);
}
$(function() {
var action = mw.config.get('wgAction');
if (window.testNewEditJs || !(action === 'edit' || action === 'submit'
|| $('#editpage-specialchars').length > 0))
return;
if (!window.doNotUseDefaultEditTools) // [[User:Conrad.Irwin/edittools.js]]
addCharSubsetMenu();
applyCharinserts();
// Provide three arguments to add one category;
// provide an array with [{name: categoryName, insertBefore: categoryInsertBefore, html: categoryHtml}]
// to add more than one.
mw.hook('enwiktionary.edittools.addCategory').add(function (categoryName, categoryInsertBefore, categoryHtml) {
var categories = (categoryInsertBefore !== undefined)
? [{name: categoryName, insertBefore: categoryInsertBefore, html: categoryHtml}]
: categoryName;
var edittools = $('#editpage-specialchars');
if (edittools.length === 0) return;
var menu = edittools.find('#charSubsetControl');
if (menu.length === 0) return;
for (var i = 0; i < categories.length; i++) {
var category = categories[i];
var name = category.name,
insertBefore = category.insertBefore,
html = category.html;
name = name.replace(/\s/g, '_');
var selector = insertBefore ? '#charSubsetControl option[value="' + insertBefore.replace('"', '\\"') + '"]' : null;
var option = $('<option>').text(makeCharSubsetMenuText(name)).val(name);
$('<p>').attr('id', 'edittools-' + name).addClass('speciallang').css('display', 'none').html(html).appendTo(edittools);
if (selector && menu.find(selector)) {
option.insertBefore(selector);
} else {
option.appendTo(menu);
}
}
applyCharinserts();
var cookieVal = charInsertCookie.get();
menu.val(cookieVal);
chooseCharSubset(cookieVal);
});
});
})();
// </nowiki>
av4h0w783rxittyviie2wjvdsukc936
မဳဒဳယာဝဳကဳ:Gadget-Edittools
8
2107
396503
6101
2026-06-07T14:34:26Z
咽頭べさ
33
396503
wikitext
text/x-wiki
ပံက်ရပ်စပ်မဳနူအက္ခရ်ဆေၚ်စပ်ကဵုကဠာပလေဝ်ဒါန် (ဗဵုရံၚ် [[MediaWiki:Edittools]]) {{gadget tag|မစပံက်ရံၚ်ကၠာ၊ မတၟာတ်ထောံနကဵုဒှ်အန္တရာဲမာန်}}
f7f4pmkp32q8s5hxf2xa5ryinzo6933
မဳဒဳယာဝဳကဳ:Gadgets-definition
8
2108
396497
6103
2026-06-07T13:47:32Z
咽頭べさ
33
396497
wikitext
text/x-wiki
{{shortcut|WT:GD}}
See [[Special:Gadgets]] for documentation of these gadgets, and [[Wiktionary:Gadgets]] for general information about gadgets on Wiktionary. See also [[WT:PREFS]] for other preferences.
{{gadgets pages}}
== appearance ==
* MonobookSmallerHeadings[ResourceLoader|default|skins=monobook]|MonobookSmallerHeadings.css
* LanguagesAndScripts[ResourceLoader|default]|LanguagesAndScripts.css
* VectorClassic[ResourceLoader|type=styles|skins=vector]|VectorClassic.css
* CatchMyAttention[ResourceLoader|type=styles]|CatchMyAttention.css
* HideLogo[ResourceLoader|type=styles]|HideLogo.css
* vectorTabs[ResourceLoader|skins=vector|type=styles]|vectorTabs.js|vectorTabs.css
* WiktCountryFlags[ResourceLoader|type=styles|namespaces=0]|WiktCountryFlags.css
* WiktPreviewRightTOCs[ResourceLoader|namespaces=0]|WiktPreviewRightTOCs.css
* LogoTiles[ResourceLoader|type=styles]|LogoTiles.css
* RoundBullets4Lists[ResourceLoader|type=styles]|RoundBullets4Lists.css
* BC-AD[ResourceLoader]|BC-AD.js
* AutoContrastFixer[ResourceLoader|skins=vector-2022,minerva|dependencies=ext.gadget.WiktGadgetPrefs]|AutoContrastFixer.js
* ShowIDs[ResourceLoader|dependencies=mediawiki.util|peers=ShowIDs-pagestyles]|ShowIDs.js
* TreeDescs[ResourceLoader|peers=TreeDescs-pagestyles]|TreeDescs.js
* NoMiniToc[ResourceLoader|type=general]|NoMiniToc.js|NoMiniToc.css
* NsfwOverride[ResourceLoader|dependencies=mediawiki.util]|NsfwOverride.js|NsfwOverride.css
* MobileCollapseCategories[ResourceLoader|skins=minerva|dependencies=mediawiki.util,ext.gadget.VisibilityToggles]|MobileCollapseCategories.js
== interface-gadgets ==
* TargetedTranslations[ResourceLoader|default|dependencies=site,mediawiki.storage,mediawiki.cookie,ext.gadget.StorageUtils|categories=Entries with translation boxes]|TargetedTranslations.js|TargetedTranslations.css
* BlockInfo[ResourceLoader|default|dependencies=site,mediawiki.api,mediawiki.language.months,mediawiki.util|namespaces=-1,2,3]|BlockInfo.js
* RevdelInfo[ResourceLoader|dependencies=site,mediawiki.api,mediawiki.language.months,mediawiki.util]|RevdelInfo.js
* CodeLinks[ResourceLoader|dependencies=site,mediawiki.util]|CodeLinks.js
* purgetab[ResourceLoader|dependencies=mediawiki.api,mediawiki.util]|purgetab.js
* Navigation_popups[ResourceLoader]|popups.js
* PagePreviews[ResourceLoader|default|dependencies=site,mediawiki.api,mediawiki.util,mediawiki.Title|skins=vector,vector-2022,monobook,timeless,modern,cologneblue]|PagePreviews.js
* CommentsInLocalTime[ResourceLoader]|CommentsInLocalTime.js
* UTCLiveClock[ResourceLoader|type=general|dependencies=mediawiki.util,mediawiki.api|peers=UTCLiveClock-pagestyles]|UTCLiveClock.js|UTCLiveClock.css
* WiktSidebarTranslation[ResourceLoader|dependencies=ext.uls.interlanguage]|WiktSidebarTranslation.js
* searchFocus[ResourceLoader]|searchFocus.js
* TabbedLanguages[ResourceLoader|dependencies=mediawiki.cookie,mediawiki.util|type=general|skins=vector,vector-2022,monobook,timeless,modern,cologneblue]|TabbedLanguages.js|TabbedLanguages.css
* FastRollback[ResourceLoader|rights=rollback|dependencies=mediawiki.api]|FastRollback.js
* AudioBotBlacklist[ResourceLoader|default|dependencies=mediawiki.api,oojs-ui-core,oojs-ui-widgets,oojs-ui-windows,ext.gadget.WiktGadgetPrefs|rights=autoconfirmed]| AudioBotBlacklist.js
* OrangeLinks[ResourceLoader|dependencies=mediawiki.api,mediawiki.Title,mediawiki.util]|OrangeLinks.js
* U2693[ResourceLoader|dependencies=mediawiki.Title,mediawiki.util|skins=vector,vector-2022,monobook,timeless,modern,cologneblue]|U2693.js
* AggregateInterprojectLinks[ResourceLoader|dependencies=mediawiki.cookie]|AggregateInterprojectLinks.js
* MoreMenu-local[ResourceLoader|dependencies=mediawiki.api,mediawiki.util,user.options]|MoreMenu-local.js
* nearby[ResourceLoader|dependencies=mediawiki.util,mediawiki.api,ext.gadget.UnsupportedTitles|namespaces=0,100,118]|nearby.js
* linkLanguageHeaders[ResourceLoader|dependencies=mediawiki.api|namespaces=0,118]|linkLanguageHeaders.js
* ReferenceTooltips[ResourceLoader|default|type=general|dependencies=mediawiki.cookie,jquery.client,site]|ReferenceTooltips.js
* categoryTreeLanguageNames[ResourceLoader|dependencies=mediawiki.api,mediawiki.util|namespaces=0,14,100,118]|categoryTreeLanguageNames.js
* Streamline[ResourceLoader|dependencies=mediawiki.api,mediawiki.util,ext.gadget.WiktGadgetPrefs,ext.gadget.defaultVisibilityToggles|namespaces=0,4,118]|Streamline.js
* minitoc[ResourceLoader|default|dependencies=site|categories=Pages calling Template:minitoc,Mammoth pages]|minitoc.js
* EtytreeZoom[ResourceLoader|default|dependencies=site,mediawiki.util|categories=Pages with etymology trees]|EtytreeZoom.js
* CategoryJumpTo[ResourceLoader|dependencies=mediawiki.api,mediawiki.notification,mediawiki.util,oojs-ui-core|namespaces=14]|CategoryJumpTo.js
* Anchor[ResourceLoader|dependencies=mediawiki.notification,mediawiki.util,ext.gadget.WiktGadgetPrefs]|Anchor.js
* JumpToError[ResourceLoader|dependencies=mediawiki.notification]|JumpToError.js
* VetVoters[ResourceLoader|dependencies=mediawiki.util,mediawiki.api|namespaces=4,5]|VetVoters.js
* VisibilityToggles[ResourceLoader|dependencies=user,mediawiki.cookie]|VisibilityToggles.js
* defaultVisibilityToggles[ResourceLoader|default|dependencies=ext.gadget.VisibilityToggles]|defaultVisibilityToggles.js
== editing-gadgets ==
* TranslationAdder[ResourceLoader|default|dependencies=site,ext.gadget.Editor,mediawiki.cookie,ext.gadget.LanguageUtils,mediawiki.util,ext.gadget.TranslationAdder-Data|categories=Entries with translation boxes]|TranslationAdder.js
* RhymesAdder[ResourceLoader|default|dependencies=site,ext.gadget.Editor,mediawiki.util,ext.gadget.LanguageUtils|namespaces=106]|RhymesAdder.js
* PatrollingEnhancements[ResourceLoader|default|dependencies=site|rights=patrol]|PatrollingEnhancements.js
* RegexMenuFramework[ResourceLoader]|RegexMenuFramework.js
* AcceleratedFormCreation[ResourceLoader|dependencies=mediawiki.util]|AcceleratedFormCreation.css|AcceleratedFormCreation.js
* HotCat[ResourceLoader]|HotCat.js
* FastRevert[ResourceLoader|dependencies=mediawiki.util]|FastRevert.js
* ChangesByLanguage[ResourceLoader]|ChangesByLanguage.js
* DefSideBoxes[ResourceLoader|dependencies=mediawiki.cookie,mediawiki.util,ext.gadget.DefinitionsAdder|type=general]|DefSideBoxes.js|DefSideBoxes.css
* DotsSyntaxHighlighter[ResourceLoader]|DotsSyntaxHighlighter.js
* DeveloperEditorTweaks[ResourceLoader|dependencies=ext.wikiEditor,mediawiki.util]|DeveloperEditorTweaks.js
* AjaxEdit[ResourceLoader|dependencies=mediawiki.util,mediawiki.user,mediawiki.api|skins=vector,vector-2022,monobook,timeless,modern,cologneblue]|AjaxEdit.js
* EditHere[ResourceLoader]|EditHere.js
* FlagMovedPageForDeletion[ResourceLoader|default|dependencies=site,mediawiki.api|namespaces=-1]|FlagMovedPageForDeletion.js
== misc-gadgets ==
* zhDialMap[ResourceLoader|default|dependencies=site,oojs-ui-core|categories=Chinese dialectal equivalent maps]|zhDialMap.js|zhDialMap.css
* DialectMap[ResourceLoader|default|dependencies=site]|DialectMap.js|DialectMap.css
* NoAutoCaseRedirect[ResourceLoader]|NoAutoCaseRedirect.js
* aWa[ResourceLoader|rights=autopatrol|dependencies=mediawiki.util,mediawiki.api,mediawiki.user,user.options,mediawiki.Title|type=general]|aWa.js|aWa.css
* QQ[ResourceLoader|dependencies=jquery.ui,mediawiki.util|type=general]|QQ.js|QQ.css
* AutoGlossary[ResourceLoader|default|dependencies=site,mediawiki.api|categories=Pages using auto-glossary]|AutoGlossary.js
== Hidden gadgets and utils ==
* AGprefs[ResourceLoader|hidden|dependencies=mediawiki.util,oojs-ui-core]|AGprefs.js
* DocTabs[ResourceLoader|hidden|default|dependencies=mediawiki.api,mediawiki.util|namespaces=0,1,10,11,114,828,829]|DocTabs.js|DocTabs.css
* Editor[ResourceLoader|hidden|dependencies=mediawiki.util,mediawiki.api]|Editor.js
* StorageUtils[ResourceLoader|hidden|dependencies=mediawiki.cookie,mediawiki.storage]|StorageUtils.js
* LanguageUtils[ResourceLoader|hidden|dependencies=mediawiki.api,ext.gadget.StorageUtils]|LanguageUtils.js
* DefinitionsAdder[ResourceLoader|hidden|dependencies=mediawiki.api,mediawiki.util,ext.gadget.Editor]|DefinitionsAdder.js
* TranslationAdder-Data[ResourceLoader|hidden|dependencies=mediawiki.api,ext.gadget.LanguageUtils|categories=Entries with translation boxes]|TranslationAdder-Data.js
* catfixRegrouper[ResourceLoader|hidden|default|dependencies=site,mediawiki.Title,ext.gadget.catfixRegrouper-Data|namespaces=14]|catfixRegrouper.js
* catfixRegrouper-Data[ResourceLoader|hidden|default|dependencies=site|namespaces=14]|catfixRegrouper-Data.js
* catfix[ResourceLoader|hidden|default|dependencies=site,mediawiki.Title|categories=Pages using catfix]|catfix.js
* Edittools[ResourceLoader|hidden|default|dependencies=mediawiki.cookie,jquery.textSelection]|Edittools.js|Edittools.css
* UnsupportedTitles[ResourceLoader|hidden|default|package]|UnsupportedTitles.js|UnsupportedTitles.json
* CssTest[ResourceLoader|hidden|type=styles]|CssTest.css
* Palette[ResourceLoader|hidden|default]|Palette.css
* UTCLiveClock-pagestyles[hidden|skins=vector,monobook]|UTCLiveClock-pagestyles.css
* Site[ResourceLoader|hidden|default]|Site.css
* SiteJS[ResourceLoader|hidden|default]|Site.js
* WiktGadgetPrefs[ResourceLoader|hidden|default|dependencies=mediawiki.user,user.options]|WiktGadgetPrefs.js
* ShowIDs-pagestyles[hidden|type=styles]|ShowIDs-pagestyles.css
* TreeDescs-pagestyles[hidden|type=styles]|TreeDescs-pagestyles.css
33dut7ud2c354nsd39mg450y9b1wiqv
ထာမ်ပလိက်:subpages
10
2340
396541
6677
2026-06-07T16:55:08Z
咽頭べさ
33
396541
wikitext
text/x-wiki
<div style="width: auto; margin: 0px; overflow: auto;">
{| width="100%" style="font-size: 90%; background:transparent;"
| {{Special:Prefixindex/{{{1|{{FULLPAGENAME}}}}}/|hideredirects={{{hideredirects|}}}|stripprefix={{{stripprefix|}}}}}
|}</div><!--
--><noinclude>{{documentation}}</noinclude>
nmuxgbm5kjbo1hcvmmp30tna7a7p15i
ကဏ္ဍ:ဝေါဟာအဓိကသၠဝ်ဝေန်နဳယျာဂမၠိုၚ်
14
8190
396468
396385
2026-06-07T12:02:04Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:ဝေါဟာအဓိကသလဝ်ဝေန်နဳယျာဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:ဝေါဟာအဓိကသၠဝ်ဝေန်နဳယျာဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396385
wikitext
text/x-wiki
[[Category:ဘာသာသၠဝ်ဝေန်နဳယျာ]]
5mglel9z1b4zwpu4djhan1r0s6omu1t
ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာပ္တိတ်ရမျာၚ် IPA ဂမၠိုၚ်
14
8191
396460
396377
2026-06-07T12:00:44Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:ဝေါဟာသလဝ်ဝေန်နဳယျာပ္တိတ်ရမျာၚ် IPA ဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာပ္တိတ်ရမျာၚ် IPA ဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396377
wikitext
text/x-wiki
[[Category:ဘာသာသၠဝ်ဝေန်နဳယျာ]]
5mglel9z1b4zwpu4djhan1r0s6omu1t
ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာကၠုၚ်နူဝေါဟာသဝါတ်ဟဳလဳဂမၠိုၚ်
14
11733
396451
396368
2026-06-07T11:59:14Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:ဝေါဟာသလဝ်ဝေန်နဳယျာကၠုၚ်နူဝေါဟာသဝါတ်ဟဳလဳဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာကၠုၚ်နူဝေါဟာသဝါတ်ဟဳလဳဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396368
wikitext
text/x-wiki
[[Category:ဘာသာသၠဝ်ဝေန်နဳယျာ]]
5mglel9z1b4zwpu4djhan1r0s6omu1t
ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာကၠုၚ်နူဝေါဟာသလာဗေတ်-အခိုက်ကၞာဂမၠိုၚ်
14
19954
396450
396367
2026-06-07T11:59:05Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:ဝေါဟာသလဝ်ဝေန်နဳယျာကၠုၚ်နူဝေါဟာသလာဗေတ်-အခိုက်ကၞာဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာကၠုၚ်နူဝေါဟာသလာဗေတ်-အခိုက်ကၞာဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396367
wikitext
text/x-wiki
[[Category:ဘာသာသၠဝ်ဝေန်နဳယျာ]]
5mglel9z1b4zwpu4djhan1r0s6omu1t
ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာဂွံလဝ်အာဲကၟာဲနူဝေါဟာသလာဗေတ်-အခိုက်ကၞာဂမၠိုၚ်
14
19955
396456
396373
2026-06-07T12:00:04Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:ဝေါဟာသလဝ်ဝေန်နဳယျာဂွံလဝ်အာဲကၟာဲနူဝေါဟာသလာဗေတ်-အခိုက်ကၞာဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာဂွံလဝ်အာဲကၟာဲနူဝေါဟာသလာဗေတ်-အခိုက်ကၞာဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396373
wikitext
text/x-wiki
[[Category:ဘာသာသၠဝ်ဝေန်နဳယျာ]]
5mglel9z1b4zwpu4djhan1r0s6omu1t
ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာကၠုၚ်နူဝေါဟာအဳတခ်လဳဂမၠိုၚ်
14
21489
396454
396371
2026-06-07T11:59:44Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:ဝေါဟာသလဝ်ဝေန်နဳယျာကၠုၚ်နူဝေါဟာအဳတခ်လဳဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာကၠုၚ်နူဝေါဟာအဳတခ်လဳဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396371
wikitext
text/x-wiki
[[Category:ဘာသာသၠဝ်ဝေန်နဳယျာ]]
5mglel9z1b4zwpu4djhan1r0s6omu1t
ကဏ္ဍ:သဗ္ဗနာမ်သၠဝ်ဝေန်နဳယျာဂမၠိုၚ်
14
23659
396478
396392
2026-06-07T12:03:34Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:သဗ္ဗနာမ်သလဝ်ဝေန်နဳယျာဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:သဗ္ဗနာမ်သၠဝ်ဝေန်နဳယျာဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396392
wikitext
text/x-wiki
[[Category:ဘာသာသၠဝ်ဝေန်နဳယျာ]]
5mglel9z1b4zwpu4djhan1r0s6omu1t
ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာကၠုၚ်နူဝေါဟာဟဳဗရဝ်ဂမၠိုၚ်
14
42696
396452
396369
2026-06-07T11:59:24Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:ဝေါဟာသလဝ်ဝေန်နဳယျာကၠုၚ်နူဝေါဟာဟဳဗရဝ်ဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာကၠုၚ်နူဝေါဟာဟဳဗရဝ်ဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396369
wikitext
text/x-wiki
[[Category:ဘာသာသၠဝ်ဝေန်နဳယျာ]]
5mglel9z1b4zwpu4djhan1r0s6omu1t
ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာကၠုၚ်နူဝေါဟာအိန်ဒဝ်-ယူရဝ်ပဳယာန်-အခိုက်ကၞာဂမၠိုၚ်
14
43429
396453
396370
2026-06-07T11:59:34Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:ဝေါဟာသလဝ်ဝေန်နဳယျာကၠုၚ်နူဝေါဟာအိန်ဒဝ်-ယူရဝ်ပဳယာန်-အခိုက်ကၞာဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာကၠုၚ်နူဝေါဟာအိန်ဒဝ်-ယူရဝ်ပဳယာန်-အခိုက်ကၞာဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396370
wikitext
text/x-wiki
[[Category:ဘာသာသၠဝ်ဝေန်နဳယျာ]]
5mglel9z1b4zwpu4djhan1r0s6omu1t
ကဏ္ဍ:ဝေါဟာအၚ်္ဂလိက်ကၠုၚ်နူဝေါဟာသၠဝ်ဝေန်နဳယျာဂမၠိုၚ်
14
53837
396472
275984
2026-06-07T12:02:35Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:ဝေါဟာအၚ်္ဂလိက်ကၠုၚ်နူဝေါဟာသလဝ်ဝေန်နဳယျာဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:ဝေါဟာအၚ်္ဂလိက်ကၠုၚ်နူဝေါဟာသၠဝ်ဝေန်နဳယျာဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
275984
wikitext
text/x-wiki
[[ကဏ္ဍ:ဘာသာအၚ်္ဂလိက်]]
cgthbuhht2vx8a42gqbqvafhhdb35kh
ကဏ္ဍ:ဝေါဟာအၚ်္ဂလိက်လွဳလဝ် နူဝေါဟာသၠဝ်ဝေန်နဳယျာဂမၠိုၚ်
14
53838
396474
276259
2026-06-07T12:02:55Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:ဝေါဟာအၚ်္ဂလိက်လွဳလဝ် နူဝေါဟာသလဝ်ဝေန်နဳယျာဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:ဝေါဟာအၚ်္ဂလိက်လွဳလဝ် နူဝေါဟာသၠဝ်ဝေန်နဳယျာဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
276259
wikitext
text/x-wiki
[[ကဏ္ဍ:ဘာသာအၚ်္ဂလိက်]]
cgthbuhht2vx8a42gqbqvafhhdb35kh
ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာမနွံကဵုမအရေဝ်ဝေါဟာဗီုပ္တိတ်ရမျာၚ်ဂမၠိုၚ်
14
55558
396462
396379
2026-06-07T12:01:05Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:ဝေါဟာသလဝ်ဝေန်နဳယျာမနွံကဵုမအရေဝ်ဝေါဟာဗီုပ္တိတ်ရမျာၚ်ဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာမနွံကဵုမအရေဝ်ဝေါဟာဗီုပ္တိတ်ရမျာၚ်ဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396379
wikitext
text/x-wiki
[[Category:ဘာသာသၠဝ်ဝေန်နဳယျာ]]
5mglel9z1b4zwpu4djhan1r0s6omu1t
ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာဂွံလဝ်အာဲကၟာဲနူဝေါဟာအိန်ဒဝ်-ယူရဝ်ပဳယာန်-အခိုက်ကၞာဂမၠိုၚ်
14
55559
396457
396374
2026-06-07T12:00:15Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:ဝေါဟာသလဝ်ဝေန်နဳယျာဂွံလဝ်အာဲကၟာဲနူဝေါဟာအိန်ဒဝ်-ယူရဝ်ပဳယာန်-အခိုက်ကၞာဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာဂွံလဝ်အာဲကၟာဲနူဝေါဟာအိန်ဒဝ်-ယူရဝ်ပဳယာန်-အခိုက်ကၞာဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396374
wikitext
text/x-wiki
[[Category:ဘာသာသၠဝ်ဝေန်နဳယျာ]]
5mglel9z1b4zwpu4djhan1r0s6omu1t
ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာဂွံလဝ်အာဲကၟာဲနူဝေါဟာဗဴတဝ်-သလာဗေတ်-အခိုက်ကၞာဂမၠိုၚ်
14
55560
396455
396372
2026-06-07T11:59:55Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:ဝေါဟာသလဝ်ဝေန်နဳယျာဂွံလဝ်အာဲကၟာဲနူဝေါဟာဗဴတဝ်-သလာဗေတ်-အခိုက်ကၞာဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာဂွံလဝ်အာဲကၟာဲနူဝေါဟာဗဴတဝ်-သလာဗေတ်-အခိုက်ကၞာဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396372
wikitext
text/x-wiki
[[Category:ဘာသာသၠဝ်ဝေန်နဳယျာ]]
5mglel9z1b4zwpu4djhan1r0s6omu1t
ကဏ္ဍ:သဒ္ဒာနာမဝိသေသနသၠဝ်ဝေန်နဳယျာဂမၠိုၚ်
14
55584
396476
396390
2026-06-07T12:03:15Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:သဒ္ဒာနာမဝိသေသနသလဝ်ဝေန်နဳယျာဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:သဒ္ဒာနာမဝိသေသနသၠဝ်ဝေန်နဳယျာဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396390
wikitext
text/x-wiki
[[Category:ဘာသာသၠဝ်ဝေန်နဳယျာ]]
5mglel9z1b4zwpu4djhan1r0s6omu1t
ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာလွဳလဝ် နူဝေါဟာဂျာမာန်ဂမၠိုၚ်
14
70534
396465
396382
2026-06-07T12:01:34Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:ဝေါဟာသလဝ်ဝေန်နဳယျာလွဳလဝ် နူဝေါဟာဂျာမာန်ဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာလွဳလဝ် နူဝေါဟာဂျာမာန်ဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396382
wikitext
text/x-wiki
[[Category:ဘာသာသၠဝ်ဝေန်နဳယျာ]]
5mglel9z1b4zwpu4djhan1r0s6omu1t
ကဏ္ဍ:ဝေါဟာအၚ်္ဂလိက်လွဳလဝ် နူဝေါဟာ သၠဝ်ဝေန်နဳယျာဂမၠိုၚ်
14
110528
396473
396388
2026-06-07T12:02:44Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:ဝေါဟာအၚ်္ဂလိက်လွဳလဝ် နူဝေါဟာ သလဝ်ဝေန်နဳယျာဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:ဝေါဟာအၚ်္ဂလိက်လွဳလဝ် နူဝေါဟာ သၠဝ်ဝေန်နဳယျာဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396388
wikitext
text/x-wiki
#REDIRECT [[:ကဏ္ဍ:ဝေါဟာအၚ်္ဂလိက်လွဳလဝ် နူဝေါဟာသၠဝ်ဝေန်နဳယျာဂမၠိုၚ်]]
e8f34oelc0hwsxvbrn8xao7q2164jax
ကဏ္ဍ:သဒ္ဒာနာမဝိသေသန သၠဝ်ဝေန်နဳယျာ
14
111480
396475
396389
2026-06-07T12:03:04Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:သဒ္ဒာနာမဝိသေသန သလဝ်ဝေန်နဳယျာ]] ဇရေင် [[ကဏ္ဍ:သဒ္ဒာနာမဝိသေသန သၠဝ်ဝေန်နဳယျာ]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396389
wikitext
text/x-wiki
#REDIRECT [[:ကဏ္ဍ:သဒ္ဒာနာမဝိသေသနသၠဝ်ဝေန်နဳယျာဂမၠိုၚ်]]
hfv7spaa9cdlvi47ccddelf1u6fyrzn
ကဏ္ဍ:သဗ္ဗနာမ် သၠဝ်ဝေန်နဳယျာ
14
111481
396477
396391
2026-06-07T12:03:24Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:သဗ္ဗနာမ် သလဝ်ဝေန်နဳယျာ]] ဇရေင် [[ကဏ္ဍ:သဗ္ဗနာမ် သၠဝ်ဝေန်နဳယျာ]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396391
wikitext
text/x-wiki
#REDIRECT [[:ကဏ္ဍ:သဗ္ဗနာမ်သၠဝ်ဝေန်နဳယျာဂမၠိုၚ်]]
erld9q462y33xxl9fkxghso8robrnkt
ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာလွဳလဝ် နူဝေါဟာ ဂျာမာန်ဂမၠိုၚ်
14
111482
396464
396381
2026-06-07T12:01:24Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:ဝေါဟာသလဝ်ဝေန်နဳယျာလွဳလဝ် နူဝေါဟာ ဂျာမာန်ဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာလွဳလဝ် နူဝေါဟာ ဂျာမာန်ဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396381
wikitext
text/x-wiki
#REDIRECT [[:ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာလွဳလဝ် နူဝေါဟာဂျာမာန်ဂမၠိုၚ်]]
pea0378vxeh4r76fjnzye2aexrngvla
ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာဗီုပြၚ်ဟွံမွဲကဵုပ္ဍဲအဘိဓာန်ဂမၠိုၚ်
14
111483
396461
396378
2026-06-07T12:00:54Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:ဝေါဟာသလဝ်ဝေန်နဳယျာဗီုပြၚ်ဟွံမွဲကဵုပ္ဍဲအဘိဓာန်ဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာဗီုပြၚ်ဟွံမွဲကဵုပ္ဍဲအဘိဓာန်ဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396378
wikitext
text/x-wiki
#REDIRECT [[:ကဏ္ဍ:ဗီုပြၚ်အပြံၚ်အလှာဲသၠဝ်ဝေန်နဳယျာဂမၠိုၚ်]]
rfx9xniq85deja4l91xhw82tvqljwux
ကဏ္ဍ:ဝေါဟာအဓိကသၠဝ်ဝေန်နဳယျာတြေံဂမၠိုၚ်
14
114908
396470
396386
2026-06-07T12:02:14Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:ဝေါဟာအဓိကသလဝ်ဝေန်နဳယျာတြေံဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:ဝေါဟာအဓိကသၠဝ်ဝေန်နဳယျာတြေံဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396386
wikitext
text/x-wiki
[[:ကဏ္ဍ:ဒၞာဲလုပ်အဝေါၚ်ကဵုပၟိက်|ဒၞာဲလုပ်အဝေါၚ်ကဵုပၟိက်]] » [[:ကဏ္ဍ:အရေဝ်ဘာသာအိုတ်သီုဂမၠိုၚ်|အရေဝ်ဘာသာအိုတ်သီုဂမၠိုၚ်]] » [[:ကဏ္ဍ:ဘာသာသၠဝ်ဝေန်နဳယျာတြေံ|သၠဝ်ဝေန်နဳယျာတြေံ]] » '''ဝေါဟာတံသ္ဇိုၚ်ဂမၠိုၚ်'''
:ဝေါဟာတံသ္ဇိုၚ်ဘာသာသၠဝ်ဝေန်နဳယျာတြေံ၊ ကဏ္ဍနူကဵုမပါ်ပရံဒကုတ်မဆေၚ်စပ်ကဵုမအရေဝ်ဝေါဟာ။
[[ကဏ္ဍ:ဘာသာသၠဝ်ဝေန်နဳယျာတြေံ]][[ကဏ္ဍ:ဝေါဟာအဓိကဗက်အလိုက်အရေဝ်ဘာသာဂမၠိုၚ်|သ]]
9drl7t835ogf7qln8xe91yavzpnghbk
ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာတြေံကၠုၚ်နူဝေါဟာပဝ်လာန်ဂမၠိုၚ်
14
114909
396458
396375
2026-06-07T12:00:24Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:ဝေါဟာသလဝ်ဝေန်နဳယျာတြေံကၠုၚ်နူဝေါဟာပဝ်လာန်ဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာတြေံကၠုၚ်နူဝေါဟာပဝ်လာန်ဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396375
wikitext
text/x-wiki
[[ကဏ္ဍ:ဘာသာသၠဝ်ဝေန်နဳယျာတြေံ]]
m2j8w9tub5jwbomks4kjg0miule4jih
ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာတြေံလွဳလဝ် နူဝေါဟာပဝ်လာန်ဂမၠိုၚ်
14
114910
396459
396376
2026-06-07T12:00:34Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:ဝေါဟာသလဝ်ဝေန်နဳယျာတြေံလွဳလဝ် နူဝေါဟာပဝ်လာန်ဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာတြေံလွဳလဝ် နူဝေါဟာပဝ်လာန်ဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396376
wikitext
text/x-wiki
[[ကဏ္ဍ:ဘာသာသၠဝ်ဝေန်နဳယျာတြေံ]]
m2j8w9tub5jwbomks4kjg0miule4jih
ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာလွဳလဝ် နူဝေါဟာအဳတခ်လဳဂမၠိုၚ်
14
117007
396467
396384
2026-06-07T12:01:55Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:ဝေါဟာသလဝ်ဝေန်နဳယျာလွဳလဝ် နူဝေါဟာအဳတခ်လဳဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာလွဳလဝ် နူဝေါဟာအဳတခ်လဳဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396384
wikitext
text/x-wiki
[[Category:ဘာသာသၠဝ်ဝေန်နဳယျာ]]
5mglel9z1b4zwpu4djhan1r0s6omu1t
ကဏ္ဍ:ဝေါဟာအရေဝ်ဗေဗာန်သၠဝ်ဝေန်နဳယျာဂမၠိုၚ်
14
119296
396471
396387
2026-06-07T12:02:24Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:ဝေါဟာအရေဝ်ဗေဗာန်သလဝ်ဝေန်နဳယျာဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:ဝေါဟာအရေဝ်ဗေဗာန်သၠဝ်ဝေန်နဳယျာဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396387
wikitext
text/x-wiki
[[Category:ဘာသာသၠဝ်ဝေန်နဳယျာ]]
5mglel9z1b4zwpu4djhan1r0s6omu1t
ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာလွဳလဝ် နူဝေါဟာရဝ်မာနဳဂမၠိုၚ်
14
119298
396466
396383
2026-06-07T12:01:45Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:ဝေါဟာသလဝ်ဝေန်နဳယျာလွဳလဝ် နူဝေါဟာရဝ်မာနဳဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာလွဳလဝ် နူဝေါဟာရဝ်မာနဳဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396383
wikitext
text/x-wiki
[[Category:ဘာသာသၠဝ်ဝေန်နဳယျာ]]
5mglel9z1b4zwpu4djhan1r0s6omu1t
ကဏ္ဍ:အဆက်လက္ကရဴသၠဝ်ဝေန်နဳယျာဂမၠိုၚ်
14
119691
396479
396393
2026-06-07T12:03:44Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:အဆက်လက္ကရဴသလဝ်ဝေန်နဳယျာဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:အဆက်လက္ကရဴသၠဝ်ဝေန်နဳယျာဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396393
wikitext
text/x-wiki
[[Category:ဘာသာသၠဝ်ဝေန်နဳယျာ]]
5mglel9z1b4zwpu4djhan1r0s6omu1t
မဝ်ဂျူ:palette
828
214322
396572
294601
2026-06-07T19:59:04Z
咽頭べさ
33
396572
Scribunto
text/plain
local export = {}
-- Given a CSS hex value, return a table of the RGB components.
-- The RGB is normalized so that white is (1.0, 1.0, 1.0).
local function get_rgb_from_hex(hex_colour)
hex_colour = hex_colour:gsub("#", "")
if #hex_colour == 3 then
return {
tonumber(hex_colour:sub(1, 1), 16) * 17 / 255,
tonumber(hex_colour:sub(2, 2), 16) * 17 / 255,
tonumber(hex_colour:sub(3, 3), 16) * 17 / 255
}
elseif #hex_colour == 6 then
return {
tonumber(hex_colour:sub(1, 2), 16) / 255,
tonumber(hex_colour:sub(3, 4), 16) / 255,
tonumber(hex_colour:sub(5, 6), 16) / 255
}
end
end
-- Calculate the WCAG2.1 relative luminance value given a RGB tuple.
-- https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html#dfn-relative-luminance
local function get_WCAG_21_luminance(rgb)
local luminance_parts = {}
for _, rgb_part in ipairs(rgb) do
if rgb_part > 0.04045 then
table.insert(luminance_parts, ((rgb_part + 0.055) / 1.055) ^ 2.4)
else
table.insert(luminance_parts, rgb_part / 12.92)
end
end
local luminance = 0.2126 * luminance_parts[1] + 0.7152 * luminance_parts[2] + 0.0722 * luminance_parts[3]
return luminance
end
-- Calculate the WCAG2.1 contrast ratio given two RGB tuples.
-- https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html#dfn-contrast-ratio
local function get_WCAG_21_contrast(rgb1, rgb2)
local luminance1 = get_WCAG_21_luminance(rgb1)
local luminance2 = get_WCAG_21_luminance(rgb2)
-- The lighter colour is used in the numerator.
local contrast_ratio
if luminance1 >= luminance2 then
contrast_ratio = (luminance1 + 0.05) / (luminance2 + 0.05)
else
contrast_ratio = (luminance2 + 0.05) / (luminance1 + 0.05)
end
return contrast_ratio
end
-- Calculate the APCA contrast ratio given a text RGB tuple and a background RGB tuple.
-- https://github.com/Myndex/SAPC-APCA/blob/master/documentation/APCA-W3-LaTeX.md
-- The variables are named to match the LaTeX document.
local function get_APCA_contrast(rgb_txt, rgb_bg)
local Ys_txt = rgb_txt[1] ^ 2.4 * 0.2126729 + rgb_txt[2] ^ 2.4 * 0.7151522 + rgb_txt[3] ^ 2.4 * 0.0721750
local Ys_bg = rgb_bg[1] ^ 2.4 * 0.2126729 + rgb_bg[2] ^ 2.4 * 0.7151522 + rgb_bg[3] ^ 2.4 * 0.0721750
local function f_sc(Y_c)
if Y_c <= 0 then
return 0
elseif Y_c <= 0.022 then
return Y_c + (0.022 - Y_c) ^ 1.414
else
return Y_c
end
end
local Y_txt = f_sc(Ys_txt)
local Y_bg = f_sc(Ys_bg)
local S_apc
if Y_bg > Y_txt then
S_apc = (Y_bg ^ 0.56 - Y_txt ^ 0.57) * 1.14
else
S_apc = (Y_bg ^ 0.65 - Y_txt ^ 0.62) * 1.14
end
local L_c
if math.abs(S_apc) < 0.1 then
L_c = 0
elseif S_apc > 0 then
L_c = (S_apc - 0.027) * 100
else
L_c = (S_apc + 0.027) * 100
end
return L_c
end
-- Get the WCAG2.1 rating as HTML, given a contrast value.
local function get_WCAG_21_rating(contrast_value)
if contrast_value >= 7 then
return "<span style=\"color:var(--wikt-palette-deepblue)\">(AAA)</span>"
elseif contrast_value >= 4.5 then
return "<span style=\"color:var(--wikt-palette-forestgreen)\">(AA)</span>"
else
return "<span style=\"color:var(--wikt-palette-red)\">(FAIL)</span>"
end
end
-- Get the APCA rating as HTML, given a contrast value.
-- Using default text values: font size = 14px, font weight = 400
local function get_APCA_rating(contrast_value)
local abs_contrast_value = math.abs(contrast_value)
if abs_contrast_value >= 100 then
return "<span style=\"color:var(--wikt-palette-deepblue)\">(R4)</span>"
elseif abs_contrast_value >= 95 then
return "<span style=\"color:var(--wikt-palette-forestgreen)\">(R3)</span>"
elseif abs_contrast_value >= 90 then
return "<span style=\"color:var(--wikt-palette-forestgreen)\">(R2)</span>"
elseif abs_contrast_value >= 85 then
return "<span style=\"color:var(--wikt-palette-forestgreen)\">(R1)</span>"
else
return "<span style=\"color:var(--wikt-palette-red)\">(R0)</span>"
end
end
function export.show(frame)
local pageContent = mw.title.new("MediaWiki:Gadget-Palette.css"):getContent()
local lightMode = true;
local lightModeData = {}
local darkModeData = {}
local commentsData = {}
local seenVariables = {} -- In order to maintain the order.
local output = {
[=[{| class="palette-table wikitable mw-no-invert"
|+Table of palette variables in light and dark mode
|-
!scope="col"| Variable name
!scope="col"| Light mode
!scope="col"| Dark mode
!scope="col"| Use as a background colour?
!scope="col"| Use as a text colour?
!scope="col"| Comments
|-"
]=]}
for line in pageContent:gmatch("[^\r\n]*") do
-- Hack to check whether we've reached the dark mode styles.
if line == "@media screen {" then
lightMode = false;
end
local definedVariable = line:match("--wikt%-palette%-[a-z0-9-]+")
if definedVariable then
if lightMode then
table.insert(seenVariables, definedVariable)
lightModeData[definedVariable] = line:match("#[a-zA-Z0-9]+")
commentsData[definedVariable] = line:match("/%*%s*(.-)%s*%*/") or ""
else
darkModeData[definedVariable] = line:match("#[a-zA-Z0-9]+")
end
end
end
for _, var in ipairs(seenVariables) do
table.insert(output, "|<code>" .. var .. "</code>")
table.insert(output, "||style=\"background:" .. lightModeData[var] .. "\"|<span class=\"palette-highlight\">" .. lightModeData[var] .. "</span>")
table.insert(output, "||style=\"background:" .. darkModeData[var] .. "\"|<span class=\"palette-highlight\">" .. darkModeData[var] .. "</span>")
-- Contrast on body text in light mode.
local WCAG_21_contrast_text_lightmode = get_WCAG_21_contrast(get_rgb_from_hex("#202122"), get_rgb_from_hex(lightModeData[var]))
local APCA_contrast_text_lightmode = get_APCA_contrast(get_rgb_from_hex("#202122"), get_rgb_from_hex(lightModeData[var]))
table.insert(output, "||'''Light mode:''' WCAG " .. string.format("%.2f", WCAG_21_contrast_text_lightmode) .. " " .. get_WCAG_21_rating(WCAG_21_contrast_text_lightmode))
table.insert(output, ", APCA " .. string.format("%.2f", APCA_contrast_text_lightmode) .. " " .. get_APCA_rating(APCA_contrast_text_lightmode))
-- Contrast on body text in dark mode.
local WCAG_21_contrast_text_darkmode = get_WCAG_21_contrast(get_rgb_from_hex("#eaecf0"), get_rgb_from_hex(darkModeData[var]))
local APCA_contrast_text_darkmode = get_APCA_contrast(get_rgb_from_hex("#eaecf0"), get_rgb_from_hex(darkModeData[var]))
table.insert(output, "<br> '''Dark mode:''' WCAG " .. string.format("%.2f", WCAG_21_contrast_text_darkmode) .. " " .. get_WCAG_21_rating(WCAG_21_contrast_text_darkmode))
table.insert(output, ", APCA " .. string.format("%.2f", APCA_contrast_text_darkmode) .. " " .. get_APCA_rating(APCA_contrast_text_darkmode))
-- Contrast on default background in light mode.
local WCAG_21_contrast_bg_lightmode = get_WCAG_21_contrast(get_rgb_from_hex(lightModeData[var]), get_rgb_from_hex("#ffffff"))
local APCA_contrast_bg_lightmode = get_APCA_contrast(get_rgb_from_hex(lightModeData[var]), get_rgb_from_hex("#ffffff"))
table.insert(output, "||'''Light mode:''' WCAG " .. string.format("%.2f", WCAG_21_contrast_bg_lightmode) .. " " .. get_WCAG_21_rating(WCAG_21_contrast_bg_lightmode))
table.insert(output, ", APCA " .. string.format("%.2f", APCA_contrast_bg_lightmode) .. " " .. get_APCA_rating(APCA_contrast_bg_lightmode))
-- Contrast on default background in dark mode.
local WCAG_21_contrast_bg_darkmode = get_WCAG_21_contrast(get_rgb_from_hex(darkModeData[var]), get_rgb_from_hex("#101418"))
local APCA_contrast_bg_darkmode = get_APCA_contrast(get_rgb_from_hex(darkModeData[var]), get_rgb_from_hex("#101418"))
table.insert(output, "<br> '''Dark mode:''' WCAG " .. string.format("%.2f", WCAG_21_contrast_bg_darkmode) .. " " .. get_WCAG_21_rating(WCAG_21_contrast_bg_darkmode))
table.insert(output, ", APCA " .. string.format("%.2f", APCA_contrast_bg_darkmode) .. " " .. get_APCA_rating(APCA_contrast_bg_darkmode))
-- Comments.
table.insert(output, "||" .. commentsData[var])
table.insert(output, "\n|-\n")
end
table.insert(output, "|-\n")
table.insert(output, "|colspan=\"6\" style=\"padding:1em;background:var(--wikt-palette-lavender,#f8f8ff)\"|According to [[mw:Recommendations for night mode compatibility on Wikimedia wikis]], editors should ensure that colours meet the WCAC 2.1 AA contrast standards (APCA is significantly stricter but corresponds more closely to human visual perception — see [[phab:T308772]]). Contrast ratios are calculated assuming standard text formatting (14px, unbolded) and standard colours on the rest of the page. To check contrast values between any two colours, see https://contrast.tools/.\n")
table.insert(output, "\n|}")
table.insert(output, frame:extensionTag("templatestyles", "", {src="Module:palette/styles.css"}))
return table.concat(output)
end
return export
qhecia48w6nk2cpmklq1wz76jb2rzu9
မဝ်ဂျူ:hsb-IPA
828
219917
396484
300488
2026-06-07T12:24:34Z
咽頭べさ
33
396484
Scribunto
text/plain
local export = {}
local m_IPA = require("Module:IPA")
local lang = require("Module:languages").getByCode("hsb")
local rsub = mw.ustring.gsub
local rlower = mw.ustring.lower
local C = "[bdfɡɦxjkβmnpʀʃstvɥzʒʲ]"
local V = "[aɛeiɔouɪ]"
local T = "[pfsxkpʃst]"
local D = "[bvβdɡɦzʒ]"
local function rsub_repeatedly(term, foo, bar)
while true do
local new_term = rsub(term, foo, bar)
if new_term == term then
return term
end
term = new_term
end
end
local di = {
["ch"]="x",
["dź"]="dʒ",
["tř"]="tS",
["wj"]="ɥ",
}
local phon = {
["a"]="a",
["b"]="b",
["c"]="ts",
["č"]="tʃ",
["ć"]="tʃ",
["d"]="d",
["e"]="ɛ",
["ě"]="ɪ",
["f"]="f",
["g"]="ɡ",
["h"]="ɦ",
["i"]="i",
["j"]="j",
["k"]="k",
["l"]="l",
["ł"]="w",
["m"]="m",
["n"]="n",
["ń"]="jn",
["o"]="ɔ",
["ó"]="ʊ",
["p"]="p",
["q"]="kʰ",
["r"]="ʀ",
["ř"]="ʃ",
["s"]="s",
["š"]="ʃ",
["t"]="t",
["u"]="u",
["w"]="w",
["v"]="v",
["y"]="ɨ",
["z"]="z",
["ž"]="ʒ",
}
local devoicing = {
["b"] = "p",
["d"] = "t",
["ɡ"] = "k",
["z"] = "s",
["v"] = "f",
["ʒ"] = "ʃ",
}
local voicing = {
["p"] = "b",
["t"] = "d",
["k"] = "ɡ",
["s"] = "z",
["f"] = "v",
["ʃ"] = "ʒ",
}
local function phonemic(text)
text = rlower(text)
text = rsub(text, " | ", "# | #")
text = "##" .. rsub(text, " ", "# #") .. "##"
-- basic phonology
text = rsub(text, "ts", "tss")
text = rsub(text, "..", di)
text = rsub("1" .. text, "..", di)
text = rsub("1" .. text, "..", di)
text = rsub(text, "1", "")
text = rsub(text, ".", phon)
--
text = rsub(text, "#x", "#kʰ")
--
text = rsub(text, "X", "x")
--
text = rsub(text, "#sx", "skʰ")
--
text = rsub(text, "#zɦ", "z")
-- palatalisation
text = rsub(text, "jʲ", "j")
-- h
text = rsub(text, "ɦ#", "")
text = rsub(text, "ɦ("..C..")", "%1")
-- ł
text = rsub(text, "("..C..")w$", "%1")
text = rsub(text, "("..C..")w("..C..")", "%1%2")
-- w
text = rsub(text, "("..C..")v$", "%1")
text = rsub(text, "("..C..")v("..C..")", "%1%2")
local function voice(sound, following)
return voicing[sound] .. following
end
local function devoice(sound, following)
return devoicing[sound] .. following
end
local function final_devoicing(sound)
return devoicing[sound]
end
--v voicing
text = rsub(text, "(" .. T .. ")v", "%1f")
-- Final devoicing
text = rsub_repeatedly(text, "(" .. D .. ")#", final_devoicing)
text = rsub_repeatedly(text, "(" .. T .. ")(" .. D .. ")", voice)
text = rsub_repeatedly(text, "(" .. D .. ")(" .. T .. ")", devoice)
-- stress
if mw.ustring.find(text, "'") == nil then
text = "ˈ" .. text
end
text = rsub(text, "^(" .. C .. "+ʲ?)'", "'%1")
text = rsub(text, " (" .. C .. "+ʲ?)'", " '%1")
text = rsub(text, "(" .. C .. "ʲ?)'", "'%1")
text = rsub(text, "t's", "'ts")
text = rsub(text, "'", "ˈ")
-- affricates
text = rsub(text, "t([sʃ])", "t͡%1")
text = rsub(text, "d([zʒ])", "d͡%1")
-- suffixes
text = rsub(text, "^ˈ%-", "")
-- resolution
text = rsub(text, "wʲ", "w")
text = rsub(text, "jʲ", "j")
-- tř
text = rsub(text, "tS", "tʃ")
text = rsub(text, "#", "")
text = rsub(text, "-", "")
return text
end
function export.IPA(frame)
local words = {}
for _, word in ipairs(frame:getParent().args) do
table.insert(words, word)
end
if #words == 0 then
words = {mw.loadData("Module:headword/data").pagename}
end
local IPA_results = {}
for _, word in ipairs(words) do
table.insert(IPA_results, { pron = "/" .. phonemic(word) .. "/" })
end
return m_IPA.format_IPA_full { lang = lang, items = IPA_results }
end
return export
lytntulrfall6l1g9j00tiiazus6muu
မဝ်ဂျူ:hsb-dsb-headword
828
220408
396548
301175
2026-06-07T17:37:19Z
咽頭べさ
33
396548
Scribunto
text/plain
local export = {}
local pos_functions = {}
local force_cat = false -- for testing; if true, categories appear in non-mainspace pages
local rfind = mw.ustring.find
local require_when_needed = require("Module:utilities/require when needed")
local m_table = require("Module:table")
local en_utilities_module = "Module:en-utilities"
local headword_utilities_module = "Module:headword utilities"
local m_headword_utilities = require_when_needed(headword_utilities_module)
local m_string_utilities = require_when_needed("Module:string utilities")
local glossary_link = require_when_needed(headword_utilities_module, "glossary_link")
local list_param = {list = true, disallow_holes = true}
local boolean_param = {type = "boolean"}
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
local insert = table.insert
local concat = table.concat
local langs_supported = {
["hsb"] = {
peri_comp = "bóle",
sup = "naj",
abs_sup_prefix = "na",
excessive = "pře",
},
["dsb"] = {
peri_comp = "wěcej",
sup = "nej",
},
}
-- Table of all valid aspects.
local valid_aspects = m_table.listToSet {
"impf", "pf", "biasp", "?",
}
local valid_verb_types = {
semelfactive = true,
durative = true,
frequentative = true,
continuative = true,
unidirectional = true,
multidirectional = true,
concrete = "unidirectional",
abstract = "multidirectional",
}
local function track(page)
require("Module:debug").track("hsb-dsb-headword/" .. page)
return true
end
local function replace_hash_with_lemma(term, lemma)
-- If there is a % sign in the lemma, we have to replace it with %% so it doesn't get interpreted as a capture
-- replace expression.
lemma = m_string_utilities.replacement_escape(lemma)
return (term:gsub("#", lemma)) -- discard second retval
end
local function frob_term_with_hash(term, lemma)
if term:find("#") then
term = replace_hash_with_lemma(term, lemma)
end
return term
end
-- Parse and insert an inflection not requiring additional processing into `data.inflections`. The raw arguments come
-- from `args[field]`, which is parsed for inline modifiers. `label` is the label that the inflections are given;
-- sections enclosed in <<...>> are linked to the glossary. `accel` is the accelerator form, or nil.
local function parse_and_insert_inflection(data, args, field, label, accel, frob)
m_headword_utilities.parse_and_insert_inflection {
headdata = data,
forms = args[field],
paramname = field,
splitchar = ",",
label = label,
include_mods = {"t"},
accel = accel and {form = accel} or nil,
frob = function(term)
term = frob_term_with_hash(term, data.pagename)
if frob then
term = frob(term)
end
return term
end,
}
end
-- Parse and return an inflection not requiring additional processing. The raw arguments come from `args[field]`, which
-- is parsed for inline modifiers.
local function parse_inflection(data, paramname, forms)
return m_headword_utilities.parse_term_list_with_modifiers {
paramname = paramname,
forms = forms,
splitchar = ",",
include_mods = {"t"},
frob = function(term)
return frob_term_with_hash(term, data.pagename)
end,
}
end
-- Insert the parsed inflections in `infls` (as parsed by `parse_inflection`) into `data.inflections`, with label
-- `label` and optional accelerator spec `accel`.
local function insert_inflection(data, terms, label, accel)
m_headword_utilities.insert_inflection {
headdata = data,
terms = terms,
label = label,
accel = accel and {form = accel} or nil,
}
end
-- The main entry point.
-- This is the only function that can be invoked from a template.
function export.show(frame)
local iparams = {
[1] = {required = true},
["lang"] = {required = true},
}
local iargs = require("Module:parameters").process(frame.args, iparams)
local poscat = iargs[1]
langcode = iargs.lang
if langcode ~= "hsb" and langcode ~= "dsb" then
error("This module currently only works for lang=hsb and lang=dsb")
end
local lang = require("Module:languages").getByCode(langcode)
local langname = lang:getCanonicalName()
local params = {
["head"] = {list = true},
["nolink"] = {type = "boolean"},
["nolinkhead"] = {type = "boolean", alias_of = "nolink"},
["id"] = {},
["sort"] = {},
["suffix"] = {type = "boolean"},
["nosuffix"] = {type = "boolean"},
["json"] = {type = "boolean"},
["pagename"] = {}, -- for testing
}
if pos_functions[poscat] then
for key, val in pairs(pos_functions[poscat].params) do
params[key] = val
end
end
local parargs = frame:getParent().args
local args = require("Module:parameters").process(parargs, params)
local pagename = args.pagename or mw.loadData("Module:headword/data").pagename
local user_specified_heads = args.head
local heads = user_specified_heads
if args.nolink then
if #heads == 0 then
heads = {pagename}
end
end
local data = {
lang = lang,
langname = langname,
langcode = langcode,
pos_category = poscat,
categories = {},
heads = heads,
user_specified_heads = user_specified_heads,
no_redundant_head_cat = #user_specified_heads == 0,
genders = {},
inflections = {},
categories = {},
pagename = pagename,
id = args.id,
sort_key = args.sort,
force_cat_output = force_cat,
}
data.is_suffix = false
if args.suffix or (
not args.nosuffix and pagename:find("^%-") and poscat ~= "အဆက်လက္ကရဴ" and poscat ~= "ဗီုပြၚ်အဆက်လက္ကရဴ"
) then
data.is_suffix = true
data.pos_category = "အဆက်လက္ကရဴ"
-- local singular_poscat = require(en_utilities_module).singularize(poscat)
-- insert(data.categories, langname .. " " .. singular_poscat .. "-forming suffixes")
-- insert(data.inflections, {label = singular_poscat .. "-forming suffix"})
end
if pos_functions[poscat] then
pos_functions[poscat].func(args, data)
end
if args.json then
return require("Module:JSON").toJSON(data)
end
return require("Module:headword").full_headword(data)
end
local function get_noun_pos(is_proper)
local noun_inflection_specs = {
{"gen", "ဗဳဇဂကူကိုန်ဨကဝုစ်"},
{"du", "မဒုၚ်ယၟုၜါလ္ပာ်"},
{"pl", "ကိုန်ဗဟုဝစ်မဒုၚ်ယၟု"},
{"genpl", "ဗဳဇဂကူကိုန်ဗဟုဝစ်"},
{"m", "မညဳညတ်သၟတ်တးကဵုပုလ္လိၚ်"},
{"f", "မညဳညတ်သၟတ်တးကဵုလိၚ်ဗြဴ"},
{"adj", "<<နာမဝိသေသနဒၞာဲမဆက်စပ်>>"},
{"poss", "<<နာမဝိသေသနဓမံက်ထ္ၜးမဒှ်တၠဒြပ်>>"},
{"dim", "<<လဟုတ်စှ်ေ>>"},
{"aug", "<<ပရေၚ်မဖပေၚ်တိုန်>>"},
{"pej", "<<စမ်ၜတ်ရံၚ်>>"},
{"dem", "<<အပၠေံရုပ်ရာ>>"},
{"fdem", "<<အပၠေံရုပ်ရာ>>ဣတ္တိလိၚ်"},
}
local params = {
[1] = {type = "genders", required = true, template_default = "?"},
["g"] = {list = true, disallow_holes = true, replaced_by = false,
instead = "use multiple comma-separated genders in |1="},
["indecl"] = {type = "boolean"},
}
for _, spec in ipairs(noun_inflection_specs) do
local param, desc = unpack(spec)
params[param] = list_param
end
return {
params = params,
func = function(args, data)
-- Compute allowed genders, and map incomplete genders to specs with a "?" in them.
local masc_genders = {"m", "mf", "mfbysense"}
local non_masc_genders = {false, "f", "n"}
local hsb_masc_animacies = {false, "in", "anml", "pr"}
local dsb_masc_animacies = {false, "in", "an"}
local numbers = {false, "d", "p"}
local allowed_genders = {}
local function insert_genders(genders, animacies, numbers, is_masc)
for _, g in ipairs(genders) do
for _, an in ipairs(animacies) do
for _, num in ipairs(numbers) do
local source_gender_parts = {}
local dest_gender_parts = {}
local function ins_part(part, partname)
if part then
insert(source_gender_parts, part)
insert(dest_gender_parts, part)
elseif partname == "g" and num == false or partname == "an" and is_masc then
-- allow incomplete gender duale tantum and plurale tantum nouns; also allow
-- incomplete animacy for fem/neut; otherwise insert a ? to indicate incomplete
-- gender spec
insert(dest_gender_parts, "?")
end
end
ins_part(g, "g")
ins_part(an, "an")
ins_part(num, "num")
if #source_gender_parts == 0 then
allowed_genders["?"] = "?"
else
allowed_genders[concat(source_gender_parts, "-")] =
concat(dest_gender_parts, "-")
end
end
end
end
end
if data.lang:getCode() == "hsb" then
insert_genders(masc_genders, hsb_masc_animacies, numbers, true)
else
insert_genders(masc_genders, dsb_masc_animacies, numbers, true)
end
insert_genders(non_masc_genders, {false}, numbers, false)
-- allow inanimate dual/plural; no gender distinction here
allowed_genders["in-d"] = "in-d"
allowed_genders["in-p"] = "in-p"
-- Validate and canonicalize genders.
for _, gspec in ipairs(args[1]) do
local g = gspec.spec
local canon_g = allowed_genders[g]
if canon_g then
g = canon_g
else
error("Unrecognized " .. data.langname .. " gender: " .. g)
end
gspec.spec = g
end
data.genders = args[1]
local function process_inflection(label, infls, qs, frob)
infls.label = label
for i, infl in ipairs(infls) do
if frob then
infl = frob(infl)
end
if qs[i] then
infls[i] = {term = infl, q = {qs[i]}}
end
end
if #infls > 0 then
insert(data.inflections, infls)
end
end
if args.indecl then
insert(data.inflections, {label = glossary_link("ပါ်ပါဲဟွံမာန်")})
insert(data.categories, "နာမ်" .. data.langname .. "နကဵုပါ်ပါဲထောံဟွံမာန်ဂမၠိုၚ်")
end
-- Parse and insert an inflection not requiring additional processing into `data.inflections`. The raw
-- arguments come from `args[field]`, which is parsed for inline modifiers. `label` is the label that the
-- inflections are given, which is linked to the glossary if preceded by * (which is removed).
local function handle_infl(field, label)
parse_and_insert_inflection(data, args, field, label)
end
-- Process all inflections.
for _, spec in ipairs(noun_inflection_specs) do
local param, desc = unpack(spec)
handle_infl(param, desc)
end
end
}
end
pos_functions["နာမ်"] = get_noun_pos(false)
pos_functions["နာမ်မကိတ်ညဳ"] = get_noun_pos(true)
pos_functions["ကြိယာ"] = {
params = {
[1] = {type = "genders", default = "?"},
["type"] = {set = valid_verb_types},
["pf"] = list_param,
["impf"] = list_param,
},
func = function(args, data)
-- Validate genders.
for _, gspec in ipairs(args[1]) do
local g = gspec.spec
if not valid_aspects[g] then
error("Unrecognized " .. data.langname .. " aspect: " .. g)
end
end
data.genders = args[1]
if args.type then
m_headword_utilities.insert_fixed_inflection {
headdata = data,
label = ("<<%s>>"):format(args.type),
}
insert(data.categories, ("%s %s verbs"):format(data.langname, args.type))
end
parse_and_insert_inflection(data, args, "pf", "perfective")
parse_and_insert_inflection(data, args, "impf", "imperfective")
end,
}
local function get_adj_adv_pos(pos)
local params = {
[1] = list_param,
["dim"] = list_param,
["sup"] = list_param,
["nodefsup"] = boolean_param,
}
if pos == "နာမဝိသေသန" then
params["adv"] = list_param
params["indecl"] = boolean_param
end
return {
params = params,
func = function(args, data)
if pos == "adjective" and args.indecl then
insert(data.inflections, {label = glossary_link("ပါ်ပါဲဟွံမာန်")})
insert(data.categories, "နာမဝိသေသန" .. data.langname .. "နကဵုပါ်ပါဲထောံဟွံမာန်ဂမၠိုၚ်")
end
local default_sups = {}
local default_abs_sups = {}
local comps = parse_inflection(data, 1, args[1])
local is_comparable = false
local lang_data = langs_supported[data.langcode]
if comps[1] then
if comps[1].term == "-" then
if not comps[2] then
m_headword_utilities.insert_fixed_inflection {
headdata = data,
label = "not <<comparable>>",
originating_term = comps[1],
}
insert(data.categories, data.pos_category .. data.langname .. "မတော်ဟွံဂွံဂမၠိုၚ်")
else
m_headword_utilities.insert_fixed_inflection {
headdata = data,
label = "not generally <<comparable>>",
originating_term = comps[1],
}
end
table.remove(comps, 1)
end
for i, comp in ipairs(comps) do
if not is_comparable then
is_comparable = true
insert(data.categories, data.pos_category .. data.langname .. "မတော်ဂွံဂမၠိုၚ်")
end
if comp.term == "peri" then
if not lang_data.peri_comp then
error("Don't know how to form periphrastic comparatives for " .. data.langname)
end
comp.term = ("[[%s]] [[%s]]"):format(lang_data.peri_comp, data.pagename)
if lang_data.sup then
local default_sup = m_table.shallowCopy(comp)
default_sup.term = ("[[%s%s]] [[%s]]"):format(lang_data.sup, lang_data.peri_comp,
data.pagename)
insert(default_sups, default_sup)
if lang_data.abs_sup_prefix then
local default_abs_sup = m_table.shallowCopy(comp)
default_abs_sup.term = ("[[%s%s%s]] [[%s]]"):format(lang_data.abs_sup_prefix,
lang_data.sup, lang_data.peri_comp, data.pagename)
insert(default_abs_sups, default_abs_sup)
end
end
else
if lang_data.sup then
local default_sup = m_table.shallowCopy(comp)
default_sup.term = ("%s%s"):format(lang_data.sup, comp.term)
insert(default_sups, default_sup)
if lang_data.abs_sup_prefix then
local default_abs_sup = m_table.shallowCopy(comp)
default_abs_sup.term = ("%s%s%s"):format(lang_data.abs_sup_prefix, lang_data.sup,
comp.term)
insert(default_abs_sups, default_abs_sup)
end
end
end
end
end
insert_inflection(data, comps, "ပတဝ်ပတုပ်ရံၚ်", "ပတဝ်ပတုပ်ရံၚ်")
local sups = parse_inflection(data, "sup", args.sup)
if not sups[1] then
sups = args.nodefsup and {} or {{term = "+"}}
end
local combined_sups = {}
for _, sup in ipairs(sups) do
if sup.term == "+" then
for _, def_sup in ipairs(default_sups) do
def_sup = m_table.shallowCopy(def_sup)
m_headword_utilities.combine_termobj_qualifiers_labels(def_sup, sup)
insert(combined_sups, def_sup)
end
else
insert(combined_sups, sup)
end
end
-- Compute the absolute superlative before inserting any inflections because the process of inserting
-- inflections may modify the superlatives that we base the absolute superlatives off of.
local combined_abs_sups = {}
if lang_data.abs_sup_prefix then
for _, sup in ipairs(sups) do
if sup.term == "+" then
for _, def_abs_sup in ipairs(default_abs_sups) do
def_abs_sup = m_table.shallowCopy(def_abs_sup)
m_headword_utilities.combine_termobj_qualifiers_labels(def_abs_sup, sup)
insert(combined_abs_sups, def_abs_sup)
end
else
sup = m_table.shallowCopy(sup)
if sup.term:find("%[%[") then
sup.term = "[[" .. lang_data.abs_sup_prefix .. sup.term:sub(3)
else
sup.term = lang_data.abs_sup_prefix .. sup.term
end
insert(combined_abs_sups, sup)
end
end
end
insert_inflection(data, combined_sups, "သဒ္ဒာ", "သဒ္ဒာ")
insert_inflection(data, combined_abs_sups, "<<သဒ္ဒာမဍိုက်ပေၚ်အိုတ်>>", "သဒ္ဒာမဍိုက်ပေၚ်အိုတ်")
if is_comparable and lang_data.excessive then
insert_inflection(data, {{term = lang_data.excessive .. data.pagename}}, "<<excessive>>", "excessive")
end
if pos == "နာမဝိသေသန" then
parse_and_insert_inflection(data, args, "adv", "derived adverb")
end
parse_and_insert_inflection(data, args, "dim", "လဟုတ်စှ်ေ")
end,
}
end
pos_functions["နာမဝိသေသန"] = get_adj_adv_pos("adjective")
pos_functions["ကြိယာဝိသေသန"] = get_adj_adv_pos("adverb")
return export
kinyw84a54ac0p8e3tp09yx1zany90h
396549
396548
2026-06-07T17:39:34Z
咽頭べさ
33
396549
Scribunto
text/plain
local export = {}
local pos_functions = {}
local force_cat = false -- for testing; if true, categories appear in non-mainspace pages
local rfind = mw.ustring.find
local require_when_needed = require("Module:require when needed")
local m_table = require("Module:table")
local en_utilities_module = "Module:en-utilities"
local headword_utilities_module = "Module:headword utilities"
local m_headword_utilities = require_when_needed(headword_utilities_module)
local m_string_utilities = require_when_needed("Module:string utilities")
local glossary_link = require_when_needed(headword_utilities_module, "glossary_link")
local list_param = {list = true, disallow_holes = true}
local boolean_param = {type = "boolean"}
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
local insert = table.insert
local concat = table.concat
local langs_supported = {
["hsb"] = {
peri_comp = "bóle",
sup = "naj",
abs_sup_prefix = "na",
excessive = "pře",
},
["dsb"] = {
peri_comp = "wěcej",
sup = "nej",
},
}
-- Table of all valid aspects.
local valid_aspects = m_table.listToSet {
"impf", "pf", "biasp", "?",
}
local valid_verb_types = {
semelfactive = true,
durative = true,
frequentative = true,
continuative = true,
unidirectional = true,
multidirectional = true,
concrete = "unidirectional",
abstract = "multidirectional",
}
local function track(page)
require("Module:debug").track("hsb-dsb-headword/" .. page)
return true
end
local function replace_hash_with_lemma(term, lemma)
-- If there is a % sign in the lemma, we have to replace it with %% so it doesn't get interpreted as a capture
-- replace expression.
lemma = m_string_utilities.replacement_escape(lemma)
return (term:gsub("#", lemma)) -- discard second retval
end
local function frob_term_with_hash(term, lemma)
if term:find("#") then
term = replace_hash_with_lemma(term, lemma)
end
return term
end
-- Parse and insert an inflection not requiring additional processing into `data.inflections`. The raw arguments come
-- from `args[field]`, which is parsed for inline modifiers. `label` is the label that the inflections are given;
-- sections enclosed in <<...>> are linked to the glossary. `accel` is the accelerator form, or nil.
local function parse_and_insert_inflection(data, args, field, label, accel, frob)
m_headword_utilities.parse_and_insert_inflection {
headdata = data,
forms = args[field],
paramname = field,
splitchar = ",",
label = label,
include_mods = {"t"},
accel = accel and {form = accel} or nil,
frob = function(term)
term = frob_term_with_hash(term, data.pagename)
if frob then
term = frob(term)
end
return term
end,
}
end
-- Parse and return an inflection not requiring additional processing. The raw arguments come from `args[field]`, which
-- is parsed for inline modifiers.
local function parse_inflection(data, paramname, forms)
return m_headword_utilities.parse_term_list_with_modifiers {
paramname = paramname,
forms = forms,
splitchar = ",",
include_mods = {"t"},
frob = function(term)
return frob_term_with_hash(term, data.pagename)
end,
}
end
-- Insert the parsed inflections in `infls` (as parsed by `parse_inflection`) into `data.inflections`, with label
-- `label` and optional accelerator spec `accel`.
local function insert_inflection(data, terms, label, accel)
m_headword_utilities.insert_inflection {
headdata = data,
terms = terms,
label = label,
accel = accel and {form = accel} or nil,
}
end
-- The main entry point.
-- This is the only function that can be invoked from a template.
function export.show(frame)
local iparams = {
[1] = {required = true},
["lang"] = {required = true},
}
local iargs = require("Module:parameters").process(frame.args, iparams)
local poscat = iargs[1]
langcode = iargs.lang
if langcode ~= "hsb" and langcode ~= "dsb" then
error("This module currently only works for lang=hsb and lang=dsb")
end
local lang = require("Module:languages").getByCode(langcode)
local langname = lang:getCanonicalName()
local params = {
["head"] = {list = true},
["nolink"] = {type = "boolean"},
["nolinkhead"] = {type = "boolean", alias_of = "nolink"},
["id"] = {},
["sort"] = {},
["suffix"] = {type = "boolean"},
["nosuffix"] = {type = "boolean"},
["json"] = {type = "boolean"},
["pagename"] = {}, -- for testing
}
if pos_functions[poscat] then
for key, val in pairs(pos_functions[poscat].params) do
params[key] = val
end
end
local parargs = frame:getParent().args
local args = require("Module:parameters").process(parargs, params)
local pagename = args.pagename or mw.loadData("Module:headword/data").pagename
local user_specified_heads = args.head
local heads = user_specified_heads
if args.nolink then
if #heads == 0 then
heads = {pagename}
end
end
local data = {
lang = lang,
langname = langname,
langcode = langcode,
pos_category = poscat,
categories = {},
heads = heads,
user_specified_heads = user_specified_heads,
no_redundant_head_cat = #user_specified_heads == 0,
genders = {},
inflections = {},
categories = {},
pagename = pagename,
id = args.id,
sort_key = args.sort,
force_cat_output = force_cat,
}
data.is_suffix = false
if args.suffix or (
not args.nosuffix and pagename:find("^%-") and poscat ~= "အဆက်လက္ကရဴ" and poscat ~= "ဗီုပြၚ်အဆက်လက္ကရဴ"
) then
data.is_suffix = true
data.pos_category = "အဆက်လက္ကရဴ"
-- local singular_poscat = require(en_utilities_module).singularize(poscat)
-- insert(data.categories, langname .. " " .. singular_poscat .. "-forming suffixes")
-- insert(data.inflections, {label = singular_poscat .. "-forming suffix"})
end
if pos_functions[poscat] then
pos_functions[poscat].func(args, data)
end
if args.json then
return require("Module:JSON").toJSON(data)
end
return require("Module:headword").full_headword(data)
end
local function get_noun_pos(is_proper)
local noun_inflection_specs = {
{"gen", "ဗဳဇဂကူကိုန်ဨကဝုစ်"},
{"du", "မဒုၚ်ယၟုၜါလ္ပာ်"},
{"pl", "ကိုန်ဗဟုဝစ်မဒုၚ်ယၟု"},
{"genpl", "ဗဳဇဂကူကိုန်ဗဟုဝစ်"},
{"m", "မညဳညတ်သၟတ်တးကဵုပုလ္လိၚ်"},
{"f", "မညဳညတ်သၟတ်တးကဵုလိၚ်ဗြဴ"},
{"adj", "<<နာမဝိသေသနဒၞာဲမဆက်စပ်>>"},
{"poss", "<<နာမဝိသေသနဓမံက်ထ္ၜးမဒှ်တၠဒြပ်>>"},
{"dim", "<<လဟုတ်စှ်ေ>>"},
{"aug", "<<ပရေၚ်မဖပေၚ်တိုန်>>"},
{"pej", "<<စမ်ၜတ်ရံၚ်>>"},
{"dem", "<<အပၠေံရုပ်ရာ>>"},
{"fdem", "<<အပၠေံရုပ်ရာ>>ဣတ္တိလိၚ်"},
}
local params = {
[1] = {type = "genders", required = true, template_default = "?"},
["g"] = {list = true, disallow_holes = true, replaced_by = false,
instead = "use multiple comma-separated genders in |1="},
["indecl"] = {type = "boolean"},
}
for _, spec in ipairs(noun_inflection_specs) do
local param, desc = unpack(spec)
params[param] = list_param
end
return {
params = params,
func = function(args, data)
-- Compute allowed genders, and map incomplete genders to specs with a "?" in them.
local masc_genders = {"m", "mf", "mfbysense"}
local non_masc_genders = {false, "f", "n"}
local hsb_masc_animacies = {false, "in", "anml", "pr"}
local dsb_masc_animacies = {false, "in", "an"}
local numbers = {false, "d", "p"}
local allowed_genders = {}
local function insert_genders(genders, animacies, numbers, is_masc)
for _, g in ipairs(genders) do
for _, an in ipairs(animacies) do
for _, num in ipairs(numbers) do
local source_gender_parts = {}
local dest_gender_parts = {}
local function ins_part(part, partname)
if part then
insert(source_gender_parts, part)
insert(dest_gender_parts, part)
elseif partname == "g" and num == false or partname == "an" and is_masc then
-- allow incomplete gender duale tantum and plurale tantum nouns; also allow
-- incomplete animacy for fem/neut; otherwise insert a ? to indicate incomplete
-- gender spec
insert(dest_gender_parts, "?")
end
end
ins_part(g, "g")
ins_part(an, "an")
ins_part(num, "num")
if #source_gender_parts == 0 then
allowed_genders["?"] = "?"
else
allowed_genders[concat(source_gender_parts, "-")] =
concat(dest_gender_parts, "-")
end
end
end
end
end
if data.lang:getCode() == "hsb" then
insert_genders(masc_genders, hsb_masc_animacies, numbers, true)
else
insert_genders(masc_genders, dsb_masc_animacies, numbers, true)
end
insert_genders(non_masc_genders, {false}, numbers, false)
-- allow inanimate dual/plural; no gender distinction here
allowed_genders["in-d"] = "in-d"
allowed_genders["in-p"] = "in-p"
-- Validate and canonicalize genders.
for _, gspec in ipairs(args[1]) do
local g = gspec.spec
local canon_g = allowed_genders[g]
if canon_g then
g = canon_g
else
error("Unrecognized " .. data.langname .. " gender: " .. g)
end
gspec.spec = g
end
data.genders = args[1]
local function process_inflection(label, infls, qs, frob)
infls.label = label
for i, infl in ipairs(infls) do
if frob then
infl = frob(infl)
end
if qs[i] then
infls[i] = {term = infl, q = {qs[i]}}
end
end
if #infls > 0 then
insert(data.inflections, infls)
end
end
if args.indecl then
insert(data.inflections, {label = glossary_link("ပါ်ပါဲဟွံမာန်")})
insert(data.categories, "နာမ်" .. data.langname .. "နကဵုပါ်ပါဲထောံဟွံမာန်ဂမၠိုၚ်")
end
-- Parse and insert an inflection not requiring additional processing into `data.inflections`. The raw
-- arguments come from `args[field]`, which is parsed for inline modifiers. `label` is the label that the
-- inflections are given, which is linked to the glossary if preceded by * (which is removed).
local function handle_infl(field, label)
parse_and_insert_inflection(data, args, field, label)
end
-- Process all inflections.
for _, spec in ipairs(noun_inflection_specs) do
local param, desc = unpack(spec)
handle_infl(param, desc)
end
end
}
end
pos_functions["နာမ်"] = get_noun_pos(false)
pos_functions["နာမ်မကိတ်ညဳ"] = get_noun_pos(true)
pos_functions["ကြိယာ"] = {
params = {
[1] = {type = "genders", default = "?"},
["type"] = {set = valid_verb_types},
["pf"] = list_param,
["impf"] = list_param,
},
func = function(args, data)
-- Validate genders.
for _, gspec in ipairs(args[1]) do
local g = gspec.spec
if not valid_aspects[g] then
error("Unrecognized " .. data.langname .. " aspect: " .. g)
end
end
data.genders = args[1]
if args.type then
m_headword_utilities.insert_fixed_inflection {
headdata = data,
label = ("<<%s>>"):format(args.type),
}
insert(data.categories, ("%s %s verbs"):format(data.langname, args.type))
end
parse_and_insert_inflection(data, args, "pf", "perfective")
parse_and_insert_inflection(data, args, "impf", "imperfective")
end,
}
local function get_adj_adv_pos(pos)
local params = {
[1] = list_param,
["dim"] = list_param,
["sup"] = list_param,
["nodefsup"] = boolean_param,
}
if pos == "နာမဝိသေသန" then
params["adv"] = list_param
params["indecl"] = boolean_param
end
return {
params = params,
func = function(args, data)
if pos == "adjective" and args.indecl then
insert(data.inflections, {label = glossary_link("ပါ်ပါဲဟွံမာန်")})
insert(data.categories, "နာမဝိသေသန" .. data.langname .. "နကဵုပါ်ပါဲထောံဟွံမာန်ဂမၠိုၚ်")
end
local default_sups = {}
local default_abs_sups = {}
local comps = parse_inflection(data, 1, args[1])
local is_comparable = false
local lang_data = langs_supported[data.langcode]
if comps[1] then
if comps[1].term == "-" then
if not comps[2] then
m_headword_utilities.insert_fixed_inflection {
headdata = data,
label = "not <<comparable>>",
originating_term = comps[1],
}
insert(data.categories, data.pos_category .. data.langname .. "မတော်ဟွံဂွံဂမၠိုၚ်")
else
m_headword_utilities.insert_fixed_inflection {
headdata = data,
label = "not generally <<comparable>>",
originating_term = comps[1],
}
end
table.remove(comps, 1)
end
for i, comp in ipairs(comps) do
if not is_comparable then
is_comparable = true
insert(data.categories, data.pos_category .. data.langname .. "မတော်ဂွံဂမၠိုၚ်")
end
if comp.term == "peri" then
if not lang_data.peri_comp then
error("Don't know how to form periphrastic comparatives for " .. data.langname)
end
comp.term = ("[[%s]] [[%s]]"):format(lang_data.peri_comp, data.pagename)
if lang_data.sup then
local default_sup = m_table.shallowCopy(comp)
default_sup.term = ("[[%s%s]] [[%s]]"):format(lang_data.sup, lang_data.peri_comp,
data.pagename)
insert(default_sups, default_sup)
if lang_data.abs_sup_prefix then
local default_abs_sup = m_table.shallowCopy(comp)
default_abs_sup.term = ("[[%s%s%s]] [[%s]]"):format(lang_data.abs_sup_prefix,
lang_data.sup, lang_data.peri_comp, data.pagename)
insert(default_abs_sups, default_abs_sup)
end
end
else
if lang_data.sup then
local default_sup = m_table.shallowCopy(comp)
default_sup.term = ("%s%s"):format(lang_data.sup, comp.term)
insert(default_sups, default_sup)
if lang_data.abs_sup_prefix then
local default_abs_sup = m_table.shallowCopy(comp)
default_abs_sup.term = ("%s%s%s"):format(lang_data.abs_sup_prefix, lang_data.sup,
comp.term)
insert(default_abs_sups, default_abs_sup)
end
end
end
end
end
insert_inflection(data, comps, "ပတဝ်ပတုပ်ရံၚ်", "ပတဝ်ပတုပ်ရံၚ်")
local sups = parse_inflection(data, "sup", args.sup)
if not sups[1] then
sups = args.nodefsup and {} or {{term = "+"}}
end
local combined_sups = {}
for _, sup in ipairs(sups) do
if sup.term == "+" then
for _, def_sup in ipairs(default_sups) do
def_sup = m_table.shallowCopy(def_sup)
m_headword_utilities.combine_termobj_qualifiers_labels(def_sup, sup)
insert(combined_sups, def_sup)
end
else
insert(combined_sups, sup)
end
end
-- Compute the absolute superlative before inserting any inflections because the process of inserting
-- inflections may modify the superlatives that we base the absolute superlatives off of.
local combined_abs_sups = {}
if lang_data.abs_sup_prefix then
for _, sup in ipairs(sups) do
if sup.term == "+" then
for _, def_abs_sup in ipairs(default_abs_sups) do
def_abs_sup = m_table.shallowCopy(def_abs_sup)
m_headword_utilities.combine_termobj_qualifiers_labels(def_abs_sup, sup)
insert(combined_abs_sups, def_abs_sup)
end
else
sup = m_table.shallowCopy(sup)
if sup.term:find("%[%[") then
sup.term = "[[" .. lang_data.abs_sup_prefix .. sup.term:sub(3)
else
sup.term = lang_data.abs_sup_prefix .. sup.term
end
insert(combined_abs_sups, sup)
end
end
end
insert_inflection(data, combined_sups, "သဒ္ဒာ", "သဒ္ဒာ")
insert_inflection(data, combined_abs_sups, "<<သဒ္ဒာမဍိုက်ပေၚ်အိုတ်>>", "သဒ္ဒာမဍိုက်ပေၚ်အိုတ်")
if is_comparable and lang_data.excessive then
insert_inflection(data, {{term = lang_data.excessive .. data.pagename}}, "<<excessive>>", "excessive")
end
if pos == "နာမဝိသေသန" then
parse_and_insert_inflection(data, args, "adv", "derived adverb")
end
parse_and_insert_inflection(data, args, "dim", "လဟုတ်စှ်ေ")
end,
}
end
pos_functions["နာမဝိသေသန"] = get_adj_adv_pos("adjective")
pos_functions["ကြိယာဝိသေသန"] = get_adj_adv_pos("adverb")
return export
r52p7km8foxjvypp8su2oblexu09o6t
မဝ်ဂျူ:template cat
828
282512
396486
395076
2026-06-07T13:20:41Z
咽頭べさ
33
396486
Scribunto
text/plain
-- Author: Benwing
local export = {}
local require_when_needed = require("Module:require when needed")
local is_callable = require_when_needed("Module:fun", "is_callable")
local format_categories = require_when_needed("Module:utilities", "format_categories")
local parse_interface_module = "Module:parse interface"
local m_string_utilities = require("Module:string utilities")
local und_lang = require("Module:languages").getByCode("und", true)
local ugsub = m_string_utilities.gsub
local ufind = m_string_utilities.find
local split = m_string_utilities.split
local insert = table.insert
local concat = table.concat
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
-- This table detects the category type of the template given its name. When this is invoked, the language code has
-- already been removed from the beginning, and anything starting with a slash has been truncated. The entries are
-- processed in order and are two-element lists of Lua patterns (anchored on both sides; beware of hyphens, which need
-- to be %-escaped) and the category type to use. The category types themselves are mapped to categories in
-- category_type_to_category.
local detect_category_type_list = {
-- order is important here
-- nouns, proper nouns, pronouns
-- (1) unambiguous decl/infl templates
{"decl%-.*proper.*", "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏနာမ်"},
{"infl%-.*proper.*", "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏနာမ်"},
{"decl%-.*pron.*", "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏသဗ္ဗနာမ်"},
{"infl%-.*pron.*", "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏသဗ္ဗနာမ်"},
{"decl%-noun.*", "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏနာမ်"},
{"infl%-noun.*", "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏနာမ်"},
-- (2) nouns
{"noun", "လာၚ်က္ဍိုပ်မအရေဝ်"},
{"noun[ -]form", "လာၚ်က္ဍိုပ်မအရေဝ်"},
{"noun[ -]pl", "လာၚ်က္ဍိုပ်မအရေဝ်"},
{"noun[ -]plonly", "လာၚ်က္ဍိုပ်မအရေဝ်"},
-- Some languages, e.g. Urdu, have inflection templates called e.g. [[Template:ur-noun-f-ī]]. They should be called
-- [[Template:ur-decl-noun-f-ī]] but we can autodetect them if we exclude the likely cases that are not declension
-- templates.
{"noun%-.*", "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏနာမ်"},
{"ndecl", "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏနာမ်"},
{"ndecl%-.*", "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏနာမ်"},
-- (3) proper nouns
{"proper[ -]?noun", "လာၚ်က္ဍိုပ်မအရေဝ်"},
{"proper[ -]?noun[ -]form", "လာၚ်က္ဍိုပ်မအရေဝ်"},
{"proper[ -]?noun[ -]pl", "လာၚ်က္ဍိုပ်မအရေဝ်"},
{"proper[ -]?noun[ -]plonly", "လာၚ်က္ဍိုပ်မအရေဝ်"},
{"pnoun", "လာၚ်က္ဍိုပ်မအရေဝ်"},
{"pnoun[ -]form", "လာၚ်က္ဍိုပ်မအရေဝ်"},
{"pnoun[ -]pl", "လာၚ်က္ဍိုပ်မအရေဝ်"},
{"pnoun[ -]plonly", "လာၚ်က္ဍိုပ်မအရေဝ်"},
{"propn", "လာၚ်က္ဍိုပ်မအရေဝ်"},
{"propn[ -]form", "လာၚ်က္ဍိုပ်မအရေဝ်"},
{"propn[ -]pl", "လာၚ်က္ဍိုပ်မအရေဝ်"},
{"propn[ -]plonly", "လာၚ်က္ဍိုပ်မအရေဝ်"},
-- See above for inflection templates without 'decl' or 'infl' in them.
{"proper[ -]?noun%-.*", "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏနာမ်"},
{"pnoun%-.*", "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏနာမ်"},
{"propn%-.*", "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏနာမ်"},
-- (4) pronouns
{"pron", "လာၚ်က္ဍိုပ်မအရေဝ်"},
{"pronoun", "လာၚ်က္ဍိုပ်မအရေဝ်"},
{"pron[ -]form", "လာၚ်က္ဍိုပ်မအရေဝ်"},
{"pronoun[ -]form", "လာၚ်က္ဍိုပ်မအရေဝ်"},
{"prondecl", "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏသဗ္ဗနာမ်"},
{"prondecl%-.*", "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏသဗ္ဗနာမ်"},
-- adjectives
{"decl%-adj.*", "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏနာမဝိသေသန"},
{"infl%-adj.*", "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏနာမဝိသေသန"},
{"adj", "လာၚ်က္ဍိုပ်မအရေဝ်"},
{"adjective", "လာၚ်က္ဍိုပ်မအရေဝ်"},
{"adj[ -]form", "လာၚ်က္ဍိုပ်မအရေဝ်"},
{"adjective[ -]form", "လာၚ်က္ဍိုပ်မအရေဝ်"},
{"adj[ -]comp", "လာၚ်က္ဍိုပ်မအရေဝ်"},
{"adjective[ -]comp", "လာၚ်က္ဍိုပ်မအရေဝ်"},
{"adj[ -]sup", "လာၚ်က္ဍိုပ်မအရေဝ်"},
{"adjective[ -]sup", "လာၚ်က္ဍိုပ်မအရေဝ်"},
-- Some languages, e.g. Urdu and Lithuanian, have inflection templates called e.g. [[Template:ur-adj-1]] and
-- [[Template:lt-adj-is]]. They should be called [[Template:ur-decl-adj-1]] and [[Template:lt-decl-adj-is]] but we
-- can autodetect them if we exclude the likely cases that are not declension templates.
{"adj%-.*", "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏနာမဝိသေသန"},
{"adecl", "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏနာမဝိသေသန"},
{"adecl%-.*", "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏနာမဝိသေသန"},
-- verbs; need to avoid including conjunctions
{"verb", "လာၚ်က္ဍိုပ်မအရေဝ်"},
{"conj", "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏကြိယာ"},
{"conj[0-9 -].*", "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏကြိယာ"},
{"conjug.*", "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏကြိယာ"},
{"infl%-verb.*", "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏကြိယာ"},
-- pronunciation
{".*IPA.*", "မပ္တိတ်ရမျာၚ်"},
{"pronunciation", "မပ္တိတ်ရမျာၚ်"},
{"pr", "မပ္တိတ်ရမျာၚ်"},
{"p", "မပ္တိတ်ရမျာၚ်"},
-- form-of
{".*form of", "ဗီုပြၚ်မဆေၚ်စပ်"},
-- pronominal boxes
{".*personal pronouns", "personal pronoun"},
{".*demonstrative.*", "demonstrative"},
{".*interrogative.*", "interrogative"},
{".*possessives", "possessive"},
{".*possessive .*", "possessive"},
{".*reflexives", "reflexive"},
{".*reflexive .*", "reflexive"},
-- these next two should precede 'relative'
{".*correlatives", "pro-form"},
{".*correlative .*", "pro-form"},
{".*relative .*", "relative"},
{".*articles", "article"},
{".*prefixes", "affix"},
{".*suffixes", "affix"},
-- TOC tables
{".*TOC", "TOC"},
-- numbers, numerals
{".*numbers", "number"},
{".*numerals", "number"},
{".*ordinals", "ordinal"},
{".*cardinals", "cardinal"},
{".*digits", "digit"},
-- transliteration
{".*xlit", "orthographic conversion"},
{".*translit", "orthographic conversion"},
-- orthographic and regional variants
{".*variant", "orthographic variant"},
{".*regional", "regional variant"},
-- sign production templates
-- FIXME: We should limit this to sign languages. As it is, we put it near the bottom of the
-- pattern list so it doesn't accidentally override other patterns for non-sign languages.
{"prod .*", "sign production"},
}
-- This table indicates how to convert template category types to categories. It consists of a list of pairs, where the
-- first element is the category type and the second element is a key-value table containing the following keys:
-- * `aliases`: Optional list of aliases for the category type, which can be used when explicitly specifying the type,
-- e.g. {{tcat|hw}} instead of {{tcat|headword-line}}.
-- * `cats`: List of categories to add the template to. Each entry either specifies a ''raw'' category (if the category
-- name begins with "Category:"), a ''full table'' category (if the category name begins with "label:", where what
-- follows specifies the full label without the prefixed language name) or a ''regular label'' category (for other
-- strings, where e.g. if the label is "noun inflection-table", the category name is
-- "LANG noun inflection-table templates"). An entry is either a string, directly specifying the category name, or a
-- key-value table with keys `name` (the category name) and `sort` (how to generate the sort base). By default, the
-- sort base for raw categories is a comma-separated list of the language names (not codes) associated with the
-- template, or the full template name if there are no languages, and the sort base for label categories is the
-- template name minus the initial language code. If this isn't correct, the `sort` field specifies how to compute the
-- sort base. It is either a function of two arguments, the template name and language object, which should return the
-- sort base; or a table of specs telling how to compute the sort base. In the case of a function, the template name
-- passed in is the full name for raw categories, but the name minus any language code prefix in the case of label
-- categories; and for raw categories, a list of all associated language objects is passed in, or {nil} if none, while
-- for label categories, a single language object is passed in. (Label categories can only exist if there are
-- associated languages.) In the case where `sort` is a table of specs, it is a list where each element is a
-- two-element list of a Lua pattern anchored on both sides and the corresponding pattern replacement string. The
-- specs are processed in order.
local category_type_to_category = {
-- Inflection-table templates
{"noun inflection-table", {
aliases = {"nouninfl", "noundecl", "ndecl"},
cats = {{name = "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏနာမ်", sort = {
{"infl%-(.*)", "%1"},
{"decl%-(.*)", "%1"},
{"noun%-(.*)", "%1"},
-- put this twice to catch noun-decl-* and decl-noun-*
{"infl%-(.*)", "%1"},
{"decl%-(.*)", "%1"},
{"noun", "*"},
{"ndecl%-base%-(.*)", " %1"},
{"ndecl%-base", " "},
{"ndecl%-(.*)", "%1"},
{"ndecl", "*"},
{"propndecl%-base%-(.*)", " %1"},
{"propndecl%-base", " "},
{"propndecl%-(.*)", "%1"},
{"propndecl", "*"},
{"proper[ -]?noun%-(.*)", "%1"},
{"propn%-(.*)", "%1"},
{"pnoun%-(.*)", "%1"},
}}},
}},
{"pronoun inflection-table", {
aliases = {"proninfl", "prondecl"},
cats = {{name = "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏသဗ္ဗနာမ်", sort = {
{"infl%-(.*)", "%1"},
{"decl%-(.*)", "%1"},
{"pronoun%-(.*)", "%1"},
{"pron%-(.*)", "%1"},
-- put this twice to catch pron-decl-* and decl-pron-*
{"infl%-(.*)", "%1"},
{"decl%-(.*)", "%1"},
{"pronoun", "*"},
{"pron", "*"},
{"prondecl%-base%-(.*)", " %1"},
{"prondecl%-base", " "},
{"prondecl%-(.*)", "%1"},
{"prondecl", "*"},
}}},
}},
{"article inflection-table", {
aliases = {"artinfl", "artdecl"},
cats = {{name = "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏပစ္စဲ", sort = {
{"infl%-(.*)", "%1"},
{"decl%-(.*)", "%1"},
{"article%-(.*)", "%1"},
{"art%-(.*)", "%1"},
-- put this twice to catch art-decl-* and decl-art-*
{"infl%-(.*)", "%1"},
{"decl%-(.*)", "%1"},
{"article", "*"},
{"art", "*"},
{"artdecl%-base%-(.*)", " %1"},
{"artdecl%-base", " "},
{"artdecl%-(.*)", "%1"},
{"artdecl", "*"},
}}},
}},
{"determiner inflection-table", {
aliases = {"detinfl", "detdecl"},
cats = {{name = "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏဖျေံလဝ်သန္နိဋ္ဌာန်", sort = {
{"infl%-(.*)", "%1"},
{"decl%-(.*)", "%1"},
{"determiner%-(.*)", "%1"},
{"det%-(.*)", "%1"},
-- put this twice to catch det-decl-* and decl-det-*
{"infl%-(.*)", "%1"},
{"decl%-(.*)", "%1"},
{"determiner", "*"},
{"det", "*"},
{"detdecl%-base%-(.*)", " %1"},
{"detdecl%-base", " "},
{"detdecl%-(.*)", "%1"},
{"detdecl", "*"},
}}},
}},
{"preposition inflection-table", {
aliases = {"prepinfl", "prepdecl"},
cats = {{name = "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏဝိဘတ်", sort = {
{"infl%-(.*)", "%1"},
{"decl%-(.*)", "%1"},
{"preposition%-(.*)", "%1"},
{"prep%-(.*)", "%1"},
-- put this twice to catch prep-decl-* and decl-prep-*
{"infl%-(.*)", "%1"},
{"decl%-(.*)", "%1"},
{"preposition", "*"},
{"prep", "*"},
{"prepdecl%-base%-(.*)", " %1"},
{"prepdecl%-base", " "},
{"prepdecl%-(.*)", "%1"},
{"prepdecl", "*"},
{"prepinfl%-base%-(.*)", " %1"},
{"prepinfl%-base", " "},
{"prepinfl%-(.*)", "%1"},
{"prepinfl", "*"},
}}},
}},
{"postposition inflection-table", {
aliases = {"postpinfl", "postpdecl"},
cats = {{name = "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏကဆံၚ်အကာဲအရာ", sort = {
{"infl%-(.*)", "%1"},
{"decl%-(.*)", "%1"},
{"postposition%-(.*)", "%1"},
{"postp%-(.*)", "%1"},
{"post%-(.*)", "%1"},
-- put this twice to catch postp-decl-* and decl-postp-*
{"infl%-(.*)", "%1"},
{"decl%-(.*)", "%1"},
{"postposition", "*"},
{"postp", "*"},
{"post", "*"},
{"postpdecl%-base%-(.*)", " %1"},
{"postpdecl%-base", " "},
{"postpdecl%-(.*)", "%1"},
{"postpdecl", "*"},
{"postpinfl%-base%-(.*)", " %1"},
{"postpinfl%-base", " "},
{"postpinfl%-(.*)", "%1"},
{"postpinfl", "*"},
}}},
}},
{"adjective inflection-table", {
aliases = {"adjinfl", "adjdecl", "adecl"},
cats = {{name = "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏနာမဝိသေသန", sort = {
{"infl%-(.*)", "%1"},
{"decl%-(.*)", "%1"},
{"adj%-(.*)", "%1"},
{"adjective%-(.*)", "%1"},
-- put this twice to catch adj-decl-* and decl-adj-*
{"infl%-(.*)", "%1"},
{"decl%-(.*)", "%1"},
{"adj", "*"},
{"adjective", "*"},
{"adecl%-base%-(.*)", " %1"},
{"adecl%-base", " "},
{"adecl%-(.*)", "%1"},
{"adecl", "*"},
}}},
}},
{"numeral inflection-table", {
aliases = {"numinfl", "numdecl"},
cats = {{name = "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏဂၞန်သၚ်္ချာ", sort = {
{"infl%-(.*)", "%1"},
{"decl%-(.*)", "%1"},
{"numeral%-(.*)", "%1"},
{"number%-(.*)", "%1"},
{"num%-(.*)", "%1"},
-- put this twice to catch num-decl-* and decl-num-*
{"infl%-(.*)", "%1"},
{"decl%-(.*)", "%1"},
{"numeral", "*"},
{"number", "*"},
{"num", "*"},
{"numdecl%-base%-(.*)", " %1"},
{"numdecl%-base", " "},
{"numdecl%-(.*)", "%1"},
{"numdecl", "*"},
}}},
}},
{"nominal inflection-table", {
aliases = {"nominfl", "nomdecl"},
cats = {{name = "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏဆမဒုၚ်ယၟု", sort = {
{"infl%-(.*)", "%1"},
{"decl%-(.*)", "%1"},
{"nominal%-(.*)", "%1"},
{"nom%-(.*)", "%1"},
-- put this twice to catch nom-decl-* and decl-nom-*
{"infl%-(.*)", "%1"},
{"decl%-(.*)", "%1"},
{"nominal", "*"},
{"nom", "*"},
{"nomdecl%-base%-(.*)", " %1"},
{"nomdecl%-base", " "},
{"nomdecl%-(.*)", "%1"},
{"nomdecl", "*"},
}}},
}},
{"verb inflection-table", {
aliases = {"verbinfl", "conj"},
cats = {{name = "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏကြိယာ", sort = {
{"infl%-(.*)", "%1"},
{"verb%-(.*)", "%1"},
-- put this twice to catch verb-infl-* and infl-verb-*
{"infl%-(.*)", "%1"},
{"conj%-base%-(.*)", " %1"},
{"conj%-base", " "},
-- handle conj2, conj1-c, etc.
{"conj%-?(.*)", "%1"},
{"conj", "*"},
}}},
}},
{"adverb inflection-table", {
aliases = {"advinfl"},
cats = {{name = "အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏကြိယာဝိသေသန", sort = {
{"infl%-(.*)", "%1"},
{"decl%-(.*)", "%1"},
{"adverb%-(.*)", "%1"},
{"adv%-(.*)", "%1"},
-- put this twice to catch adv-decl-* and decl-adv-*
{"infl%-(.*)", "%1"},
{"decl%-(.*)", "%1"},
{"adverb", "*"},
{"adv", "*"},
{"advinfl%-base%-(.*)", " %1"},
{"advinfl%-base", " "},
{"advinfl%-(.*)", "%1"},
{"advinfl", "*"},
}}},
}},
-- Inflection-table subtemplates
{"inflection-table sub", {
aliases = {"inflsub"},
cats = {"label:inflection-table subtemplates"},
}},
-- Definition templates
-- Headword-line templates
{"headword-line", {
aliases = {"hw", "headword"},
cats = {"လာၚ်က္ဍိုပ်မအရေဝ်"},
}},
-- Definition templates
{"definition", {
aliases = {"def", "defn"},
cats = {"definition"},
}},
{"form-of", {
aliases = {"form of"},
cats = {"ဗီုပြၚ်မဆေၚ်စပ်"},
}},
-- Etymology and pronunciation templates
{"etymology", {
aliases = {"etym"},
cats = {"etymology"},
}},
{"morphology", {
aliases = {"morph"},
cats = {"etymology", "ကဏ္ဍ:ထာမ်ပလိက်ဗီုပြၚ်သဏ္ဌာန်ဗေဒအရေဝ်ဘာသာမချိုတ်ပၠိုတ်ဂမၠိုၚ်"},
}},
{"pronunciation", {
aliases = {"pron"},
cats = {"မပ္တိတ်ရမျာၚ်"},
}},
{"sign production", {
aliases = {"signprod"},
cats = {{name = "sign production", sort = {
{"prod (.*)", "%1"},
}}},
}},
-- Pseudo-namespace templates
{"reference", {
aliases = {"ref"},
cats = {{name = "နိဿဲ", allow_etym = true}},
}},
{"quotation", {
aliases = {"quote"},
cats = {"quotation"},
}},
{"usage", {
cats = {"usage"},
}},
{"list", {
cats = {"list"},
}},
{"auto-table", {
aliases = {"table"},
cats = {"auto-table"},
}},
-- Navigation templates
-- Pro-form box templates
{"pro-form", {
aliases = {"pro-forms"},
cats = {"ပွမပ္ညုၚ်ထ္ၜးဝေါဟာ", "ကဏ္ဍ:ထာမ်ပလိက်ကဠာဗီုပြၚ်ပရုဝ်ဂမၠိုၚ်"},
}},
{"adposition", {
aliases = {"adpositions", "preposition", "prepositions", "postposition", "postpositions"},
cats = {"ပွမပ္ညုၚ်ထ္ၜးဝေါဟာ", "ကဏ္ဍ:ထာမ်ပလိက်ကဠာမဗပေၚ်စုတ်အကာဲအရာဂမၠိုၚ်"},
}},
{"affix", {
aliases = {"affixes", "prefix", "prefixes", "suffix", "suffixes"},
cats = {"ပွမပ္ညုၚ်ထ္ၜးဝေါဟာ", "ကဏ္ဍ:ထာမ်ပလိက်ကဠာဗီုပြၚ်ပရုဝ်ပရေၚ်မဆက်ဖျပ်စုတ်ဂမၠိုၚ်"},
}},
{"article", {
aliases = {"articles"},
cats = {"ပွမပ္ညုၚ်ထ္ၜးဝေါဟာ", "ကဏ္ဍ:ထာမ်ပလိက်ကဠာပစ္စဲဆေၚ်စပ်ကဵုသဒ္ဒာဂမၠိုၚ်"},
}},
{"demonstrative", {
aliases = {"demonstratives"},
cats = {"ပွမပ္ညုၚ်ထ္ၜးဝေါဟာ", "ကဏ္ဍ:ထာမ်ပလိက်ကဠာပရေၚ်စၞောန်ထ္ၜးသာဓကရုပ်ရာဂမၠိုၚ်"},
}},
{"interrogative", {
aliases = {"interrogatives"},
cats = {"ပွမပ္ညုၚ်ထ္ၜးဝေါဟာ", "ကဏ္ဍ:ထာမ်ပလိက်ကဠာစၟဳစၟတ်သၟာန်ဇာန်ဂမၠိုၚ်"},
}},
{"personal pronoun", {
aliases = {"perspron", "personal pronouns"},
cats = {"ပွမပ္ညုၚ်ထ္ၜးဝေါဟာ", "ကဏ္ဍ:ထာမ်ပလိက်ကဠာသဗ္ဗနာမ်ပရေၚ်ပူဂဵုဂမၠိုၚ်"},
}},
{"possessive", {
aliases = {"possessives"},
cats = {"ပွမပ္ညုၚ်ထ္ၜးဝေါဟာ", "ကဏ္ဍ:ထာမ်ပလိက်ကဠာမဆက်စပ်သဗ္ဗနာမ်ကဵုဖျေံလဝ်သန္နိဋ္ဌာန်ဂမၠိုၚ်"},
}},
{"reflexive", {
aliases = {"reflexives"},
cats = {"ပွမပ္ညုၚ်ထ္ၜးဝေါဟာ", "ကဏ္ဍ:ထာမ်ပလိက်ကဠာမဆက်စပ်သဗ္ဗနာမ်ကဵုဖျေံလဝ်သန္နိဋ္ဌာန်ဂမၠိုၚ်"},
}},
{"relative", {
aliases = {"relatives"},
cats = {"ပွမပ္ညုၚ်ထ္ၜးဝေါဟာ", "ကဏ္ဍ:ထာမ်ပလိက်ကဠာမဆက်စပ်သဗ္ဗနာမ်ကဵုဖျေံလဝ်သန္နိဋ္ဌာန်ဂမၠိုၚ်"},
}},
{"navigation", {
-- miscellaneous navigation box templates like {{eu-aux verbs}}, {{pt-forms of address}}
aliases = {"nav"},
cats = {"ပွမပ္ညုၚ်ထ္ၜးဝေါဟာ"},
}},
{"TOC", {
cats = {{name = "navigation", sort = {
{"categoryTOC", "TOC"},
}}, "ကဏ္ဍ:ထာမ်ပလိက်TOCဂမၠိုၚ်"},
}},
{"number", {
aliases = {"numbers"},
cats = {"ပွမပ္ညုၚ်ထ္ၜးဝေါဟာ", "ကဏ္ဍ:ထာမ်ပလိက်ဂၞန်အရေဝ်ဘာသာမချိုတ်ပၠိုတ်ဂမၠိုၚ်"},
}},
{"cardinal", {
aliases = {"cardinals"},
cats = {"ပွမပ္ညုၚ်ထ္ၜးဝေါဟာ", "ကဏ္ဍ:ထာမ်ပလိက်ဂၞန်အရေဝ်ဘာသာမချိုတ်ပၠိုတ်ဂမၠိုၚ်"},
}},
{"ordinal", {
aliases = {"ordinals"},
cats = {"ပွမပ္ညုၚ်ထ္ၜးဝေါဟာ", "ကဏ္ဍ:ထာမ်ပလိက်ဂၞန်အရေဝ်ဘာသာမချိုတ်ပၠိုတ်ဂမၠိုၚ်"},
}},
{"digit", {
aliases = {"digits"},
cats = {"list", "ကဏ္ဍ:ထာမ်ပလိက်ဂၞန်အရေဝ်ဘာသာမချိုတ်ပၠိုတ်ဂမၠိုၚ်"},
}},
-- Entry templates
{"entry", {
cats = {"ပရေၚ်ပၠောပ်စုတ်စရၚ်"},
}},
-- Orthographic conversion templates (e.g. for converting between scripts)
{"orthographic conversion", {
aliases = {"transliteration", "translit", "xlit", "orthconv", "scriptconv"},
cats = {"conversion", "ကဏ္ဍ:ထာမ်ပလိက်ပရေၚ်အပြံၚ်အလှာဲဂမၠိုၚ်"},
}},
-- Orthographic and regional variant templates (for displaying orthographic, script and/or regional variants)
{"orthographic variant", {
aliases = {"orthvar", "scriptvar"},
-- Currently we categorize orthographic and regional variants the same but we could split them if needed
cats = {"ပွမပ္ညုၚ်ထ္ၜးဝေါဟာ", "ကဏ္ဍ:ထာမ်ပလိက်တၞဟ်ခြာဆေၚ်စပ်ကဵုအဝ်တိုဝ်ဂရာပ်ဗောတ်ကဵုဒေသဂမၠိုၚ်"},
}},
{"regional variant", {
aliases = {"regvar"},
cats = {"ပွမပ္ညုၚ်ထ္ၜးဝေါဟာ", "ကဏ္ဍ:ထာမ်ပလိက်တၞဟ်ခြာဆေၚ်စပ်ကဵုအဝ်တိုဝ်ဂရာပ်ဗောတ်ကဵုဒေသဂမၠိုၚ်"},
}},
-- Internal link templates
{"link", {
cats = {"လေန်", "ကဏ္ဍ:ထာမ်ပလိက်လေန်မစှ်ေအပ္ဍဲအရေဝ်ဘာသာမချိုတ်ပၠိုတ်ဂမၠိုၚ်"},
}},
}
local category_type_to_category_map = {}
for _, category_type_to_category_spec in ipairs(category_type_to_category) do
local category_type, props = unpack(category_type_to_category_spec)
category_type_to_category_map[category_type] = props
if props.aliases then
for _, alias in ipairs(props.aliases) do
category_type_to_category_map[alias] = props
end
end
end
-- Split an argument on comma, but not comma followed by whitespace; split off sort base after a colon.
local function split_on_comma_and_split_off_sort_base(val)
local cattypes
if val:find(",") then
-- Don't optimize more than this because there can be commas backslashed, inside of links or followed by
-- whitespace that don't cause splitting.
cattypes = require(parse_interface_module).split_on_comma(val)
else
cattypes = {val}
end
for i, cattype_spec in ipairs(cattypes) do
if cattype_spec:find(":") then
local cattype, sort_base = cattype_spec:match("^(.-):(.*)$")
sort_base = sort_base:gsub("_", " ")
cattypes[i] = {name = cattype, sort_base = sort_base}
end
end
return cattypes
end
local function get_lang_or_script(code)
return code == "-" and code or
require("Module:languages").getByCode(code, nil, "allow etym") or
require("Module:languages").getByCode(code .. "-pro", nil, "allow etym") or
require("Module:scripts").getByCode(code)
end
local function obj_code(obj)
if obj == "-" then
return obj
end
return obj:getCode()
end
local function get_prefixed_obj(after_prefix)
return after_prefix:match("^(%a[%a-]*%a):(.+)$")
end
local function get_suffixed_obj(after_prefix)
local rest, objcode = after_prefix:match("^(.+)/(%a[%a-]*%a)$")
return objcode, rest
end
local pseudo_namespace_templates = {
{"R:", {
category_type = "နိဿဲ",
get_obj_and_rest = get_prefixed_obj,
}},
{"RQ:", {
category_type = "quotation",
get_obj_and_rest = get_prefixed_obj,
}},
{"U:", {
category_type = "usage",
get_obj_and_rest = get_prefixed_obj,
}},
{"list:", {
category_type = "list",
get_obj_and_rest = get_suffixed_obj,
}},
{"table:", {
category_type = "auto-table",
get_obj_and_rest = get_suffixed_obj,
}},
}
local function infer_lang_or_script_code_and_category_type(name)
if name:find(":") then -- only check for pseudo-namespace prefix when a colon is present
for _, pseudo_namespace_spec in ipairs(pseudo_namespace_templates) do
local prefix, props = unpack(pseudo_namespace_spec)
local after_prefix = name:match("^" .. prefix .. "(.+)$")
if after_prefix then
local objcode, rest = props.get_obj_and_rest(after_prefix)
local obj
if objcode then
obj = get_lang_or_script(objcode) -- may return nil
if not obj then
rest = after_prefix
end
else
rest = after_prefix
end
return obj, rest, props.category_type
end
end
end
local hyphen_parts = split(name, "%-")
for i = #hyphen_parts - 1, 1, -1 do
local code = concat(hyphen_parts, "-", 1, i)
local obj = get_lang_or_script(code)
if obj then
local rest = concat(hyphen_parts, "-", i + 1)
return obj, rest, nil
end
end
return nil, name, nil
end
local function process_sortbase_specs(sortbase, specs)
for _, spec in ipairs(specs) do
local from, to = unpack(spec)
sortbase = ugsub(sortbase, "^" .. from .. "$", to)
end
return sortbase
end
local function template_name_minus_langcode_to_category_type(name)
for _, type_spec in ipairs(detect_category_type_list) do
local pattern, intended_type = unpack(type_spec)
if ufind(name, "^" .. pattern .. "$") then
return intended_type
end
end
return nil
end
local function compute_categories_for_template(full_template_name, template_name_minus_langcode, category_type,
langs_or_scripts)
local overriding_sort_base
if type(category_type) == "table" then
overriding_sort_base = category_type.sort_base
category_type = category_type.name
end
if not category_type_to_category_map[category_type] then
error("Unrecognized template category type: " .. category_type)
end
local props = category_type_to_category_map[category_type]
if not props.cats then
error("Internal error: No categories given for category type: " .. category_type)
end
local categories = {}
for _, catspec in ipairs(props.cats) do
if type(catspec) == "string" then
catspec = {name = catspec}
end
local rawcat = catspec.name:match("^ကဏ္ဍ:(.*)")
if rawcat then
local sortbase
-- User-specified sort base does not apply to raw categories, which have a different sort key format
-- than language-specific categories.
if not catspec.sort then
if langs_or_scripts then
local langnames = {}
for _, lang_or_sc in ipairs(langs_or_scripts) do
insert(langnames, lang_or_sc:getCanonicalName()) -- FIXME: or lang:getFullName()?
end
sortbase = concat(langnames, ",")
else
sortbase = full_template_name
end
elseif is_callable(catspec.sort) then
sortbase = catspec.sort(full_template_name, langs_or_scripts)
else
sortbase = process_sortbase_specs(full_template_name, catspec.sort)
end
insert(categories, {cat = rawcat, lang = und_lang, sort_base = sortbase})
elseif langs_or_scripts then
for _, lang_or_sc in ipairs(langs_or_scripts) do
local sortbase
if overriding_sort_base then
sortbase = overriding_sort_base
elseif not catspec.sort then
sortbase = template_name_minus_langcode
elseif is_callable(catspec.sort) then
sortbase = catspec.sort(template_name_minus_langcode, lang_or_sc)
else
sortbase = process_sortbase_specs(template_name_minus_langcode, catspec.sort)
end
if lang_or_sc:hasType("script") then
insert(categories, {
cat = ("ထာမ်ပလိက်%sဂမၠိုၚ်"):format(lang_or_sc:getCategoryName()), lang = und_lang, sc = lang_or_sc,
sort_base = sortbase,
})
else
local cat
local full_label = catspec.name:match("^label:(.*)$")
local lang_name = catspec.allow_etym and lang_or_sc:getCanonicalName() or lang_or_sc:getFullName()
if full_label then
cat = ("%s%s"):format(lang_name, full_label)
else
cat = ("ထာမ်ပလိက်%s%sဂမၠိုၚ်"):format(lang_name, catspec.name)
end
insert(categories, {
cat = cat, lang = lang_or_sc:getFull(), sort_base = sortbase,
})
end
end
end
end
if not categories[1] then
error(("No categories generated for template [[Template:%s]] with category type '%s'"):format(
full_template_name, category_type))
end
return categories
end
--[==[
Main entry point.
]==]
function export.categorize(frame)
local params = {
[1] = {}, -- comma-separated list of category types; by default, inferred from template name
lang = {}, -- comma-separated list of languages; by default, inferred from template name
["pagename"] = {}, -- for testing
["json"] = {type = "boolean"}, -- for testing
}
local parent_args = frame:getParent().args
args = require("Module:parameters").process(parent_args, params)
local category_specs = {}
local function insert_cat(cat, sort_key)
for _, existing_cat in ipairs(category_specs) do
if existing_cat.cat == cat then
return
end
end
insert(category_specs, {cat = cat, sort_key = sort_key})
end
local pagename = args.pagename
if not pagename then
title = mw.title.getCurrentTitle()
pagename = title.fullText
end
if pagename:find("/documentation$") or pagename:find("/documentation/") then
return ""
end
if pagename:find("^ထာမ်ပလိက်:User:") then
insert_cat("ထာမ်ပလိက်ကဠာဗ္တဳညးလွပ်ဂမၠိုၚ်", (pagename:gsub("^ထာမ်ပလိက်:User:", "")))
elseif pagename:find("^ညးလွပ်:") then
insert_cat("ထာမ်ပလိက်ကဠာဗ္တဳညးလွပ်ဂမၠိုၚ်", (pagename:gsub("^ညးလွပ်:", "")))
else
if not pagename:find("^ထာမ်ပလိက်:") then
error(("This template should only be used in the Template namespace, not on page '%s'"):format(pagename))
end
local full_template_name = pagename:gsub("^ထာမ်ပလိက်:", "")
local rootpage = full_template_name:gsub("/.*", "")
if full_template_name:find("/sandbox") then
insert_cat("ထာမ်ပလိက်ကဠာဗ္တဳဂမၠိုၚ်", full_template_name)
elseif full_template_name:find("^sandbox/") then
insert_cat("ထာမ်ပလိက်ကဠာဗ္တဳဂမၠိုၚ်", full_template_name:gsub("^sandbox/", ""))
else
local template_objs
if args.lang == "-" then
template_objs = false
elseif args.lang then
template_objs = {}
for _, code in ipairs(split(args.lang, ",")) do
-- We need to have an indicator of families because we allow bare family codes to stand for proto-languages.
if code:find("^fam:") then
code = code:gsub("^fam:", "")
local family = require("Module:families").getByCode(code) or
error(("Unrecognized family code '%s' in [[Module:template cat]]"):format(code))
local descendants = family:getDescendantCodes()
for _, desc in ipairs(descendants) do
local obj = get_lang_or_script(desc)
if obj then
-- make sure we skip families without proto-languages
insert(template_objs, obj)
end
end
else
local obj = get_lang_or_script(code)
if not obj then
error(("Unrecognized language or script code '%s'"):format(code))
end
insert(template_objs, obj)
end
end
end
local cattypes
if args[1] then
cattypes = split_on_comma_and_split_off_sort_base(args[1])
end
local inferred_obj, inferred_rest, inferred_cattype =
infer_lang_or_script_code_and_category_type(rootpage)
if template_objs == nil or not cattypes then
if template_objs == nil then
if not inferred_obj then
if not inferred_cattype then
error(("Unable to infer language or script from template root page '%s' for template '%s'; specify lang/script and type explicitly"):format(
rootpage, pagename))
else
error(("Unable to infer language or script from template root page '%s' for template '%s', inferred category type '%s'; specify lang/script explicitly"):format(
rootpage, pagename, inferred_cattype))
end
else
template_objs = {inferred_obj}
end
end
if not cattypes then
inferred_cattype = inferred_cattype or
template_name_minus_langcode_to_category_type(inferred_rest)
if not inferred_cattype then
error(("Unable to infer template category type from template remainder (after stripping langcode) '%s' for template '%s'; specify type explicitly"):format(
inferred_rest, pagename))
end
cattypes = {inferred_cattype}
end
end
for _, cattype in ipairs(cattypes) do
local cats = compute_categories_for_template(full_template_name, inferred_rest, cattype, template_objs)
for _, cat in ipairs(cats) do
insert(category_specs, cat)
end
end
end
end
-- We are returning categories for templates or user-space pages, so we need to force the output.
local retval = format_categories(category_specs, nil, nil, nil, "force_output")
if args.json then
return require("Module:JSON").toJSON {
category_specs = category_specs,
retval = mw.text.nowiki(retval),
}
else
return retval
end
end
--[==[Table used in the documentation to {{tl|template cat}}.]==]
function export.pattern_to_category_type_table()
local parts = {}
local function ins(text)
insert(parts, text)
end
ins('{|class="wikitable"')
ins("! ဗီုပြၚ် !! ကဏ္ဍသ္ဂုတ်သွာတ်လဝ်က္ဍိုပ်လိက်")
for _, detect_spec in ipairs(detect_category_type_list) do
local pattern, category_type = unpack(detect_spec)
ins("|-")
ins(("| <code>%s</code> || <code>%s</code>"):format(pattern, category_type))
end
ins("|}")
return concat(parts, "\n")
end
--[==[Table used in the documentation to {{tl|template cat}}.]==]
function export.category_type_to_category_table()
local parts = {}
local function ins(text)
insert(parts, text)
end
local category_types = {}
local category_type_to_aliases = {}
for _, category_type_to_category_spec in ipairs(category_type_to_category) do
local category_type, props = unpack(category_type_to_category_spec)
insert(category_types, category_type)
category_type_to_aliases[category_type] = {}
if props.aliases then
for _, alias in ipairs(props.aliases) do
insert(category_type_to_aliases[category_type], alias)
end
table.sort(category_type_to_aliases[category_type])
end
end
table.sort(category_types)
local function get_category_type_categories(category_type)
local cats = {}
for _, catspec in ipairs(category_type_to_category_map[category_type].cats) do
if type(catspec) == "string" then
catspec = {name = catspec}
end
local cat = catspec.name
if cat:find("^ကဏ္ဍ:") then
insert(cats, ("<code>%s</code>"):format((cat:gsub("^ကဏ္ဍ:", ""))))
elseif cat:find("^label:") then
insert(cats, ("<code><var>LANG</var> %s</code>"):format((cat:gsub("^label:", ""))))
else
insert(cats, ("<code><var>LANG</var> ထာမ်ပလိက်%sဂမၠိုၚ်</code>"):format(cat))
end
end
return concat(cats, ", ")
end
ins('{|class="wikitable"')
ins("! Category type !! Canonical category type !! Categories")
for _, category_type in ipairs(category_types) do
ins("|-")
ins(("| <code>'''%s'''</code> || ''(same)'' || <code>%s</code>"):format(
category_type, get_category_type_categories(category_type)))
for _, alias in ipairs(category_type_to_aliases[category_type]) do
ins("|-")
ins(("| <code>%s</code> || <code>'''%s'''</code> || <code>%s</code>"):format(
alias, category_type, get_category_type_categories(category_type)))
end
end
ins("|}")
return concat(parts, "\n")
end
return export
lniihw6qwcofgn27thdfxafpbj1tals
ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာမနွံလေန်ပ္တိတ်ရမျာၚ်ဂမၠိုၚ်
14
282527
396463
396380
2026-06-07T12:01:14Z
Intobesa.bot
1035
Intobesa.bot ပြံင်ပဆုဲလဝ် မုက်လိက် [[ကဏ္ဍ:ဝေါဟာသလဝ်ဝေန်နဳယျာမနွံလေန်ပ္တိတ်ရမျာၚ်ဂမၠိုၚ်]] ဇရေင် [[ကဏ္ဍ:ဝေါဟာသၠဝ်ဝေန်နဳယျာမနွံလေန်ပ္တိတ်ရမျာၚ်ဂမၠိုၚ်]] သီုကဵု ဟွံဂွံ ဂိုင်စွံလဝ် မကလေင်ပညုင်: Bot: ယၟုသလဝ်ဝေန်နဳယျာမပြံၚ်လှာဲထောံခ္ဍံက်လိက်နကဵုသၠဝ်ဝေန်နဳယျာအပ္ဍဲယၟုကဏ္ဍဂမၠိုၚ်
396380
wikitext
text/x-wiki
[[:ကဏ္ဍ:ဒၞာဲလုပ်အဝေါၚ်ကဵုပၟိက်|ဒၞာဲလုပ်အဝေါၚ်ကဵုပၟိက်]] » [[:ကဏ္ဍ:အရေဝ်ဘာသာအိုတ်သီုဂမၠိုၚ်|အရေဝ်ဘာသာအိုတ်သီုဂမၠိုၚ်]] » [[:ကဏ္ဍ:ဘာသာသၠဝ်ဝေန်နဳယျာ|သၠဝ်ဝေန်နဳယျာ]] » '''{{PAGENAME}}'''
:ဝေါဟာသၠဝ်ဝေန်နဳယျာအဝေါၚ်မဆေၚ်စပ်မပတိတ်ရမျာၚ်ပ္ဍဲနကဵုဗီုပြၚ်ဖှာၚ်အဝ်ဒဳယဝ်။
သွက်မအာတ်မိက်ဆေၚ်စပ်ကဵုကဏ္ဍတဏအ်၊ ဗဵုရံၚ် ကဏ္ဍ:မအာတ်မိက်သွက်ရမျာၚ်အဝ်ဒဳယဝ်ပ္ဍဲစရၚ်သၠဝ်ဝေန်နဳယျာဂမၠိုၚ်။
[[ကဏ္ဍ:ဘာသာသၠဝ်ဝေန်နဳယျာ]][[ကဏ္ဍ:ဝေါဟာမနွံကဵုလေန်ရမျာၚ်အဝ်ဒဳယဝ်ဗက်အလိုက်အရေဝ်ဘာသာဂမၠိုၚ်|သ]]
oem1yrz0uvcukvg46qq85l2v7pvry7r
မဝ်ဂျူ:sk-noun
828
295618
396480
396270
2026-06-07T12:11:07Z
咽頭べさ
33
396480
Scribunto
text/plain
--[=[
This module contains functions for creating inflection tables for Slovak
nouns.
]=]--
local export = {}
-- Within this module, declensions are the functions that do the actual
-- declension by creating the forms of a basic noun.
-- They are defined further down.
--local lemma = "Bratislava"
local declensions = {}
local lang = require("Module:languages").getByCode("sk")
local m_table = require("Module:table")
local m_links = require("Module:links")
local m_adj = require("Module:sk-adjective")
local allowed_genders = {
"m-pr", "m-anml", "m-in", "f", "n"
}
local allowed_genders_set = m_table.listToSet(allowed_genders)
local hard_consonants = {"d", "t", "n", "l", "h", "ch", "k", "g"}
local soft_consonants = {"ď", "ť", "ň", "ľ", "ž", "š", "č", "dž", "c", "dz", "j"}
local ambiguous_consonants = {"b", "m", "p", "r", "s", "v", "z", "f"}
local hard_consonants_set = m_table.listToSet(hard_consonants)
local soft_consonants_set = m_table.listToSet(soft_consonants)
local ambiguous_consonants_set = m_table.listToSet(ambiguous_consonants)
local consonants = m_table.append(hard_consonants, soft_consonants, ambiguous_consonants)
local consonants_set = m_table.listToSet(consonants)
local short_vowels = {"a", "e", "i", "o", "u", "y", "ä", "ö", "ü"}
local long_vowels = {"á", "é", "í", "ó", "ú", "ý", "ő", "ű"}
local diphthongs = {"ia", "ie", "iu", "ô"}
local short_vowels_set = m_table.listToSet(short_vowels)
local long_vowels_set = m_table.listToSet(long_vowels)
local diphthongs_set = m_table.listToSet(diphthongs)
local vowels = m_table.append(short_vowels, long_vowels)
local vowels_set = m_table.listToSet(vowels)
local obstruents = {"p", "t", "k", "b", "d", "g", "f", "s", "š", "ch", "z", "ž", "č", "dž"} --"v" removed??
local sonorants = {"m", "n", "l", "r", "ʋ", "v"} --"v" added??
local obstruents_set = m_table.listToSet(obstruents)
local sonorants_set = m_table.listToSet(sonorants)
local bigraphs = {"ch", "dz", "dž"}
local bigraphs_set = m_table.listToSet(bigraphs)
local prepositions = {"bez", "bezo", "cez", "cezo", "do", "k", "ku", "medzi",
"na", "nad", "nado", "o", "do", "okrem", "po", "pod", "podo", "pre", "pred",
"predo", "pri", "proti", "naproti", "oproti", "s", "so", "skrz", "u", "v",
"vo", "z", "zo", "za"} -- Add more prepositions as needed
local prepositions_set = m_table.listToSet(prepositions)
local conjunctions = {"a"} -- Add more conjunctions as needed
local conjunctions_set = m_table.listToSet(conjunctions)
-- The main entry point.
-- This is the only function that can be invoked from a template.
function export.show(frame)
local args = frame:getParent().args
NAMESPACE = mw.title.getCurrentTitle().nsText
if NAMESPACE == "" then
PAGENAME = mw.loadData("Module:headword/data").pagename
else
if args["pagename"] then
PAGENAME = args["pagename"] -- TEST: lemma
else
error("Pagename not specified")
end
end
GENDER = check_gender(args[1])
NUMBER = args["n"]
TYPE = args["t"]
GEN_ENDING = args["g"]
PLURAL = args["pl"]
PLURAL2 = args["pl2"]
PRONUNCIATION = args["p"]
local genitive = args[2]
local category = ""
-- Check if PAGENAME is a multiword expression
if not mw.ustring.find(PAGENAME, " ") then
-- Single-word expression, handle as usual
PATTERN = determine_pattern(PAGENAME, genitive)
-- Category
if NAMESPACE == "" then
if PATTERN == "indeclinable" then
category = "[[ကဏ္ဍ:နာမ်သလဝ်ဝေန်နဳယျာနကဵုပါ်ပါဲထောံဟွံမာန်ဂမၠိုၚ်|" .. PAGENAME .. "]]"
elseif PATTERN == "irregular" then
category = "[[ကဏ္ဍ:နာမ်ကိုန်ဨကဝုစ်သလဝ်ဝေန်နဳယျာဂမၠိုၚ်|" .. PAGENAME .. "]]"
elseif PATTERN == "adjective" then
category = "[[ကဏ္ဍ:နာမ်မဆေၚ်စပ်ကဵုနာမဝိသေသနသလဝ်ဝေန်နဳယျာဂမၠိုၚ်|" .. PAGENAME .. "]]"
else
category = "[[ကဏ္ဍ:ဝေါဟာသလဝ်ဝေန်နဳယျာမၞုံကဵုဝေါဟာ " .. PATTERN .. " မလဟုတ်စှ်ေဂမၠိုၚ်|" .. PAGENAME .. "]]"
end
if GENDER == "m-anml" then
category = category .. "[[ကဏ္ဍ:ဝေါဟာသလဝ်ဝေန်နဳယျာမၞုံကဵုဝေါဟာ chlap မလဟုတ်စှ်ေဂမၠိုၚ်|" .. PAGENAME .. "]]"
end
if PAGENAME == "cól" then
category = category .. "[[ကဏ္ဍ:ဝေါဟာသလဝ်ဝေန်နဳယျာမၞုံကဵုဝေါဟာ dub မလဟုတ်စှ်ေဂမၠိုၚ်|" .. PAGENAME .. "]]"
end
end
local forms, title
-- Find out if there are more genitive stems and if so, process them
if genitive then
if mw.ustring.find(genitive, "/") then
forms, title = decline_with_more_stems(genitive)
else
forms, title = declensions[PATTERN](PAGENAME, genitive)
normalize_forms(forms)
end
else
forms, title = declensions[PATTERN](PAGENAME, genitive)
normalize_forms(forms)
end
specified_by_user(forms, args)
return make_table(forms, title) .. category
end
-- Multiword expressions
-- Category
if NAMESPACE == "" then
category = "[[ကဏ္ဍ:ဝေါဟာသလဝ်ဝေန်နဳယျာမဂၠိုၚ်ကဵုမအရေဝ်ဂမၠိုၚ်|" .. PAGENAME .. "]]"
end
-- Split into words
local units, gen_units = split_into_units(PAGENAME, genitive)
-- Process each unit to generate forms
local combined_forms = generate_combined_forms(units, gen_units)
-- Add forms specified by the user
specified_by_user(combined_forms, args)
-- Generate the final title and table
local title = "မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု ''" .. PAGENAME .. "'' (multiword term)"
return make_table(combined_forms, title) .. category
end
--[=[
Declension functions
]=]--
declensions["chlap"] = function(word, genitive)
local forms = {}
local syllable_count, syllables = split_into_syllables(word)
local mobile_removed = false -- Tracks if a mobile vowel was removed
local words_with_mobile = {"kmotor", "švagor", "svokor", "lotor",
"obor", "Uhor", "pochábeľ", "väzeň", "učeň",
"posol", "diabol", "blázon", "Turek", "fanúšek", "center",
"hypochonder", "Kafer", "neger", "gangster",
"kapor", "zubor", "bobor", "orol", "pes"
}
local surn_ech_always_declined = {"Čech", "Plech", "Pech", "Štech", "Vojtech"}
-- Determine the stem by removing mobile vowels if applicable
local stem = word
if (matches_any(word, words_with_mobile)
or ends_with_any(word, {"ec", "ček", "íček", "ok"}))
and not (TYPE == "surn"
and (ends_with_any(word, {"[aeiouyáéíóúýäöüőű]ček", "[aeiouyáéíóúýäöüőű]čok"})
or syllable_count == 1))
and not matches_any(word, {"otrok"})
then
stem = remove_mobile_vowel(word)
mobile_removed = true -- Mark mobile vowel as removed
end
-- Adjust specific stems based on predefined mappings
stem = ({["švec"] = "ševc", ["žnec"] = "ženc"})[word] or stem
-- Remove certain endings for words ending with "us", "os", "es" and "o"
if ends_with(word, "[uoea]s") then
stem = mw.ustring.sub(word, 1, -3)
end
if ends_with(word, "o") then
stem = remove_last_char(word)
end
-- Override if a specific argument is given
if genitive and NUMBER ~= "pl" then
stem = remove_last_char(genitive)
if mw.ustring.sub(word, 1, -3) .. get_last_char(word) == remove_last_char(genitive) then
mobile_removed = true
end
end
-- Title for the declension table
local title = "Declension of ''" .. word .. "'' <br>(pattern " .. pattern_link("chlap") .. ")"
if TYPE == "surn" then
if ends_with(word, "a[iy]") then
title = "မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု ''" .. word .. "'' <br>(pattern " .. pattern_link("chlap") .. " or " .. pattern_link("kuli") .. ")"
elseif ends_with_any(word, {"ých", "ech"}) and not matches_any(word, surn_ech_always_declined) then
title = "Declension of ''" .. word .. "'' <br>(pattern " .. pattern_link("chlap") .. " or indeclinable)"
end
end
-- Helper variables for analyzing the last and penultimate characters
local ultimate, penultimate = get_last_letters(stem)
local first = get_first_char(word)
-- Determine alternative dative and locative for some nouns
if matches_any(word, {"človek", "pán", "čert", "diabol", "vlk", "pes",
"Boh", "boh", "otec", "syn", "duch", "Kristus", "don", "šor"})
then
set_forms(forms, {"dat_sg2", "loc_sg2"}, {stem .. "u", stem .. "u"})
end
local nom_pl = "i"
if PLURAL then
if mw.ustring.find(PLURAL, "/") then
local endings = {}
for part in mw.ustring.gmatch(PLURAL, "[^/]+") do
table.insert(endings, part)
end
nom_pl = endings[1]
for i = 2, 4 do
if endings[i] then
set_forms(forms, {"nom_pl" .. i}, {append_ending(stem, endings[i])})
else
break
end
end
else
nom_pl = PLURAL
end
elseif (ends_with_any(word, {"fil", "fób", "graf", "nóm"})
and not matches_any(word, {"xylograf", "typograf"}))
or matches_any(word, {"cynik", "epik", "klasik", "stoik",
"Brit", "Búr", "Gal", "Ír", "Dór", "Italik", "Ión", "Nór",
"cár", "lodivod", "odkundes", "starík", "muž", "svat", "apoštol",
"augur", "bán", "bogomil", "buržuj", "don", "druid", "eféb",
"famulus", "héros", "man", "posol"
})
then
set_forms(forms, {"nom_pl2"}, {append_ending(stem, "ovia")})
elseif (ends_with_any(word, {"[^c]h", "g", "ček", "ek", "ik", "čik", "ok", "o", "duch"}) and word ~= "pes")
or matches_any(word, {"otec", "syn", "ujec", "strýc", "svák", "svokor", "tesť", "švagor",
"zať", "kmotor", "ded", "kmeť", "pobratim", "otčim", "zlosyn", "princ",
"Bask", "Frank", "Ind", "Ink", "Kurd", "Dák", "Čudo", "Fríz", "Azték",
"Asýr", "Etrusk", "Kazach", "Kašub", "odľud", "člen", "zved", "hňup", "cicerone"})
or (ends_with(word, "ch") and TYPE == "loan")
or (ends_with_any(word, {"us", "es"}) and genitive ~= word .. "a")
or matches_any(TYPE, {"comp-deverb", "surn", "name"})
or PLURAL == "ovia"
then
nom_pl = "ovia"
if TYPE == "surn" then
append_second_plural(forms, stem .. "ovci", stem .. "ovcov", stem .. "ovcom",
stem .. "ovcov", stem .. "ovcoch", stem .. "ovcami")
end
elseif ends_with(word, "teľ")
or (ends_with(word, "an") and is_capital(first))
or matches_any(word, {"občan", "dedinčan", "mešťan", "zeman", "ostrovan",
"krajan", "dolňan", "horňan", "južan", "severan", "rodič", "dedič", "brat", "hosť",
"manžel", "exmanžel", "sused", "žid", "Žid", "kresťan", "nekresťan", "pohan"})
then
nom_pl = "ia"
if word == "pohan" then
set_forms(forms, {"nom_pl2"}, {append_ending(stem, "i")})
end
end
-- Determine instrumental plural ending
local ins_pl = "mi" -- Default instrumental plural ending is "mi"
if ultimate == "m"
or mobile_removed
or ends_with_any(word, {"o", "ius"})
or matches_any(word, {"hosť", "tesť", "Kopt"}) --obstruents_set[penultimate] and obstruents_set[ultimate]
then
ins_pl = "ami"
end
-- Populate forms table with singular and plural endings
append_endings(forms, word, stem,
"a", "ovi", "a", "ovi", "om",
nom_pl, append_ending(stem, "ov"), "om", "ov", "och", ins_pl
)
-- Surnames ending with -ých, -ech, -ai, -ay
if TYPE == "surn" then
if ends_with_any(word, {"ých", "ech"}) and not matches_any(word, surn_ech_always_declined) then
append_alternative_singular(forms, word, word, word, word, word)
append_alternative_plural(forms, word, word, word, word, word, word)
elseif ends_with(word, "a[iy]") then
append_alternative_singular(forms, word .. "ho", word .. "mu", word .. "ho", word .. "m", word .. "m")
set_forms(forms, {"ins_pl2"}, {word .. "ami"})
end
end
-- Exceptions
if word == "človek" then
set_forms(forms,
{"nom_pl", "gen_pl", "dat_pl", "acc_pl", "loc_pl", "ins_pl"},
{"ľudia", "ľudí", "ľuďom", "ľudí", "ľuďoch", "ľuďmi"}
)
elseif word == "héros" then
set_forms(forms,
{"gen_sg", "dat_sg", "acc_sg", "loc_sg", "ins_sg",
"nom_pl", "gen_pl", "dat_pl", "acc_pl", "loc_pl", "ins_pl",
"gen_sg2", "dat_sg2", "acc_sg2", "loc_sg2", "ins_sg2",
"nom_pl2", "gen_pl2", "dat_pl2", "acc_pl2", "loc_pl2", "ins_pl2"},
{"hérosa", "hérosovi", "hérosa", "hérosovi", "hérosom",
"hérosi", "hérosov", "hérosom", "hérosov", "hérosoch", "hérosmi",
"héroa", "héroovi", "héroa", "héroovi", "héroom",
"héroovia", "héroov", "héroom", "héroov", "hérooch", "héroami"}
)
end
-- Vocative
if matches_any(word, {"syn", "sváko", "švagor", "kmotor", "brat", "synko",
"synáčik", "pán", "chlapec", "priateľ", "majster", "Boh", "boh", "Pán", "Syn",
"Duch", "Ježiš", "Ježiško", "Kristus", "Spasiteľ", "Vykupiteľ", "Stvoriteľ",
"Premožiteľ", "Kráľ", "Hospodin", "baránok", "Baránok", "tešiteľ", "Tešiteľ",
"hriešnik", "protivník", "kresťan", "otec", "Otec", "chlap", "človek"})
then
voc_stem = stem
if word == "synáčik" then voc_stem = "synáčk" end
if word == "pán" then voc_stem = "pan" end
if word == "Pán" then voc_stem = "Pan" end
if ends_with_any(word, {"teľ", "o"})
or matches_any(word, {"syn", "Syn", "Duch", "Ježiš", "Kráľ",
"hriešnik", "synáčik", "baránok", "Baránok"})
then
set_forms(forms, {"voc_sg"}, {append_ending(voc_stem, "u")})
else
if ends_with_any(voc_stem, {"k", "c"}) then
voc_stem = remove_last_char(voc_stem) .. "č"
elseif ends_with(voc_stem, "h") then
voc_stem = remove_last_char(voc_stem) .. "ž"
end
set_forms(forms, {"voc_sg"}, {append_ending(voc_stem, "e")})
end
if not matches_any(word, {"človek", "pán", "brat"}) then
set_forms(forms, {"voc_sg2"}, {word})
elseif word == "brat" then
set_forms(forms, {"voc_sg2", "voc_sg3"}, {"bratu", "brat"})
end
end
return forms, title -- Return forms table and title for declension
end
declensions["hrdina"] = function(word, genitive)
local forms = {}
local title = "မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု ''" .. word .. "'' <br>(pattern " .. pattern_link("hrdina") .. ")"
local stem = remove_suffix(word, "a")
if ends_with(word, "[oeě]") then
stem = remove_last_char(word)
if ends_with(word, "ě") and ends_with(stem, "[dtnl]") then
stem = soften_last_consonant(stem)
end
end
local nom_pl_ending = "ovia"
if ends_with_any(word, {"ista", "ita"})
or (ends_with(word, "ta") and TYPE == "loan") then
nom_pl_ending = "i"
end
local ins_pl_ending = "ami"
if ends_with(word, "[aeiouyáéíóúýäöüőű]ta") and TYPE == "loan" then
ins_pl_ending = "mi"
end
append_endings(forms, word, stem,
"u", "ovi", "u", "ovi", "om",
nom_pl_ending, stem .. "ov", "om", "ov", "och", ins_pl_ending
)
if TYPE == "surn" then
append_second_plural(forms, stem .. "ovci", stem .. "ovcov", stem .. "ovcom",
stem .. "ovcov", stem .. "ovcoch", stem .. "ovcami")
end
if word == "buržoa" then
set_forms(forms,
{"dat_sg", "loc_sg", "ins_sg",
"nom_pl", "gen_pl", "dat_pl", "acc_pl", "loc_pl"},
{"buržuovi", "buržuovi", "buržuom",
"buržuovia", "buržuov", "buržuom", "buržuov", "buržuoch"}
)
end
return forms, title
end
declensions["kuli"] = function(word, genitive)
local forms = {}
local stem = word
local title = "မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု ''" .. word .. "'' <br>(pattern " .. pattern_link("kuli") .. ")"
append_endings(forms, word, stem,
"ho", "mu", "ho", "m", "m",
"ovia", stem .. "ov", "om", "ov", "och", "ami"
)
if TYPE == "surn" then
append_second_plural(forms, stem .. "ovci", stem .. "ovcov", stem .. "ovcom",
stem .. "ovcov", stem .. "ovcoch", stem .. "ovcami")
end
return forms, title
end
declensions["dub"] = function(word, genitive)
local forms = {}
local mobile_removed = false -- Tracks if a mobile vowel was removed
local words_with_mobile = {"priemysel", "počet", "ocot", "bahor", "chrbát",
"ker", "cukor", "uhor", "vietor", "víchor", "vôdor", "kotol",
"kapor", "zubor", "bobor", "orol", "pes", "mozog", "uhol"}
local syllable_count, syllables = split_into_syllables(word)
-- Determine the stem by removing mobile vowels if applicable
local stem = word
if (matches_any(word, words_with_mobile)
or (ends_with_any(word, {"íček", "ýček", "ok"}) and syllable_count ~= 1))
and not matches_any(word, {"polrok", "nárok", "zákrok", "rozkrok"})
then
stem = remove_mobile_vowel(word)
mobile_removed = true -- Mark mobile vowel as removed
end
-- Adjust specific stems based on predefined mappings
stem = ({["mráz"] = "mraz", ["vietor"] = "vetr", ["kôl"] = "kol",
["stôl"] = "stol", ["vôl"] = "vol", ["chlieb"] = "chleb",
["ensemble"] = "ensembl"})[word] or stem
-- Remove certain endings for words ending with "us", "os", "es" and "o"
if (ends_with(word, "[uoe]s") and TYPE == "loan") or ends_with(word, "izmus") then
stem = mw.ustring.sub(word, 1, -3)
end
if ends_with(word, "o") then
stem = remove_last_char(word)
end
-- Pluralia tantum
if NUMBER == "pl" then
stem = remove_suffix(word, "y")
end
-- Override if a specific argument is given
if genitive and NUMBER ~= "pl" then
stem = remove_last_char(genitive)
if stem == remove_last_char(remove_last_char(word)) .. get_last_char(word) then
mobile_removed = true
elseif stem == word then
mobile_removed = false
end
end
-- Set "mobile_removed" if genitive was set by the user
if ends_with(word, "e[lr]") and ends_with(stem, "[^e][lr]") then
mobile_removed = true
end
-- Title for the declension table
local title = "မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု ''" .. word .. "'' <br>(pattern " .. pattern_link("dub") .. ")"
if GENDER == "m-anml" then
if matches_any(word, {"vták", "vlk", "pes"}) then
title = "မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု ''" .. word .. "'' <br>(patterns " .. pattern_link("chlap") .. " <small>(singular, plural 1)</small> and " .. pattern_link("dub") .. " <small>(plural 2)</small>)"
elseif PLURAL2 then
title = "မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု ''" .. word .. "'' <br>(patterns " .. pattern_link("chlap") .. " <small>(singular, plural 2)</small> and " .. pattern_link("dub") .. " <small>(plural 1)</small>)"
else
title = "မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု ''" .. word .. "'' <br>(patterns " .. pattern_link("chlap") .. " <small>(singular)</small> and " .. pattern_link("dub") .. " <small>(plural)</small>)"
end
end
-- Helper variables for analyzing the last and penultimate characters
local ultimate, penultimate = get_last_letters(stem)
local first = get_first_char(word)
-- Determine genitive singular ending
local gen_sg = "a"
if (matches_any(TYPE, {"abstr", "comp-deverb", "material", "collect", "loan"})
or matches_any(GEN_ENDING, {"u", "u/a"})
or ends_with_any(word, {"izmus", "x"})
or (ends_with(word, "m") and not is_capital(first))
or (ends_with(word, "z") and is_capital(first)))
and not matches_any(GEN_ENDING, {"a", "a/u"})
then
gen_sg = "u"
end
-- Override genitive singular ending if a specific argument is given
if genitive then
gen_sg = get_last_char(genitive)
end
-- Determine locative singular ending based on specific suffixes and patterns
local loc_sg = "e"
if ends_with_any(word, {"k", "g", "h", "ch"}) or (ends_with(word, "i[ue]s") and TYPE == "loan") then
loc_sg = "u"
elseif (ends_with(word, "ér")
or (ends_with(word, "ál") and not matches_any(word, {"bál", "paškál", "paušál", "šál", "škandál", "žurnál"}))
or matches_any(word, {"Alžír", "klavír", "apríl", "jún", "júl"}))
then
loc_sg = "i"
end
-- Determine genitive plural ending
local gen_pl = stem .. "ov" -- Default genitive plural ending is "ov"
-- Adjust genitive plural for specific place names and patterns
if is_capital(first) and (ends_with_any(word, {"any", "íky", "áky", "iaky"})
or matches_any(word, {"Krompachy", "Kubachy", "Veľbachy", "Spišské Vlachy",
"Žabokreky", "Čechy", "Brezolupy", "Bruty", "Rakoľuby",
"Rakúsy", "Prusy", "Veľaty", "Piešťany", "Bieščary"}))
then
local gen_stem = remove_last_char(word) -- Remove last character to get the base stem
local syllable_count, syllables = split_into_syllables(gen_stem)
local last_syllable = syllables[1]
local penultimate_syllable = syllables[2] or ""
-- Lengthen the vowel if the penultimate syllable is short, otherwise use unchanged stem
if is_long(penultimate_syllable) then
gen_pl = gen_stem
else
gen_pl = remove_suffix(gen_stem, last_syllable) .. lengthen_vowel(last_syllable)
end
end
-- Special cases for genitive plural of specific words
if word == "čas" then
gen_pl = "čias"
elseif word == "raz" then
gen_pl = "ráz"
end
-- Determine instrumental plural ending
local ins_pl = "mi" -- Default instrumental plural ending is "mi"
if ultimate == "m"
or mobile_removed
or ends_with_any(word, {"o", "ius", "eus"})
or (penultimate == "m" and consonants_set[ultimate])
then
ins_pl = "ami"
end
-- Doublets for instrumental plural
if matches_any(word, {"schod", "schody", "fúz", "fúzy", "roh", "rohy",
"paroh", "parohy", "čary", "orech"})
then
set_forms(forms, {"ins_pl2"}, {stem .. "ami"})
elseif matches_any(word, {"zub", "dol", "most", "prst", "piest", "necht"}) then
ins_pl = "ami"
set_forms(forms, {"ins_pl2"}, {stem .. "mi"})
end
-- Populate forms table with singular and plural endings
append_endings(forms, word, stem,
gen_sg, "u", nil, loc_sg, "om",
"y", gen_pl, "om", nil, "och", ins_pl
)
-- Alternative genitive ending
if GEN_ENDING == "a/u" then
set_forms(forms, {"gen_sg2"}, {stem .. "u"})
elseif GEN_ENDING == "u/a" then
set_forms(forms, {"gen_sg2"}, {stem .. "a"})
end
-- Exceptions
if matches_any(word, {"sen", "dol"}) then
set_forms(forms, {"loc_pl2"}, {stem .. "ách"})
elseif word == "čas" then
set_forms(forms, {"gen_pl2"}, {"časov"})
end
-- Animal nouns
if GENDER == "m-anml" then
append_animal_singular(forms, stem)
if matches_any(word, {"vták", "vlk", "pes"}) or PLURAL2 then
local forms2, title2 = declensions["chlap"](word, stem .. gen_sg)
append_second_plural(forms, forms2["nom_pl"], forms2["gen_pl"], forms2["dat_pl"],
forms2["acc_pl"], forms2["loc_pl"], forms2["ins_pl"])
if matches_any(word, {"vták", "vlk", "pes"}) then
switch_plural_forms(forms) --changes the prefered forms
-- Determine alternative dative and locative for some nouns
if matches_any(word, {"vlk", "pes"}) then
set_forms(forms, {"dat_sg2", "loc_sg2"}, {stem .. "u", stem .. "u"})
end
end
end
end
return forms, title -- Return forms table and title for declension
end
declensions["stroj"] = function(word, genitive)
local forms = {}
local mobile_removed = false -- Tracks if a mobile vowel was removed
local words_with_no_mobile = {"kúpeľ", "červeň", "kameň", "jačmeň", "koreň",
"prameň", "prsteň", "jeleň"}
local first = get_first_char(word)
-- Determine the stem by removing mobile vowels if applicable
local stem = word
if (ends_with_any(word, {"ec", "iec", "[^ti]eľ", "oľ", "[^i]eň", "eť", "[^i]er", "el"})
or (ends_with(word, "ac") and (is_capital(first) or word == "desaterac")))
and not matches_any(word, words_with_no_mobile)
then
stem = remove_mobile_vowel(word)
mobile_removed = true -- Mark mobile vowel as removed
end
-- Adjust specific stems based on predefined mappings
stem = ({["dážď"] = "dažď", ["kôš"] = "koš", ["nôž"] = "nož",
["kôň"] = "koň", ["timbre"] = "timbr"})[word] or stem
-- Pluralia tantum
if NUMBER == "pl" then
stem = remove_suffix(word, "e")
end
-- Override if a specific argument is given
if genitive and NUMBER ~= "pl" then
stem = remove_last_char(genitive)
if stem == remove_last_char(remove_last_char(word)) .. get_last_char(word) then
mobile_removed = true
elseif stem == word then
mobile_removed = false
end
end
-- Set "mobile_removed" if genitive was set by the user
if ends_with(word, "e[lr]") and ends_with(stem, "[^e][lr]") then
mobile_removed = true
end
-- Title for the declension table
local title = "မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု ''" .. word .. "'' <br>(pattern " .. pattern_link("stroj") .. ")"
if GENDER == "m-anml" then
if PLURAL2 then
title = "မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု ''" .. word .. "'' <br>(patterns " .. pattern_link("chlap") .. " <small>(singular, plural 2)</small> and " .. pattern_link("stroj") .. " <small>(plural 1)</small>)"
else
title = "မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု ''" .. word .. "'' <br>(patterns " .. pattern_link("chlap") .. " <small>(singular)</small> and " .. pattern_link("stroj") .. " <small>(plural)</small>)"
end
elseif word == "cól" then
title = "မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု ''" .. word .. "'' <br>(pattern " .. pattern_link("stroj") .. " or " .. pattern_link("dub") .. ")"
end
-- Helper variables for analyzing the last and penultimate characters
local ultimate, penultimate = get_last_letters(stem)
local first = get_first_char(word)
-- Determine genitive singular ending
local gen_sg = "a"
if ((matches_any(TYPE, {"abstr", "material"})
or matches_any(GEN_ENDING, {"u", "u/a"}))
and not matches_any(word, {"jačmeň", "kameň", "olej", "loj", "vývoj", "rozvoj"}))
and not matches_any(GEN_ENDING, {"a", "a/u"})
then
gen_sg = "u"
end
-- Override genitive singular ending if a specific argument is given
if genitive then
gen_sg = get_last_char(genitive)
end
-- Determine genitive plural ending
local gen_pl = stem .. "ov" -- Default genitive plural ending is "ov"
-- Adjust genitive plural for specific place names and patterns
if is_capital(first) and (ends_with_any(word, {"áre", "iare"})
or word == "Tlmače")
then
local gen_stem = remove_last_char(word) -- Remove last character to get the base stem
local syllable_count, syllables = split_into_syllables(gen_stem)
local last_syllable = syllables[1]
local penultimate_syllable = syllables[2] or ""
-- Lengthen the vowel if the penultimate syllable is short, otherwise use unchanged stem
if is_long(penultimate_syllable) then
gen_pl = gen_stem
else
gen_pl = remove_suffix(gen_stem, last_syllable) .. lengthen_vowel(last_syllable)
end
end
-- Special cases for genitive plural of specific words
if word == "Ladce" then
gen_pl = "Ladiec"
elseif word == "Vráble" then
gen_pl = "Vrábeľ"
elseif word == "peniaze" then
gen_pl = "peňazí"
elseif matches_any(word, {"deň", "kôň", "groš"}) then
gen_pl = append_ending(stem, "í")
end
-- Determine instrumental plural ending
local ins_pl = "mi" -- Default instrumental plural ending is "mi"
if mobile_removed
or (obstruents_set[penultimate] and obstruents_set[ultimate])
then
ins_pl = "ami"
end
-- Populate forms table with singular and plural endings
append_endings(forms, word, stem,
gen_sg, "u", nil, "i", "om",
"e", gen_pl, "om", nil, "och", ins_pl
)
-- Alternative genitive ending
if GEN_ENDING == "a/u" then
set_forms(forms, {"gen_sg2"}, {stem .. "u"})
elseif GEN_ENDING == "u/a" then
set_forms(forms, {"gen_sg2"}, {stem .. "a"})
end
-- Exceptions
if matches_any(word, {"deň", "poldeň"}) then
set_forms(forms,
{"nom_pl", "acc_pl", "loc_sg2"},
{append_ending(stem, "i"), append_ending(stem, "i"), append_ending(stem, "e")}
)
elseif word == "ďateľ" then
set_forms(forms,
{"gen_sg2", "dat_sg2", "loc_sg2", "ins_sg2",
"nom_pl2", "gen_pl2", "dat_pl2", "acc_pl2", "loc_pl2", "ins_pl2"},
{"ďatľa", "ďatľovi", "ďatľovi", "ďatľom",
"ďatle", "ďatľov", "ďatľom", "ďatle", "ďatľoch", "ďatľami"}
)
elseif word == "cól" then
set_forms(forms,
{"loc_sg2", "nom_pl2", "acc_pl2"},
{"cóle", "cóly", "cóly"}
)
end
-- Animal nouns
if GENDER == "m-anml" then
append_animal_singular(forms, stem)
if PLURAL2 then
local forms2, title2 = declensions["chlap"](word, stem .. gen_sg)
append_second_plural(forms, forms2["nom_pl"], forms2["gen_pl"], forms2["dat_pl"],
forms2["acc_pl"], forms2["loc_pl"], forms2["ins_pl"])
end
end
return forms, title -- Return forms table and title for declension
end
declensions["žena"] = function(word, genitive)
local forms = {}
local stem = remove_suffix(word, "a")
local title = "မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု ''" .. word .. "'' <br>(pattern " .. pattern_link("žena") .. ")"
if ends_with(word, "ea") then
title = "မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု ''" .. word .. "'' <br>(pattern " .. pattern_link("žena") .. ", loanword ending with ''-ea'')"
end
-- Pluralia tantum or genitive
if NUMBER == "pl" then
stem = remove_suffix(word, "y")
end
if genitive and NUMBER ~= "pl" then
stem = remove_suffix(genitive, "y")
end
-- Split into syllables
local gen_pl = ""
local syllable_count, syllables = split_into_syllables(stem)
local word_syllable_count, word_syllables = split_into_syllables(word)
-- Get the last and second-to-last syllables for rule checks
local last_syllable = syllables[1]
local penultimate_syllable = syllables[2] or ""
local word_penultimate_syllable = word_syllables[2] or ""
local ultimate, penultimate = get_last_letters(stem)
-- 1. Add a vowel between the last two consonants of a cluster at the end of the stem
if ends_with_consonant_cluster(last_syllable) or word_syllable_count == 1 then
if (ultimate == "k" and not matches_any(word, {"banka"}))
or (ultimate == "b" and (TYPE == "der" or not sonorants_set[penultimate]))
or sonorants_set[ultimate]
or matches_any(word, {"farba", "karta", "buchta", "astma"})
or (obstruents_set[penultimate] and ultimate == "v")
then
if penultimate ~= "j" and (word_syllable_count == 1 or is_short(word_penultimate_syllable)) then
gen_pl = harden_last_consonant(remove_suffix(stem, ultimate)) .. "ie" .. ultimate
if matches_any(word, {"handra", "perla", "metla", "slivka",
"tehla", "vidly", "karta", "kvapka", "stovka", "doska"})
then
gen_pl = remove_suffix(stem, ultimate) .. "á" .. ultimate
set_forms(forms, {"gen_pl2"}, {harden_last_consonant(remove_suffix(stem, ultimate)) .. "ie" .. ultimate})
end
if matches_any(word, {"jamka", "kvapka"}) then
set_forms(forms, {"gen_pl2"}, {remove_suffix(stem, ultimate) .. "ô" .. ultimate})
end
if matches_any(word, {"astma"}) then
set_forms(forms, {"gen_pl2"}, {stem .. "í"})
end
elseif obstruents_set[ultimate] and (is_long(word_penultimate_syllable) or penultimate == "j") then
gen_pl = remove_suffix(stem, ultimate) .. "o" .. ultimate
elseif (sonorants_set[ultimate] or ultimate == "v") and (is_long(word_penultimate_syllable) or penultimate == "j") then
gen_pl = remove_suffix(stem, ultimate) .. "e" .. ultimate
if penultimate ~= "j" then
set_forms(forms, {"gen_pl2"}, {remove_suffix(stem, ultimate) .. "ie" .. ultimate})
end
end
end
end
-- 2. Add "í" if stem ends with a vowel or a soft consonant
if gen_pl == "" and (vowels_set[ultimate]
or matches_any(word, {"medaila", "pera", "kanva", "panva", "skepsa", "nuansa"}))
then
gen_pl = stem .. "í"
if matches_any(word, {"nuansa"}) then
set_forms(forms, {"gen_pl2"}, {remove_suffix(stem, last_syllable) .. lengthen_vowel(last_syllable)})
end
end
-- 3. Return the stem with no change if conditions match
if gen_pl == "" and (is_long(penultimate_syllable)
or ends_with_any(stem, {"tvor", "ov"})
or mw.ustring.match(last_syllable, "[Jj][eo]")
or (TYPE == "loan" and matches_any(get_vowel(last_syllable), {"e", "o"}))) then
gen_pl = stem
end
-- 4. Logic to use the lengthened vowel if conditions match
if gen_pl == "" and (not (ends_with_consonant_cluster(last_syllable) or vowels_set[ultimate])
or (ends_with_consonant_cluster(last_syllable) and sonorants_set[penultimate] and obstruents_set[ultimate])
or (ends_with_consonant_cluster(last_syllable) and matches_any(penultimate, {"c", "s", "z", "š", "ž", "p"}) and matches_any(ultimate, {"t", "d"})))
and not matches_any(word, {"šachta", "mzda"}) then
gen_pl = remove_suffix(stem, last_syllable) .. lengthen_vowel(last_syllable)
if mw.ustring.find(stem, "au[bcčdďfghjklľmnňpqrsštťvwxzž]$") then
gen_pl = remove_suffix(stem, "a" .. last_syllable) .. "á" .. last_syllable
end
end
-- 5. long -á- instead of -ia- for some loanwords
if matches_any(word, {"pyžama", "šachta"}) then
gen_pl = (word == "pyžama") and "pyžám" or "šácht"
if word == "šachta" then
set_forms(forms, {"gen_pl2"}, {"šachiet"})
end
end
-- 6. Exceptions
if word == "mzda" then
gen_pl = "miezd"
end
if gen_pl == "" then gen_pl = stem end
local dat_pl, loc_pl
if is_long(word_penultimate_syllable) then
dat_pl, loc_pl = "am", "ach"
else
dat_pl, loc_pl = "ám", "ách"
end
local dat_sg, loc_sg
if ultimate == "e" then
dat_sg, loc_sg = "i", "i"
else
dat_sg, loc_sg = "e", "e"
end
append_endings(forms, word, stem,
"y", dat_sg, "u", loc_sg, "ou",
"y", gen_pl, dat_pl, nil, loc_pl, "ami"
)
if matches_any(word, {"zora", "žiara", "žiabra"}) then
set_forms(forms,
{"nom_pl", "acc_pl"},
{stem .. "e", stem .. "e"}
)
end
return forms, title
end
declensions["gazdiná"] = function(word, genitive)
local forms = {}
local stem = remove_suffix(word, "á")
local title = "မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု ''" .. word .. "'' <br>(pattern " .. pattern_link("gazdiná") .. ")"
local syllable_count, syllables = split_into_syllables(stem)
local last_syllable = syllables[1]
local gen_pl = ""
if ends_with_consonant_cluster(last_syllable) then
gen_pl = mw.ustring.sub(stem, 1, -2) .. "ien"
else
gen_pl = remove_suffix(stem, last_syllable) .. lengthen_vowel(last_syllable)
end
append_endings(forms, word, stem,
"ej", "ej", "ú", "ej", "ou",
"é", gen_pl, "ám", nil, "ách", "ami"
)
return forms, title
end
declensions["ulica"] = function(word, genitive)
local forms = {}
local stem = remove_suffix(word, "a")
local title = "မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု ''" .. word .. "'' <br>(pattern " .. pattern_link("ulica") .. ")"
-- Pluralia tantum
if NUMBER == "pl" then
stem = remove_suffix(word, "e")
if ends_with_any(stem, {"d", "t", "n", "l"}) then
stem = soften_last_consonant(stem)
end
end
-- Split into syllables
local gen_pl = ""
local syllable_count, syllables = split_into_syllables(stem)
local word_syllable_count, word_syllables = split_into_syllables(word)
-- Get the last and second-to-last syllables for rule checks
local last_syllable = syllables[1]
local penultimate_syllable = syllables[2] or ""
local word_penultimate_syllable = word_syllables[2] or ""
local ultimate, penultimate = get_last_letters(stem)
local first = get_first_char(word)
-- 1. Add a vowel between the last two consonants of a cluster at the end of the stem
if ends_with_consonant_cluster(last_syllable) then
if (is_capital(first) and ends_with(word, "ce"))
or matches_any(word, {"dverce", "ovca", "fakľa", "drumbľa", "husle", "jasle",
"kachle", "hrable", "šabľa", "mašľa", "žemľa", "ríbezľa", "čerešňa", "sukňa", "višňa"})
then
gen_pl = harden_last_consonant(remove_suffix(stem, ultimate)) .. "ie" .. ultimate
if matches_any(word, {"drumbľa", "husle", "jasle", "kachle", "hrable",
"šabľa", "mašľa", "žemľa", "ríbezľa", "čerešňa", "sukňa", "višňa"}) then
set_forms(forms, {"gen_pl2"}, {append_ending(stem, "í")})
end
end
end
-- 2. Add "í" if stem ends with a vowel or a soft consonant
if gen_pl == "" and (matches_any(ultimate, {"dz", "dž", "ž", "ť", "ď", "j", "i", "y"})
or ends_with_any(word, {"nca", "oľa", "ôľa", "aľa", "ša", "ča"})
or matches_any(word, {"liace", "pasca", "páľa", "trúbeľa", "mrľa", "konope", "večera", "rozopra"})
or (matches_any(ultimate, {"ľ"}) and consonants_set[penultimate])
or (matches_any(ultimate, {"ň"}) and penultimate ~= "y"))
and not matches_any(word, {"papuča", "priča", "hrča", "paprča",
"garniža", "fakľa", "skriňa", "sviňa", "abatiša", "čaša", "fľaša",
"Krkonoše", "ríša", "skrýša"})
then
gen_pl = append_ending(stem, "í")
end
-- 3. Logic to use the lengthened vowel if conditions match
if gen_pl == "" and not ends_with_consonant_cluster(last_syllable) and not vowels_set[ultimate]
and ((ends_with(word, "ca") and TYPE ~= "der")
or ends_with_any(word, {"yňa", "uľa"})
or matches_any(word, {"papuča", "priča", "hrča", "chvíľa", "míľa", "košeľa", "nedeľa",
"guľa", "hoľa", "homoľa", "skriňa", "sviňa", "abatiša", "čaša", "fľaša",
"Krkonoše", "ríša", "skrýša", "garniža", "Hybe", "dvere", "kuša", "moruša"}))
then
if is_long(penultimate_syllable) then
gen_pl = stem
else
gen_pl = remove_suffix(stem, last_syllable) .. lengthen_vowel(last_syllable)
if matches_any(word, {"homoľa", "kuša", "moruša", "hrča", "paprča",
"guľa", "hoľa"})
then
set_forms(forms, {"gen_pl2"}, {append_ending(stem, "í")})
end
end
end
if gen_pl == "" then gen_pl = stem end
local dat_pl, loc_pl
if ends_with_any(stem, {"i", "y"}) or word == "pizza" then
dat_pl, loc_pl = "ám", "ách"
elseif is_long(word_penultimate_syllable) or ends_with(stem, "j") then
dat_pl, loc_pl = "am", "ach"
else
dat_pl, loc_pl = "iam", "iach"
end
append_endings(forms, word, stem,
"e", "i", "u", "i", "ou",
"e", gen_pl, dat_pl, nil, loc_pl, "ami"
)
if word == "rozopra" then
set_forms(forms,
{"dat_pl2", "loc_pl2"},
{"rozoprám", "rozoprách"}
)
elseif word == "dvere" then
set_forms(forms,
{"dat_pl", "loc_pl", "gen_pl2", "ins_pl2"},
{"dverám", "dverách", "dverí", "dvermi"}
)
end
return forms, title
end
declensions["irregular"] = function(word, genitive)
local forms = {}
local title = "မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု ''" .. word .. "'' (irregular)"
if matches_any(word, {"mať", "mater", "mati"}) then
local stem = "mater"
append_endings(forms, word, stem,
"e", "i", "", "i", "ou",
"e", stem .. "í", "iam", "e", "iach", "ami"
)
local accusative = (word == "mater") and {"mater", "mať"} or {"mať", "mater"}
set_forms(forms, {"acc_sg", "acc_sg2"}, accusative)
elseif ends_with(word, "pani") then
local stem = remove_suffix(word, "ni") .. "ň"
append_endings(forms, word, stem,
"ej", "ej", "iu", "ej", "ou",
"ie", stem .. "í", "iam", nil, "iach", "iami"
)
end
return forms, title
end
declensions["dlaň"] = function(word, genitive)
local forms = {}
local words_with_mobile_vowel = {"faloš", "osuheľ", "siheľ", "myseľ"}
local stem
if genitive then
stem = remove_suffix(genitive, "e")
elseif matches_any(word, words_with_mobile_vowel) or ends_with(word, "eň") then
stem = remove_mobile_vowel(word)
else
stem = word
end
local title = "မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု ''" .. word .. "'' <br>(pattern " .. pattern_link("dlaň") .. ")"
local syllable_count, syllables = split_into_syllables(word)
local last_syllable = syllables[1]
local penultimate_syllable = syllables[2] or ""
local last_char = get_last_char(word)
local dat_pl, loc_pl
if is_long(penultimate_syllable) or last_char == "j" then
dat_pl, loc_pl = "am", "ach"
elseif word == "kader" then
dat_pl, loc_pl = "ám", "ách"
else
dat_pl, loc_pl = "iam", "iach"
end
append_endings(forms, word, stem,
"e", "i", nil, "i", "ou",
"e", append_ending(stem, "í"), dat_pl, nil, loc_pl, "ami"
)
if matches_any(word, {"myseľ", "tvár", "hneď", "raž"}) then
set_forms(forms, {"gen_sg2"}, {append_ending(stem, "i")})
end
return forms, title
end
declensions["kosť"] = function(word, genitive)
local forms = {}
local words_with_mobile_vowel = {"voš", "lož", "Ves", "ves", "cirkev", "reďkev"}
local stem = (matches_any(word, words_with_mobile_vowel))
and remove_mobile_vowel(word) or word
if word == "česť" then stem = "cť" end
local title = "မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု ''" .. word .. "'' <br>(pattern " .. pattern_link("kosť") .. ")"
append_endings(forms, word, stem,
"i", "i", nil, "i", "ou",
"i", append_ending(stem, "í"), "iam", nil, "iach", "ami"
)
if word == "hrsť" then
set_forms(forms,
{"nom_pl", "acc_pl"},
{"hrste", "hrste"}
)
elseif word == "lesť" then
append_alternative_singular(forms, "ľsti", "ľsti", nil, "ľsti", "ľsťou")
append_alternative_plural(forms, "ľsti", "ľstí", "ľstiam", nil, "ľstiach", "ľsťami")
elseif matches_any(word, {"cirkev", "reďkev"}) then
set_forms(forms,
{"dat_pl", "loc_pl"},
{stem .. "ám", stem .. "ách"}
)
end
return forms, title
end
declensions["mesto"] = function(word, genitive)
local forms = {}
local ultimate, penultimate = get_last_letters(word)
local special_type = ""
local stem = remove_suffix(word, "o")
if penultimate == "u" and ultimate == "m" then
stem = remove_suffix(word, "um")
special_type = ", of Latin origin ending with ''-um''"
elseif penultimate == "o" and ultimate == "n" then
stem = remove_suffix(word, "on")
special_type = ", of Greek origin ending with ''-on''"
elseif penultimate == "m" and ultimate == "ä" then
stem = remove_suffix(word, "ä") .. "en"
special_type = ", archaic type ending with ''-mä''"
end
-- Pluralia tantum
if NUMBER == "pl" then
stem = remove_suffix(word, ultimate)
end
local title = "မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု ''" .. word .. "'' <br>(pattern " .. pattern_link("mesto") .. special_type .. ")"
local stem_ultimate, stem_penultimate = get_last_letters(stem)
local loc_sg = "e"
if vowels_set[stem_ultimate] or matches_any(stem_ultimate, {"k", "g", "ch", "h"}) then
loc_sg = "u"
elseif matches_any(word, {"vnútro", "nebo"}) then
loc_sg = "i"
end
-- Split into syllables
local gen_pl = ""
local syllable_count, syllables = split_into_syllables(stem)
local word_syllable_count, word_syllables = split_into_syllables(word)
-- Get the last and second-to-last syllables for rule checks
local last_syllable = syllables[1]
local penultimate_syllable = syllables[2] or ""
local word_penultimate_syllable = word_syllables[2] or ""
-- 1. Add a vowel between the last two consonants of a cluster at the end of the stem
if ends_with_consonant_cluster(last_syllable) then
if matches_any(word, {"jedlo", "predjedlo", "jutro", "vrecko", "brvno"}) then
if matches_any(word, {"jedlo", "predjedlo", "jutro"}) then
gen_pl = remove_suffix(stem, stem_ultimate) .. "á" .. stem_ultimate
else
gen_pl = remove_suffix(stem, stem_ultimate) .. "ie" .. stem_ultimate
set_forms(forms, {"gen_pl2"}, {remove_suffix(stem, stem_ultimate) .. "á" .. stem_ultimate})
end
elseif sonorants_set[stem_ultimate] or ends_with_any(last_syllable, {"stv", "ctv", "íčk", "ečk", "očk"}) or TYPE == "dim" then
if stem_penultimate ~= "j" and (word_syllable_count == 1 or ends_with_any(last_syllable, {"stv", "ctv"}) or is_short(word_penultimate_syllable)) then
gen_pl = remove_suffix(stem, stem_ultimate) .. "ie" .. stem_ultimate
elseif obstruents_set[stem_ultimate] and (is_long(word_penultimate_syllable) or stem_penultimate == "j") then
gen_pl = remove_suffix(stem, stem_ultimate) .. "o" .. stem_ultimate
elseif sonorants_set[stem_ultimate] and (is_long(word_penultimate_syllable) or stem_penultimate == "j") then
gen_pl = remove_suffix(stem, stem_ultimate) .. "e" .. stem_ultimate
if stem_penultimate ~= "j" then
set_forms(forms, {"gen_pl2"}, {remove_suffix(stem, stem_ultimate) .. "ie" .. stem_ultimate})
end
end
end
end
-- 2. Add "í" if stem ends with a vowel or a soft consonant
if gen_pl == "" and (vowels_set[stem_ultimate] or soft_consonants_set[stem_ultimate]) then
gen_pl = stem .. "í"
end
-- 3. Return the stem with no change if conditions match
if gen_pl == "" and (is_long(penultimate_syllable)
or ends_with_any(stem, {"vojsk", "ov"})
or (TYPE == "loan" and matches_any(get_vowel(last_syllable), {"e", "o"}))) then
gen_pl = stem
end
-- 4. Logic to use the lengthened vowel if conditions match
if gen_pl == "" and (not (ends_with_consonant_cluster(last_syllable) or vowels_set[stem_ultimate])
or (ends_with_consonant_cluster(last_syllable) and obstruents_set[stem_ultimate])) then
gen_pl = remove_suffix(stem, last_syllable) .. lengthen_vowel(last_syllable)
end
local nom_pl = "á"
if NUMBER == "pl" then
nom_pl = ultimate
end
if gen_pl == "" then gen_pl = stem end
if (is_long(word_penultimate_syllable) or word == "jojo") and not ends_with(word, "ium") then
append_endings(forms, word, stem,
"a", "u", nil, loc_sg, "om",
"a", gen_pl, "am", nil, "ach", "ami"
)
else
append_endings(forms, word, stem,
"a", "u", nil, loc_sg, "om",
nom_pl, gen_pl, "ám", nil, "ách", "ami"
)
end
if matches_any(word, {"oko", "ucho"}) then
local pl_stem = (word == "oko") and "oč" or "uš"
append_second_plural(forms, pl_stem .. "i", pl_stem .. "í", pl_stem .. "iam",
nil, pl_stem .. "iach", pl_stem .. "ami")
switch_plural_forms(forms) --changes the prefered forms
set_forms(forms,
{"gen_pl2"},
{pl_stem .. "ú"}
)
elseif word == "nebo" then
set_forms(forms,
{"nom_pl", "nom_pl2", "gen_pl", "dat_pl", "dat_pl2", "acc_pl",
"acc_pl2", "loc_pl", "loc_pl2", "ins_pl"},
{"nebesá", "nebesia", "nebies", "nebesám", "nebesiam", "nebesá",
"nebesia", "nebesách", "nebesiach", "nebesami"}
)
end
return forms, title
end
declensions["srdce"] = function(word, genitive)
local forms = {}
local stem = remove_suffix(word, "e")
if ends_with(word, "ě") then
stem = remove_suffix(word, "ě")
end
local title = "မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု ''" .. word .. "'' <br>(pattern " .. pattern_link("srdce") .. ")"
if NUMBER == "pl" then
if ends_with(word, "ia") then
stem = remove_suffix(word, "ia")
else
stem = remove_suffix(word, "a")
end
end
if ends_with(stem, "[dtnl]") then
stem = soften_last_consonant(stem)
end
-- Split into syllables
local gen_pl = ""
local syllable_count, syllables = split_into_syllables(stem)
local word_syllable_count, word_syllables = split_into_syllables(word)
-- Get the last and second-to-last syllables for rule checks
local last_syllable = syllables[1]
local penultimate_syllable = syllables[2] or ""
local word_penultimate_syllable = word_syllables[2] or ""
local last_char = get_last_char(stem)
local stem_ultimate, stem_penultimate = get_last_letters(stem)
-- 1 & 2. Lengthen the last syllable's vowel if conditions apply,
-- otherwise leave the stem unchanged if penultimate syllable is long
if not ends_with_consonant_cluster(last_syllable)
or (ends_with(word, "ce") and matches_any(get_vowel(word_penultimate_syllable), {"r", "l"}) and TYPE ~= "dim")
or ends_with(word, "ište") then
if is_long(penultimate_syllable) then
gen_pl = stem -- If penultimate syllable of the stem is long, use the stem unchanged
else
-- Otherwise, lengthen the last syllable's vowel
gen_pl = remove_suffix(stem, last_syllable) .. lengthen_vowel(last_syllable)
end
end
-- 3. Add a vowel between the last two consonants if the conditions apply
if gen_pl == "" and (word == "citoslovce" or word == "vajce"
or ends_with(word, "ce")) then
if stem_penultimate ~= "j" and is_short(word_penultimate_syllable) then
gen_pl = remove_suffix(stem, stem_ultimate) .. "ie" .. stem_ultimate
elseif is_long(word_penultimate_syllable) or stem_penultimate == "j" then
gen_pl = remove_suffix(stem, stem_ultimate) .. "e" .. stem_ultimate
end
end
-- 4. Add "í" to the stem for specific words
if matches_any(word, {"more", "oje", "pole", "lože"}) then
gen_pl = harden_last_consonant(stem) .. "í"
end
-- Fallback: if none of the rules apply, return the original stem
if gen_pl == "" then gen_pl = stem end
if is_long(word_penultimate_syllable) or ends_with(stem, "j") then
append_endings(forms, word, stem,
"a", "u", nil, "i", "om",
"a", gen_pl, "am", nil, "ach", "ami"
)
else
append_endings(forms, word, stem,
"a", "u", nil, "i", "om",
"ia", gen_pl, "iam", nil, "iach", "ami"
)
end
return forms, title
end
declensions["vysvedčenie"] = function(word, genitive)
local forms = {}
local stem = remove_suffix(word, "ie")
if ends_with(word, "í") then
stem = remove_suffix(word, "í")
end
local title = "မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု ''" .. word .. "'' <br>(pattern " .. pattern_link("vysvedčenie") .. ")"
append_endings(forms, word, stem,
"ia", "iu", nil, "í", "ím",
"ia", stem .. "í", "iam", nil, "iach", "iami"
)
if word == "storočie" then
append_second_plural(forms, "stáročia", "stáročí", "stáročiam",
nil, "stáročiach", "stáročiami")
end
return forms, title
end
declensions["dievča"] = function(word, genitive)
local forms = {}
local stem = remove_suffix(word, "a")
if ends_with(word, "ä") then
stem = remove_suffix(word, "ä")
end
local ending = get_last_char(word)
local title = "မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု ''" .. word .. "'' <br>(pattern " .. pattern_link("dievča") .. ")"
local gen_pl = ""
local syllable_count, syllables = split_into_syllables(word)
local last_syllable = syllables[1]
local penultimate_syllable = syllables[2] or ""
if is_long(penultimate_syllable) then
gen_pl = stem .. ending .. "t"
else
gen_pl = append_ending(stem, "iat")
end
append_endings(forms, word, stem,
ending .. "ťa", ending .. "ťu", nil, ending .. "ti", ending .. "ťom",
ending .. "tá", gen_pl, ending .. "tám", nil, ending .. "tách", ending .. "tami"
)
local pl_stem = harden_last_consonant(stem)
append_second_plural(forms, pl_stem .. "ence", pl_stem .. "eniec", pl_stem .. "encom",
nil, pl_stem .. "encoch", pl_stem .. "encami")
if matches_any(word, {"kura", "strídža", "drumblence", "gajdence", "deťúrence"}) then
switch_plural_forms(forms) --changes the prefered forms
unset_alt_forms(forms)
elseif matches_any(word, {"páža", "knieža", "kurča", "plánča", "pôrča",
"zviera", "pachoľa", "mláďa", "dúpä", "chlápä", "žieňa", "nemluvňa"}) then
unset_alt_forms(forms)
elseif matches_any(word, {"prasa", "teľa", "šteňa"}) then
local pl_stem = (word == "šteňa") and "šten" or stem
append_second_plural(forms, pl_stem .. "ce", append_ending(pl_stem, "iec"),
pl_stem .. "com", nil, pl_stem .. "coch", pl_stem .. "cami")
switch_plural_forms(forms) --changes the prefered forms
elseif word == "dieťa" then
set_forms(forms,
{"nom_pl", "gen_pl", "dat_pl", "acc_pl", "loc_pl", "ins_pl"},
{"deti", "detí", "deťom", "deti", "deťoch", "deťmi"}
)
unset_alt_forms(forms)
end
return forms, title
end
declensions["နာမဝိသေသန"] = function(word, genitive)
local forms = {}
local title = "မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု ''" .. word .. "'' (adjective declension)"
local ultimate, penultimate = get_last_letters(word)
local lemma_form = word
if NUMBER == "pl" then
if ends_with(word, "é") then
lemma_form = remove_suffix(word, "é") .. "ý"
elseif ends_with(word, "ie") then
lemma_form = remove_suffix(word, "ie") .. "í"
elseif ends_with_any(word, {"ove", "ine"}) then
lemma_form = remove_suffix(word, "e")
elseif ends_with(word, "e") then
lemma_form = remove_suffix(word, "e") .. "y"
elseif ends_with(word, "í") and not soft_consonants_set[penultimate] then
lemma_form = remove_suffix(word, "í") .. "ý"
elseif ends_with(word, "i") and not soft_consonants_set[penultimate] then
lemma_form = remove_suffix(word, "i") .. "y"
end
else
if GENDER == "f" or GENDER == "n" then
if ends_with_any(word, {"á", "é"}) then
lemma_form = remove_last_char(word) .. "ý"
elseif ends_with_any(word, {"ia", "ie"}) then
lemma_form = remove_suffix(remove_last_char(word), "i") .. "í"
elseif ends_with_any(word, {"ova", "ovo", "ina", "ino"}) then
lemma_form = remove_last_char(word)
elseif ends_with_any(word, {"a", "e"}) then
lemma_form = remove_last_char(word) .. "y"
end
end
end
local forms_raw = m_adj.do_generate_forms({pagename=lemma_form}, "adjective").forms
set_forms(forms,
{"nom_sg"},
{forms_raw["nom_" .. get_first_char(GENDER)][1].form}
)
-- Singular
if GENDER == "f" then
set_forms(forms,
{"gen_sg", "dat_sg", "loc_sg", "ins_sg"},
{
forms_raw["gen_f"][1].form, forms_raw["dat_f"][1].form,
forms_raw["loc_f"][1].form, forms_raw["ins_f"][1].form
}
)
else
set_forms(forms,
{"gen_sg", "dat_sg", "loc_sg", "ins_sg"},
{
forms_raw["gen_mn"][1].form, forms_raw["dat_mn"][1].form,
forms_raw["loc_mn"][1].form, forms_raw["ins_mn"][1].form
}
)
end
-- special accusative singular
if GENDER == "m-pr" or GENDER == "m-anml" then
set_forms(forms,
{"acc_sg"},
{forms_raw["acc_m_an"][1].form}
)
elseif GENDER == "m-in" then
set_forms(forms,
{"acc_sg"},
{forms_raw["acc_m_in"][1].form}
)
else
set_forms(forms,
{"acc_sg"},
{forms_raw["acc_" .. get_first_char(GENDER)][1].form}
)
end
-- Plural
set_forms(forms,
{"gen_pl", "dat_pl", "loc_pl", "ins_pl"},
{
forms_raw["gen_p"][1].form, forms_raw["dat_p"][1].form,
forms_raw["loc_p"][1].form, forms_raw["ins_p"][1].form
}
)
if GENDER == "m-pr" then
set_forms(forms,
{"nom_pl", "acc_sg", "acc_pl"},
{forms_raw["nom_mp_an"][1].form, forms_raw["acc_m_an"][1].form, forms_raw["acc_mp_an"][1].form}
)
else
set_forms(forms,
{"nom_pl", "acc_pl"},
{forms_raw["nom_fnp"][1].form, forms_raw["acc_fnp"][1].form}
)
end
return forms, title
end
declensions["indeclinable"] = function(word, genitive)
local forms = {}
local title = "မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု ''" .. word .. "'' (indeclinable)"
local cases = {"nom", "gen", "dat", "acc", "loc", "ins"} -- List of cases
local numbers = {"sg", "pl"}
for _, case in ipairs(cases) do
for _, number in ipairs(numbers) do
local form_key = case .. "_" .. number
forms[form_key] = word
end
end
-- Surnames ending with -ů, -eje, -oje and -ovie
if TYPE == "surn" and ends_with_any(word, {"ů", "eje", "e", "oje", "ovie"}) then
append_second_plural(forms, word .. "ovci", word .. "ovcov", word .. "ovcom",
word .. "ovcov", word .. "ovcoch", word .. "ovcami")
end
return forms, title
end
--[=[
Partial declination functions
]=]--
function check_gender(gender)
if gender == nil then
return error("No gender entered. Please pass one of these values as parameter 1: m-pr, m-anml, m-in, f, n.")
elseif allowed_genders_set[gender] then
return gender
else
return error("Unknown gender. Please pass one of these values as parameter 1: m-pr, m-anml, m-in, f, n.")
end
end
function determine_pattern(word, genitive)
local pattern
local phon_word = (PRONUNCIATION) and PRONUNCIATION or word
local ultimate, penultimate = get_last_letters(phon_word)
local first = get_first_char(phon_word)
local dlan_endings = m_table.listToSet({"ň", "č", "ž", "ľ", "ď", "j", "š", "m", "z", "dz", "x"})
local dlan_exceptions_neg = m_table.listToSet({"reč", "seč", "lož", "beľ", "soľ", "mlaď", "meď", "myš", "voš"})
local dlan_exceptions_pos = m_table.listToSet({"obec", "pec", "čelusť", "kysť", "päsť", "Provence"})
local dlan_end_with_r_t = m_table.listToSet({"kader", "neter", "šír", "tvár",
"činovať", "drobäť", "droboť", "hať", "hrochoť", "Hrochoť", "hrsť", "inovať", "labuť",
"niť", "obeť", "paruť", "pažiť", "pečať", "perepúť", "perleť", "peruť", "pípeť", "plť",
"postať", "prť", "púť", "sieť", "sihoť", "stať", "štvrť", "trať", "úvrať", "vňať",
"violeť", "záhať", "žlť"})
local stroj_exceptions_pos = {"timbre", "cól", "gáfor", "hámor", "kôpor",
"kufor", "Pôtor", "šiator", "pedál", "sandál", "kanál", "peniaz", "daniel"}
local stroj_exceptions_neg = {"nesvár", "nešvár", "pár", "suchopár", "svár", "ker"}
if matches_any(ultimate, {"r", "l"}) and penultimate == "e" and not ends_with(phon_word, "ier") and genitive == nil then
genitive = phon_word .. "a"
end
if mw.ustring.find(phon_word, " ") or word == genitive then
return "indeclinable"
end
if NUMBER == "pl" then
if matches_any(ultimate, {"é", "i", "í"}) or (ultimate == "e" and ends_with(genitive, "ch")) then
pattern = "adjective"
elseif GENDER == "m-in" then
if ultimate == "y" then
pattern = "dub"
else
pattern = "stroj"
end
elseif GENDER == "f" then
if ultimate == "y" then
pattern = "žena"
else
pattern = "ulica"
end
elseif GENDER == "n" then
if ultimate == "á" then
pattern = "mesto"
elseif penultimate == "i" and ultimate == "a" then
pattern = "srdce"
else
if soft_consonants_set[penultimate] then
pattern = "srdce"
else
pattern = "mesto"
end
end
end
else
if GENDER == "m-pr" then
if (matches_any(ultimate, {"y", "ý", "i", "í"}) and not ends_with_any(genitive, {"[yií]ho", "a"})
and not (TYPE == "surn" and matches_any(penultimate .. ultimate, {"ay", "ai"})))
or (matches_any(penultimate .. ultimate, {"ov", "in"}) and ends_with_any(genitive, {"ovho", "inho"}))
then
pattern = "နာမဝိသေသန"
elseif ultimate == "a"
or (TYPE == "surn" and matches_any(ultimate, {"e", "ě"}) and (penultimate .. ultimate == "ně" or ends_with(genitive, "u")))
then
pattern = "hrdina"
elseif (matches_any(ultimate, {"i", "í", "y", "e", "é", "ä"})
or (TYPE == "name" and matches_any(ultimate, {"ü", "ö", "ő"}))
or matches_any(phon_word, {"Hrabě", "Poupě"}))
and not (TYPE == "surn" and (ends_with_any(phon_word, {"ay", "ai", "eje", "oje", "ovie"}) or ends_with(genitive, "a")))
then
pattern = "kuli"
elseif TYPE == "surn" and ends_with_any(phon_word, {"ů", "eje", "oje", "ovie"}) then
pattern = "indeclinable"
else
pattern = "chlap"
end
elseif GENDER == "m-in" or GENDER == "m-anml" then
if matches_any(ultimate, {"y", "ý", "i", "í"})
or (matches_any(penultimate .. ultimate, {"ov", "in"}) and ends_with(genitive, "ho"))
then
pattern = "နာမဝိသေသန"
elseif (soft_consonants_set[ultimate]
or (matches_any(ultimate, {"r", "l"}) and penultimate == "e"
and (ends_with(genitive, "[^e][rl]a")))
or ends_with_any(phon_word, {"ár", "iar", "ier"})
or matches_any(phon_word, stroj_exceptions_pos))
and not matches_any(phon_word, stroj_exceptions_neg)
then
pattern = "stroj"
else
pattern = "dub"
end
elseif GENDER == "f" then
if matches_any(phon_word, {"gazdiná", "švagriná", "testiná", "ujčiná", "stryná",
"kňažná", "kráľovná", "cisárovná", "cárovná", "šľachtičná", "princezná"
}) then
pattern = "gazdiná"
elseif ultimate == "a" and not ends_with(genitive, "ej") then
if soft_consonants_set[penultimate] or matches_any(penultimate, {"i", "y"}) or matches_any(phon_word, {"rozopra", "konopa", "večera"}) then
pattern = "ulica"
else
pattern = "žena"
end
elseif matches_any(ultimate, {"a", "á"})
or (ends_with_any(phon_word, {"ova", "ina"}) and ends_with(genitive, "ej"))
then
pattern = "adjective"
elseif ends_with(phon_word, "pani") or matches_any(phon_word, {"Mať", "mať", "mater", "mati"}) then
pattern = "irregular"
else
if not dlan_exceptions_neg[phon_word]
and (dlan_endings[ultimate] or dlan_exceptions_pos[phon_word]
or dlan_end_with_r_t[phon_word] or (penultimate == "š" and ultimate == "ť" and not is_capital(first)))
then
pattern = "dlaň"
else
pattern = "kosť"
end
end
if genitive then
local g_ultimate = get_last_char(genitive)
if g_ultimate == "y" then
pattern = "žena"
elseif g_ultimate == "i" then
pattern = "kosť"
end
end
elseif GENDER == "n" then
if (ultimate == "o" or (penultimate == "u" and ultimate == "m")
or (penultimate == "o" and ultimate == "n")
or (penultimate == "m" and ultimate == "ä"))
and not ends_with(genitive, "ho")
then
pattern = "mesto"
elseif (penultimate == "i" and ultimate == "e")
or ultimate == "í"
then
pattern = "vysvedčenie"
elseif (ultimate == "e" or ultimate == "ě")
and not ends_with(genitive, "ho")
then
pattern = "srdce"
elseif matches_any(ultimate, {"a", "ä"}) then
pattern = "dievča"
elseif matches_any(ultimate, {"e", "é"})
or ends_with_any(phon_word, {"ovo", "ino"})
then
pattern = "နာမဝိသေသန"
else
pattern = "indeclinable"
end
end
end
return pattern
end
function append_ending(stem1, ending)
if matches_any(get_first_char(ending), {"e", "i", "é", "í"}) then
return harden_last_consonant(stem1) .. ending
else
return stem1 .. ending
end
end
function append_endings(forms, word, stem, end2, end3, end4, end5, end6, end7, gen_pl, end9, end10, end11, end12)
forms["nom_sg"] = word
forms["gen_sg"] = append_ending(stem, end2)
forms["dat_sg"] = append_ending(stem, end3)
if GENDER == "m-pr"
or (GENDER == "f"
and (ends_with_any(word, {"pani", "a", "á"})
or matches_any(word, {"Mať", "mať", "mater", "mati"})))
then
forms["acc_sg"] = append_ending(stem, end4)
else
forms["acc_sg"] = word
end
forms["loc_sg"] = append_ending(stem, end5)
forms["ins_sg"] = append_ending(stem, end6)
local nom_pl = stem
if ends_with_any(stem, {"k", "ch"}) and matches_any(GENDER, {"m-pr", "m-anml"}) and end7 == "i" then
if ends_with(stem, "k") then
nom_pl = remove_suffix(stem, "k") .. "c"
else
nom_pl = remove_suffix(stem, "ch") .. "s"
end
end
forms["nom_pl"] = append_ending(nom_pl, end7)
forms["gen_pl"] = gen_pl
forms["dat_pl"] = append_ending(stem, end9)
if GENDER == "m-pr"
or end10
or (GENDER == "f"
and (ends_with_any(word, {"pani"})
or matches_any(word, {"Mať", "mať", "mater", "mati"})))
then
forms["acc_pl"] = append_ending(stem, end10)
else
forms["acc_pl"] = forms["nom_pl"]
end
forms["loc_pl"] = append_ending(stem, end11)
forms["ins_pl"] = append_ending(stem, end12)
end
function set_forms(forms, indices, values)
for i = 1, #indices do
forms[indices[i]] = values[i]
end
end
function switch_plural_forms(forms)
local indices = {"nom_pl", "gen_pl", "dat_pl", "acc_pl", "loc_pl", "ins_pl"}
for _, index in ipairs(indices) do
forms[index], forms[index .. "_alt"] = forms[index .. "_alt"], forms[index]
end
end
function unset_alt_forms(forms)
for key in pairs(forms) do
if mw.ustring.find(key, "_alt$") then
forms[key] = nil
end
end
end
function append_alternative_singular(forms, form2, form3, form4, form5, form6)
forms["gen_sg2"] = (forms["gen_sg"] ~= form2) and form2
forms["dat_sg2"] = (forms["dat_sg"] ~= form3) and form3
if matches_any(GENDER, {"m-pr", "m-anml"})
or (GENDER == "f"
and (ends_with_any(word, {"pani", "a", "á"})
or matches_any(word, {"Mať", "mať", "mater", "mati"})))
then
forms["acc_sg2"] = (forms["acc_sg"] ~= form4) and form4
end
forms["loc_sg2"] = (forms["loc_sg"] ~= form5) and form5
forms["ins_sg2"] = (forms["ins_sg"] ~= form6) and form6
end
function append_alternative_plural(forms, form1, form2, form3, form4, form5, form6)
forms["nom_pl2"] = (forms["nom_pl"] ~= form2) and form1
forms["gen_pl2"] = (forms["gen_pl"] ~= form2) and form2
forms["dat_pl2"] = (forms["dat_pl"] ~= form2) and form3
if form4 ~= nil and forms["acc_pl"] ~= form4 then
forms["acc_pl2"] = form4
elseif forms["acc_pl"] ~= form1 then
forms["acc_pl2"] = form1
end
forms["loc_pl2"] = (forms["loc_pl"] ~= form2) and form5
forms["ins_pl2"] = (forms["ins_pl"] ~= form2) and form6
end
function append_second_plural(forms, form1, form2, form3, form4, form5, form6)
forms["nom_pl_alt"] = form1
forms["gen_pl_alt"] = form2
forms["dat_pl_alt"] = form3
if form4 ~= nil then
forms["acc_pl_alt"] = form4
else
forms["acc_pl_alt"] = forms["nom_pl_alt"]
end
forms["loc_pl_alt"] = form5
forms["ins_pl_alt"] = form6
end
function append_animal_singular(forms, stem)
set_forms(forms,
{"gen_sg", "dat_sg", "acc_sg", "loc_sg"},
{
append_ending(stem, "a"),
append_ending(stem, "ovi"),
append_ending(stem, "a"),
append_ending(stem, "ovi")
}
)
end
function get_last_char(str)
local last = mw.ustring.sub(str, -1, -1)
return last
end
function get_first_char(str)
local first = mw.ustring.sub(str, 1, 1)
return first
end
function remove_last_char(str)
local stem = mw.ustring.sub(str, 1, -2)
return stem
end
function is_capital(str)
return mw.ustring.find(str, "[A-Z]")
end
function get_last_letters(word)
local ultimate = get_last_char(word)
local penultimate = get_last_char(remove_last_char(word))
local antepenultimate = get_last_char(remove_last_char(remove_last_char(word)))
if (penultimate == "c" and ultimate == "h")
or (penultimate == "d" and (ultimate == "z" or ultimate == "ž"))
then
ultimate = penultimate .. ultimate
penultimate = antepenultimate
antepenultimate = get_last_char(remove_last_char(remove_last_char(remove_last_char(word))))
end
if (antepenultimate == "c" and penultimate == "h")
or (antepenultimate == "d" and (penultimate == "z" or penultimate == "ž"))
then
penultimate = antepenultimate .. penultimate
end
if penultimate == "v" and consonants_set[ultimate] and not matches_any(ultimate, {"r", "l", "ŕ", "ĺ"}) then
penultimate = "ʋ"
end
return ultimate, penultimate
end
function ends_with(word, suffix)
-- Check if suffix matches the end of word
word = (word ~= nil) and word or ""
return mw.ustring.find(word, suffix .. "$") ~= nil
end
function ends_with_any(word, suffixes)
for _, suffix in ipairs(suffixes) do
if ends_with(word, suffix) then
return true -- Return true if any suffix matches
end
end
return false -- Return false if no suffix matches
end
function matches_any(value, list)
for _, item in ipairs(list) do
if value == item then
return true
end
end
return false
end
function get_vowel(syllable)
-- Define patterns for diphthongs and vowels
local diphthong_pattern = "ia|ie|iu|ô"
local vowel_pattern = "[aeiouyáéíóúýäöőüű]"
local syllabic_consonant_pattern = "[rl]"
-- Check for a diphthong first
local diphthong = mw.ustring.match(syllable, diphthong_pattern)
if diphthong then
return diphthong
end
-- Check for a single vowel
local vowel = mw.ustring.match(syllable, vowel_pattern)
if vowel then
return vowel
end
-- Check for a syllabic consonant if no vowel is found
local syllabic_consonant = mw.ustring.match(syllable, syllabic_consonant_pattern)
if syllabic_consonant then
return syllabic_consonant
end
return nil -- Return nil if no vowel or syllabic consonant is found
end
-- Function to split a word into characters and bigraphs
function split_into_letter_units(word)
local units = {}
local i_word = 1
local i_pron = 1
local word_len = mw.ustring.len(word)
local pron_len = PRONUNCIATION and mw.ustring.len(PRONUNCIATION) or 0
while i_word <= word_len do
local two_char = mw.ustring.sub(word, i_word, i_word + 1)
-- Check if PRONUNCIATION forbids combining here
local combine_forbidden = false
if PRONUNCIATION then
-- Advance pronunciation pointer to skip slashes until it aligns with actual letters
-- We only block combination if the *next* character in PRON is a slash
while i_pron <= pron_len and mw.ustring.sub(PRONUNCIATION, i_pron, i_pron) == "/" do
i_pron = i_pron + 1
end
-- Look ahead: is the *next* pronunciation char a slash?
if i_pron < pron_len then
local next_ch = mw.ustring.sub(PRONUNCIATION, i_pron + 1, i_pron + 1)
if next_ch == "/" then
combine_forbidden = true
end
end
end
-- Normal combination if allowed
if not combine_forbidden and (bigraphs_set[two_char] or diphthongs_set[two_char]) then
table.insert(units, two_char)
i_word = i_word + 2
i_pron = i_pron + 2 -- move pronunciation index along with it
else
-- Fallback: single character
local char = mw.ustring.sub(word, i_word, i_word)
table.insert(units, char)
i_word = i_word + 1
i_pron = i_pron + 1
end
end
return units
end
-- check if a unit is a vowel or syllabic element
function is_vowel(unit, prev_unit, next_unit)
if diphthongs_set[unit] then return true end
if vowels_set[unit] then return true end
-- Check if 'r' or 'l' are syllabic (preceded and followed by consonants)
if matches_any(unit, {"r", "ŕ", "l", "ĺ"})
and prev_unit and next_unit
and not (vowels_set[prev_unit] or vowels_set[next_unit]
or diphthongs_set[prev_unit] or diphthongs_set[next_unit])
then
return true
end
return false
end
-- Function to split a word into syllables according to Slovak rules
function split_into_syllables(word)
local units = split_into_letter_units(word)
local syllables = {}
local current_syllable = ""
local i = 1
local length = #units
local first_vowel_found = false -- Flag to indicate when the first vowel has been encountered
-- Iterate over the units in the word
while i <= length do
local unit = units[i]
local next_unit = i < length and units[i + 1] or nil
local previous_unit = i > 1 and units[i - 1] or nil
local is_current_vowel = is_vowel(unit, previous_unit, next_unit)
-- If we haven't encountered the first vowel, keep adding to the first syllable
if not first_vowel_found then
current_syllable = current_syllable .. unit
if is_current_vowel then
first_vowel_found = true -- Mark that the first vowel has been found
end
else
if is_current_vowel then
-- If a vowel is encountered after the first vowel has been found, finalize the current syllable
if current_syllable ~= "" and is_vowel(previous_unit, nil, nil) then
table.insert(syllables, current_syllable)
current_syllable = ""
end
current_syllable = current_syllable .. unit
else
-- Handling consonants between vowels
local consonant_cluster = unit
-- Collect any consecutive consonants into a cluster
local j = i + 1
while j <= length and not is_vowel(units[j], units[j - 1], units[j + 1]) do
consonant_cluster = consonant_cluster .. units[j]
j = j + 1
end
local consonant_count = mw.ustring.len(consonant_cluster)
if next_unit and is_vowel(next_unit, unit, units[j]) then
-- Apply syllable rules based on the number of consonants in the cluster
if consonant_count == 1 then
-- Rule 3: Single consonant goes to the next syllable
table.insert(syllables, current_syllable) -- End the current syllable without the consonant
current_syllable = consonant_cluster -- Start the next syllable with the consonant
elseif consonant_count == 2 then
-- Rule 4: Two consonants split between syllables
current_syllable = current_syllable .. mw.ustring.sub(consonant_cluster, 1, 1)
table.insert(syllables, current_syllable)
current_syllable = mw.ustring.sub(consonant_cluster, 2, 2)
else
-- Rule 5: Three or more consonants - first goes with current syllable, rest with next
current_syllable = current_syllable .. mw.ustring.sub(consonant_cluster, 1, 1)
table.insert(syllables, current_syllable)
current_syllable = mw.ustring.sub(consonant_cluster, 2)
end
i = j - 1 -- Adjust the index to skip the processed consonants
else
current_syllable = current_syllable .. unit
end
end
end
i = i + 1
end
-- Add any remaining characters as the final syllable
if #current_syllable > 0 then
table.insert(syllables, current_syllable)
end
-- Reverse the syllables array for the requested output order
local reversed_syllables = {}
for j = #syllables, 1, -1 do
reversed_syllables[#reversed_syllables + 1] = syllables[j]
end
-- Return the count of syllables and the reversed syllable array
return #syllables, reversed_syllables
end
function is_long(syllable)
return mw.ustring.find(syllable, "[áéíóúýôĺŕ]") ~= nil or mw.ustring.find(syllable, "i[aeu]") ~= nil
end
function is_short(syllable)
return not is_long(syllable)
end
local function get_last_consonant_before_vowel(syllable)
local vowel = get_vowel(syllable)
-- If there is no vowel in the syllable, return false
if not vowel then
return false
end
-- Find the position of the vowel in the syllable
local vowel_pos = mw.ustring.find(syllable, vowel)
-- Loop backwards from the position of the vowel to find the last consonant
for i = vowel_pos - 1, 1, -1 do
local char = mw.ustring.sub(syllable, i, i)
if not mw.ustring.find(char, "[aeiouáéíóúýôä]") then
return char -- Return the last consonant before the vowel
end
end
return false -- Return false if no consonant is found before the vowel
end
-- Function to lengthen the last vowel in a syllable if it’s not already long
function lengthen_vowel(syllable)
if is_long(syllable) then
return syllable -- Return as-is if the syllable is already long
end
local lengthening_map = {
["a"] = "á", ["i"] = "í", ["y"] = "ý", ["u"] = "ú",
["ä"] = "ia", ["e"] = "ie", ["o"] = "ô"
}
local cons_map = {
["ď"] = "d", ["ť"] = "t", ["ň"] = "n", ["ľ"] = "l"
}
-- Check for regular vowels first and replace if found
for vowel, long_vowel in pairs(lengthening_map) do
if mw.ustring.find(syllable, vowel) then
-- if there is a soft consonant before "a", it becomes "ia" instead of "á"
local last_cons = get_last_consonant_before_vowel(syllable) or ""
if soft_consonants_set[last_cons] and last_cons ~= "j" and vowel == "a" then
long_vowel = "ia"
end
-- if a or ä changes into "ia", the previous consonant should be written as hard
if matches_any(vowel, {"ä", "a"}) and matches_any(last_cons, {"ď", "ť", "ň", "ľ"}) then
syllable = mw.ustring.gsub(syllable, last_cons .. vowel, cons_map[last_cons] .. vowel, 1)
end
syllable = mw.ustring.gsub(syllable, vowel, long_vowel, 1)
return syllable -- Return immediately after replacing a regular vowel
end
end
-- Only replace "r" and "l" if no other vowels were found
if not mw.ustring.find(syllable, "[aeiouyáéíóúýôä]") then
syllable = mw.ustring.gsub(syllable, "r", "ŕ")
syllable = mw.ustring.gsub(syllable, "l", "ĺ")
end
return syllable
end
-- Helper function to determine if the syllable ends with a consonant cluster
function ends_with_consonant_cluster(last_syllable)
local last_char = get_last_char(last_syllable)
local second_last_char = mw.ustring.sub(last_syllable, -2, -2)
local third_last_char = mw.ustring.sub(last_syllable, -3, -3)
local last_two_chars = mw.ustring.sub(last_syllable, -2)
-- Check if the syllable contains any regular vowels
local has_vowel = mw.ustring.find(last_syllable, "[aeiouyáéíóúýôä]")
if has_vowel then
-- If the last two characters form a digraph, check the third-last character for a cluster
if bigraphs_set[last_two_chars] then
return not vowels_set[third_last_char]
else
-- If no digraph, just check the last two characters
return not (vowels_set[last_char] or vowels_set[second_last_char]
or diphthongs_set[last_char] or diphthongs_set[second_last_char])
end
else
-- No regular vowel; treat `r` or `l` as syllabic if either is in the last two characters
if matches_any(last_char, {'ŕ', 'ĺ'}) or matches_any(second_last_char, {'ŕ', 'ĺ'}) then
return false
elseif mw.ustring.find(last_syllable, "[bcčdďfghjkľmnňpqsštťvwxzž][rl][bcčdďfghjkľmnňpqysštťvwxzž][rl]$") then
return true
else
-- No syllabic `r` or `l`, so both characters are treated as consonants
return not vowels_set[last_char] and not vowels_set[second_last_char]
end
end
end
function remove_mobile_vowel(word)
local units = split_into_letter_units(word) -- Split word into units
for i = #units, 1, -1 do
local unit = units[i]
if matches_any(unit, {"e", "ie", "o", "i", "á"}) then
-- Remove the mobile vowel unit and reassemble the word
table.remove(units, i)
return table.concat(units)
end
end
return word -- Return the original word if no mobile vowel is found
end
function soften_last_consonant(str)
local consonants_t = { ["c"] = "č", ["d"] = "ď", ["l"] = "ľ", ["n"] = "ň",
["s"] = "š", ["t"] = "ť", ["z"] = "ž" }
local last_char = get_last_char(str)
-- Check if the last character is in consonants_t and replace if needed
return consonants_t[last_char] and remove_last_char(str) .. consonants_t[last_char] or str
end
function harden_last_consonant(str)
local soft_to_hard = { ["ď"] = "d", ["ť"] = "t", ["ň"] = "n", ["ľ"] = "l" }
local last_char = get_last_char(str)
-- Check if the last character is a soft consonant and replace if needed
return soft_to_hard[last_char] and remove_last_char(str) .. soft_to_hard[last_char] or str
end
function remove_suffix(form, suffix)
if mw.ustring.find(form, suffix .. "$") then
local length = mw.ustring.len(suffix)
return mw.ustring.sub(form, 1, -length-1)
end
return form
end
function pattern_link(pattern)
return "''[[Appendix:Slovak declension pattern " .. pattern .. "|" .. pattern .. "]]''"
end
function decline_with_more_stems(genitive)
local genitives = {}
for part in mw.ustring.gmatch(genitive, "[^/]+") do
table.insert(genitives, part)
end
local forms, title = declensions[PATTERN](PAGENAME, genitives[1])
normalize_forms(forms)
local forms2, title2 = declensions[PATTERN](PAGENAME, genitives[2])
normalize_forms(forms2)
append_alternative_singular(forms, forms2["gen_sg"], forms2["dat_sg"], forms2["acc_sg"],
forms2["loc_sg"], forms2["ins_sg"])
append_alternative_plural(forms, forms2["nom_pl"], forms2["gen_pl"], forms2["dat_pl"],
forms2["acc_pl"], forms2["loc_pl"], forms2["ins_pl"])
return forms, title
end
function split_into_units(expression, genitive)
-- Split the original expression into words
local words = {}
for word in mw.ustring.gmatch(expression, "%S+") do
table.insert(words, word)
end
-- Split the genitive phrase into words, if provided
local gen_words = {}
if genitive then
for word in mw.ustring.gmatch(genitive, "%S+") do
table.insert(gen_words, word)
end
-- Check if the genitive phrase has the same structure as the original
if #gen_words ~= #words then
error("Genitive phrase must have the same number of words as the original expression.")
end
end
-- Combine words into units, grouping prepositional phrases for both expressions
local units = {}
local gen_units = {}
local i = 1
while i <= #words do
if prepositions_set[words[i]] and words[i + 1] then
-- Combine preposition with the following words as one unit
local phrase = words[i]
local gen_phrase = genitive and gen_words[i] or nil
i = i + 1
while i <= #words do
phrase = phrase .. " " .. words[i]
if genitive then
gen_phrase = gen_phrase .. " " .. gen_words[i]
end
if i == #words then
table.insert(units, phrase)
if genitive then table.insert(gen_units, gen_phrase) end
end
i = i + 1
end
else
-- Add standalone word as a unit
table.insert(units, words[i])
if genitive then
table.insert(gen_units, gen_words[i])
end
i = i + 1
end
end
-- Return both units and genitive units
return units, gen_units
end
function generate_combined_forms(units, gen_units)
-- Process each unit to generate forms
local all_forms = {}
local patterns = ""
for i = 1, #units do
local unit = units[i]
local gen_unit = gen_units[i]
if conjunctions_set[unit] or unit == gen_unit then
-- Conjunctions are indeclinable
local forms = declensions["indeclinable"](unit, gen_unit)
table.insert(all_forms, forms)
else
-- Decline each unit using determine_pattern and declensions
local pattern = determine_pattern(unit, gen_unit)
local forms, title = declensions[pattern](unit, gen_unit)
normalize_forms(forms)
table.insert(all_forms, forms)
end
end
-- Combine forms into a single forms table
local combined_forms = {}
local cases = {"nom", "gen", "dat", "acc", "loc", "ins"}
local numbers = {"sg", "pl"}
-- check if vocative and plural 2 forms are necessary
for _, forms in ipairs(all_forms) do
if forms["voc_sg"] and #cases == 6 then
table.insert(cases, "voc")
end
if forms["nom_pl_alt"] and #numbers == 2 then
table.insert(numbers, "pl_alt")
end
end
for _, case in ipairs(cases) do
for _, number in ipairs(numbers) do
local form_key = case .. "_" .. number
local combined_form, highest_index = {}, 1
-- Gather primary and numbered alternative forms (e.g., gen_sg2, gen_sg3)
for _, forms in ipairs(all_forms) do
local primary_form = forms[form_key] or forms["nom_" .. number] or forms[case .. "_pl"]
table.insert(combined_form, primary_form)
-- Check the highest alternative index
for alt_index = 2, 4 do
local alt_key = form_key .. alt_index
if forms[alt_key] then
highest_index = alt_index
end
end
end
-- Combine primary forms
combined_forms[form_key] = mw.ustring.gsub(table.concat(combined_form, " "), "^%s+", "")
-- Combine alternative forms if present
if highest_index > 1 then
for alt_index = 2, highest_index do
-- Check and add numbered alternative forms
local alt_key = form_key .. alt_index
alt_form = {}
for _, forms in ipairs(all_forms) do
local primary_form = forms[form_key] or forms["nom_" .. number]
if forms[alt_key] then
table.insert(alt_form, forms[alt_key])
else
table.insert(alt_form, primary_form)
end
end
combined_forms[alt_key] = mw.ustring.gsub(table.concat(alt_form, " "), "^%s+", "")
end
end
end
end
return combined_forms
end
function normalize_forms(forms)
-- Step 1: Remove forms based on NUMBER
if NUMBER == "sg" then
for key in pairs(forms) do
if mw.ustring.find(key, "_pl") then
forms[key] = nil -- Remove plural forms if "n" is "sg"
end
end
elseif NUMBER == "pl" then
for key in pairs(forms) do
if mw.ustring.find(key, "_sg") then
forms[key] = nil -- Remove singular forms if "n" is "pl"
end
end
end
-- Step 2: Ensure all mandatory indices are set
local cases = {"nom", "gen", "dat", "acc", "loc", "ins"}
local numbers = {"sg", "pl"}
for _, case in ipairs(cases) do
for _, number in ipairs(numbers) do
local form_key = case .. "_" .. number
if not forms[form_key] then
forms[form_key] = "—" -- Set missing forms to —
end
end
end
-- Step 3: Create missing _pl_alt indices if at least one exists
local has_alt_plural = false
for key in pairs(forms) do
if mw.ustring.find(key, "_pl_alt") then
has_alt_plural = true
break
end
end
if has_alt_plural then
for _, case in ipairs(cases) do
local form_key_alt = case .. "_pl_alt"
if not forms[form_key_alt] then
forms[form_key_alt] = "—" -- Create missing _pl_alt forms
end
end
end
-- Step 4: Create vocative plural forms if voc_sg exists
if forms["voc_sg"] then
forms["voc_pl"] = forms["nom_pl"] -- Set voc_pl to nom_pl
if has_alt_plural then
forms["voc_pl_alt"] = forms["nom_pl_alt"] -- Set voc_pl_alt to nom_pl_alt if it exists
end
end
end
function specified_by_user(forms, args)
local cases = {nom = true, gen = true, dat = true, acc = true, voc = true, loc = true, ins = true}
local numbers = {sg = true, pl = true}
for key, value in pairs(args) do
-- Match the pattern "<case>_<number><optional digit>" using mw.ustring.match
local case, number, optional_digit = mw.ustring.match(key, "^(%a%a%a)_(%a%a)(%d?)$")
-- Check if it matches the cases and numbers you want
if case and cases[case] and number and numbers[number] then
forms[key] = make_link(value, case .. "|" .. get_first_char(number))
end
end
end
function make_link(link, accel_form)
local new_link = link
-- If link is not empty, valid, and not "—", create the full link
if link ~= "" and link and link ~= "—" then
new_link = m_links.full_link({lang = lang, term = link, accel = {form = accel_form}})
end
return new_link
end
function make_table_header(title)
return mw.getCurrentFrame():expandTemplate{
title = 'inflection-table-top',
args = {
title = title,
palette = 'blue',
tall = 'yes'
}
}
end
function make_simple_row(forms, case)
local case_code = mw.ustring.sub(case, 1, 3)
local row = "<tr><th>" .. case .. "</th>"
-- Singular
if NUMBER ~= "pl" then
row = row .. "<td><span lang=\"sk\">" .. make_link(forms[case_code .. "_sg"], case_code .. "|s") .. "</span>"
-- Loop to check and display secondary singular forms in the same cell
for i = 2, 4 do
local form_key = case_code .. "_sg" .. i
if not forms[form_key] then
break -- Exit loop if form doesn't exist
end
row = row .. ",<br /><span lang=\"sk\">" .. make_link(forms[form_key], case_code .. "|s") .. "</span>"
end
row = row .. "</td>"
end
-- Plural
if NUMBER ~= "sg" then
-- Primary plural form
row = row .. "<td><span lang=\"sk\">" .. make_link(forms[case_code .. "_pl"], case_code .. "|p") .. "</span>"
-- Loop to check and display secondary plural forms in the same cell
for i = 2, 4 do
local form_key = case_code .. "_pl" .. i
if not forms[form_key] then
break -- Exit loop if form doesn't exist
end
row = row .. ",<br /><span lang=\"sk\">" .. make_link(forms[form_key], case_code .. "|p") .. "</span>"
end
row = row .. "</td>"
-- Check for alternative plural and add it in a separate cell
if forms[case_code .. "_pl_alt"] then
row = row .. "<td><span lang=\"sk\">" .. make_link(forms[case_code .. "_pl_alt"], case_code .. "|p") .. "</span></td>"
end
end
row = row .. "</tr>"
return row
end
function make_table_header2(forms)
local tr_open = "<tr><th></th>"
local singular = "<th>singular</th>"
local plural = "<th>plural</th>"
local plural1 = "<th>plural 1</th>"
local plural2 = "<th>plural 2</th>"
local tr_close = "</tr>"
local alt_plural = false
if forms["nom_pl_alt"] or forms["gen_pl_alt"] or forms["dat_pl_alt"]
or forms["acc_pl_alt"] or forms["loc_pl_alt"] or forms["ins_pl_alt"]
then
alt_plural = true
end
local header
if NUMBER == "sg" then
header = tr_open .. singular .. tr_close
elseif NUMBER == "pl" then
if alt_plural then
header = tr_open .. plural1 .. plural2 .. tr_close
else
header = tr_open .. plural .. tr_close
end
elseif alt_plural then
header = tr_open .. singular .. plural1 .. plural2 .. tr_close
else
header = tr_open .. singular .. plural .. tr_close
end
return header
end
function make_table_footer()
return '\n' .. mw.getCurrentFrame():expandTemplate{ title = 'inflection-table-bottom' }
end
-- Make the table
function make_table(forms, title)
for key, form in pairs(forms) do
-- check for empty strings and nil's
if form == "" or not form then
forms[key] = "—"
end
end
local final = make_table_header(title)
final = final .. make_table_header2(forms)
final = final .. make_simple_row(forms, "မဒုၚ်ယၟု")
final = final .. make_simple_row(forms, "ဗဳဇဂကူ")
final = final .. make_simple_row(forms, "ပြကမ္မကာရက")
final = final .. make_simple_row(forms, "ကမ္မကာရက")
if forms["voc_sg"] then
final = final .. make_simple_row(forms, "ပရေၚ်ဂယိုၚ်လမျီု")
end
final = final .. make_simple_row(forms, "ခၞံဗဒှ်ဌာန်မတန်တဴ")
final = final .. make_simple_row(forms, "တိၚ်တိုက်ကပေါတ်ကွိၚ်ကွိုက်")
final = final .. make_table_footer()
return final
end
return export
80v8btzuj82bv49bezf5en6bgyezsyk
မဝ်ဂျူ:sk-adjective
828
295621
396469
396273
2026-06-07T12:02:13Z
咽頭べさ
33
396469
Scribunto
text/plain
local export = {}
--[=[
Authorship: Zhnka, heavily based on [[Module:cs-adjective]] by Benwing
]=]
--[=[
TERMINOLOGY:
-- "slot" = A particular combination of case/gender/number.
Example slot names for adjectives are "gen_f" (genitive feminine singular) and
"nom_mp_an" (animate nominative masculine plural). Each slot is filled with zero or more forms.
-- "form" = The declined Slovak form representing the value of a given slot.
-- "lemma" = The dictionary form of a given Slovak term. Generally the nominative
masculine singular, but may occasionally be another form if the nominative
masculine singular is missing.
]=]
local lang = require("Module:languages").getByCode("sk")
local m_links = require("Module:links")
local m_table = require("Module:table")
local m_string_utilities = require("Module:string utilities")
local iut = require("Module:inflection utilities")
local put = require("Module:parse utilities")
local com = require("Module:sk-common")
local en_utilities_module = "Module:en-utilities"
local u = mw.ustring.char
local rsplit = mw.text.split
local rfind = mw.ustring.find
local rmatch = mw.ustring.match
local rgmatch = mw.ustring.gmatch
local rsubn = mw.ustring.gsub
local ulen = mw.ustring.len
local uupper = mw.ustring.upper
-- 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
-- version of rsubn() that returns a 2nd argument boolean indicating whether
-- a substitution was made.
local function rsubb(term, foo, bar)
local retval, nsubs = rsubn(term, foo, bar)
return retval, nsubs > 0
end
-- All slots that are used by any of the different tables. The key is the slot and the value is a list of the tables
-- that use the slot. "" = regular, "plonly" = special=plonly in {{sk-adecl-manual}}, "numeral" = special=numeral in
-- {{sk-adecl-manual}}.
local input_adjective_slots = {
nom_m = {""},
nom_f = {""},
nom_n = {""},
nom_mp_an = {"", "plonly", "numeral"},
nom_mp_in = {"numeral"},
nom_fnp = {"", "plonly", "numeral"},
gen_mn = {""},
gen_f = {""},
gen_p = {"", "plonly", "numeral"},
dat_mn = {""},
dat_f = {""},
dat_p = {"", "plonly", "numeral"},
acc_m_an = {""},
acc_m_in = {""},
acc_f = {""},
acc_n = {""},
acc_mp_an = {"", "numeral"},
acc_fnp = {"", "numeral"},
ins_mn = {""},
ins_f = {""},
ins_p = {"", "plonly", "numeral"},
loc_mn = {""},
loc_f = {""},
loc_p = {"", "plonly", "numeral"},
short_m = {""},
short_f = {""},
short_n = {""},
short_mp_an = {""},
short_fnp = {""},
}
local output_adjective_slots = {
nom_m = "nom|m|s",
nom_m_linked = "nom|m|s", -- used in [[Module:sk-noun]]?
nom_f = "nom|f|s",
nom_n = "nom|n|s",
nom_mp_an = "an|nom|m|p",
nom_mp_in = "in|nom|m|p",
nom_mp = "nom|m|p",
nom_fnp = "in|nom|m|p|;|nom|f//n|p",
gen_mn = "gen|m//n|s",
gen_f = "gen|f|s",
gen_p = "gen|p",
dat_mn = "dat|m//n|s",
dat_f = "dat|f|s",
dat_p = "dat|p",
acc_m_an = "an|acc|m|s",
acc_m_in = "in|acc|m|s",
acc_f = "acc|f|s",
acc_n = "acc|n|s",
acc_mp_an = "an|acc|m|p",
acc_mp_in = "in|acc|m|p",
acc_fnp = "in|acc|m|p|;|acc|f//n|p",
ins_mn = "ins|m//n|s",
ins_f = "ins|f|s",
ins_p = "ins|p",
loc_mn = "loc|m//n|s",
loc_f = "loc|f|s",
loc_p = "loc|p",
short_m = "short|m|s",
short_f = "short|f|s",
short_n = "short|n|s",
short_mp_an = "short|an|m|p",
short_fnp = "short|in|m|p|;|short|f//n|p",
}
local function get_output_adjective_slots(alternant_multiword_spec)
return output_adjective_slots
end
local function combine_stem_ending(stem, ending)
if stem == "?" then
return "?"
else
return stem .. ending
end
end
local function add(base, slot, stems, endings, footnote)
if stems then
stems = iut.combine_form_and_footnotes(stems, footnote)
end
iut.add_forms(base.forms, slot, stems, endings, combine_stem_ending)
end
local function add_normal_decl(base, stems,
nom_m, nom_f, nom_n, nom_mp_an, nom_fnp,
gen_mn, gen_f, gen_p,
dat_mn, dat_f, dat_p,
acc_f,
loc_mn, loc_f, loc_p,
ins_mn, ins_f, ins_p,
footnote)
if stems then
stems = iut.combine_form_and_footnotes(stems, footnote)
end
add(base, "nom_m", stems, nom_m)
add(base, "nom_f", stems, nom_f)
add(base, "nom_n", stems, nom_n)
add(base, "nom_mp_an", stems, nom_mp_an)
add(base, "nom_fnp", stems, nom_fnp)
add(base, "gen_mn", stems, gen_mn)
add(base, "gen_f", stems, gen_f)
add(base, "gen_p", stems, gen_p)
add(base, "dat_mn", stems, dat_mn)
add(base, "dat_f", stems, dat_f)
add(base, "dat_p", stems, dat_p)
add(base, "acc_f", stems, acc_f)
add(base, "loc_mn", stems, loc_mn)
add(base, "loc_f", stems, loc_f)
add(base, "loc_p", stems, loc_p)
add(base, "ins_mn", stems, ins_mn)
add(base, "ins_f", stems, ins_f)
add(base, "ins_p", stems, ins_p)
end
local function add_short_decl(base, stems, m, f, n, mp_an, fnp, footnote)
if stems then
stems = iut.combine_form_and_footnotes(stems, footnote)
end
add(base, "short_m", stems, m)
add(base, "short_f", stems, f)
add(base, "short_n", stems, n)
add(base, "short_mp_an", stems, mp_an)
add(base, "short_fnp", stems, fnp)
end
local decls = {}
decls["normal"] = function(base)
local stem, suffix
-- hard-long in -ý
stem, suffix = rmatch(base.lemma, "^(.*)(ý)$")
if stem then
add_normal_decl(base, stem,
"ý", "á", "é", "í", "é",
"ého", "ej", "ých",
"ému", "ej", "ým",
"ú",
"om", "ej", "ých",
"ým", "ou", "ými"
)
if base.short then
for _, short_stem_obj in ipairs(base.short) do
add_short_decl(base, short_stem_obj.base, "")
add_short_decl(base, short_stem_obj.stem, nil, "a", "o", "i", "y")
end
end
return
end
-- hard-short in -y
stem, suffix = rmatch(base.lemma, "^(.*)(y)$")
if stem then
add_normal_decl(base, stem,
"y", "a", "e", "i", "e",
"eho", "ej", "ych",
"emu", "ej", "ym",
"u",
"om", "ej", "ych",
"ym", "ou", "ymi"
)
return
end
-- soft-long in -í
stem, suffix = rmatch(base.lemma, "^(.*)(í)$")
if stem then
add_normal_decl(base, stem,
"í", "ia", "ie", "í", "ie",
"ieho", "ej", "ích",
"iemu", "ej", "ím",
"iu",
{}, "ej", "ích",
"ím", {}, "ími"
)
add_normal_decl(base, com.convert_paired_plain_to_palatal(stem, ending), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, "om", nil, nil, nil, "ou")
return
end
-- soft-short in -i
stem, suffix = rmatch(base.lemma, "^(.*)(i)$")
if stem then
add_normal_decl(base, stem,
"i", {}, "e", "i", "e",
"eho", "ej", "ich",
"emu", "ej", "im",
{},
{}, "ej", "ich",
"im", {}, "imi"
)
add_normal_decl(base, com.convert_paired_plain_to_palatal(stem, ending), nil, "a", nil, nil, nil, nil, nil, nil, nil, nil, nil, "u", "om", nil, nil, nil, "ou")
return
end
-- possessive in -ov
stem, suffix = rmatch(base.lemma, "^(.*)(ov)$")
if stem then
add_normal_decl(base, stem,
"ov", "ova", "ovo", "ovi", "ove",
"ovho", "ovej", "ových",
"ovmu", "ovej", "ovým",
"ovu",
"ovom", "ovej", "ových",
"ovým", "ovou", "ovými"
)
return
end
-- possessive in -in
stem, suffix = rmatch(base.lemma, "^(.*)(in)$")
if stem then
add_normal_decl(base, stem,
"in", "ina", "ino", "ini", "ine",
"inho", "inej", "iných",
"inmu", "inej", "iným",
"inu",
"inom", "inej", "iných",
"iným", "inou", "inými"
)
return
end
error("Unrecognized adjective lemma, should end in '-ý', '-y', '-í', '-i', '-ov' or '-in': '" .. base.lemma .. "'")
end
decls["irreg"] = function(base)
local stem, suffix
if base.lemma == "môj" then
add_normal_decl(base, "",
"môj", "moja", "moje", "moji", "moje",
"môjho", "mojej", "mojich",
"môjmu", "mojej", "mojim",
"moju",
"mojom", "mojej", "mojich",
"mojím", "mojou", "mojimi"
)
return
end
-- determiner like tvoj
stem, suffix = rmatch(base.lemma, "^(.*)(oj)$")
if stem then
add_normal_decl(base, stem,
"oj", "oja", "oje", "oji", "oje",
"ojho", "ojej", "ojich",
"ojmu", "ojej", "ojim",
"oju",
"ojom", "ojej", "ojich",
"ojím", "ojou", "ojimi"
)
return
end
if base.lemma == "jeden" then
add_normal_decl(base, "",
"jeden", "jedna", "jedno", "jedni", "jedny",
"jedného", "jednej", "jedných",
"jednému", "jednej", "jedným",
"jednu",
"jednom", "jednej", "jedných",
"jedným", "jednou", "jednými"
)
return
end
if base.lemma == "všetok" then
add_normal_decl(base, "",
"všetok", "všetka", "všetko", "všetci", "všetky",
"všetkého", "všetkej", "všetkých",
"všetkému", "všetkej", "všetkým",
"všetku",
"všetkom", "všetkej", "všetkých",
"všetkým", "všetkou", "všetkými"
)
return
end
-- determiner like [[ten]], [[tamten]], [[onen]]
stem, suffix = rmatch(base.lemma, "^(.*)(en)$")
if stem then
local nom_stem = stem .. suffix
add_normal_decl(base, nom_stem, "")
add_normal_decl(base, stem,
nil, "á", "o", "í", "ie",
"oho", "ej", "ých",
"omu", "ej", "ým",
"ú",
"om", "ej", "ých",
"ým", "ou", "ými"
)
return
end
if base.lemma == "tento" then
add_normal_decl(base, "",
"tento", "táto", "toto", "títo", "tieto",
"tohto", "tejto", "týchto",
"tomuto", "tejto", "týmto",
"tuto",
"tomto", "tejto", "týchto",
"týmto", "touto", "týmito"
)
return
end
if base.lemma == "všetok" then
add_normal_decl(base, nom_stem, "")
add_normal_decl(base, stem,
nil, "á", "o", "í", "ie",
"oh", "ej", "ých",
"omu", "ej", "ým",
"ú",
"om", "ej", "ých",
"ým", "ou", "ými"
)
return
end
-- [[náš]], [[váš]]
stem, suffix = rmatch(base.lemma, "^(.*)(áš)$")
if stem then
add_normal_decl(base, stem,
"áš", "aša", "aše", "aši", "aše",
"ášho", "ašej", "ašich",
"ášmu", "ašej", "ašim",
"ašu",
"ašom", "ašej", "ašich",
"aším", "ašou", "ašimi"
)
return
end
if base.lemma == "sám" then
add_normal_decl(base, "sám", "")
add_normal_decl(base, "sam",
nil, "a", "o", "i", "y",
"ého", "ej", "ých",
"ému", "ej", "ým",
"u",
"om", "ej", "ých",
"ým", "ou", "ými"
)
return
end
error("Unrecognized irregular lemma '" .. base.lemma .. "'")
end
local function fetch_footnotes(separated_group)
local footnotes
for j = 2, #separated_group - 1, 2 do
if separated_group[j + 1] ~= "" then
error("Extraneous text after bracketed footnotes: '" .. table.concat(separated_group) .. "'")
end
if not footnotes then
footnotes = {}
end
table.insert(footnotes, separated_group[j])
end
return footnotes
end
local function parse_indicator_spec(angle_bracket_spec)
local inside = rmatch(angle_bracket_spec, "^<(.*)>$")
assert(inside)
local base = {forms = {}}
if inside ~= "" then
local parts = rsplit(inside, ".", true)
for _, part in ipairs(parts) do
if part == "irreg" then
base.irreg = true
elseif part == "short" then
base.short = {{
base = {
form = "+",
},
stem = {
form = "+",
}
}}
elseif rfind(part, "^short:") then
part = rsub(part, "^short:%s*", "")
base.short = {}
local segments = put.parse_balanced_segment_run(part, "[", "]")
local comma_separated_groups = put.split_alternating_runs(segments, "%s*,%s*")
for _, comma_separated_group in ipairs(comma_separated_groups) do
if comma_separated_group[1] == "*" then
-- reducible
table.insert(base.short, {
base = {
form = "*",
footnotes = fetch_footnotes(comma_separated_group),
},
stem = {
form = "*",
footnotes = fetch_footnotes(comma_separated_group),
}
})
else
local slash_separated_groups = put.split_alternating_runs(comma_separated_group, "%s*/%s*")
if #slash_separated_groups > 2 then
error("Too many slash-separated stems: '" .. inside .. "'")
end
local short_base = slash_separated_groups[1]
local short_stem = slash_separated_groups[2]
local short_base_obj = {
form = short_base[1],
footnotes = fetch_footnotes(short_base),
}
local short_stem_obj
if short_stem then
short_stem_obj = {
form = short_stem[1],
footnotes = fetch_footnotes(short_stem),
}
end
table.insert(base.short, {
base = short_base_obj,
stem = short_stem_obj,
})
end
iut.insert_form(forms, slot, formobj)
end
else
error("Unrecognized indicator '" .. part .. "': '" .. inside .. "'")
end
end
end
return base
end
local function normalize_all_lemmas(alternant_multiword_spec, pagename)
iut.map_word_specs(alternant_multiword_spec, function(base)
if base.lemma == "" then
base.lemma = pagename
end
base.orig_lemma = base.lemma
base.orig_lemma_no_links = m_links.remove_links(base.lemma)
base.lemma = base.orig_lemma_no_links
end)
end
local function detect_indicator_spec(base)
if base.short then
if not base.lemma:find("ý$") then
error("Short forms can only be specified for lemmas ending in -ý, but saw '" .. base.lemma .. "'")
end
local stem = rmatch(base.lemma, "^(.*)ý$")
for _, short_spec in ipairs(base.short) do
if short_spec.base.form == "+" then
short_spec.base.form = stem
elseif short_spec.base.form == "*" then
short_spec.base.form = com.dereduce(base, stem)
if not short_spec.base.form then
error("Unable to construct non-reduced variant of stem '" .. stem .. "'")
end
end
if not short_spec.stem then
short_spec.stem = {
form = short_spec.base.form,
footnotes = short_spec.base.footnotes
}
end
if short_spec.stem.form == "+" or short_spec.stem.form == "*" then
short_spec.stem.form = stem
end
end
end
if base.irreg then
base.decl = "irreg"
else
base.decl = "normal"
end
end
local function detect_all_indicator_specs(alternant_multiword_spec)
iut.map_word_specs(alternant_multiword_spec, function(base)
detect_indicator_spec(base)
end)
end
local function decline_adjective(base)
if not decls[base.decl] then
error("Internal error: Unrecognized declension type '" .. base.decl .. "'")
end
decls[base.decl](base)
-- handle_derived_slots_and_overrides(base)
end
-- Process override for the arguments in `args`, storing the results into `forms`. If `do_acc`, only do accusative
-- slots; otherwise, don't do accusative slots.
local function process_overrides(forms, args, do_acc)
for slot, _ in pairs(input_adjective_slots) do
if args[slot] and not not do_acc == not not slot:find("^acc") then
forms[slot] = nil
if args[slot] ~= "-" and args[slot] ~= "—" then
local segments = put.parse_balanced_segment_run(args[slot], "[", "]")
local comma_separated_groups = put.split_alternating_runs(segments, "%s*,%s*")
for _, comma_separated_group in ipairs(comma_separated_groups) do
local formobj = {
form = comma_separated_group[1],
footnotes = fetch_footnotes(comma_separated_group),
}
iut.insert_form(forms, slot, formobj)
end
end
end
end
end
local function check_allowed_overrides(alternant_multiword_spec, args)
local special = alternant_multiword_spec.special or alternant_multiword_spec.surname and "surname" or ""
for slot, types in pairs(input_adjective_slots) do
if args[slot] then
local allowed = false
for _, typ in ipairs(types) do
if typ == special then
allowed = true
break
end
end
if not allowed then
error(("Override %s= not allowed for %s"):format(slot, special == "" and "regular declension" or
"special=" .. special))
end
end
end
end
local function set_accusative(alternant_multiword_spec)
local forms = alternant_multiword_spec.forms
local function copy_if(from_slot, to_slot)
if not forms[to_slot] then
iut.insert_forms(forms, to_slot, forms[from_slot])
end
end
copy_if("nom_n", "acc_n")
copy_if("gen_mn", "acc_m_an")
copy_if("nom_m", "acc_m_in")
copy_if("gen_p", "acc_mp_an")
copy_if("nom_mp_in", "acc_mp_in")
copy_if("nom_fnp", "acc_fnp")
end
local function add_categories(alternant_multiword_spec)
local cats = {}
local plpos = require(en_utilities_module).pluralize(alternant_multiword_spec.pos or "နာမဝိသေသန")
local function insert(cattype)
-- m_table.insertIfNot(cats, "Slovak " .. cattype .. " " .. plpos)
end
if not alternant_multiword_spec.manual then
iut.map_word_specs(alternant_multiword_spec, function(base)
if base.decl == "irreg" then
insert("irregular")
elseif rfind(base.lemma, "ý$") then
insert("hard long")
elseif rfind(base.lemma, "y$") then
insert("hard short")
elseif rfind(base.lemma, "í$") then
insert("soft long")
elseif rfind(base.lemma, "i$") then
insert("soft short")
else
insert("possessive")
end
if base.short then
-- table.insert(cats, "ဝေါဟာသလဝ်ဝေန်နဳယျာ" .. plpos .. "မၞုံဗီုပြၚ်ဂၠေံဂၠေံဂမၠိုၚ်")
end
end)
end
alternant_multiword_spec.categories = cats
end
local function show_forms(alternant_multiword_spec)
local lemmas = {}
local lemmaform = alternant_multiword_spec.forms.nom_m or alternant_multiword_spec.forms.nom_mp or
alternant_multiword_spec.forms.nom_mp_an
if lemmaform then
for _, form in ipairs(lemmaform) do
table.insert(lemmas, form.form)
end
end
local props = {
lemmas = lemmas,
slot_table = get_output_adjective_slots(alternant_multiword_spec),
lang = lang,
}
iut.show_forms(alternant_multiword_spec.forms, props)
end
local function make_table(alternant_multiword_spec)
local forms = alternant_multiword_spec.forms
local function template_prelude()
return mw.getCurrentFrame():expandTemplate{
title = 'inflection-table-top',
args = {
title = '{title}{annotation}',
palette = 'blue',
tall = 'yes',
}
}
end
local function template_postlude()
return mw.getCurrentFrame():expandTemplate{
title = 'inflection-table-bottom',
args = {
notes = '{notes_clause}' or nil
}
}
end
local table_spec_sg = [=[
! colspan=5 class="outer" | ကိုန်ဨကဝုစ်
|-
! rowspan="2" |
! colspan="2" | ပုလ္လိၚ်
! rowspan="2" | ဣတ္တိလိၚ်
! rowspan="2" | နပုလ္လိၚ်
|-
! class="secondary" | လမျီုလုပ်ကၠုၚ်
! class="secondary" | မသက္ကုဟၟဲကဵုလမျီု
|-
! မဒုၚ်ယၟု
| colspan=2 | {nom_m}
| {nom_f}
| {nom_n}
|-
! ဗဳဇဂကူ
| colspan=2 | {gen_mn}
| {gen_f}
| {gen_mn}
|-
! ပြကမ္မကာရက
| colspan=2 | {dat_mn}
| {dat_f}
| {dat_mn}
|-
! ကမ္မကာရက
| {acc_m_an}
| {acc_m_in}
| {acc_f}
| {acc_n}
|-
! ခၞံဗဒှ်ဌာန်မတန်တဴ
| colspan=2 | {loc_mn}
| {loc_f}
| {loc_mn}
|-
! တိၚ်တိုက်ကပေါတ်ကွိၚ်ကွိုက်
| colspan=2 | {ins_mn}
| {ins_f}
| {ins_mn}{short_sg_clause}
]=]
local table_spec_pl = [=[
! colspan=5 class="outer" | ကိုန်ဗဟုဝစ်
|-
! rowspan="2" |
! colspan="2" | ပုလ္လိၚ်
! colspan="2" rowspan="2" | ဣတ္တိလိၚ်/နပုလ္လိၚ်
|-
! class="secondary" | <abbr title="ပူဂဵုပုလ္လိၚ်">virile</abbr>
! class="secondary" | <abbr title="ပုလ္လိၚ်မသက္ကုဟၟဲကဵုလမျီု ဝါ မသက္ကုဟၟဲကဵုလမျီု">nonvirile</abbr>
|-
! မဒုၚ်ယၟု
| {nom_mp_an}
| colspan=3 | {nom_fnp}
|-
! ဗဳဇဂကူ
| colspan=4 | {gen_p}
|-
! ပြကမ္မကာရက
| colspan=4 | {dat_p}
|-
! ကမ္မကာရက
| {acc_mp_an}
| colspan=3 | {acc_fnp}
|-
! ခၞံဗဒှ်ဌာန်မတန်တဴ
| colspan=4 | {loc_p}
|-
! တိၚ်တိုက်ကပေါတ်ကွိၚ်ကွိုက်
| colspan=4 | {ins_p}{short_pl_clause}
]=]
local table_spec = template_prelude("55") .. table_spec_sg .. "|-\n" .. table_spec_pl .. template_postlude()
local table_spec_plonly = template_prelude("55") .. table_spec_pl .. template_postlude()
local table_spec_numeral = template_prelude("40") .. [=[
! colspan=5 class="outer" | ကိုန်ဗဟုဝစ်
|-
! rowspan="2" colspan="2" |
! colspan="2" | ပုလ္လိၚ်
! rowspan="2" | ဣတ္တိလိၚ်/နပုလ္လိၚ်
|-
! class="secondary" | လမျီုလုပ်ကၠုၚ်
! class="secondary" | မသက္ကုဟၟဲကဵုလမျီု
|-
! colspan="2" | မဒုၚ်ယၟု
| {nom_mp_an}
| {nom_mp_in}
| {nom_fnp}
|-
! colspan="2" | ဗဳဇဂကူ
| colspan="3" | {gen_p}
|-
! colspan="2" | ပြကမ္မကာရက
| colspan="3" | {dat_p}
|-
! colspan="2" | ကမ္မကာရက
| {acc_mp_an}
| {acc_mp_in}
| {acc_fnp}
|-
! colspan="2" | ခၞံဗဒှ်ဌာန်မတန်တဴ
| colspan="3" | {loc_p}
|-
! colspan="2" | တိၚ်တိုက်ကပေါတ်ကွိၚ်ကွိုက်
| colspan="3" | {ins_p}
]=] .. template_postlude()
local short_sg_template = [=[
|-
! ဓမၠေံ
| colspan=2 | {short_m}
| {short_f}
| {short_n}
]=]
local short_pl_template = [=[
|-
! ဓမၠေံ
| {short_mp_an}
| colspan=3 | {short_fnp}]=]
local table_spec = template_prelude("55") .. table_spec_sg .. "|-\n" .. table_spec_pl .. template_postlude()
local table_spec_plonly = template_prelude("55") .. table_spec_pl .. template_postlude()
if alternant_multiword_spec.title then
forms.title = alternant_multiword_spec.title
else
forms.title = 'မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု <i lang="sk">' .. forms.lemma .. '</i>'
end
if alternant_multiword_spec.manual then
forms.annotation = ""
else
local ann_parts = {}
local decls = {}
iut.map_word_specs(alternant_multiword_spec, function(base)
if base.decl == "irreg" then
m_table.insertIfNot(decls, "irregular")
elseif rfind(base.lemma, "ý$") then
m_table.insertIfNot(decls, "hard-long")
elseif rfind(base.lemma, "y$") then
m_table.insertIfNot(decls, "hard-short")
elseif rfind(base.lemma, "í$") then
m_table.insertIfNot(decls, "soft-long")
elseif rfind(base.lemma, "i$") then
m_table.insertIfNot(decls, "soft-short")
else
m_table.insertIfNot(decls, "possessive")
end
end)
table.insert(ann_parts, table.concat(decls, " // "))
forms.annotation = " (" .. table.concat(ann_parts, ", ") .. ")"
end
forms.notes_clause = forms.footnote ~= "" and forms.footnote or ""
forms.short_sg_clause = forms.short_m and forms.short_m ~= "—" and
m_string_utilities.format(short_sg_template, forms) or ""
forms.short_pl_clause = forms.short_mp_an and forms.short_mp_an ~= "—" and
m_string_utilities.format(short_pl_template, forms) or ""
return m_string_utilities.format(
alternant_multiword_spec.special == "plonly" and table_spec_plonly or
alternant_multiword_spec.special == "ဂၞန်သၚ်္ချာ" and table_spec_numeral or
table_spec, forms
)
end
-- Externally callable function to parse and decline an adjective given
-- user-specified arguments. Return value is WORD_SPEC, an object where the
-- declined forms are in `WORD_SPEC.forms` for each slot. If there are no values
-- for a slot, the slot key will be missing. The value for a given slot is a
-- list of objects {form=FORM, footnotes=FOOTNOTES}.
function export.do_generate_forms(parent_args, pos, from_headword, def)
local params = {
[1] = {template_default = "pekný"},
pos = true,
json = {type = "boolean"}, -- for use with bots
title = true,
pagename = true,
}
for slot, _ in pairs(input_adjective_slots) do
params[slot] = true
end
-- Only default param 1 when displaying the template.
local args = require("Module:parameters").process(parent_args, params)
local pagename = args.pagename or mw.loadData("Module:headword/data").pagename
local args1 = args[1] or pagename
local parse_props = {
parse_indicator_spec = parse_indicator_spec,
allow_default_indicator = true,
allow_blank_lemma = true,
}
local alternant_multiword_spec = iut.parse_inflected_text(args1, parse_props)
alternant_multiword_spec.pos = args.pos
alternant_multiword_spec.title = args.title
alternant_multiword_spec.forms = {}
normalize_all_lemmas(alternant_multiword_spec, pagename)
detect_all_indicator_specs(alternant_multiword_spec)
check_allowed_overrides(alternant_multiword_spec, args)
local inflect_props = {
slot_table = get_output_adjective_slots(alternant_multiword_spec),
inflect_word_spec = decline_adjective,
}
iut.inflect_multiword_or_alternant_multiword_spec(alternant_multiword_spec, inflect_props)
-- Do non-accusative overrides so they get copied to the accusative forms appropriately.
process_overrides(alternant_multiword_spec.forms, args)
set_accusative(alternant_multiword_spec)
-- Do accusative overrides after copying the accusative forms.
process_overrides(alternant_multiword_spec.forms, args, "do acc")
add_categories(alternant_multiword_spec)
if args.json and not from_headword then
return require("Module:JSON").toJSON(alternant_multiword_spec)
end
return alternant_multiword_spec
end
-- Externally callable function to parse and decline an adjective where all
-- forms are given manually. Return value is WORD_SPEC, an object where the
-- declined forms are in `WORD_SPEC.forms` for each slot. If there are no values
-- for a slot, the slot key will be missing. The value for a given slot is a
-- list of objects {form=FORM, footnotes=FOOTNOTES}.
function export.do_generate_forms_manual(parent_args, pos, from_headword, def)
local params = {
pos = true,
special = true,
json = {type = "boolean"}, -- for use with bots
title = true,
}
for slot, _ in pairs(input_adjective_slots) do
params[slot] = true
end
local args = require("Module:parameters").process(parent_args, params)
local alternant_multiword_spec = {
pos = args.pos,
special = args.special,
title = args.title,
forms = {},
manual = true,
}
check_allowed_overrides(alternant_multiword_spec, args)
-- Do non-accusative overrides so they get copied to the accusative forms appropriately.
process_overrides(alternant_multiword_spec.forms, args)
set_accusative(alternant_multiword_spec)
-- Do accusative overrides after copying the accusative forms.
process_overrides(alternant_multiword_spec.forms, args, "do acc")
add_categories(alternant_multiword_spec)
if args.json and not from_headword then
return require("Module:JSON").toJSON(alternant_multiword_spec)
end
return alternant_multiword_spec
end
-- Entry point for {{sk-adecl}}. Template-callable function to parse and decline
-- an adjective given user-specified arguments and generate a displayable table
-- of the declined forms.
function export.show(frame)
local parent_args = frame:getParent().args
local alternant_multiword_spec = export.do_generate_forms(parent_args)
show_forms(alternant_multiword_spec)
return make_table(alternant_multiword_spec) .. require("Module:utilities").format_categories(alternant_multiword_spec.categories, lang)
end
-- Entry point for {{sk-adecl-manual}}. Template-callable function to parse and
-- decline an adjective given manually-specified inflections and generate a
-- displayable table of the declined forms.
function export.show_manual(frame)
local parent_args = frame:getParent().args
local alternant_multiword_spec = export.do_generate_forms_manual(parent_args)
show_forms(alternant_multiword_spec)
return make_table(alternant_multiword_spec) .. require("Module:utilities").format_categories(alternant_multiword_spec.categories, lang)
end
return export
rqpdtdfgccgjtvs5tv80u51i0d39zea
ထာမ်ပလိက်:hsb-IPA
10
295673
396481
2026-06-07T12:18:53Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "{{#invoke:hsb-IPA|IPA}}<!-- --><noinclude>{{documentation}}</noinclude>"
396481
wikitext
text/x-wiki
{{#invoke:hsb-IPA|IPA}}<!--
--><noinclude>{{documentation}}</noinclude>
t476lm7bo28vrc2rddk7xn4uvj7rpzc
မဝ်ဂျူ:hsb-IPA/doc
828
295674
396482
2026-06-07T12:20:55Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "{{documentation needed}}<!-- Replace this with a short description of the purpose of the module, and how to use it. --> <includeonly> {{module cat|hsb}} </includeonly>"
396482
wikitext
text/x-wiki
{{documentation needed}}<!-- Replace this with a short description of the purpose of the module, and how to use it. -->
<includeonly>
{{module cat|hsb}}
</includeonly>
3wx2f9ddkp3hn4wolfluvd77m1w4dze
ကဏ္ဍ:မဝ်ဂျူသဝ်ဗဳယျာ လ္ပာ်သၠုၚ်ကျာဂမၠိုၚ်
14
295675
396483
2026-06-07T12:23:15Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "[[:ကဏ္ဍ:ဒၞာဲလုပ်အဝေါၚ်ကဵုပၟိက်|ဒၞာဲလုပ်အဝေါၚ်ကဵုပၟိက်]] » [[:ကဏ္ဍ:အရေဝ်ဘာသာအိုတ်သီုဂမၠိုၚ်|အရေဝ်ဘာသာအိုတ်သီုဂမၠိုၚ်]] » :ကဏ္ဍ:ဘာသာသဝ်ဗဳယျာ လ္ပာ်သၠုၚ်ကျာ|သဝ..."
396483
wikitext
text/x-wiki
[[:ကဏ္ဍ:ဒၞာဲလုပ်အဝေါၚ်ကဵုပၟိက်|ဒၞာဲလုပ်အဝေါၚ်ကဵုပၟိက်]] » [[:ကဏ္ဍ:အရေဝ်ဘာသာအိုတ်သီုဂမၠိုၚ်|အရေဝ်ဘာသာအိုတ်သီုဂမၠိုၚ်]] » [[:ကဏ္ဍ:ဘာသာသဝ်ဗဳယျာ လ္ပာ်သၠုၚ်ကျာ|သဝ်ဗဳယျာ လ္ပာ်သၠုၚ်ကျာ]] » '''မဝ်ဂျူဂမၠိုၚ်'''
:[[:ကဏ္ဍ:မဝ်ဂျူဂမၠိုၚ်|မဝ်ဂျူ]]ဘာသာသဝ်ဗဳယျာ လ္ပာ်သၠုၚ်ကျာ၊ မနွံကဵုလုပ်အဝေါၚ်ကုဒ် Lua နကဵုမကၠောန်ဗဒှ် ကဵု မစဳရေၚ်ယဵုဒုၚ်သ္ပမာန်ဂမၠိုၚ်။
[[ကဏ္ဍ:ဘာသာသဝ်ဗဳယျာ လ္ပာ်သၠုၚ်ကျာ]][[ကဏ္ဍ:မဝ်ဂျူဗက်အလိုက်အရေဝ်ဘာသာဂမၠိုၚ်|သ]]
08jrd4e5h7skuynjicwx08c19ruo29r
ထာမ်ပလိက်:hsb-IPA/documentation
10
295676
396485
2026-06-07T12:26:29Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "{{documentation subpage}} {{maintenance box|yellow<!-- -->| title = Use {{temp|hsb-pr}} instead.<!-- -->| image = [[File:Text-x-generic with pencil.svg|40px]]<!-- -->| text = Use {{temp|hsb-pr}} instead of invoking this template directly. {{temp|hsb-pr}} works similarly to this template but autogenerates rhymes and hyphenation as well as pronunciation.<!-- -->}} This is the pronunciation template for Upper Sorbian wor..."
396485
wikitext
text/x-wiki
{{documentation subpage}}
{{maintenance box|yellow<!--
-->| title = Use {{temp|hsb-pr}} instead.<!--
-->| image = [[File:Text-x-generic with pencil.svg|40px]]<!--
-->| text = Use {{temp|hsb-pr}} instead of invoking this template directly. {{temp|hsb-pr}} works similarly to this template but autogenerates rhymes and hyphenation as well as pronunciation.<!--
-->}}
This is the pronunciation template for Upper Sorbian words. Every parameter generates a separate pronunciation, based on the respelling given in the parameter. The following special characters are used to specify pronunciation that cannot be deducted from the spelling:
* <code>'</code> (apostrophe) Used before a vowel to mark it as stressed; The stress mark will automatically be moved to a consonant preceding the vowel (if any).
* <code>-</code> (n dash) Used between consonants to prevent assimilations.
==Respellings==
* <v> can be used to print the rare /v/.
* /au/ should be spelled with <ał>.
* <q> is used to spell internal /kʰ/. Any loan words spelled with <q> should be respelled in the module with <k>.
<includeonly>
{{tcat}}
</includeonly>
i96uqydsvjic8vyc1hwnoz8qc55p0h2
396487
396485
2026-06-07T13:23:02Z
咽頭べさ
33
396487
wikitext
text/x-wiki
{{documentation subpage}}
{{maintenance box|yellow<!--
-->| title = Use {{temp|hsb-pr}} instead.<!--
-->| image = [[File:Text-x-generic with pencil.svg|40px]]<!--
-->| text = Use {{temp|hsb-pr}} instead of invoking this template directly. {{temp|hsb-pr}} works similarly to this template but autogenerates rhymes and hyphenation as well as pronunciation.<!--
-->}}
This is the pronunciation template for Upper Sorbian words. Every parameter generates a separate pronunciation, based on the respelling given in the parameter. The following special characters are used to specify pronunciation that cannot be deducted from the spelling:
* <code>'</code> (apostrophe) Used before a vowel to mark it as stressed; The stress mark will automatically be moved to a consonant preceding the vowel (if any).
* <code>-</code> (n dash) Used between consonants to prevent assimilations.
==Respellings==
* <v> can be used to print the rare /v/.
* /au/ should be spelled with <ał>.
* <q> is used to spell internal /kʰ/. Any loan words spelled with <q> should be respelled in the module with <k>.
<includeonly>
[[ကဏ္ဍ:ထာမ်ပလိက်မပ္တိတ်ရမျာၚ်သဝ်ဗဳယျာ လ္ပာ်သၠုၚ်ကျာဂမၠိုၚ်]]
</includeonly>
srefrhlvfsotai3mwgep98lwe207ugp
ကဏ္ဍ:ထာမ်ပလိက်မပ္တိတ်ရမျာၚ်သဝ်ဗဳယျာ လ္ပာ်သၠုၚ်ကျာဂမၠိုၚ်
14
295677
396488
2026-06-07T13:24:08Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "[[ကဏ္ဍ:ထာမ်ပလိက်သဝ်ဗဳယျာ လ္ပာ်သၠုၚ်ကျာဂမၠိုၚ်]][[ကဏ္ဍ:ထာမ်ပလိက်ပ္တိတ်ရမျာၚ်ဗက်အလိုက်အရေဝ်ဘာသာဂမၠိုၚ်|သ]]"
396488
wikitext
text/x-wiki
[[ကဏ္ဍ:ထာမ်ပလိက်သဝ်ဗဳယျာ လ္ပာ်သၠုၚ်ကျာဂမၠိုၚ်]][[ကဏ္ဍ:ထာမ်ပလိက်ပ္တိတ်ရမျာၚ်ဗက်အလိုက်အရေဝ်ဘာသာဂမၠိုၚ်|သ]]
bpfg8tz23m1a07en1eski33arkfp1vt
ကဏ္ဍ:ထာမ်ပလိက်သဝ်ဗဳယျာ လ္ပာ်သၠုၚ်ကျာဂမၠိုၚ်
14
295678
396489
2026-06-07T13:27:17Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "[[ကဏ္ဍ:ဘာသာသဝ်ဗဳယျာ လ္ပာ်သၠုၚ်ကျာ]][[ကဏ္ဍ:ထာမ်ပလိက်ဗက်အလိုက်အရေဝ်ဘာသာဂမၠိုၚ်|သ]]"
396489
wikitext
text/x-wiki
[[ကဏ္ဍ:ဘာသာသဝ်ဗဳယျာ လ္ပာ်သၠုၚ်ကျာ]][[ကဏ္ဍ:ထာမ်ပလိက်ဗက်အလိုက်အရေဝ်ဘာသာဂမၠိုၚ်|သ]]
0tgzciasz3m9r4wxh6lqzuc2fhlcn27
မဳဒဳယာဝဳကဳ:Gadget-Site.css
8
295679
396490
2026-06-07T13:31:25Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "/* ---------- <nowiki> CSS Style * Indent with tab characters * One line space between each declaration * One selector per line * Opening brace on the same line as the last selector * Don't omit optional semicolons * Closing brace on its own line * Closing brace indented to match the preceding style declarations * Alternatively, very short style rules may be all on one line ---------- */ /* csslint adjoining-classes:..."
396490
css
text/css
/* ----------
<nowiki>
CSS Style
* Indent with tab characters
* One line space between each declaration
* One selector per line
* Opening brace on the same line as the last selector
* Don't omit optional semicolons
* Closing brace on its own line
* Closing brace indented to match the preceding style declarations
* Alternatively, very short style rules may be all on one line
---------- */
/* csslint adjoining-classes:false */
/* See also [[MediaWiki:Minerva.css]], [[MediaWiki:Vector.css]] */
sup,
sub {
/* prevent odd line-height for raised and lowered characters */
line-height: 1em;
}
/* and keep links at normal font size/weight, else they inherit the header
attributes */
.editsection {
font-size: 100%;
font-weight: normal;
}
pre code,
code code {
border: none;
padding: 0;
}
pre var,
code var {
color: var(--wikt-palette-grey-9,#777);
}
/* This fixes a bug where certain Tibetan terms are cut off at the bottom... */
#firstHeading {
overflow: visible;
}
.ns-0 .timed-text {
display: none;
}
.client-js .audiometa {
display: none;
}
.client-nojs .audiofile {
display: none;
}
.nowrap {
white-space: nowrap;
}
/* wikitable is part of core. prettytable is deprecated but still used in some
places, until fully replaced: */
table.prettytable {
margin: 1em 1em 1em 0;
background: var(--wikt-palette-paleblue, #f8f9fa);
border: 1px var(--border-color-base, #aaaaaa) solid;
border-collapse: collapse;
}
table.prettytable th,
table.prettytable td {
border: 1px var(--border-color-base, #aaaaaa) solid;
padding: 0.2em;
}
table.prettytable th {
background: var(--wikt-palette-lightergrey, #eeeeee);
text-align: center;
}
table.prettytable caption {
margin-left: inherit;
margin-right: inherit;
}
/* default setting for {{qualifier}} and {{sense}}.
(Related classes that can be overridden: .qualifier-comma,
.qualifier-content, .ib-brac, .qualifier-brac, .sense-qualifier-colon.) */
.ib-comma {
font-style: normal;
}
.ib-content {
font-style: italic;
}
.ib-content i,
.ib-content em {
font-style: normal;
}
.context-qualifier-colon {
display: none;
}
/* default style for indented "see also" */
.disambig-see-also {
text-indent: 2em;
}
.mention-tr {
font-style: italic;
}
/* add left-to-right mark */
.mention-gloss-paren::before,
.mention-tr-paren::before {
content: "";
}
/* non-italic and non-bold gloss */
.mention-gloss,
.mention-gloss-double-quote,
.mention-gloss-paren,
.mention-tr-gloss-separator-comma,
.mention-tr-paren,
.mention-tr-gloss-separator-comma {
font-style: normal;
font-weight: inherit;
}
/* default style for "form of" definitions */
.use-with-mention,
.form-of-definition {
font-style: italic;
}
.use-with-mention i,
.form-of-definition-link {
font-style: normal;
}
.use-with-mention .mention,
.form-of-definition-link .mention {
font-style: normal;
font-weight: bold;
}
/* display normalization in {{ux}}/{{usex}}, {{quote}}, {{quote-*}} in italics */
.e-normalization {
font-style: italic;
}
/* Inflection tables
----------------------------- */
/* To colour links in inflection tables (currently done with
{{notred}}) black without using #ifexist and allowing for user
override */
.inflection-table a.new {
color: inherit;
}
/* fill a parent NavContent collapsing box */
.NavContent table.inflection,
.NavContent table.inflection-table {
margin-top: 0;
width: 100%;
display: table;
border: 0;
}
/* basic inflection table styles */
table.inflection {
border-collapse: collapse;
border: 1px solid;
background: #ffffff;
/* TODO consider palettizing */
}
table.inflection caption {
caption-side: bottom;
padding: 0.33em 1em;
text-align: left;
font-size: smaller;
}
/* subtle grey for alternate row backgrounds */
table.inflection tr:nth-child(odd) {
background: #fcfcfc;
/* TODO consider palettizing */
}
/* and for row headers */
table.inflection tr:nth-child(odd):not(.rowgroup) th {
background: #f6f6f6;
/* TODO consider palettizing */
}
/* dark mode background colors for table.inflection */
@media screen {
html.skin-theme-clientpref-night table.inflection {
background-color: #242424;
}
html.skin-theme-clientpref-night table.inflection tr:nth-child(odd) {
background-color: #2E2E2E;
}
html.skin-theme-clientpref-night table.inflection tr:nth-child(odd):not(.rowgroup) th {
background-color: #303030;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os table.inflection {
background-color: #242424;
}
html.skin-theme-clientpref-os table.inflection tr:nth-child(odd) {
background-color: #2E2E2E;
}
html.skin-theme-clientpref-os table.inflection tr:nth-child(odd):not(.rowgroup) th {
background-color: #303030;
}
}
table.inflection td,
table.inflection th {
padding: 0.33em 1em;
vertical-align: baseline;
}
/* but vertically center multi-row cells */
table.inflection td[rowspan] {
vertical-align: middle;
}
table.inflection td {
text-align: center;
}
/* regular row headers, in the left column */
table.inflection th {
background: var(--wikt-palette-paleblue, #f8f9fa);
text-align: left;
}
/* rowgroup header rows */
table.inflection tr.rowgroup th {
background: var(--wikt-palette-lightergrey, #eeeeee);
text-align: center;
}
/* first-of-several rowgroup header cells is aligned left */
table.inflection tr.rowgroup th:first-child {
text-align: left;
}
table.inflection tr.rowgroup th:first-child:only-child {
text-align: center;
}
/* rows with dividers above */
table.inflection tr.divider th {
border-top: 3px solid var(--wikt-palette-grey-7,#999999);
}
/* transliterations */
table.inflection .translit {
color: var(--wikt-palette-grey-8,#888);
}
/* header links aren’t coloured until hover/active */
table.inflection th a:link,
table.inflection th a:visited {
color: inherit;
text-decoration: inherit;
}
table.inflection th a:hover {
color: var(--wikt-palette-blue-9,#0645ad);
text-decoration: underline;
}
table.inflection th a:active {
color: var(--wikt-palette-gold,#faa700);
text-decoration: underline;
}
/* ----
end of Inflection tables */
/* Hide interproject links used by [[MediaWiki:Gadget-AggregateInterprojectLinks.js]] */
.interProject {
display: none;
clear: both;
border-top: 2px dotted var(--border-color-base,#AAAAAA);
margin-top: 2em;
}
/* Translation styling */
.checktrans,
.rfc-trans,
.trreq,
.ttbc {
background: var(--wikt-palette-palegreen, #f0fff0);
}
/* t+ t t- templates:
TODO are these still wanted? */
.tneg,
.tpos,
.tunk {
vertical-align: 20%;
font-size: 80%;
}
.tneg a {
color: var(--wikt-palette-red,#cc2200) !important;
}
/* monobook redlink colour, standard #ba0000 */
.tneg a:visited {
color: var(--wikt-palette-grey-red-9,#a55858) !important;
}
/* standard visited redlink colour */
.tpos a {
color: var(--wikt-palette-deepblue);
}
/* standard bluelink colour */
.tpos a:visited {
color: var(--wikt-palette-blue);
}
/* standard bluelink colour */
.tunk a {
color: var(--wikt-palette-blue-9,#3366BB) !important;
}
/* standard external interwiki colour */
.tunk a:active {
color: var(--wikt-palette-blue-9,#3366BB) !important;
}
/* redundant */
/* for parentheses, we use .tlc and .tlcp, the latter has no definition here
(display:inline) */
.tlc {
display: none;
}
/* Main page fixes */
#interwiki-completelist {
font-weight: bold;
}
/* Try to make search results more obviously links */
.mw-special-Search #bodyContent li a {
font-weight: bold;
}
.mw-special-Search .searchmatch {
font-weight: normal;
color: var(--wikt-palette-black, #000000);
background-color: var(--wikt-palette-brightyellow, #FFEE77);
}
/* [[WT:FEED]] */
#p-feedback a {
cursor: pointer;
}
/* Old revisions */
#mw-revision-info {
font-size: 110%;
margin: 5px 0;
}
#mw-editingold {
font-size: 120%;
}
/* maintenance line */
.maintenance-line {
color: #777777;
/* TODO consider palettizing */
}
/* dark mode text color for maintenance line */
@media screen {
html.skin-theme-clientpref-night .maintenance-line {
color: #adb7bf;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .maintenance-line {
color: #adb7bf;
}
}
/* Night mode logo appearance, see [[Wiktionary:Grease pit/2025/January#Top left Wiktionary logo in dark mode]] */
@media screen {
html.skin-theme-clientpref-night img.mw-logo-icon {
color-scheme: light;
filter: invert(1) brightness(55%) contrast(250%) hue-rotate(180deg);
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os img.mw-logo-icon {
color-scheme: light;
filter: invert(1) brightness(55%) contrast(250%) hue-rotate(180deg);
}
}
/* Make Recent changes look nice again */
span.newpage,
span.minor,
span.searchmatch,
span.bot,
span.unpatrolled {
font-family: sans-serif !important;
}
/* Styling for the different kinds of collapsible boxes and lists */
/* NavFrames */
div.NavFrame,
details.NavFrame {
border: 1px solid var(--wikt-palette-grey, #9e9e9e);
text-align: center;
border-collapse: collapse;
font-size: 95%;
overflow: auto;
width: auto;
}
div.NavFrame>ul,
div.NavFrame>ol {
text-align: left;
}
div.NavFrame div.NavHead,
details.NavFrame summary.NavHead {
margin: 2px;
min-height: 1.6em;
font-weight: bold;
font-size: 100%;
text-align: left;
padding: 1px 5px 1px 10px;
background-color: var(--wikt-palette-dullcyan, #eaecf0);
}
div.NavFrame p {
font-size: 100%;
}
div.NavFrame div.NavContent,
details.NavFrame div.NavContent {
font-size: 100%;
border-top: 1px solid var(--wikt-palette-grey, #9e9e9e);
}
div.NavFrame div.NavContent p,
details.NavFrame div.NavContent p {
font-size: 100%;
}
/* Used by box-top/box-bottom */
.NavContent.boxcontent {
padding: 0.25em 0.5em;
}
.NavContent.boxcontent>p:last-child {
margin-bottom: 0.5em;
/* from 1em to avoid some extra asymmetric space */
}
/* Override default added margins which mess up the background */
.NavContent.derivedterms>p,
.NavContent.columns-bg>p {
margin: 0;
}
.NavContent.derivedterms>p,
.NavContent.derivedterms>ul,
.NavContent.columns-bg>p,
.NavContent.columns-bg>ul {
padding: 0.5em;
}
/* Collapsible term lists */
.list-switcher {
/* This is a hack so we can use getComputedStyle to find the
maximum permissible height value of a list-switcher.
It must be kept in sync with .list-switcher-collapsed `max-height` */
bottom: calc(3.2 * 1.6em);
/* for browsers that don't support `lh` units */
bottom: 3.2lh;
}
.list-switcher-wrapper,
:not(.list-switcher-wrapper)>.list-switcher {
contain: layout;
/* stop floating elements from messing with layout */
margin-top: 0.3em;
/* replaces margin lost on <ul> in following ruleset */
}
.list-switcher-wrapper+.list-switcher-wrapper {
margin-top: 0.6em;
}
.list-switcher>div>ul,
.list-switcher-wrapper>div>ul {
margin-top: 0;
/* needed to avoid jumps when collapsing/uncollapsing */
}
.list-switcher ul ul {
margin-top: 0.1em;
/* make margin above nested lists equal to margin at bottom of <li>s */
}
.list-switcher-collapsed {
/* If changing this value, change .list-switcher `bottom` above */
max-height: calc(3.2 * 1.6em);
/* for browsers that don't support `lh` units */
max-height: 3.2lh;
overflow-y: hidden;
transform: translateZ(0); /* Fix for iOS Safari overflow clipping */
}
.list-switcher-header {
background: var(--wikt-palette-grey-indigo-2);
font-weight: bold;
font-size: 95%;
padding: 0.2em 0.4em 0.1em 0.6em;
}
.list-switcher-header+.list-switcher .term-list,
.list-switcher-header+.term-list {
padding-top: 0.1em;
}
.list-switcher-edit {
margin-left: 1.5em;
font-size: 87.75%;
/* exactly match font size of [show more] button (in Chromium-based browsers at least) */
font-weight: normal;
}
.list-switcher-edit::before {
content: "[";
}
.list-switcher-edit::after {
content: "]";
}
.list-switcher-large-text {
/* Keep in sync with next ruleset */
bottom: calc(4 * 1.6em);
/* for browsers that don't support `lh` units */
bottom: 4lh;
}
.list-switcher-large-text.list-switcher-collapsed {
/* Keep in sync with previous ruleset */
max-height: calc(4 * 1.6em);
/* for browsers that don't support `lh` units */
max-height: 4lh;
}
.list-switcher-element {
text-align: center;
cursor: pointer;
background: var(--wikt-palette-grey-indigo-2);
border-bottom-left-radius: 0.3lh;
border-bottom-right-radius: 0.3lh;
}
.list-switcher-element .NavToggle {
float: none;
}
/* this is a legacy rule that only applies to {{rootsee}} and
{{script appendix}} - these need a fairly wide width */
.columns-bg:not(.term-list)>div>div>.CategoryTreeChildren,
.columns-bg:not(.term-list)>div>.CategoryTreeChildren {
-moz-column-width: 25em;
column-width: 25em;
}
/* Hidden quotes */
.client-js .ns-0 ol>li>ul {
display: none;
}
/* Hidden Quotes and *nyms toggle */
.HQToggle,
.nyms-toggle,
.usex-toggle {
font-size: smaller;
position: relative;
bottom: 1px;
left: 5px;
}
.NavToggle {
float: right;
font-weight: normal;
font-size: smaller;
margin-top: 1px;
margin-left: 0.5em;
}
/* wrap toggle links with [ and ] */
.HQToggle:before,
.NavToggle:before,
.nyms-toggle:before,
.usex-toggle:before {
content: '[';
}
.HQToggle:after,
.NavToggle:after,
.nyms-toggle:after,
.usex-toggle:after {
content: ']';
}
.HQToggle a,
.NavToggle a,
.nyms-toggle a,
.usex-toggle a {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
color: var(--wikt-palette-black);
}
.HQToggle a:focus:not(:hover),
.NavToggle a:focus:not(:hover),
.nyms-toggle a:focus:not(:hover),
.usex-toggle a:focus:not(:hover) {
text-decoration: none;
}
.client-js .NavFrame:not(.no-collapse) .NavContent {
display: none;
}
/* View switcher */
.vsShow {
display: none;
}
/* End styling for collapsible lists */
/* Quote clarifications, e.g. {{quote-gloss}}, {{quote-qualifier}} */
.quote-note {
color: var(--wikt-palette-grey-9,#999);
}
/* Use same formatting for <mark> as used for bolding with certain scripts. */
mark {
background-color: var(--wikt-palette-pink, #ffe0f0);
color: var(--wikt-palette-black, #202122);
}
/* stuff visible when previewing an edit only */
.previewonly {
display: none;
}
#wikiPreview .previewonly {
display: inline;
}
/* Highlight the relevant sense when the user follows a link to a {{senseid}}.
The class="senseid" should ideally be on the enclosing <li> tag. We attempt to make the
highlight work when the class="senseid" is on a <span> inside the <li>, but the :has
selector was not supported by all browsers as of late 2023. This feature was nonetheless
requested by Benwing at [[Wiktionary:Grease_pit/2023/November#Why_does_Template%3Asenseid_use_HTML_li_tag%3F]].
THe version with :has is provided as a separate declaration to ensure maximum compatibility. */
.senseid:target {
background: var(--wikt-palette-lightblue, #DEF);
}
li:has(> span.senseid:target) {
background: var(--wikt-palette-lightblue, #DEF);
}
/* highlight the next paragraph for an etymid */
.etymid:target+p, .etymonid:target+p {
background: var(--wikt-palette-lightblue, #DEF);
}
/* Highlight selected source in a bibliography */
.bibliography-id:target {
background: var(--wikt-palette-lightblue, #DEF);
}
/* Skin CSS highlights table rows targeted by the current browser location
(tr:target). This rule highlight table rows containing target cells so that
there can be multiple IDs in a table row in [[Wiktionary:List of languages]]
and [[Wiktionary:List of languages/special#Etymology-only languages]]. */
tr:has(> :target),
tr:has(> td > code:target) {
background: var(--wikt-palette-lightblue, #DEF);
}
/* show the "serial and" in the disambiguation see also */
.serial-and {
display: inline;
}
/* hide the serial commas */
.serial-comma {
display: none;
}
/* Gender and number templates */
.gender,
.number,
.noun-class {
font-style: italic;
}
abbr {
border-bottom-width: 0;
}
/* hide "did you mean" on Noarticletext self-links */
.did-you-mean strong {
display: none;
}
/* make script/module errors smaller */
.scribunto-error {
font-size: 75%;
}
/* allow inline hieroglyphs */
.mw-hiero-outer {
display: inline-table !important;
/* override skin styling on mobile */
vertical-align: middle;
font-size: 10px;
}
/* allow mirrored custom hieroglyph (and other) images */
.mw-mirrored img {
transform: scaleX(-1);
}
/* softer highlighting of 'updated since last visit' */
span.updatedmarker {
background-color: var(--wikt-palette-mint, #c0e4c0);
}
/* {{TOC limit}} */
/* Allow limiting of which header levels are shown in a TOC;
<div class="toclimit-3">, for instance, will limit to showing ==headings==
and ===headings=== but no further (as long as there are no =headings= on the
page, which there shouldn't be according to the MoS). */
.toclimit-2 .toclevel-1 ul,
.toclimit-3 .toclevel-2 ul,
.toclimit-4 .toclevel-3 ul,
.toclimit-5 .toclevel-4 ul,
.toclimit-6 .toclevel-5 ul,
.toclimit-7 .toclevel-6 ul {
display: none;
}
/*
* Script and language-related styles are in [[MediaWiki:Gadget-LanguagesAndScripts.css]]
*/
/* A kludge that lets the members of [[:Category:Redirected combining characters]] be displayed. */
body.page-Category_Combining_characters div#mw-pages li,
body.page-Category_Redirected_combining_characters div#mw-pages li,
body.page-Category_IPA_combining_characters div#mw-pages li {
letter-spacing: 0.3em;
}
/* Grease Pit edit button fix */
.page-Wiktionary_Grease_pit.skin-vector #ca-edit,
.page-Wiktionary_Etymology_scriptorium.skin-vector #ca-edit,
.page-Wiktionary_Information_desk.skin-vector #ca-edit,
.page-Wiktionary_Beer_parlour.skin-vector #ca-edit,
.page-Wiktionary_Tea_room.skin-vector #ca-edit {
display: none;
}
/* unit tests filter */
table.unit-tests.unit-tests-hide-passing tr.unit-test-pass {
display: none;
}
table.unit-tests th.unit-tests-img-corner {
background: var(--wikt-palette-lightblue, #d9ebff);
min-width: 1em;
}
table.unit-tests.unit-tests-hide-passing th.unit-tests-img-corner {
background: red;
}
/* multi-column lists */
li,
.group-in-column {
-moz-column-break-inside: avoid;
break-inside: avoid;
}
/* {{shortcut}} */
.shortcut-box,
.category-edit-box {
border: 1px solid var(--border-color-base, #aaaaaa);
color: var(--color-base, black);
background: var(--wikt-palette-paleblue, #f9f9f9);
margin: 0 0 .5em 1em;
text-align: center;
padding: 5px;
float: right;
clear: both;
font-weight: bold;
font-size: smaller;
}
/* Display tabs with 4 spaces, see [[phab:T59824]] and [[phab:T59825]] */
textarea#wpTextbox1+div:last-child,
/* workaround for [[mw:User:Remember the dot/Syntax highlighter]] */
textarea,
pre,
code,
.mw-highlight {
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
}
/* stop double-bolding of self-links like in [[acquit]], [[fishbone]] */
/* old code: b .selflink, strong .selflink { font-weight: inherit; } */
b,
strong {
font-weight: bold;
}
tr:target {
background: var(--wikt-palette-lightblue, #DEF);
}
#firstHeading var,
.headword var {
color: var(--wikt-palette-grey-9, #888);
}
/* Display a {{tlb}} / {{term-label}} template on its own line if it comes right after a headword template.
It is too easy to miss after headwords (especially long ones). */
p .headword-line~.usage-label-term::before {
content: "\a";
/* newline */
white-space: pre;
}
/* Temporary kludge to be used because categoryTree is generating a newline. This CSS line should be removed after the developers fix it. */
.CategoryTreeTag+p br:first-child {
display: none;
}
/* Used by column templates (see [[Module:columns]]). */
.ul-column-count>ul,
.ul-column-count>ol,
.ul-column-count>dl {
-moz-column-gap: 20px;
column-gap: 20px;
}
.ul-column-count[data-column-count="2"]>ul,
.ul-column-count[data-column-count="2"]>ol,
.ul-column-count[data-column-count="2"]>dl {
-moz-column-count: 2;
column-count: 2;
}
.ul-column-count[data-column-count="3"]>ul,
.ul-column-count[data-column-count="3"]>ol,
.ul-column-count[data-column-count="3"]>dl {
-moz-column-count: 3;
column-count: 3;
}
.ul-column-count[data-column-count="4"]>ul,
.ul-column-count[data-column-count="4"]>ol,
.ul-column-count[data-column-count="4"]>dl {
-moz-column-count: 4;
column-count: 4;
}
.ul-column-count[data-column-count="5"]>ul,
.ul-column-count[data-column-count="5"]>ol,
.ul-column-count[data-column-count="5"]>dl {
-moz-column-count: 5;
column-count: 5;
}
.ul-column-count[data-column-count="6"]>ul,
.ul-column-count[data-column-count="6"]>ol,
.ul-column-count[data-column-count="6"]>dl {
-moz-column-count: 6;
column-count: 6;
}
.columns-bg {
/* Default background color for multi-column lists. */
background: var(--wikt-palette-lavender, #f8f8ff);
/* prevent text crashing into edge of box */
padding-right: 0.3em;
}
/* Default style for hypothetical inflections -- currently italic. */
.hypothetical {
font-style: italic;
}
/* Style for entries using deprecated templates */
.deprecated {
color: olivedrab;
}
.deprecated a {
color: darkgreen;
}
/* Style for entries using deprecated labels */
.deprecated-label {
color: olivedrab;
text-decoration: line-through;
}
.deprecated-label a {
color: darkgreen;
text-decoration: line-through;
}
/* Style for notes added using the bad= param in {{audio}} */
.bad-audio-note {
color: var(--wikt-palette-red, #FF2200);
font-weight: bold;
}
/* WikiHiero kludge */
/* disable for now, apparently not necessary and breaks wrapping
.wikt-hierokludge {
display: inline-block;
} */
/* Support lists that automatically split into limited-width columns (for translations tables and the like): */
.multicolumn-list>ul {
-moz-column-width: 25em;
column-width: 25em;
-moz-column-gap: 20px;
column-gap: 20px;
}
.multicolumn-list-wide>ul {
-moz-column-width: 55em;
column-width: 55em;
-moz-column-gap: 20px;
column-gap: 20px;
}
.multicolumn-list-narrow>ul {
-moz-column-width: 22em;
column-width: 22em;
-moz-column-gap: 20px;
column-gap: 20px;
}
/* Horizontal rules */
body.ns-0 .mw-parser-output> :not(.collapsible-block:not(.open-block))+h2:not(:first-of-type),
body.ns-0 .mw-parser-output> .mw-heading2 ~ :not(.collapsible-block:not(.open-block))+.mw-heading2 {
border-top: 1px solid var(--border-color-base, #a2a9b1);
padding-top: 1em;
margin-top: 0.2em;
}
/* [[phab:T156351]]: Support for Parsoid's Cite implementation */
a[ rel="mw:referencedBy"]::before {
content: " ^";
}
/* [[phab:T316670]]: fix header issues */
.mw-heading * {
word-break: normal;
}
/* From [[Module:shortcut box/styles.css]] */
a .redlink {
color: var(--color-destructive, #d73333);
}
a:visited .redlink {
color: var(--color-destructive--visited, #a55858);
}
a:visited:hover .redlink {
color: var(--color-destructive--visited, #a55858);
}
a:hover .redlink {
color: var(--color-destructive--hover, #ff4242);
text-decoration: underline;
}
a:active .redlink {
color: var(--color-destructive--active, #b32424);
text-decoration: underline;
}
a:focus .redlink {
outline-color: var(--outline-color-progressive--focus, #36c);
}
/* Change the external link icon to a PDF icon for all PDF files */
.mw-parser-output a[href$=".pdf"].external,
.mw-parser-output a[href*=".pdf?"].external,
.mw-parser-output a[href*=".pdf#"].external,
.mw-parser-output a[href$=".PDF"].external,
.mw-parser-output a[href*=".PDF?"].external,
.mw-parser-output a[href*=".PDF#"].external {
background: url("//upload.wikimedia.org/wikipedia/commons/4/4d/Icon_pdf_file.png") no-repeat right;
/* @noflip */
padding: 8px 18px 8px 0;
}
/* Add YouTube icon after links to YouTube. */
.mw-parser-output a[href*="youtube.com"].external,
.mw-parser-output a[href*="youtu.be"].external {
background: url("//upload.wikimedia.org/wikipedia/commons/f/fd/YouTube_full-color_icon_(2024).svg") no-repeat right;
background-size: auto;
padding: 8px 18px 8px 0;
background-size: 16px;
}
/* approximate replacement for .toccolours, which is not defined in the Vector 2022 skin */
.standard-box {
background-color: var(--wikt-palette-paleblue, #f8f9fa);
border: 1px solid var(--wikt-palette-grey, #9e9e9e);
/* was #aabbdd but no real equivalent in our palette */
padding: 5px;
}
/* fix for long titles e.g. [[Cnile]] */
.cited-source {
overflow-wrap: break-word;
}
/* </nowiki> */
cwtiynx3ccovf6knw72g4ojp9cyxz0c
မဳဒဳယာဝဳကဳ:Gadget-Site.css/documentation
8
295680
396491
2026-06-07T13:32:52Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "This CSS is included for all users, both registered and not, on both desktop and mobile. <includeonly> [[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]] </includeonly>"
396491
wikitext
text/x-wiki
This CSS is included for all users, both registered and not, on both desktop and mobile.
<includeonly>
[[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]]
</includeonly>
mwuh5zl3klxzpetuimuyv2vdchk4jzb
မဳဒဳယာဝဳကဳ:Gadget-vectorTabs.js
8
295681
396492
2026-06-07T13:39:28Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "// {{documentation}} $(function() { $("#p-cactions").remove().find("li").insertBefore("#ca-watch,#ca-unwatch").find("a").wrap("<span>"); });"
396492
javascript
text/javascript
// {{documentation}}
$(function() {
$("#p-cactions").remove().find("li").insertBefore("#ca-watch,#ca-unwatch").find("a").wrap("<span>");
});
bzkksiyyf8g2iaqdoxvrx59s6gdq2l5
မဳဒဳယာဝဳကဳ:Gadget-UTCLiveClock.js
8
295682
396493
2026-06-07T13:41:41Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "// {{documentation}} window.UTCLiveClockLocation = window.UTCLiveClockLocation || 'outside'; // see [[mw:MediaWiki:Gadget-UTCLiveClock]] mw.loader.load('//www.mediawiki.org/w/index.php?title=MediaWiki:Gadget-UTCLiveClock.js&action=raw&ctype=text/javascript');"
396493
javascript
text/javascript
// {{documentation}}
window.UTCLiveClockLocation = window.UTCLiveClockLocation || 'outside'; // see [[mw:MediaWiki:Gadget-UTCLiveClock]]
mw.loader.load('//www.mediawiki.org/w/index.php?title=MediaWiki:Gadget-UTCLiveClock.js&action=raw&ctype=text/javascript');
br5it9eznjp6c2wr62ib6dljeq06622
မဳဒဳယာဝဳကဳ:Gadget-vectorTabs.js/documentation
8
295683
396494
2026-06-07T13:42:38Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "{{documentation subpage}} <!--Add description here. This subpage initially added just for categorization.--> See also: [[Special:Gadgets]]. See also: [[MediaWiki:Gadget-vectorTabs.css]] <includeonly> [[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]] </includeonly>"
396494
wikitext
text/x-wiki
{{documentation subpage}}
<!--Add description here. This subpage initially added just for categorization.-->
See also: [[Special:Gadgets]].
See also: [[MediaWiki:Gadget-vectorTabs.css]]
<includeonly>
[[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]]
</includeonly>
ge8bnqu1zf8m76p4wg4u6ypity2exwz
မဳဒဳယာဝဳကဳ:Gadget-vectorTabs.css
8
295684
396495
2026-06-07T13:43:18Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "/*.menu li{background-color: #F3F3F3;background-image: url(/w/skins/Vector/images/tab-normal-fade.png);background-position: 0% 100%;background-repeat: repeat-x;float: left;line-height: 1.125em !important;} .menu{display:block !important;clear:none !important;} .menu ul{position:static !important;margin-top:10px !important;} #p-cactions h5{display:none;} .menu li a{background-image: url(/w/skins/Vector/images/tab-break..."
396495
css
text/css
/*.menu li{background-color: #F3F3F3;background-image: url(/w/skins/Vector/images/tab-normal-fade.png);background-position: 0% 100%;background-repeat: repeat-x;float: left;line-height: 1.125em !important;}
.menu{display:block !important;clear:none !important;}
.menu ul{position:static !important;margin-top:10px !important;}
#p-cactions h5{display:none;}
.menu li a{background-image: url(/w/skins/Vector/images/tab-break.png);background-position: 100% 100%;background-repeat: no-repeat;}
*/
#p-cactions {
display:none;
}
fsmgox7en3feoembh09cc0xuc4gqqsw
မဳဒဳယာဝဳကဳ:Gadget-UTCLiveClock.js/documentation
8
295685
396496
2026-06-07T13:44:23Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "{{documentation subpage}} <!--Add description here. This subpage initially added just for categorization.--> See also: [[Special:Gadgets]]. <includeonly> [[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]] </includeonly>"
396496
wikitext
text/x-wiki
{{documentation subpage}}
<!--Add description here. This subpage initially added just for categorization.-->
See also: [[Special:Gadgets]].
<includeonly>
[[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]]
</includeonly>
nh2z4mxstdhoy5k1d14op93dv5v8194
မဳဒဳယာဝဳကဳ:Gadget-LanguagesAndScripts.css
8
295686
396501
2026-06-07T14:26:24Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "/* Support for scripts. See [[Wiktionary:Scripts]]. */ /* Default style for mentions outside of "form of" definitions. See [[Wiktionary:Votes/2007-10/style for mentioned terms]] */ .mention { font-style: italic; } .mention i, i .mention { font-style: normal; } /* The following scripts do not support italic text Note: we repeat the first class selector twice to increase CSS 'specificity'. */ .Aghb.Aghb, .Agh..."
396501
css
text/css
/*
Support for scripts. See [[Wiktionary:Scripts]].
*/
/* Default style for mentions outside of "form of" definitions.
See [[Wiktionary:Votes/2007-10/style for mentioned terms]] */
.mention {
font-style: italic;
}
.mention i, i .mention {
font-style: normal;
}
/* The following scripts do not support italic text
Note: we repeat the first class selector twice to increase CSS 'specificity'. */
.Aghb.Aghb, .Aghb *,
.Ahom.Ahom, .Ahom *,
.Armi.Armi, .Armi *,
.Armn.Armn, .Armn *,
.Avst.Avst, .Avst *,
.Bali.Bali, .Bali *,
.Bamu.Bamu, .Bamu *,
.Batk.Batk, .Batk *,
.Beng.Beng, .Beng *,
.as-Beng.as-Beng, .as-Beng *,
.Bopo.Bopo, .Bopo *,
.Brai.Brai, .Brai *,
.Bugi.Bugi, .Bugi *,
.Buhd.Buhd, .Buhd *,
.Cakm.Cakm, .Cakm *,
.Cans.Cans, .Cans *,
.Cari.Cari, .Cari *,
.Cham.Cham, .Cham *,
.Cher.Cher, .Cher *,
.Copt.Copt, .Copt *,
.Cprt.Cprt, .Cprt *,
.Cyrl.Cyrl, /* .Cyrl *, */
.Cyrs.Cyrs, .Cyrs *,
.Deva.Deva, .Deva *,
.Dsrt.Dsrt, .Dsrt *,
.Egyp.Egyp, .Egyp *,
.Ethi.Ethi, .Ethi *,
.Geok.Geok, .Geok *,
.Geor.Geor, .Geor *,
.Glag.Glag, .Glag *,
.Goth.Goth, .Goth *,
.Grek.Grek, /* .Grek *, */
.Gujr.Gujr, .Gujr *,
.Guru.Guru, .Guru *,
.Hang.Hang, .Hang *,
.Hani.Hani, .Hani *,
.Hano.Hano, .Hano *,
.Hans.Hans, .Hans *,
.Hant.Hant, .Hant *,
.Hira.Hira, .Hira *,
.Hluw.Hluw, .Hluw *,
.IPA.IPA, .IPA *,
.Ipach.Ipach, .Ipach *,
.IPAchar.IPAchar, .IPAchar *,
.Ital.Ital, .Ital *,
.Java.Java, .Java *,
.Jpan.Jpan, .Jpan *,
.Kali.Kali, .Kali *,
.Kana.Kana, .Kana *,
.Khar.Khar, .Khar *,
.Khmr.Khmr, .Khmr *,
.Knda.Knda, .Knda *,
.Kore.Kore, .Kore *,
.Kthi.Kthi, .Kthi *,
.Lana.Lana, .Lana *,
.Laoo.Laoo, .Laoo *,
.Lepc.Lepc, .Lepc *,
.Limb.Limb, .Limb *,
.Lisu.Lisu, .Lisu *,
.Lyci.Lyci, .Lyci *,
.Lydi.Lydi, .Lydi *,
.Mand.Mand, .Mand *,
.Merc.Merc, .Merc *,
.Mero.Mero, .Mero *,
.Mlym.Mlym, .Mlym *,
.Modi.Modi, .Modi *,
.Mtei.Mtei, .Mtei *,
.Music.Music, .Music *,
.musical.musical, .musical *,
.Narb.Narb, .Narb *,
.Nkoo.Nkoo, .Nkoo *,
.Olck.Olck, .Olck *,
.Orya.Orya, .Orya *,
.Osma.Osma, .Osma *,
.Phag.Phag, .Phag *,
.Phli.Phli, .Phli *,
.Phlv.Phlv, .Phlv *,
.Phnx.Phnx, .Phnx *,
.Plrd.Plrd, .Plrd *,
.Polyt.Polyt, .Polyt *,
.polytonic.polytonic, .polytonic *,
.Prti.Prti, .Prti *,
.Rjng.Rjng, .Rjng *,
.Samr.Samr, .Samr *,
.Sarb.Sarb, .Sarb *,
.Saur.Saur, .Saur *,
.Sgnw.Sgnw, .Sgnw *,
.Shaw.Shaw, .Shaw *,
.Shrd.Shrd, .Shrd *,
.Sinh.Sinh, .Sinh *,
.Sora.Sora, .Sora *,
.Sund.Sund, .Sund *,
.Sylo.Sylo, .Sylo *,
.Tagb.Tagb, .Tagb *,
.Tale.Tale, .Tale *,
.Talu.Talu, .Talu *,
.Taml.Taml, .Taml *,
.Tavt.Tavt, .Tavt *,
.Telu.Telu, .Telu *,
.Tfng.Tfng, .Tfng *,
.Tglg.Tglg, .Tglg *,
.Thaa.Thaa, .Thaa *,
.Thai.Thai, .Thai *,
.Tibt.Tibt, .Tibt *,
.Ugar.Ugar, .Ugar *,
.Vaii.Vaii, .Vaii *,
.Xpeo.Xpeo, .Xpeo *,
.Yiii.Yiii, .Yiii *,
.Zmth.Zmth, .Zmth *,
.Zname.Zname, .Zname *,
.Zsym.Zsym, .Zsym * {
font-style: normal;
}
/* The following scripts do not support bold OR italic text.
Note: we repeat the first class selector twice to increase CSS 'specificity'. */
.Arab.Arab, .Arab *,
.fa-Arab.fa-Arab, .fa-Arab *,
.glk-Arab.glk-Arab, .glk-Arab *,
.kk-Arab.kk-Arab, .kk-Arab *,
.ks-Arab.ks-Arab, .ks-Arab *,
.ku-Arab.ku-Arab, .ku-Arab *,
.mzn-Arab.mzn-Arab, .mzn-Arab *,
.ota-Arab.ota-Arab, .ota-Arab *,
.pa-Arab.pa-Arab, .pa-Arab *,
.ps-Arab.ps-Arab, .ps-Arab *,
.sd-Arab.sd-Arab, .sd-Arab *,
.tt-Arab.tt-Arab, .tt-Arab *,
.ug-Arab.ug-Arab, .ug-Arab *,
.ur-Arab.ur-Arab, .ur-Arab *,
.ms-Arab.ms-Arab, .ms-Arab *,
.Brah.Brah, .Brah *,
.Hebr.Hebr, .Hebr *,
.Lina.Lina, .Lina *,
.Linb.Linb, .Linb *,
.Mani.Mani, .Mani *,
.Mymr.Mymr, .Mymr *,
.Mong.Mong, .Mong *,
.mnc-Mong.mnc-Mong, .mnc-Mong *,
.sjo-Mong.sjo-Mong, .sjo-Mong *,
.xwo-Mong.xwo-Mong, .xwo-Mong *,
.Ogam.Ogam, .Ogam *,
.Orkh.Orkh, .Orkh *,
.Ougr.Ougr, .Ougr *,
.Runr.Runr, .Runr *,
.Sogd.Sogd, .Sogd *,
.Syrc.Syrc, .Syrc *,
.Tang.Tang, .Tang *,
.Vith.Vith, .Vith *,
.Xsux.Xsux, .Xsux * {
font-style: normal;
font-weight: normal;
}
/* For scripts where bolding is not used, as well as cuneiform scripts where the bolding
* can be hard to see, highlight the word in usage examples */
.Arab.e-example b, .Arab.e-quotation b, .Arab.e-normalization b,
.Brah.e-example b, .Brah.e-quotation b, .Brah.e-normalization b,
.Deva.e-example b, .Deva.e-quotation b, .Deva.e-normalization b,
.Hebr.e-example b, .Hebr.e-quotation b, .Hebr.e-normalization b,
.Lina.e-example b, .Lina.e-quotation b, .Lina.e-normalization b,
.Linb.e-example b, .Linb.e-quotation b, .Linb.e-normalization b,
.Mani.e-example b, .Mani.e-quotation b, .Mani.e-normalization b,
.Mong.e-example b, .Mong.e-quotation b, .Mong.e-normalization b,
.Ogam.e-example b, .Ogam.e-quotation b, .Ogam.e-normalization b,
.Orkh.e-example b, .Orkh.e-quotation b, .Orkh.e-normalization b,
.Ougr.e-example b, .Ougr.e-quotation b, .Ougr.e-normalization b,
.Runr.e-example b, .Runr.e-quotation b, .Runr.e-normalization b,
.Sogd.e-example b, .Sogd.e-quotation b, .Sogd.e-normalization b,
.Syrc.e-example b, .Syrc.e-quotation b, .Syrc.e-normalization b,
.Ugar.e-example b, .Ugar.e-quotation b, .Ugar.e-normalization b,
.Vith.e-example b, .Vith.e-quotation b, .Vith.e-normalization b,
.Xpeo.e-example b, .Xpeo.e-quotation b, .Xpeo.e-normalization b,
.Xsux.e-example b, .Xsux.e-quotation b, .Xsux.e-normalization b {
background-color: var(--wikt-palette-pink, #ffe0f0);
font-size: inherit;
}
/* Latin (.Latn)
This is the default script, so it has no definitions. */
/* This is needed to overcome the "smartness" of certain web browsers */
.Latn:lang(ja),
.Latn:lang(ko),
.Latn:lang(zh) {
font-family: inherit;
}
/* Latin Fraktur */
.Latf {
font-family: UnifrakturMaguntia, UnifrakturCook, Unifraktur, 'Code2001', Tahoma, 'Arial Unicode MS', sans-serif;
font-size: 125%;
}
/* Navajo */
.nv-Latn {
font-family: Calibri, 'Aboriginal Sans', 'DejaVu Sans', 'Arial Unicode MS', sans-serif !important;
}
/* Pitjantjatjara (ḻ ṉ ṟ ṯ and capitals) */
.pjt-Latn {
font-family: Arial, 'Microsoft Sans Serif', Tahoma, 'Code2000', sans-serif;
}
/* Latin extended */
/* no styles for .Latnx, .Latinx, .unicode, .Unicode */
/* Aghwan */
.Aghb {
font-family: OptimaModoki, sans-serif;
}
/* Ahom */
.Ahom {
font-family: AhomUnicode, 'Noto Serif Ahom', 'Unifont Upper', sans-serif;
}
/* Arabic */
.Arab,
.fa-Arab,
.glk-Arab,
.kk-Arab,
.ks-Arab,
.ku-Arab,
.mzn-Arab,
.ota-Arab,
.pa-Arab,
.ps-Arab,
.sd-Arab,
.tt-Arab,
.ug-Arab,
.ur-Arab,
.ms-Arab {
font-family: 'Noto Naskh Arabic', 'Iranian Sans', Tahoma, 'Microsoft Sans Serif', 'Arial Unicode MS', sans-serif;
font-size: 133%;
direction: rtl;
unicode-bidi: isolate;
}
/* Persian, Ottoman Turkish */
.fa-Arab {
font-family: 'Iranian Sans', 'Geeza Pro', Tahoma, sans-serif;
}
/* Kashmiri */
.ks-Arab {
font-family: 'Noto Naskh Arabic', Amiri, Mirza, 'Scheherazade New', Scheherazade, 'Geeza Pro', Tahoma, sans-serif;
}
/* Kurdish */
.ku-Arab {
font-family: 'Noto Naskh Arabic', Tahoma, 'Arial Unicode MS', 'UT Cairo', 'UT Naskh', sans-serif;
}
/* Punjabi Shahmukhi, Urdu */
.pa-Arab,
.ur-Arab {
font-family: 'Noto Nastaliq Urdu', Tahoma, 'Arial Unicode MS', 'UT Cairo', 'UT Naskh', sans-serif;
}
/* Pashto */
.ps-Arab {
font-family: Pokhto, 'Pashto Kror Asiatype', 'Scheherazade New', Scheherazade, sans-serif;
}
/* Sindhi */
.sd-Arab {
font-family: 'MB Sindhi', Tahoma, 'MBSarang Sattar', 'MB Bhitai Sattar', 'MB Lateefi', 'Ayaz Gul', 'Iranian Sans', sans-serif;
}
/* Uyghur */
.ug-Arab {
font-family: 'UKIJ Tuz', 'Microsoft Uighur', 'Scheherazade New', Scheherazade, 'Iranian Sans', 'Code2000', sans-serif;
}
/* Imperial Aramaic */
.Armi {
font-family: 'Segoe UI Historic', 'Aramaic Imperial Yeb', 'Noto Sans Imperial Aramaic', sans-serif;
direction: rtl;
unicode-bidi: isolate;
}
/* Armenian */
.Armn {
font-family: Mshtakan, Arial, 'Segoe UI', Tahoma, 'Arian AMU', 'DejaVu Sans', sans-serif;
}
/* Avestan */
.Avst {
font-family: Avestan, Ahuramzda, 'Noto Sans Avestan', sans-serif;
font-size: 125%;
direction: rtl;
unicode-bidi: isolate;
font-variant-ligatures: none; /* Avestan language */
}
.Avst:lang(pal) { /* Pazend (Middle Persian in Avestan script) */
font-variant-ligatures: normal;
}
/* Balinese */
.Bali {
font-family: 'Noto Sans Balinese', sans-serif;
}
/* Bamum */
.Bamu {
font-family: 'Noto Sans Bamum', sans-serif;
}
/* Batak */
.Batk {
font-family: Batak-Unicode, sans-serif;
}
/* Bengali */
.Beng {
font-family: 'Bangla Sangam MN', UniBangla, 'Code2000', Likhan, 'UT Bengali Dhaka', Vrinda, sans-serif;
font-size: 130%;
}
.as-Beng {
font-family: 'Bangla Sangam MN', UniBangla, 'Code2000', Likhan, 'UT Bengali Dhaka', Vrinda, sans-serif;
font-size: 130%;
}
/* Zhuyin (Bopomofo) */
.Bopo {
font-family: MOESongUN, DFKai-SB, 'Microsoft Yahei', 'Microsoft Jhenghei', 'Source Han Sans TC', 'Source Han Sans TW', 'Noto Sans CJK TC', sans-serif;
}
/* Brahmi */
.Brah {
font-family: 'Segoe UI Historic', 'Noto Sans Brahmi';
}
/* Braille */
.Brai {
font-size: 150%;
}
/* Buginese */
.Bugi {
font-family: Saweri, sans-serif;
}
/* Buhid [[Wiktionary:Grease pit/2025/February#Buhid]] */
.Buhd {
font-family: 'Noto Sans Buhid', Quivira, sans-serif;
font-size: 110%;
}
/* Chakma */
.Cakm {
font-family: RibengUni, sans-serif;
}
/* Unified Canadian Aboriginal Syllabics */
.Cans {
font-family: 'Euphemia UCAS', Euphemia, 'Aboriginal Sans', OskiBlackfoot, 'Code2000', 'Everson Mono Unicode', sans-serif;
font-size: 110%;
}
/* Carian, Italic, Lycian, Lydian */
.Cari,
.Ital,
.Lyci,
.Lydi {
font-family: 'Segoe UI Historic', Aegean, 'Noto Sans Carian', 'Noto Sans Old Italic', 'Noto Sans Lycian', 'Noto Sans Lydian', sans-serif;
font-size: 125%;
}
/* Cham */
.Cham {
font-family: 'Code2000', 'JG ChamCambodia', sans-serif;
font-size: 1.1em;
}
/* Cherokee */
.Cher {
font-family: Digohweli, 'Aboriginal Sans', 'Code2000', Marin, 'Rotinonhsonni Sans', 'Everson Mono Unicode', sans-serif;
font-size: 110%;
}
/* Coptic */
.Copt {
font-family: Quivira, Antinoou, 'New Athena Unicode', Analecta, FreeSerifAvvaShenouda, 'Arial Coptic', 'Sophia Nubian', 'Code2000', sans-serif;
font-size: 1.3em;
}
/* Cypriot */
.Cprt {
font-family: 'Segoe UI Historic', Aegean, sans-serif;
font-size: 1.15em;
direction: rtl;
unicode-bidi: isolate;
}
/* Cyrillic */
.Cyrl {
font-family: Helvetica, Geneva, 'Arial Unicode MS', 'Lucida Sans Unicode', 'Code2000', sans-serif;
}
/* Old Cyrillic (Old Church Slavonic, Old East Slavic) */
.Cyrs {
font-family: Monomakh, 'Monomakh Unicode', 'Monomakh Unicode TT', Menaion, 'Menaion Unicode', 'Menaion Unicode TT', Ponomar, 'Ponomar Unicode', 'Ponomar Unicode TT', Fedorovsk, 'Fedorovsk Unicode', 'Fedorovsk Unicode TT', Pochaevsk, 'Pochaevsk Unicode', 'Pochaevsk Unicode TT', Triodion, 'Triodion Unicode', 'Triodion Unicode TT', 'Acathist', 'Shafarik', Vilnius, BukyVede, 'Kliment Std', 'RomanCyrillic Std', 'Monomachus', 'Old Standard', 'Old Standard TT', Dilyana, 'Hirmos Ponomar', 'Hirmos Ponomar TT', 'Menaion Medieval', Lazov, 'Code2000', 'DejaVu Sans', 'Lucida Grande', 'Arial Unicode MS', 'Lucida Sans Unicode', sans-serif;
font-size: 125%;
}
/* Devanagari */
.Deva {
font-family: 'Adobe Devanagari', 'Noto Serif Devanagari', Utsaah, 'Devanagari MT', Raghu, Gargi, JanaSanskrit, JanaHindi, Siddhanta, sans-serif;
font-size: 120%;
}
/* Dogra */
.Dogr {
font-family: 'Noto Serif Dogra', sans-serif;
}
/* Deseret */
.Dsrt {
font-family: 'Segoe UI Symbol', 'Code2001', 'MPH 2B Damase', 'Everson Mono', sans-serif;
}
/* Demotic (Egyptian) */
.Egyd, .Egyd * {
font-style: normal;
direction: rtl;
}
/* Egyptian hieroglyphs */
.Egyp {
font-family: 'NewGardiner', 'Noto Sans Egyptian Hieroglyphs', Abydos, Aegyptus, 'Segoe UI Historic', sans-serif;
font-size: 150%;
}
/* Elbasan */
.Elba {
font-family: 'Noto Sans Elbasan', sans-serif;
}
/* Ethiopic (Ge'ez) */
.Ethi {
font-family: 'Abyssinica SIL', Nyala, 'Code2000', 'Ethiopia Jiret', 'GF Zemen Unicode', 'TITUS Cyberbit Basic', 'Visual Geez Unicode', 'Visual Geez Unicode Agazian', 'Visual Geez Unicode Title', sans-serif;
font-size: 120%;
}
/* Georgian */
.Geor {
font-family: 'DejaVu Sans', 'Arial Unicode MS', Sylfaen, sans-serif;
}
/* Glagolitic */
.Glag {
font-family: Shafarik, Menaion, 'Menaion Unicode', 'Menaion Unicode TT', 'Segoe UI Historic', BukyVede, Dilyana, 'Noto Sans Glagolitic', sans-serif;
font-size: 150%;
}
/* Gothic */
.Goth {
font-family: 'Segoe UI Historic', 'Code2001', Skeirs, 'MPH 2B Damase', sans-serif;
}
b.Goth,
strong.Goth {
font-size: larger;
}
/* Greek (.Grek) removed - [[Wiktionary:Grease pit/2025/February#Sans-serif Greek font]] */
/* Albanian, Pontic and Tsakonian in Greek script */
.Grek:lang(sq),
.Grek:lang(pnt),
.Grek:lang(tsd) {
font-family: 'Gentium Plus', 'Gentium', 'Lucida Sans Unicode', sans-serif;
}
/* Polytonic Greek */
.polytonic, .Polyt {
font-family: 'SBL Greek', 'SBL BibLit', 'New Athena Unicode', 'DejaVu Sans', Athena, Gentium, 'Gentium Plus', 'Palatino Linotype', Menaion, Times, 'Arial Unicode MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Code2000', sans-serif;
}
/* Messapic */
.Polyt:lang(cms) {
font-family: 'Noto Serif', 'Lucida Sans Unicode', 'Gentium Plus', 'Gentium', 'Noto Sans', sans-serif;
}
/* Gujarati */
.Gujr {
font-family: 'Noto Sans Gujarati', 'Gujarati Sangam MN', 'Gujarati MT', Shruti, 'Lucida Grande', 'Arial Unicode MS', 'Lucida Sans Unicode', 'Code2000', 'TITUS Cyberbit Basic', 'Chrysanthi Unicode', 'Bitstream Cyberbit', 'Bitstream CyberBase', 'Bitstream Vera', 'Visual Geez Unicode', sans-serif;
font-size: 125%;
}
/* Gurmukhi */
.Guru {
font-family: 'Gurmukhi MN', 'Gurmukhi MT', 'UT Punjabi Amritsar', 'Lucida Grande', 'Arial Unicode MS', 'Lucida Sans Unicode', 'Code2000', 'TITUS Cyberbit Basic', 'Chrysanthi Unicode', 'Bitstream Cyberbit', 'Bitstream CyberBase', 'Bitstream Vera', 'Visual Geez Unicode', sans-serif;
font-size: 125%;
}
/* Chinese (Han)
.Hani = generic
.Hans = simplified
.Hant = traditional
*/
.Hans {
font-family: 'PingFang SC', DengXian, 'Source Han Sans SC', 'Source Han Sans CN', 'Noto Sans CJK SC', 'Microsoft Yahei', SimHei, SimSun, NSimSun, SimSun-ExtB, Song, 'Heiti SC', HanaMinA, HanaMinB, sans-serif;
}
.Hani,
.Hant {
font-family: 'PingFang TC', 'Source Han Sans TC', 'Source Han Sans TW', 'Noto Sans CJK TC', 'Microsoft Jhenghei', MOESongUN, PMingLiU, PMingLiU-ExtB, MingLiU, MingLiU-ExtB, Ming, 'Heiti TC', HanaMinA, HanaMinB, sans-serif;
}
.Hani,
.Hans,
.Hant {
font-size: 120%;
line-height: 1;
}
.Hani:lang(vi) {
font-family: 'Nom Na Tong', 'HAN NOM A', 'HAN NOM B', Sun-ExtA, Sun-ExtB, Ming-Lt-HKSCS-UNI-H, Ming-Lt-HKSCS-ExtB, HanaMinA, HanaMinB, HanaMin, 'PingFang TC', MingLiU, MingLiU-ExtB, 'MingLiU_HKSCS', 'MingLiU_HKSCS-ExtB', SimSun, SimSun-ExtB, 'Arial Unicode MS', 'TITUS Cyberbit Basic', sans-serif;
/* CJK Unified Ideographs Extension C and Extension D (U+2A700..U+2B734, U+2B740..U+2B81F)
font-family: Sun-ExtB, 'MingLiU_HKSCS-ExtB', Ming-Lt-HKSCS-ExtB, HanaMinB, sans-serif;
**/
}
/* Hanunoo */
.Hano {
font-family: 'Noto Sans Hanunoo', Quivira, 'MPH 2B Damase', sans-serif;
font-size: 1.1em;
}
/* Hatran */
.Hatr {
font-family: 'Noto Sans Hatran', sans-serif;
direction: rtl;
}
/* Hebrew */
.Hebr {
font-family: 'SBL Hebrew', 'SBL BibLit', 'Taamey David CLM', 'Taamey Frank CLM', Alef, 'Noto Sans Hebrew', Narkisim, Miriam, Kinryu, 'Arial Hebrew', Arial, 'Adobe Hebrew', serif;
font-size: 133%;
direction: rtl;
unicode-bidi: isolate;
}
/* Hluw: Anatolian Hieroglyphs */
.Hluw {
font-family: 'Noto Sans Anatolian Hieroglyphs', Anatolian;
font-size: 200%;
}
/* Hiragana: see .Jpan */
/* Javanese */
.Java {
font-family: 'Tuladha Jejeg', 'Javanese Text', adjisaka, 'Noto Sans Javanese', sans-serif;
}
/* Japanese scripts
.Hira = Hiragana
.Kana = Katakana (used for Ainu language)
.Jpan = Hiragana + Katakana + Kanji
*/
.Hira,
.Jpan,
.Kana {
font-family: 'Hiragino Kaku Gothic Pro', Osaka, 'Yu Gothic', Meiryo, 'Source Han Sans J', 'Source Han Sans JP', 'Noto Sans CJK JP', 'Droid Sans Japanese', 'MS PGothic', 'MS Gothic', 'MS PMincho', 'MS Mincho', HanaMinA, HanaMinB, sans-serif;
font-size: 120%;
line-height: 1;
}
.Jpan ruby rt {
font-family: 'Yu Gothic UI', 'Meiryo UI', 'MS UI Gothic', sans-serif;
font-size: 60%;
}
/* Kayah Li, Ol Chiki, Rejang */
.Kali,
.Olck {
font-family: 'Code2000', sans-serif;
}
/* Katakana (Ainu language): see .Jpan */
/* Kharoshthi */
.Khar {
font-family: 'Segoe UI Historic', 'MPH 2B Damase', sans-serif;
font-size: 1.1em;
direction: rtl;
unicode-bidi: isolate;
}
/* Khmer */
.Khmr {
font-family: 'Khmer OS', 'Khmer OS Content', 'Leelawadee UI', 'Noto Sans Khmer', 'Code2000', 'Khmer Mondulkiri U OT ls', sans-serif;
font-size: 125%;
}
/* Kannada */
.Knda {
font-family: 'Kannada Sangam MN', JanaKannada, Tunga, Kedage, RaghuKannada, Sampige, 'Arial Unicode MS', 'Code2000', 'Bitstream Cyberbit', 'Bitstream CyberBase', sans-serif;
font-size: 125%;
}
/* Korean */
.Kore,
.Hang {
font-family: 'Apple SD Gothic Neo', 'Malgun Gothic', Dotum, Gulim, 'NanumBarunGothic YetHangul', NanumBarunGothic, UnDotum, 'Source Han Sans K', 'Source Han Sans KR', 'Noto Sans CJK KR', NanumGothic, 'NanumMyeongjo YetHangul', NanumMyeongjo, Batang, UnBatang, sans-serif;
font-size: 120%;
line-height: 1;
}
/* Kaithi */
.Kthi {
font-family: 'Noto Sans Kaithi', sans-serif;
}
/* Lanna */
.Lana {
font-family: 'Lanna Alif', 'Noto Sans Tai Tham', sans-serif;
}
/* Lao */
.Laoo {
font-family: 'Phetsarath OT', 'Saysettha OT', 'JG Basic Lao Opentype', 'JG Basic2 Lao Opentype', 'JG LaoTimes Opentype', 'Phagnoum Lao Unicode Opentype', 'JG Lao Old Arial Opentype', DokChampa, 'Code2000', 'JG Lao Classic Opentype', 'Alice0 Unicode', 'Alice1 Unicode', 'Alice2 Unicode', 'Alice3 Unicode', 'Alice4 Unicode', 'Alice5 Unicode', sans-serif;
font-size: 125%;
}
/* Lepcha */
.Lepc {
font-family: 'Noto Sans Lepcha', Mingzat, sans-serif;
}
/* Limbu */
.Limb {
font-family: 'Code2000', 'MPH 2B Damase', sans-serif;
}
/* Linear A */
.Lina {
font-family: Aegean, 'Noto Sans Linear A', sans-serif;
font-size: 125%;
}
/* Linear B */
.Linb {
font-family: Aegean, 'Noto Sans Linear B', sans-serif;
font-size: 125%;
}
/* Lisu */
.Lisu {
font-family: 'Noto Sans Lisu', Quivira, sans-serif;
}
/* Mahajani */
.Mahj {
font-family: 'Noto Sans Mahajani', sans-serif;
font-size: 125%;
}
/* Mandaic */
.Mand {
font-family: 'Noto Sans Mandaic', sans-serif;
direction: rtl;
unicode-bidi: isolate;
}
/* Manichaean */
.Mani {
font-family: 'Noto Sans Manichaean', sans-serif;
font-size: 133%;
direction: rtl;
unicode-bidi: isolate;
}
/* Meroitic cursive */
.Merc {
font-family: 'Segoe UI Historic', Aegyptus, sans-serif;
font-size: 150%;
}
/* Meroitic hieroglyphs */
.Mero {
font-family: Aegyptus, sans-serif;
font-size: 150%;
}
/* Malayalam */
.Mlym {
font-family: 'Malayalam Sangam MN', Kartika, 'Code2000', 'Lucida Grande', 'Arial Unicode MS', 'Lucida Sans Unicode', 'TITUS Cyberbit Basic', 'Chrysanthi Unicode', 'Bitstream Cyberbit', 'Bitstream CyberBase', 'Bitstream Vera', 'Visual Geez Unicode', sans-serif;
font-size: 125%;
}
/* Classical Mongolian, Manchu, Xibe, Clear Script */
.Mong,
.mnc-Mong, /* Manchu */
.sjo-Mong, /* Xibe */
.xwo-Mong /* Clear Script, Todo */ {
font-family: 'Mongolian Baiti', 'Noto Sans Mongolian', sans-serif;
font-size: 140%;
-webkit-writing-mode: vertical-lr;
-moz-writing-mode: vertical-lr;
writing-mode: vertical-lr;
}
/* Meitei Mayek */
.Mtei {
font-family: 'Eeyek Unicode', 'Noto Sans Meetei Mayek', sans-serif;
}
/* Burmese */
.Mymr {
font-family: Pyidaungsu, MON3 Anonta 1';
font-size: 130%;
}
big.Mymr,
strong.Mymr,
b.Mymr,
b .Mymr {
font-size: 162%;
}
.Mymr b {
font-size: 130%;
}
/* North Arabian */
.Narb {
font-family: 'Noto Sans Old North Arabian', sans-serif;
}
/* Nüshu or Nushu */
.Nshu {
font-family: 'Noto Sans Nushu', 'Noto Traditional Nushu', 'Unicode Nushu', sans-serif;
font-size: 130%;
}
/* Ogham */
.Ogam {
font-family: 'Segoe UI Historic', Beth-Luis-Nion, Pollach, 'Maigh Nuad', 'Craobh Ruadh', 'Everson Mono Ogham', Cog, Crosta, 'TITUS Ogham', 'Ragnarok Ogham', sans-serif;
font-size: 125%;
}
big.Ogam, strong.Ogam, b.Ogam, b .Ogam {
font-size: 156%;
}
.Ogam b {
font-size: 125%;
}
/* Orkhon Runes */
.Orkh {
font-family: 'Segoe UI Historic', Orkun, 'Old Turkic', sans-serif;
}
/* Odia */
.Orya {
font-family: 'Oriya Sangam MN', sans-serif;
font-size: 125%;
}
/* Old Uyghur */
.Ougr {
font-family: 'Noto Serif Old Uyghur';
font-size: 133%;
direction: rtl;
unicode-bidi: isolate;
}
/* Palmyrene */
.Palm, .Palm * {
font-family: 'Noto Sans Palmyrene';
}
/* Old Permian */
.Perm {
font-family: 'Noto Sans Old Permic';
}
/* Phags-pa */
.Phag {
font-family: 'BabelStone Phags-pa Book', 'Microsoft PhagsPa', sans-serif;
-webkit-writing-mode: vertical-lr;
-moz-writing-mode: vertical-lr;
writing-mode: vertical-lr;
layout-flow: vertical-ideographic;
}
/* Inscriptional Pahlavi */
.Phli {
font-family: 'Segoe UI Historic', 'Noto Sans Inscriptional Pahlavi', Shapour, 'ZH Mono', sans-serif;
direction: rtl;
unicode-bidi: isolate;
}
/* Phoenician */
.Phnx {
font-family: 'Segoe UI Historic', 'ALPHABETUM Unicode', 'MPH 2B Damase', Aegean, 'Code2001', 'Free Sans', sans-serif;
font-size: 125%;
direction: rtl;
unicode-bidi: isolate;
}
/* Parthian */
.Prti {
font-family: 'Segoe UI Historic', 'Noto Sans Inscriptional Parthian', 'ZH Mono', sans-serif;
}
/* Rejang */
.Rjng, .Rjng * {
font-family: 'Noto Sans Rejang', 'Code2000', sans-serif;
}
/* Runic (Germanic) */
.Runr {
font-family: 'Segoe UI Historic', Junicode, 'Free Mono', 'Caslon Roman', 'Segoe UI Symbol', 'Code2000', 'Everson Mono', 'TITUS Cyberbit Basic', sans-serif;
font-size: 130%;
}
/* Samaritan */
.Samr {
font-family: 'Noto Sans Samaritan', Quivira, 'Everson Mono', Unifont, sans-serif;
font-size: 133%;
direction: rtl;
unicode-bidi: isolate;
}
/* South Arabian */
.Sarb {
font-family: 'Segoe UI Historic', Qataban, 'Noto Sans Old South Arabian', sans-serif;
}
/* Saurashtra */
.Saur {
font-family: Pagul, sans-serif;
}
/* Shavian */
.Shaw {
font-family: 'Segoe UI Historic', 'Code2001', 'Everson Mono', sans-serif;
}
/* Sharada */
.Shrd {
font-family: 'Noto Sans Sharada', sans-serif;
font-size: 125%;
}
/* Siddham */
.Sidd {
font-family: 'Noto Sans Siddham', sans-serif;
}
/* Sinhalese */
.Sinh {
font-family: 'Sinhala Sangam MN', KaputaUnicode, KandyUnicode, Dinamina, DinaminaUniWeb, Potha, Madhura, sans-serif;
font-size: 125%;
}
/* Sundanese */
.Sund {
font-family: 'Sundanese Unicode', 'Noto Sans Sundanese', sans-serif;
}
/* Syloti Nagri */
.Sylo {
font-family: 'Noto Sans Syloti Nagri', sans-serif;
}
/* Syriac */
.Syrc {
font-family: 'Estrangelo Edessa', 'San Francisco', 'Code2000', 'Bitstream Cyberbit', 'Bitstream CyberBase', 'Bitstream Vera', 'TITUS Cyberbit Basic', sans-serif;
font-size: 1.3em;
direction: rtl;
unicode-bidi: isolate;
}
/* Serto or Western form of Syriac */
.Syrc:lang(tru),
.Syrc:lang(amw) {
font-family: 'Noto Sans Syriac Western', 'Serto Jerusalem', 'Noto Sans Syriac';
}
/* Madnhaya or Eastern form of Syriac */
.Syrc:lang(aii) {
font-family: 'Noto Sans Syriac Eastern', 'Noto Sans Syriac';
}
/* Tagbanwa */
.Tagb {
font-family: Quivira, Tagbanwa, sans-serif;
font-size: 1.1em;
}
/* Tai Le */
.Tale {
font-family: 'Microsoft Tai Le', 'Tai Le Valentinium', 'MPH 2B Damase', sans-serif;
}
/* New Tai Lue */
.Talu {
font-family: 'Cherry Unicode', sans-serif;
}
/* Tamil */
.Taml {
font-family: 'Tamil Sangam MN', InaiMathi, Vijaya, Akshar, JanaTamil, 'Code2000', ETTamilNew, 'Lohit Tamil', 'Arial Unicode MS', 'Free Serif', Latha, sans-serif;
font-size: 125%;
}
/* Tangut */
.Tang {
font-family: 'BabelStone Tangut Wenhai', 'Tangut N4694', 'Tangut Yinchuan', 'New Tangut', 'New Tangut Std', 'Tangut TWU', 'Babelstone Tangut Wenhai', sans-serif;
font-size: 1.2em;
text-orientation: upright;
-webkit-text-orientation: upright
}
/* Tai Viet */
.Tavt {
font-family: 'Tai Heritage Pro', 'Noto Sans Tai Viet', sans-serif;
}
/* Telugu */
.Telu {
font-family: 'Telugu Sangam MN', sans-serif;
font-size: 125%;
}
/* Tifinagh (Berber) */
.Tfng {
font-family: 'Hapax Berbère', Ebrima, 'Code2000', DejaVu, sans-serif;
font-size: 120%;
}
/* Tagalog (Baybayin) */
.Tglg {
font-family: 'Noto Sans Tagalog', 'Tagalog Stylized', 'Baybayin Lopez', 'Tagalog Doctrina 1593', Quivira, 'Code2000', sans-serif;
}
.Tglg:lang(bcl) {
font-family: 'Bikol Mintz', 'Noto Sans Tagalog', 'Tagalog Stylized', 'Baybayin Lopez', 'Tagalog Doctrina 1593', Quivira, 'Code2000', sans-serif;
}
.Tglg:lang(ceb) {
font-family: 'Bisaya Hervas', 'Noto Sans Tagalog', 'Tagalog Stylized', 'Baybayin Lopez', 'Tagalog Doctrina 1593', Quivira, 'Code2000', sans-serif;
}
/* Thai */
.Thai {
font-family: 'Leelawadee UI', Leelawadee, 'Arial Unicode MS', 'Code2000', sans-serif;
font-size: 125%;
}
/* Tibetan */
.Tibt {
font-family: 'Noto Serif Tibetan', 'Noto Sans Tibetan', Jomolhari-ID, 'Tibetan Machine Uni', 'Tibetan Machine Web', Jomolhari, 'Microsoft Himalaya', sans-serif;
font-size: 130%;
}
/* Ugaritic */
.Ugar {
font-family: 'Oxford Ugaritic', 'Segoe UI Historic', Aegean, 'Code2001', sans-serif;
}
/* Vai */
.Vaii {
font-family: Ebrima, 'Code2000', sans-serif;
font-size: 1.1em;
}
/* Vithkuqi */
.Vith {
font-family: "Noto Serif Vithkuqi", "Noto Sans Vithkuqi", sans-serif;
}
/* Old Persian cuneiform */
.Xpeo {
font-family: 'Segoe UI Historic', Aegean, Xerxes, 'Code2001', sans-serif;
}
/* Sumero-Akkadian cuneiform */
.Xsux {
font-family: Akkadian, FreeIdgSerif, CuneiformComposite, 'Segoe UI Historic', sans-serif; /* Neo-Assyrian form */
/* font-family: FreeIdgSerif, Akkadian, CuneiformComposite, 'Segoe UI Historic', sans-serif; */ /* Old Assyrian form */
/* font-family: Akkadian, CuneiformComposite, FreeIdgSerif, 'Segoe UI Historic', sans-serif; /* /* Neo-Sumerian form */
/* font-family: Akkadian, CuneiformComposite, FreeIdgSerif, 'Segoe UI Historic', sans-serif; /* /* Classical Sumerian form */
font-size: 125%;
}
/* Specify Hittite fonts */
.Xsux:lang(hit) {
font-family: UllikummiA, Akkadian, FreeIdgSerif, CuneiformComposite, 'Segoe UI Historic', sans-serif;
}
/* Specify Sumerian fonts */
.Xsux:lang(sux) {
font-family: CuneiformComposite, Akkadian, 'Segoe UI Historic', sans-serif;
}
/* Symbols */
.Zsym {
font-family: Symbola, sans-serif;
font-size: 150%;
}
/* Rumi numerals; see <https://en.wikipedia.org/wiki/Rumi_Numeral_Symbols> */
.Rumin, .Ruminumerals {
font-size:1.2em;
}
/*
Multilingual writing systems and notations
*/
/* English Phonemic Representation (AHD) */
.enPR {
font-family: 'Arial Unicode MS', 'Lucida Grande', Gentium, 'Gentium Plus', 'Gentium Alternative', 'TITUS Cyberbit Basic', 'Code2000', 'Lucida Sans Unicode', sans-serif;
}
/* International Phonetic Alphabet */
.IPA, .Ipach, .IPAchar {
font-family: Gentium, 'Gentium Plus', GentiumAlt, 'DejaVu Sans', 'Segoe UI', 'Lucida Grande', 'Charis SIL', 'Doulos SIL', 'TITUS Cyberbit Basic', 'Code2000', 'Lucida Sans Unicode', sans-serif;
font-size: 110%;
font-variant-ligatures: no-common-ligatures;
}
/* Musical notation */
.Music, .musical {
font-family: Musica, 'Musical Symbols', Euterpe, 'Noto Music', sans-serif;
font-size: 1.4em;
}
/* Znamenny musical notation */
.Zname {
font-family: 'Mezenets Unicode', Slavonic, Voskresensky, Smolensky, Symbola, sans-serif;
}
twfx372uqvz5ttyggko437cnn3rnp6r
ထာမ်ပလိက်:gadget tag
10
295687
396504
2026-06-07T14:35:03Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "<span style="font-size: 90%; background: var(--background-color-neutral,#ddd); color: var(--color-base,black); border-radius: 4px; border: 1px solid var(--border-color-base,#808080); padding: 0.1em 0.3em;{{#if:{{{2|}}}| cursor: help}}" {{#if:{{{2|}}}|title="{{{2|}}}"}}>{{{1}}}</span>"
396504
wikitext
text/x-wiki
<span style="font-size: 90%; background: var(--background-color-neutral,#ddd); color: var(--color-base,black); border-radius: 4px; border: 1px solid var(--border-color-base,#808080); padding: 0.1em 0.3em;{{#if:{{{2|}}}| cursor: help}}" {{#if:{{{2|}}}|title="{{{2|}}}"}}>{{{1}}}</span>
q2umsf9ygy0s5vzek09dfq0rubxmob0
မဳဒဳယာဝဳကဳ:Gadget-RegexMenuFramework.js
8
295688
396506
2026-06-07T14:45:27Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "// {{documentation}} /** * TemplateScript adds configurable templates and scripts to the sidebar, and adds an example regex editor. * @see [[meta:TemplateScript]] * @author [[User:Pathoschild]] * @update-token [[meta:File:pathoschild/templatescript.js]] */ mw.loader.load('//tools-static.wmflabs.org/meta/scripts/pathoschild.templatescript.js');"
396506
javascript
text/javascript
// {{documentation}}
/**
* TemplateScript adds configurable templates and scripts to the sidebar, and adds an example regex editor.
* @see [[meta:TemplateScript]]
* @author [[User:Pathoschild]]
* @update-token [[meta:File:pathoschild/templatescript.js]]
*/
mw.loader.load('//tools-static.wmflabs.org/meta/scripts/pathoschild.templatescript.js');
7blaltfqd7vx48e4935xh5r92z0lips
မဳဒဳယာဝဳကဳ:Gadget-purgetab.js
8
295689
396507
2026-06-07T14:46:33Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "// {{documentation}} jQuery(document).ready(function() { if (!mw.config.get('wgArticleId')) return; var link; var portletIdToAdd = $("#p-cactions").length != 0 ? "p-cactions" : "p-views"; link = mw.util.addPortletLink( portletIdToAdd, mw.util.getUrl(mw.config.get('wgPageName'), { 'action': 'purge' }), (mw.config.get('skin') == "vector") ? "Purge" : "*", 'ca-purge', "Purge cache for this page", '*'..."
396507
javascript
text/javascript
// {{documentation}}
jQuery(document).ready(function() {
if (!mw.config.get('wgArticleId'))
return;
var link;
var portletIdToAdd = $("#p-cactions").length != 0 ? "p-cactions" : "p-views";
link = mw.util.addPortletLink(
portletIdToAdd, mw.util.getUrl(mw.config.get('wgPageName'), {
'action': 'purge'
}),
(mw.config.get('skin') == "vector") ? "Purge" : "*",
'ca-purge', "Purge cache for this page", '*'
);
var api = new mw.Api();
link = mw.util.addPortletLink(
portletIdToAdd, mw.util.getUrl(mw.config.get('wgPageName'), {
'action': 'purge',
'forcelinkupdate': 1
}), // not sure if raw link works...
(mw.config.get('skin') == "vector") ? "Hard purge" : "**",
'ca-purge-forcelinkupdate', "Purge with forced link table update", ','
);
link.addEventListener('click', function(ev) {
api.post({
action: 'purge',
pageids: mw.config.get('wgArticleId'),
forcelinkupdate: 1
}).then(function() {
location.reload();
}, function(code, details) {
var mesg;
switch (code) {
case 'http':
mesg = 'HTTP error: ' + details.xhr.statusText;
break;
case 'ok-but-empty':
mesg = 'Received empty response.';
break;
default:
mesg = details.error.info;
}
mw.util.jsMessage('<b>Hard purge failed</b>: ' + mesg);
console.error(arguments);
});
ev.preventDefault();
}, false);
link = mw.util.addPortletLink(
portletIdToAdd, 'javascript:void window.warranty',
(mw.config.get('skin') == "vector") ? "Null edit" : "***",
'ca-nulledit', "Null edit", '0'
);
link.addEventListener('click', function(ev) {
api.post({
action: 'edit',
pageid: mw.config.get('wgArticleId'),
appendtext: '',
watchlist: 'nochange',
nocreate: '1',
token: mw.user.tokens.get('csrfToken')
}).then(function() {
location.reload();
}, function(code, details) {
var mesg;
switch (code) {
case 'http':
mesg = 'HTTP error: ' + details.xhr.statusText;
break;
case 'ok-but-empty':
mesg = 'Received empty response.';
break;
default:
mesg = details.error.info;
}
mw.util.jsMessage('<b>Null edit failed</b>: ' + mesg);
console.error(arguments);
});
ev.preventDefault();
}, false);
});
0l8v0diur942q6kq5oa89o3eoji1t0e
မဳဒဳယာဝဳကဳ:Gadget-purgetab.js/documentation
8
295690
396508
2026-06-07T14:47:29Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "{{documentation subpage}} <!--Add description here. This subpage initially added just for categorization.--> See also: [[Special:Gadgets]]. <includeonly> [[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]] </includeonly>"
396508
wikitext
text/x-wiki
{{documentation subpage}}
<!--Add description here. This subpage initially added just for categorization.-->
See also: [[Special:Gadgets]].
<includeonly>
[[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]]
</includeonly>
nh2z4mxstdhoy5k1d14op93dv5v8194
မဳဒဳယာဝဳကဳ:Gadget-popups.js
8
295691
396509
2026-06-07T14:48:45Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "// {{documentation}} // _________________________________________________________________________________________ // | | // | === WARNING: GLOBAL GADGET FILE === | // | Changes to this page affect many users. | // | Please discuss c..."
396509
javascript
text/javascript
// {{documentation}}
// _________________________________________________________________________________________
// | |
// | === WARNING: GLOBAL GADGET FILE === |
// | Changes to this page affect many users. |
// | Please discuss changes on the [[Wiktionary:Grease Pit]] before editing. |
// |_________________________________________________________________________________________|
mw.loader.load('//en.wikipedia.org/w/index.php?action=raw&title=MediaWiki:Gadget-navpop.css&ctype=text/css', "text/css");
// Forked from [[:w:MediaWiki:Gadget-popups.js]]
$(function () {
//////////////////////////////////////////////////
// Globals
//
// Trying to shove as many of these as possible into the pg (popup globals) object
var pg = {
re: {}, // regexps
ns: {}, // namespaces
string: {}, // translatable strings
wiki: {}, // local site info
user: {}, // current user info
misc: {}, // YUCK PHOOEY
option: {}, // options, see newOption etc
optionDefault: {}, // default option values
flag: {}, // misc flags
cache: {}, // page and image cache
structures: {}, // navlink structures
timer: {}, // all sorts of timers (too damn many)
counter: {}, // .. and all sorts of counters
current: {}, // state info
fn: {}, // functions
endoflist: null
};
/* Bail if the gadget/script is being loaded twice */
if( window.pg ) {
return;
}
/* Export to global context */
window.pg = pg;
/// Local Variables: ///
/// mode:c ///
/// End: ///
// ENDFILE: main.js
// STARTFILE: actions.js
function setupTooltips(container, remove, force, popData) {
log('setupTooltips, container='+container+', remove='+remove);
if (!container) {
//<NOLITE>
// the main initial call
if (getValueOf('popupOnEditSelection') && document && document.editform && document.editform.wpTextbox1) {
document.editform.wpTextbox1.onmouseup=doSelectionPopup;
}
//</NOLITE>
// article/content is a structure-dependent thing
container = defaultPopupsContainer();
}
if (!remove && !force && container.ranSetupTooltipsAlready) { return; }
container.ranSetupTooltipsAlready = !remove;
var anchors;
anchors=container.getElementsByTagName('A');
setupTooltipsLoop(anchors, 0, 250, 100, remove, popData);
}
function defaultPopupsContainer() {
if (getValueOf('popupOnlyArticleLinks')) {
return document.getElementById('mw_content') ||
document.getElementById('content') ||
document.getElementById('article') || document;
}
return document;
}
function setupTooltipsLoop(anchors,begin,howmany,sleep, remove, popData) {
log(simplePrintf('setupTooltipsLoop(%s,%s,%s,%s,%s)', arguments));
var finish=begin+howmany;
var loopend = Math.min(finish, anchors.length);
var j=loopend - begin;
log ('setupTooltips: anchors.length=' + anchors.length + ', begin=' + begin +
', howmany=' + howmany + ', loopend=' + loopend + ', remove=' + remove);
var doTooltip= remove ? removeTooltip : addTooltip;
// try a faster (?) loop construct
if (j > 0) {
do {
var a=anchors[loopend - j];
if (typeof a==='undefined' || !a || !a.href) {
log('got null anchor at index ' + loopend - j);
continue;
}
doTooltip(a, popData);
} while (--j);
}
if (finish < anchors.length) {
setTimeout(function() {
setupTooltipsLoop(anchors,finish,howmany,sleep,remove,popData);},
sleep);
} else {
if ( !remove && ! getValueOf('popupTocLinks')) { rmTocTooltips(); }
pg.flag.finishedLoading=true;
}
}
// eliminate popups from the TOC
// This also kills any onclick stuff that used to be going on in the toc
function rmTocTooltips() {
var toc=document.getElementById('toc');
if (toc) {
var tocLinks=toc.getElementsByTagName('A');
var tocLen = tocLinks.length;
for (var j=0; j<tocLen; ++j) {
removeTooltip(tocLinks[j], true);
}
}
}
function addTooltip(a, popData) {
if ( !isPopupLink(a) ) { return; }
a.onmouseover=mouseOverWikiLink;
a.onmouseout= mouseOutWikiLink;
a.onmousedown = killPopup;
a.hasPopup = true;
a.popData = popData;
}
function removeTooltip(a) {
if ( !a.hasPopup ) { return; }
a.onmouseover = null;
a.onmouseout = null;
if (a.originalTitle) { a.title = a.originalTitle; }
a.hasPopup=false;
}
function removeTitle(a) {
if (!a.originalTitle) {
a.originalTitle=a.title;
}
a.title='';
}
function restoreTitle(a) {
if ( a.title || !a.originalTitle ) { return; }
a.title = a.originalTitle;
}
function registerHooks(np) {
var popupMaxWidth=getValueOf('popupMaxWidth');
if (typeof popupMaxWidth === 'number') {
var setMaxWidth = function () {
np.mainDiv.style.maxWidth = popupMaxWidth + 'px';
np.maxWidth = popupMaxWidth;
};
np.addHook(setMaxWidth, 'unhide', 'before');
}
//<NOLITE>
np.addHook(addPopupShortcuts, 'unhide', 'after');
np.addHook(rmPopupShortcuts, 'hide', 'before');
//</NOLITE>
}
function removeModifierKeyHandler(a) {
//remove listeners for modifier key if any that were added in mouseOverWikiLink
document.removeEventListener('keydown', a.modifierKeyHandler, false);
document.removeEventListener('keyup', a.modifierKeyHandler, false);
}
function mouseOverWikiLink(evt) {
if (!evt && window.event) {evt=window.event;}
// if the modifier is needed, listen for it,
// we will remove the listener when we mouseout of this link or kill popup.
if (getValueOf('popupModifier')) {
// if popupModifierAction = enable, we should popup when the modifier is pressed
// if popupModifierAction = disable, we should popup unless the modifier is pressed
var action = getValueOf('popupModifierAction');
var key = action=='disable' ? 'keyup' : 'keydown';
var a = this;
a.modifierKeyHandler = function(evt) {
mouseOverWikiLink2(a, evt);
};
document.addEventListener(key, a.modifierKeyHandler, false);
}
return mouseOverWikiLink2(this, evt);
}
/**
* Gets the references list item that the provided footnote link targets. This
* is typically a li element within the ol.references element inside the reflist.
* @param {Element} a - A footnote link.
* @returns {Element|boolean} The targeted element, or false if one can't be found.
*/
function footnoteTarget(a) {
var aTitle=Title.fromAnchor(a);
// We want ".3A" rather than "%3A" or "?" here, so use the anchor property directly
var anch = aTitle.anchor;
if ( ! /^(cite_note-|_note-|endnote)/.test(anch) ) { return false; }
var lTitle=Title.fromURL(location.href);
if ( lTitle.toString(true) !== aTitle.toString(true) ) { return false; }
var el=document.getElementById(anch);
while ( el && typeof el.nodeName === 'string') {
var nt = el.nodeName.toLowerCase();
if ( nt === 'li' ) { return el; }
else if ( nt === 'body' ) { return false; }
else if ( el.parentNode ) { el=el.parentNode; }
else { return false; }
}
return false;
}
function footnotePreview(x, navpop) {
setPopupHTML('<hr />' + x.innerHTML, 'popupPreview', navpop.idNumber);
}
function modifierPressed(evt) {
var mod=getValueOf('popupModifier');
if (!mod) { return false; }
if (!evt && window.event) {evt=window.event;}
return ( evt && mod && evt[mod.toLowerCase() + 'Key'] );
}
// Checks if the correct modifier pressed/unpressed if needed
function isCorrectModifier(a,evt) {
if (!getValueOf('popupModifier')) { return true; }
// if popupModifierAction = enable, we should popup when the modifier is pressed
// if popupModifierAction = disable, we should popup unless the modifier is pressed
var action = getValueOf('popupModifierAction');
return ( action == 'enable' && modifierPressed(evt) ||
action == 'disable' && !modifierPressed(evt) );
}
function mouseOverWikiLink2(a, evt) {
if (!isCorrectModifier(a,evt)) { return; }
if ( getValueOf('removeTitles') ) { removeTitle(a); }
if ( a==pg.current.link && a.navpopup && a.navpopup.isVisible() ) { return; }
pg.current.link=a;
if (getValueOf('simplePopups') && !pg.option.popupStructure) {
// reset *default value* of popupStructure
setDefault('popupStructure', 'original');
}
var article=(new Title()).fromAnchor(a);
// set global variable (ugh) to hold article (wikipage)
pg.current.article = article;
if (!a.navpopup) {
a.navpopup=newNavpopup(a, article);
pg.current.linksHash[a.href] = a.navpopup;
pg.current.links.push(a);
}
if (a.navpopup.pending === null || a.navpopup.pending !== 0) {
// either fresh popups or those with unfinshed business are redone from scratch
simplePopupContent(a, article);
}
a.navpopup.showSoonIfStable(a.navpopup.delay);
clearInterval(pg.timer.checkPopupPosition);
pg.timer.checkPopupPosition=setInterval(checkPopupPosition, 600);
if(getValueOf('simplePopups')) {
if (getValueOf('popupPreviewButton') && !a.simpleNoMore) {
var d=document.createElement('div');
d.className='popupPreviewButtonDiv';
var s=document.createElement('span');
d.appendChild(s);
s.className='popupPreviewButton';
s['on' + getValueOf('popupPreviewButtonEvent')] = function() {
a.simpleNoMore=true;
d.style.display = "none";
nonsimplePopupContent(a,article);
};
s.innerHTML=popupString('show preview');
setPopupHTML(d, 'popupPreview', a.navpopup.idNumber);
}
}
if (a.navpopup.pending !== 0 ) {
nonsimplePopupContent(a, article);
}
}
// simplePopupContent: the content that do not require additional download
// (it is shown even when simplePopups is true)
function simplePopupContent(a, article) {
/* FIXME hack */ a.navpopup.hasPopupMenu=false;
a.navpopup.setInnerHTML(popupHTML(a));
fillEmptySpans({navpopup:a.navpopup});
if (getValueOf('popupDraggable'))
{
var dragHandle = getValueOf('popupDragHandle') || null;
if (dragHandle && dragHandle != 'all') {
dragHandle += a.navpopup.idNumber;
}
setTimeout(function(){a.navpopup.makeDraggable(dragHandle);}, 150);
}
//<NOLITE>
if (getValueOf('popupRedlinkRemoval') && a.className=='new') {
setPopupHTML('<br>'+popupRedlinkHTML(article), 'popupRedlink', a.navpopup.idNumber);
}
//</NOLITE>
}
function debugData(navpopup) {
if(getValueOf('popupDebugging') && navpopup.idNumber) {
setPopupHTML('idNumber='+navpopup.idNumber + ', pending=' + navpopup.pending,
'popupError', navpopup.idNumber);
}
}
function newNavpopup(a, article) {
var navpopup = new Navpopup();
navpopup.fuzz=5;
navpopup.delay=getValueOf('popupDelay')*1000;
// increment global counter now
navpopup.idNumber = ++pg.idNumber;
navpopup.parentAnchor = a;
navpopup.parentPopup = (a.popData && a.popData.owner);
navpopup.article = article;
registerHooks(navpopup);
return navpopup;
}
// Should we show nonsimple context?
// If simplePopups is set to true, then we do not show nonsimple context,
// but if a bottom "show preview" was clicked we do show nonsimple context
function shouldShowNonSimple(a) {
return !getValueOf('simplePopups') || a.simpleNoMore;
}
// Should we show nonsimple context govern by the option (e.g. popupUserInfo)?
// If the user explicitly asked for nonsimple context by setting the option to true,
// then we show it even in nonsimple mode.
function shouldShow(a,option) {
if (shouldShowNonSimple(a)) {
return getValueOf(option);
} else {
return (typeof window[option] != 'undefined' ) && window[option];
}
}
function nonsimplePopupContent(a, article) {
var diff=null, history=null;
var params=parseParams(a.href);
var oldid=(typeof params.oldid=='undefined' ? null : params.oldid);
//<NOLITE>
if(shouldShow(a,'popupPreviewDiffs')) {
diff=params.diff;
}
if(shouldShow(a,'popupPreviewHistory')) {
history=(params.action=='history');
}
//</NOLITE>
a.navpopup.pending=0;
var referenceElement = footnoteTarget(a);
if (referenceElement) {
footnotePreview(referenceElement, a.navpopup);
//<NOLITE>
} else if ( diff || diff === 0 ) {
loadDiff(article, oldid, diff, a.navpopup);
} else if ( history ) {
loadAPIPreview('history', article, a.navpopup);
} else if ( shouldShowNonSimple(a) && pg.re.contribs.test(a.href) ) {
loadAPIPreview('contribs', article, a.navpopup);
} else if ( shouldShowNonSimple(a) && pg.re.backlinks.test(a.href) ) {
loadAPIPreview('backlinks', article, a.navpopup);
} else if ( // FIXME should be able to get all preview combinations with options
article.namespaceId()==pg.nsImageId &&
( shouldShow(a,'imagePopupsForImages') || ! anchorContainsImage(a) )
) {
loadAPIPreview('imagepagepreview', article, a.navpopup);
loadImage(article, a.navpopup);
//</NOLITE>
} else {
if (article.namespaceId() == pg.nsCategoryId &&
shouldShow(a,'popupCategoryMembers')) {
loadAPIPreview('category', article, a.navpopup);
} else if ((article.namespaceId() == pg.nsUserId || article.namespaceId() == pg.nsUsertalkId) &&
shouldShow(a,'popupUserInfo')) {
loadAPIPreview('userinfo', article, a.navpopup);
}
if (shouldShowNonSimple(a)) startArticlePreview(article, oldid, a.navpopup);
}
}
function pendingNavpopTask(navpop) {
if (navpop && navpop.pending === null) { navpop.pending=0; }
++navpop.pending;
debugData(navpop);
}
function completedNavpopTask(navpop) {
if (navpop && navpop.pending) { --navpop.pending; }
debugData(navpop);
}
function startArticlePreview(article, oldid, navpop) {
navpop.redir=0;
loadPreview(article, oldid, navpop);
}
function loadPreview(article, oldid, navpop) {
if (!navpop.redir) { navpop.originalArticle=article; }
article.oldid = oldid;
loadAPIPreview('revision', article, navpop);
}
function loadPreviewFromRedir(redirMatch, navpop) {
// redirMatch is a regex match
var target = new Title().fromWikiText(redirMatch[2]);
// overwrite (or add) anchor from original target
// mediawiki does overwrite; eg [[User:Lupin/foo3#Done]]
if ( navpop.article.anchor ) { target.anchor = navpop.article.anchor; }
navpop.redir++;
navpop.redirTarget=target;
//<NOLITE>
var warnRedir = redirLink(target, navpop.article);
setPopupHTML(warnRedir, 'popupWarnRedir', navpop.idNumber);
//</NOLITE>
navpop.article=target;
fillEmptySpans({redir: true, redirTarget: target, navpopup:navpop});
return loadPreview(target, null, navpop);
}
function insertPreview(download) {
if (!download.owner) { return; }
var redirMatch = pg.re.redirect.exec(download.data);
if (download.owner.redir === 0 && redirMatch) {
loadPreviewFromRedir(redirMatch, download.owner);
return;
}
if (download.owner.visible || !getValueOf('popupLazyPreviews')) {
insertPreviewNow(download);
} else {
var id=(download.owner.redir) ? 'PREVIEW_REDIR_HOOK' : 'PREVIEW_HOOK';
download.owner.addHook( function(){insertPreviewNow(download); return true;},
'unhide', 'after', id );
}
}
function insertPreviewNow(download) {
if (!download.owner) { return; }
var wikiText=download.data;
var navpop=download.owner;
var art=navpop.redirTarget || navpop.originalArticle;
//<NOLITE>
makeFixDabs(wikiText, navpop);
if (getValueOf('popupSummaryData')) {
getPageInfo(wikiText, download);
setPopupTrailer(getPageInfo(wikiText, download), navpop.idNumber);
}
var imagePage='';
if (art.namespaceId()==pg.nsImageId) { imagePage=art.toString(); }
else { imagePage=getValidImageFromWikiText(wikiText); }
if(imagePage) { loadImage(Title.fromWikiText(imagePage), navpop); }
//</NOLITE>
if (getValueOf('popupPreviews')) { insertArticlePreview(download, art, navpop); }
}
function insertArticlePreview(download, art, navpop) {
if (download && typeof download.data == typeof ''){
if (art.namespaceId()==pg.nsTemplateId && getValueOf('popupPreviewRawTemplates')) {
// FIXME compare/consolidate with diff escaping code for wikitext
var h='<hr /><span style="font-family: monospace;">' + download.data.entify().split('\\n').join('<br />\\n') + '</span>';
setPopupHTML(h, 'popupPreview', navpop.idNumber);
}
else {
var p=prepPreviewmaker(download.data, art, navpop);
p.showPreview();
}
}
}
function prepPreviewmaker(data, article, navpop) {
// deal with tricksy anchors
var d=anchorize(data, article.anchorString());
var urlBase=joinPath([pg.wiki.articlebase, article.urlString()]);
var p=new Previewmaker(d, urlBase, navpop);
return p;
}
// Try to imitate the way mediawiki generates HTML anchors from section titles
function anchorize(d, anch) {
if (!anch) { return d; }
var anchRe=RegExp('(?:=+\\s*' + literalizeRegex(anch).replace(/[_ ]/g, '[_ ]') + '\\s*=+|\\{\\{\\s*'+getValueOf('popupAnchorRegexp')+'\\s*(?:\\|[^|}]*)*?\\s*'+literalizeRegex(anch)+'\\s*(?:\\|[^}]*)?}})');
var match=d.match(anchRe);
if(match && match.length > 0 && match[0]) { return d.substring(d.indexOf(match[0])); }
// now try to deal with == foo [[bar|baz]] boom == -> #foo_baz_boom
var lines=d.split('\n');
for (var i=0; i<lines.length; ++i) {
lines[i]=lines[i].replace(RegExp('[[]{2}([^|\\]]*?[|])?(.*?)[\\]]{2}', 'g'), '$2')
.replace(/'''([^'])/g, '$1').replace(RegExp("''([^'])", 'g'), '$1');
if (lines[i].match(anchRe)) {
return d.split('\n').slice(i).join('\n').replace(RegExp('^[^=]*'), '');
}
}
return d;
}
function killPopup() {
removeModifierKeyHandler(this);
if (getValueOf('popupShortcutKeys')) { rmPopupShortcuts(); }
if (!pg) { return; }
if (pg.current.link && pg.current.link.navpopup) { pg.current.link.navpopup.banish(); }
pg.current.link=null;
abortAllDownloads();
if (pg.timer.checkPopupPosition) {
clearInterval(pg.timer.checkPopupPosition);
pg.timer.checkPopupPosition=null;
}
return true; // preserve default action
}
// ENDFILE: actions.js
// STARTFILE: domdrag.js
/**
@fileoverview
The {@link Drag} object, which enables objects to be dragged around.
<pre>
*************************************************
dom-drag.js
09.25.2001
www.youngpup.net
**************************************************
10.28.2001 - fixed minor bug where events
sometimes fired off the handle, not the root.
*************************************************
Pared down, some hooks added by [[User:Lupin]]
Copyright Aaron Boodman.
Saying stupid things daily since March 2001.
</pre>
*/
/**
Creates a new Drag object. This is used to make various DOM elements draggable.
@constructor
*/
function Drag () {
/**
Condition to determine whether or not to drag. This function should take one parameter, an Event.
To disable this, set it to <code>null</code>.
@type Function
*/
this.startCondition = null;
/**
Hook to be run when the drag finishes. This is passed the final coordinates of
the dragged object (two integers, x and y). To disables this, set it to <code>null</code>.
@type Function
*/
this.endHook = null;
}
/**
Gets an event in a cross-browser manner.
@param {Event} e
@private
*/
Drag.prototype.fixE = function(e) {
if (typeof e == 'undefined') { e = window.event; }
if (typeof e.layerX == 'undefined') { e.layerX = e.offsetX; }
if (typeof e.layerY == 'undefined') { e.layerY = e.offsetY; }
return e;
};
/**
Initialises the Drag instance by telling it which object you want to be draggable, and what you want to drag it by.
@param {DOMElement} o The "handle" by which <code>oRoot</code> is dragged.
@param {DOMElement} oRoot The object which moves when <code>o</code> is dragged, or <code>o</code> if omitted.
*/
Drag.prototype.init = function(o, oRoot) {
var dragObj = this;
this.obj = o;
o.onmousedown = function(e) { dragObj.start.apply( dragObj, [e]); };
o.dragging = false;
o.popups_draggable = true;
o.hmode = true;
o.vmode = true;
o.root = oRoot ? oRoot : o ;
if (isNaN(parseInt(o.root.style.left, 10))) { o.root.style.left = "0px"; }
if (isNaN(parseInt(o.root.style.top, 10))) { o.root.style.top = "0px"; }
o.root.onthisStart = function(){};
o.root.onthisEnd = function(){};
o.root.onthis = function(){};
};
/**
Starts the drag.
@private
@param {Event} e
*/
Drag.prototype.start = function(e) {
var o = this.obj; // = this;
e = this.fixE(e);
if (this.startCondition && !this.startCondition(e)) { return; }
var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom, 10);
var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right, 10);
o.root.onthisStart(x, y);
o.lastMouseX = e.clientX;
o.lastMouseY = e.clientY;
var dragObj = this;
o.onmousemoveDefault = document.onmousemove;
o.dragging = true;
document.onmousemove = function(e) { dragObj.drag.apply( dragObj, [e] ); };
document.onmouseup = function(e) { dragObj.end.apply( dragObj, [e] ); };
return false;
};
/**
Does the drag.
@param {Event} e
@private
*/
Drag.prototype.drag = function(e) {
e = this.fixE(e);
var o = this.obj;
var ey = e.clientY;
var ex = e.clientX;
var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom, 10);
var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right, 10 );
var nx, ny;
nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1));
ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1));
this.obj.root.style[o.hmode ? "left" : "right"] = nx + "px";
this.obj.root.style[o.vmode ? "top" : "bottom"] = ny + "px";
this.obj.lastMouseX = ex;
this.obj.lastMouseY = ey;
this.obj.root.onthis(nx, ny);
return false;
};
/**
Ends the drag.
@private
*/
Drag.prototype.end = function() {
document.onmousemove=this.obj.onmousemoveDefault;
document.onmouseup = null;
this.obj.dragging = false;
if (this.endHook) {
this.endHook( parseInt(this.obj.root.style[this.obj.hmode ? "left" : "right"], 10),
parseInt(this.obj.root.style[this.obj.vmode ? "top" : "bottom"], 10));
}
};
// ENDFILE: domdrag.js
// STARTFILE: structures.js
//<NOLITE>
pg.structures.original={};
pg.structures.original.popupLayout=function () {
return ['popupError', 'popupImage', 'popupTopLinks', 'popupTitle',
'popupUserData', 'popupData', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks',
'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
pg.structures.original.popupRedirSpans=function () {
return ['popupRedir', 'popupWarnRedir', 'popupRedirTopLinks',
'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'];
};
pg.structures.original.popupTitle=function (x) {
log ('defaultstructure.popupTitle');
if (!getValueOf('popupNavLinks')) {
return navlinkStringToHTML('<b><<mainlink>></b>',x.article,x.params);
}
return '';
};
pg.structures.original.popupTopLinks=function (x) {
log ('defaultstructure.popupTopLinks');
if (getValueOf('popupNavLinks')) { return navLinksHTML(x.article, x.hint, x.params); }
return '';
};
pg.structures.original.popupImage=function(x) {
log ('original.popupImage, x.article='+x.article+', x.navpop.idNumber='+x.navpop.idNumber);
return imageHTML(x.article, x.navpop.idNumber);
};
pg.structures.original.popupRedirTitle=pg.structures.original.popupTitle;
pg.structures.original.popupRedirTopLinks=pg.structures.original.popupTopLinks;
function copyStructure(oldStructure, newStructure) {
pg.structures[newStructure]={};
for (var prop in pg.structures[oldStructure]) {
pg.structures[newStructure][prop]=pg.structures[oldStructure][prop];
}
}
copyStructure('original', 'nostalgia');
pg.structures.nostalgia.popupTopLinks=function(x) {
var str='';
str += '<b><<mainlink|shortcut= >></b>';
// user links
// contribs - log - count - email - block
// count only if applicable; block only if popupAdminLinks
str += 'if(user){<br><<contribs|shortcut=c>>';
str+='if(wikimedia){*<<count|shortcut=#>>}';
str+='if(ipuser){}else{*<<email|shortcut=E>>}if(admin){*<<block|shortcut=b>>}}';
// editing links
// talkpage -> edit|new - history - un|watch - article|edit
// other page -> edit - history - un|watch - talk|edit|new
var editstr='<<edit|shortcut=e>>';
var editOldidStr='if(oldid){<<editOld|shortcut=e>>|<<revert|shortcut=v|rv>>|<<edit|cur>>}else{' +
editstr + '}';
var historystr='<<history|shortcut=h>>';
var watchstr='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
str += '<br>if(talk){' +
editOldidStr+'|<<new|shortcut=+>>' + '*' + historystr+'*'+watchstr + '*' +
'<b><<article|shortcut=a>></b>|<<editArticle|edit>>' +
'}else{' + // not a talk page
editOldidStr + '*' + historystr + '*' + watchstr + '*' +
'<b><<talk|shortcut=t>></b>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>}';
// misc links
str += '<br><<whatLinksHere|shortcut=l>>*<<relatedChanges|shortcut=r>>';
str += 'if(admin){<br>}else{*}<<move|shortcut=m>>';
// admin links
str += 'if(admin){*<<unprotect|unprotectShort>>|<<protect|shortcut=p>>*' +
'<<undelete|undeleteShort>>|<<delete|shortcut=d>>}';
return navlinkStringToHTML(str, x.article, x.params);
};
pg.structures.nostalgia.popupRedirTopLinks=pg.structures.nostalgia.popupTopLinks;
/** -- fancy -- **/
copyStructure('original', 'fancy');
pg.structures.fancy.popupTitle=function (x) {
return navlinkStringToHTML('<font size=+0><<mainlink>></font>',x.article,x.params);
};
pg.structures.fancy.popupTopLinks=function(x) {
var hist='<<history|shortcut=h|hist>>|<<lastEdit|shortcut=/|last>>|<<editors|shortcut=E|eds>>';
var watch='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
var move='<<move|shortcut=m|move>>';
return navlinkStringToHTML('if(talk){' +
'<<edit|shortcut=e>>|<<new|shortcut=+|+>>*' + hist + '*' +
'<<article|shortcut=a>>|<<editArticle|edit>>' + '*' + watch + '*' + move +
'}else{<<edit|shortcut=e>>*' + hist +
'*<<talk|shortcut=t|>>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>' +
'*' + watch + '*' + move+'}<br>', x.article, x.params);
};
pg.structures.fancy.popupOtherLinks=function(x) {
var admin='<<unprotect|unprotectShort>>|<<protect|shortcut=p>>*<<undelete|undeleteShort>>|<<delete|shortcut=d|del>>';
var user='<<contribs|shortcut=c>>if(wikimedia){|<<count|shortcut=#|#>>}';
user+='if(ipuser){|<<arin>>}else{*<<email|shortcut=E|'+
popupString('email')+'>>}if(admin){*<<block|shortcut=b>>}';
var normal='<<whatLinksHere|shortcut=l|links here>>*<<relatedChanges|shortcut=r|related>>';
return navlinkStringToHTML('<br>if(user){' + user + '*}if(admin){'+admin+'if(user){<br>}else{*}}' + normal,
x.article, x.params);
};
pg.structures.fancy.popupRedirTitle=pg.structures.fancy.popupTitle;
pg.structures.fancy.popupRedirTopLinks=pg.structures.fancy.popupTopLinks;
pg.structures.fancy.popupRedirOtherLinks=pg.structures.fancy.popupOtherLinks;
/** -- fancy2 -- **/
// hack for [[User:MacGyverMagic]]
copyStructure('fancy', 'fancy2');
pg.structures.fancy2.popupTopLinks=function(x) { // hack out the <br> at the end and put one at the beginning
return '<br>'+pg.structures.fancy.popupTopLinks(x).replace(RegExp('<br>$','i'),'');
};
pg.structures.fancy2.popupLayout=function () { // move toplinks to after the title
return ['popupError', 'popupImage', 'popupTitle', 'popupUserData', 'popupData', 'popupTopLinks', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks', 'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
/** -- menus -- **/
copyStructure('original', 'menus');
pg.structures.menus.popupLayout=function () {
return ['popupError', 'popupImage', 'popupTopLinks', 'popupTitle', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks', 'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupUserData', 'popupData', 'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
pg.structures.menus.popupTopLinks = function (x, shorter) {
// FIXME maybe this stuff should be cached
var s=[];
var dropdiv='<div class="popup_drop">';
var enddiv='</div>';
var hist='<<history|shortcut=h>>';
if (!shorter) { hist = '<menurow>' + hist +
'|<<historyfeed|rss>>|<<editors|shortcut=E>></menurow>'; }
var lastedit='<<lastEdit|shortcut=/|show last edit>>';
var thank='if(diff){<<thank|send thanks>>}';
var jsHistory='<<lastContrib|last set of edits>><<sinceMe|changes since mine>>';
var linkshere='<<whatLinksHere|shortcut=l|what links here>>';
var related='<<relatedChanges|shortcut=r|related changes>>';
var search='<menurow><<search|shortcut=s>>if(wikimedia){|<<globalsearch|shortcut=g|global>>}' +
'|<<google|shortcut=G|web>></menurow>';
var watch='<menurow><<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>></menurow>';
var protect='<menurow><<unprotect|unprotectShort>>|' +
'<<protect|shortcut=p>>|<<protectlog|log>></menurow>';
var del='<menurow><<undelete|undeleteShort>>|<<delete|shortcut=d>>|' +
'<<deletelog|log>></menurow>';
var move='<<move|shortcut=m|move page>>';
var nullPurge='<menurow><<nullEdit|shortcut=n|null edit>>|<<purge|shortcut=P>></menurow>';
var viewOptions='<menurow><<view|shortcut=v>>|<<render|shortcut=S>>|<<raw>></menurow>';
var editRow='if(oldid){' +
'<menurow><<edit|shortcut=e>>|<<editOld|shortcut=e|this revision>></menurow>' +
'<menurow><<revert|shortcut=v>>|<<undo>></menurow>' + '}else{<<edit|shortcut=e>>}';
var markPatrolled='if(rcid){<<markpatrolled|mark patrolled>>}';
var newTopic='if(talk){<<new|shortcut=+|new topic>>}';
var protectDelete='if(admin){' + protect + del + '}';
if (getValueOf('popupActionsMenu')) {
s.push( '<<mainlink>>*' + dropdiv + menuTitle('actions'));
} else {
s.push( dropdiv + '<<mainlink>>');
}
s.push( '<menu>');
s.push( editRow + markPatrolled + newTopic + hist + lastedit + thank );
if (!shorter) { s.push(jsHistory); }
s.push( move + linkshere + related);
if (!shorter) { s.push(nullPurge + search); }
if (!shorter) { s.push(viewOptions); }
s.push('<hr />' + watch + protectDelete);
s.push('<hr />' +
'if(talk){<<article|shortcut=a|view article>><<editArticle|edit article>>}' +
'else{<<talk|shortcut=t|talk page>><<editTalk|edit talk>>' +
'<<newTalk|shortcut=+|new topic>>}</menu>' + enddiv);
// user menu starts here
var email='<<email|shortcut=E|email user>>';
var contribs= 'if(wikimedia){<menurow>}<<contribs|shortcut=c|contributions>>if(wikimedia){</menurow>}' +
'if(admin){<menurow><<deletedContribs>></menurow>}';
s.push('if(user){*' + dropdiv + menuTitle('user'));
s.push('<menu>');
s.push('<menurow><<userPage|shortcut=u|user page>>|<<userSpace|space>></menurow>');
s.push('<<userTalk|shortcut=t|user talk>><<editUserTalk|edit user talk>>' +
'<<newUserTalk|shortcut=+|leave comment>>');
if(!shorter) { s.push( 'if(ipuser){<<arin>>}else{' + email + '}' ); }
else { s.push( 'if(ipuser){}else{' + email + '}' ); }
s.push('<hr />' + contribs + '<<userlog|shortcut=L|user log>>');
s.push('if(wikimedia){<<count|shortcut=#|edit counter>>}');
s.push('if(admin){<menurow><<unblock|unblockShort>>|<<block|shortcut=b|block user>></menurow>}');
s.push('<<blocklog|shortcut=B|block log>>');
s.push('</menu>' + enddiv + '}');
// popups menu starts here
if (getValueOf('popupSetupMenu') && !x.navpop.hasPopupMenu /* FIXME: hack */) {
x.navpop.hasPopupMenu=true;
s.push('*' + dropdiv + menuTitle('popupsMenu') + '<menu>');
s.push('<<togglePreviews|toggle previews>>');
s.push('<<purgePopups|reset>>');
s.push('<<disablePopups|disable>>');
s.push('</menu>'+enddiv);
}
return navlinkStringToHTML(s.join(''), x.article, x.params);
};
function menuTitle(s) {
return '<a href="#" noPopup=1>' + popupString(s) + '</a>';
}
pg.structures.menus.popupRedirTitle=pg.structures.menus.popupTitle;
pg.structures.menus.popupRedirTopLinks=pg.structures.menus.popupTopLinks;
copyStructure('menus', 'shortmenus');
pg.structures.shortmenus.popupTopLinks=function(x) {
return pg.structures.menus.popupTopLinks(x,true);
};
pg.structures.shortmenus.popupRedirTopLinks=pg.structures.shortmenus.popupTopLinks;
//</NOLITE>
pg.structures.lite={};
pg.structures.lite.popupLayout=function () {
return ['popupTitle', 'popupPreview' ];
};
pg.structures.lite.popupTitle=function (x) {
log (x.article + ': structures.lite.popupTitle');
//return navlinkStringToHTML('<b><<mainlink>></b>',x.article,x.params);
return '<div><span class="popup_mainlink"><b>' + x.article.toString() + '</b></span></div>';
};
// ENDFILE: structures.js
// STARTFILE: autoedit.js
//<NOLITE>
function substitute(data,cmdBody) {
// alert('sub\nfrom: '+cmdBody.from+'\nto: '+cmdBody.to+'\nflags: '+cmdBody.flags);
var fromRe=RegExp(cmdBody.from, cmdBody.flags);
return data.replace(fromRe, cmdBody.to);
}
function execCmds(data, cmdList) {
for (var i=0; i<cmdList.length; ++i) {
data=cmdList[i].action(data, cmdList[i]);
}
return data;
}
function parseCmd(str) {
// returns a list of commands
if (!str.length) { return []; }
var p=false;
switch (str.charAt(0)) {
case 's':
p=parseSubstitute(str);
break;
default:
return false;
}
if (p) { return [p].concat(parseCmd(p.remainder)); }
return false;
}
function unEscape(str, sep) {
return str.split('\\\\').join('\\').split('\\'+sep).join(sep).split('\\n').join('\n');
}
function parseSubstitute(str) {
// takes a string like s/a/b/flags;othercmds and parses it
var from,to,flags,tmp;
if (str.length<4) { return false; }
var sep=str.charAt(1);
str=str.substring(2);
tmp=skipOver(str,sep);
if (tmp) { from=tmp.segment; str=tmp.remainder; }
else { return false; }
tmp=skipOver(str,sep);
if (tmp) { to=tmp.segment; str=tmp.remainder; }
else { return false; }
flags='';
if (str.length) {
tmp=skipOver(str,';') || skipToEnd(str, ';');
if (tmp) {flags=tmp.segment; str=tmp.remainder; }
}
return {action: substitute, from: from, to: to, flags: flags, remainder: str};
}
function skipOver(str,sep) {
var endSegment=findNext(str,sep);
if (endSegment<0) { return false; }
var segment=unEscape(str.substring(0,endSegment), sep);
return {segment: segment, remainder: str.substring(endSegment+1)};
}
/*eslint-disable*/
function skipToEnd(str,sep) {
return {segment: str, remainder: ''};
}
/*eslint-enable */
function findNext(str, ch) {
for (var i=0; i<str.length; ++i) {
if (str.charAt(i)=='\\') { i+=2; }
if (str.charAt(i)==ch) { return i; }
}
return -1;
}
function setCheckbox(param, box) {
var val=mw.util.getParamValue(param);
if (val) {
switch (val) {
case '1': case 'yes': case 'true':
box.checked=true;
break;
case '0': case 'no': case 'false':
box.checked=false;
}
}
}
function autoEdit() {
setupPopups( function () {
if (mw.util.getParamValue('autoimpl') !== popupString('autoedit_version') ) { return false; }
if (mw.util.getParamValue('autowatchlist') && mw.util.getParamValue('actoken')===autoClickToken()) {
pg.fn.modifyWatchlist(mw.util.getParamValue('title'), mw.util.getParamValue('action'));
}
if (!document.editform) { return false; }
if (autoEdit.alreadyRan) { return false; }
autoEdit.alreadyRan=true;
var cmdString=mw.util.getParamValue('autoedit');
if (cmdString) {
try {
var editbox=document.editform.wpTextbox1;
var cmdList=parseCmd(cmdString);
var input=editbox.value;
var output=execCmds(input, cmdList);
editbox.value=output;
} catch (dang) { return; }
// wikEd user script compatibility
if (typeof(wikEdUseWikEd) != 'undefined') {
if (wikEdUseWikEd === true) {
WikEdUpdateFrame();
}
}
}
setCheckbox('autominor', document.editform.wpMinoredit);
setCheckbox('autowatch', document.editform.wpWatchthis);
var rvid = mw.util.getParamValue('autorv');
if (rvid) {
var url=pg.wiki.apiwikibase + '?action=query&format=json&formatversion=2&prop=revisions&revids='+rvid;
startDownload(url, null, autoEdit2);
} else { autoEdit2(); }
} );
}
function autoEdit2(d) {
var summary=mw.util.getParamValue('autosummary');
var summaryprompt=mw.util.getParamValue('autosummaryprompt');
var summarynotice='';
if (d && d.data && mw.util.getParamValue('autorv')) {
var s = getRvSummary(summary, d.data);
if (s === false) {
summaryprompt=true;
summarynotice=popupString('Failed to get revision information, please edit manually.\n\n');
summary = simplePrintf(summary, [mw.util.getParamValue('autorv'), '(unknown)', '(unknown)']);
} else { summary = s; }
}
if (summaryprompt) {
var txt= summarynotice +
popupString('Enter a non-empty edit summary or press cancel to abort');
var response=prompt(txt, summary);
if (response) { summary=response; }
else { return; }
}
if (summary) { document.editform.wpSummary.value=summary; }
// Attempt to avoid possible premature clicking of the save button
// (maybe delays in updates to the DOM are to blame?? or a red herring)
setTimeout(autoEdit3, 100);
}
function autoClickToken() {
return mw.user.sessionId();
}
function autoEdit3() {
if( mw.util.getParamValue('actoken') != autoClickToken()) { return; }
var btn=mw.util.getParamValue('autoclick');
if (btn) {
if (document.editform && document.editform[btn]) {
var button=document.editform[btn];
var msg=tprintf('The %s button has been automatically clicked. Please wait for the next page to load.',
[ button.value ]);
bannerMessage(msg);
document.title='('+document.title+')';
button.click();
} else {
alert(tprintf('Could not find button %s. Please check the settings in your javascript file.',
[ btn ]));
}
}
}
function bannerMessage(s) {
var headings=document.getElementsByTagName('h1');
if (headings) {
var div=document.createElement('div');
div.innerHTML='<font size=+1><b>' + s + '</b></font>';
headings[0].parentNode.insertBefore(div, headings[0]);
}
}
function getRvSummary(template, json) {
try {
var o=getJsObj(json);
var edit = anyChild(o.query.pages).revisions[0];
var timestamp = edit.timestamp.split(/[A-Z]/g).join(' ').replace(/^ *| *$/g, '');
return simplePrintf(template, [edit.revid, timestamp, edit.userhidden ? '(hidden)' : edit.user ]);
} catch (badness) {
return false;
}
}
//</NOLITE>
// ENDFILE: autoedit.js
// STARTFILE: downloader.js
/**
@fileoverview
{@link Downloader}, a xmlhttprequest wrapper, and helper functions.
*/
/**
Creates a new Downloader
@constructor
@class The Downloader class. Create a new instance of this class to download stuff.
@param {String} url The url to download. This can be omitted and supplied later.
*/
function Downloader(url) {
if (typeof XMLHttpRequest!='undefined') { this.http = new XMLHttpRequest(); }
/**
The url to download
@type String
*/
this.url = url;
/**
A universally unique ID number
@type integer
*/
this.id=null;
/**
Modification date, to be culled from the incoming headers
@type Date
@private
*/
this.lastModified = null;
/**
What to do when the download completes successfully
@type Function
@private
*/
this.callbackFunction = null;
/**
What to do on failure
@type Function
@private
*/
this.onFailure = null;
/**
Flag set on <code>abort</code>
@type boolean
*/
this.aborted = false;
/**
HTTP method. See https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html for possibilities.
@type String
*/
this.method='GET';
/**
Async flag.
@type boolean
*/
this.async=true;
}
new Downloader();
/** Submits the http request. */
Downloader.prototype.send = function (x) {
if (!this.http) { return null; }
return this.http.send(x);
};
/** Aborts the download, setting the <code>aborted</code> field to true. */
Downloader.prototype.abort = function () {
if (!this.http) { return null; }
this.aborted=true;
return this.http.abort();
};
/** Returns the downloaded data. */
Downloader.prototype.getData = function () {if (!this.http) { return null; } return this.http.responseText;};
/** Prepares the download. */
Downloader.prototype.setTarget = function () {
if (!this.http) { return null; }
this.http.open(this.method, this.url, this.async);
this.http.setRequestHeader( 'Api-User-Agent', pg.misc.userAgent );
};
/** Gets the state of the download. */
Downloader.prototype.getReadyState=function () {if (!this.http) { return null; } return this.http.readyState;};
pg.misc.downloadsInProgress = { };
/** Starts the download.
Note that setTarget {@link Downloader#setTarget} must be run first
*/
Downloader.prototype.start=function () {
if (!this.http) { return; }
pg.misc.downloadsInProgress[this.id] = this;
this.http.send(null);
};
/** Gets the 'Last-Modified' date from the download headers.
Should be run after the download completes.
Returns <code>null</code> on failure.
@return {Date}
*/
Downloader.prototype.getLastModifiedDate=function () {
if(!this.http) { return null; }
var lastmod=null;
try {
lastmod=this.http.getResponseHeader('Last-Modified');
} catch (err) {}
if (lastmod) { return new Date(lastmod); }
return null;
};
/** Sets the callback function.
@param {Function} f callback function, called as <code>f(this)</code> on success
*/
Downloader.prototype.setCallback = function (f) {
if(!this.http) { return; }
this.http.onreadystatechange = f;
};
Downloader.prototype.getStatus = function() { if (!this.http) { return null; } return this.http.status; };
//////////////////////////////////////////////////
// helper functions
/** Creates a new {@link Downloader} and prepares it for action.
@param {String} url The url to download
@param {integer} id The ID of the {@link Downloader} object
@param {Function} callback The callback function invoked on success
@return {String/Downloader} the {@link Downloader} object created, or 'ohdear' if an unsupported browser
*/
function newDownload(url, id, callback, onfailure) {
var d=new Downloader(url);
if (!d.http) { return 'ohdear'; }
d.id=id;
d.setTarget();
if (!onfailure) {
onfailure=2;
}
var f = function () {
if (d.getReadyState() == 4) {
delete pg.misc.downloadsInProgress[this.id];
try {
if ( d.getStatus() == 200 ) {
d.data=d.getData();
d.lastModified=d.getLastModifiedDate();
callback(d);
} else if (typeof onfailure == typeof 1) {
if (onfailure > 0) {
// retry
newDownload(url, id, callback, onfailure - 1);
}
} else if (typeof onfailure === 'function') {
onfailure(d,url,id,callback);
}
} catch (somerr) { /* ignore it */ }
}
};
d.setCallback(f);
return d;
}
/** Simulates a download from cached data.
The supplied data is put into a {@link Downloader} as if it had downloaded it.
@param {String} url The url.
@param {integer} id The ID.
@param {Function} callback The callback, which is invoked immediately as <code>callback(d)</code>,
where <code>d</code> is the new {@link Downloader}.
@param {String} data The (cached) data.
@param {Date} lastModified The (cached) last modified date.
*/
function fakeDownload(url, id, callback, data, lastModified, owner) {
var d=newDownload(url,callback);
d.owner=owner;
d.id=id; d.data=data;
d.lastModified=lastModified;
return callback(d);
}
/**
Starts a download.
@param {String} url The url to download
@param {integer} id The ID of the {@link Downloader} object
@param {Function} callback The callback function invoked on success
@return {String/Downloader} the {@link Downloader} object created, or 'ohdear' if an unsupported browser
*/
function startDownload(url, id, callback) {
var d=newDownload(url, id, callback);
if (typeof d == typeof '' ) { return d; }
d.start();
return d;
}
/**
Aborts all downloads which have been started.
*/
function abortAllDownloads() {
for ( var x in pg.misc.downloadsInProgress ) {
try {
pg.misc.downloadsInProgress[x].aborted=true;
pg.misc.downloadsInProgress[x].abort();
delete pg.misc.downloadsInProgress[x];
} catch (e) {}
}
}
// ENDFILE: downloader.js
// STARTFILE: livepreview.js
// TODO: location is often not correct (eg relative links in previews)
// NOTE: removed md5 and image and math parsing. was broken, lots of bytes.
/**
* InstaView - a Mediawiki to HTML converter in JavaScript
* Version 0.6.1
* Copyright (C) Pedro Fayolle 2005-2006
* https://en.wikipedia.org/wiki/User:Pilaf
* Distributed under the BSD license
*
* Changelog:
*
* 0.6.1
* - Fixed problem caused by \r characters
* - Improved inline formatting parser
*
* 0.6
* - Changed name to InstaView
* - Some major code reorganizations and factored out some common functions
* - Handled conversion of relative links (i.e. [[/foo]])
* - Fixed misrendering of adjacent definition list items
* - Fixed bug in table headings handling
* - Changed date format in signatures to reflect Mediawiki's
* - Fixed handling of [[:Image:...]]
* - Updated MD5 function (hopefully it will work with UTF-8)
* - Fixed bug in handling of links inside images
*
* To do:
* - Better support for math tags
* - Full support for <nowiki>
* - Parser-based (as opposed to RegExp-based) inline wikicode handling (make it one-pass and bullet-proof)
* - Support for templates (through AJAX)
* - Support for coloured links (AJAX)
*/
var Insta = {};
function setupLivePreview() {
// options
Insta.conf =
{
baseUrl: '',
user: {},
wiki: {
lang: pg.wiki.lang,
interwiki: pg.wiki.interwiki,
default_thumb_width: 180
},
paths: {
articles: pg.wiki.articlePath + '/',
// Only used for Insta previews with images. (not in popups)
math: '/math/',
images: '//upload.wikimedia.org/wikipedia/en/', // FIXME getImageUrlStart(pg.wiki.hostname),
images_fallback: '//upload.wikimedia.org/wikipedia/commons/',
},
locale: {
user: mw.config.get('wgFormattedNamespaces')[pg.nsUserId],
image: mw.config.get('wgFormattedNamespaces')[pg.nsImageId],
category: mw.config.get('wgFormattedNamespaces')[pg.nsCategoryId],
// shouldn't be used in popup previews, i think
months: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
}
};
// options with default values or backreferences
Insta.conf.user.name = Insta.conf.user.name || 'Wikipedian';
Insta.conf.user.signature = '[['+Insta.conf.locale.user+':'+Insta.conf.user.name+'|'+Insta.conf.user.name+']]';
//Insta.conf.paths.images = '//upload.wikimedia.org/wikipedia/' + Insta.conf.wiki.lang + '/';
// define constants
Insta.BLOCK_IMAGE = new RegExp('^\\[\\[(?:File|Image|'+Insta.conf.locale.image+
'):.*?\\|.*?(?:frame|thumbnail|thumb|none|right|left|center)', 'i');
}
Insta.dump = function(from, to)
{
if (typeof from == 'string') { from = document.getElementById(from); }
if (typeof to == 'string') { to = document.getElementById(to); }
to.innerHTML = this.convert(from.value);
};
Insta.convert = function(wiki)
{
var ll = (typeof wiki == 'string')? wiki.replace(/\r/g,'').split(/\n/): wiki, // lines of wikicode
o = '', // output
p = 0, // para flag
$r; // result of passing a regexp to $()
// some shorthands
function remain() { return ll.length; }
function sh() { return ll.shift(); } // shift
function ps(s) { o += s; } // push
// similar to C's printf, uses ? as placeholders, ?? to escape question marks
function f()
{
var i=1, a=arguments, f=a[0], o='', c, p;
for (; i<a.length; i++) {
if ((p=f.indexOf('?'))+1) {
// allow character escaping
i -= c = f.charAt(p+1)=='?' ? 1 : 0;
o += f.substring(0,p) + (c ? '?' : a[i]);
f = f.substr(p+1+c);
} else { break; }
}
return o+f;
}
function html_entities(s) {
return s.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
}
// Wiki text parsing to html is a nightmare.
// The below functions deliberately don't escape the ampersand since this would make it more difficult,
// and we don't absolutely need to for how we need it.
// This means that any unescaped ampersands in wikitext will remain unescaped and can cause invalid HTML.
// Browsers should all be able to handle it though.
// We also escape significant wikimarkup characters to prevent further matching on the processed text
function htmlescape_text(s) {
return s.replace(/</g,"<").replace(/>/g,">").replace(/:/g,":").replace(/\[/g,"[").replace(/]/g,"]");
}
function htmlescape_attr(s) {
return htmlescape_text(s).replace(/'/g,"'").replace(/"/g,""");
}
// return the first non matching character position between two strings
function str_imatch(a, b)
{
for (var i=0, l=Math.min(a.length, b.length); i<l; i++) {
if (a.charAt(i)!=b.charAt(i)) { break; }
}
return i;
}
// compare current line against a string or regexp
// if passed a string it will compare only the first string.length characters
// if passed a regexp the result is stored in $r
function $(c) { return (typeof c == 'string') ? (ll[0].substr(0,c.length)==c) : ($r = ll[0].match(c)); }
function $$(c) { return ll[0]==c; } // compare current line against a string
function _(p) { return ll[0].charAt(p); } // return char at pos p
function endl(s) { ps(s); sh(); }
function parse_list()
{
var prev='';
while (remain() && $(/^([*#:;]+)(.*)$/)) {
var l_match = $r;
sh();
var ipos = str_imatch(prev, l_match[1]);
// close uncontinued lists
for (var prevPos=prev.length-1; prevPos >= ipos; prevPos--) {
var pi = prev.charAt(prevPos);
if (pi=='*') { ps('</ul>'); }
else if (pi=='#') { ps('</ol>'); }
// close a dl only if the new item is not a dl item (:, ; or empty)
else if($.inArray(l_match[1].charAt(prevPos), ['','*','#'])) { ps('</dl>'); }
}
// open new lists
for (var matchPos=ipos; matchPos<l_match[1].length; matchPos++) {
var li = l_match[1].charAt(matchPos);
if (li=='*') { ps('<ul>'); }
else if (li=='#') { ps('<ol>'); }
// open a new dl only if the prev item is not a dl item (:, ; or empty)
else if ($.inArray(prev.charAt(matchPos), ['','*','#'])) { ps('<dl>'); }
}
switch (l_match[1].charAt(l_match[1].length-1)) {
case '*': case '#':
ps('<li>' + parse_inline_nowiki(l_match[2]));
break;
case ';':
ps('<dt>');
var dt_match = l_match[2].match(/(.*?)(:.*?)$/);
// handle ;dt :dd format
if (dt_match) {
ps(parse_inline_nowiki(dt_match[1]));
ll.unshift(dt_match[2]);
} else ps(parse_inline_nowiki(l_match[2]));
break;
case ':':
ps('<dd>' + parse_inline_nowiki(l_match[2]));
}
prev=l_match[1];
}
// close remaining lists
for (var i=prev.length-1; i>=0; i--) {
ps(f('</?>', (prev.charAt(i)=='*')? 'ul': ((prev.charAt(i)=='#')? 'ol': 'dl')));
}
}
function parse_table()
{
endl(f('<table>', $(/^\{\|( .*)$/)? $r[1]: ''));
for (;remain();) if ($('|')) switch (_(1)) {
case '}':
endl('</table>');
return;
case '-':
endl(f('<tr>', $(/\|-*(.*)/)[1]));
break;
default:
parse_table_data();
}
else if ($('!')) { parse_table_data(); }
else { sh(); }
}
function parse_table_data()
{
var td_line, match_i;
// 1: "|+", '|' or '+'
// 2: ??
// 3: attributes ??
// TODO: finish commenting this regexp
var td_match = sh().match(/^(\|\+|\||!)((?:([^[|]*?)\|(?!\|))?(.*))$/);
if (td_match[1] == '|+') ps('<caption');
else ps('<t' + ((td_match[1]=='|')?'d':'h'));
if (typeof td_match[3] != 'undefined') {
//ps(' ' + td_match[3])
match_i = 4;
} else match_i = 2;
ps('>');
if (td_match[1] != '|+') {
// use || or !! as a cell separator depending on context
// NOTE: when split() is passed a regexp make sure to use non-capturing brackets
td_line = td_match[match_i].split((td_match[1] == '|')? '||': /(?:\|\||!!)/);
ps(parse_inline_nowiki(td_line.shift()));
while (td_line.length) ll.unshift(td_match[1] + td_line.pop());
} else ps(td_match[match_i]);
var tc = 0, td = [];
while (remain()) {
td.push(sh());
if ($('|')) {
if (!tc) break; // we're at the outer-most level (no nested tables), skip to td parse
else if (_(1)=='}') tc--;
}
else if (!tc && $('!')) break;
else if ($('{|')) tc++;
}
if (td.length) ps(Insta.convert(td));
}
function parse_pre()
{
ps('<pre>');
do {
endl(parse_inline_nowiki(ll[0].substring(1)) + "\n");
} while (remain() && $(' '));
ps('</pre>');
}
function parse_block_image()
{
ps(parse_image(sh()));
}
function parse_image(str)
{
//<NOLITE>
// get what's in between "[[Image:" and "]]"
var tag = str.substring(str.indexOf(':') + 1, str.length - 2);
/* eslint-disable no-unused-vars */
var width;
var attr = [], filename, caption = '';
var thumb=0, frame=0, center=0;
var align='';
/* eslint-enable no-unused-vars */
if (tag.match(/\|/)) {
// manage nested links
var nesting = 0;
var last_attr;
for (var i = tag.length-1; i > 0; i--) {
if (tag.charAt(i) == '|' && !nesting) {
last_attr = tag.substr(i+1);
tag = tag.substring(0, i);
break;
} else switch (tag.substr(i-1, 2)) {
case ']]':
nesting++;
i--;
break;
case '[[':
nesting--;
i--;
}
}
attr = tag.split(/\s*\|\s*/);
attr.push(last_attr);
filename = attr.shift();
var w_match;
for (;attr.length; attr.shift()) {
w_match = attr[0].match(/^(\d*)(?:[px]*\d*)?px$/);
if (w_match) width = w_match[1];
else switch(attr[0]) {
case 'thumb':
case 'thumbnail':
thumb=true;
frame=true;
break;
case 'frame':
frame=true;
break;
case 'none':
case 'right':
case 'left':
center=false;
align=attr[0];
break;
case 'center':
center=true;
align='none';
break;
default:
if (attr.length == 1) caption = attr[0];
}
}
} else filename = tag;
return '';
//</NOLITE>
}
function parse_inline_nowiki(str)
{
var start, lastend=0;
var substart=0, nestlev=0, open, close, subloop;
var html='';
while (-1 != (start = str.indexOf('<nowiki>', substart))) {
html += parse_inline_wiki(str.substring(lastend, start));
start += 8;
substart = start;
subloop = true;
do {
open = str.indexOf('<nowiki>', substart);
close = str.indexOf('</nowiki>', substart);
if (close<=open || open==-1) {
if (close==-1) {
return html + html_entities(str.substr(start));
}
substart = close+9;
if (nestlev) {
nestlev--;
} else {
lastend = substart;
html += html_entities(str.substring(start, lastend-9));
subloop = false;
}
} else {
substart = open+8;
nestlev++;
}
} while (subloop);
}
return html + parse_inline_wiki(str.substr(lastend));
}
function parse_inline_images(str)
{
//<NOLITE>
var start, substart=0, nestlev=0;
var loop, close, open, wiki, html;
while (-1 != (start=str.indexOf('[[', substart))) {
if(str.substr(start+2).match(RegExp('^(Image|File|' + Insta.conf.locale.image + '):','i'))) {
loop=true;
substart=start;
do {
substart+=2;
close=str.indexOf(']]',substart);
open=str.indexOf('[[',substart);
if (close<=open||open==-1) {
if (close==-1) return str;
substart=close;
if (nestlev) {
nestlev--;
} else {
wiki=str.substring(start,close+2);
html=parse_image(wiki);
str=str.replace(wiki,html);
substart=start+html.length;
loop=false;
}
} else {
substart=open;
nestlev++;
}
} while (loop);
} else break;
}
//</NOLITE>
return str;
}
// the output of this function doesn't respect the FILO structure of HTML
// but since most browsers can handle it I'll save myself the hassle
function parse_inline_formatting(str)
{
var em,st,i,li,o='';
while ((i=str.indexOf("''",li))+1) {
o += str.substring(li,i);
li=i+2;
if (str.charAt(i+2)=="'") {
li++;
st=!st;
o+=st?'<strong>':'</strong>';
} else {
em=!em;
o+=em?'<em>':'</em>';
}
}
return o+str.substr(li);
}
function parse_inline_wiki(str)
{
str = parse_inline_images(str);
str = parse_inline_formatting(str);
// math
str = str.replace(/<(?:)math>(.*?)<\/math>/ig, '');
// Build a Mediawiki-formatted date string
var date = new Date();
var minutes = date.getUTCMinutes();
if (minutes < 10) minutes = '0' + minutes;
date = f("?:?, ? ? ? (UTC)", date.getUTCHours(), minutes, date.getUTCDate(), Insta.conf.locale.months[date.getUTCMonth()], date.getUTCFullYear());
// text formatting
return str.
// signatures
replace(/~{5}(?!~)/g, date).
replace(/~{4}(?!~)/g, Insta.conf.user.name+' '+date).
replace(/~{3}(?!~)/g, Insta.conf.user.name).
// [[:Category:...]], [[:Image:...]], etc...
replace(RegExp('\\[\\[:((?:'+Insta.conf.locale.category+'|Image|File|'+Insta.conf.locale.image+'|'+Insta.conf.wiki.interwiki+'):[^|]*?)\\]\\](\\w*)','gi'), function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($1) + htmlescape_text($2));}).
// remove straight category and interwiki tags
replace(RegExp('\\[\\[(?:'+Insta.conf.locale.category+'|'+Insta.conf.wiki.interwiki+'):.*?\\]\\]','gi'),'').
// [[:Category:...|Links]], [[:Image:...|Links]], etc...
replace(RegExp('\\[\\[:((?:'+Insta.conf.locale.category+'|Image|File|'+Insta.conf.locale.image+'|'+Insta.conf.wiki.interwiki+'):.*?)\\|([^\\]]+?)\\]\\](\\w*)','gi'), function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($2) + htmlescape_text($3));}).
// [[/Relative links]]
replace(/\[\[(\/[^|]*?)\]\]/g, function($0,$1){return f("<a href='?'>?</a>", Insta.conf.baseUrl + htmlescape_attr($1), htmlescape_text($1)); }).
// [[/Replaced|Relative links]]
replace(/\[\[(\/.*?)\|(.+?)\]\]/g, function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.baseUrl + htmlescape_attr($1), htmlescape_text($2)); }).
// [[Common links]]
replace(/\[\[([^[|]*?)\]\](\w*)/g, function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($1) + htmlescape_text($2)); }).
// [[Replaced|Links]]
replace(/\[\[([^[]*?)\|([^\]]+?)\]\](\w*)/g, function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($2) + htmlescape_text($3)); }).
// [[Stripped:Namespace|Namespace]]
replace(/\[\[([^\]]*?:)?(.*?)( *\(.*?\))?\|\]\]/g, function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1) + htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($2)); }).
// External links
replace(/\[(https?|news|ftp|mailto|gopher|irc):(\/*)([^\]]*?) (.*?)\]/g, function($0,$1,$2,$3,$4){return f("<a class='external' href='?:?'>?</a>", htmlescape_attr($1), htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($4)); }).
replace(/\[http:\/\/(.*?)\]/g, function($0,$1){return f("<a class='external' href='http://?'>[#]</a>", htmlescape_attr($1)); }).
replace(/\[(news|ftp|mailto|gopher|irc):(\/*)(.*?)\]/g, function($0,$1,$2,$3){return f("<a class='external' href='?:?'>?:?</a>", htmlescape_attr($1), htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($1), htmlescape_text($2) + htmlescape_text($3)); }).
replace(/(^| )(https?|news|ftp|mailto|gopher|irc):(\/*)([^ $]*[^.,!?;: $])/g, function($0,$1,$2,$3,$4){return f("?<a class='external' href='?:?'>?:?</a>", htmlescape_text($1), htmlescape_attr($2), htmlescape_attr($3) + htmlescape_attr($4), htmlescape_text($2), htmlescape_text($3) + htmlescape_text($4)); }).
replace('__NOTOC__','').
replace('__NOEDITSECTION__','');
}
// begin parsing
for (;remain();) if ($(/^(={1,6})(.*)\1(.*)$/)) {
p=0;
endl(f('<h?>?</h?>?', $r[1].length, parse_inline_nowiki($r[2]), $r[1].length, $r[3]));
} else if ($(/^[*#:;]/)) {
p=0;
parse_list();
} else if ($(' ')) {
p=0;
parse_pre();
} else if ($('{|')) {
p=0;
parse_table();
} else if ($(/^----+$/)) {
p=0;
endl('<hr />');
} else if ($(Insta.BLOCK_IMAGE)) {
p=0;
parse_block_image();
} else {
// handle paragraphs
if ($$('')) {
p = (remain()>1 && ll[1]===(''));
if (p) endl('<p><br>');
} else {
if(!p) {
ps('<p>');
p=1;
}
ps(parse_inline_nowiki(ll[0]) + ' ');
}
sh();
}
return o;
};
function wiki2html(txt,baseurl) {
Insta.conf.baseUrl=baseurl;
return Insta.convert(txt);
}
// ENDFILE: livepreview.js
// STARTFILE: pageinfo.js
//<NOLITE>
function popupFilterPageSize(data) {
return formatBytes(data.length);
}
function popupFilterCountLinks(data) {
var num=countLinks(data);
return String(num) + ' ' + ((num!=1)?popupString('wikiLinks'):popupString('wikiLink'));
}
function popupFilterCountImages(data) {
var num=countImages(data);
return String(num) + ' ' + ((num!=1)?popupString('images'):popupString('image'));
}
function popupFilterCountCategories(data) {
var num=countCategories(data);
return String(num) + ' ' + ((num!=1)?popupString('categories'):popupString('category'));
}
function popupFilterLastModified(data,download) {
var lastmod=download.lastModified;
var now=new Date();
var age=now-lastmod;
if (lastmod && getValueOf('popupLastModified')) {
return (tprintf('%s old', [formatAge(age)])).replace(RegExp(' ','g'), ' ');
}
return '';
}
function formatAge(age) {
// coerce into a number
var a=0+age, aa=a;
var seclen = 1000;
var minlen = 60*seclen;
var hourlen = 60*minlen;
var daylen = 24*hourlen;
var weeklen = 7*daylen;
var numweeks = (a-a%weeklen)/weeklen; a = a-numweeks*weeklen; var sweeks = addunit(numweeks, 'week');
var numdays = (a-a%daylen)/daylen; a = a-numdays*daylen; var sdays = addunit(numdays, 'day');
var numhours = (a-a%hourlen)/hourlen; a = a-numhours*hourlen; var shours = addunit(numhours,'hour');
var nummins = (a-a%minlen)/minlen; a = a-nummins*minlen; var smins = addunit(nummins, 'minute');
var numsecs = (a-a%seclen)/seclen; a = a-numsecs*seclen; var ssecs = addunit(numsecs, 'second');
if (aa > 4*weeklen) { return sweeks; }
if (aa > weeklen) { return sweeks + ' ' + sdays; }
if (aa > daylen) { return sdays + ' ' + shours; }
if (aa > 6*hourlen) { return shours; }
if (aa > hourlen) { return shours + ' ' + smins; }
if (aa > 10*minlen) { return smins; }
if (aa > minlen) { return smins + ' ' + ssecs; }
return ssecs;
}
function addunit(num,str) { return '' + num + ' ' + ((num!=1) ? popupString(str+'s') : popupString(str)) ;}
function runPopupFilters(list, data, download) {
var ret=[];
for (var i=0; i<list.length; ++i) {
if (list[i] && typeof list[i] == 'function') {
var s=list[i](data, download, download.owner.article);
if (s) { ret.push(s); }
}
}
return ret;
}
function getPageInfo(data, download) {
if (!data || data.length === 0) { return popupString('Empty page'); }
var popupFilters=getValueOf('popupFilters') || [];
var extraPopupFilters = getValueOf('extraPopupFilters') || [];
var pageInfoArray = runPopupFilters(popupFilters.concat(extraPopupFilters), data, download);
var pageInfo=pageInfoArray.join(', ');
if (pageInfo !== '' ) { pageInfo = upcaseFirst(pageInfo); }
return pageInfo;
}
// this could be improved!
function countLinks(wikiText) { return wikiText.split('[[').length - 1; }
// if N = # matches, n = # brackets, then
// String.parenSplit(regex) intersperses the N+1 split elements
// with Nn other elements. So total length is
// L= N+1 + Nn = N(n+1)+1. So N=(L-1)/(n+1).
function countImages(wikiText) {
return (wikiText.parenSplit(pg.re.image).length - 1) / (pg.re.imageBracketCount + 1);
}
function countCategories(wikiText) {
return (wikiText.parenSplit(pg.re.category).length - 1) / (pg.re.categoryBracketCount + 1);
}
function popupFilterStubDetect(data, download, article) {
var counts=stubCount(data, article);
if (counts.real) { return popupString('stub'); }
if (counts.sect) { return popupString('section stub'); }
return '';
}
function popupFilterDisambigDetect(data, download, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return ''; }
return (isDisambig(data, article)) ? popupString('disambig') : '';
}
function formatBytes(num) {
return (num > 949) ? (Math.round(num/100)/10+popupString('kB')) : (num +' ' + popupString('bytes')) ;
}
//</NOLITE>
// ENDFILE: pageinfo.js
// STARTFILE: titles.js
/**
@fileoverview Defines the {@link Title} class, and associated crufty functions.
<code>Title</code> deals with article titles and their various
forms. {@link Stringwrapper} is the parent class of
<code>Title</code>, which exists simply to make things a little
neater.
*/
/**
Creates a new Stringwrapper.
@constructor
@class the Stringwrapper class. This base class is not really
useful on its own; it just wraps various common string operations.
*/
function Stringwrapper() {
/**
Wrapper for this.toString().indexOf()
@param {String} x
@type integer
*/
this.indexOf=function(x){return this.toString().indexOf(x);};
/**
Returns this.value.
@type String
*/
this.toString=function(){return this.value;};
/**
Wrapper for {@link String#parenSplit} applied to this.toString()
@param {RegExp} x
@type Array
*/
this.parenSplit=function(x){return this.toString().parenSplit(x);};
/**
Wrapper for this.toString().substring()
@param {String} x
@param {String} y (optional)
@type String
*/
this.substring=function(x,y){
if (typeof y=='undefined') { return this.toString().substring(x); }
return this.toString().substring(x,y);
};
/**
Wrapper for this.toString().split()
@param {String} x
@type Array
*/
this.split=function(x){return this.toString().split(x);};
/**
Wrapper for this.toString().replace()
@param {String} x
@param {String} y
@type String
*/
this.replace=function(x,y){ return this.toString().replace(x,y); };
}
/**
Creates a new <code>Title</code>.
@constructor
@class The Title class. Holds article titles and converts them into
various forms. Also deals with anchors, by which we mean the bits
of the article URL after a # character, representing locations
within an article.
@param {String} value The initial value to assign to the
article. This must be the canonical title (see {@link
Title#value}. Omit this in the constructor and use another function
to set the title if this is unavailable.
*/
function Title(val) {
/**
The canonical article title. This must be in UTF-8 with no
entities, escaping or nasties. Also, underscores should be
replaced with spaces.
@type String
@private
*/
this.value=null;
/**
The canonical form of the anchor. This should be exactly as
it appears in the URL, i.e. with the .C3.0A bits in.
@type String
*/
this.anchor='';
this.setUtf(val);
}
Title.prototype=new Stringwrapper();
/**
Returns the canonical representation of the article title, optionally without anchor.
@param {boolean} omitAnchor
@fixme Decide specs for anchor
@return String The article title and the anchor.
*/
Title.prototype.toString=function(omitAnchor) {
return this.value + ( (!omitAnchor && this.anchor) ? '#' + this.anchorString() : '' );
};
Title.prototype.anchorString=function() {
if (!this.anchor) { return ''; }
var split=this.anchor.parenSplit(/((?:[.][0-9A-F]{2})+)/);
var len=split.length;
for (var j=1; j<len; j+=2) {
// FIXME s/decodeURI/decodeURIComponent/g ?
split[j]=decodeURIComponent(split[j].split('.').join('%')).split('_').join(' ');
}
return split.join('');
};
Title.prototype.urlAnchor=function() {
var split=this.anchor.parenSplit('/((?:[%][0-9A-F]{2})+)/');
var len=split.length;
for (var j=1; j<len; j+=2) {
split[j]=split[j].split('%').join('.');
}
return split.join('');
};
Title.prototype.anchorFromUtf=function(str) {
this.anchor=encodeURIComponent(str.split(' ').join('_'))
.split('%3A').join(':').split("'").join('%27').split('%').join('.');
};
Title.fromURL=function(h) {
return new Title().fromURL(h);
};
Title.prototype.fromURL=function(h) {
if (typeof h != 'string') {
this.value=null;
return this;
}
// NOTE : playing with decodeURI, encodeURI, escape, unescape,
// we seem to be able to replicate the IE borked encoding
// IE doesn't do this new-fangled utf-8 thing.
// and it's worse than that.
// IE seems to treat the query string differently to the rest of the url
// the query is treated as bona-fide utf8, but the first bit of the url is pissed around with
// we fix up & for all browsers, just in case.
var splitted=h.split('?');
splitted[0]=splitted[0].split('&').join('%26');
h=splitted.join('?');
var contribs=pg.re.contribs.exec(h);
if (contribs) {
if (contribs[1]=='title=') { contribs[3]=contribs[3].split('+').join(' '); }
var u=new Title(contribs[3]);
this.setUtf(this.decodeNasties(mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + u.stripNamespace()));
return this;
}
var email=pg.re.email.exec(h);
if (email) {
this.setUtf(this.decodeNasties(mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + new Title(email[3]).stripNamespace()));
return this;
}
var backlinks=pg.re.backlinks.exec(h);
if (backlinks) {
this.setUtf(this.decodeNasties(new Title(backlinks[3])));
return this;
}
//A dummy title object for a Special:Diff link.
var specialdiff=pg.re.specialdiff.exec(h);
if (specialdiff) {
this.setUtf(this.decodeNasties(new Title(mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId] + ':Diff')));
return this;
}
// no more special cases to check --
// hopefully it's not a disguised user-related or specially treated special page
var m=pg.re.main.exec(h);
if(m === null) { this.value=null; }
else {
var fromBotInterface = /[?](.+[&])?title=/.test(h);
if (fromBotInterface) {
m[2]=m[2].split('+').join('_');
}
var extracted = m[2] + (m[3] ? '#' + m[3] : '');
if (pg.flag.isSafari && /%25[0-9A-Fa-f]{2}/.test(extracted)) {
// Fix Safari issue
// Safari sometimes encodes % as %25 in UTF-8 encoded strings like %E5%A3 -> %25E5%25A3.
this.setUtf(decodeURIComponent(unescape(extracted)));
} else {
this.setUtf(this.decodeNasties(extracted));
}
}
return this;
};
Title.prototype.decodeNasties=function(txt) {
var ret= this.decodeEscapes(decodeURI(txt));
ret = ret.replace(/[_ ]*$/, '');
return ret;
};
Title.prototype.decodeEscapes=function(txt) {
var split=txt.parenSplit(/((?:[%][0-9A-Fa-f]{2})+)/);
var len=split.length;
for (var i=1; i<len; i=i+2) {
// FIXME is decodeURIComponent better?
split[i]=unescape(split[i]);
}
return split.join('');
};
Title.fromAnchor=function(a) {
return new Title().fromAnchor(a);
};
Title.prototype.fromAnchor=function(a) {
if (!a) { this.value=null; return this; }
return this.fromURL(a.href);
};
Title.fromWikiText=function(txt) {
return new Title().fromWikiText(txt);
};
Title.prototype.fromWikiText=function(txt) {
// FIXME - testing needed
txt=myDecodeURI(txt);
this.setUtf(txt);
return this;
};
Title.prototype.hintValue=function(){
if(!this.value) { return ''; }
return safeDecodeURI(this.value);
};
//<NOLITE>
Title.prototype.toUserName=function(withNs) {
if (this.namespaceId() != pg.nsUserId && this.namespaceId() != pg.nsUsertalkId) {
this.value=null;
return;
}
this.value = (withNs ? mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' : '') + this.stripNamespace().split('/')[0];
};
Title.prototype.userName=function(withNs) {
var t=(new Title(this.value));
t.toUserName(withNs);
if (t.value) { return t; }
return null;
};
Title.prototype.toTalkPage=function() {
// convert article to a talk page, or if we can't, return null
// In other words: return null if this ALREADY IS a talk page
// and return the corresponding talk page otherwise
//
// Per https://www.mediawiki.org/wiki/Manual:Namespace#Subject_and_talk_namespaces
// * All discussion namespaces have odd-integer indices
// * The discussion namespace index for a specific namespace with index n is n + 1
if (this.value === null) { return null; }
var namespaceId = this.namespaceId();
if (namespaceId>=0 && namespaceId % 2 === 0) //non-special and subject namespace
{
var localizedNamespace = mw.config.get('wgFormattedNamespaces')[namespaceId+1];
if (typeof localizedNamespace!=='undefined')
{
if (localizedNamespace === '') {
this.value = this.stripNamespace();
} else {
this.value = localizedNamespace.split(' ').join('_') + ':' + this.stripNamespace();
}
return this.value;
}
}
this.value=null;
return null;
};
//</NOLITE>
// Return canonical, localized namespace
Title.prototype.namespace=function() {
return mw.config.get('wgFormattedNamespaces')[this.namespaceId()];
};
Title.prototype.namespaceId=function() {
var n=this.value.indexOf(':');
if (n<0) { return 0; } //mainspace
var namespaceId = mw.config.get('wgNamespaceIds')[this.value.substring(0,n).split(' ').join('_').toLowerCase()];
if (typeof namespaceId=='undefined') return 0; //mainspace
return namespaceId;
};
//<NOLITE>
Title.prototype.talkPage=function() {
var t=new Title(this.value);
t.toTalkPage();
if (t.value) { return t; }
return null;
};
Title.prototype.isTalkPage=function() {
if (this.talkPage()===null) { return true; }
return false;
};
Title.prototype.toArticleFromTalkPage=function() {
//largely copy/paste from toTalkPage above.
if (this.value === null) { return null; }
var namespaceId = this.namespaceId();
if (namespaceId >= 0 && namespaceId % 2 == 1) //non-special and talk namespace
{
var localizedNamespace = mw.config.get('wgFormattedNamespaces')[namespaceId-1];
if (typeof localizedNamespace!=='undefined')
{
if (localizedNamespace === '') {
this.value = this.stripNamespace();
} else {
this.value = localizedNamespace.split(' ').join('_') + ':' + this.stripNamespace();
}
return this.value;
}
}
this.value=null;
return null;
};
Title.prototype.articleFromTalkPage=function() {
var t=new Title(this.value);
t.toArticleFromTalkPage();
if (t.value) { return t; }
return null;
};
Title.prototype.articleFromTalkOrArticle=function() {
var t=new Title(this.value);
if ( t.toArticleFromTalkPage() ) { return t; }
return this;
};
Title.prototype.isIpUser=function() {
return pg.re.ipUser.test(this.userName());
};
//</NOLITE>
Title.prototype.stripNamespace=function(){ // returns a string, not a Title
var n=this.value.indexOf(':');
if (n<0) { return this.value; }
var namespaceId = this.namespaceId();
if (namespaceId === pg.nsMainspaceId) return this.value;
return this.value.substring(n+1);
};
Title.prototype.setUtf=function(value){
if (!value) { this.value=''; return; }
var anch=value.indexOf('#');
if(anch < 0) { this.value=value.split('_').join(' '); this.anchor=''; return; }
this.value=value.substring(0,anch).split('_').join(' ');
this.anchor=value.substring(anch+1);
this.ns=null; // wait until namespace() is called
};
Title.prototype.setUrl=function(urlfrag) {
var anch=urlfrag.indexOf('#');
this.value=safeDecodeURI(urlfrag.substring(0,anch));
this.anchor=this.value.substring(anch+1);
};
Title.prototype.append=function(x){
this.setUtf(this.value + x);
};
Title.prototype.urlString=function(x) {
if(!x) { x={}; }
var v=this.toString(true);
if (!x.omitAnchor && this.anchor) { v+= '#' + this.urlAnchor(); }
if (!x.keepSpaces) { v=v.split(' ').join('_'); }
return encodeURI(v).split('&').join('%26').split('?').join('%3F').split('+').join('%2B');
};
Title.prototype.removeAnchor=function() {
return new Title(this.toString(true));
};
Title.prototype.toUrl=function() {
return pg.wiki.titlebase + this.urlString();
};
function parseParams(url) {
var specialDiff = pg.re.specialdiff.exec(url);
if (specialDiff)
{
var split= specialDiff[1].split('/');
if (split.length==1) return {oldid:split[0], diff: 'prev'};
else if (split.length==2) return {oldid: split[0], diff: split[1]};
}
var ret={};
if (url.indexOf('?')==-1) { return ret; }
url = url.split('#')[0];
var s=url.split('?').slice(1).join();
var t=s.split('&');
for (var i=0; i<t.length; ++i) {
var z=t[i].split('=');
z.push(null);
ret[z[0]]=z[1];
}
//Diff revision with no oldid is interpreted as a diff to the previous revision by MediaWiki
if (ret.diff && typeof(ret.oldid)==='undefined')
{
ret.oldid = "prev";
}
//Documentation seems to say something different, but oldid can also accept prev/next, and Echo is emitting such URLs. Simple fixup during parameter decoding:
if (ret.oldid && (ret.oldid==='prev' || ret.oldid==='next' || ret.oldid==='cur'))
{
var helper = ret.diff;
ret.diff = ret.oldid;
ret.oldid = helper;
}
return ret;
}
// (a) myDecodeURI (first standard decodeURI, then pg.re.urlNoPopup)
// (b) change spaces to underscores
// (c) encodeURI (just the straight one, no pg.re.urlNoPopup)
function myDecodeURI (str) {
var ret;
// FIXME decodeURIComponent??
try { ret=decodeURI(str.toString()); }
catch (summat) { return str; }
for (var i=0; i<pg.misc.decodeExtras.length; ++i) {
var from=pg.misc.decodeExtras[i].from;
var to=pg.misc.decodeExtras[i].to;
ret=ret.split(from).join(to);
}
return ret;
}
function safeDecodeURI(str) { var ret=myDecodeURI(str); return ret || str; }
///////////
// TESTS //
///////////
//<NOLITE>
function isDisambig(data, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return false; }
return ! article.isTalkPage() && pg.re.disambig.test(data);
}
function stubCount(data, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return false; }
var sectStub=0;
var realStub=0;
if (pg.re.stub.test(data)) {
var s=data.parenSplit(pg.re.stub);
for (var i=1; i<s.length; i=i+2) {
if (s[i]) { ++sectStub; }
else { ++realStub; }
}
}
return { real: realStub, sect: sectStub };
}
function isValidImageName(str){ // extend as needed...
return ( str.indexOf('{') == -1 );
}
function isInStrippableNamespace(article) {
// Does the namespace allow subpages
// Note, would be better if we had access to wgNamespacesWithSubpages
return ( article.namespaceId() !== 0 );
}
function isInMainNamespace(article) { return article.namespaceId() === 0; }
function anchorContainsImage(a) {
// iterate over children of anchor a
// see if any are images
if (a === null) { return false; }
var kids=a.childNodes;
for (var i=0; i<kids.length; ++i) { if (kids[i].nodeName=='IMG') { return true; } }
return false;
}
//</NOLITE>
function isPopupLink(a) {
// NB for performance reasons, TOC links generally return true
// they should be stripped out later
if (!markNopopupSpanLinks.done) { markNopopupSpanLinks(); }
if (a.inNopopupSpan) { return false; }
// FIXME is this faster inline?
if (a.onmousedown || a.getAttribute('nopopup')) { return false; }
var h=a.href;
if (h === document.location.href+'#') { return false; }
if (!pg.re.basenames.test(h)) { return false; }
if (!pg.re.urlNoPopup.test(h)) { return true; }
return (
(pg.re.email.test(h) || pg.re.contribs.test(h) || pg.re.backlinks.test(h) || pg.re.specialdiff.test(h)) &&
h.indexOf('&limit=') == -1 );
}
function markNopopupSpanLinks() {
if( !getValueOf('popupOnlyArticleLinks'))
fixVectorMenuPopups();
var s = $('.nopopups').toArray();
for (var i=0; i<s.length; ++i) {
var as=s[i].getElementsByTagName('a');
for (var j=0; j<as.length; ++j) {
as[j].inNopopupSpan=true;
}
}
markNopopupSpanLinks.done=true;
}
function fixVectorMenuPopups() {
$('div.vectorMenu h3:first a:first').prop('inNopopupSpan', true);
}
// ENDFILE: titles.js
// STARTFILE: getpage.js
//////////////////////////////////////////////////
// Wiki-specific downloading
//
// Schematic for a getWiki call
//
// getPageWithCaching
// |
// false | true
// getPage<-[findPictureInCache]->-onComplete(a fake download)
// \.
// (async)->addPageToCache(download)->-onComplete(download)
// check cache to see if page exists
function getPageWithCaching(url, onComplete, owner) {
log('getPageWithCaching, url='+url);
var i=findInPageCache(url);
var d;
if (i > -1) {
d=fakeDownload(url, owner.idNumber, onComplete,
pg.cache.pages[i].data, pg.cache.pages[i].lastModified,
owner);
} else {
d=getPage(url, onComplete, owner);
if (d && owner && owner.addDownload) {
owner.addDownload(d);
d.owner=owner;
}
}
}
function getPage(url, onComplete, owner) {
log('getPage');
var callback= function (d) { if (!d.aborted) {addPageToCache(d); onComplete(d);} };
return startDownload(url, owner.idNumber, callback);
}
function findInPageCache(url) {
for (var i=0; i<pg.cache.pages.length; ++i) {
if (url==pg.cache.pages[i].url) { return i; }
}
return -1;
}
function addPageToCache(download) {
log('addPageToCache '+download.url);
var page = {url: download.url, data: download.data, lastModified: download.lastModified};
return pg.cache.pages.push(page);
}
// ENDFILE: getpage.js
// STARTFILE: parensplit.js
//////////////////////////////////////////////////
// parenSplit
// String.prototype.parenSplit should do what ECMAscript says String.prototype.split does,
// interspersing paren matches (regex capturing groups) between the split elements.
// i.e. 'abc'.split(/(b)/)) should return ['a','b','c'], not ['a','c']
if (String('abc'.split(/(b)/))!='a,b,c') {
// broken String.split, e.g. konq, IE < 10
String.prototype.parenSplit=function (re) {
re=nonGlobalRegex(re);
var s=this;
var m=re.exec(s);
var ret=[];
while (m && s) {
// without the following loop, we have
// 'ab'.parenSplit(/a|(b)/) != 'ab'.split(/a|(b)/)
for(var i=0; i<m.length; ++i) {
if (typeof m[i]=='undefined') m[i]='';
}
ret.push(s.substring(0,m.index));
ret = ret.concat(m.slice(1));
s=s.substring(m.index + m[0].length);
m=re.exec(s);
}
ret.push(s);
return ret;
};
} else {
String.prototype.parenSplit=function (re) { return this.split(re); };
String.prototype.parenSplit.isNative=true;
}
function nonGlobalRegex(re) {
var s=re.toString();
var flags='';
for (var j=s.length; s.charAt(j) != '/'; --j) {
if (s.charAt(j) != 'g') { flags += s.charAt(j); }
}
var t=s.substring(1,j);
return RegExp(t,flags);
}
// ENDFILE: parensplit.js
// STARTFILE: tools.js
// IE madness with encoding
// ========================
//
// suppose throughout that the page is in utf8, like wikipedia
//
// if a is an anchor DOM element and a.href should consist of
//
// http://host.name.here/wiki/foo?bar=baz
//
// then IE gives foo as "latin1-encoded" utf8; we have foo = decode_utf8(decodeURI(foo_ie))
// but IE gives bar=baz correctly as plain utf8
//
// ---------------------------------
//
// IE's xmlhttp doesn't understand utf8 urls. Have to use encodeURI here.
//
// ---------------------------------
//
// summat else
// Source: http://aktuell.de.selfhtml.org/artikel/javascript/utf8b64/utf8.htm
//<NOLITE>
function getJsObj(json) {
try {
var json_ret = JSON.parse(json);
if( json_ret.warnings ) {
for( var w=0; w < json_ret.warnings.length; w++ ) {
if( json_ret.warnings[w]['*'] ) {
log( json_ret.warnings[w]['*'] );
} else {
log( json_ret.warnings[w]['warnings'] );
}
}
} else if ( json_ret.error ) {
errlog( json_ret.error.code + ': ' + json_ret.error.info );
}
return json_ret;
} catch (someError) {
errlog('Something went wrong with getJsObj, json='+json);
return 1;
}
}
function anyChild(obj) {
for (var p in obj) {
return obj[p];
}
return null;
}
//</NOLITE>
function upcaseFirst(str) {
if (typeof str != typeof '' || str === '') return '';
return str.charAt(0).toUpperCase() + str.substring(1);
}
function findInArray(arr, foo) {
if (!arr || !arr.length) { return -1; }
var len=arr.length;
for (var i=0; i<len; ++i) { if (arr[i]==foo) { return i; } }
return -1;
}
/* eslint-disable no-unused-vars */
function nextOne (array, value) {
// NB if the array has two consecutive entries equal
// then this will loop on successive calls
var i=findInArray(array, value);
if (i<0) { return null; }
return array[i+1];
}
/* eslint-enable no-unused-vars */
function literalizeRegex(str){
return mw.util.escapeRegExp(str);
}
String.prototype.entify=function() {
//var shy='­';
return this.split('&').join('&').split('<').join('<').split('>').join('>'/*+shy*/).split('"').join('"');
};
// Array filter function
function removeNulls(val) { return val !== null; }
function joinPath(list) {
return list.filter(removeNulls).join('/');
}
function simplePrintf(str, subs) {
if (!str || !subs) { return str; }
var ret=[];
var s=str.parenSplit(/(%s|\$[0-9]+)/);
var i=0;
do {
ret.push(s.shift());
if ( !s.length ) { break; }
var cmd=s.shift();
if (cmd == '%s') {
if ( i < subs.length ) { ret.push(subs[i]); } else { ret.push(cmd); }
++i;
} else {
var j=parseInt( cmd.replace('$', ''), 10 ) - 1;
if ( j > -1 && j < subs.length ) { ret.push(subs[j]); } else { ret.push(cmd); }
}
} while (s.length > 0);
return ret.join('');
}
/* eslint-disable no-unused-vars */
function isString(x) { return (typeof x === 'string' || x instanceof String); }
function isNumber(x) { return (typeof x === 'number' || x instanceof Number); }
function isRegExp(x) { return x instanceof RegExp; }
function isArray (x) { return x instanceof Array; }
function isObject(x) { return x instanceof Object; }
function isFunction(x) {
return !isRegExp(x) && (typeof x === 'function' || x instanceof Function);
}
/* eslint-enable no-unused-vars */
function repeatString(s,mult) {
var ret='';
for (var i=0; i<mult; ++i) { ret += s; }
return ret;
}
function zeroFill(s, min) {
min = min || 2;
var t=s.toString();
return repeatString('0', min - t.length) + t;
}
function map(f, o) {
if (isArray(o)) { return map_array(f,o); }
return map_object(f,o);
}
function map_array(f,o) {
var ret=[];
for (var i=0; i<o.length; ++i) {
ret.push(f(o[i]));
}
return ret;
}
function map_object(f,o) {
var ret={};
for (var i in o) { ret[o]=f(o[i]); }
return ret;
}
pg.escapeQuotesHTML = function ( text ) {
return text
.replace(/&/g, "&")
.replace(/"/g, """)
.replace(/</g, "<")
.replace(/>/g, ">");
};
// ENDFILE: tools.js
// STARTFILE: dab.js
//<NOLITE>
//////////////////////////////////////////////////
// Dab-fixing code
//
function retargetDab(newTarget, oldTarget, friendlyCurrentArticleName, titleToEdit) {
log('retargetDab: newTarget='+newTarget + ' oldTarget=' + oldTarget);
return changeLinkTargetLink(
{newTarget: newTarget,
text: newTarget.split(' ').join(' '),
hint: tprintf('disambigHint', [newTarget]),
summary: simplePrintf(
getValueOf('popupFixDabsSummary'), [friendlyCurrentArticleName, newTarget ]),
clickButton: getValueOf('popupDabsAutoClick'), minor: true, oldTarget: oldTarget,
watch: getValueOf('popupWatchDisambiggedPages'),
title: titleToEdit});
}
function listLinks(wikitext, oldTarget, titleToEdit) {
// mediawiki strips trailing spaces, so we do the same
// testcase: https://en.wikipedia.org/w/index.php?title=Radial&oldid=97365633
var reg=RegExp('\\[\\[([^|]*?) *(\\||\\]\\])', 'gi');
var ret=[];
var splitted=wikitext.parenSplit(reg);
// ^[a-z]+ should match interwiki links, hopefully (case-insensitive)
// and ^[a-z]* should match those and [[:Category...]] style links too
var omitRegex=RegExp('^[a-z]*:|^[Ss]pecial:|^[Ii]mage|^[Cc]ategory');
var friendlyCurrentArticleName= oldTarget.toString();
var wikPos = getValueOf('popupDabWiktionary');
for (var i=1; i<splitted.length; i=i+3) {
if (typeof splitted[i] == typeof 'string' && splitted[i].length>0 && !omitRegex.test(splitted[i])) {
ret.push( retargetDab(splitted[i], oldTarget, friendlyCurrentArticleName, titleToEdit) );
} /* if */
} /* for loop */
ret = rmDupesFromSortedList(ret.sort());
if (wikPos) {
var wikTarget='wiktionary:' +
friendlyCurrentArticleName.replace( RegExp('^(.+)\\s+[(][^)]+[)]\\s*$'), '$1' );
var meth;
if (wikPos.toLowerCase() == 'first') { meth = 'unshift'; }
else { meth = 'push'; }
ret[meth]( retargetDab(wikTarget, oldTarget, friendlyCurrentArticleName, titleToEdit) );
}
ret.push(changeLinkTargetLink(
{ newTarget: null,
text: popupString('remove this link').split(' ').join(' '),
hint: popupString("remove all links to this disambig page from this article"),
clickButton: getValueOf('popupDabsAutoClick'), oldTarget: oldTarget,
summary: simplePrintf(getValueOf('popupRmDabLinkSummary'), [friendlyCurrentArticleName]),
watch: getValueOf('popupWatchDisambiggedPages'),
title: titleToEdit
}));
return ret;
}
function rmDupesFromSortedList(list) {
var ret=[];
for (var i=0; i<list.length; ++i) {
if (ret.length === 0 || list[i]!=ret[ret.length-1]) { ret.push(list[i]); }
}
return ret;
}
function makeFixDab(data, navpop) {
// grab title from parent popup if there is one; default exists in changeLinkTargetLink
var titleToEdit=(navpop.parentPopup && navpop.parentPopup.article.toString());
var list=listLinks(data, navpop.originalArticle, titleToEdit);
if (list.length === 0) { log('listLinks returned empty list'); return null; }
var html='<hr />' + popupString('Click to disambiguate this link to:') + '<br>';
html+=list.join(', ');
return html;
}
function makeFixDabs(wikiText, navpop) {
if (getValueOf('popupFixDabs') && isDisambig(wikiText, navpop.article) &&
Title.fromURL(location.href).namespaceId() != pg.nsSpecialId &&
navpop.article.talkPage() ) {
setPopupHTML(makeFixDab(wikiText, navpop), 'popupFixDab', navpop.idNumber);
}
}
function popupRedlinkHTML(article) {
return changeLinkTargetLink(
{ newTarget: null, text: popupString('remove this link').split(' ').join(' '),
hint: popupString("remove all links to this page from this article"),
clickButton: getValueOf('popupRedlinkAutoClick'),
oldTarget: article.toString(),
summary: simplePrintf(getValueOf('popupRedlinkSummary'), [article.toString()])});
}
//</NOLITE>
// ENDFILE: dab.js
// STARTFILE: htmloutput.js
// this has to use a timer loop as we don't know if the DOM element exists when we want to set the text
function setPopupHTML (str, elementId, popupId, onSuccess, append) {
if (typeof popupId === 'undefined') {
//console.error('popupId is not defined in setPopupHTML, html='+str.substring(0,100));
popupId = pg.idNumber;
}
var popupElement=document.getElementById(elementId+popupId);
if (popupElement) {
if (!append) { popupElement.innerHTML=''; }
if (isString(str)) {
popupElement.innerHTML+=str;
} else {
popupElement.appendChild(str);
}
if (onSuccess) { onSuccess(); }
setTimeout(checkPopupPosition, 100);
return true;
} else {
// call this function again in a little while...
setTimeout(function(){
setPopupHTML(str,elementId,popupId,onSuccess);
}, 600);
}
return null;
}
//<NOLITE>
function setPopupTrailer(str,id) {return setPopupHTML(str, 'popupData', id);}
//</NOLITE>
// args.navpopup is mandatory
// optional: args.redir, args.redirTarget
// FIXME: ye gods, this is ugly stuff
function fillEmptySpans(args) {
// if redir is present and true then redirTarget is mandatory
var redir=true;
var rcid;
if (typeof args != 'object' || typeof args.redir == 'undefined' || !args.redir) { redir=false; }
var a=args.navpopup.parentAnchor;
var article, hint=null, oldid=null, params={};
if (redir && typeof args.redirTarget == typeof {}) {
article=args.redirTarget;
//hint=article.hintValue();
} else {
article=(new Title()).fromAnchor(a);
hint=a.originalTitle || article.hintValue();
params=parseParams(a.href);
oldid=(getValueOf('popupHistoricalLinks')) ? params.oldid : null;
rcid=params.rcid;
}
var x={ article:article, hint: hint, oldid: oldid, rcid: rcid, navpop:args.navpopup, params:params };
var structure=pg.structures[getValueOf('popupStructure')];
if (typeof structure != 'object') {
setPopupHTML('popupError', 'Unknown structure (this should never happen): '+
pg.option.popupStructure, args.navpopup.idNumber);
return;
}
var spans=flatten(pg.misc.layout);
var numspans = spans.length;
var redirs=pg.misc.redirSpans;
for (var i=0; i<numspans; ++i) {
var found = redirs && (redirs.indexOf( spans[i] ) !== -1);
//log('redir='+redir+', found='+found+', spans[i]='+spans[i]);
if ( (found && !redir) || (!found && redir) ) {
//log('skipping this set of the loop');
continue;
}
var structurefn=structure[spans[i]];
var setfn = setPopupHTML;
if (getValueOf('popupActiveNavlinks') &&
(spans[i].indexOf('popupTopLinks')===0 || spans[i].indexOf('popupRedirTopLinks')===0)
) {
setfn = setPopupTipsAndHTML;
}
switch (typeof structurefn) {
case 'function':
log('running '+spans[i]+'({article:'+x.article+', hint:'+x.hint+', oldid: '+x.oldid+'})');
setfn(structurefn(x), spans[i], args.navpopup.idNumber);
break;
case 'string':
setfn(structurefn, spans[i], args.navpopup.idNumber);
break;
default:
errlog('unknown thing with label '+spans[i] + ' (span index was ' + i + ')');
break;
}
}
}
// flatten an array
function flatten(list, start) {
var ret=[];
if (typeof start == 'undefined') { start=0; }
for (var i=start; i<list.length; ++i) {
if (typeof list[i] == typeof []) {
return ret.concat(flatten(list[i])).concat(flatten(list, i+1));
}
else { ret.push(list[i]); }
}
return ret;
}
// Generate html for whole popup
function popupHTML (a) {
getValueOf('popupStructure');
var structure=pg.structures[pg.option.popupStructure];
if (typeof structure != 'object') {
//return 'Unknown structure: '+pg.option.popupStructure;
// override user choice
pg.option.popupStructure=pg.optionDefault.popupStructure;
return popupHTML(a);
}
if (typeof structure.popupLayout != 'function') { return 'Bad layout'; }
pg.misc.layout=structure.popupLayout();
if (typeof structure.popupRedirSpans === 'function') { pg.misc.redirSpans=structure.popupRedirSpans(); }
else { pg.misc.redirSpans=[]; }
return makeEmptySpans(pg.misc.layout, a.navpopup);
}
function makeEmptySpans (list, navpop) {
var ret='';
for (var i=0; i<list.length; ++i) {
if (typeof list[i] == typeof '') {
ret += emptySpanHTML(list[i], navpop.idNumber, 'div');
} else if (typeof list[i] == typeof [] && list[i].length > 0 ) {
ret = ret.parenSplit(RegExp('(</[^>]*?>$)')).join(makeEmptySpans(list[i], navpop));
} else if (typeof list[i] == typeof {} && list[i].nodeType ) {
ret += emptySpanHTML(list[i].name, navpop.idNumber, list[i].nodeType);
}
}
return ret;
}
function emptySpanHTML(name, id, tag, classname) {
tag = tag || 'span';
if (!classname) { classname = emptySpanHTML.classAliases[name]; }
classname = classname || name;
if (name == getValueOf('popupDragHandle')) { classname += ' popupDragHandle'; }
return simplePrintf('<%s id="%s" class="%s"></%s>', [tag, name + id, classname, tag]);
}
emptySpanHTML.classAliases={ 'popupSecondPreview': 'popupPreview' };
// generate html for popup image
// <a id="popupImageLinkn"><img id="popupImagen">
// where n=idNumber
function imageHTML(article, idNumber) {
return simplePrintf('<a id="popupImageLink$1">' +
'<img align="right" valign="top" id="popupImg$1" style="display: none;"></img>' +
'</a>', [ idNumber ]);
}
function popTipsSoonFn(id, when, popData) {
if (!when) { when=250; }
var popTips=function(){ setupTooltips(document.getElementById(id), false, true, popData); };
return function() { setTimeout( popTips, when, popData ); };
}
function setPopupTipsAndHTML(html, divname, idnumber, popData) {
setPopupHTML(html, divname, idnumber,
getValueOf('popupSubpopups') ?
popTipsSoonFn(divname + idnumber, null, popData) :
null);
}
// ENDFILE: htmloutput.js
// STARTFILE: mouseout.js
//////////////////////////////////////////////////
// fuzzy checks
function fuzzyCursorOffMenus(x,y, fuzz, parent) {
if (!parent) { return null; }
var uls=parent.getElementsByTagName('ul');
for (var i=0; i<uls.length; ++i) {
if (uls[i].className=='popup_menu') {
if (uls[i].offsetWidth > 0) return false;
} // else {document.title+='.';}
}
return true;
}
function checkPopupPosition () { // stop the popup running off the right of the screen
// FIXME avoid pg.current.link
if (pg.current.link && pg.current.link.navpopup)
pg.current.link.navpopup.limitHorizontalPosition();
}
function mouseOutWikiLink () {
//console ('mouseOutWikiLink');
var a=this;
removeModifierKeyHandler(a);
if (a.navpopup === null || typeof a.navpopup === 'undefined') return;
if ( ! a.navpopup.isVisible() ) {
a.navpopup.banish();
return;
}
restoreTitle(a);
Navpopup.tracker.addHook(posCheckerHook(a.navpopup));
}
function posCheckerHook(navpop) {
return function() {
if (!navpop.isVisible()) { return true; /* remove this hook */ }
if (Navpopup.tracker.dirty) {
return false;
}
var x=Navpopup.tracker.x, y=Navpopup.tracker.y;
var mouseOverNavpop = navpop.isWithin(x,y,navpop.fuzz, navpop.mainDiv) ||
!fuzzyCursorOffMenus(x,y,navpop.fuzz, navpop.mainDiv);
// FIXME it'd be prettier to do this internal to the Navpopup objects
var t=getValueOf('popupHideDelay');
if (t) { t = t * 1000; }
if (!t) {
if(!mouseOverNavpop) {
if(navpop.parentAnchor) {
restoreTitle( navpop.parentAnchor );
}
navpop.banish();
return true; /* remove this hook */
}
return false;
}
// we have a hide delay set
var d=+(new Date());
if ( !navpop.mouseLeavingTime ) {
navpop.mouseLeavingTime = d;
return false;
}
if ( mouseOverNavpop ) {
navpop.mouseLeavingTime=null;
return false;
}
if (d - navpop.mouseLeavingTime > t) {
navpop.mouseLeavingTime=null;
navpop.banish(); return true; /* remove this hook */
}
return false;
};
}
function runStopPopupTimer(navpop) {
// at this point, we should have left the link but remain within the popup
// so we call this function again until we leave the popup.
if (!navpop.stopPopupTimer) {
navpop.stopPopupTimer=setInterval(posCheckerHook(navpop), 500);
navpop.addHook(function(){clearInterval(navpop.stopPopupTimer);},
'hide', 'before');
}
}
// ENDFILE: mouseout.js
// STARTFILE: previewmaker.js
/**
@fileoverview
Defines the {@link Previewmaker} object, which generates short previews from wiki markup.
*/
/**
Creates a new Previewmaker
@constructor
@class The Previewmaker class. Use an instance of this to generate short previews from Wikitext.
@param {String} wikiText The Wikitext source of the page we wish to preview.
@param {String} baseUrl The url we should prepend when creating relative urls.
@param {Navpopup} owner The navpop associated to this preview generator
*/
function Previewmaker(wikiText, baseUrl, owner) {
/** The wikitext which is manipulated to generate the preview. */
this.originalData=wikiText;
this.baseUrl=baseUrl;
this.owner=owner;
this.maxCharacters=getValueOf('popupMaxPreviewCharacters');
this.maxSentences=getValueOf('popupMaxPreviewSentences');
this.setData();
}
Previewmaker.prototype.setData=function() {
var maxSize=Math.max(10000, 2*this.maxCharacters);
this.data=this.originalData.substring(0,maxSize);
};
/** Remove HTML comments
@private
*/
Previewmaker.prototype.killComments = function () {
// this also kills one trailing newline, eg [[diamyo]]
this.data=this.data.replace(RegExp('^<!--[^$]*?-->\\n|\\n<!--[^$]*?-->(?=\\n)|<!--[^$]*?-->', 'g'), '');
};
/**
@private
*/
Previewmaker.prototype.killDivs = function () {
// say goodbye, divs (can be nested, so use * not *?)
this.data=this.data.replace(RegExp('< *div[^>]* *>[\\s\\S]*?< */ *div *>',
'gi'), '');
};
/**
@private
*/
Previewmaker.prototype.killGalleries = function () {
this.data=this.data.replace(RegExp('< *gallery[^>]* *>[\\s\\S]*?< */ *gallery *>',
'gi'), '');
};
/**
@private
*/
Previewmaker.prototype.kill = function(opening, closing, subopening, subclosing, repl) {
var oldk=this.data;
var k=this.killStuff(this.data, opening, closing, subopening, subclosing, repl);
while (k.length < oldk.length) {
oldk=k;
k=this.killStuff(k, opening, closing, subopening, subclosing, repl);
}
this.data=k;
};
/**
@private
*/
Previewmaker.prototype.killStuff = function (txt, opening, closing, subopening, subclosing, repl) {
var op=this.makeRegexp(opening);
var cl=this.makeRegexp(closing, '^');
var sb=subopening ? this.makeRegexp(subopening, '^') : null;
var sc=subclosing ? this.makeRegexp(subclosing, '^') : cl;
if (!op || !cl) {
alert('Navigation Popups error: op or cl is null! something is wrong.');
return;
}
if (!op.test(txt)) { return txt; }
var ret='';
var opResult = op.exec(txt);
ret = txt.substring(0,opResult.index);
txt=txt.substring(opResult.index+opResult[0].length);
var depth = 1;
while (txt.length > 0) {
var removal=0;
if (depth==1 && cl.test(txt)) {
depth--;
removal=cl.exec(txt)[0].length;
} else if (depth > 1 && sc.test(txt)) {
depth--;
removal=sc.exec(txt)[0].length;
}else if (sb && sb.test(txt)) {
depth++;
removal=sb.exec(txt)[0].length;
}
if ( !removal ) { removal = 1; }
txt=txt.substring(removal);
if (depth === 0) { break; }
}
return ret + (repl || '') + txt;
};
/**
@private
*/
Previewmaker.prototype.makeRegexp = function (x, prefix, suffix) {
prefix = prefix || '';
suffix = suffix || '';
var reStr='';
var flags='';
if (isString(x)) {
reStr=prefix + literalizeRegex(x) + suffix;
} else if (isRegExp(x)) {
var s=x.toString().substring(1);
var sp=s.split('/');
flags=sp[sp.length-1];
sp[sp.length-1]='';
s=sp.join('/');
s=s.substring(0,s.length-1);
reStr= prefix + s + suffix;
} else {
log ('makeRegexp failed');
}
log ('makeRegexp: got reStr=' + reStr + ', flags=' + flags);
return RegExp(reStr, flags);
};
/**
@private
*/
Previewmaker.prototype.killBoxTemplates = function () {
// taxobox removal... in fact, there's a saudiprincebox_begin, so let's be more general
// also, have float_begin, ... float_end
this.kill(RegExp('[{][{][^{}\\s|]*?(float|box)[_ ](begin|start)', 'i'), /[}][}]\s*/, '{{');
// infoboxes etc
// from [[User:Zyxw/popups.js]]: kill frames too
this.kill(RegExp('[{][{][^{}\\s|]*?(infobox|elementbox|frame)[_ ]', 'i'), /[}][}]\s*/, '{{');
};
/**
@private
*/
Previewmaker.prototype.killTemplates = function () {
// the plus sign is to avoid transcluding [[Template:', ']]
this.kill('{'+'{', '}'+'}', '{', '}', ' ');
};
/**
@private
*/
Previewmaker.prototype.killTables = function () {
// tables are bad, too
// this can be slow, but it's an inprovement over a browser hang
// torture test: [[Comparison_of_Intel_Central_Processing_Units]]
this.kill('{|', /[|]}\s*/, '{|');
this.kill(/<table.*?>/i, /<\/table.*?>/i, /<table.*?>/i);
// remove lines starting with a pipe for the hell of it (?)
this.data=this.data.replace(RegExp('^[|].*$', 'mg'), '');
};
/**
@private
*/
Previewmaker.prototype.killImages = function () {
var forbiddenNamespaceAliases = [];
jQuery.each(mw.config.get('wgNamespaceIds'), function(_localizedNamespaceLc, _namespaceId) {
if (_namespaceId!=pg.nsImageId && _namespaceId!=pg.nsCategoryId) return;
forbiddenNamespaceAliases.push(_localizedNamespaceLc.split(' ').join('[ _]')); //todo: escape regexp fragments!
});
// images and categories are a nono
this.kill(RegExp('[[][[]\\s*(' + forbiddenNamespaceAliases.join('|') + ')\\s*:', 'i'),
/\]\]\s*/, '[', ']');
};
/**
@private
*/
Previewmaker.prototype.killHTML = function () {
// kill <ref ...>...</ref>
this.kill(/<ref\b[^/>]*?>/i, /<\/ref>/i);
// let's also delete entire lines starting with <. it's worth a try.
this.data=this.data.replace(RegExp('(^|\\n) *<.*', 'g'), '\n');
// and those pesky html tags, but not <nowiki> or <blockquote>
var splitted=this.data.parenSplit(/(<[\w\W]*?(?:>|$|(?=<)))/);
var len=splitted.length;
for (var i=1; i<len; i=i+2) {
switch (splitted[i]) {
case '<nowiki>':
case '</nowiki>':
case '<blockquote>':
case '</blockquote>':
break;
default:
splitted[i]='';
}
}
this.data=splitted.join('');
};
/**
@private
*/
Previewmaker.prototype.killChunks = function() { // heuristics alert
// chunks of italic text? you crazy, man?
var italicChunkRegex=new RegExp
("((^|\\n)\\s*:*\\s*''[^']([^']|'''|'[^']){20}(.|\\n[^\\n])*''[.!?\\s]*\\n)+", 'g');
// keep stuff separated, though, so stick in \n (fixes [[Union Jack]]?
this.data=this.data.replace(italicChunkRegex, '\n');
};
/**
@private
*/
Previewmaker.prototype.mopup = function () {
// we simply *can't* be doing with horizontal rules right now
this.data=this.data.replace(RegExp('^-{4,}','mg'),'');
// no indented lines
this.data=this.data.replace(RegExp('(^|\\n) *:[^\\n]*','g'), '');
// replace __TOC__, __NOTOC__ and whatever else there is
// this'll probably do
this.data=this.data.replace(RegExp('^__[A-Z_]*__ *$', 'gmi'),'');
};
/**
@private
*/
Previewmaker.prototype.firstBit = function () {
// dont't be givin' me no subsequent paragraphs, you hear me?
/// first we "normalize" section headings, removing whitespace after, adding before
var d=this.data;
if (getValueOf('popupPreviewCutHeadings')) {
this.data=this.data.replace(RegExp('\\s*(==+[^=]*==+)\\s*', 'g'), '\n\n$1 ');
/// then we want to get rid of paragraph breaks whose text ends badly
this.data=this.data.replace(RegExp('([:;]) *\\n{2,}', 'g'), '$1\n');
this.data=this.data.replace(RegExp('^[\\s\\n]*'), '');
var stuff=(RegExp('^([^\\n]|\\n[^\\n\\s])*')).exec(this.data);
if (stuff) { d = stuff[0]; }
if (!getValueOf('popupPreviewFirstParOnly')) { d = this.data; }
/// now put \n\n after sections so that bullets and numbered lists work
d=d.replace(RegExp('(==+[^=]*==+)\\s*', 'g'), '$1\n\n');
}
// Split sentences. Superfluous sentences are RIGHT OUT.
// note: exactly 1 set of parens here needed to make the slice work
d = d.parenSplit(RegExp('([!?.]+["'+"'"+']*\\s)','g'));
// leading space is bad, mmkay?
d[0]=d[0].replace(RegExp('^\\s*'), '');
var notSentenceEnds=RegExp('([^.][a-z][.] *[a-z]|etc|sic|Dr|Mr|Mrs|Ms|St|no|op|cit|\\[[^\\]]*|\\s[A-Zvclm])$', 'i');
d = this.fixSentenceEnds(d, notSentenceEnds);
this.fullLength=d.join('').length;
var n=this.maxSentences;
var dd=this.firstSentences(d,n);
do {
dd=this.firstSentences(d,n); --n;
} while ( dd.length > this.maxCharacters && n !== 0 );
this.data = dd;
};
/**
@private
*/
Previewmaker.prototype.fixSentenceEnds = function(strs, reg) {
// take an array of strings, strs
// join strs[i] to strs[i+1] & strs[i+2] if strs[i] matches regex reg
for (var i=0; i<strs.length-2; ++i) {
if (reg.test(strs[i])) {
var a=[];
for (var j=0; j<strs.length; ++j) {
if (j<i) a[j]=strs[j];
if (j==i) a[i]=strs[i]+strs[i+1]+strs[i+2];
if (j>i+2) a[j-2]=strs[j];
}
return this.fixSentenceEnds(a,reg);
}
}
return strs;
};
/**
@private
*/
Previewmaker.prototype.firstSentences = function(strs, howmany) {
var t=strs.slice(0, 2*howmany);
return t.join('');
};
/**
@private
*/
Previewmaker.prototype.killBadWhitespace = function() {
// also cleans up isolated '''', eg [[Suntory Sungoliath]]
this.data=this.data.replace(RegExp('^ *\'+ *$', 'gm'), '');
};
/**
Runs the various methods to generate the preview.
The preview is stored in the <code>html</html> field.
@private
*/
Previewmaker.prototype.makePreview = function() {
if (this.owner.article.namespaceId()!=pg.nsTemplateId &&
this.owner.article.namespaceId()!=pg.nsImageId ) {
this.killComments();
this.killDivs();
this.killGalleries();
this.killBoxTemplates();
if (getValueOf('popupPreviewKillTemplates')) {
this.killTemplates();
} else {
this.killMultilineTemplates();
}
this.killTables();
this.killImages();
this.killHTML();
this.killChunks();
this.mopup();
this.firstBit();
this.killBadWhitespace();
}
else
{
this.killHTML();
}
this.html=wiki2html(this.data, this.baseUrl); // needs livepreview
this.fixHTML();
this.stripLongTemplates();
};
/**
@private
*/
Previewmaker.prototype.esWiki2HtmlPart = function(data) {
var reLinks = /(?:\[\[([^|\]]*)(?:\|([^|\]]*))*]]([a-z]*))/gi; //match a wikilink
reLinks.lastIndex = 0; //reset regex
var match;
var result = "";
var postfixIndex = 0;
while ((match = reLinks.exec(data))) //match all wikilinks
{
//FIXME: the way that link is built here isn't perfect. It is clickable, but popups preview won't recognize it in some cases.
result += pg.escapeQuotesHTML(data.substring(postfixIndex, match.index)) +
'<a href="'+Insta.conf.paths.articles+pg.escapeQuotesHTML(match[1])+'">'+pg.escapeQuotesHTML((match[2]?match[2]:match[1])+match[3])+"</a>";
postfixIndex = reLinks.lastIndex;
}
//append the rest
result += pg.escapeQuotesHTML(data.substring(postfixIndex));
return result;
};
Previewmaker.prototype.editSummaryPreview=function() {
var reAes = /\/\* *(.*?) *\*\//g; //match the first section marker
reAes.lastIndex = 0; //reset regex
var match;
match = reAes.exec(this.data);
if (match)
{
//we have a section link. Split it, process it, combine it.
var prefix = this.data.substring(0,match.index-1);
var section = match[1];
var postfix = this.data.substring(reAes.lastIndex);
var start = "<span class='autocomment'>";
var end = "</span>";
if (prefix.length>0) start = this.esWiki2HtmlPart(prefix) + " " + start + "- ";
if (postfix.length>0) end = ": " + end + this.esWiki2HtmlPart(postfix);
var t=new Title().fromURL(this.baseUrl);
t.anchorFromUtf(section);
var sectionLink = Insta.conf.paths.articles + pg.escapeQuotesHTML(t.toString(true)) + '#' + pg.escapeQuotesHTML(t.anchor);
return start + '<a href="'+sectionLink+'">→</a> '+pg.escapeQuotesHTML(section) + end;
}
//else there's no section link, htmlify the whole thing.
return this.esWiki2HtmlPart(this.data);
};
//<NOLITE>
/** Test function for debugging preview problems one step at a time.
*/
/*eslint-disable */
function previewSteps(txt) {
try {
txt=txt || document.editform.wpTextbox1.value;
} catch (err) {
if (pg.cache.pages.length > 0) {
txt=pg.cache.pages[pg.cache.pages.length-1].data;
} else {
alert('provide text or use an edit page');
}
}
txt=txt.substring(0,10000);
var base=pg.wiki.articlebase + Title.fromURL(document.location.href).urlString();
var p=new Previewmaker(txt, base, pg.current.link.navpopup);
if (this.owner.article.namespaceId() != pg.nsTemplateId) {
p.killComments(); if (!confirm('done killComments(). Continue?\n---\n' + p.data)) { return; }
p.killDivs(); if (!confirm('done killDivs(). Continue?\n---\n' + p.data)) { return; }
p.killGalleries(); if (!confirm('done killGalleries(). Continue?\n---\n' + p.data)) { return; }
p.killBoxTemplates(); if (!confirm('done killBoxTemplates(). Continue?\n---\n' + p.data)) { return; }
if (getValueOf('popupPreviewKillTemplates')) {
p.killTemplates(); if (!confirm('done killTemplates(). Continue?\n---\n' + p.data)) { return; }
} else {
p.killMultilineTemplates(); if (!confirm('done killMultilineTemplates(). Continue?\n---\n' + p.data)) { return; }
}
p.killTables(); if (!confirm('done killTables(). Continue?\n---\n' + p.data)) { return; }
p.killImages(); if (!confirm('done killImages(). Continue?\n---\n' + p.data)) { return; }
p.killHTML(); if (!confirm('done killHTML(). Continue?\n---\n' + p.data)) { return; }
p.killChunks(); if (!confirm('done killChunks(). Continue?\n---\n' + p.data)) { return; }
p.mopup(); if (!confirm('done mopup(). Continue?\n---\n' + p.data)) { return; }
p.firstBit(); if (!confirm('done firstBit(). Continue?\n---\n' + p.data)) { return; }
p.killBadWhitespace(); if (!confirm('done killBadWhitespace(). Continue?\n---\n' + p.data)) { return; }
}
p.html=wiki2html(p.data, base); // needs livepreview
p.fixHTML(); if (!confirm('done fixHTML(). Continue?\n---\n' + p.html)) { return; }
p.stripLongTemplates(); if (!confirm('done stripLongTemplates(). Continue?\n---\n' + p.html)) { return; }
alert('finished preview - end result follows.\n---\n' + p.html);
}
/*eslint-enable */
//</NOLITE>
/**
Works around livepreview bugs.
@private
*/
Previewmaker.prototype.fixHTML = function() {
if(!this.html) return;
var ret = this.html;
// fix question marks in wiki links
// maybe this'll break some stuff :-(
ret=ret.replace(RegExp('(<a href="' + pg.wiki.articlePath + '/[^"]*)[?](.*?")', 'g'), '$1%3F$2');
ret=ret.replace(RegExp('(<a href=\'' + pg.wiki.articlePath + '/[^\']*)[?](.*?\')', 'g'), '$1%3F$2');
// FIXME fix up % too
this.html=ret;
};
/**
Generates the preview and displays it in the current popup.
Does nothing if the generated preview is invalid or consists of whitespace only.
Also activates wikilinks in the preview for subpopups if the popupSubpopups option is true.
*/
Previewmaker.prototype.showPreview = function () {
this.makePreview();
if (typeof this.html != typeof '') return;
if (RegExp('^\\s*$').test(this.html)) return;
setPopupHTML('<hr />', 'popupPrePreviewSep', this.owner.idNumber);
setPopupTipsAndHTML(this.html, 'popupPreview', this.owner.idNumber, { owner: this.owner });
var more = (this.fullLength > this.data.length) ? this.moreLink() : '';
setPopupHTML(more, 'popupPreviewMore', this.owner.idNumber);
};
/**
@private
*/
Previewmaker.prototype.moreLink=function() {
var a=document.createElement('a');
a.className='popupMoreLink';
a.innerHTML=popupString('more...');
var savedThis=this;
a.onclick=function() {
savedThis.maxCharacters+=2000;
savedThis.maxSentences+=20;
savedThis.setData();
savedThis.showPreview();
};
return a;
};
/**
@private
*/
Previewmaker.prototype.stripLongTemplates = function() {
// operates on the HTML!
this.html=this.html.replace(RegExp('^.{0,1000}[{][{][^}]*?(<(p|br)( /)?>\\s*){2,}([^{}]*?[}][}])?', 'gi'), '');
this.html=this.html.split('\n').join(' '); // workaround for <pre> templates
this.html=this.html.replace(RegExp('[{][{][^}]*<pre>[^}]*[}][}]','gi'), '');
};
/**
@private
*/
Previewmaker.prototype.killMultilineTemplates = function() {
this.kill('{{{', '}}}');
this.kill(RegExp('\\s*[{][{][^{}]*\\n'), '}}', '{{');
};
// ENDFILE: previewmaker.js
// STARTFILE: querypreview.js
function loadAPIPreview(queryType, article, navpop) {
var art=new Title(article).urlString();
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&';
var htmlGenerator=function(/*a, d*/){alert('invalid html generator');};
var usernameart = '';
switch (queryType) {
case 'history':
url += 'titles=' + art + '&prop=revisions&rvlimit=' +
getValueOf('popupHistoryPreviewLimit');
htmlGenerator=APIhistoryPreviewHTML;
break;
case 'category':
url += 'list=categorymembers&cmtitle=' + art;
htmlGenerator=APIcategoryPreviewHTML;
break;
case 'userinfo':
var username = new Title( article ).userName();
usernameart = encodeURIComponent( username );
if (pg.re.ipUser.test(username)) {
url += 'list=blocks&bkprop=range|restrictions&bkip=' + usernameart;
} else {
url += 'list=users|usercontribs&usprop=blockinfo|groups|editcount|registration|gender&ususers=' + usernameart + "&meta=globaluserinfo&guiprop=groups|unattached&guiuser="+ usernameart + "&uclimit=1&ucprop=timestamp&ucuser=" + usernameart;
}
htmlGenerator=APIuserInfoPreviewHTML;
break;
case 'contribs':
usernameart = encodeURIComponent( new Title( article ).userName() );
url += 'list=usercontribs&ucuser=' + usernameart +
'&uclimit=' + getValueOf('popupContribsPreviewLimit');
htmlGenerator=APIcontribsPreviewHTML;
break;
case 'imagepagepreview':
var trail='';
if (getValueOf('popupImageLinks')) { trail = '&list=imageusage&iutitle=' + art; }
url += 'titles=' + art + '&prop=revisions|imageinfo&rvprop=content' + trail;
htmlGenerator=APIimagepagePreviewHTML;
break;
case 'backlinks':
url += 'list=backlinks&bltitle=' + art;
htmlGenerator=APIbacklinksPreviewHTML;
break;
case 'revision':
if (article.oldid) {
url += 'revids=' + article.oldid;
} else {
url += 'titles=' + article.removeAnchor().urlString();
}
url += '&prop=revisions|pageprops|info|images|categories&rvprop=ids|timestamp|flags|comment|user|content&cllimit=max&imlimit=max';
htmlGenerator=APIrevisionPreviewHTML;
break;
}
pendingNavpopTask(navpop);
var callback=function(d){
log( "callback of API functions was hit" );
showAPIPreview(queryType, htmlGenerator(article,d,navpop), navpop.idNumber, navpop, d);
};
var go = function(){
getPageWithCaching(url, callback, navpop);
return true;
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'before', 'DOWNLOAD_'+queryType+'_QUERY_DATA'); }
}
function linkList(list) {
list.sort(function(x,y) { return (x==y ? 0 : (x<y ? -1 : 1)); });
var buf=[];
for (var i=0; i<list.length; ++i) {
buf.push(wikiLink({article: new Title(list[i]),
text: list[i].split(' ').join(' '),
action: 'view'}));
}
return buf.join(', ');
}
function getTimeOffset() {
var tz = mw.user.options.get('timecorrection');
if(tz) {
if( tz.indexOf('|') > -1 ) {
// New format
return parseInt(tz.split('|')[1],10);
} else if ( tz.indexOf(':') > -1 ) {
// Old format
return( parseInt(tz,10)*60 + parseInt(tz.split(':')[1],10) );
}
}
return 0;
}
/*
* Creates a HTML table that's shown in the history and user-contribs popups.
* @param {Object[]} h - a list of revisions, returned from the API
* @param {boolean} reallyContribs - true only if we're displaying user contributions
*/
function editPreviewTable(article, h, reallyContribs, timeOffset) {
var html=['<table>'];
var day=null;
var curart=article;
var page=null;
var makeFirstColumnLinks;
if(reallyContribs) {
// We're showing user contributions, so make (diff | hist) links
makeFirstColumnLinks = function(currentRevision) {
var result = '(';
result += '<a href="' + pg.wiki.titlebase +
new Title(currentRevision.title).urlString() + '&diff=prev' +
'&oldid=' + currentRevision.revid + '">' + popupString('diff') + '</a>';
result += ' | ';
result += '<a href="' + pg.wiki.titlebase +
new Title(currentRevision.title).urlString() + '&action=history">' +
popupString('hist') + '</a>';
result += ')';
return result;
};
} else {
// It's a regular history page, so make (cur | last) links
var firstRevid = h[0].revid;
makeFirstColumnLinks = function(currentRevision) {
var result = '(';
result += '<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&diff=' + firstRevid + '&oldid=' + currentRevision.revid + '">' + popupString('cur') + '</a>';
result += ' | ';
result += '<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&diff=prev&oldid=' + currentRevision.revid + '">' + popupString('last') + '</a>';
result += ')';
return result;
};
}
for (var i=0; i<h.length; ++i) {
if (reallyContribs) {
page = h[i].title;
curart = new Title(page);
}
var minor = h[i].minor ? '<b>m </b>' : '';
var editDate = adjustDate(getDateFromTimestamp(h[i].timestamp), timeOffset);
var thisDay = dayFormat(editDate);
var thisTime = timeFormat(editDate);
if (thisDay == day) {
thisDay = '';
} else {
day = thisDay;
}
if (thisDay) {
html.push( '<tr><td colspan=3><span class="popup_history_date">' +
thisDay+'</span></td></tr>' );
}
html.push('<tr class="popup_history_row_' + ( (i%2) ? 'odd' : 'even') + '">');
html.push('<td>' + makeFirstColumnLinks(h[i]) + '</td>');
html.push('<td>' +
'<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&oldid=' + h[i].revid + '">' + thisTime + '</a></td>');
var col3url='', col3txt='';
if (!reallyContribs) {
var user=h[i].user;
if( !h[i].userhidden ) {
if( pg.re.ipUser.test(user) ) {
col3url=pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId] + ':Contributions&target=' + new Title(user).urlString();
} else {
col3url=pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + new Title(user).urlString();
}
col3txt=pg.escapeQuotesHTML(user);
} else {
col3url=getValueOf('popupRevDelUrl');
col3txt=pg.escapeQuotesHTML( popupString('revdel'));
}
} else {
col3url=pg.wiki.titlebase + curart.urlString();
col3txt=pg.escapeQuotesHTML(page);
}
html.push('<td>' + (reallyContribs ? minor : '') +
'<a href="' + col3url + '">' + col3txt + '</a></td>');
var comment='';
var c=h[i].comment || h[i].content;
if (c) {
comment=new Previewmaker(c, new Title(curart).toUrl()).editSummaryPreview();
} else if ( h[i].commenthidden ) {
comment=popupString('revdel');
}
html.push('<td>' + (!reallyContribs ? minor : '') + comment + '</td>');
html.push('</tr>');
html=[html.join('')];
}
html.push('</table>');
return html.join('');
}
function getDateFromTimestamp(t) {
var s=t.split(/[^0-9]/);
switch(s.length) {
case 0: return null;
case 1: return new Date(s[0]);
case 2: return new Date(s[0], s[1]-1);
case 3: return new Date(s[0], s[1]-1, s[2]);
case 4: return new Date(s[0], s[1]-1, s[2], s[3]);
case 5: return new Date(s[0], s[1]-1, s[2], s[3], s[4]);
case 6: return new Date(s[0], s[1]-1, s[2], s[3], s[4], s[5]);
default: return new Date(s[0], s[1]-1, s[2], s[3], s[4], s[5], s[6]);
}
}
function adjustDate(d, offset) {
// offset is in minutes
var o=offset * 60 * 1000;
return new Date( +d + o);
}
function dayFormat(editDate, utc) {
if (utc) { return map(zeroFill, [editDate.getUTCFullYear(), editDate.getUTCMonth()+1, editDate.getUTCDate()]).join('-'); }
return map(zeroFill, [editDate.getFullYear(), editDate.getMonth()+1, editDate.getDate()]).join('-');
}
function timeFormat(editDate, utc) {
if (utc) { return map(zeroFill, [editDate.getUTCHours(), editDate.getUTCMinutes(), editDate.getUTCSeconds()]).join(':'); }
return map(zeroFill, [editDate.getHours(), editDate.getMinutes(), editDate.getSeconds()]).join(':');
}
function showAPIPreview(queryType, html, id, navpop, download) {
// DJ: done
var target='popupPreview';
completedNavpopTask(navpop);
switch (queryType) {
case 'imagelinks':
case 'category':
target='popupPostPreview'; break;
case 'userinfo':
target='popupUserData'; break;
case 'revision':
insertPreview(download);
return;
}
setPopupTipsAndHTML(html, target, id);
}
function APIrevisionPreviewHTML(article, download) {
try{
var jsObj=getJsObj(download.data);
var page=anyChild(jsObj.query.pages);
if( page.missing ) {
// TODO we need to fix this proper later on
download.owner = null;
return;
}
var content = (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
if( typeof content === 'string' )
{
download.data = content;
download.lastModified = new Date(page.revisions[0].timestamp);
}
} catch(someError) {
return 'Revision preview failed :(';
}
}
function APIbacklinksPreviewHTML(article, download/*, navpop*/ ) {
try {
var jsObj=getJsObj(download.data);
var list=jsObj.query.backlinks;
var html=[];
if (!list) { return popupString('No backlinks found'); }
for ( var i=0; i < list.length; i++ ) {
var t=new Title(list[i].title);
html.push('<a href="' + pg.wiki.titlebase + t.urlString() + '">' + t + '</a>');
}
html=html.join(', ');
if (jsObj['continue'] && jsObj['continue'].blcontinue) {
html += popupString(' and more');
}
return html;
} catch (someError) {
return 'backlinksPreviewHTML went wonky';
}
}
pg.fn.APIsharedImagePagePreviewHTML = function APIsharedImagePagePreviewHTML(obj) {
log( "APIsharedImagePagePreviewHTML" );
var popupid = obj.requestid;
if( obj.query && obj.query.pages )
{
var page=anyChild(obj.query.pages );
var content = (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
if( typeof content === 'string' )
{
/* Not entirely safe, but the best we can do */
var p=new Previewmaker(content, pg.current.link.navpopup.article, pg.current.link.navpopup);
p.makePreview();
setPopupHTML( p.html, "popupSecondPreview", popupid );
}
}
};
function APIimagepagePreviewHTML(article, download, navpop) {
try {
var jsObj=getJsObj(download.data);
var page=anyChild(jsObj.query.pages);
var content= (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
var ret='';
var alt='';
try{alt=navpop.parentAnchor.childNodes[0].alt;} catch(e){}
if (alt) {
ret = ret + '<hr /><b>' + popupString('Alt text:') + '</b> ' + pg.escapeQuotesHTML(alt);
}
if (typeof content === 'string') {
var p=prepPreviewmaker(content, article, navpop);
p.makePreview();
if (p.html) { ret += '<hr />' + p.html; }
if (getValueOf('popupSummaryData')) {
var info=getPageInfo(content, download);
log(info);
setPopupTrailer(info, navpop.idNumber);
}
}
if (page && page.imagerepository == "shared" ) {
var art=new Title(article);
var encart = encodeURIComponent( "File:" + art.stripNamespace() );
var shared_url = pg.wiki.apicommonsbase + '?format=json&formatversion=2' +
'&callback=pg.fn.APIsharedImagePagePreviewHTML' +
'&requestid=' + navpop.idNumber +
'&action=query&prop=revisions&rvprop=content&titles=' + encart;
ret = ret +'<hr />' + popupString( 'Image from Commons') +
': <a href="' + pg.wiki.commonsbase + '?title=' + encart + '">' +
popupString( 'Description page') + '</a>';
mw.loader.load( shared_url );
}
showAPIPreview('imagelinks', APIimagelinksPreviewHTML(article,download), navpop.idNumber, download);
return ret;
} catch (someError) {
return 'API imagepage preview failed :(';
}
}
function APIimagelinksPreviewHTML(article, download) {
try {
var jsobj=getJsObj(download.data);
var list=jsobj.query.imageusage;
if (list) {
var ret=[];
for (var i=0; i < list.length; i++) {
ret.push(list[i].title);
}
if (ret.length === 0) { return popupString('No image links found'); }
return '<h2>' + popupString('File links') + '</h2>' + linkList(ret);
} else {
return popupString('No image links found');
}
} catch(someError) {
return 'Image links preview generation failed :(';
}
}
function APIcategoryPreviewHTML(article, download) {
try{
var jsobj=getJsObj(download.data);
var list=jsobj.query.categorymembers;
var ret=[];
for (var p=0; p < list.length; p++) {
ret.push(list[p].title);
}
if (ret.length === 0) { return popupString('Empty category'); }
ret = '<h2>' + tprintf('Category members (%s shown)', [ret.length]) + '</h2>' +linkList(ret);
if (jsobj['continue'] && jsobj['continue'].cmcontinue) {
ret += popupString(' and more');
}
return ret;
} catch(someError) {
return 'Category preview failed :(';
}
}
function APIuserInfoPreviewHTML(article, download) {
var ret=[];
var queryobj = {};
try{
queryobj=getJsObj(download.data).query;
} catch(someError) { return 'Userinfo preview failed :('; }
var user=anyChild(queryobj.users);
if (user) {
var globaluserinfo=queryobj.globaluserinfo;
if (user.invalid === '') {
ret.push( popupString( 'Invalid user') );
} else if (user.missing === '') {
ret.push( popupString( 'Not a registered username') );
}
if( user.blockedby ) {
if( user.blockpartial ) {
ret.push('<b>' + popupString('Has blocks') + '</b>');
} else {
ret.push('<b>' + popupString('BLOCKED') + '</b>');
}
}
if( globaluserinfo && ( 'locked' in globaluserinfo || 'hidden' in globaluserinfo ) ) {
var lockedSulAccountIsAttachedToThis = true;
for( var i=0; globaluserinfo.unattached && i < globaluserinfo.unattached.length; i++) {
if ( globaluserinfo.unattached[i].wiki === mw.config.get('wgDBname') ) {
lockedSulAccountIsAttachedToThis=false;
break;
}
}
if (lockedSulAccountIsAttachedToThis) {
if ( 'locked' in globaluserinfo ) ret.push('<b><i>' + popupString('LOCKED') + '</i></b>');
if ( 'hidden' in globaluserinfo ) ret.push('<b><i>' + popupString('HIDDEN') + '</i></b>');
}
}
if( getValueOf('popupShowGender') && user.gender ) {
switch( user.gender ) {
case "male": ret.push( popupString( "\u2642" ) ); break;
case "female": ret.push( popupString( "\u2640" ) ); break;
}
}
if( user.groups ) {
for( var j=0; j < user.groups.length; j++) {
var currentGroup = user.groups[j];
if( ["*", "user", "autoconfirmed", "extendedconfirmed"].indexOf( currentGroup ) === -1 ) {
ret.push( pg.escapeQuotesHTML(user.groups[j]) );
}
}
}
if( globaluserinfo && globaluserinfo.groups ) {
for( var k=0; k < globaluserinfo.groups.length; k++) {
ret.push( '<i>'+pg.escapeQuotesHTML(globaluserinfo.groups[k])+'</i>' );
}
}
if( user.registration )
ret.push( pg.escapeQuotesHTML((user.editcount?user.editcount:'0') + popupString(' edits since: ') + (user.registration?dayFormat(getDateFromTimestamp(user.registration)):'')) );
}
if (queryobj.usercontribs && queryobj.usercontribs.length) {
ret.push( popupString('last edit on ') + dayFormat(getDateFromTimestamp(queryobj.usercontribs[0].timestamp)) );
}
if (queryobj.blocks) {
ret.push( popupString( 'IP user') ); //we only request list=blocks for IPs
for (var l=0; l<queryobj.blocks.length; l++) {
console.log(queryobj);
var rbstr = queryobj.blocks[l].rangestart === queryobj.blocks[l].rangeend ? 'BLOCK' : 'RANGEBLOCK';
rbstr = (!Array.isArray(queryobj.blocks[l].restrictions) ? 'Has ' + rbstr.toLowerCase() + 's' : rbstr + 'ED');
ret.push('<b>' + popupString(rbstr) + '</b>' );
}
}
ret = '<hr />' + ret.join( ', ' );
return ret;
}
function APIcontribsPreviewHTML(article, download, navpop) {
return APIhistoryPreviewHTML(article, download, navpop, true);
}
function APIhistoryPreviewHTML(article, download, navpop, reallyContribs) {
try {
var jsobj=getJsObj(download.data);
var edits = [];
if( reallyContribs ) {
edits=jsobj.query.usercontribs;
} else {
edits=anyChild(jsobj.query.pages).revisions;
}
var ret=editPreviewTable(article, edits, reallyContribs, getTimeOffset());
return ret;
} catch (someError) {
return 'History preview failed :-(';
}
}
//</NOLITE>
// ENDFILE: querypreview.js
// STARTFILE: debug.js
////////////////////////////////////////////////////////////////////
// Debugging functions
////////////////////////////////////////////////////////////////////
function setupDebugging() {
//<NOLITE>
if (window.popupDebug) { // popupDebug is set from .version
window.log=function(x) { //if(gMsg!='')gMsg += '\n'; gMsg+=time() + ' ' + x; };
window.console.log(x);
};
window.errlog=function(x) {
window.console.error(x);
};
log('Initializing logger');
} else {
//</NOLITE>
window.log = function() {};
window.errlog = function() {};
//<NOLITE>
}
//</NOLITE>
}
// ENDFILE: debug.js
// STARTFILE: images.js
// load image of type Title.
function loadImage(image, navpop) {
if (typeof image.stripNamespace != 'function') { alert('loadImages bad'); }
// API call to retrieve image info.
if ( !getValueOf('popupImages') ) return;
if ( !isValidImageName(image) ) return false;
var art=image.urlString();
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query';
url += '&prop=imageinfo&iiprop=url|mime&iiurlwidth=' + getValueOf('popupImageSizeLarge');
url += '&titles=' + art;
pendingNavpopTask(navpop);
var callback=function(d){
popupsInsertImage(navpop.idNumber, navpop, d);
};
var go = function(){
getPageWithCaching(url, callback, navpop);
return true;
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'after', 'DOWNLOAD_IMAGE_QUERY_DATA'); }
}
function popupsInsertImage(id, navpop, download) {
log( "popupsInsertImage");
var imageinfo;
try {
var jsObj=getJsObj(download.data);
var imagepage=anyChild(jsObj.query.pages);
if (typeof imagepage.imageinfo === 'undefined') return;
imageinfo = imagepage.imageinfo[0];
} catch (someError) {
log( "popupsInsertImage failed :(" );
return;
}
var popupImage = document.getElementById("popupImg"+id);
if (!popupImage) {
log( "could not find insertion point for image");
return;
}
popupImage.width=getValueOf('popupImageSize');
popupImage.style.display='inline';
// Set the source for the image.
if( imageinfo.thumburl )
popupImage.src=imageinfo.thumburl;
else if( imageinfo.mime.indexOf("image") === 0 ){
popupImage.src=imageinfo.url;
log( "a thumb could not be found, using original image" );
} else log( "fullsize imagethumb, but not sure if it's an image");
var a=document.getElementById("popupImageLink"+id);
if (a === null) { return null; }
// Determine the action of the surrouding imagelink.
switch (getValueOf('popupThumbAction')) {
case 'imagepage':
if (pg.current.article.namespaceId()!=pg.nsImageId) {
a.href=imageinfo.descriptionurl;
// FIXME: unreliable pg.idNumber
popTipsSoonFn('popupImage' + id)();
break;
}
/* falls through */
case 'sizetoggle':
a.onclick=toggleSize;
a.title=popupString('Toggle image size');
return;
case 'linkfull':
a.href = imageinfo.url;
a.title=popupString('Open full-size image');
return;
}
}
// Toggles the image between inline small and navpop fullwidth.
// It's the same image, no actual sizechange occurs, only display width.
function toggleSize() {
var imgContainer=this;
if (!imgContainer) {
alert('imgContainer is null :/');
return;
}
var img=imgContainer.firstChild;
if (!img) {
alert('img is null :/');
return;
}
if (!img.style.width || img.style.width==='') {
img.style.width='100%';
} else {
img.style.width='';
}
}
// Returns one title of an image from wikiText.
function getValidImageFromWikiText(wikiText) {
// nb in pg.re.image we're interested in the second bracketed expression
// this may change if the regex changes :-(
//var match=pg.re.image.exec(wikiText);
var matched=null;
var match;
// strip html comments, used by evil bots :-(
var t = removeMatchesUnless(wikiText, RegExp('(<!--[\\s\\S]*?-->)'), 1,
RegExp('^<!--[^[]*popup', 'i'));
while ( ( match = pg.re.image.exec(t) ) ) {
// now find a sane image name - exclude templates by seeking {
var m = match[2] || match[6];
if ( isValidImageName(m) ) {
matched=m;
break;
}
}
pg.re.image.lastIndex=0;
if (!matched) { return null; }
return mw.config.get('wgFormattedNamespaces')[pg.nsImageId]+':'+upcaseFirst(matched);
}
function removeMatchesUnless(str, re1, parencount, re2) {
var split=str.parenSplit(re1);
var c=parencount + 1;
for (var i=0; i<split.length; ++i) {
if ( i%c === 0 || re2.test(split[i]) ) { continue; }
split[i]='';
}
return split.join('');
}
//</NOLITE>
// ENDFILE: images.js
// STARTFILE: namespaces.js
// Set up namespaces and other non-strings.js localization
// (currently that means redirs too)
function setNamespaces() {
pg.nsSpecialId = -1;
pg.nsMainspaceId = 0;
pg.nsImageId = 6;
pg.nsUserId = 2;
pg.nsUsertalkId = 3;
pg.nsCategoryId = 14;
pg.nsTemplateId = 10;
}
function setRedirs() {
var r='redirect';
var R='REDIRECT';
var redirLists={
//<NOLITE>
'ar': [ R, 'تحويل' ],
'be': [ r, 'перанакіраваньне' ],
'bg': [ r, 'пренасочване', 'виж' ],
'bs': [ r, 'Preusmjeri', 'preusmjeri', 'PREUSMJERI' ],
'cs': [ R, 'PŘESMĚRUJ' ],
'cy': [ r, 'ail-cyfeirio' ],
'de': [ R, 'WEITERLEITUNG' ],
'el': [ R, 'ΑΝΑΚΑΤΕΥΘΥΝΣΗ'],
'eo': [ R, 'ALIDIREKTU', 'ALIDIREKTI' ],
'es': [ R, 'REDIRECCIÓN' ],
'et': [ r, 'suuna' ],
'ga': [ r, 'athsheoladh' ],
'gl': [ r, 'REDIRECCIÓN', 'REDIRECIONAMENTO'],
'he': [ R, 'הפניה' ],
'hu': [ R, 'ÁTIRÁNYÍTÁS' ],
'is': [ r, 'tilvísun', 'TILVÍSUN' ],
'it': [ R, 'RINVIA', 'Rinvia'],
'ja': [ R, '転送' ],
'mk': [ r, 'пренасочување', 'види' ],
'nds': [ r, 'wiederleiden' ],
'nl': [ R, 'DOORVERWIJZING' ],
'nn': [ r, 'omdiriger' ],
'pl': [ R, 'PATRZ', 'PRZEKIERUJ', 'TAM' ],
'pt': [ R, 'redir' ],
'ru': [ R, 'ПЕРЕНАПРАВЛЕНИЕ', 'ПЕРЕНАПР' ],
'sk': [ r, 'presmeruj' ],
'sr': [ r, 'Преусмери', 'преусмери', 'ПРЕУСМЕРИ', 'Preusmeri', 'preusmeri', 'PREUSMERI' ],
'tt': [ R, 'yünältü', 'перенаправление', 'перенапр' ],
'uk': [ R, 'ПЕРЕНАПРАВЛЕННЯ', 'ПЕРЕНАПР' ],
'vi': [ r, 'đổi' ],
'zh': [ R, '重定向'] // no comma
//</NOLITE>
};
var redirList=redirLists[ pg.wiki.lang ] || [r, R];
// Mediawiki is very tolerant about what comes after the #redirect at the start
pg.re.redirect=RegExp('^\\s*[#](' + redirList.join('|') + ').*?\\[{2}([^\\|\\]]*)(|[^\\]]*)?\\]{2}\\s*(.*)', 'i');
}
function setInterwiki() {
if (pg.wiki.wikimedia) {
// From https://meta.wikimedia.org/wiki/List_of_Wikipedias
pg.wiki.interwiki='aa|ab|ace|af|ak|als|am|an|ang|ar|arc|arz|as|ast|av|ay|az|ba|bar|bat-smg|bcl|be|be-x-old|bg|bh|bi|bjn|bm|bn|bo|bpy|br|bs|bug|bxr|ca|cbk-zam|cdo|ce|ceb|ch|cho|chr|chy|ckb|co|cr|crh|cs|csb|cu|cv|cy|da|de|diq|dsb|dv|dz|ee|el|eml|en|eo|es|et|eu|ext|fa|ff|fi|fiu-vro|fj|fo|fr|frp|frr|fur|fy|ga|gag|gan|gd|gl|glk|gn|got|gu|gv|ha|hak|haw|he|hi|hif|ho|hr|hsb|ht|hu|hy|hz|ia|id|ie|ig|ii|ik|ilo|io|is|it|iu|ja|jbo|jv|ka|kaa|kab|kbd|kg|ki|kj|kk|kl|km|kn|ko|koi|kr|krc|ks|ksh|ku|kv|kw|ky|la|lad|lb|lbe|lg|li|lij|lmo|ln|lo|lt|ltg|lv|map-bms|mdf|mg|mh|mhr|mi|mk|ml|mn|mo|mr|mrj|ms|mt|mus|mwl|my|myv|mzn|na|nah|nap|nds|nds-nl|ne|new|ng|nl|nn|no|nov|nrm|nv|ny|oc|om|or|os|pa|pag|pam|pap|pcd|pdc|pfl|pi|pih|pl|pms|pnb|pnt|ps|pt|qu|rm|rmy|rn|ro|roa-rup|roa-tara|ru|rue|rw|sa|sah|sc|scn|sco|sd|se|sg|sh|si|simple|sk|sl|sm|sn|so|sq|sr|srn|ss|st|stq|su|sv|sw|szl|ta|te|tet|tg|th|ti|tk|tl|tn|to|tpi|tr|ts|tt|tum|tw|ty|udm|ug|uk|ur|uz|ve|vec|vi|vls|vo|wa|war|wo|wuu|xal|xh|yi|yo|za|zea|zh|zh-classical|zh-min-nan|zh-yue|zu';
pg.re.interwiki=RegExp('^'+pg.wiki.interwiki+':');
} else {
pg.wiki.interwiki=null;
pg.re.interwiki=RegExp('^$');
}
}
// return a regexp pattern matching all variants to write the given namespace
function nsRe(namespaceId) {
var imageNamespaceVariants = [];
jQuery.each(mw.config.get('wgNamespaceIds'), function(_localizedNamespaceLc, _namespaceId) {
if (_namespaceId!=namespaceId) return;
_localizedNamespaceLc = upcaseFirst(_localizedNamespaceLc);
imageNamespaceVariants.push(mw.util.escapeRegExp(_localizedNamespaceLc).split(' ').join('[ _]'));
imageNamespaceVariants.push(mw.util.escapeRegExp(encodeURI(_localizedNamespaceLc)));
});
return '(?:' + imageNamespaceVariants.join('|') + ')';
}
function nsReImage() {
return nsRe(pg.nsImageId);
}
// ENDFILE: namespaces.js
// STARTFILE: selpop.js
//<NOLITE>
function getEditboxSelection() {
// see http://www.webgurusforum.com/8/12/0
var editbox;
try {
editbox=document.editform.wpTextbox1;
} catch (dang) { return; }
// IE, Opera
if (document.selection) { return document.selection.createRange().text; }
// Mozilla
var selStart = editbox.selectionStart;
var selEnd = editbox.selectionEnd;
return (editbox.value).substring(selStart, selEnd);
}
function doSelectionPopup() {
// popup if the selection looks like [[foo|anything afterwards at all
// or [[foo|bar]]text without ']]'
// or [[foo|bar]]
var sel=getEditboxSelection();
var open=sel.indexOf('[[');
var pipe=sel.indexOf('|');
var close=sel.indexOf(']]');
if (open == -1 || ( pipe == -1 && close == -1) ) { return; }
if (pipe != -1 && open > pipe || close != -1 && open > close) { return; }
if (getValueOf('popupOnEditSelection')=='boxpreview') {
return doSeparateSelectionPopup(sel);
}
var article=new Title(sel.substring(open+2, (pipe < 0) ? close : pipe)).urlString();
if (close > 0 && sel.substring(close+2).indexOf('[[') >= 0) {
return;
}
var a=document.createElement('a');
a.href=pg.wiki.titlebase + article;
mouseOverWikiLink2(a);
if (a.navpopup) {
a.navpopup.addHook(function(){runStopPopupTimer(a.navpopup);}, 'unhide', 'after');
}
}
function doSeparateSelectionPopup(str) {
var div=document.getElementById('selectionPreview');
if (!div) {
div = document.createElement('div');
div.id='selectionPreview';
try {
var box=document.editform.wpTextbox1;
box.parentNode.insertBefore(div, box);
} catch (error) {
return;
}
}
div.innerHTML=wiki2html(str);
div.ranSetupTooltipsAlready = false;
popTipsSoonFn('selectionPreview')();
}
//</NOLITE>
// ENDFILE: selpop.js
// STARTFILE: navpopup.js
/**
@fileoverview Defines two classes: {@link Navpopup} and {@link Mousetracker}.
<code>Navpopup</code> describes popups: when they appear, where, what
they look like and so on.
<code>Mousetracker</code> "captures" the mouse using
<code>document.onmousemove</code>.
*/
/**
Creates a new Mousetracker.
@constructor
@class The Mousetracker class. This monitors mouse movements and manages associated hooks.
*/
function Mousetracker() {
/**
Interval to regularly run the hooks anyway, in milliseconds.
@type Integer
*/
this.loopDelay=400;
/**
Timer for the loop.
@type Timer
*/
this.timer=null;
/**
Flag - are we switched on?
@type Boolean
*/
this.active=false;
/**
Flag - are we probably inaccurate, i.e. not reflecting the actual mouse position?
*/
this.dirty=true;
/**
Array of hook functions.
@private
@type Array
*/
this.hooks=[];
}
/**
Adds a hook, to be called when we get events.
@param {Function} f A function which is called as
<code>f(x,y)</code>. It should return <code>true</code> when it
wants to be removed, and <code>false</code> otherwise.
*/
Mousetracker.prototype.addHook = function (f) {
this.hooks.push(f);
};
/**
Runs hooks, passing them the x
and y coords of the mouse. Hook functions that return true are
passed to {@link Mousetracker#removeHooks} for removal.
@private
*/
Mousetracker.prototype.runHooks = function () {
if (!this.hooks || !this.hooks.length) { return; }
//log('Mousetracker.runHooks; we got some hooks to run');
var remove=false;
var removeObj={};
// this method gets called a LOT -
// pre-cache some variables
var x=this.x, y=this.y, len = this.hooks.length;
for (var i=0; i<len; ++i) {
//~ run the hook function, and remove it if it returns true
if (this.hooks[i](x, y)===true) {
remove=true;
removeObj[i]=true;
}
}
if (remove) { this.removeHooks(removeObj); }
};
/**
Removes hooks.
@private
@param {Object} removeObj An object whose keys are the index
numbers of functions for removal, with values that evaluate to true
*/
Mousetracker.prototype.removeHooks = function(removeObj) {
var newHooks=[];
var len = this.hooks.length;
for (var i=0; i<len; ++i) {
if (! removeObj[i]) { newHooks.push(this.hooks[i]); }
}
this.hooks=newHooks;
};
/**
Event handler for mouse wiggles.
We simply grab the event, set x and y and run the hooks.
This makes the cpu all hot and bothered :-(
@private
@param {Event} e Mousemove event
*/
Mousetracker.prototype.track=function (e) {
//~ Apparently this is needed in IE.
e = e || window.event;
var x, y;
if (e) {
if (e.pageX) { x=e.pageX; y=e.pageY; }
else if (typeof e.clientX!='undefined') {
var left, top, docElt = document.documentElement;
if (docElt) { left=docElt.scrollLeft; }
left = left || document.body.scrollLeft || document.scrollLeft || 0;
if (docElt) { top=docElt.scrollTop; }
top = top || document.body.scrollTop || document.scrollTop || 0;
x=e.clientX + left;
y=e.clientY + top;
} else { return; }
this.setPosition(x,y);
}
};
/**
Sets the x and y coordinates stored and takes appropriate action,
running hooks as appropriate.
@param {Integer} x, y Screen coordinates to set
*/
Mousetracker.prototype.setPosition=function(x,y) {
this.x = x;
this.y = y;
if (this.dirty || this.hooks.length === 0) { this.dirty=false; return; }
if (typeof this.lastHook_x != 'number') { this.lastHook_x = -100; this.lastHook_y=-100; }
var diff = (this.lastHook_x - x)*(this.lastHook_y - y);
diff = (diff >= 0) ? diff : -diff;
if ( diff > 1 ) {
this.lastHook_x=x;
this.lastHook_y=y;
if (this.dirty) { this.dirty = false; }
else { this.runHooks(); }
}
};
/**
Sets things in motion, unless they are already that is, registering an event handler on <code>document.onmousemove</code>.
A half-hearted attempt is made to preserve the old event handler if there is one.
*/
Mousetracker.prototype.enable = function () {
if (this.active) { return; }
this.active=true;
//~ Save the current handler for mousemove events. This isn't too
//~ robust, of course.
this.savedHandler=document.onmousemove;
//~ Gotta save @tt{this} again for the closure, and use apply for
//~ the member function.
var savedThis=this;
document.onmousemove=function (e) {savedThis.track.apply(savedThis, [e]);};
if (this.loopDelay) { this.timer = setInterval(function() { //log('loop delay in mousetracker is working');
savedThis.runHooks();}, this.loopDelay); }
};
/**
Disables the tracker, removing the event handler.
*/
Mousetracker.prototype.disable = function () {
if (!this.active) { return; }
if (typeof this.savedHandler === 'function') {
document.onmousemove=this.savedHandler;
} else { delete document.onmousemove; }
if (this.timer) { clearInterval(this.timer); }
this.active=false;
};
/**
Creates a new Navpopup.
Gets a UID for the popup and
@param init Contructor object. If <code>init.draggable</code> is true or absent, the popup becomes draggable.
@constructor
@class The Navpopup class. This generates popup hints, and does some management of them.
*/
function Navpopup(/*init*/) {
//alert('new Navpopup(init)');
/** UID for each Navpopup instance.
Read-only.
@type integer
*/
this.uid=Navpopup.uid++;
/**
Read-only flag for current visibility of the popup.
@type boolean
@private
*/
this.visible=false;
/** Flag to be set when we want to cancel a previous request to
show the popup in a little while.
@private
@type boolean
*/
this.noshow=false;
/** Categorised list of hooks.
@see #runHooks
@see #addHook
@private
@type Object
*/
this.hooks={
'create': [],
'unhide': [],
'hide': []
};
/** list of unique IDs of hook functions, to avoid duplicates
@private
*/
this.hookIds={};
/** List of downloads associated with the popup.
@private
@type Array
*/
this.downloads=[];
/** Number of uncompleted downloads.
@type integer
*/
this.pending=null;
/** Tolerance in pixels when detecting whether the mouse has left the popup.
@type integer
*/
this.fuzz=5;
/** Flag to toggle running {@link #limitHorizontalPosition} to regulate the popup's position.
@type boolean
*/
this.constrained=true;
/** The popup width in pixels.
@private
@type integer
*/
this.width=0;
/** The popup width in pixels.
@private
@type integer
*/
this.height=0;
/** The main content DIV element.
@type HTMLDivElement
*/
this.mainDiv=null;
this.createMainDiv();
// if (!init || typeof init.popups_draggable=='undefined' || init.popups_draggable) {
// this.makeDraggable(true);
// }
}
/**
A UID for each Navpopup. This constructor property is just a counter.
@type integer
@private
*/
Navpopup.uid=0;
/**
Retrieves the {@link #visible} attribute, indicating whether the popup is currently visible.
@type boolean
*/
Navpopup.prototype.isVisible=function() {
return this.visible;
};
/**
Repositions popup using CSS style.
@private
@param {integer} x x-coordinate (px)
@param {integer} y y-coordinate (px)
@param {boolean} noLimitHor Don't call {@link #limitHorizontalPosition}
*/
Navpopup.prototype.reposition= function (x,y, noLimitHor) {
log ('reposition('+x+','+y+','+noLimitHor+')');
if (typeof x != 'undefined' && x !== null) { this.left=x; }
if (typeof y != 'undefined' && y !== null) { this.top=y; }
if (typeof this.left != 'undefined' && typeof this.top != 'undefined') {
this.mainDiv.style.left=this.left + 'px';
this.mainDiv.style.top=this.top + 'px';
}
if (!noLimitHor) { this.limitHorizontalPosition(); }
//console.log('navpop'+this.uid+' - (left,top)=(' + this.left + ',' + this.top + '), css=('
//+ this.mainDiv.style.left + ',' + this.mainDiv.style.top + ')');
};
/**
Prevents popups from being in silly locations. Hopefully.
Should not be run if {@link #constrained} is true.
@private
*/
Navpopup.prototype.limitHorizontalPosition=function() {
if (!this.constrained || this.tooWide) { return; }
this.updateDimensions();
var x=this.left;
var w=this.width;
var cWidth=document.body.clientWidth;
// log('limitHorizontalPosition: x='+x+
// ', this.left=' + this.left +
// ', this.width=' + this.width +
// ', cWidth=' + cWidth);
if ( (x+w) >= cWidth ||
( x > 0 &&
this.maxWidth &&
this.width < this.maxWidth &&
this.height > this.width &&
x > cWidth - this.maxWidth ) ) {
// This is a very nasty hack. There has to be a better way!
// We find the "natural" width of the div by positioning it at the far left
// then reset it so that it should be flush right (well, nearly)
this.mainDiv.style.left='-10000px';
this.mainDiv.style.width = this.maxWidth + 'px';
var naturalWidth=parseInt(this.mainDiv.offsetWidth, 10);
var newLeft=cWidth - naturalWidth - 1;
if (newLeft < 0) { newLeft = 0; this.tooWide=true; } // still unstable for really wide popups?
log ('limitHorizontalPosition: moving to ('+newLeft + ','+ this.top+');' + ' naturalWidth=' + naturalWidth + ', clientWidth=' + cWidth);
this.reposition(newLeft, null, true);
}
};
/**
Counter indicating the z-order of the "highest" popup.
We start the z-index at 1000 so that popups are above everything
else on the screen.
@private
@type integer
*/
Navpopup.highest=1000;
/**
Brings popup to the top of the z-order.
We increment the {@link #highest} property of the contructor here.
@private
*/
Navpopup.prototype.raise = function () {
this.mainDiv.style.zIndex=Navpopup.highest + 1;
++Navpopup.highest;
};
/**
Shows the popup provided {@link #noshow} is not true.
Updates the position, brings the popup to the top of the z-order and unhides it.
*/
Navpopup.prototype.show = function () {
//document.title+='s';
if (this.noshow) { return; }
//document.title+='t';
this.reposition();
this.raise();
this.unhide();
};
/**
Checks to see if the mouse pointer has
stabilised (checking every <code>time</code>/2 milliseconds) and runs the
{@link #show} method if it has.
@param {integer} time The minimum time (ms) before the popup may be shown.
*/
Navpopup.prototype.showSoonIfStable = function (time) {
log ('showSoonIfStable, time='+time);
if (this.visible) { return; }
this.noshow = false;
//~ initialize these variables so that we never run @tt{show} after
//~ just half the time
this.stable_x = -10000; this.stable_y = -10000;
var stableShow = function() {
log('stableShow called');
var new_x = Navpopup.tracker.x, new_y = Navpopup.tracker.y;
var dx = savedThis.stable_x - new_x, dy = savedThis.stable_y - new_y;
var fuzz2 = 0; // savedThis.fuzz * savedThis.fuzz;
//document.title += '[' + [savedThis.stable_x,new_x, savedThis.stable_y,new_y, dx, dy, fuzz2].join(',') + '] ';
if ( dx * dx <= fuzz2 && dy * dy <= fuzz2 ) {
log ('mouse is stable');
clearInterval(savedThis.showSoonStableTimer);
savedThis.reposition.apply(savedThis, [new_x + 2, new_y + 2]);
savedThis.show.apply(savedThis, []);
savedThis.limitHorizontalPosition.apply(savedThis, []);
return;
}
savedThis.stable_x = new_x; savedThis.stable_y = new_y;
};
var savedThis = this;
this.showSoonStableTimer = setInterval(stableShow, time/2);
};
/**
Sets the {@link #noshow} flag and hides the popup. This should be called
when the mouse leaves the link before
(or after) it's actually been displayed.
*/
Navpopup.prototype.banish = function () {
log ('banish called');
// hide and prevent showing with showSoon in the future
this.noshow=true;
if (this.showSoonStableTimer) {
log('clearing showSoonStableTimer');
clearInterval(this.showSoonStableTimer);
}
this.hide();
};
/**
Runs hooks added with {@link #addHook}.
@private
@param {String} key Key name of the {@link #hooks} array - one of 'create', 'unhide', 'hide'
@param {String} when Controls exactly when the hook is run: either 'before' or 'after'
*/
Navpopup.prototype.runHooks = function (key, when) {
if (!this.hooks[key]) { return; }
var keyHooks=this.hooks[key];
var len=keyHooks.length;
for (var i=0; i< len; ++i) {
if (keyHooks[i] && keyHooks[i].when == when) {
if (keyHooks[i].hook.apply(this, [])) {
// remove the hook
if (keyHooks[i].hookId) {
delete this.hookIds[keyHooks[i].hookId];
}
keyHooks[i]=null;
}
}
}
};
/**
Adds a hook to the popup. Hook functions are run with <code>this</code> set to refer to the Navpopup instance, and no arguments.
@param {Function} hook The hook function. Functions that return true are deleted.
@param {String} key Key name of the {@link #hooks} array - one of 'create', 'unhide', 'hide'
@param {String} when Controls exactly when the hook is run: either 'before' or 'after'
@param {String} uid A truthy string identifying the hook function; if it matches another hook in this position, it won't be added again.
*/
Navpopup.prototype.addHook = function ( hook, key, when, uid ) {
when = when || 'after';
if (!this.hooks[key]) { return; }
// if uid is specified, don't add duplicates
var hookId=null;
if (uid) {
hookId=[key,when,uid].join('|');
if (this.hookIds[hookId]) {
return;
}
this.hookIds[hookId]=true;
}
this.hooks[key].push( {hook: hook, when: when, hookId: hookId} );
};
/**
Creates the main DIV element, which contains all the actual popup content.
Runs hooks with key 'create'.
@private
*/
Navpopup.prototype.createMainDiv = function () {
if (this.mainDiv) { return; }
this.runHooks('create', 'before');
var mainDiv=document.createElement('div');
var savedThis=this;
mainDiv.onclick=function(e) {savedThis.onclickHandler(e);};
mainDiv.className=(this.className) ? this.className : 'navpopup_maindiv';
mainDiv.id=mainDiv.className + this.uid;
mainDiv.style.position='absolute';
mainDiv.style.minWidth = '350px';
mainDiv.style.display='none';
mainDiv.className='navpopup';
// easy access to javascript object through DOM functions
mainDiv.navpopup=this;
this.mainDiv=mainDiv;
document.body.appendChild(mainDiv);
this.runHooks('create', 'after');
};
/**
Calls the {@link #raise} method.
@private
*/
Navpopup.prototype.onclickHandler=function(/*e*/) {
this.raise();
};
/**
Makes the popup draggable, using a {@link Drag} object.
@private
*/
Navpopup.prototype.makeDraggable=function(handleName) {
if (!this.mainDiv) { this.createMainDiv(); }
var drag=new Drag();
if (!handleName) {
drag.startCondition=function(e) {
try { if (!e.shiftKey) { return false; } } catch (err) { return false; }
return true;
};
}
var dragHandle;
if (handleName) dragHandle = document.getElementById(handleName);
if (!dragHandle) dragHandle = this.mainDiv;
var np=this;
drag.endHook=function(x,y) {
Navpopup.tracker.dirty=true;
np.reposition(x,y);
};
drag.init(dragHandle,this.mainDiv);
};
/** Hides the popup using CSS. Runs hooks with key 'hide'.
Sets {@link #visible} appropriately. {@link #banish} should be called externally instead of this method.
@private
*/
Navpopup.prototype.hide = function () {
this.runHooks('hide', 'before');
this.abortDownloads();
if (typeof this.visible != 'undefined' && this.visible) {
this.mainDiv.style.display='none';
this.visible=false;
}
this.runHooks('hide', 'after');
};
/** Shows the popup using CSS. Runs hooks with key 'unhide'.
Sets {@link #visible} appropriately. {@link #show} should be called externally instead of this method.
@private
*/
Navpopup.prototype.unhide = function () {
this.runHooks('unhide', 'before');
if (typeof this.visible != 'undefined' && !this.visible) {
this.mainDiv.style.display='inline';
this.visible=true;
}
this.runHooks('unhide', 'after');
};
/**
Sets the <code>innerHTML</code> attribute of the main div containing the popup content.
@param {String} html The HTML to set.
*/
Navpopup.prototype.setInnerHTML = function (html) {
this.mainDiv.innerHTML = html;
};
/**
Updates the {@link #width} and {@link #height} attributes with the CSS properties.
@private
*/
Navpopup.prototype.updateDimensions = function () {
this.width=parseInt(this.mainDiv.offsetWidth, 10);
this.height=parseInt(this.mainDiv.offsetHeight, 10);
};
/**
Checks if the point (x,y) is within {@link #fuzz} of the
{@link #mainDiv}.
@param {integer} x x-coordinate (px)
@param {integer} y y-coordinate (px)
@type boolean
*/
Navpopup.prototype.isWithin = function(x,y) {
//~ If we're not even visible, no point should be considered as
//~ being within the popup.
if (!this.visible) { return false; }
this.updateDimensions();
var fuzz=this.fuzz || 0;
//~ Use a simple box metric here.
return (x+fuzz >= this.left && x-fuzz <= this.left + this.width &&
y+fuzz >= this.top && y-fuzz <= this.top + this.height);
};
/**
Adds a download to {@link #downloads}.
@param {Downloader} download
*/
Navpopup.prototype.addDownload=function(download) {
if (!download) { return; }
this.downloads.push(download);
};
/**
Aborts the downloads listed in {@link #downloads}.
@see Downloader#abort
*/
Navpopup.prototype.abortDownloads=function() {
for(var i=0; i<this.downloads.length; ++i) {
var d=this.downloads[i];
if (d && d.abort) { d.abort(); }
}
this.downloads=[];
};
/**
A {@link Mousetracker} instance which is a property of the constructor (pseudo-global).
*/
Navpopup.tracker=new Mousetracker();
// ENDFILE: navpopup.js
// STARTFILE: diff.js
//<NOLITE>
/*
* Javascript Diff Algorithm
* By John Resig (http://ejohn.org/) and [[:en:User:Lupin]]
*
* More Info:
* http://ejohn.org/projects/javascript-diff-algorithm/
*/
function delFmt(x) {
if (!x.length) { return ''; }
return "<del class='popupDiff'>" + x.join('') +"</del>";
}
function insFmt(x) {
if (!x.length) { return ''; }
return "<ins class='popupDiff'>" + x.join('') +"</ins>";
}
function countCrossings(a, b, i, eject) {
// count the crossings on the edge starting at b[i]
if (!b[i].row && b[i].row !== 0) { return -1; }
var count=0;
for (var j=0; j<a.length; ++j) {
if (!a[j].row && a[j].row !== 0) { continue; }
if ( (j-b[i].row)*(i-a[j].row) > 0) {
if(eject) { return true; }
count++;
}
}
return count;
}
function shortenDiffString(str, context) {
var re=RegExp('(<del[\\s\\S]*?</del>|<ins[\\s\\S]*?</ins>)');
var splitted=str.parenSplit(re);
var ret=[''];
for (var i=0; i<splitted.length; i+=2) {
if (splitted[i].length < 2*context) {
ret[ret.length-1] += splitted[i];
if (i+1<splitted.length) { ret[ret.length-1] += splitted[i+1]; }
continue;
}
else {
if (i > 0) { ret[ret.length-1] += splitted[i].substring(0,context); }
if (i+1 < splitted.length) {
ret.push(splitted[i].substring(splitted[i].length-context) +
splitted[i+1]);
}
}
}
while (ret.length > 0 && !ret[0]) { ret = ret.slice(1); }
return ret;
}
function diffString( o, n, simpleSplit ) {
var splitRe=RegExp('([[]{2}|[\\]]{2}|[{]{2,3}|[}]{2,3}|[|]|=|<|>|[*:]+|\\s|\\b)');
// We need to split the strings o and n first, and entify() the parts
// individually, so that the HTML entities are never cut apart. (AxelBoldt)
var out, i, oSplitted, nSplitted;
if (simpleSplit) {
oSplitted=o.split(/\b/);
nSplitted=n.split(/\b/);
} else {
oSplitted=o.parenSplit(splitRe);
nSplitted=n.parenSplit(splitRe);
}
for (i=0; i<oSplitted.length; ++i) {oSplitted[i]=oSplitted[i].entify();}
for (i=0; i<nSplitted.length; ++i) {nSplitted[i]=nSplitted[i].entify();}
out = diff (oSplitted, nSplitted);
var str = "";
var acc=[]; // accumulator for prettier output
// crossing pairings -- eg 'A B' vs 'B A' -- cause problems, so let's iron them out
// this doesn't always do things optimally but it should be fast enough
var maxOutputPair=0;
for (i=0; i<out.n.length; ++i) {
if ( out.n[i].paired ) {
if( maxOutputPair > out.n[i].row ) {
// tangle - delete pairing
out.o[ out.n[i].row ]=out.o[ out.n[i].row ].text;
out.n[i]=out.n[i].text;
}
if (maxOutputPair < out.n[i].row) { maxOutputPair = out.n[i].row; }
}
}
// output the stuff preceding the first paired old line
for (i=0; i<out.o.length && !out.o[i].paired; ++i) { acc.push( out.o[i] ); }
str += delFmt(acc); acc=[];
// main loop
for ( i = 0; i < out.n.length; ++i ) {
// output unpaired new "lines"
while ( i < out.n.length && !out.n[i].paired ) { acc.push( out.n[i++] ); }
str += insFmt(acc); acc=[];
if ( i < out.n.length ) { // this new "line" is paired with the (out.n[i].row)th old "line"
str += out.n[i].text;
// output unpaired old rows starting after this new line's partner
var m = out.n[i].row + 1;
while ( m < out.o.length && !out.o[m].paired ) { acc.push ( out.o[m++] ); }
str += delFmt(acc); acc=[];
}
}
return str;
}
// see http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Object
// FIXME: use obj.hasOwnProperty instead of this kludge!
var jsReservedProperties=RegExp('^(constructor|prototype|__((define|lookup)[GS]etter)__' +
'|eval|hasOwnProperty|propertyIsEnumerable' +
'|to(Source|String|LocaleString)|(un)?watch|valueOf)$');
function diffBugAlert(word) {
if (!diffBugAlert.list[word]) {
diffBugAlert.list[word]=1;
alert('Bad word: '+word+'\n\nPlease report this bug.');
}
}
diffBugAlert.list={};
function makeDiffHashtable(src) {
var ret={};
for ( var i = 0; i < src.length; i++ ) {
if ( jsReservedProperties.test(src[i]) ) { src[i] += '<!-- -->'; }
if ( !ret[ src[i] ] ) { ret[ src[i] ] = []; }
try { ret[ src[i] ].push( i ); } catch (err) { diffBugAlert(src[i]); }
}
return ret;
}
function diff( o, n ) {
// pass 1: make hashtable ns with new rows as keys
var ns = makeDiffHashtable(n);
// pass 2: make hashtable os with old rows as keys
var os = makeDiffHashtable(o);
// pass 3: pair unique new rows and matching unique old rows
var i;
for ( i in ns ) {
if ( ns[i].length == 1 && os[i] && os[i].length == 1 ) {
n[ ns[i][0] ] = { text: n[ ns[i][0] ], row: os[i][0], paired: true };
o[ os[i][0] ] = { text: o[ os[i][0] ], row: ns[i][0], paired: true };
}
}
// pass 4: pair matching rows immediately following paired rows (not necessarily unique)
for ( i = 0; i < n.length - 1; i++ ) {
if ( n[i].paired && ! n[i+1].paired && n[i].row + 1 < o.length && ! o[ n[i].row + 1 ].paired &&
n[i+1] == o[ n[i].row + 1 ] ) {
n[i+1] = { text: n[i+1], row: n[i].row + 1, paired: true };
o[n[i].row+1] = { text: o[n[i].row+1], row: i + 1, paired: true };
}
}
// pass 5: pair matching rows immediately preceding paired rows (not necessarily unique)
for ( i = n.length - 1; i > 0; i-- ) {
if ( n[i].paired && ! n[i-1].paired && n[i].row > 0 && ! o[ n[i].row - 1 ].paired &&
n[i-1] == o[ n[i].row - 1 ] ) {
n[i-1] = { text: n[i-1], row: n[i].row - 1, paired: true };
o[n[i].row-1] = { text: o[n[i].row-1], row: i - 1, paired: true };
}
}
return { o: o, n: n };
}
//</NOLITE>
// ENDFILE: diff.js
// STARTFILE: init.js
function setSiteInfo() {
if (window.popupLocalDebug) {
pg.wiki.hostname = 'en.wikipedia.org';
} else {
pg.wiki.hostname = location.hostname; // use in preference to location.hostname for flexibility (?)
}
pg.wiki.wikimedia=RegExp('(wiki([pm]edia|source|books|news|quote|versity)|wiktionary|mediawiki)[.]org').test(pg.wiki.hostname);
pg.wiki.wikia=RegExp('[.]wikia[.]com$', 'i').test(pg.wiki.hostname);
pg.wiki.isLocal=RegExp('^localhost').test(pg.wiki.hostname);
pg.wiki.commons=( pg.wiki.wikimedia && pg.wiki.hostname != 'commons.wikimedia.org') ? 'commons.wikimedia.org' : null;
pg.wiki.lang = mw.config.get('wgContentLanguage');
var port = location.port ? ':' + location.port : '';
pg.wiki.sitebase = pg.wiki.hostname + port;
}
function setUserInfo() {
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'query',
list: 'users',
ususers: mw.config.get('wgUserName'),
usprop: 'rights'
};
pg.user.canReview = false;
if (getValueOf('popupReview')) {
api.get(params).done(function(data){
var rights = data.query.users[0].rights;
pg.user.canReview = rights.indexOf('review') !== -1; // TODO: Should it be a getValueOf('ReviewRight') ?
});
}
}
function setTitleBase() {
var protocol = ( window.popupLocalDebug ? 'http:' : location.protocol );
pg.wiki.articlePath = mw.config.get('wgArticlePath').replace(/\/\$1/, ""); // as in http://some.thing.com/wiki/Article
pg.wiki.botInterfacePath = mw.config.get('wgScript');
pg.wiki.APIPath = mw.config.get('wgScriptPath') +"/api.php";
// default mediawiki setting is paths like http://some.thing.com/articlePath/index.php?title=foo
var titletail = pg.wiki.botInterfacePath + '?title=';
//var titletail2 = joinPath([pg.wiki.botInterfacePath, 'wiki.phtml?title=']);
// other sites may need to add code here to set titletail depending on how their urls work
pg.wiki.titlebase = protocol + '//' + pg.wiki.sitebase + titletail;
//pg.wiki.titlebase2 = protocol + '//' + joinPath([pg.wiki.sitebase, titletail2]);
pg.wiki.wikibase = protocol + '//' + pg.wiki.sitebase + pg.wiki.botInterfacePath;
pg.wiki.apiwikibase = protocol + '//' + pg.wiki.sitebase + pg.wiki.APIPath;
pg.wiki.articlebase = protocol + '//' + pg.wiki.sitebase + pg.wiki.articlePath;
pg.wiki.commonsbase = protocol + '//' + pg.wiki.commons + pg.wiki.botInterfacePath;
pg.wiki.apicommonsbase = protocol + '//' + pg.wiki.commons + pg.wiki.APIPath;
pg.re.basenames = RegExp( '^(' +
map( literalizeRegex, [ pg.wiki.titlebase, //pg.wiki.titlebase2,
pg.wiki.articlebase ]).join('|') + ')' );
}
//////////////////////////////////////////////////
// Global regexps
function setMainRegex() {
var reStart='[^:]*://';
var preTitles = literalizeRegex( mw.config.get('wgScriptPath') ) + '/(?:index[.]php|wiki[.]phtml)[?]title=';
preTitles += '|' + literalizeRegex( pg.wiki.articlePath + '/' );
var reEnd='(' + preTitles + ')([^&?#]*)[^#]*(?:#(.+))?';
pg.re.main = RegExp(reStart + literalizeRegex(pg.wiki.sitebase) + reEnd);
}
function setRegexps() {
// TODO: We shoud use an api call to get the aliases for special pages, now it does not work for non-English wikipedias:
// E.g., https://ru.wikipedia.org/w/api.php?action=query&meta=siteinfo&siprop=specialpagealiases&formatversion=2
setMainRegex();
var sp=nsRe(pg.nsSpecialId);
pg.re.urlNoPopup=RegExp('((title=|/)' + sp + '(?:%3A|:)|section=[0-9]|^#$)') ;
pg.re.contribs =RegExp('(title=|/)' + sp + '(?:%3A|:)Contributions' + '(&target=|/|/' + nsRe(pg.nsUserId)+':)(.*)') ;
pg.re.email =RegExp('(title=|/)' + sp + '(?:%3A|:)EmailUser' + '(&target=|/|/(?:' + nsRe(pg.nsUserId)+':)?)(.*)') ;
pg.re.backlinks =RegExp('(title=|/)' + sp + '(?:%3A|:)WhatLinksHere' + '(&target=|/)([^&]*)');
pg.re.specialdiff=RegExp('/' + sp + '(?:%3A|:)Diff/([^?#]*)');
//<NOLITE>
var im=nsReImage();
// note: tries to get images in infobox templates too, e.g. movie pages, album pages etc
// (^|\[\[)image: *([^|\]]*[^|\] ]) *
// (^|\[\[)image: *([^|\]]*[^|\] ])([^0-9\]]*([0-9]+) *px)?
// $4 = 120 as in 120px
pg.re.image = RegExp('(^|\\[\\[)' + im + ': *([^|\\]]*[^|\\] ])' +
'([^0-9\\]]*([0-9]+) *px)?|(?:\\n *[|]?|[|]) *' +
'(' + getValueOf('popupImageVarsRegexp') + ')' +
' *= *(?:\\[\\[ *)?(?:' + im + ':)?' +
'([^|]*?)(?:\\]\\])? *[|]? *\\n', 'img') ;
pg.re.imageBracketCount = 6;
pg.re.category = RegExp('\\[\\[' +nsRe(pg.nsCategoryId) +
': *([^|\\]]*[^|\\] ]) *', 'i');
pg.re.categoryBracketCount = 1;
pg.re.ipUser=RegExp('^' +
// IPv6
'(?::(?::|(?::[0-9A-Fa-f]{1,4}){1,7})|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0,6}::|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){7})' +
// IPv4
'|(((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}' +
'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9]))$');
pg.re.stub= RegExp(getValueOf('popupStubRegexp'), 'im');
pg.re.disambig=RegExp(getValueOf('popupDabRegexp'), 'im');
//</NOLITE>
// FIXME replace with general parameter parsing function, this is daft
pg.re.oldid=RegExp('[?&]oldid=([^&]*)');
pg.re.diff=RegExp('[?&]diff=([^&]*)');
}
//////////////////////////////////////////////////
// miscellany
function setupCache() {
// page caching
pg.cache.pages = [];
}
function setMisc() {
pg.current.link=null;
pg.current.links=[];
pg.current.linksHash={};
setupCache();
pg.timer.checkPopupPosition=null;
pg.counter.loop=0;
// ids change with each popup: popupImage0, popupImage1 etc
pg.idNumber=0;
// for myDecodeURI
pg.misc.decodeExtras = [
{from: '%2C', to: ',' },
{from: '_', to: ' ' },
{from: '%24', to: '$'},
{from: '%26', to: '&' } // no ,
];
pg.misc.userAgent = 'Navigation popups/1.0 (' + mw.config.get( 'wgServerName' ) +')';
}
// We need a callback since this might end up asynchronous because of
// the mw.loader.using() call.
function setupPopups( callback ) {
if ( setupPopups.completed ) {
if ( typeof callback === 'function' ) {
callback();
}
return;
}
// These dependencies are also enforced from the gadget,
// but not everyone loads this as a gadget, so double check
mw.loader.using( ['mediawiki.util', 'mediawiki.user', 'user.options'] ).then( function() {
// NB translatable strings should be set up first (strings.js)
// basics
setupDebugging();
setSiteInfo();
setTitleBase();
setOptions(); // see options.js
setUserInfo();
// namespaces etc
setNamespaces();
setInterwiki();
// regexps
setRegexps();
setRedirs();
// other stuff
setMisc();
setupLivePreview();
// main deal here
setupTooltips();
log('In setupPopups(), just called setupTooltips()');
Navpopup.tracker.enable();
setupPopups.completed = true;
if ( typeof callback === 'function' ) {
callback();
}
});
}
// ENDFILE: init.js
// STARTFILE: navlinks.js
//<NOLITE>
//////////////////////////////////////////////////
// navlinks... let the fun begin
//
function defaultNavlinkSpec() {
var str='';
str += '<b><<mainlink|shortcut= >></b>';
if (getValueOf('popupLastEditLink')) {
str += '*<<lastEdit|shortcut=/>>|<<lastContrib>>|<<sinceMe>>if(oldid){|<<oldEdit>>|<<diffCur>>}';
}
// user links
// contribs - log - count - email - block
// count only if applicable; block only if popupAdminLinks
str += 'if(user){<br><<contribs|shortcut=c>>*<<userlog|shortcut=L|log>>';
str+='if(ipuser){*<<arin>>}if(wikimedia){*<<count|shortcut=#>>}';
str+='if(ipuser){}else{*<<email|shortcut=E>>}if(admin){*<<block|shortcut=b>>|<<blocklog|log>>}}';
// editing links
// talkpage -> edit|new - history - un|watch - article|edit
// other page -> edit - history - un|watch - talk|edit|new
var editstr='<<edit|shortcut=e>>';
var editOldidStr='if(oldid){<<editOld|shortcut=e>>|<<revert|shortcut=v|rv>>|<<edit|cur>>}else{' + editstr + '}';
var historystr='<<history|shortcut=h>>|<<editors|shortcut=E|>>';
var watchstr='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
str+='<br>if(talk){' +
editOldidStr+'|<<new|shortcut=+>>' + '*' + historystr+'*'+watchstr + '*' +
'<b><<article|shortcut=a>></b>|<<editArticle|edit>>' +
'}else{' + // not a talk page
editOldidStr + '*' + historystr + '*' + watchstr + '*' +
'<b><<talk|shortcut=t>></b>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>}';
// misc links
str += '<br><<whatLinksHere|shortcut=l>>*<<relatedChanges|shortcut=r>>*<<move|shortcut=m>>';
// admin links
str += 'if(admin){<br><<unprotect|unprotectShort>>|<<protect|shortcut=p>>|<<protectlog|log>>*' +
'<<undelete|undeleteShort>>|<<delete|shortcut=d>>|<<deletelog|log>>}';
return str;
}
function navLinksHTML (article, hint, params) { //oldid, rcid) {
var str = '<span class="popupNavLinks">' + defaultNavlinkSpec() + '</span>';
// BAM
return navlinkStringToHTML(str, article, params);
}
function expandConditionalNavlinkString(s,article,z,recursionCount) {
var oldid=z.oldid, rcid=z.rcid, diff=z.diff;
// nested conditionals (up to 10 deep) are ok, hopefully! (work from the inside out)
if (typeof recursionCount!=typeof 0) { recursionCount=0; }
var conditionalSplitRegex=RegExp(
//(1 if \\( (2 2) \\) {(3 3)} (4 else {(5 5)} 4)1)
'(;?\\s*if\\s*\\(\\s*([\\w]*)\\s*\\)\\s*\\{([^{}]*)\\}(\\s*else\\s*\\{([^{}]*?)\\}|))', 'i');
var splitted=s.parenSplit(conditionalSplitRegex);
// $1: whole conditional
// $2: test condition
// $3: true expansion
// $4: else clause (possibly empty)
// $5: false expansion (possibly null)
var numParens=5;
var ret = splitted[0];
for (var i=1; i<splitted.length; i=i+numParens+1) {
var testString=splitted[i+2-1];
var trueString=splitted[i+3-1];
var falseString=splitted[i+5-1];
if (typeof falseString=='undefined' || !falseString) { falseString=''; }
var testResult=null;
switch (testString) {
case 'user':
testResult=(article.userName())?true:false;
break;
case 'talk':
testResult=(article.talkPage())?false:true; // talkPage converts _articles_ to talkPages
break;
case 'admin':
testResult=getValueOf('popupAdminLinks')?true:false;
break;
case 'oldid':
testResult=(typeof oldid != 'undefined' && oldid)?true:false;
break;
case 'rcid':
testResult=(typeof rcid != 'undefined' && rcid)?true:false;
break;
case 'ipuser':
testResult=(article.isIpUser())?true:false;
break;
case 'mainspace_en':
testResult=isInMainNamespace(article) &&
pg.wiki.hostname=='en.wikipedia.org';
break;
case 'wikimedia':
testResult=(pg.wiki.wikimedia) ? true : false;
break;
case 'diff':
testResult=(typeof diff != 'undefined' && diff)?true:false;
break;
}
switch(testResult) {
case null: ret+=splitted[i]; break;
case true: ret+=trueString; break;
case false: ret+=falseString; break;
}
// append non-conditional string
ret += splitted[i+numParens];
}
if (conditionalSplitRegex.test(ret) && recursionCount < 10) {
return expandConditionalNavlinkString(ret,article,z,recursionCount+1);
}
return ret;
}
function navlinkStringToArray(s, article, params) {
s=expandConditionalNavlinkString(s,article,params);
var splitted=s.parenSplit(RegExp('<<(.*?)>>'));
var ret=[];
for (var i=0; i<splitted.length; ++i) {
if (i%2) { // i odd, so s is a tag
var t=new navlinkTag();
var ss=splitted[i].split('|');
t.id=ss[0];
for (var j=1; j<ss.length; ++j) {
var sss=ss[j].split('=');
if (sss.length>1) {
t[sss[0]]=sss[1];
}
else { // no assignment (no "="), so treat this as a title (overwriting the last one)
t.text=popupString(sss[0]);
}
}
t.article=article;
var oldid=params.oldid, rcid=params.rcid, diff=params.diff;
if (typeof oldid !== 'undefined' && oldid !== null) { t.oldid=oldid; }
if (typeof rcid !== 'undefined' && rcid !== null) { t.rcid=rcid; }
if (typeof diff !== 'undefined' && diff !== null) { t.diff=diff; }
if (!t.text && t.id !== 'mainlink') { t.text=popupString(t.id); }
ret.push(t);
}
else { // plain HTML
ret.push(splitted[i]);
}
}
return ret;
}
function navlinkSubstituteHTML(s) {
return s.split('*').join(getValueOf('popupNavLinkSeparator'))
.split('<menurow>').join('<li class="popup_menu_row">')
.split('</menurow>').join('</li>')
.split('<menu>').join('<ul class="popup_menu">')
.split('</menu>').join('</ul>');
}
function navlinkDepth(magic,s) {
return s.split('<' + magic + '>').length - s.split('</' + magic + '>').length;
}
// navlinkString: * becomes the separator
// <<foo|bar=baz|fubar>> becomes a foo-link with attribute bar='baz'
// and visible text 'fubar'
// if(test){...} and if(test){...}else{...} work too (nested ok)
function navlinkStringToHTML(s,article,params) {
//limitAlert(navlinkStringToHTML, 5, 'navlinkStringToHTML\n' + article + '\n' + (typeof article));
var p=navlinkStringToArray(s,article,params);
var html='';
var menudepth = 0; // nested menus not currently allowed, but doesn't do any harm to code for it
var menurowdepth = 0;
for (var i=0; i<p.length; ++i) {
if (typeof p[i] == typeof '') {
html+=navlinkSubstituteHTML(p[i]);
menudepth += navlinkDepth('menu', p[i]);
menurowdepth += navlinkDepth('menurow', p[i]);
// if (menudepth === 0) {
// tagType='span';
// } else if (menurowdepth === 0) {
// tagType='li';
// } else {
// tagType = null;
// }
} else if (typeof p[i].type != 'undefined' && p[i].type=='navlinkTag') {
if (menudepth > 0 && menurowdepth === 0) {
html += '<li class="popup_menu_item">' + p[i].html() + '</li>';
} else {
html+=p[i].html();
}
}
}
return html;
}
function navlinkTag() {
this.type='navlinkTag';
}
navlinkTag.prototype.html=function () {
this.getNewWin();
this.getPrintFunction();
var html='';
var opening, closing;
var tagType='span';
if (!tagType) {
opening = ''; closing = '';
} else {
opening = '<' + tagType + ' class="popup_' + this.id + '">';
closing = '</' + tagType + '>';
}
if (typeof this.print!='function') {
errlog ('Oh dear - invalid print function for a navlinkTag, id='+this.id);
} else {
html=this.print(this);
if (typeof html != typeof '') {html='';}
else if (typeof this.shortcut!='undefined') html=addPopupShortcut(html, this.shortcut);
}
return opening + html + closing;
};
navlinkTag.prototype.getNewWin=function() {
getValueOf('popupLinksNewWindow');
if (typeof pg.option.popupLinksNewWindow[this.id] === 'undefined') { this.newWin=null; }
this.newWin=pg.option.popupLinksNewWindow[this.id];
};
navlinkTag.prototype.getPrintFunction=function() { //think about this some more
// this.id and this.article should already be defined
if (typeof this.id!=typeof '' || typeof this.article!=typeof {} ) { return; }
this.noPopup=1;
switch (this.id) {
case 'contribs': case 'history': case 'whatLinksHere':
case 'userPage': case 'monobook': case 'userTalk':
case 'talk': case 'article': case 'lastEdit':
this.noPopup=null;
}
switch (this.id) {
case 'email': case 'contribs': case 'block': case 'unblock':
case 'userlog': case 'userSpace': case 'deletedContribs':
this.article=this.article.userName();
}
switch (this.id) {
case 'userTalk': case 'newUserTalk': case 'editUserTalk':
case 'userPage': case 'monobook': case 'editMonobook': case 'blocklog':
this.article=this.article.userName(true);
/* fall through */
case 'pagelog': case 'deletelog': case 'protectlog':
delete this.oldid;
}
if (this.id=='editMonobook' || this.id=='monobook') { this.article.append('/monobook.js'); }
if (this.id != 'mainlink') {
// FIXME anchor handling should be done differently with Title object
this.article=this.article.removeAnchor();
// if (typeof this.text=='undefined') this.text=popupString(this.id);
}
switch (this.id) {
case 'undelete': this.print=specialLink; this.specialpage='Undelete'; this.sep='/'; break;
case 'whatLinksHere': this.print=specialLink; this.specialpage='Whatlinkshere'; break;
case 'relatedChanges': this.print=specialLink; this.specialpage='Recentchangeslinked'; break;
case 'move': this.print=specialLink; this.specialpage='Movepage'; break;
case 'contribs': this.print=specialLink; this.specialpage='Contributions'; break;
case 'deletedContribs':this.print=specialLink; this.specialpage='Deletedcontributions'; break;
case 'email': this.print=specialLink; this.specialpage='EmailUser'; this.sep='/'; break;
case 'block': this.print=specialLink; this.specialpage='Blockip'; this.sep='&ip='; break;
case 'unblock': this.print=specialLink; this.specialpage='Ipblocklist'; this.sep='&action=unblock&ip='; break;
case 'userlog': this.print=specialLink; this.specialpage='Log'; this.sep='&user='; break;
case 'blocklog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=block&page='; break;
case 'pagelog': this.print=specialLink; this.specialpage='Log'; this.sep='&page='; break;
case 'protectlog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=protect&page='; break;
case 'deletelog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=delete&page='; break;
case 'userSpace': this.print=specialLink; this.specialpage='PrefixIndex'; this.sep='&namespace=2&prefix='; break;
case 'search': this.print=specialLink; this.specialpage='Search'; this.sep='&fulltext=Search&search='; break;
case 'thank': this.print=specialLink; this.specialpage='Thanks'; this.sep='/'; this.article.value = this.diff; break;
case 'unwatch': case 'watch':
this.print=magicWatchLink; this.action=this.id+'&autowatchlist=1&autoimpl=' + popupString('autoedit_version') + '&actoken='+autoClickToken(); break;
case 'history': case 'historyfeed':
case 'unprotect': case 'protect':
this.print=wikiLink; this.action=this.id; break;
case 'delete':
this.print=wikiLink; this.action='delete';
if (this.article.namespaceId()==pg.nsImageId) {
var img=this.article.stripNamespace();
this.action+='&image='+img;
}
break;
case 'markpatrolled':
case 'edit': // editOld should keep the oldid, but edit should not.
delete this.oldid;
/* fall through */
case 'view': case 'purge': case 'render':
this.print=wikiLink;
this.action=this.id; break;
case 'raw':
this.print=wikiLink; this.action='raw'; break;
case 'new':
this.print=wikiLink; this.action='edit§ion=new'; break;
case 'mainlink':
if (typeof this.text=='undefined') { this.text=this.article.toString().entify(); }
if (getValueOf('popupSimplifyMainLink') && isInStrippableNamespace(this.article)) {
// only show the /subpage part of the title text
var s=this.text.split('/'); this.text=s[s.length-1];
if (this.text==='' && s.length > 1) { this.text=s[s.length-2]; }
}
this.print=titledWikiLink;
if (typeof this.title==='undefined' && pg.current.link && typeof pg.current.link.href !== 'undefined') {
this.title=safeDecodeURI((pg.current.link.originalTitle)?pg.current.link.originalTitle:this.article);
if (typeof this.oldid !== 'undefined' && this.oldid) {
this.title=tprintf('Revision %s of %s', [this.oldid, this.title]);
}
}
this.action='view'; break;
case 'userPage':
case 'article':
case 'monobook':
case 'editMonobook':
case 'editArticle':
delete this.oldid;
//alert(this.id+'\n'+this.article + '\n'+ typeof this.article);
this.article=this.article.articleFromTalkOrArticle();
//alert(this.id+'\n'+this.article + '\n'+ typeof this.article);
this.print=wikiLink;
if (this.id.indexOf('edit')===0) {
this.action='edit';
} else { this.action='view';}
break;
case 'userTalk':
case 'talk':
this.article=this.article.talkPage();
delete this.oldid;
this.print=wikiLink;
this.action='view'; break;
case 'arin':
this.print=arinLink; break;
case 'count':
this.print=editCounterLink; break;
case 'google':
this.print=googleLink; break;
case 'editors':
this.print=editorListLink; break;
case 'globalsearch':
this.print=globalSearchLink; break;
case 'lastEdit':
this.print=titledDiffLink;
this.title=popupString('Show the last edit');
this.from='prev'; this.to='cur'; break;
case 'oldEdit':
this.print=titledDiffLink;
this.title=popupString('Show the edit made to get revision') + ' ' + this.oldid;
this.from='prev'; this.to=this.oldid; break;
case 'editOld':
this.print=wikiLink; this.action='edit'; break;
case 'undo':
this.print=wikiLink; this.action='edit&undo='; break;
case 'revert':
this.print=wikiLink; this.action='revert'; break;
case 'nullEdit':
this.print=wikiLink; this.action='nullEdit'; break;
case 'diffCur':
this.print=titledDiffLink;
this.title=tprintf('Show changes since revision %s', [this.oldid]);
this.from=this.oldid; this.to='cur'; break;
case 'editUserTalk':
case 'editTalk':
delete this.oldid;
this.article=this.article.talkPage();
this.action='edit'; this.print=wikiLink; break;
case 'newUserTalk':
case 'newTalk':
this.article=this.article.talkPage();
this.action='edit§ion=new'; this.print=wikiLink; break;
case 'lastContrib':
case 'sinceMe':
this.print=magicHistoryLink;
break;
case 'togglePreviews':
this.text=popupString(pg.option.simplePopups ? 'enable previews' : 'disable previews');
/* fall through */
case 'disablePopups': case 'purgePopups':
this.print=popupMenuLink;
break;
default:
this.print=function () {return 'Unknown navlink type: '+this.id+'';};
}
};
//
// end navlinks
//////////////////////////////////////////////////
//</NOLITE>
// ENDFILE: navlinks.js
// STARTFILE: shortcutkeys.js
//<NOLITE>
function popupHandleKeypress(evt) {
var keyCode = window.event ? window.event.keyCode : ( evt.keyCode ? evt.keyCode : evt.which);
if (!keyCode || !pg.current.link || !pg.current.link.navpopup) { return; }
if (keyCode==27) { // escape
killPopup();
return false; // swallow keypress
}
var letter=String.fromCharCode(keyCode);
var links=pg.current.link.navpopup.mainDiv.getElementsByTagName('A');
var startLink=0;
var i,j;
if (popupHandleKeypress.lastPopupLinkSelected) {
for (i=0; i<links.length; ++i) {
if (links[i]==popupHandleKeypress.lastPopupLinkSelected) { startLink=i; }
}
}
for (j=0; j<links.length; ++j) {
i=(startLink + j + 1) % links.length;
if (links[i].getAttribute('popupkey')==letter) {
if (evt && evt.preventDefault) evt.preventDefault();
links[i].focus();
popupHandleKeypress.lastPopupLinkSelected=links[i];
return false; // swallow keypress
}
}
// pass keypress on
if (document.oldPopupOnkeypress) { return document.oldPopupOnkeypress(evt); }
return true;
}
function addPopupShortcuts() {
if (document.onkeypress!=popupHandleKeypress) {
document.oldPopupOnkeypress=document.onkeypress;
}
document.onkeypress=popupHandleKeypress;
}
function rmPopupShortcuts() {
popupHandleKeypress.lastPopupLinkSelected=null;
try {
if (document.oldPopupOnkeypress && document.oldPopupOnkeypress==popupHandleKeypress) {
// panic
document.onkeypress=null; //function () {};
return;
}
document.onkeypress=document.oldPopupOnkeypress;
} catch (nasties) { /* IE goes here */ }
}
function addLinkProperty(html, property) {
// take "<a href=...>...</a> and add a property
// not sophisticated at all, easily broken
var i=html.indexOf('>');
if (i<0) { return html; }
return html.substring(0,i) + ' ' + property + html.substring(i);
}
function addPopupShortcut(html, key) {
if (!getValueOf('popupShortcutKeys')) { return html; }
var ret= addLinkProperty(html, 'popupkey="'+key+'"');
if (key==' ') { key=popupString('spacebar'); }
return ret.replace(RegExp('^(.*?)(title=")(.*?)(".*)$', 'i'),'$1$2$3 ['+key+']$4');
}
//</NOLITE>
// ENDFILE: shortcutkeys.js
// STARTFILE: diffpreview.js
//<NOLITE>
//lets jump through hoops to find the rev ids we need to retrieve
function loadDiff(article, oldid, diff, navpop) {
navpop.diffData={ oldRev: {}, newRev: {} };
mw.loader.using( 'mediawiki.api' ).then( function() {
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'compare',
prop: 'ids|title'
};
if(article.title){
params.fromtitle = article.title;
}
switch (diff) {
case 'cur':
switch ( oldid ) {
case null:
case '':
case 'prev':
// this can only work if we have the title
// cur -> prev
params.torelative = 'prev';
break;
default:
params.fromrev = oldid;
params.torelative = 'cur';
break;
}
break;
case 'prev':
if( oldid ) {
params.fromrev = oldid;
} else {
params.fromtitle;
}
params.torelative = 'prev';
break;
case 'next':
params.fromrev = oldid || 0;
params.torelative = 'next';
break;
default:
params.fromrev = oldid || 0;
params.torev = diff || 0;
break;
}
api.get( params ).then( function( data ) {
navpop.diffData.oldRev.revid = data.compare.fromrevid;
navpop.diffData.newRev.revid = data.compare.torevid;
addReviewLink(navpop, 'popupMiscTools');
var go = function() {
pendingNavpopTask(navpop);
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&';
url += 'revids=' + navpop.diffData.oldRev.revid + '|' + navpop.diffData.newRev.revid;
url += '&prop=revisions&rvprop=ids|timestamp|content';
getPageWithCaching(url, doneDiff, navpop);
return true; // remove hook once run
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'before', 'DOWNLOAD_DIFFS'); }
} );
} );
}
// Put a "mark patrolled" link to an element target
// TODO: Allow patrol a revision, as well as a diff
function addReviewLink (navpop, target) {
if (! pg.user.canReview) return;
// If 'newRev' is older than 'oldRev' than it could be confusing, so we do not show the review link.
if (navpop.diffData.newRev.revid <= navpop.diffData.oldRev.revid) return;
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'query',
prop: 'info|flagged',
revids: navpop.diffData.oldRev.revid,
formatversion : 2
};
api.get (params).then(function(data){
var stable_revid = data.query.pages[0].flagged && data.query.pages[0].flagged.stable_revid || 0;
// The diff can be reviewed if the old version is the last reviewed version
// TODO: Other possible conditions that we may want to implement instead of this one:
// * old version is patrolled and the new version is not patrolled
// * old version is patrolled and the new version is more recent than the last reviewed version
if (stable_revid == navpop.diffData.oldRev.revid) {
var a = document.createElement('a');
a.innerHTML = popupString('mark patrolled');
a.title=popupString('markpatrolledHint');
a.onclick = function() {
var params = {
action: 'review',
revid: navpop.diffData.newRev.revid,
comment: tprintf('defaultpopupReviewedSummary', [navpop.diffData.oldRev.revid, navpop.diffData.newRev.revid])
};
api.postWithToken('csrf',params).done(function(){
a.style.display = "none";
// TODO: Update current page and other already constructed popups
} ).fail(function(){
alert(popupString('Could not marked this edit as patrolled'));
});
};
setPopupHTML(a, target, navpop.idNumber,null,true);
}
});
}
function doneDiff(download) {
if (!download.owner || !download.owner.diffData) { return; }
var navpop=download.owner;
completedNavpopTask(navpop);
var pages, revisions=[];
try{
// Process the downloads
pages = getJsObj(download.data).query.pages;
for(var i=0; i < pages.length; i++ ) {
revisions = revisions.concat(pages[i].revisions);
}
for(i=0; i< revisions.length; i++){
if(revisions[i].revid == navpop.diffData.oldRev.revid) {
navpop.diffData.oldRev.revision = revisions[i];
} else if (revisions[i].revid == navpop.diffData.newRev.revid) {
navpop.diffData.newRev.revision = revisions[i];
}
}
} catch(someError) {
errlog( 'Could not get diff' );
}
insertDiff(navpop);
}
function rmBoringLines(a,b,context) {
if (typeof context == 'undefined') { context=2; }
// this is fairly slow... i think it's quicker than doing a word-based diff from the off, though
var aa=[], aaa=[];
var bb=[], bbb=[];
var i, j;
// first, gather all disconnected nodes in a and all crossing nodes in a and b
for (i=0; i<a.length; ++i ) {
if(!a[i].paired) { aa[i]=1; }
else if (countCrossings(b,a,i, true)) {
aa[i]=1;
bb[ a[i].row ] = 1;
}
}
// pick up remaining disconnected nodes in b
for (i=0; i<b.length; ++i ) {
if (bb[i]==1) { continue; }
if(!b[i].paired) { bb[i]=1; }
}
// another pass to gather context: we want the neighbours of included nodes which are not yet included
// we have to add in partners of these nodes, but we don't want to add context for *those* nodes in the next pass
for (i=0; i<b.length; ++i) {
if ( bb[i] == 1 ) {
for (j=Math.max(0,i-context); j < Math.min(b.length, i+context); ++j) {
if ( !bb[j] ) { bb[j] = 1; aa[ b[j].row ] = 0.5; }
}
}
}
for (i=0; i<a.length; ++i) {
if ( aa[i] == 1 ) {
for (j=Math.max(0,i-context); j < Math.min(a.length, i+context); ++j) {
if ( !aa[j] ) { aa[j] = 1; bb[ a[j].row ] = 0.5; }
}
}
}
for (i=0; i<bb.length; ++i) {
if (bb[i] > 0) { // it's a row we need
if (b[i].paired) { bbb.push(b[i].text); } // joined; partner should be in aa
else {
bbb.push(b[i]);
}
}
}
for (i=0; i<aa.length; ++i) {
if (aa[i] > 0) { // it's a row we need
if (a[i].paired) { aaa.push(a[i].text); } // joined; partner should be in aa
else {
aaa.push(a[i]);
}
}
}
return { a: aaa, b: bbb};
}
function stripOuterCommonLines(a,b,context) {
var i=0;
while (i<a.length && i < b.length && a[i]==b[i]) { ++i; }
var j=a.length-1; var k=b.length-1;
while ( j>=0 && k>=0 && a[j]==b[k] ) { --j; --k; }
return { a: a.slice(Math.max(0,i - 1 - context), Math.min(a.length+1, j + context+1)),
b: b.slice(Math.max(0,i - 1 - context), Math.min(b.length+1, k + context+1)) };
}
function insertDiff(navpop) {
// for speed reasons, we first do a line-based diff, discard stuff that seems boring, then do a word-based diff
// FIXME: sometimes this gives misleading diffs as distant chunks are squashed together
var oldlines = navpop.diffData.oldRev.revision.content.split('\n');
var newlines = navpop.diffData.newRev.revision.content.split('\n');
var inner=stripOuterCommonLines(oldlines,newlines,getValueOf('popupDiffContextLines'));
oldlines=inner.a; newlines=inner.b;
var truncated=false;
getValueOf('popupDiffMaxLines');
if (oldlines.length > pg.option.popupDiffMaxLines || newlines.length > pg.option.popupDiffMaxLines) {
// truncate
truncated=true;
inner=stripOuterCommonLines(oldlines.slice(0,pg.option.popupDiffMaxLines),
newlines.slice(0,pg.option.popupDiffMaxLines),
pg.option.popupDiffContextLines);
oldlines=inner.a; newlines=inner.b;
}
var lineDiff=diff(oldlines, newlines);
var lines2=rmBoringLines(lineDiff.o, lineDiff.n);
var oldlines2=lines2.a; var newlines2=lines2.b;
var simpleSplit = !String.prototype.parenSplit.isNative;
var html='<hr />';
if (getValueOf('popupDiffDates')) {
html += diffDatesTable(navpop);
html += '<hr />';
}
html += shortenDiffString(
diffString(oldlines2.join('\n'), newlines2.join('\n'), simpleSplit),
getValueOf('popupDiffContextCharacters') ).join('<hr />');
setPopupTipsAndHTML(html.split('\n').join('<br>') +
(truncated ? '<hr /><b>'+popupString('Diff truncated for performance reasons')+'</b>' : '') ,
'popupPreview', navpop.idNumber);
}
function diffDatesTable( navpop ) {
var html='<table class="popup_diff_dates">';
html += diffDatesTableRow( navpop.diffData.newRev.revision, tprintf('New revision'));
html += diffDatesTableRow( navpop.diffData.oldRev.revision, tprintf('Old revision'));
html += '</table>';
return html;
}
function diffDatesTableRow( revision, label ) {
var txt='';
var lastModifiedDate = new Date(revision.timestamp);
var datePrint=getValueOf('popupDiffDatePrinter');
if (typeof lastModifiedDate[datePrint] == 'function') {
var d2 = adjustDate(lastModifiedDate, getTimeOffset());
txt = dayFormat(d2, true) + ' ' + timeFormat(d2, true);
} else {
txt = tprintf('Invalid %s %s', ['popupDiffDatePrinter', datePrint]);
}
var revlink = generalLink({url: mw.config.get('wgScript') + '?oldid='+revision.revid,
text: label, title: label});
return simplePrintf('<tr><td>%s</td><td>%s</td></tr>', [ revlink, txt ]);
}
//</NOLITE>
// ENDFILE: diffpreview.js
// STARTFILE: links.js
//<NOLITE>
/////////////////////
// LINK GENERATION //
/////////////////////
// titledDiffLink --> titledWikiLink --> generalLink
// wikiLink --> titledWikiLink --> generalLink
// editCounterLink --> generalLink
// TODO Make these functions return Element objects, not just raw HTML strings.
function titledDiffLink(l) { // article, text, title, from, to) {
return titledWikiLink({article: l.article, action: l.to + '&oldid=' + l.from,
newWin: l.newWin,
noPopup: l.noPopup,
text: l.text, title: l.title,
/* hack: no oldid here */
actionName: 'diff'});
}
function wikiLink(l) {
//{article:article, action:action, text:text, oldid, newid}) {
if (! (typeof l.article == typeof {} &&
typeof l.action == typeof '' &&
typeof l.text==typeof '')) return null;
if (typeof l.oldid == 'undefined') { l.oldid=null; }
var savedOldid = l.oldid;
if (!/^(edit|view|revert|render)$|^raw/.test(l.action)) { l.oldid=null; }
var hint=popupString(l.action + 'Hint'); // revertHint etc etc etc
var oldidData=[l.oldid, safeDecodeURI(l.article)];
var revisionString = tprintf('revision %s of %s', oldidData);
log('revisionString='+revisionString);
switch (l.action) {
case 'edit§ion=new': hint = popupString('newSectionHint'); break;
case 'edit&undo=':
if (l.diff && l.diff != 'prev' && savedOldid ) {
l.action += l.diff + '&undoafter=' + savedOldid;
} else if (savedOldid) {
l.action += savedOldid;
}
hint = popupString('undoHint');
break;
case 'raw&ctype=text/css': hint=popupString('rawHint'); break;
case 'revert':
var p=parseParams(pg.current.link.href);
l.action='edit&autoclick=wpSave&actoken=' + autoClickToken() + '&autoimpl=' + popupString('autoedit_version') + '&autosummary=' + revertSummary(l.oldid, p.diff);
if (p.diff=='prev') {
l.action += '&direction=prev';
revisionString = tprintf('the revision prior to revision %s of %s', oldidData);
}
if (getValueOf('popupRevertSummaryPrompt')) { l.action += '&autosummaryprompt=true'; }
if (getValueOf('popupMinorReverts')) { l.action += '&autominor=true'; }
log('revisionString is now '+revisionString);
break;
case 'nullEdit':
l.action='edit&autoclick=wpSave&actoken=' + autoClickToken() + '&autoimpl=' + popupString('autoedit_version') + '&autosummary=null';
break;
case 'historyfeed':
l.action='history&feed=rss';
break;
case 'markpatrolled':
l.action='markpatrolled&rcid='+l.rcid;
}
if (hint) {
if (l.oldid) {
hint = simplePrintf(hint, [revisionString]);
}
else {
hint = simplePrintf(hint, [safeDecodeURI(l.article)]);
}
}
else {
hint = safeDecodeURI(l.article + '&action=' + l.action) + (l.oldid) ? '&oldid='+l.oldid : '';
}
return titledWikiLink({article: l.article, action: l.action, text: l.text, newWin:l.newWin,
title: hint, oldid: l.oldid, noPopup: l.noPopup, onclick: l.onclick});
}
function revertSummary(oldid, diff) {
var ret='';
if (diff == 'prev') {
ret=getValueOf('popupQueriedRevertToPreviousSummary');
} else { ret = getValueOf('popupQueriedRevertSummary'); }
return ret + '&autorv=' + oldid;
}
function titledWikiLink(l) {
// possible properties of argument:
// article, action, text, title, oldid, actionName, className, noPopup
// oldid = null is fine here
// article and action are mandatory args
if (typeof l.article == 'undefined' || typeof l.action=='undefined') {
errlog('got undefined article or action in titledWikiLink');
return null;
}
var base = pg.wiki.titlebase + l.article.urlString();
var url=base;
if (typeof l.actionName=='undefined' || !l.actionName) { l.actionName='action'; }
// no need to add &action=view, and this confuses anchors
if (l.action != 'view') { url = base + '&' + l.actionName + '=' + l.action; }
if (typeof l.oldid!='undefined' && l.oldid) { url+='&oldid='+l.oldid; }
var cssClass=pg.misc.defaultNavlinkClassname;
if (typeof l.className!='undefined' && l.className) { cssClass=l.className; }
return generalNavLink({url: url, newWin: l.newWin,
title: (typeof l.title != 'undefined') ? l.title : null,
text: (typeof l.text!='undefined')?l.text:null,
className: cssClass, noPopup:l.noPopup, onclick:l.onclick});
}
pg.fn.getLastContrib = function getLastContrib(wikipage, newWin) {
getHistoryInfo(wikipage, function(x) {
processLastContribInfo(x, {page: wikipage, newWin: newWin});
});
};
function processLastContribInfo(info, stuff) {
if(!info.edits || !info.edits.length) { alert('Popups: an odd thing happened. Please retry.'); return; }
if(!info.firstNewEditor) {
alert(tprintf('Only found one editor: %s made %s edits', [info.edits[0].editor,info.edits.length]));
return;
}
var newUrl=pg.wiki.titlebase + new Title(stuff.page).urlString() + '&diff=cur&oldid='+info.firstNewEditor.oldid;
displayUrl(newUrl, stuff.newWin);
}
pg.fn.getDiffSinceMyEdit = function getDiffSinceMyEdit(wikipage, newWin) {
getHistoryInfo(wikipage, function(x){
processDiffSinceMyEdit(x, {page: wikipage, newWin: newWin});
});
};
function processDiffSinceMyEdit(info, stuff) {
if(!info.edits || !info.edits.length) { alert('Popups: something fishy happened. Please try again.'); return; }
var friendlyName=stuff.page.split('_').join(' ');
if(!info.myLastEdit) {
alert(tprintf('Couldn\'t find an edit by %s\nin the last %s edits to\n%s',
[info.userName, getValueOf('popupHistoryLimit'), friendlyName]));
return;
}
if(info.myLastEdit.index === 0) {
alert(tprintf("%s seems to be the last editor to the page %s", [info.userName, friendlyName]));
return;
}
var newUrl=pg.wiki.titlebase + new Title(stuff.page).urlString() + '&diff=cur&oldid='+ info.myLastEdit.oldid;
displayUrl(newUrl, stuff.newWin);
}
function displayUrl(url, newWin){
if(newWin) { window.open(url); }
else { document.location=url; }
}
pg.fn.purgePopups = function purgePopups() {
processAllPopups(true);
setupCache(); // deletes all cached items (not browser cached, though...)
pg.option={};
abortAllDownloads();
};
function processAllPopups(nullify, banish) {
for (var i=0; pg.current.links && i<pg.current.links.length; ++i) {
if (!pg.current.links[i].navpopup) { continue; }
if (nullify || banish) pg.current.links[i].navpopup.banish();
pg.current.links[i].simpleNoMore=false;
if (nullify) pg.current.links[i].navpopup=null;
}
}
pg.fn.disablePopups = function disablePopups(){
processAllPopups(false, true);
setupTooltips(null, true);
};
pg.fn.togglePreviews = function togglePreviews() {
processAllPopups(true, true);
pg.option.simplePopups=!pg.option.simplePopups;
abortAllDownloads();
};
function magicWatchLink(l) {
//Yuck!! Would require a thorough redesign to add this as a click event though ...
l.onclick = simplePrintf( 'pg.fn.modifyWatchlist(\'%s\',\'%s\');return false;', [l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), this.id] );
return wikiLink(l);
}
pg.fn.modifyWatchlist = function modifyWatchlist(title, action) {
var reqData = {
'action': 'watch',
'formatversion': 2,
'titles': title,
'uselang': mw.config.get('wgUserLanguage')
};
if ( action === 'unwatch' ) reqData.unwatch = true;
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
// Load the Addedwatchtext or Removedwatchtext message and show it
var mwTitle = mw.Title.newFromText( title );
var messageName;
if ( mwTitle && mwTitle.getNamespaceId() > 0 && mwTitle.getNamespaceId() % 2 === 1 ) {
messageName = action === 'watch' ? 'addedwatchtext-talk' : 'removedwatchtext-talk';
} else {
messageName = action === 'watch' ? 'addedwatchtext' : 'removedwatchtext';
}
$.when(
api.postWithToken( 'watch', reqData ),
mw.loader.using( [ 'mediawiki.api', 'mediawiki.jqueryMsg' ] ).then( function () {
return api.loadMessagesIfMissing( [ messageName ] );
} )
).done( function () {
mw.notify( mw.message( messageName, title ).parseDom() );
} );
};
function magicHistoryLink(l) {
// FIXME use onclick change href trick to sort this out instead of window.open
var jsUrl='', title='', onClick='';
switch(l.id) {
case 'lastContrib':
onClick=simplePrintf('pg.fn.getLastContrib(\'%s\',%s)',
[l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), l.newWin]);
title=popupString('lastContribHint');
break;
case 'sinceMe':
onClick=simplePrintf('pg.fn.getDiffSinceMyEdit(\'%s\',%s)',
[l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), l.newWin]);
title=popupString('sinceMeHint');
break;
}
jsUrl = 'javascript:' + onClick; // jshint ignore:line
onClick += ';return false;';
return generalNavLink({url: jsUrl, newWin: false, // can't have new windows with JS links, I think
title: title, text: l.text, noPopup: l.noPopup, onclick: onClick });
}
function popupMenuLink(l) {
var jsUrl=simplePrintf('javascript:pg.fn.%s()', [l.id]); // jshint ignore:line
var title=popupString(simplePrintf('%sHint', [l.id]));
var onClick=simplePrintf('pg.fn.%s();return false;', [l.id]);
return generalNavLink({url: jsUrl, newWin:false, title:title, text:l.text, noPopup:l.noPopup, onclick: onClick});
}
function specialLink(l) {
// properties: article, specialpage, text, sep
if (typeof l.specialpage=='undefined'||!l.specialpage) return null;
var base = pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId]+':'+l.specialpage;
if (typeof l.sep == 'undefined' || l.sep === null) l.sep='&target=';
var article=l.article.urlString({keepSpaces: l.specialpage=='Search'});
var hint=popupString(l.specialpage+'Hint');
switch (l.specialpage) {
case 'Log':
switch (l.sep) {
case '&user=': hint=popupString('userLogHint'); break;
case '&type=block&page=': hint=popupString('blockLogHint'); break;
case '&page=': hint=popupString('pageLogHint'); break;
case '&type=protect&page=': hint=popupString('protectLogHint'); break;
case '&type=delete&page=': hint=popupString('deleteLogHint'); break;
default: log('Unknown log type, sep=' + l.sep); hint='Missing hint (FIXME)';
}
break;
case 'PrefixIndex': article += '/'; break;
}
if (hint) hint = simplePrintf(hint, [safeDecodeURI(l.article)]);
else hint = safeDecodeURI(l.specialpage+':'+l.article) ;
var url = base + l.sep + article;
return generalNavLink({url: url, title: hint, text: l.text, newWin:l.newWin, noPopup:l.noPopup});
}
function generalLink(l) {
// l.url, l.text, l.title, l.newWin, l.className, l.noPopup, l.onclick
if (typeof l.url=='undefined') return null;
// only quotation marks in the url can screw us up now... I think
var url=l.url.split('"').join('%22');
var ret='<a href="' + url + '"';
if (typeof l.title!='undefined' && l.title) { ret += ' title="' + pg.escapeQuotesHTML(l.title) + '"'; }
if (typeof l.onclick!='undefined' && l.onclick) { ret += ' onclick="' + pg.escapeQuotesHTML(l.onclick) + '"'; }
if (l.noPopup) { ret += ' noPopup=1'; }
var newWin;
if (typeof l.newWin=='undefined' || l.newWin === null) { newWin=getValueOf('popupNewWindows'); }
else { newWin=l.newWin; }
if (newWin) { ret += ' target="_blank"'; }
if (typeof l.className!='undefined'&&l.className) { ret+=' class="'+l.className+'"'; }
ret += '>';
if (typeof l.text==typeof '') { ret+= l.text; }
ret +='</a>';
return ret;
}
function appendParamsToLink(linkstr, params) {
var sp=linkstr.parenSplit(RegExp('(href="[^"]+?)"', 'i'));
if (sp.length<2) return null;
var ret=sp.shift() + sp.shift();
ret += '&' + params + '"';
ret += sp.join('');
return ret;
}
function changeLinkTargetLink(x) { // newTarget, text, hint, summary, clickButton, minor, title (optional), alsoChangeLabel {
if (x.newTarget) {
log ('changeLinkTargetLink: newTarget=' + x.newTarget);
}
if (x.oldTarget !== decodeURIComponent( x.oldTarget ) ) {
log ('This might be an input problem: ' + x.oldTarget );
}
// FIXME: first character of page title as well as namespace should be case insensitive
// eg [[category:X1]] and [[Category:X1]] are equivalent
// this'll break if charAt(0) is nasty
var cA = mw.util.escapeRegExp(x.oldTarget);
var chs = cA.charAt(0).toUpperCase();
chs='['+chs + chs.toLowerCase()+']';
var currentArticleRegexBit=chs+cA.substring(1);
currentArticleRegexBit=currentArticleRegexBit
.split(RegExp('(?:[_ ]+|%20)', 'g')).join('(?:[_ ]+|%20)')
.split('\\(').join('(?:%28|\\()')
.split('\\)').join('(?:%29|\\))'); // why does this need to match encoded strings ? links in the document ?
// leading and trailing space should be ignored, and anchor bits optional:
currentArticleRegexBit = '\\s*(' + currentArticleRegexBit + '(?:#[^\\[\\|]*)?)\\s*';
// e.g. Computer (archaic) -> \s*([Cc]omputer[_ ](?:%2528|\()archaic(?:%2528|\)))\s*
// autoedit=s~\[\[([Cc]ad)\]\]~[[Computer-aided%20design|$1]]~g;s~\[\[([Cc]AD)[|]~[[Computer-aided%20design|~g
var title=x.title || mw.config.get('wgPageName').split('_').join(' ');
var lk=titledWikiLink({article: new Title(title), newWin:x.newWin,
action: 'edit',
text: x.text,
title: x.hint,
className: 'popup_change_title_link'
});
var cmd='';
if (x.newTarget) {
// escape '&' and other nasties
var t = x.newTarget;
var s = mw.util.escapeRegExp(x.newTarget);
if (x.alsoChangeLabel) {
cmd += 's~\\[\\[' + currentArticleRegexBit + '\\]\\]~[[' + t + ']]~g;';
cmd += 's~\\[\\[' + currentArticleRegexBit + '[|]~[[' + t + '|~g;';
cmd += 's~\\[\\[' + s + '\\|' + s + '\\]\\]~[[' + t + ']]~g';
} else {
cmd += 's~\\[\\[' + currentArticleRegexBit + '\\]\\]~[[' + t + '|$1]]~g;';
cmd += 's~\\[\\[' + currentArticleRegexBit + '[|]~[[' + t + '|~g;';
cmd += 's~\\[\\[' + s + '\\|' + s + '\\]\\]~[[' + t + ']]~g';
}
} else {
cmd += 's~\\[\\['+currentArticleRegexBit+'\\]\\]~$1~g;';
cmd += 's~\\[\\['+currentArticleRegexBit+'[|](.*?)\\]\\]~$2~g';
}
// Build query
cmd = 'autoedit=' + encodeURIComponent ( cmd );
cmd += '&autoclick='+ encodeURIComponent( x.clickButton ) + '&actoken=' + encodeURIComponent( autoClickToken() );
cmd += ( x.minor === null ) ? '' : '&autominor='+ encodeURIComponent( x.minor );
cmd += ( x.watch === null ) ? '' : '&autowatch='+ encodeURIComponent( x.watch );
cmd += '&autosummary='+encodeURIComponent(x.summary);
cmd += '&autoimpl='+encodeURIComponent( popupString('autoedit_version') );
return appendParamsToLink(lk, cmd);
}
function redirLink(redirMatch, article) {
// NB redirMatch is in wikiText
var ret='';
if (getValueOf('popupAppendRedirNavLinks') && getValueOf('popupNavLinks')) {
ret += '<hr />';
if (getValueOf('popupFixRedirs') && typeof autoEdit != 'undefined' && autoEdit) {
ret += popupString('Redirects to: (Fix ');
log('redirLink: newTarget=' + redirMatch);
ret += addPopupShortcut(changeLinkTargetLink({
newTarget: redirMatch,
text: popupString('target'),
hint: popupString('Fix this redirect, changing just the link target'),
summary: simplePrintf(getValueOf('popupFixRedirsSummary'),[article.toString(), redirMatch]),
oldTarget: article.toString(),
clickButton: getValueOf('popupRedirAutoClick'),
minor: true,
watch: getValueOf('popupWatchRedirredPages')
}), 'R');
ret += popupString(' or ');
ret += addPopupShortcut(changeLinkTargetLink({
newTarget: redirMatch,
text: popupString('target & label'),
hint: popupString('Fix this redirect, changing the link target and label'),
summary: simplePrintf(getValueOf('popupFixRedirsSummary'),[article.toString(), redirMatch]),
oldTarget: article.toString(),
clickButton: getValueOf('popupRedirAutoClick'),
minor: true,
watch: getValueOf('popupWatchRedirredPages'),
alsoChangeLabel: true
}), 'R');
ret += popupString(')');
}
else ret += popupString('Redirects') + popupString(' to ');
return ret;
}
else return '<br> ' + popupString('Redirects') + popupString(' to ') +
titledWikiLink({article: new Title().fromWikiText(redirMatch), action: 'view', /* FIXME: newWin */
text: safeDecodeURI(redirMatch), title: popupString('Bypass redirect')});
}
function arinLink(l) {
if (!saneLinkCheck(l)) { return null; }
if ( ! l.article.isIpUser() || ! pg.wiki.wikimedia) return null;
var uN=l.article.userName();
return generalNavLink({url:'http://ws.arin.net/cgi-bin/whois.pl?queryinput=' + encodeURIComponent(uN), newWin:l.newWin,
title: tprintf('Look up %s in ARIN whois database', [uN]),
text: l.text, noPopup:1});
}
function toolDbName(cookieStyle) {
var ret = mw.config.get('wgDBname');
if (!cookieStyle) { ret+= '_p'; }
return ret;
}
function saneLinkCheck(l) {
if (typeof l.article != typeof {} || typeof l.text != typeof '') { return false; }
return true;
}
function editCounterLink(l) {
if(!saneLinkCheck(l)) return null;
if (! pg.wiki.wikimedia) return null;
var uN=l.article.userName();
var tool=getValueOf('popupEditCounterTool');
var url;
var defaultToolUrl='//tools.wmflabs.org/supercount/index.php?user=$1&project=$2.$3';
switch(tool) {
case 'custom':
url=simplePrintf(getValueOf('popupEditCounterUrl'), [ encodeURIComponent(uN), toolDbName() ]);
break;
case 'soxred': // no longer available
case 'kate': // no longer available
case 'interiot':// no longer available
/* fall through */
case 'supercount':
default:
var theWiki=pg.wiki.hostname.split('.');
url=simplePrintf(defaultToolUrl, [ encodeURIComponent(uN), theWiki[0], theWiki[1] ]);
}
return generalNavLink({url:url, title: tprintf('editCounterLinkHint', [uN]),
newWin:l.newWin, text: l.text, noPopup:1});
}
function globalSearchLink(l) {
if(!saneLinkCheck(l)) return null;
var base='http://vs.aka-online.de/cgi-bin/globalwpsearch.pl?timeout=120&search=';
var article=l.article.urlString({keepSpaces:true});
return generalNavLink({url:base + article, newWin:l.newWin,
title: tprintf('globalSearchHint', [safeDecodeURI(l.article)]),
text: l.text, noPopup:1});
}
function googleLink(l) {
if(!saneLinkCheck(l)) return null;
var base='https://www.google.com/search?q=';
var article=l.article.urlString({keepSpaces:true});
return generalNavLink({url:base + '%22' + article + '%22', newWin:l.newWin,
title: tprintf('googleSearchHint', [safeDecodeURI(l.article)]),
text: l.text, noPopup:1});
}
function editorListLink(l) {
if(!saneLinkCheck(l)) return null;
var article= l.article.articleFromTalkPage() || l.article;
var url='https://xtools.wmflabs.org/articleinfo/' +
encodeURI( pg.wiki.hostname ) + '/' +
article.urlString() +
'?uselang=' + mw.config.get('wgUserLanguage');
return generalNavLink({url:url,
title: tprintf('editorListHint', [article]),
newWin:l.newWin, text: l.text, noPopup:1});
}
function generalNavLink(l) {
l.className = (l.className === null) ? 'popupNavLink' : l.className;
return generalLink(l);
}
//////////////////////////////////////////////////
// magic history links
//
function getHistoryInfo(wikipage, whatNext) {
log('getHistoryInfo');
getHistory(wikipage, whatNext ? function(d){whatNext(processHistory(d));} : processHistory);
}
// FIXME eliminate pg.idNumber ... how? :-(
function getHistory(wikipage, onComplete) {
log('getHistory');
var url = pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&prop=revisions&titles=' +
new Title(wikipage).urlString() + '&rvlimit=' + getValueOf('popupHistoryLimit');
log('getHistory: url='+url);
return startDownload(url, pg.idNumber+'history', onComplete);
}
function processHistory(download) {
var jsobj = getJsObj(download.data);
try {
var revisions = anyChild(jsobj.query.pages).revisions;
var edits=[];
for (var i=0; i<revisions.length; ++i) {
edits.push({ oldid: revisions[i].revid, editor: revisions[i].user });
}
log('processed ' + edits.length + ' edits');
return finishProcessHistory( edits, mw.config.get('wgUserName') );
} catch (someError) {
log('Something went wrong with JSON business');
return finishProcessHistory([]);
}
}
function finishProcessHistory(edits, userName) {
var histInfo={};
histInfo.edits=edits;
histInfo.userName=userName;
for (var i=0; i<edits.length; ++i) {
if (typeof histInfo.myLastEdit === 'undefined' && userName && edits[i].editor==userName) {
histInfo.myLastEdit={index: i, oldid: edits[i].oldid, previd: (i === 0 ? null : edits[i-1].oldid)};
}
if (typeof histInfo.firstNewEditor === 'undefined' && edits[i].editor != edits[0].editor) {
histInfo.firstNewEditor={index:i, oldid:edits[i].oldid, previd: (i === 0 ? null : edits[i-1].oldid)};
}
}
//pg.misc.historyInfo=histInfo;
return histInfo;
}
//</NOLITE>
// ENDFILE: links.js
// STARTFILE: options.js
//////////////////////////////////////////////////
// options
// check for existing value, else use default
function defaultize(x) {
if (pg.option[x]===null || typeof pg.option[x]=='undefined') {
if (typeof window[x] != 'undefined' ) pg.option[x]=window[x];
else pg.option[x]=pg.optionDefault[x];
}
}
function newOption(x, def) {
pg.optionDefault[x]=def;
}
function setDefault(x, def) {
return newOption(x, def);
}
function getValueOf(varName) {
defaultize(varName);
return pg.option[varName];
}
/*eslint-disable */
function useDefaultOptions() { // for testing
for (var p in pg.optionDefault) {
pg.option[p]=pg.optionDefault[p];
if (typeof window[p]!='undefined') { delete window[p]; }
}
}
/*eslint-enable */
function setOptions() {
// user-settable parameters and defaults
var userIsSysop = false;
if ( mw.config.get('wgUserGroups') ) {
for ( var g = 0; g < mw.config.get('wgUserGroups').length; ++g ) {
if ( mw.config.get('wgUserGroups')[g] == "sysop" )
userIsSysop = true;
}
}
// Basic options
newOption('popupDelay', 0.5);
newOption('popupHideDelay', 0.5);
newOption('simplePopups', false);
newOption('popupStructure', 'shortmenus'); // see later - default for popupStructure is 'original' if simplePopups is true
newOption('popupActionsMenu', true);
newOption('popupSetupMenu', true);
newOption('popupAdminLinks', userIsSysop);
newOption('popupShortcutKeys', false);
newOption('popupHistoricalLinks', true);
newOption('popupOnlyArticleLinks', true);
newOption('removeTitles', true);
newOption('popupMaxWidth', 350);
newOption('popupSimplifyMainLink', true);
newOption('popupAppendRedirNavLinks', true);
newOption('popupTocLinks', false);
newOption('popupSubpopups', true);
newOption('popupDragHandle', false /* 'popupTopLinks'*/);
newOption('popupLazyPreviews', true);
newOption('popupLazyDownloads', true);
newOption('popupAllDabsStubs', false);
newOption('popupDebugging', false);
newOption('popupActiveNavlinks', true);
newOption('popupModifier', false); // ctrl, shift, alt or meta
newOption('popupModifierAction', 'enable'); // or 'disable'
newOption('popupDraggable', true);
newOption('popupReview', false);
//<NOLITE>
// images
newOption('popupImages', true);
newOption('imagePopupsForImages', true);
newOption('popupNeverGetThumbs', false);
//newOption('popupImagesToggleSize', true);
newOption('popupThumbAction', 'imagepage'); //'sizetoggle');
newOption('popupImageSize', 60);
newOption('popupImageSizeLarge', 200);
// redirs, dabs, reversion
newOption('popupFixRedirs', false);
newOption('popupRedirAutoClick', 'wpDiff');
newOption('popupFixDabs', false);
newOption('popupDabsAutoClick', 'wpDiff');
newOption('popupRevertSummaryPrompt', false);
newOption('popupMinorReverts', false);
newOption('popupRedlinkRemoval', false);
newOption('popupRedlinkAutoClick', 'wpDiff');
newOption('popupWatchDisambiggedPages', null);
newOption('popupWatchRedirredPages', null);
newOption('popupDabWiktionary', 'last');
// navlinks
newOption('popupNavLinks', true);
newOption('popupNavLinkSeparator', ' ⋅ ');
newOption('popupLastEditLink', true);
newOption('popupEditCounterTool', 'supercount');
newOption('popupEditCounterUrl', '');
//</NOLITE>
// previews etc
newOption('popupPreviews', true);
newOption('popupSummaryData', true);
newOption('popupMaxPreviewSentences', 9);
newOption('popupMaxPreviewCharacters', 900);
newOption('popupLastModified', true);
newOption('popupPreviewKillTemplates', true);
newOption('popupPreviewRawTemplates', true);
newOption('popupPreviewFirstParOnly', false);
newOption('popupPreviewCutHeadings', false);
newOption('popupPreviewButton', false);
newOption('popupPreviewButtonEvent', 'click');
//<NOLITE>
// diffs
newOption('popupPreviewDiffs', true);
newOption('popupDiffMaxLines', 100);
newOption('popupDiffContextLines', 2);
newOption('popupDiffContextCharacters', 40);
newOption('popupDiffDates', true);
newOption('popupDiffDatePrinter', 'toLocaleString');
// edit summaries. God, these are ugly.
newOption('popupReviewedSummary', popupString('defaultpopupReviewedSummary') );
newOption('popupFixDabsSummary', popupString('defaultpopupFixDabsSummary') );
newOption('popupExtendedRevertSummary', popupString('defaultpopupExtendedRevertSummary') );
newOption('popupRevertSummary', popupString('defaultpopupRevertSummary') );
newOption('popupRevertToPreviousSummary', popupString('defaultpopupRevertToPreviousSummary') );
newOption('popupQueriedRevertSummary', popupString('defaultpopupQueriedRevertSummary') );
newOption('popupQueriedRevertToPreviousSummary', popupString('defaultpopupQueriedRevertToPreviousSummary') );
newOption('popupFixRedirsSummary', popupString('defaultpopupFixRedirsSummary') );
newOption('popupRedlinkSummary', popupString('defaultpopupRedlinkSummary') );
newOption('popupRmDabLinkSummary', popupString('defaultpopupRmDabLinkSummary') );
//</NOLITE>
// misc
newOption('popupHistoryLimit', 50);
//<NOLITE>
newOption('popupFilters', [popupFilterStubDetect, popupFilterDisambigDetect,
popupFilterPageSize, popupFilterCountLinks,
popupFilterCountImages, popupFilterCountCategories,
popupFilterLastModified]);
newOption('extraPopupFilters', []);
newOption('popupOnEditSelection', 'cursor');
newOption('popupPreviewHistory', true);
newOption('popupImageLinks', true);
newOption('popupCategoryMembers', true);
newOption('popupUserInfo', true);
newOption('popupHistoryPreviewLimit', 25);
newOption('popupContribsPreviewLimit',25);
newOption('popupRevDelUrl', '//en.wikipedia.org/wiki/Wikipedia:Revision_deletion');
newOption('popupShowGender', true);
//</NOLITE>
// new windows
newOption('popupNewWindows', false);
newOption('popupLinksNewWindow', {'lastContrib': true, 'sinceMe': true});
// regexps
newOption('popupDabRegexp', '(\\{\\{\\s*disambig(?!uation needed)|disambig(uation|)\\s*\\}\\}|disamb\\s*\\}\\}|dab\\s*\\}\\})|\\{\\{\\s*(((geo|hn|road?|school|number)dis)|[234][lc][acw]|(road|ship)index)(\\s*[|][^}]*)?\\s*[}][}]|is a .*disambiguation.*page');
newOption('popupAnchorRegexp', 'anchors?'); //how to identify an anchors template
newOption('popupStubRegexp', '(sect)?stub[}][}]|This .*-related article is a .*stub');
newOption('popupImageVarsRegexp', 'image|image_(?:file|skyline|name|flag|seal)|cover|badge|logo');
}
// ENDFILE: options.js
// STARTFILE: strings.js
//<NOLITE>
//////////////////////////////////////////////////
// Translatable strings
//////////////////////////////////////////////////
//
// See instructions at
// https://en.wikipedia.org/wiki/Wikipedia:Tools/Navigation_popups/Translation
pg.string = {
/////////////////////////////////////
// summary data, searching etc.
/////////////////////////////////////
'article': 'article',
'category': 'category',
'categories': 'categories',
'image': 'image',
'images': 'images',
'stub': 'stub',
'section stub': 'section stub',
'Empty page': 'Empty page',
'kB': 'kB',
'bytes': 'bytes',
'day': 'day',
'days': 'days',
'hour': 'hour',
'hours': 'hours',
'minute': 'minute',
'minutes': 'minutes',
'second': 'second',
'seconds': 'seconds',
'week': 'week',
'weeks': 'weeks',
'search': 'search',
'SearchHint': 'Find English Wikipedia articles containing %s',
'web': 'web',
'global': 'global',
'globalSearchHint': 'Search across Wikipedias in different languages for %s',
'googleSearchHint': 'Google for %s',
/////////////////////////////////////
// article-related actions and info
// (some actions also apply to user pages)
/////////////////////////////////////
'actions': 'actions', ///// view articles and view talk
'popupsMenu': 'popups',
'togglePreviewsHint': 'Toggle preview generation in popups on this page',
'enable previews': 'enable previews',
'disable previews': 'disable previews',
'toggle previews': 'toggle previews',
'show preview': 'show preview',
'reset': 'reset',
'more...': 'more...',
'disable': 'disable popups',
'disablePopupsHint': 'Disable popups on this page. Reload page to re-enable.',
'historyfeedHint': 'RSS feed of recent changes to this page',
'purgePopupsHint': 'Reset popups, clearing all cached popup data.',
'PopupsHint': 'Reset popups, clearing all cached popup data.',
'spacebar': 'space',
'view': 'view',
'view article': 'view article',
'viewHint': 'Go to %s',
'talk': 'talk',
'talk page': 'talk page',
'this revision': 'this revision',
'revision %s of %s': 'revision %s of %s',
'Revision %s of %s': 'Revision %s of %s',
'the revision prior to revision %s of %s': 'the revision prior to revision %s of %s',
'Toggle image size': 'Click to toggle image size',
'del': 'del', ///// delete, protect, move
'delete': 'delete',
'deleteHint': 'Delete %s',
'undeleteShort': 'un',
'UndeleteHint': 'Show the deletion history for %s',
'protect': 'protect',
'protectHint': 'Restrict editing rights to %s',
'unprotectShort': 'un',
'unprotectHint': 'Allow %s to be edited by anyone again',
'send thanks': 'send thanks',
'ThanksHint': 'Send a thank you notification to this user',
'move': 'move',
'move page': 'move page',
'MovepageHint': 'Change the title of %s',
'edit': 'edit', ///// edit articles and talk
'edit article': 'edit article',
'editHint': 'Change the content of %s',
'edit talk': 'edit talk',
'new': 'new',
'new topic': 'new topic',
'newSectionHint': 'Start a new section on %s',
'null edit': 'null edit',
'nullEditHint': 'Submit an edit to %s, making no changes ',
'hist': 'hist', ///// history, diffs, editors, related
'history': 'history',
'historyHint': 'List the changes made to %s',
'last': 'prev', // For labelling the previous revision in history pages; the key is "last" for backwards compatibility
'lastEdit': 'lastEdit',
'mark patrolled': 'mark patrolled',
'markpatrolledHint': 'Mark this edit as patrolled',
'Could not marked this edit as patrolled': 'Could not marked this edit as patrolled',
'show last edit': 'most recent edit',
'Show the last edit': 'Show the effects of the most recent change',
'lastContrib': 'lastContrib',
'last set of edits': 'latest edits',
'lastContribHint': 'Show the net effect of changes made by the last editor',
'cur': 'cur',
'diffCur': 'diffCur',
'Show changes since revision %s': 'Show changes since revision %s',
'%s old': '%s old', // as in 4 weeks old
'oldEdit': 'oldEdit',
'purge': 'purge',
'purgeHint': 'Demand a fresh copy of %s',
'raw': 'source',
'rawHint': 'Download the source of %s',
'render': 'simple',
'renderHint': 'Show a plain HTML version of %s',
'Show the edit made to get revision': 'Show the edit made to get revision',
'sinceMe': 'sinceMe',
'changes since mine': 'diff my edit',
'sinceMeHint': 'Show changes since my last edit',
'Couldn\'t find an edit by %s\nin the last %s edits to\n%s': 'Couldn\'t find an edit by %s\nin the last %s edits to\n%s',
'eds': 'eds',
'editors': 'editors',
'editorListHint': 'List the users who have edited %s',
'related': 'related',
'relatedChanges': 'relatedChanges',
'related changes': 'related changes',
'RecentchangeslinkedHint': 'Show changes in articles related to %s',
'editOld': 'editOld', ///// edit old version, or revert
'rv': 'rv',
'revert': 'revert',
'revertHint': 'Revert to %s',
'defaultpopupReviewedSummary': 'Accepted by reviewing the [[Special:diff/%s/%s|difference]] between this version and previously accepted version using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRedlinkSummary': 'Removing link to empty page [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupFixDabsSummary': 'Disambiguate [[%s]] to [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupFixRedirsSummary': 'Redirect bypass from [[%s]] to [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupExtendedRevertSummary': 'Revert to revision dated %s by %s, oldid %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRevertToPreviousSummary': 'Revert to the revision prior to revision %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRevertSummary': 'Revert to revision %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupQueriedRevertToPreviousSummary': 'Revert to the revision prior to revision $1 dated $2 by $3 using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupQueriedRevertSummary': 'Revert to revision $1 dated $2 by $3 using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRmDabLinkSummary': 'Remove link to dab page [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'Redirects': 'Redirects', // as in Redirects to ...
' to ': ' to ', // as in Redirects to ...
'Bypass redirect': 'Bypass redirect',
'Fix this redirect': 'Fix this redirect',
'disambig': 'disambig', ///// add or remove dab etc.
'disambigHint': 'Disambiguate this link to [[%s]]',
'Click to disambiguate this link to:': 'Click to disambiguate this link to:',
'remove this link': 'remove this link',
'remove all links to this page from this article': 'remove all links to this page from this article',
'remove all links to this disambig page from this article': 'remove all links to this disambig page from this article',
'mainlink': 'mainlink', ///// links, watch, unwatch
'wikiLink': 'wikiLink',
'wikiLinks': 'wikiLinks',
'links here': 'links here',
'whatLinksHere': 'whatLinksHere',
'what links here': 'what links here',
'WhatlinkshereHint': 'List the pages that are hyperlinked to %s',
'unwatchShort': 'un',
'watchThingy': 'watch', // called watchThingy because {}.watch is a function
'watchHint': 'Add %s to my watchlist',
'unwatchHint': 'Remove %s from my watchlist',
'Only found one editor: %s made %s edits': 'Only found one editor: %s made %s edits',
'%s seems to be the last editor to the page %s': '%s seems to be the last editor to the page %s',
'rss': 'rss',
/////////////////////////////////////
// diff previews
/////////////////////////////////////
'Diff truncated for performance reasons': 'Diff truncated for performance reasons',
'Old revision': 'Old revision',
'New revision': 'New revision',
'Something went wrong :-(': 'Something went wrong :-(',
'Empty revision, maybe non-existent': 'Empty revision, maybe non-existent',
'Unknown date': 'Unknown date',
/////////////////////////////////////
// other special previews
/////////////////////////////////////
'Empty category': 'Empty category',
'Category members (%s shown)': 'Category members (%s shown)',
'No image links found': 'No image links found',
'File links': 'File links',
'No image found': 'No image found',
'Image from Commons': 'Image from Commons',
'Description page': 'Description page',
'Alt text:': 'Alt text:',
'revdel':'Hidden revision',
/////////////////////////////////////
// user-related actions and info
/////////////////////////////////////
'user': 'user', ///// user page, talk, email, space
'user page': 'user page',
'user talk': 'user talk',
'edit user talk': 'edit user talk',
'leave comment': 'leave comment',
'email': 'email',
'email user': 'email user',
'EmailuserHint': 'Send an email to %s',
'space': 'space', // short form for userSpace link
'PrefixIndexHint': 'Show pages in the userspace of %s',
'count': 'count', ///// contributions, log
'edit counter': 'edit counter',
'editCounterLinkHint': 'Count the contributions made by %s',
'contribs': 'contribs',
'contributions': 'contributions',
'deletedContribs': 'deleted contributions',
'DeletedcontributionsHint': 'List deleted edits made by %s',
'ContributionsHint': 'List the contributions made by %s',
'log': 'log',
'user log': 'user log',
'userLogHint': 'Show %s\'s user log',
'arin': 'ARIN lookup', ///// ARIN lookup, block user or IP
'Look up %s in ARIN whois database': 'Look up %s in the ARIN whois database',
'unblockShort': 'un',
'block': 'block',
'block user': 'block user',
'IpblocklistHint': 'Unblock %s',
'BlockipHint': 'Prevent %s from editing',
'block log': 'block log',
'blockLogHint': 'Show the block log for %s',
'protectLogHint': 'Show the protection log for %s',
'pageLogHint': 'Show the page log for %s',
'deleteLogHint': 'Show the deletion log for %s',
'Invalid %s %s': 'The option %s is invalid: %s',
'No backlinks found': 'No backlinks found',
' and more': ' and more',
'undo': 'undo',
'undoHint': 'undo this edit',
'Download preview data': 'Download preview data',
'Invalid or IP user': 'Invalid or IP user',
'Not a registered username': 'Not a registered username',
'BLOCKED': 'BLOCKED',
'Has blocks': 'Has blocks',
' edits since: ': ' edits since: ',
'last edit on ': 'last edit on ',
/////////////////////////////////////
// Autoediting
/////////////////////////////////////
'Enter a non-empty edit summary or press cancel to abort': 'Enter a non-empty edit summary or press cancel to abort',
'Failed to get revision information, please edit manually.\n\n': 'Failed to get revision information, please edit manually.\n\n',
'The %s button has been automatically clicked. Please wait for the next page to load.': 'The %s button has been automatically clicked. Please wait for the next page to load.',
'Could not find button %s. Please check the settings in your javascript file.': 'Could not find button %s. Please check the settings in your javascript file.',
/////////////////////////////////////
// Popups setup
/////////////////////////////////////
'Open full-size image': 'Open full-size image',
'zxy': 'zxy',
'autoedit_version': 'np20140416'
};
function popupString(str) {
if (typeof popupStrings != 'undefined' && popupStrings && popupStrings[str]) { return popupStrings[str]; }
if (pg.string[str]) { return pg.string[str]; }
return str;
}
function tprintf(str,subs) {
if (typeof subs != typeof []) { subs = [subs]; }
return simplePrintf(popupString(str), subs);
}
//</NOLITE>
// ENDFILE: strings.js
// STARTFILE: run.js
////////////////////////////////////////////////////////////////////
// Run things
////////////////////////////////////////////////////////////////////
// For some reason popups requires a fully loaded page jQuery.ready(...) causes problems for some.
// The old addOnloadHook did something similar to the below
if (document.readyState=="complete")
autoEdit(); //will setup popups
else
$( window ).on( 'load', autoEdit );
// Support for MediaWiki's live preview, VisualEditor's saves and Echo's flyout.
( function () {
var once = true;
function dynamicContentHandler( $content ) {
// Try to detect the hook fired on initial page load and disregard
// it, we already hook to onload (possibly to different parts of
// page - it's configurable) and running twice might be bad. Ugly…
if ( $content.attr( 'id' ) == 'mw-content-text' ) {
if ( once ) {
once = false;
return;
}
}
function registerHooksForVisibleNavpops () {
for (var i=0; pg.current.links && i<pg.current.links.length; ++i) {
var navpop = pg.current.links[i].navpopup;
if (!navpop || !navpop.isVisible()) { continue; }
Navpopup.tracker.addHook(posCheckerHook(navpop));
}
}
function doIt () {
registerHooksForVisibleNavpops();
$content.each( function () {
this.ranSetupTooltipsAlready = false;
setupTooltips( this );
} );
}
setupPopups( doIt );
}
// This hook is also fired after page load.
mw.hook( 'wikipage.content' ).add( dynamicContentHandler );
mw.hook( 'ext.echo.overlay.beforeShowingOverlay' ).add( function($overlay){
dynamicContentHandler( $overlay.find(".mw-echo-state") );
});
} )();
// Forked from [[User:Connel MacKenzie/mess-with-popups.js]]
Previewmaker.prototype.firstBit = function() {
try{
if (mw.config.get("wgNamespaceNumber") !== 0){
// dont't be givin' me no subsequent paragraphs, you hear me?
/// first we "normalize" section headings, removing whitespace after, adding before
var d = this.data;
if (getValueOf('popupPreviewCutHeadings')) {
this.data = this.data.replace(RegExp('\\s*(==+[^=]*==+)\\s*', 'g'), '\n\n$1 ');
/// then we want to get rid of paragraph breaks whose text ends badly
this.data = this.data.replace(RegExp('([:;]) *\\n{2,}', 'g'), '$1\n');
this.data = this.data.replace(RegExp('^[\\s\\n]*'), '');
stuff = (RegExp('^([^\\n]|\\n[^\\n\\s])*')).exec(this.data);
if (stuff) {
d = stuff[0];
}
if (!getValueOf('popupPreviewFirstParOnly')) {
d = this.data;
}
/// now put \n\n after sections so that bullets and numbered lists work
d = d.replace(RegExp('(==+[^=]*==+)\\s*', 'g'), '$1\n\n');
}
// superfluous sentences are RIGHT OUT.
// note: exactly 1 set of parens here needed to make the slice work
d = d.parenSplit(RegExp('([!?.]+["' + "'" + ']*\\s)', 'g'));
// leading space is bad, mmkay?
d[0] = d[0].replace(RegExp('^\\s*'), '');
var notSentenceEnds = RegExp('([^.][a-z][.][a-z]|etc|sic|Dr|Mr|Mrs|Ms|St|no|\\[[^\\]]*|\\s[A-Zvclm])$', 'i');
d = this.fixSentenceEnds(d, notSentenceEnds);
var maxChars=getValueOf('popupMaxPreviewCharacters');
var n = getValueOf('popupMaxPreviewSentences');
var dd;
do {
dd = this.firstSentences(d, n);
--n;
}
while (dd.length > maxChars && n > 0);
this.data = dd;
} else {
//TODO: Limit this override to urls that start with http://*.wiktionary.org/
//TODO: extend the above section to also preview Wikipedia links
var charsWikt = 0;
var linesWikt = this.data.split('\n');
for (jWikt = 0; jWikt < linesWikt.length; jWikt++) {
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{[in]transitive}}/g, '');
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{[un]countable}}/g, '');
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{cattag\|(.*?)}}/g, "");
linesWikt[jWikt] = linesWikt[jWikt].replace(/[\{\}]/g, "'");
if (linesWikt[jWikt].search(/^[#]/) == -1) {
if (linesWikt[jWikt].search(/^==[A-Z]/) == -1) {
linesWikt[jWikt] = '';
}
}
charsWikt += linesWikt[jWikt].length;
}
if (charsWikt < 2) {
linesWikt[0] = "No properly formatted content";
}
var maxChars = getValueOf('popupMaxPreviewCharacters');
var n = getValueOf('popupMaxPreviewSentences');
var dd = '';
for (jWikt = 0; jWikt < linesWikt.length; jWikt++) {
if (linesWikt[jWikt] !== '') {
dd += linesWikt[jWikt] + '\n';
}
if (dd.length < maxChars) {
this.data = dd;
}
}
}
}
catch (e) {
console.log("error from mess with popups: ", e);
}
};
});
orsd53nf3vg9wb097lspoxfv57tbbij
မဳဒဳယာဝဳကဳ:Gadget-popups.js/documentation
8
295692
396510
2026-06-07T14:49:31Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "{{documentation subpage}} <!--Add description here. This subpage initially added just for categorization.--> See also: [[Special:Gadgets]]. <includeonly> [[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]] </includeonly>"
396510
wikitext
text/x-wiki
{{documentation subpage}}
<!--Add description here. This subpage initially added just for categorization.-->
See also: [[Special:Gadgets]].
<includeonly>
[[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]]
</includeonly>
nh2z4mxstdhoy5k1d14op93dv5v8194
မဳဒဳယာဝဳကဳ:Gadget-LocalLiveClock.js
8
295693
396511
2026-06-07T14:51:41Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "// {{documentation}} mw.loader.load( '//www.mediawiki.org/w/index.php?title=MediaWiki:Gadget-LocalLiveClock.js&action=raw&ctype=text/javascript&smaxage=21600&maxage=86400' );"
396511
javascript
text/javascript
// {{documentation}}
mw.loader.load( '//www.mediawiki.org/w/index.php?title=MediaWiki:Gadget-LocalLiveClock.js&action=raw&ctype=text/javascript&smaxage=21600&maxage=86400' );
mlnbe15x5acg4ptrdldcl7vsnxe28ky
မဳဒဳယာဝဳကဳ:Gadget-LocalLiveClock.js/documentation
8
295694
396512
2026-06-07T14:52:41Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "{{documentation subpage}} <!--Add description here. This subpage initially added just for categorization.--> See also: [[Special:Gadgets]]. <includeonly> [[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]] </includeonly>"
396512
wikitext
text/x-wiki
{{documentation subpage}}
<!--Add description here. This subpage initially added just for categorization.-->
See also: [[Special:Gadgets]].
<includeonly>
[[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]]
</includeonly>
nh2z4mxstdhoy5k1d14op93dv5v8194
မဳဒဳယာဝဳကဳ:Gadget-linkLanguageHeaders.js
8
295695
396513
2026-06-07T14:59:13Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု ""use strict"; // {{documentation}} // <nowiki> (function linkLanguageHeadersGadget() { var CACHE_DURATION = 24 * 60 * 60; // 24 hours var ALLOWED_NAMESPACES = [ 0, // main 118 // Reconstruction ]; function categoryNameFromLanguageName(name) { return ("ဘာသာ" + name).replace("ဘာသာ", /Language language$/); } function getLanguageCodesByCanonicalName(callback, cacheDuration) { va..."
396513
javascript
text/javascript
"use strict";
// {{documentation}}
// <nowiki>
(function linkLanguageHeadersGadget() {
var CACHE_DURATION = 24 * 60 * 60; // 24 hours
var ALLOWED_NAMESPACES = [
0, // main
118 // Reconstruction
];
function categoryNameFromLanguageName(name) {
return ("ဘာသာ" + name).replace("ဘာသာ", /Language language$/);
}
function getLanguageCodesByCanonicalName(callback, cacheDuration) {
var KEY = "mnwwiktLanguageCanonicalNamesJson";
var timeNow = new Date().getTime() * 1e-3;
try {
var cachedData = JSON.parse(localStorage.getItem(KEY));
if (timeNow - cachedData.timestamp < cacheDuration) {
callback(cachedData.data);
return;
}
} catch (e) { }
var request = new mw.Api().get({
"action": "parse",
"page": "Module:languages/canonical names.json",
"prop": "wikitext",
"formatversion": 2,
"format": "json"
});
request.done(function(response) {
var languageData = JSON.parse(response.parse.wikitext);
callback(languageData);
localStorage.setItem(KEY, JSON.stringify({
timestamp: timeNow,
data: languageData
}));
});
}
function linkLanguageHeaders(codeByCanonicalName) {
var categoryPrefix = mnwcodeURIComponent(mw.config.get("wgFormattedNamespaces")[14].replace(/ /g, "_")) + ":";
$("#mw-content-text h2 .mw-headline, #mw-content-text .mw-heading2 h2").each(function() {
var headline = $(this);
var languageName = headline.text();
var languageCode = codeByCanonicalName[languageName];
if (languageCode) {
var categoryName = categoryNameFromLanguageName(languageName);
headline.html($("<a>", {
text: languageName,
title: languageName + " [" + languageCode + "]",
href: mw.config.get("wgArticlePath").replace("$1", categoryPrefix + mnwcodeURIComponent(categoryName.replace(/ /g, "_")))
}));
}
});
}
mw.hook("wikipage.content").add(function($) {
if (mw.config.get("wgAction") === "ဗီုလညာတ်" &&
ALLOWED_NAMESPACES.indexOf(mw.config.get("wgNamespaceNumber")) >= 0) {
getLanguageCodesByCanonicalName(function (codeByCanonicalName) {
linkLanguageHeaders(codeByCanonicalName);
}, CACHE_DURATION);
}
});
})();
// </nowiki>
7dofrlp6plbe6u4fjtmquure70exjd3
မဝ်ဂျူ:languages/canonical names.json
828
295696
396514
2026-06-07T15:00:02Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "{ "'Are'are": "alu", "A'ou": "aou", "A-Hmao": "hmd", "A-Pucikwar": "apq", "Aari": "aiw", "Aasax": "aas", "Aba": "utp", "Abaga": "abg", "Abai": "poz-abi", "Abai Sungai": "abf", "Abanyom": "abm", "Abau": "aau", "Abaza": "abq", "Abenaki": "abe", "Abenlen Ayta": "abp", "Abidji": "abi", "Abinomn": "bsa", "Abipón": "axb", "Abishira": "ash", "Abkhaz": "ab", "Abom": "aob", "Abon": "abo", "Aborlan Tagba..."
396514
json
application/json
{
"'Are'are": "alu",
"A'ou": "aou",
"A-Hmao": "hmd",
"A-Pucikwar": "apq",
"Aari": "aiw",
"Aasax": "aas",
"Aba": "utp",
"Abaga": "abg",
"Abai": "poz-abi",
"Abai Sungai": "abf",
"Abanyom": "abm",
"Abau": "aau",
"Abaza": "abq",
"Abenaki": "abe",
"Abenlen Ayta": "abp",
"Abidji": "abi",
"Abinomn": "bsa",
"Abipón": "axb",
"Abishira": "ash",
"Abkhaz": "ab",
"Abom": "aob",
"Abon": "abo",
"Aborlan Tagbanwa": "tbw",
"Abu": "ado",
"Abu'": "aah",
"Abua": "abn",
"Abui": "abz",
"Abun": "kgr",
"Abung": "abl",
"Abure": "abu",
"Abureni": "mgj",
"Abé": "aba",
"Acatepec Me'phaa": "tpx",
"Acehnese": "ace",
"Achagua": "aca",
"Achang": "acn",
"Ache": "yif",
"Acheron": "acz",
"Achi": "acr",
"Acholi": "ach",
"Achuar": "acu",
"Achumawi": "acv",
"Aché": "guq",
"Acroá": "acs",
"Adai": "xad",
"Adamorobe Sign Language": "ads",
"Adang": "adn",
"Adangbe": "adq",
"Adangme": "ada",
"Adap": "adp",
"Adasen": "tiu",
"Adele": "ade",
"Adhola": "adh",
"Adi": "adi",
"Adioukrou": "adj",
"Adithinngithigh": "dth",
"Adivasi Odia": "ort",
"Adiwasi Garasia": "gas",
"Adnyamathanha": "adt",
"Adonara": "adr",
"Aduge": "adu",
"Adzera": "adz",
"Adûni": "art-adu",
"Aeka": "aez",
"Aekyom": "awi",
"Aequian": "xae",
"Aer": "aeq",
"Afade": "aal",
"Afar": "aa",
"Afghan Sign Language": "afg",
"Afitti": "aft",
"Afra": "ulf",
"Afrihili": "afh",
"Afrikaans": "af",
"Afro-Seminole Creole": "afs",
"Agarabi": "agd",
"Agariya": "agi",
"Agatu": "agc",
"Agavotaguerra": "avo",
"Agawam": "alg-aga",
"Aghem": "agq",
"Aghu": "ahh",
"Aghu Tharrnggala": "gtu",
"Aghul": "agx",
"Aghwan": "xag",
"Agi": "aif",
"Agob": "kit",
"Agoi": "ibm",
"Aguacateca": "agu",
"Aguano": "aga",
"Aguaruna": "agr",
"Aguna": "aug",
"Agusan Manobo": "msm",
"Agutaynen": "agn",
"Agwagwune": "yay",
"Ahanta": "aha",
"Ahirani": "ahr",
"Ahom": "aho",
"Ahtna": "aht",
"Ahwai": "nfd",
"Ai-Cham": "aih",
"Aighon": "aix",
"Aikanã": "tba",
"Aiklep": "mwg",
"Aimele": "ail",
"Aimol": "aim",
"Ainbai": "aic",
"Ainu": "ain",
"Aiome": "aki",
"Airoran": "air",
"Aisi": "mmq",
"Aiton": "aio",
"Aja (East Africa)": "aja",
"Aja (West Africa)": "ajg",
"Ajawa": "ajw",
"Ajië": "aji",
"Ak": "akq",
"Aka (Central Africa)": "axk",
"Aka (Sudan)": "soh",
"Aka-Bea": "abj",
"Aka-Bo": "akm",
"Aka-Cari": "aci",
"Aka-Kede": "akx",
"Aka-Kol": "aky",
"Aka-Kora": "ack",
"Akan": "ak",
"Akar-Bale": "acl",
"Akaselem": "aks",
"Akatek": "knj",
"Akawaio": "ake",
"Ake": "aik",
"Akebu": "keu",
"Akei": "tsr",
"Akeu": "aeu",
"Akha": "ahk",
"Akhvakh": "akv",
"Akkadian": "akk",
"Akkala Sami": "sia",
"Aklanon": "akl",
"Akolet": "akt",
"Akoose": "bss",
"Akoye": "miw",
"Akpa": "akf",
"Akpes": "ibe",
"Akrukay": "afi",
"Akuku": "ayk",
"Akum": "aku",
"Akuntsu": "aqz",
"Akurio": "ako",
"Akwa": "akw",
"Akyaung Ari": "nqy",
"Al-Sayyid Bedouin Sign Language": "syy",
"Alaba": "alw",
"Alabama": "akz",
"Alabat Island Agta": "dul",
"Alacatlatzala Mixtec": "mim",
"Alago": "ala",
"Alagwa": "wbj",
"Alak": "alk",
"Alamblak": "amp",
"Alangan": "alj",
"Alanic": "xln",
"Alapmunte": "apv",
"Alas-Kluet Batak": "btz",
"Alawa": "alh",
"Alazapa": "nai-ala",
"Albanian": "sq",
"Albanian Sign Language": "sqk",
"Alchuka": "tuw-alk",
"Alcozauca Mixtec": "xta",
"Alege": "alf",
"Alekano": "gah",
"Alemannic German": "gsw",
"Aleut": "ale",
"Algerian Arabic": "arq",
"Algerian Sign Language": "asp",
"Algonquin": "alq",
"Ali": "aiy",
"Alladian": "ald",
"Allar": "all",
"Allentiac": "sai-all",
"Alngith": "aid",
"Alo Phola": "ypo",
"Alorese": "aol",
"Aloápam Zapotec": "zaq",
"Alsea": "aes",
"Alu": "mte",
"Alu Kurumba": "xua",
"Alugu": "aub",
"Alumu-Tesu": "aab",
"Alune": "alp",
"Alungul": "aus-alu",
"Aluo": "yna",
"Alur": "alz",
"Alutiiq": "ems",
"Alutor": "alr",
"Alviri-Vidari": "avd",
"Alyawarr": "aly",
"Ama": "amm",
"Amahai": "amq",
"Amahuaca": "amc",
"Amaimon": "ali",
"Amal": "aad",
"Amanab": "amn",
"Amanayé": "ama",
"Amara": "aie",
"Amarakaeri": "amr",
"Amarasi": "aaz",
"Amarizana": "awd-ama",
"Amasi": "alv-ama",
"Amatlán Zapotec": "zpo",
"Amba": "rwm",
"Ambai": "amk",
"Ambakich": "aew",
"Ambala Ayta": "abc",
"Ambelau": "amv",
"Ambele": "ael",
"Amblong": "alm",
"Ambo": "amb",
"Ambonese Malay": "abs",
"Ambrak": "aag",
"Ambul": "apo",
"Ambulas": "abt",
"Amdang": "amj",
"Amele": "aey",
"American Sign Language": "ase",
"Amganad Ifugao": "ifa",
"Amharic": "am",
"Ami": "amy",
"Amis": "ami",
"Ammonite": "sem-amm",
"Amo": "amo",
"Amol": "alx",
"Amoltepec Mixtec": "mbz",
"Amondawa": "adw",
"Amorite": "sem-amo",
"Ampanang": "apg",
"Ampari Dogon": "aqd",
"Amri Karbi": "ajz",
"Amto": "amt",
"Amurdag": "amg",
"Ana Tinga Dogon": "dti",
"Anaang": "anw",
"Anakalangu": "akg",
"Anam": "pda",
"Anambé": "aan",
"Anamgura": "imi",
"Anasi": "bpo",
"Anauyá": "awd-ana",
"Ancient Greek": "grc",
"Ancient Ligurian": "xlg",
"Ancient Macedonian": "xmk",
"Ancient North Arabian": "xna",
"Ancient Zapotec": "xzp",
"Andai": "afd",
"Andajin": "ajn",
"Andalusian Arabic": "xaa",
"Andaman Creole Hindi": "hca",
"Andaqui": "ana",
"Andarum": "aod",
"Andegerebinha": "adg",
"Andh": "anr",
"Andi": "ani",
"Andio": "bzb",
"Andjingith": "aus-and",
"Andoa": "anb",
"Andoque": "ano",
"Andoquero": "sai-and",
"Andra-Hus": "anx",
"Aneityum": "aty",
"Anem": "anz",
"Aneme Wake": "aby",
"Anfillo": "myo",
"Angaataha": "agm",
"Angaité": "aqt",
"Angal": "age",
"Angal Enen": "aoe",
"Angal Heneng": "akh",
"Angami": "njm",
"Angevin": "roa-ang",
"Angguruk Yali": "yli",
"Angika": "anp",
"Angkamuthi": "avm",
"Angkola Batak": "akb",
"Angkula": "aus-ang",
"Angloromani": "rme",
"Angolar": "aoa",
"Angor": "agg",
"Angoram": "aog",
"Angosturas Tunebo": "tnd",
"Anguthimri": "awg",
"Ani Phowa": "ypn",
"Anii": "blo",
"Animere": "anf",
"Anindilyakwa": "aoi",
"Anjam": "boj",
"Ankave": "aak",
"Anmatyerre": "amx",
"Annobonese": "fab",
"Anong": "nun",
"Anor": "anj",
"Anserma": "ans",
"Ansus": "and",
"Antakarinya": "ant",
"Antigua and Barbuda Creole English": "aig",
"Antillean Creole": "gcf",
"Anu": "anl",
"Anuak": "anu",
"Anufo": "cko",
"Anuki": "aui",
"Anus": "auq",
"Anuta": "aud",
"Anyi": "any",
"Anyin Morofo": "mtb",
"Anāl": "anm",
"Ao": "njo",
"Aoheng": "pni",
"Aore": "aor",
"Ap Ma": "kbx",
"Apabhramsa": "inc-apa",
"Apalachee": "xap",
"Apalaí": "apy",
"Apali": "ena",
"Apasco-Apoala Mixtec": "mip",
"Apatani": "apt",
"Apiaká": "api",
"Apinayé": "apn",
"Apma": "app",
"Apolista": "awd-apo",
"Apro": "ahp",
"Apurinã": "apu",
"Apurucayali Ashéninka": "cpc",
"Aputai": "apx",
"Arabana": "ard",
"Arabela": "arl",
"Arabic": "ar",
"Aragonese": "an",
"Araki": "akr",
"Arakwal": "rkw",
"Aralle-Tabulahan": "atq",
"Aramaic": "arc",
"Arammba": "stk",
"Aranadan": "aaf",
"Aranama-Tamique": "xrt",
"Arandai": "jbj",
"Araona": "aro",
"Arapaho": "arp",
"Arapaso": "arj",
"Arara-Karo": "arr",
"Ararandewára": "xaj",
"Araweté": "awt",
"Arawum": "awm",
"Arbore": "arv",
"Archi": "aqc",
"Are": "mwc",
"Areba": "aea",
"Arem": "aem",
"Argentine Sign Language": "aed",
"Argobba": "agj",
"Arguni": "agf",
"Arhuaco": "arh",
"Arhâ": "aqr",
"Arhö": "aok",
"Ari": "aac",
"Aribwatsa": "laz",
"Aribwaung": "ylu",
"Arifama-Miniafia": "aai",
"Arigidi": "aqg",
"Arikapú": "ark",
"Arikara": "ari",
"Arikem": "ait",
"Arin": "xrn",
"Aringa": "luc",
"Armazic": "xrm",
"Armenian": "hy",
"Armenian Sign Language": "aen",
"Aromanian": "rup",
"Arop-Lokep": "apr",
"Arop-Sissano": "aps",
"Arosi": "aia",
"Arritinngithigh": "rrt",
"Arta": "atz",
"Arua": "aru",
"Aruamu": "msy",
"Aruek": "aur",
"Aruop": "lsr",
"Arutani": "atx",
"Aruá": "arx",
"Arára (Mato Grosso)": "axg",
"Arára (Pará)": "aap",
"As": "asz",
"Asaba": "seo",
"Asaro'o": "mtv",
"Ashe": "ahs",
"Ashkun": "ask",
"Asho Chin": "csh",
"Ashokan Prakrit": "inc-ash",
"Ashraaf": "cus-ash",
"Asháninka": "cni",
"Asi": "bno",
"Asilulu": "asl",
"Askopan": "eiv",
"Asoa": "asv",
"Assamese": "as",
"Assan": "xss",
"Assangori": "sjg",
"Assiniboine": "asb",
"Assyrian Neo-Aramaic": "aii",
"Asturian": "ast",
"Asu": "aum",
"Asue Awyu": "psa",
"Asumboa": "aua",
"Asunción Mixtepec Zapotec": "zoo",
"Asuri": "asr",
"Ata": "atm",
"Ata Manobo": "atd",
"Atakapa": "aqp",
"Atampaya": "amz",
"Atanques": "cba-ata",
"Atatláhuca Mixtec": "mib",
"Atauran": "adb",
"Atayal": "tay",
"Atemble": "ate",
"Ateso": "teo",
"Athpare": "aph",
"Ati": "atk",
"Atikamekw": "atj",
"Atohwaim": "aqm",
"Atong (Cameroon)": "ato",
"Atong (India)": "aot",
"Atorada": "aox",
"Atsahuaca": "atc",
"Atsam": "cch",
"Atsugewi": "atw",
"Attapady Kurumba": "pkr",
"Attié": "ati",
"Au": "avt",
"Auhelawa": "kud",
"Aukan": "djk",
"Aulua": "aul",
"Aurá": "aux",
"Aushi": "auh",
"Aushiri": "avs",
"Auslan": "asf",
"Austral": "aut",
"Australian Aboriginal Sign Language": "asw",
"Australian Kriol": "rop",
"Austrian Sign Language": "asq",
"Auwe": "smf",
"Auyana": "auy",
"Auye": "auu",
"Auyokawa": "auo",
"Avar": "av",
"Avatime": "avn",
"Avau": "avb",
"Avava": "tmb",
"Avestan": "ae",
"Avikam": "avi",
"Avokaya": "avu",
"Avá-Canoeiro": "avv",
"Awa (China)": "vwa",
"Awa (New Guinea)": "awb",
"Awa-Cuaiquer": "kwi",
"Awabakal": "awk",
"Awadhi": "awa",
"Awak": "awo",
"Awar": "aya",
"Awara": "awx",
"Awbono": "awh",
"Aweer": "bob",
"Awera": "awr",
"Awetí": "awe",
"Awing": "azo",
"Awjila": "auj",
"Awngi": "awn",
"Awngthim": "gwm",
"Awtuw": "kmn",
"Awun": "aww",
"Awutu": "afu",
"Awyi": "auw",
"Axamb": "ahb",
"Axi": "yix",
"Ayabadhu": "ayd",
"Ayautla Mazatec": "vmy",
"Ayere": "aye",
"Ayerrerenge": "axe",
"Ayi": "ayq",
"Ayizi": "yyz",
"Ayizo": "ayb",
"Aymara": "ay",
"Ayomán": "sai-ayo",
"Ayoquesco Zapotec": "zaf",
"Ayoreo": "ayo",
"Ayu": "ayu",
"Ayutla Mixtec": "miy",
"Azerbaijani": "az",
"Azha": "aza",
"Azhe": "yiz",
"Azoyú Me'phaa": "tpc",
"Baa": "kwb",
"Baagandji": "drl",
"Baan": "bvj",
"Baangi": "bqx",
"Baatonum": "bba",
"Baba": "bbw",
"Baba Malay": "mbf",
"Babango": "bbm",
"Babanki": "bbk",
"Babatana": "baa",
"Babine-Witsuwit'en": "bcr",
"Babole": "bvx",
"Babungo": "bav",
"Babuza": "bzg",
"Bacama": "bcy",
"Bacanese Malay": "btj",
"Bactrian": "xbc",
"Bada": "bhz",
"Badaga": "bfq",
"Badanchi": "bau",
"Bade": "bde",
"Badeshi": "bdz",
"Badimaya": "bia",
"Badjiri": "jbi",
"Baduy": "bac",
"Badyara": "pbp",
"Baeggu": "bvd",
"Baekje": "pkc",
"Baelelea": "bvc",
"Baenan": "sai-bae",
"Baetora": "btr",
"Bafanji": "bfj",
"Bafaw": "bwt",
"Bafia": "ksf",
"Bafut": "bfd",
"Baga Kaloum": "bqf",
"Baga Koga": "bgo",
"Baga Manduri": "bmd",
"Baga Pokur": "bcg",
"Baga Sitemu": "bsp",
"Baga Sobané": "bsv",
"Bagheli": "bfy",
"Bagirmi": "bmi",
"Bago-Kusuntu": "bqg",
"Bagri": "bgq",
"Bagua": "sai-bag",
"Bagupi": "bpi",
"Bagusa": "bqb",
"Bagvalal": "kva",
"Baha": "yha",
"Baham": "bdw",
"Bahamian Creole": "bah",
"Baharna Arabic": "abv",
"Bahau": "bhv",
"Bahinemo": "bjh",
"Bahing": "bhj",
"Bahnar": "bdq",
"Bahonsuai": "bsu",
"Bai (South Sudan)": "bdj",
"Baibai": "bbf",
"Baikeno": "bkx",
"Bailang": "tbq-blg",
"Baima": "bqh",
"Baimak": "bmx",
"Bainouk Gubeeher": "alv-bgu",
"Bainouk-Gunyaamolo": "bcz",
"Bainouk-Gunyuño": "bab",
"Bainouk-Samik": "bcb",
"Baiso": "bsw",
"Baissa Fali": "fah",
"Bajan": "bjs",
"Bajelani": "bjm",
"Bajjika": "vjk",
"Baka": "bkc",
"Bakairí": "bkq",
"Bakaka": "bqz",
"Bakhtiari": "bqi",
"Baki": "bki",
"Bakoko": "bkh",
"Bakole": "kme",
"Bakpinka": "bbs",
"Bakulung": "bbu",
"Bakumpai": "bkr",
"Bakung": "xkl",
"Bakwé": "bjw",
"Bala": "tuw-bal",
"Balaesang": "bls",
"Balangao": "blw",
"Balangingi": "sse",
"Balanta-Ganja": "bjt",
"Balanta-Kentohe": "ble",
"Balantak": "blz",
"Balau": "blg",
"Baldemu": "bdn",
"Bali": "bcp",
"Baliledo": "poz-bal",
"Balinese": "ban",
"Balinese Malay": "mhp",
"Balkan Gagauz Turkish": "bgx",
"Balkan Romani": "rmn",
"Balo": "bqo",
"Baloi": "biz",
"Balong": "bnt-bal",
"Balti": "bft",
"Baltic Romani": "rml",
"Baluan-Pam": "blq",
"Baluchi": "bal",
"Bamako Sign Language": "bog",
"Bamali": "bbq",
"Bambalang": "bmo",
"Bambam": "ptu",
"Bambara": "bm",
"Bambassi": "myf",
"Bambili-Bambui": "baw",
"Bamenyam": "bce",
"Bamu": "bcf",
"Bamukumbit": "bqt",
"Bamum": "bax",
"Bamunka": "bvm",
"Bamwe": "bmg",
"Ban Khor Sign Language": "bfk",
"Bana": "bcw",
"Banao Itneg": "bjx",
"Banaro": "byz",
"Banda": "bnd",
"Banda Malay": "bpq",
"Banda-Bambari": "liy",
"Banda-Banda": "bpd",
"Banda-Mbrès": "bqk",
"Banda-Ndélé": "bfl",
"Banda-Yangere": "yaj",
"Bandi": "bza",
"Bandial": "bqj",
"Bandjalang": "bdy",
"Bangala": "bxg",
"Bangandu": "bgf",
"Bangba": "bbe",
"Banggai": "bgz",
"Bangi": "bni",
"Bangime": "dba",
"Bangka": "mfb",
"Bangolan": "bgj",
"Bangru": "sit-ban",
"Bangubangu": "bnx",
"Bangwinji": "bsj",
"Baniva": "bvv",
"Baniwa": "bwi",
"Banjarese": "bjn",
"Banka": "bxw",
"Bankan Tey Dogon": "dbw",
"Bankon": "abb",
"Banoni": "bcm",
"Bantawa": "bap",
"Bantayanon": "bfx",
"Bantik": "bnq",
"Banyumasan": "map-bms",
"Baoule": "bci",
"Baraamu": "brd",
"Barai": "bbb",
"Barakai": "baj",
"Baram Kayan": "kys",
"Barama": "bbg",
"Barambu": "brm",
"Baramu": "bmz",
"Barapasi": "brp",
"Baras": "brs",
"Barasana": "bsn",
"Barbareño": "boi",
"Barclayville Grebo": "gry",
"Bardi": "bcj",
"Barein": "bva",
"Bargam": "mlp",
"Bari": "bfa",
"Bariai": "bch",
"Bariji": "bjc",
"Barikanchi": "bxo",
"Barikewa": "jbk",
"Barngarla": "bjb",
"Barok": "bjk",
"Barombi": "bbi",
"Barranbinya": "aus-bra",
"Barro Negro Tunebo": "tbn",
"Barrow Point": "bpt",
"Baruga": "bjz",
"Barunggam": "aus-brm",
"Baruya": "byr",
"Barwe": "bwg",
"Barzani Jewish Neo-Aramaic": "bjf",
"Baré": "bae",
"Barí": "mot",
"Basa": "bzw",
"Basa-Gumna": "bsl",
"Basa-Gurmana": "buj",
"Basaa": "bas",
"Basap": "bdb",
"Basay": "byq",
"Bashkardi": "bsg",
"Bashkir": "ba",
"Basketo": "bst",
"Basque": "eu",
"Basque-Icelandic Pidgin": "crp-bip",
"Bassa": "bsq",
"Bassa-Kontagora": "bsr",
"Bassari": "bsc",
"Bassossi": "bsi",
"Bata": "bta",
"Bataan Ayta": "ayt",
"Batad Ifugao": "ifb",
"Batanga": "bnm",
"Batek": "btq",
"Bateri": "btv",
"Bathari": "bhm",
"Bati (Cameroon)": "btc",
"Bati (Indonesia)": "bvt",
"Bats": "bbl",
"Batu": "btu",
"Batui": "zbt",
"Batuley": "bay",
"Bau": "bbd",
"Bau Bidayuh": "sne",
"Bauchi": "bsf",
"Bauni": "bpe",
"Baure": "brg",
"Bauria": "bge",
"Bauro": "bxa",
"Bauwaki": "bwk",
"Bauzi": "bvz",
"Bavarian": "bar",
"Bawm Chin": "bgr",
"Bay Miwok": "mkq",
"Bayali": "bjy",
"Baybayanon": "bvy",
"Baygo": "byg",
"Bayogoula": "nai-bay",
"Bayono": "byl",
"Bayot": "bda",
"Bayungu": "bxj",
"Bazigar": "bfr",
"Beami": "beo",
"Beary": "dra-bry",
"Beaver": "bea",
"Beba": "bfp",
"Bebe": "bzv",
"Bebele": "beb",
"Bebeli": "bek",
"Bebil": "bxp",
"Bedik": "tnr",
"Bedjond": "bjv",
"Bedoanas": "bed",
"Beeke": "bkf",
"Beele": "bxq",
"Beembe": "beq",
"Beezen": "bnz",
"Befang": "bby",
"Begbere-Ejar": "bqv",
"Beja": "bej",
"Bekati'": "bei",
"Bekwarra": "bkv",
"Bekwel": "bkw",
"Belait": "beg",
"Belanda Bor": "bxb",
"Belanda Viri": "bvi",
"Belarusian": "be",
"Belhariya": "byw",
"Beli (New Guinea)": "bey",
"Beli (South Sudan)": "blm",
"Belizean Creole": "bzj",
"Bella Coola": "blc",
"Bellari": "brw",
"Belter Creole": "art-bel",
"Bemba": "bem",
"Bembe": "bmb",
"Ben Tey": "dbt",
"Bena": "yun",
"Benabena": "bef",
"Bench": "bcq",
"Bende": "bdp",
"Bendi": "bct",
"Beneraf": "bnv",
"Beng": "nhb",
"Benga": "bng",
"Bengali": "bn",
"Benggoi": "bgy",
"Bengkala Sign Language": "bqy",
"Bentong": "bnu",
"Benyadu'": "byd",
"Beothuk": "bue",
"Bepour": "bie",
"Bera": "brf",
"Berakou": "bxv",
"Berau Malay": "bve",
"Berawan": "lod",
"Berbice Creole Dutch": "brc",
"Bergish": "gmw-bgh",
"Berik": "bkl",
"Berinomo": "bit",
"Berom": "bom",
"Berta": "wti",
"Berti": "byt",
"Besisi": "mhe",
"Besme": "bes",
"Besoa": "bep",
"Betaf": "bfe",
"Betawi": "bew",
"Bete": "byf",
"Bete-Bendi": "btt",
"Betoi": "sai-bet",
"Betta Kurumba": "xub",
"Bezhta": "kap",
"Bhadrawahi": "bhd",
"Bhalay": "bhx",
"Bharia": "bha",
"Bhatri": "bgw",
"Bhattiyali": "bht",
"Bhaya": "bhe",
"Bhele": "bhy",
"Bhilali": "bhi",
"Bhili": "bhb",
"Bhojpuri": "bho",
"Bhoti Kinnauri": "nes",
"Bhunjia": "bhu",
"Biafada": "bif",
"Biage": "bdf",
"Biak": "bhw",
"Biali": "beh",
"Bian Marind": "bpv",
"Biangai": "big",
"Biao": "byk",
"Biao Mon": "bmt",
"Biao-Jiao Mien": "bje",
"Biatah Bidayuh": "bth",
"Bibaali": "bcn",
"Bibbulman": "xbp",
"Bidiyo": "bid",
"Bidyara": "bym",
"Bidyogo": "bjg",
"Biem": "bmc",
"Bierebo": "bnk",
"Bieria": "brj",
"Biete": "biu",
"Big Nambas": "nmb",
"Biga": "bhc",
"Bigambal": "xbe",
"Bih": "ibh",
"Bihari": "bh",
"Bijori": "bix",
"Bikya": "byb",
"Bila": "bip",
"Bilaspuri": "kfs",
"Bilba": "bpz",
"Bilbil": "brz",
"Bile": "bil",
"Biliau": "bcu",
"Biloxi": "bll",
"Bilua": "blb",
"Bilur": "bxf",
"Bima": "bhp",
"Bimin": "bhl",
"Bimoba": "bim",
"Bina": "bmn",
"Binahari": "bxz",
"Binandere": "bhg",
"Binawa": "byj",
"Bindal": "xbd",
"Bine": "bon",
"Binji": "bpj",
"Binongan Itneg": "itb",
"Bintauna": "bne",
"Bintulu": "bny",
"Binukid": "bkd",
"Binumarien": "bjr",
"Bipi": "biq",
"Birao": "brr",
"Birgid": "brk",
"Birgit": "btf",
"Birhor": "biy",
"Biri": "bzr",
"Biritai": "bqq",
"Birri": "bvq",
"Birrpayi": "xbj",
"Birwa": "brl",
"Biseni": "ije",
"Bishnupriya Manipuri": "bpy",
"Bishuo": "bwh",
"Bisis": "bnw",
"Bislama": "bi",
"Bisorio": "bir",
"Bissa": "bib",
"Bisu": "bzi",
"Bit": "bgk",
"Bitare": "brt",
"Bitur": "mcc",
"Biwat": "bwm",
"Biyo": "byo",
"Biyom": "bpm",
"Blablanga": "blp",
"Black Speech": "art-bsp",
"Blackfoot": "bla",
"Blafe": "bfh",
"Blagar": "beu",
"Blang": "blr",
"Blin": "byn",
"Bo": "bgl",
"Bo-Rukul": "mae",
"Bo-Ung": "mux",
"Boano (Maluku)": "bzn",
"Boano (Sulawesi)": "bzl",
"Bobongko": "bgb",
"Bobot": "bty",
"Bodo (Central Africa)": "boy",
"Bodo (India)": "brx",
"Bodo Gadaba": "gbj",
"Bodo Parja": "bdv",
"Bofi": "bff",
"Boga": "bvw",
"Bogaya": "boq",
"Boghom": "bux",
"Boguru": "bqu",
"Bohtan Neo-Aramaic": "bhn",
"Boikin": "bzf",
"Bokar": "sit-bok",
"Bokha": "ybk",
"Boko": "bqc",
"Bokobaru": "bus",
"Bokoto": "bdt",
"Bokyi": "bky",
"Bola": "bnp",
"Bolak": "art-blk",
"Bolango": "bld",
"Bole": "bol",
"Bolgo": "bvo",
"Bolia": "bli",
"Bolinao": "smk",
"Bolivian Sign Language": "bvl",
"Boloki": "bkt",
"Bolon": "bof",
"Bolondo": "bzm",
"Bolyu": "ply",
"Bom": "bmf",
"Boma Nkuu": "bnt-bon",
"Boma Yumu": "bnt-boy",
"Bomboli": "bml",
"Bomboma": "bws",
"Bomitaba": "zmx",
"Bomu": "bmq",
"Bomwali": "bmw",
"Bon Gula": "glc",
"Bonan": "peh",
"Bondei": "bou",
"Bondo": "bfw",
"Bondoukou Kulango": "kzc",
"Bondum Dom Dogon": "dbu",
"Bonerate": "bna",
"Bonggi": "bdg",
"Bonggo": "bpg",
"Bongili": "bui",
"Bongo": "bot",
"Bongu": "bpu",
"Bonjo": "bok",
"Bonkeng": "bvg",
"Bonkiman": "bop",
"Bookan": "bnb",
"Boon": "bnl",
"Boor": "bvf",
"Bora": "boa",
"Border Kuna": "kvn",
"Borei": "gai",
"Boro": "xxb",
"Borong": "ksr",
"Boruca": "brn",
"Borôro": "bor",
"Boselewa": "bwf",
"Bosngun": "bqs",
"Bote-Majhi": "bmj",
"Botlikh": "bph",
"Botolan Sambal": "sbl",
"Bouna Kulango": "nku",
"Bouni": "suo",
"Bourbonnais-Berrichon": "roa-bbn",
"Bourguignon": "roa-brg",
"Bouyei": "pcc",
"Bozaba": "bzo",
"Bragat": "aof",
"Brahui": "brh",
"Braj": "bra",
"Brazilian Sign Language": "bzs",
"Brek Karen": "kvl",
"Brem": "buq",
"Breri": "brq",
"Breton": "br",
"Bribri": "bzd",
"British Sign Language": "bfi",
"Brokkat": "bro",
"Brokpake": "sgt",
"Brokskat": "bkk",
"Brooke's Point Palawano": "plw",
"Broome Pearling Lugger Pidgin": "bpl",
"Brunei Bisaya": "bsb",
"Brunei Malay": "kxd",
"Bruny Island": "xpz",
"Bu": "jid",
"Bu-Nao Bunu": "bwx",
"Bua": "bub",
"Bualkhaw Chin": "cbl",
"Buamu": "box",
"Bube": "bvb",
"Bubi": "buw",
"Bubia": "bbx",
"Budeh Stieng": "stt",
"Budibud": "btp",
"Budong-Budong": "bdx",
"Budu": "buu",
"Budukh": "bdk",
"Buduma": "bdm",
"Budza": "bja",
"Buena Vista Yokuts": "yok-bvy",
"Bugan": "bbh",
"Bughotu": "bgt",
"Buginese": "bug",
"Buglere": "sab",
"Bugun": "bgg",
"Buhi'non Bikol": "ubl",
"Buhid": "bku",
"Buhutu": "bxh",
"Bujhyal": "byh",
"Bukar-Sadung Bidayuh": "sdo",
"Bukat": "bvk",
"Bukawa": "buk",
"Bukhari": "bhh",
"Bukit Malay": "bvu",
"Bukitan": "bkn",
"Bukiyip": "ape",
"Buksa": "tkb",
"Bukusu": "bxk",
"Bulgar": "xbo",
"Bulgarian": "bg",
"Bulgarian Sign Language": "bqn",
"Bulgebi": "bmp",
"Buli (Ghana)": "bwu",
"Buli (Indonesia)": "bzq",
"Bulo Stieng": "sti",
"Bulu (Cameroon)": "bum",
"Bulu (New Guinea)": "bjl",
"Bulungan": "blj",
"Bum": "bmv",
"Bumaji": "byp",
"Bumang": "bvp",
"Bumbita Arapesh": "aon",
"Bumthangkha": "kjz",
"Bun": "buv",
"Buna": "bvn",
"Bunaba": "bck",
"Bunak": "bfn",
"Bunama": "bdd",
"Bundeli": "bns",
"Bung": "bqd",
"Bungain": "but",
"Bunganditj": "xbg",
"Bungku": "bkz",
"Bungu": "wun",
"Bunoge": "dgb",
"Bunun": "bnn",
"Buol": "blf",
"Bura": "bwr",
"Bura Mabang": "mde",
"Burak": "bys",
"Buraka": "bkg",
"Burarra": "bvr",
"Burate": "bti",
"Burduna": "bxn",
"Bure": "bvh",
"Burgundian": "gme-bur",
"Burji": "bji",
"Burmbar": "vrt",
"Burmese": "my",
"Burmeso": "bzu",
"Buru (Indonesia)": "mhs",
"Buru (Nigeria)": "bqw",
"Burui": "bry",
"Burumakok": "aip",
"Burun": "bdi",
"Burundian Sign Language": "lsb",
"Burunge": "bds",
"Burushaski": "bsk",
"Burusu": "bqr",
"Buruwai": "asi",
"Buryat": "bua",
"Busa": "bqp",
"Busam": "bxs",
"Busami": "bsm",
"Busang Kayan": "bfg",
"Bushoong": "buf",
"Buso": "bso",
"Busoa": "bup",
"Bussa": "dox",
"Busuu": "bju",
"Butbut Kalinga": "kyb",
"Butchulla": "xby",
"Butmas-Tur": "bnr",
"Butuanon": "btw",
"Buwal": "bhs",
"Buyeo": "xpy",
"Buyu": "byi",
"Buyuan Jino": "jiy",
"Bwa": "bww",
"Bwaidoka": "bwd",
"Bwala": "bnt-bwa",
"Bwanabwana": "tte",
"Bwatoo": "bwa",
"Bwe Karen": "bwe",
"Bwela": "bwl",
"Bwile": "bwc",
"Bwisi": "bwz",
"Byangsi": "bee",
"Byep": "mkk",
"Bädi Kanum": "khd",
"Caac": "msq",
"Cabiyarí": "cbb",
"Cabre": "awd-cab",
"Cabécar": "cjp",
"Cacaloxtepec Mixtec": "miu",
"Cacaopera": "ccr",
"Cacgia Roglai": "roc",
"Cacua": "cbv",
"Cacán": "sai-cac",
"Caddo": "cad",
"Cafundó": "ccd",
"Cahuarano": "cah",
"Cahuilla": "chl",
"Caijia": "sit-cai",
"Cajonos Zapotec": "zad",
"Caka": "ckx",
"Cakfem-Mushere": "cky",
"Calabrian Greek": "grk-cal",
"Calamian Tagbanwa": "tbk",
"Calusa": "nai-cal",
"Caluyanun": "clu",
"Caló": "rmq",
"Camarines Norte Agta": "abd",
"Cambodian Sign Language": "csx",
"Cameroon Mambila": "mcu",
"Cameroon Pidgin": "wes",
"Campalagian": "cml",
"Camsá": "kbh",
"Camtho": "cmt",
"Camunic": "xcc",
"Candoshi-Shapra": "cbu",
"Canela": "ram",
"Canichana": "caz",
"Cantabrian": "roa-can",
"Cantonese": "yue",
"Cao Miao": "cov",
"Caolan": "mlc",
"Capanahua": "kaq",
"Capiznon": "cps",
"Cappadocian Greek": "cpg",
"Caquinte": "cot",
"Car Nicobarese": "caq",
"Cara": "cfd",
"Carabayo": "cby",
"Caramanta": "crf",
"Caranqui": "sai-caq",
"Carapana": "cbc",
"Carian": "xcr",
"Cariay": "awd-kar",
"Caribbean Hindustani": "hns",
"Caribbean Javanese": "jvn",
"Carijona": "cbd",
"Carolina Algonquian": "crr",
"Carolinian": "cal",
"Carpathian Romani": "rmc",
"Carpathian Rusyn": "rue",
"Carrier": "crx",
"Cashibo-Cacataibo": "cbr",
"Cashinahua": "cbs",
"Casiguran Dumagat Agta": "dgc",
"Casuarina Coast Asmat": "asc",
"Catacao": "sai-cat",
"Catalan": "ca",
"Catalan Sign Language": "csc",
"Catawba": "chc",
"Catuquinaru": "sai-ctq",
"Catío Chibcha": "cba-cat",
"Cauca": "cca",
"Cavineña": "cav",
"Cayubaba": "cyb",
"Cayuga": "cay",
"Cayuse": "xcy",
"Cazcan": "azc-caz",
"Cañari": "sai-cnr",
"Cebaara": "sef",
"Cebuano": "ceb",
"Celtiberian": "xce",
"Cen": "cen",
"Central Asmat": "cns",
"Central Atlas Tamazight": "tzm",
"Central Awyu": "awu",
"Central Bai": "bca",
"Central Bikol": "bcl",
"Central Bontoc": "lbk",
"Central Cagayan Agta": "agt",
"Central Dusun": "dtp",
"Central Franconian": "gmw-cfr",
"Central Grebo": "grv",
"Central Huasteca Nahuatl": "nch",
"Central Huishui Hmong": "hmc",
"Central Kurdish": "ckb",
"Central Mahuatlán Zapotec": "zam",
"Central Malay": "pse",
"Central Mansi": "mns-cen",
"Central Masela": "mxz",
"Central Mashan Hmong": "hmm",
"Central Mazahua": "maz",
"Central Melanau": "mel",
"Central Min": "czo",
"Central Mnong": "cmo",
"Central Nahuatl": "nhn",
"Central Nicobarese": "ncb",
"Central Ojibwa": "ojc",
"Central Palawano": "plc",
"Central Pame": "pbs",
"Central Pomo": "poo",
"Central Puebla Nahuatl": "ncx",
"Central Sama": "sml",
"Central Siberian Yupik": "ess",
"Central Sierra Miwok": "csm",
"Central Subanen": "syb",
"Central Tagbanwa": "tgt",
"Central Tarahumara": "tar",
"Central Teke": "nzu",
"Central Tunebo": "tuf",
"Centúúm": "cet",
"Cerma": "cme",
"Ch'olti'": "myn-chl",
"Ch'orti'": "caa",
"Chaap Wuurong": "tjw",
"Chachi": "cbi",
"Chadian Arabic": "shu",
"Chadian Sign Language": "cds",
"Chadong": "cdy",
"Chagatai": "chg",
"Chaha": "sem-cha",
"Chaima": "ciy",
"Chairel": "sit-cha",
"Chak": "ckh",
"Chakali": "cli",
"Chakma": "ccp",
"Chala": "cll",
"Chaldean Neo-Aramaic": "cld",
"Chali": "tgf",
"Chamacoco": "ceg",
"Chamalal": "cji",
"Chamba Daka": "ccg",
"Chamba Leko": "ndi",
"Chambeali": "cdh",
"Chambri": "can",
"Chamicuro": "ccc",
"Chamling": "rab",
"Chamorro": "ch",
"Champenois": "roa-cha",
"Chang": "nbc",
"Changriwa": "cga",
"Changthang": "cna",
"Chantyal": "chx",
"Chaná": "sai-chn",
"Chané": "caj",
"Chapacura": "sai-chp",
"Chara": "cra",
"Charrua": "sai-chr",
"Chaudangsi": "cdn",
"Chaura": "crv",
"Chavacano": "cbk",
"Chayahuita": "cbt",
"Chayuco Mixtec": "mih",
"Chazumba Mixtec": "xtb",
"Che": "ruk",
"Chechen": "ce",
"Cheke Holo": "mrn",
"Chemakum": "xch",
"Chenapian": "cjn",
"Chenchu": "cde",
"Chenoua": "cnu",
"Chepang": "cdm",
"Chepya": "ycp",
"Cherepon": "cpn",
"Cherokee": "chr",
"Chesu": "ych",
"Chetco-Tolowa": "ctc",
"Chewong": "cwg",
"Cheyenne": "chy",
"Chhattisgarhi": "hne",
"Chhintange": "ctn",
"Chhulung": "cur",
"Chiangmai Sign Language": "csd",
"Chiapanec": "cip",
"Chibcha": "chb",
"Chicahuaxtla Triqui": "trs",
"Chichewa": "ny",
"Chichicapan Zapotec": "zpv",
"Chichimeca-Jonaz": "pei",
"Chichonyi-Chidzihana-Chikauma": "coh",
"Chickasaw": "cic",
"Chicomuceltec": "cob",
"Chiduruma": "dug",
"Chigmecatitlán Mixtec": "mii",
"Chilcotin": "clc",
"Chilean Sign Language": "csg",
"Chilisso": "clh",
"Chiltepec Chinantec": "csa",
"Chimalapa Zoque": "zoh",
"Chimariko": "cid",
"Chimila": "cbg",
"Chimwiini": "bnt-cmw",
"Chinali": "cih",
"Chinbon Chin": "cnb",
"Chinese": "zh",
"Chinese Pidgin English": "cpi",
"Chinese Sign Language": "csl",
"Chinook": "chh",
"Chinook Jargon": "chn",
"Chipaya": "cap",
"Chipewyan": "chp",
"Chiquihuitlán Mazatec": "maq",
"Chiquimulilla": "nai-chi",
"Chiquitano": "cax",
"Chiricahua": "apm",
"Chirino": "sai-chi",
"Chiripá": "nhd",
"Chiru": "cdf",
"Chitimacha": "ctm",
"Chitkuli Kinnauri": "cik",
"Chittagonian": "ctg",
"Chitwania Tharu": "the",
"Chiwere": "iow",
"Choapan Zapotec": "zpc",
"Chocangaca": "cgk",
"Chochotec": "coz",
"Choctaw": "cho",
"Chodri": "cdi",
"Chokri Naga": "nri",
"Chokwe": "cjk",
"Chol": "ctu",
"Cholón": "cht",
"Chong": "cog",
"Choni": "cda",
"Chono": "sai-cno",
"Chontal Maya": "chf",
"Chopi": "cce",
"Chothe Naga": "nct",
"Chrau": "crw",
"Chru": "cje",
"Chuabo": "chw",
"Chuanqiandian Cluster Miao": "cqd",
"Chuave": "cjv",
"Chug": "cvg",
"Chuj": "cac",
"Chuka": "cuh",
"Chukchi": "ckt",
"Chukwa": "cuw",
"Chulym": "clw",
"Chumburung": "ncu",
"Chungli Ao": "njo-jgl",
"Churahi": "cdj",
"Church Slavonic": "zls-chs",
"Churuya": "sai-chu",
"Chut": "scb",
"Chuukese": "chk",
"Chuvan": "xcv",
"Chuvash": "cv",
"Chácobo": "cao",
"Ci Gbe": "cib",
"Cia-Cia": "cia",
"Cibak": "ckl",
"Cicipu": "awc",
"Ciguayo": "nai-cig",
"Cimbrian": "cim",
"Cinamiguin Manobo": "mkx",
"Cinda-Regi-Tiyal": "cdr",
"Cineni": "cie",
"Cinta Larga": "cin",
"Cishingini": "asg",
"Citak": "txt",
"Ciwogai": "tgd",
"Classical Chinese": "lzh",
"Classical Gaelic": "ghc",
"Classical Guarani": "gn-cls",
"Classical Mandaic": "myz",
"Classical Mongolian": "cmg",
"Classical Nahuatl": "nci",
"Classical Newar": "nwc",
"Classical Quechua": "qwc",
"Classical Syriac": "syc",
"Classical Tibetan": "xct",
"Coahuilteco": "xcw",
"Coast Miwok": "csi",
"Coastal Kadazan": "kzj",
"Coastal Konjo": "kjc",
"Coatecas Altas Zapotec": "zca",
"Coatepec Nahuatl": "naz",
"Coatlán Mixe": "mco",
"Coatlán Zapotec": "zps",
"Coatzospan Mixtec": "miz",
"Cocama": "cod",
"Cochimi": "coj",
"Cocopa": "coc",
"Cocos Islands Malay": "coa",
"Coeruna": "sai-coe",
"Coeur d'Alene": "crd",
"Cofán": "con",
"Cogui": "kog",
"Col": "liw",
"Colombian Sign Language": "csn",
"Colonia Tovar German": "gct",
"Columbia-Wenatchi": "col",
"Colán": "sai-col",
"Comaltepec Chinantec": "cco",
"Comanche": "com",
"Comechingon": "sai-cmg",
"Comecrudo": "xcm",
"Communicationssprache": "art-com",
"Como Karim": "cfg",
"Comox": "coo",
"Con": "cno",
"Coos": "csz",
"Copainalá Zoque": "zoc",
"Copala Triqui": "trc",
"Copallén": "sai-cop",
"Coptic": "cop",
"Coquille": "coq",
"Cora": "crn",
"Cori": "cry",
"Cornish": "kw",
"Coroado Puri": "sai-crd",
"Corsican": "co",
"Cosoleacaque Nahuatl": "nhk",
"Costa Rican Sign Language": "csr",
"Cotabato Manobo": "mta",
"Cotoname": "xcn",
"Cowlitz": "cow",
"Coyaima": "coy",
"Coyotepec Popoloca": "pbf",
"Coyutla Totonac": "toc",
"Cree": "cr",
"Creek": "mus",
"Crimean Gothic": "gme-cgo",
"Crimean Tatar": "crh",
"Croatian Sign Language": "csq",
"Cross River Mbembe": "mfn",
"Crow": "cro",
"Cruzeño": "crz",
"Cua": "cua",
"Cuban Sign Language": "csf",
"Cubeo": "cub",
"Cueva": "sai-cva",
"Cuiba": "cui",
"Cuitlatec": "cuy",
"Culina": "cul",
"Culli": "sai-cul",
"Cumanagoto": "cuo",
"Cumbric": "xcb",
"Cun": "cuq",
"Cung": "cug",
"Cupeño": "cup",
"Curonian": "xcu",
"Curripaco": "kpc",
"Cutchi-Swahili": "ccl",
"Cuvok": "cuv",
"Cuyamecalco Mixtec": "xtu",
"Cuyunon": "cyo",
"Cwi Bwamu": "bwy",
"Cypriot Arabic": "acy",
"Czech": "cs",
"Czech Sign Language": "cse",
"Cèmuhî": "cam",
"Cốông": "cnc",
"Da'a Kaili": "kzf",
"Daai Chin": "dao",
"Daakaka": "bpa",
"Daantanai'": "lni",
"Daasanach": "dsh",
"Daba": "dbq",
"Dabarre": "dbr",
"Dabe": "dbe",
"Dacian": "xdc",
"Dadanitic": "sem-dad",
"Dadi Dadi": "dda",
"Dadibi": "mps",
"Dadiya": "dbd",
"Daga": "dgz",
"Dagaari Dioula": "dgd",
"Dagba": "dgk",
"Dagbani": "dag",
"Dagik": "dec",
"Dagoman": "dgn",
"Dahalik": "dlk",
"Dahalo": "dal",
"Daho-Doo": "das",
"Dai": "dij",
"Dair": "drb",
"Dairi Batak": "btd",
"Dakka": "dkk",
"Dakota": "dak",
"Dakpa": "dka",
"Dalmatian": "dlm",
"Daloa Bété": "bev",
"Dama (Nigeria)": "dmm",
"Dama (Sierra Leone)": "dmn-dam",
"Damakawa": "dam",
"Damal": "uhn",
"Dambi": "dac",
"Dameli": "dml",
"Dampelas": "dms",
"Dan": "dnj",
"Danaru": "dnr",
"Danau": "dnu",
"Dandami Maria": "daq",
"Dangaléat": "daa",
"Dangaura Tharu": "thl",
"Danish": "da",
"Danish Sign Language": "dsl",
"Dano": "aso",
"Danu": "dnv",
"Danuwar": "dhw",
"Dao": "daz",
"Daonda": "dnd",
"Dar Daju Daju": "djc",
"Dar Fur Daju": "daj",
"Dar Sila Daju": "dau",
"Darai": "dry",
"Dargwa": "dar",
"Darkinjung": "xda",
"Darlong": "dln",
"Darmiya": "drd",
"Daro-Matu Melanau": "dro",
"Darumbal": "xgm",
"Dass": "dot",
"Datian Min": "nan-dat",
"Datooga": "tcc",
"Daungwurrung": "dgw",
"Daur": "dta",
"Davawenyo": "daw",
"Dawawa": "dww",
"Dawera-Daweloor": "ddw",
"Dawro": "dwr",
"Day": "dai",
"Dayi": "dax",
"Dazaga": "dzg",
"Deccani": "dcc",
"Dedua": "ded",
"Defaka": "afn",
"Defi Gbe": "gbh",
"Deg": "mzw",
"Deg Xinag": "ing",
"Degema": "deg",
"Degenan": "dge",
"Dehwari": "deh",
"Dela-Oenale": "row",
"Delo": "ntr",
"Delta Yokuts": "yok-dly",
"Dem": "dem",
"Dema": "dmx",
"Demisa": "dei",
"Demotic Egyptian": "egx-dem",
"Demta": "dmy",
"Dena'ina": "tfn",
"Dendi": "ddn",
"Dengese": "dez",
"Dengka": "dnk",
"Deno": "dbb",
"Denya": "anv",
"Dení": "dny",
"Deori": "der",
"Desano": "des",
"Desiya": "dso",
"Dewas Rai": "dwz",
"Dewoin": "dee",
"Dezfuli": "def",
"Dghwede": "dgh",
"Dhaiso": "dhs",
"Dhalandji": "dhl",
"Dhangu": "dhg",
"Dhanki": "dhn",
"Dhao": "nfa",
"Dharug": "xdk",
"Dhatki": "mki",
"Dhimal": "dhi",
"Dhivehi": "dv",
"Dhodia": "dho",
"Dhofari Arabic": "adf",
"Dhudhuroa": "ddr",
"Dhundhari": "dhd",
"Dhungaloo": "dhx",
"Dhurga": "dhu",
"Dhuwal": "dwu",
"Dhuwaya": "dwy",
"Dia": "dia",
"Dibabawon Manobo": "mbd",
"Dibiyaso": "dby",
"Dibo": "dio",
"Dicamay Agta": "duy",
"Didinga": "did",
"Dieri": "dif",
"Digo": "dig",
"Dii": "dur",
"Dijim-Bwilim": "cfa",
"Dilling": "dil",
"Dima": "jma",
"Dimasa": "dis",
"Dimbong": "dii",
"Dime": "dim",
"Dinapigue Agta": "phi-din",
"Dineor": "mrx",
"Ding": "diz",
"Dinka": "din",
"Diodio": "ddi",
"Dirasha": "gdl",
"Diri": "dwa",
"Dirim": "dir",
"Disa": "dsi",
"Ditammari": "tbz",
"Ditidaht": "dtd",
"Diuwe": "diy",
"Diuxi-Tilantongo Mixtec": "xtd",
"Dixon Reef": "dix",
"Dizin": "mdx",
"Djadjawurrung": "dja",
"Djambarrpuyngu": "djr",
"Djangun": "djf",
"Djauan": "djn",
"Djawi": "djw",
"Djimini": "dyi",
"Djinang": "dji",
"Djinba": "djb",
"Djiwarli": "djl",
"Dobel": "kvo",
"Dobu": "dob",
"Doe": "doe",
"Doga": "dgg",
"Doghoro": "dgx",
"Dogoso": "dgs",
"Dogosé": "dos",
"Dogri": "doi",
"Dogrib": "dgr",
"Dogul Dom": "dbg",
"Doka": "dbi",
"Doko-Uyanga": "uya",
"Dolgan": "dlg",
"Dom": "doa",
"Domaaki": "dmk",
"Domari": "rmt",
"Dominican Sign Language": "doq",
"Dompo": "doy",
"Domu": "dof",
"Domung": "dev",
"Dondo": "dok",
"Dong": "doh",
"Dongo": "doo",
"Dongolawi": "kzh",
"Dongotono": "ddd",
"Dongshanba Lalo": "yik",
"Dongxiang": "sce",
"Donno So Dogon": "dds",
"Doondo": "dde",
"Dorasque": "cba-dor",
"Dori'o": "dor",
"Dorig": "wwo",
"Doromu-Koki": "kqc",
"Dorze": "doz",
"Doso": "dol",
"Doteli": "dty",
"Dothraki": "art-dtk",
"Doura": "don",
"Doutai": "tds",
"Doyayo": "dow",
"Drehu": "dhv",
"Drung": "duu",
"Duala": "dua",
"Duano": "dup",
"Duau": "dva",
"Dubli": "dub",
"Dubu": "dmu",
"Dugun": "ndu",
"Duguri": "dbm",
"Dugwor": "dme",
"Duhwa": "kbz",
"Duit": "cba-dui",
"Duke": "nke",
"Dukhan": "trk-dkh",
"Dulbu": "dbo",
"Duli": "duz",
"Duma": "dma",
"Dumaitic": "sem-dum",
"Dumbea": "duf",
"Dumi": "dus",
"Dumpas": "dmv",
"Dumun": "dui",
"Duna": "duc",
"Dungan": "dng",
"Dungmali": "raa",
"Dungra Bhil": "duh",
"Dungu": "dbv",
"Dupaningan Agta": "duo",
"Dura": "drq",
"Duri": "mvp",
"Duriankere": "dbn",
"Duruwa": "pci",
"Dusner": "dsn",
"Dusun Deyah": "dun",
"Dusun Malang": "duq",
"Dusun Witu": "duw",
"Dutch": "nl",
"Dutch Sign Language": "dse",
"Duun": "dux",
"Duupa": "dae",
"Duvle": "duv",
"Duwai": "dbp",
"Duwet": "gve",
"Dwang": "nnu",
"Dyaabugay": "dyy",
"Dyaberdyaber": "dyb",
"Dyan": "dya",
"Dyangadi": "dyn",
"Dyirbal": "dbl",
"Dyugun": "dyd",
"Dyula": "dyu",
"Dza": "jen",
"Dzala": "dzl",
"Dzando": "dzn",
"Dzao Min": "bpn",
"Dzodinka": "add",
"Dzongkha": "dz",
"Dzuun": "dnn",
"Dâw": "kwa",
"E": "eee",
"E'ma Buyang": "yzg",
"Early Assamese": "inc-oas",
"Early Modern Korean": "ko-ear",
"Early Old Oghuz": "trk-eog",
"Early Tripuri": "xtr",
"East Central German": "gmw-ecg",
"East Circassian": "kbd",
"East Damar": "dmr",
"East Franconian": "vmf",
"East Futuna": "fud",
"East Kewa": "kjs",
"East Limba": "lma",
"East Masela": "vme",
"East Miraya Bikol": "rbl",
"East Nyala": "nle",
"East Tarangan": "tre",
"East Yugur": "yuy",
"Eastern Acipa": "acp",
"Eastern Arrernte": "aer",
"Eastern Bolivian Guarani": "gui",
"Eastern Bontoc": "ebk",
"Eastern Bru": "bru",
"Eastern Canadian Inuktitut": "ike",
"Eastern Cham": "cjm",
"Eastern Durango Nahuatl": "azd",
"Eastern Gorkha Tamang": "tge",
"Eastern Highland Chatino": "cly",
"Eastern Highland Otomi": "otm",
"Eastern Huasteca Nahuatl": "nhe",
"Eastern Huishui Hmong": "hme",
"Eastern Karaboro": "xrb",
"Eastern Katu": "ktv",
"Eastern Kayah": "eky",
"Eastern Keres": "kee",
"Eastern Khanty": "kca-eas",
"Eastern Krahn": "kqo",
"Eastern Lalu": "yit",
"Eastern Lawa": "lwl",
"Eastern Magar": "mgp",
"Eastern Maninkakan": "emk",
"Eastern Mari": "mhr",
"Eastern Meohang": "emg",
"Eastern Min": "cdo",
"Eastern Mnong": "mng",
"Eastern Muria": "emu",
"Eastern Ngad'a": "nea",
"Eastern Nisu": "nos",
"Eastern Ojibwa": "ojg",
"Eastern Parbate Kham": "kif",
"Eastern Penan": "pez",
"Eastern Pomo": "peb",
"Eastern Pwo": "kjp",
"Eastern Qiandong Miao": "hmq",
"Eastern Subanun": "sfe",
"Eastern Tamang": "taj",
"Eastern Tawbuid": "bnj",
"Eastern Xiangxi Miao": "muq",
"Eastern Xwla Gbe": "gbx",
"Ebira": "igb",
"Eblaite": "xeb",
"Ebrié": "ebr",
"Ebughu": "ebg",
"Ecuadorian Sign Language": "ecs",
"Ede Cabe": "cbj",
"Ede Ica": "ica",
"Ede Idaca": "idd",
"Ede Ije": "ijj",
"Ede Nago": "nqg",
"Edera Awyu": "awy",
"Edo": "bin",
"Edolo": "etr",
"Edomite": "xdm",
"Edopi": "dbf",
"Efai": "efa",
"Efe": "efe",
"Efik": "efi",
"Efutop": "ofu",
"Ega": "ega",
"Eggon": "ego",
"Egyptian": "egy",
"Egyptian Arabic": "arz",
"Egyptian Sign Language": "esl",
"Ehueun": "ehu",
"Eipomek": "eip",
"Eitiep": "eit",
"Ejagham": "etu",
"Ejamat": "eja",
"Ekajuk": "eka",
"Ekari": "ekg",
"Ekele": "khy",
"Eki": "eki",
"Ekit": "eke",
"Ekpeye": "ekp",
"El Alto Zapotec": "zpp",
"El Hugeirat": "elh",
"El Molo": "elo",
"Elamite": "elx",
"Eleme": "elm",
"Elepi": "ele",
"Elfdalian": "ovd",
"Elip": "ekm",
"Elkei": "elk",
"Eloi": "art-elo",
"Elotepec Zapotec": "zte",
"Eloyi": "afo",
"Elseng": "mrf",
"Elu": "elu",
"Elymian": "xly",
"Emae": "mmw",
"Emai": "ema",
"Eman": "emn",
"Embaloh": "emb",
"Emberá-Baudó": "bdc",
"Emberá-Catío": "cto",
"Emberá-Chamí": "cmi",
"Emberá-Tadó": "tdc",
"Embu": "ebu",
"Emem": "enr",
"Emerillon": "eme",
"Emilian": "egl",
"Emplawas": "emw",
"En": "enc",
"Enawené-Nawé": "unk",
"Ende": "end",
"Enga": "enq",
"Engenni": "enn",
"Enggano": "eno",
"English": "en",
"Enlhet": "enl",
"Enrekang": "ptt",
"Enu": "enu",
"Enwan": "env",
"Enwang": "enw",
"Enxet": "enx",
"Enya": "gey",
"Eotile": "eot",
"Epena": "sja",
"Epi-Olmec": "xep",
"Epie": "epi",
"Epigraphic Mayan": "emy",
"Eravallan": "era",
"Erave": "kjy",
"Ere": "twp",
"Erie": "iro-ere",
"Eritai": "ert",
"Erokwanas": "erw",
"Erre": "err",
"Erromintxela": "emx",
"Ersu": "ers",
"Eruwa": "erh",
"Erzya": "myv",
"Esan": "ish",
"Ese": "mcq",
"Ese Ejja": "ese",
"Eshtehardi": "esh",
"Esimbi": "ags",
"Eskayan": "esy",
"Esmeralda": "sai-esm",
"Esperanto": "eo",
"Esselen": "esq",
"Estado de México Otomi": "ots",
"Estonian": "et",
"Estonian Sign Language": "eso",
"Esuma": "esm",
"Etchemin": "etc",
"Etebi": "etb",
"Eten": "etx",
"Eteocretan": "ecr",
"Eteocypriot": "ecy",
"Ethiopian Sign Language": "eth",
"Etkywan": "ich",
"Eton (Cameroon)": "eto",
"Eton (Vanuatu)": "etn",
"Etruscan": "ett",
"Etulo": "utr",
"Evant": "bzz",
"Even": "eve",
"Evenki": "evn",
"Ewage-Notu": "nou",
"Ewarhuyana": "sai-ewa",
"Ewe": "ee",
"Ewondo": "ewo",
"Extremaduran": "ext",
"Eyak": "eya",
"Ezaa": "eza",
"Fagani": "faf",
"Faifi": "fif",
"Faire Atta": "azt",
"Faiwol": "fai",
"Fakkanci": "gel",
"Fala": "fax",
"Falam Chin": "cfm",
"Fali": "fli",
"Faliscan": "xfa",
"Fam": "fam",
"Fanagalo": "fng",
"Fanamaket": "bjp",
"Fang (Bantu)": "fan",
"Fang (Beboid)": "fak",
"Fania": "fni",
"Far Western Muria": "fmu",
"Farefare": "gur",
"Faroese": "fo",
"Fas": "fqs",
"Fasu": "faa",
"Fataleka": "far",
"Fataluku": "ddg",
"Fayu": "fau",
"Fe'fe'": "fmp",
"Fedan": "pdn",
"Fembe": "agl",
"Fer": "kah",
"Feroge": "fer",
"Fiji Hindi": "hif",
"Fijian": "fj",
"Filomena Mata-Coahuitlán Totonac": "tlp",
"Fingallian": "gmw-fin",
"Finnish": "fi",
"Finnish Sign Language": "fse",
"Finnish-Swedish Sign Language": "fss",
"Finongan": "fag",
"Fipa": "fip",
"Firan": "fir",
"Fiwaga": "fiw",
"Flemish Sign Language": "vgt",
"Flinders Island": "fln",
"Foau": "flh",
"Fogaha": "ber-fog",
"Foi": "foi",
"Foia Foia": "ffi",
"Folopa": "ppo",
"Foma": "fom",
"Fon": "fon",
"Fongoro": "fgr",
"Foodo": "fod",
"Forak": "frq",
"Fordata": "frd",
"Fore": "for",
"Forest Enets": "enf",
"Forest Nenets": "yrk-for",
"Fortsenal": "frt",
"Fox": "sac",
"Franc-Comtois": "roa-fcm",
"Francisco León Zoque": "zos",
"Franco-Provençal": "frp",
"French": "fr",
"French Belgian Sign Language": "sfb",
"French Sign Language": "fsl",
"Friulian": "fur",
"Fula": "ff",
"Fuliiru": "flr",
"Fulniô": "fun",
"Fum": "fum",
"Fungwa": "ula",
"Fur": "fvr",
"Furu": "fuu",
"Futuna-Aniwa": "fut",
"Fuyug": "fuy",
"Fwe": "fwe",
"Fwâi": "fwa",
"Fyam": "pym",
"Fyer": "fie",
"Ga": "gaa",
"Ga'anda": "gqa",
"Ga'dang": "gdg",
"Gaa": "ttb",
"Gaam": "tbi",
"Gabadi": "kbt",
"Gabi": "gbw",
"Gabri": "gab",
"Gabrielino-Fernandeño": "xgf",
"Gadang": "gdk",
"Gaddang": "gad",
"Gaddi": "gbk",
"Gade": "ged",
"Gadjerawang": "gdh",
"Gadsup": "gaj",
"Gafat": "gft",
"Gagadu": "gbu",
"Gagauz": "gag",
"Gagnoa Bété": "btg",
"Gahri": "bfu",
"Gaikundi": "gbf",
"Gaina": "gcn",
"Gal": "gap",
"Galambu": "glo",
"Galatian": "xga",
"Galela": "gbi",
"Galeya": "gar",
"Galice": "gce",
"Galician": "gl",
"Galindian": "xgl",
"Gallaecian": "cel-gal",
"Gallo": "roa-gal",
"Gallo-Italic of Basilicata": "roa-gib",
"Gallo-Italic of Sicily": "roa-gis",
"Gallurese": "sdn",
"Galo": "adl",
"Galoli": "gal",
"Gamale Kham": "kgj",
"Gambera": "gma",
"Gamela": "sai-gam",
"Gamilaraay": "kld",
"Gamit": "gbl",
"Gamkonora": "gak",
"Gamo": "gmv",
"Gamo-Ningi": "bte",
"Gan": "gan",
"Gana": "gnq",
"Ganang": "gne",
"Gandhari": "pgd",
"Gane": "gzn",
"Ganggalida": "gcd",
"Ganglau": "ggl",
"Gangte": "gnb",
"Gangulu": "gnl",
"Gants": "gao",
"Ganza": "gza",
"Ganzi": "gnz",
"Gao": "gga",
"Gapapaiwa": "pwg",
"Garawa": "wrk",
"Garhwali": "gbm",
"Garifuna": "cab",
"Garingbal": "xgi",
"Garo": "grt",
"Garre": "gex",
"Garus": "gyb",
"Garza": "xgr",
"Gashowu Yokuts": "yok-gsy",
"Gata'": "gaq",
"Gaulish": "cel-gau",
"Gavak": "dmc",
"Gavar": "gou",
"Gavião do Jiparaná": "gvo",
"Gawar-Bati": "gwt",
"Gawwada": "gwd",
"Gaya": "zra",
"Gayil": "gyl",
"Gayo": "gay",
"Gayón": "sai-gay",
"Gbagyi": "gbr",
"Gban": "ggu",
"Gbanu": "gbv",
"Gbanziri": "gbg",
"Gbari": "gby",
"Gbaya-Bossangoa": "gbp",
"Gbaya-Bozoum": "gbq",
"Gbaya-Mbodomo": "gmm",
"Gbayi": "gyg",
"Gbesi Gbe": "gbs",
"Gbii": "ggb",
"Gbin": "xgb",
"Gbiri-Niragu": "grh",
"Gboloo Grebo": "gec",
"Gciriku": "diu",
"Gcwi": "gwj",
"Ge": "hmj",
"Ge'ez": "gez",
"Geba Karen": "kvq",
"Gebe": "gei",
"Gedaged": "gdd",
"Gedeo": "drs",
"Geji": "gji",
"Geko Karen": "ghk",
"Gela": "nlg",
"Gele'": "sbc",
"Geme": "geq",
"Gen": "gej",
"Gende": "gaf",
"Gengle": "geg",
"Georgian": "ka",
"Gepo": "ygp",
"Gera": "gew",
"Gerka": "gek",
"German": "de",
"German Sign Language": "gsg",
"Geruma": "gea",
"Geser-Gorom": "ges",
"Geshiza": "ero-gsz",
"Gey": "guv",
"Ghadames": "gha",
"Ghanaian Sign Language": "gse",
"Ghandruk Sign Language": "gds",
"Ghanongga": "ghn",
"Ghari": "gri",
"Ghayavi": "bmk",
"Ghera": "ghr",
"Ghomala'": "bbj",
"Ghomara": "gho",
"Ghotuo": "aaa",
"Ghulfan": "ghl",
"Giangan": "bgi",
"Gibanawa": "gib",
"Gidar": "gid",
"Gikyode": "acd",
"Gilaki": "glk",
"Gilbertese": "gil",
"Gilima": "gix",
"Gimi (Austronesian)": "gip",
"Gimi (Papuan)": "gim",
"Gimme": "kmp",
"Gimnime": "gmn",
"Ginuman": "gnm",
"Girawa": "bbr",
"Girirra": "gii",
"Giryama": "nyf",
"Githabul": "gih",
"Gitua": "ggt",
"Gitxsan": "git",
"Giyug": "giy",
"Gizrra": "tof",
"Glaro-Twabo": "glr",
"Glavda": "glw",
"Glio-Oubi": "oub",
"Glosa": "igs",
"Gnau": "gnu",
"Goa'uld": "art-gld",
"Goaria": "gig",
"Gobasi": "goi",
"Gobu": "gox",
"Godié": "god",
"Godoberi": "gdo",
"Godwari": "gdx",
"Goemai": "ank",
"Gofa": "gof",
"Gogo": "gog",
"Gogodala": "ggw",
"Goguryeo": "zkg",
"Gojri": "gju",
"Gokana": "gkn",
"Gokhy": "tbq-gkh",
"Gola": "gol",
"Golin": "gvf",
"Golpa": "lja",
"Gondi": "gon",
"Gone Dau": "goo",
"Gong": "ugo",
"Gongduk": "goe",
"Gonja": "gjn",
"Goo": "gov",
"Gooniyandi": "gni",
"Gor": "gqr",
"Gorakor": "goc",
"Gorap": "goq",
"Goreng": "xgg",
"Gorontalo": "gor",
"Gorovu": "grq",
"Gorwaa": "gow",
"Gothic": "got",
"Gottscheerish": "gmw-gts",
"Goundo": "goy",
"Gourmanchéma": "gux",
"Gowlan": "goj",
"Gowro": "gwf",
"Gozarkhani": "goz",
"Grangali": "nli",
"Grass Koiari": "kbk",
"Greek": "el",
"Greek Sign Language": "gss",
"Green Gelao": "giq",
"Green Hmong": "hnj",
"Greenlandic": "kl",
"Grenadian Creole English": "gcl",
"Gresi": "grs",
"Groma": "gro",
"Gros Ventre": "ats",
"Gua": "gwx",
"Guachí": "sai-gua",
"Guahibo": "guh",
"Guajajára": "gub",
"Guajá": "gvj",
"Guambiano": "gum",
"Guamo": "sai-gmo",
"Guanano": "gvc",
"Guanche": "gnc",
"Guarayu": "gyr",
"Guatemalan Sign Language": "gsm",
"Guató": "gta",
"Guayabero": "guo",
"Guazacapán": "nai-guz",
"Gudang": "xgd",
"Gudanji": "nji",
"Gude": "gde",
"Gudu": "gdu",
"Guduf-Gava": "gdf",
"Guerrero Amuzgo": "amu",
"Guerrero Nahuatl": "ngu",
"Guevea de Humboldt Zapotec": "zpg",
"Gugadj": "ggd",
"Gugu Badhun": "gdc",
"Gugu Warra": "wrw",
"Guhu-Samane": "ghs",
"Guianese Creole": "gcr",
"Guiberoua Bété": "bet",
"Guinau": "awd-gnu",
"Guinea Kpelle": "gkp",
"Guinea-Bissau Creole": "pov",
"Guinea-Bissau Sign Language": "lgs",
"Guinean Sign Language": "gus",
"Guiqiong": "gqi",
"Gujarati": "gu",
"Gula": "glu",
"Gula'alaa": "gmb",
"Gulay": "gvl",
"Gule": "gly",
"Gulf Arabic": "afb",
"Gullah": "gul",
"Gumalu": "gmu",
"Gumatj": "gnn",
"Gumawana": "gvs",
"Gumuz": "guk",
"Gun": "guw",
"Gundi": "gdi",
"Gunditjmara": "gjm",
"Gundungurra": "xrd",
"Gungabula": "gyf",
"Gungu": "rub",
"Guntai": "gnt",
"Gunu": "yas",
"Gunwinggu": "gup",
"Gunya": "gyy",
"Gupa-Abawa": "gpa",
"Gupapuyngu": "guf",
"Gur Lama": "las",
"Guragone": "gge",
"Guramalum": "grz",
"Gurani": "hac",
"Gureng Gureng": "gnr",
"Gurgula": "ggg",
"Guriaso": "grx",
"Gurindji": "gue",
"Gurindji Kriol": "gjr",
"Gurmana": "gvm",
"Guro": "goa",
"Gurung": "gvr",
"Guruntum": "grd",
"Gusan": "gsn",
"Gusii": "guz",
"Gusilay": "gsl",
"Gutnish": "gmq-gut",
"Guugu Yimidhirr": "kky",
"Guwa": "xgw",
"Guwamu": "gwu",
"Guwar": "aus-guw",
"Guya": "gka",
"Guyanese Creole English": "gyn",
"Guyani": "gvy",
"Guébie": "gie",
"Gvoko": "ngs",
"Gwa": "gwb",
"Gwahatike": "dah",
"Gwak": "jgk",
"Gwamhi-Wuri": "bga",
"Gwandara": "gwn",
"Gwara": "alv-gwa",
"Gweda": "grw",
"Gweno": "gwe",
"Gwere": "gwr",
"Gwich'in": "gwi",
"Gyalsumdo": "gyo",
"Gyele": "gyi",
"Gyem": "gye",
"Güenoa": "sai-gue",
"Habu": "hbu",
"Hachijō": "jpx-hcj",
"Hadiyya": "hdy",
"Hadoti": "hoj",
"Hadrami": "xhd",
"Hadza": "hts",
"Haeke": "aek",
"Hahon": "hah",
"Haida": "hai",
"Haigwai": "hgw",
"Hailufeng Min": "nan-hlh",
"Hainanese": "hnm",
"Hainyaxo Bozo": "bzx",
"Haiphong Sign Language": "haf",
"Haisla": "has",
"Haitian Creole": "ht",
"Haitian Vodoun Culture Language": "hvc",
"Haiǁom": "hgm",
"Haji": "hji",
"Hajong": "haj",
"Hakka": "hak",
"Hakö": "hao",
"Halang": "hal",
"Halang Doan": "hld",
"Halbi": "hlb",
"Halia": "hla",
"Halkomelem": "hur",
"Hamap": "hmu",
"Hamba": "hba",
"Hamer-Banna": "amf",
"Hamtai": "hmt",
"Hanga": "hag",
"Hanga Hundi": "wos",
"Hani": "hni",
"Hanoi Sign Language": "hab",
"Hanunoo": "hnn",
"Harami": "xha",
"Harappan": "xiv",
"Harari": "har",
"Haraza": "nub-har",
"Harijan Kinnauri": "kjo",
"Haroi": "hro",
"Harsusi": "hss",
"Haruai": "tmd",
"Haruku": "hrk",
"Haryanvi": "bgc",
"Harzani": "hrz",
"Hasaitic": "sem-has",
"Hasha": "ybj",
"Hassaniya Arabic": "mey",
"Hatam": "had",
"Hattic": "xht",
"Hausa": "ha",
"Hausa Sign Language": "hsl",
"Haush": "sai-hau",
"Havasupai-Walapai-Yavapai": "yuf",
"Haveke": "hvk",
"Havu": "hav",
"Hawai'i Pidgin Sign Language": "hps",
"Hawaiian": "haw",
"Hawaiian Creole": "hwc",
"Haya": "hay",
"Hdi": "xed",
"Hebrew": "he",
"Hehe": "heh",
"Heiban": "hbn",
"Heiltsuk": "hei",
"Helong": "heg",
"Hema": "nix",
"Hemba": "hem",
"Hember Avu": "mmi",
"Herdé": "hed",
"Herero": "hz",
"Hermit": "llf",
"Hernican": "xhr",
"Hewa": "ham",
"Heyo": "auk",
"Hibito": "hib",
"Hidatsa": "hid",
"Higaonon": "mba",
"High Valyrian": "art-vlh",
"Highland Konjo": "kjk",
"Highland Oaxaca Chontal": "chd",
"Highland Popoluca": "poi",
"Highland Puebla Nahuatl": "azz",
"Highland Totonac": "tos",
"Hijazi Arabic": "acw",
"Hijuk": "hij",
"Hiligaynon": "hil",
"Hill Maria": "mrr",
"Himarimã": "hir",
"Hindi": "hi",
"Hindi Dogri": "dgo",
"Hinduri": "hii",
"Hinukh": "gin",
"Hiri Motu": "ho",
"Hismaic": "sem-his",
"Hitchiti": "nai-hit",
"Hittite": "hit",
"Hitu": "htu",
"Hiw": "hiw",
"Hixkaryana": "hix",
"Hlai": "lic",
"Hlepho Phowa": "yhl",
"Hlersu": "hle",
"Hmar": "hmr",
"Hmong Don": "hmf",
"Hmong Dô": "hmv",
"Hmong Shua": "hmz",
"Hmwaveke": "mrk",
"Ho": "hoc",
"Ho Chi Minh City Sign Language": "hos",
"Hoava": "hoa",
"Hobyót": "hoh",
"Hoia Hoia": "hhi",
"Hokkien": "nan-hbl",
"Holikachuk": "hoi",
"Holiya": "hoy",
"Holma": "hod",
"Holoholo": "hoo",
"Holu": "hol",
"Homa": "hom",
"Honduran Lenca": "len",
"Honduras Sign Language": "hds",
"Hone": "juh",
"Hong Kong Sign Language": "hks",
"Honi": "how",
"Hopi": "hop",
"Horned Miao": "hrm",
"Horo": "hor",
"Horom": "hoe",
"Hote": "hot",
"Hoti": "hti",
"Hovongan": "hov",
"Hoyahoya": "hhy",
"Hozo": "hoz",
"Hpon": "hpo",
"Hrangkhol": "hra",
"Hruso": "hru",
"Hrê": "hre",
"Hu": "huo",
"Huachipaeri": "hug",
"Huambisa": "hub",
"Huaorani": "auc",
"Huarijio": "var",
"Huaulu": "hud",
"Huautla Mazatec": "mau",
"Huave": "huv",
"Huaxcaleca Nahuatl": "nhq",
"Huba": "hbb",
"Huehuetla Tepehua": "tee",
"Huetar": "cba-hue",
"Huichol": "hch",
"Huilliche": "huh",
"Huitepec Mixtec": "mxs",
"Huizhou": "czh",
"Hukumina": "huw",
"Hula": "hul",
"Hulaulá": "huy",
"Huli": "hui",
"Hulung": "huk",
"Humburi Senni": "hmb",
"Humene": "huf",
"Hun": "uth",
"Hunde": "hke",
"Hung": "hnu",
"Hungana": "hum",
"Hungarian": "hu",
"Hungarian Sign Language": "hsh",
"Hungworo": "nat",
"Hunjara-Kaina Ke": "hkk",
"Hunnic": "xhc",
"Hunsrik": "hrx",
"Hunzib": "huz",
"Hupa": "hup",
"Hupdë": "jup",
"Hupla": "hap",
"Hurrian": "xhu",
"Hutterisch": "geh",
"Hwana": "hwo",
"Hya": "hya",
"Hyam": "jab",
"Hän": "haa",
"Hértevin": "hrt",
"I-Wak": "iwk",
"Iaai": "iai",
"Iamalele": "yml",
"Iatmul": "ian",
"Iau": "tmu",
"Ibali Teke": "tek",
"Ibaloi": "ibl",
"Iban": "iba",
"Ibanag": "ibg",
"Ibani": "iby",
"Ibatan": "ivb",
"Iberian": "xib",
"Ibibio": "ibb",
"Ibino": "ibn",
"Iboko": "bkp",
"Ibu": "ibu",
"Ibuoro": "ibr",
"Icelandic": "is",
"Icelandic Sign Language": "icl",
"Iceve-Maci": "bec",
"Ida'an": "dbj",
"Idakho-Isukha-Tiriki": "ida",
"Idaté": "idt",
"Idere": "ide",
"Idesa": "ids",
"Idi": "idi",
"Idiom Neutral": "mis-idn",
"Ido": "io",
"Idoma": "idu",
"Idon": "idc",
"Idu": "clk",
"Idun": "ldb",
"Iduna": "viv",
"Ifo": "iff",
"Ifè": "ife",
"Igala": "igl",
"Igana": "igg",
"Igbo": "ig",
"Igede": "ige",
"Ignaciano": "ign",
"Igo": "ahl",
"Iguta": "nar",
"Igwe": "igw",
"Iha": "ihp",
"Ihievbe": "ihi",
"Ija-Zuba": "vki",
"Ik": "ikx",
"Ika": "ikk",
"Ikaranggal": "ikr",
"Ikizu": "ikz",
"Iko": "iki",
"Ikobi-Mena": "meb",
"Ikoma": "ntk",
"Ikpeng": "txi",
"Ikpeshi": "ikp",
"Ikposo": "kpo",
"Iku-Gora-Ankwa": "ikv",
"Ikulu": "ikl",
"Ikwere": "ikw",
"Ikwo": "iqw",
"Ila": "ilb",
"Ile Ape": "ila",
"Ilgar": "ilg",
"Ili Turki": "ili",
"Ili'uun": "ilu",
"Ilianen Manobo": "mbi",
"Illyrian": "xil",
"Ilocano": "ilo",
"Ilongot": "ilk",
"Ilue": "ilv",
"Ilwana": "mlk",
"Imbongu": "imo",
"Imonda": "imn",
"Imroing": "imr",
"Inabaknon": "abx",
"Inapang": "mzu",
"Inari Sami": "smn",
"Indanga": "bnt-ind",
"Indian Sign Language": "ins",
"Indo-Portuguese": "idb",
"Indonesian": "id",
"Indonesian Bajau": "bdl",
"Indonesian Sign Language": "inl",
"Indri": "idr",
"Indus Kohistani": "mvy",
"Inebu One": "oin",
"Ineseño": "inz",
"Inga": "inb",
"Ingrian": "izh",
"Ingush": "inh",
"Inlaod Itneg": "iti",
"Inoke-Yate": "ino",
"Inonhan": "loc",
"Inor": "ior",
"Inpui Naga": "nkf",
"Interlingua": "ia",
"Interlingue": "ie",
"International Sign": "ils",
"Interslavic": "isv",
"Intha": "int",
"Inuinnaqtun": "esx-inq",
"Inuit Sign Language": "iks",
"Inuktitut": "iu",
"Inuktun": "esx-ink",
"Inupiaq": "ik",
"Inuvialuktun": "ikt",
"Ipai": "nai-ipa",
"Ipalapa Amuzgo": "azm",
"Ipiko": "ipo",
"Ipili": "ipi",
"Ipulo": "ass",
"Iquito": "iqu",
"Ir": "irr",
"Irantxe": "irn",
"Iranun": "ill",
"Iraqi Arabic": "acm",
"Iraqw": "irk",
"Irarutu": "irh",
"Iraya": "iry",
"Iresim": "ire",
"Irish": "ga",
"Irish Sign Language": "isg",
"Irula": "iru",
"Isabi": "isa",
"Isan": "tts",
"Isanzu": "isn",
"Isarog Agta": "agk",
"Isaurian": "mis-isa",
"Isconahua": "isc",
"Isebe": "igo",
"Ishkashimi": "isk",
"Isinai": "inn",
"Isirawa": "srl",
"Islander Creole English": "icr",
"Isnag": "isd",
"Isoko": "iso",
"Israeli Sign Language": "isr",
"Isthmus Mixe": "mir",
"Isthmus Zapotec": "zai",
"Istriot": "ist",
"Istro-Romanian": "ruo",
"Isu": "isu",
"Isubu": "szv",
"Italian": "it",
"Italian Sign Language": "ise",
"Italiot Greek": "grk-ita",
"Itawit": "itv",
"Itelmen": "itl",
"Itene": "ite",
"Iteri": "itr",
"Itik": "itx",
"Ito": "itw",
"Itonama": "ito",
"Itsekiri": "its",
"Itu Mbon Uzo": "itm",
"Itundujia Mixtec": "mce",
"Itza'": "itz",
"Iu Mien": "ium",
"Ivatan": "ivv",
"Iwaidja": "ibd",
"Iwal": "kbm",
"Iwam": "iwm",
"Iwur": "iwo",
"Ixcatec": "ixc",
"Ixcatlán Mazatec": "mzi",
"Ixil": "ixl",
"Ixtayutla Mixtec": "vmj",
"Ixtenco Otomi": "otz",
"Iyayu": "iya",
"Iyive": "uiv",
"Iyo": "nca",
"Iyo'wujwa Chorote": "crq",
"Iyojwa'ja Chorote": "crt",
"Izere": "izr",
"Izi": "izz",
"Izi-Ezaa-Ikwo-Mgbo": "izi",
"Izon": "ijc",
"Izora": "cbo",
"Iñapari": "inp",
"Jabem": "jae",
"Jabutí": "jbt",
"Jad": "jda",
"Jadgali": "jdg",
"Jah Hut": "jah",
"Jahanka": "jad",
"Jair Awyu": "awv",
"Jakaltek": "jac",
"Jakati": "jat",
"Jalapa de Díaz Mazatec": "maj",
"Jalkunan": "bxl",
"Jamaican Country Sign Language": "jcs",
"Jamaican Creole": "jam",
"Jamaican Sign Language": "jls",
"Jamamadí": "jaa",
"Jambi Malay": "jax",
"Jamiltepec Mixtec": "mxt",
"Jaminjung": "djd",
"Jamsay": "djm",
"Jamtish": "gmq-jmk",
"Jandavra": "jnd",
"Janday": "jan",
"Jangkang": "djo",
"Jangshung": "jna",
"Janji": "jni",
"Japanese": "ja",
"Japanese Sign Language": "jsl",
"Japhug": "sit-jap",
"Japrería": "jru",
"Jaqaru": "jqr",
"Jara": "jaf",
"Jarai": "jra",
"Jarawa": "anq",
"Jaru": "ddj",
"Jassic": "ysc",
"Jaunsari": "jns",
"Javanese": "jv",
"Javindo": "jvd",
"Jawe": "jaz",
"Jaya": "jyy",
"Jebero": "jeb",
"Jeh": "jeh",
"Jehai": "jhi",
"Jeikó": "sai-jko",
"Jeju": "jje",
"Jemez": "tow",
"Jenaama Bozo": "bze",
"Jeng": "jeg",
"Jennu Kurumba": "xuj",
"Jere": "jer",
"Jeri Kuo": "jek",
"Jersey Dutch": "gmw-jdt",
"Jeru": "akj",
"Jerung": "jee",
"Jhankot Sign Language": "jhs",
"Jiamao": "jio",
"Jiba": "juo",
"Jibu": "jib",
"Jicarilla": "apj",
"Jie": "mis-jie",
"Jiiddu": "jii",
"Jilbe": "jie",
"Jili": "mgi",
"Jilim": "jil",
"Jimi": "jmi",
"Jimjimen": "jim",
"Jin": "cjy",
"Jina": "jia",
"Jingpho": "kac",
"Jingulu": "jig",
"Jiongnai Bunu": "pnu",
"Jirajara": "sai-jrj",
"Jirel": "jul",
"Jiru": "jrr",
"Jita": "jit",
"Jizhao": "mis-jzh",
"Jju": "kaj",
"Joba": "job",
"Jofotek-Bromnya": "jbr",
"Jola-Fonyi": "dyo",
"Jola-Kasa": "csk",
"Jonkor Bourmataguil": "jeu",
"Jordanian Sign Language": "jos",
"Jorá": "jor",
"Jowulu": "jow",
"Ju": "juu",
"Juang": "jun",
"Juba Arabic": "pga",
"Judeo-Italian": "itk",
"Judeo-Persian": "jpr",
"Judeo-Tat": "jdt",
"Jukun Takum": "jbu",
"Jumaytepeque": "nai-jum",
"Jumjum": "jum",
"Jumla Sign Language": "jus",
"Jumli": "jml",
"Jungle Inga": "inj",
"Juquila Mixe": "mxq",
"Jur Modo": "bex",
"Juray": "juy",
"Jurchen": "juc",
"Jurúna": "jur",
"Jutiapa": "nai-jtp",
"Jutish": "jut",
"Juwal": "mwb",
"Juxtlahuaca Mixtec": "vmc",
"Juǀ'hoan": "ktz",
"Jwira-Pepesa": "jwi",
"Júma": "jua",
"K'iche'": "quc",
"Kaamba": "xku",
"Kaan": "ldl",
"Kaang Chin": "ckn",
"Kaansa": "gna",
"Kaapor Sign Language": "uks",
"Kaba": "ksp",
"Kabalai": "kvf",
"Kabatei": "xkp",
"Kabba-Laka": "lap",
"Kabishiana": "tup-kab",
"Kabiye": "kbp",
"Kabola": "klz",
"Kabore One": "onk",
"Kabras": "lkb",
"Kaburi": "uka",
"Kabutra": "kbu",
"Kabuverdianu": "kea",
"Kabwa": "cwa",
"Kabwari": "kcw",
"Kabyle": "kab",
"Kachama-Ganjule": "kcx",
"Kachari": "xac",
"Kachchi": "kfr",
"Kachi Koli": "gjk",
"Kacipo-Balesi": "koe",
"Kaco'": "xkk",
"Kadai": "kzd",
"Kadar": "kej",
"Kadara": "kad",
"Kadaru": "kdu",
"Kadiwéu": "kbc",
"Kado": "kdv",
"Kadu (Myanmar)": "zkd",
"Kadugli": "xtc",
"Kaduo": "ktp",
"Kaera": "jka",
"Kafa": "kbr",
"Kafoa": "kpu",
"Kagan Kalagan": "kll",
"Kagate": "syw",
"Kagayanen": "cgc",
"Kagoma": "kdm",
"Kagoro": "xkg",
"Kagulu": "kki",
"Kahe": "hka",
"Kahua": "agw",
"Kaian": "kct",
"Kaibobo": "kzb",
"Kaidipang": "kzp",
"Kaiep": "kbw",
"Kaikadi": "kep",
"Kaike": "kzq",
"Kaiku": "kkq",
"Kaimbulawa": "zka",
"Kaimbé": "xai",
"Kaingang": "kgp",
"Kairak": "ckr",
"Kairiru": "kxa",
"Kairui-Midiki": "krd",
"Kais": "kzm",
"Kaitag": "xdq",
"Kaivi": "kce",
"Kaiwá": "kgk",
"Kaiy": "tcq",
"Kajakse": "ckq",
"Kajali": "xkj",
"Kajaman": "kag",
"Kakabai": "kqf",
"Kakabe": "kke",
"Kakanda": "kka",
"Kaki Ae": "tbd",
"Kakihum": "kxe",
"Kako": "kkj",
"Kakwa": "keo",
"Kala": "kcl",
"Kala Lagaw Ya": "mwp",
"Kalaamaya": "lkm",
"Kalabakan": "kve",
"Kalabari": "ijn",
"Kalabra": "kzz",
"Kalagan": "kqe",
"Kalaktang Monpa": "kkf",
"Kalam": "kmh",
"Kalami": "gwc",
"Kalamsé": "knz",
"Kalanadi": "wkl",
"Kalanga": "kck",
"Kalao": "kly",
"Kalapuya": "kyl",
"Kalarko": "kba",
"Kalasha": "kls",
"Kalasuri": "xme-kls",
"Kalašma": "ine-kal",
"Kalenjin": "kln",
"Kalinago": "crb",
"Kalkatungu": "ktg",
"Kalkoti": "xka",
"Kallawaya": "caw",
"Kalmyk": "xal",
"Kalo Finnish Romani": "rmf",
"Kalou": "ywa",
"Kaluli": "bco",
"Kalumpang": "kli",
"Kam": "kdx",
"Kamakan": "vkm",
"Kamang": "woi",
"Kamano": "kbq",
"Kamantan": "kci",
"Kamar": "keq",
"Kamara": "jmr",
"Kamarian": "kzx",
"Kamaru": "kgx",
"Kamarupi Prakrit": "inc-kam",
"Kamasa": "klp",
"Kamasau": "kms",
"Kamassian": "xas",
"Kamayo": "kyk",
"Kamayurá": "kay",
"Kamba": "kam",
"Kambaata": "ktb",
"Kambaira": "kyy",
"Kambera": "xbr",
"Kamberataro": "kbv",
"Kamberau": "irx",
"Kambiwá": "xbw",
"Kami": "kmi",
"Kamkata-viri": "bsh",
"Kamo": "kcq",
"Kamoro": "kgq",
"Kamta": "rkt",
"Kamu": "xmu",
"Kamula": "xla",
"Kamwe": "hig",
"Kanakanabu": "xnb",
"Kanakuru": "kna",
"Kanamari": "knm",
"Kanashi": "xns",
"Kanasi": "soq",
"Kandas": "kqw",
"Kandawo": "gam",
"Kande": "kbs",
"Kang": "kyp",
"Kanga": "kcp",
"Kangean": "kkv",
"Kanggape": "igm",
"Kangjia": "kxs",
"Kango": "kty",
"Kango-Sua": "kzy",
"Kangri": "xnr",
"Kaniet": "ktk",
"Kanikkaran": "kev",
"Kaningdon-Nindem": "kdp",
"Kaningi": "kzo",
"Kaningra": "knr",
"Kaninuwa": "wat",
"Kanite": "kmu",
"Kanjari": "kft",
"Kanju": "kbe",
"Kankanaey": "kne",
"Kannada": "kn",
"Kannada Kurumba": "kfi",
"Kannauji": "bjj",
"Kanowit": "kxn",
"Kanoé": "kxo",
"Kansa": "ksk",
"Kantosi": "xkt",
"Kanu": "khx",
"Kanufi": "kni",
"Kanuri": "kr",
"Kanyok": "kny",
"Kao": "kax",
"Kaonde": "kqn",
"Kap": "ykm",
"Kapampangan": "pam",
"Kapauri": "khp",
"Kapin": "tbx",
"Kapinawá": "xpn",
"Kapingamarangi": "kpg",
"Kapriman": "dju",
"Kaptiau": "kbi",
"Kapya": "klo",
"Kaqchikel": "cak",
"Kaqchikel-K'iche' Mixed Language": "ckz",
"Kara (New Guinea)": "leu",
"Kara (Tanzania)": "reg",
"Karachay-Balkar": "krc",
"Karadjeri": "gbd",
"Karaga Mandaya": "mry",
"Karaim": "kdr",
"Karajá": "kpj",
"Karakalpak": "kaa",
"Karakhanid": "xqa",
"Karami": "xar",
"Karamojong": "kdj",
"Karang": "kzr",
"Karanga": "kth",
"Karankawa": "zkk",
"Karao": "kyj",
"Karas": "kgv",
"Karata": "kpt",
"Karawa": "xrw",
"Karbi": "mjw",
"Kare (Central Africa)": "kbn",
"Kare (New Guinea)": "kmf",
"Karekare": "kai",
"Karelian": "krl",
"Karey": "kyd",
"Kari": "kbj",
"Kari'na": "car",
"Karian": "bql",
"Karingani": "kgn",
"Karipuna": "kuq",
"Karipúna Creole French": "kmv",
"Kariri": "kzw",
"Karitiâna": "ktn",
"Kariya": "kil",
"Kariyarra": "vka",
"Karkar-Yuri": "yuj",
"Karkin": "krb",
"Karko": "kko",
"Karnai": "bbv",
"Karo": "kxh",
"Karo Batak": "btx",
"Karok": "kyh",
"Karolanos": "kyn",
"Karon": "krx",
"Karon Dori": "kgw",
"Karore": "xkx",
"Karranga": "xrq",
"Karuwali": "rxw",
"Kasanga": "ccj",
"Kasem": "xsm",
"Kashaya": "kju",
"Kashmiri": "ks",
"Kashubian": "csb",
"Kasiguranin": "ksn",
"Kaska": "kkz",
"Kaskean": "zsk",
"Kaskihá": "gva",
"Kassite": "mis-kas",
"Kassonke": "kao",
"Kasua": "khs",
"Kataang": "kgd",
"Katabaga": "ktq",
"Katawixi": "xat",
"Katembri": "sai-kat",
"Kathlamet": "nai-kat",
"Kathoriya Tharu": "tkt",
"Kathu": "ykt",
"Katkari": "kfu",
"Katla": "kcr",
"Kato": "ktw",
"Katso": "kaf",
"Katua": "kta",
"Katukina": "knt",
"Kaulong": "pss",
"Kaur": "vkk",
"Kaure": "bpp",
"Kaurna": "zku",
"Kauwera": "xau",
"Kavalan": "ckv",
"Kavet": "krv",
"Kawacha": "kcb",
"Kawaiisu": "xaw",
"Kawe": "kgb",
"Kawishana": "awd-kaw",
"Kawésqar": "alc",
"Kaxararí": "ktx",
"Kaxuyana": "kbb",
"Kayabí": "kyz",
"Kayagar": "kyt",
"Kayan": "pdu",
"Kayan Mahakam": "xay",
"Kayan River Kayan": "xkn",
"Kayapa Kallahan": "kak",
"Kayapó": "txu",
"Kayardild": "gyd",
"Kayeli": "kzl",
"Kayong": "kxy",
"Kayort": "kyv",
"Kaytetye": "gbb",
"Kayupulau": "kzu",
"Kazakh": "kk",
"Kazukuru": "kzk",
"Ke'o": "xxk",
"Keak": "keh",
"Keapara": "khz",
"Kedah Malay": "meo",
"Kedang": "ksx",
"Keder": "kdy",
"Kehu": "khh",
"Kei": "kei",
"Keiga": "kec",
"Kein": "bmh",
"Keiyo": "eyo",
"Kela-Yela": "kel",
"Kelabit": "kzi",
"Kelantan Peranakan Hokkien": "mis-hkl",
"Keley-I Kallahan": "ify",
"Keliko": "kbo",
"Kelo": "xel",
"Kelon": "kyo",
"Kemak": "kem",
"Kembayan": "xem",
"Kemberano": "bzp",
"Kembra": "xkw",
"Kemezung": "dmo",
"Kemi Sami": "sjk",
"Kemiehua": "kfj",
"Kemtuik": "kmt",
"Kenaboi": "xbn",
"Kenati": "gat",
"Kendayan": "knx",
"Kendeje": "klf",
"Kendem": "kvm",
"Kenga": "kyq",
"Keningau Murut": "kxi",
"Keninjal": "knl",
"Kensiu": "kns",
"Kenswei Nsei": "ndb",
"Kenyan Sign Language": "xki",
"Kenyang": "ken",
"Kenyi": "lke",
"Keoru-Ahia": "xeu",
"Kepkiriwát": "kpn",
"Kepo'": "kuk",
"Kera": "ker",
"Kerak": "hhr",
"Kereho": "xke",
"Kerek": "krk",
"Kerewe": "ked",
"Kerewo": "kxz",
"Kerinci": "kvr",
"Kermanic": "xme-ker",
"Ket": "ket",
"Ketangalan": "kae",
"Kete": "kcv",
"Ketengban": "xte",
"Ketum": "ktt",
"Kewa": "kew",
"Keyagana": "kyg",
"Kgalagadi": "xkv",
"Khakas": "kjh",
"Khalaj": "klj",
"Khaling": "klr",
"Khamnigan Mongol": "ykh",
"Khamti": "kht",
"Khamyang": "ksu",
"Khana": "ogo",
"Khandeshi": "khn",
"Khao": "xao",
"Kharam Naga": "kfw",
"Kharia": "khr",
"Kharia Thar": "ksy",
"Khasi": "kha",
"Khayo": "lko",
"Khazar": "zkz",
"Khe": "kqg",
"Khehek": "tlx",
"Khengkha": "xkf",
"Khetrani": "xhe",
"Khezha Naga": "nkh",
"Khiamniungan Naga": "kix",
"Khinalug": "kjj",
"Khirwar": "kwx",
"Khisa": "kqm",
"Khitan": "zkt",
"Khlula": "ykl",
"Khmer": "km",
"Khmu": "kjg",
"Khoekhoe": "naq",
"Khoibu Naga": "nkb",
"Khoini": "xkc",
"Kholok": "ktc",
"Kholosi": "inc-kho",
"Khorasani Turkish": "kmz",
"Khorezmian Turkic": "zkh",
"Khotanese": "kho",
"Khowar": "khw",
"Khroskyabs": "jiq",
"Khua": "xhv",
"Khuen": "khf",
"Khumi Chin": "cnk",
"Khvarshi": "khv",
"Khwarezmian": "xco",
"Khwe": "xuu",
"Kháng": "kjm",
"Khün": "kkh",
"Kiautschou German Pidgin": "crp-kia",
"Kibala": "blv",
"Kibena": "bez",
"Kibet": "kie",
"Kibiri": "prm",
"Kichwa": "qwe-kch",
"Kickapoo": "kic",
"Kikai": "kzg",
"Kikami": "kcu",
"Kikuyu": "ki",
"Kildin Sami": "sjd",
"Kili": "tuw-kli",
"Kilit": "xme-klt",
"Kilivila": "kij",
"Kiliwa": "klb",
"Kilmeri": "kih",
"Kim": "kia",
"Kim Mun": "mji",
"Kimaama": "kig",
"Kimaragang": "kqr",
"Kimbu": "kiv",
"Kimbundu": "kmb",
"Kimki": "sbt",
"Kimré": "kqp",
"Kinabalian": "cbw",
"Kinalakna": "kco",
"Kinaray-a": "krj",
"Kinga": "zga",
"Kings River Yokuts": "yok-kry",
"Kinikinao": "gqn",
"Kinnauri": "kfk",
"Kintaq": "knq",
"Kinuku": "kkd",
"Kioko": "ues",
"Kiong": "kkm",
"Kiorr": "xko",
"Kiowa": "kio",
"Kipchak": "qwm",
"Kipfokomo": "pkb",
"Kipsigis": "sgc",
"Kiput": "kyi",
"Kir-Balar": "kkr",
"Kire": "geb",
"Kirfi": "kks",
"Kirike": "okr",
"Kirikiri": "kiy",
"Kirya-Konzel": "fkk",
"Kis": "kis",
"Kisa": "lks",
"Kisan": "xis",
"Kisankasa": "kqh",
"Kisar": "kje",
"Kisi": "kiz",
"Kistane": "gru",
"Kita Maninkakan": "mwk",
"Kitanemuk": "azc-ktn",
"Kitembo": "tbt",
"Kitja": "gia",
"Kitsai": "kii",
"Kituba": "ktu",
"Kiunum": "wei",
"Kla": "lda",
"Klallam": "clm",
"Klamath-Modoc": "kla",
"Klao": "klu",
"Klias River Kadazan": "kqt",
"Klingon": "tlh",
"Knaanic": "czk",
"Ko": "fuj",
"Koalib": "kib",
"Koasati": "cku",
"Koba": "kpd",
"Kobiana": "kcj",
"Kobol": "kgu",
"Kobon": "kpw",
"Koch": "kdq",
"Kochila Tharu": "thq",
"Koda": "cdz",
"Kodaku": "ksz",
"Kodava": "kfa",
"Kodeoha": "vko",
"Kodi": "kod",
"Kodia": "kwp",
"Koenoem": "kcs",
"Kofa": "kso",
"Kofei": "kpi",
"Kofyar": "kwl",
"Kohin": "kkx",
"Kohistani Shina": "plk",
"Koho": "kpm",
"Kohumono": "bcs",
"Koi": "kkt",
"Koibal": "zkb",
"Koireng": "nkd",
"Koitabu": "kqi",
"Koiwat": "kxt",
"Kok-Nar": "gko",
"Kok-Paponk": "okg",
"Kokata": "ktd",
"Kokborok": "trp",
"Koke": "kou",
"Koko-Bera": "kkp",
"Kokoda": "xod",
"Kokola": "kzn",
"Kokota": "kkk",
"Kol (Cameroon)": "biw",
"Kol (New Guinea)": "kol",
"Kola": "kvv",
"Kolami": "kfb",
"Kolbila": "klc",
"Kolhe": "ekl",
"Kolibugan Subanon": "skn",
"Kolom": "klm",
"Koluwawa": "klx",
"Kom (Cameroon)": "bkm",
"Kom (India)": "kmm",
"Koma": "kmy",
"Komba": "kpf",
"Kombai": "tyn",
"Kombio": "xbi",
"Komering": "kge",
"Komi-Permyak": "koi",
"Komi-Yazva": "urj-kya",
"Komi-Zyrian": "kpv",
"Kominimung": "xoi",
"Komo": "xom",
"Komodo": "kvh",
"Kompane": "kvp",
"Komyandaret": "kzv",
"Kon Keu": "kkn",
"Konabéré": "bbo",
"Konai": "kxw",
"Konda": "knd",
"Konda-Dora": "kfc",
"Kondekor": "gau",
"Koneraw": "kdw",
"Kongo": "kg",
"Konkani": "kok",
"Konkomba": "xon",
"Konni": "kma",
"Kono (Guinea)": "knu",
"Kono (Nigeria)": "klk",
"Kono (Sierra Leone)": "kno",
"Konomala": "koa",
"Konomihu": "nai-knm",
"Konongo": "kcz",
"Konso": "kxc",
"Konyak Naga": "nbe",
"Konyanka Maninka": "mku",
"Konzo": "koo",
"Koonzime": "ozm",
"Koorete": "kqy",
"Kopar": "xop",
"Kopkaka": "opk",
"Korafe-Yegha": "kpr",
"Korak": "koz",
"Korana": "kqz",
"Korandje": "kcy",
"Korean": "ko",
"Korean Sign Language": "kvk",
"Koreguaje": "coe",
"Koresh-e Rostam": "okh",
"Korku": "kfq",
"Korlai Creole Portuguese": "vkp",
"Koro (India)": "jkr",
"Koro (Vanuatu)": "krf",
"Koro (West Africa)": "kfo",
"Koromfé": "kfz",
"Koromira": "kqj",
"Koromu": "xes",
"Koronadal Blaan": "bpr",
"Koroni": "xkq",
"Korop": "krp",
"Koropó": "xxr",
"Koroshi": "ktl",
"Korowai": "khe",
"Korra Koraga": "kfd",
"Korubo": "xor",
"Korupun-Sela": "kpq",
"Korwa": "kfp",
"Koryak": "kpy",
"Kosadle": "kiq",
"Kosarek Yale": "kkl",
"Koshin": "kid",
"Kosraean": "kos",
"Kota (Gabon)": "koq",
"Kota (India)": "kfe",
"Kota Bangun Kutai Malay": "mqg",
"Kota Marudu Talantang": "grm",
"Kota Marudu Tinagas": "ktr",
"Kotafon Gbe": "kqk",
"Kotava": "avk",
"Koti": "eko",
"Kott": "zko",
"Kou": "snz",
"Kouya": "kyf",
"Kovai": "kqb",
"Kove": "kvc",
"Kowaki": "xow",
"Kowiai": "kwh",
"Koy Sanjaq Surat": "kqd",
"Koya": "kff",
"Koyaga": "kga",
"Koyo": "koh",
"Koyra Chiini": "khq",
"Koyraboro Senni": "ses",
"Koyukon": "koy",
"Kpagua": "kuw",
"Kpala": "kpl",
"Kpan": "kpk",
"Kpasam": "pbn",
"Kpati": "koc",
"Kpatili": "kym",
"Kpee": "cpo",
"Kpelle": "kpe",
"Kpessi": "kef",
"Kplang": "kph",
"Krache": "kye",
"Krahô": "xra",
"Kraol": "rka",
"Krenak": "kqq",
"Kresh": "krs",
"Krevinian": "zkv",
"Kreye": "xre",
"Krikati-Timbira": "xri",
"Krim": "krm",
"Krio": "kri",
"Krisa": "ksi",
"Kristang": "mcm",
"Krobu": "kxb",
"Krongo": "kgo",
"Kru'ng": "krr",
"Krymchak": "jct",
"Kryts": "kry",
"Ktunaxa": "kut",
"Kua": "tyu",
"Kua-nsi": "ykn",
"Kuamasi": "yku",
"Kuan": "uan",
"Kuanhua": "xnh",
"Kube": "kgf",
"Kubi": "kof",
"Kubo": "jko",
"Kubu": "kvb",
"Kucong": "lkc",
"Kudiya": "kfg",
"Kudmali": "kyw",
"Kudu-Camo": "kov",
"Kugama": "kow",
"Kugbo": "kes",
"Kugu-Muminh": "xmh",
"Kui (India)": "kxu",
"Kui (Indonesia)": "kvd",
"Kuijau": "dkr",
"Kuikúro": "kui",
"Kujarge": "vkj",
"Kuk": "kfn",
"Kukatja": "kux",
"Kukele": "kez",
"Kukkuzi": "urj-kuk",
"Kukna": "kex",
"Kuku-Mangk": "xmq",
"Kuku-Mu'inh": "xmp",
"Kuku-Thaypan": "typ",
"Kuku-Ugbanh": "ugb",
"Kuku-Uwanh": "uwa",
"Kuku-Yalanji": "gvn",
"Kula": "tpg",
"Kulaal": "glj",
"Kulere": "kul",
"Kulfa": "kxj",
"Kulina": "xpk",
"Kulisusu": "vkl",
"Kullu Pahari": "kfx",
"Kulon": "uon",
"Kulung": "kle",
"Kumak": "nee",
"Kumalu": "ksl",
"Kumam": "kdi",
"Kuman": "kue",
"Kumaoni": "kfy",
"Kumarbhag Paharia": "kmj",
"Kumba": "ksm",
"Kumbainggar": "kgs",
"Kumbaran": "wkb",
"Kumbewaha": "xks",
"Kumeyaay": "nai-kum",
"Kumhali": "kra",
"Kumu": "kmw",
"Kumukio": "kuo",
"Kumyk": "kum",
"Kumzari": "zum",
"Kuna": "cuk",
"Kunama": "kun",
"Kunbarlang": "wlg",
"Kunda": "kdn",
"Kundal Shahi": "shd",
"Kunduvadi": "wku",
"Kung": "kfl",
"Kungarakany": "ggk",
"Kungardutyi": "gdt",
"Kunggari": "kgl",
"Kungkari": "lku",
"Kuni": "kse",
"Kuni-Boazi": "kvg",
"Kunigami": "xug",
"Kunimaipa": "kup",
"Kunja": "pep",
"Kunjen": "kjn",
"Kunyi": "njx",
"Kunza": "kuz",
"Kuo": "xuo",
"Kuot": "kto",
"Kupa": "kug",
"Kupang Malay": "mkn",
"Kupia": "key",
"Kupsabiny": "kpz",
"Kur": "kuv",
"Kura Ede Nago": "nqk",
"Kurama": "krh",
"Kuranko": "knk",
"Kuri": "nbn",
"Kuria": "kuj",
"Kurichiya": "kfh",
"Kurmukar": "kfv",
"Kurnai": "unn",
"Kurrama": "vku",
"Kursav": "faj",
"Kurti": "ktm",
"Kurtjar": "gdj",
"Kurtöp": "xkz",
"Kurudu": "kjr",
"Kurux": "kru",
"Kuruáya": "kyr",
"Kusaal": "kus",
"Kusaghe": "ksg",
"Kushi": "kuh",
"Kustenau": "awd-kus",
"Kusu": "ksv",
"Kusunda": "kgg",
"Kutang Ghale": "ght",
"Kutep": "kub",
"Kuthant": "xut",
"Kutto": "kpa",
"Kutu": "kdc",
"Kuturmi": "khj",
"Kuuk Thaayorre": "thd",
"Kuuk Yak": "uky",
"Kuuku-Ya'u": "kuy",
"Kuvale": "olu",
"Kuvi": "kxv",
"Kuwaa": "blh",
"Kuwaataay": "cwt",
"Kuwani": "paa-kwn",
"Kuy": "kdt",
"Kven": "fkv",
"Kw'adza": "wka",
"Kwa'": "bko",
"Kwaami": "ksq",
"Kwadi": "kwz",
"Kwaio": "kwd",
"Kwaja": "kdz",
"Kwak": "kwq",
"Kwak'wala": "kwk",
"Kwakum": "kwu",
"Kwalhioqua-Tlatskanai": "qwt",
"Kwama": "kmq",
"Kwambi": "kwm",
"Kwamera": "tnk",
"Kwami": "ktf",
"Kwamtim One": "okk",
"Kwang": "kvi",
"Kwanga": "kwj",
"Kwangali": "kwn",
"Kwanja": "knp",
"Kwanka": "bij",
"Kwanyama": "kj",
"Kwara'ae": "kwf",
"Kwasio": "nmg",
"Kwaya": "kya",
"Kwaza": "xwa",
"Kwegu": "xwg",
"Kwer": "kwr",
"Kwerba": "kwe",
"Kwerba Mamberamo": "xwr",
"Kwere": "cwe",
"Kwerisa": "kkb",
"Kwese": "kws",
"Kwesten": "kwt",
"Kwini": "gww",
"Kwinsu": "kuc",
"Kwinti": "kww",
"Kwoma": "kmo",
"Kwomtari": "kwo",
"Kyak": "bka",
"Kyaka": "kyc",
"Kyakala": "tuw-kkl",
"Kyan-Karyaw Naga": "nqq",
"Kyenele": "kql",
"Kyenga": "tye",
"Kyerung": "kgy",
"Kyrgyz": "ky",
"Kâte": "kmg",
"Kélé": "keb",
"Kómnzo": "paa-kmn",
"La'bi": "lbi",
"Laal": "gdm",
"Laalaa": "cae",
"Laba": "lau",
"Label": "lbb",
"Labir": "jku",
"Labo": "mwi",
"Labo Phowa": "ypb",
"Laboya": "lmy",
"Labu": "lbu",
"Labuk-Kinabatangan Kadazan": "dtb",
"Lacandon": "lac",
"Lachi": "lbt",
"Lachiguiri Zapotec": "zpa",
"Lachixío Zapotec": "zpl",
"Ladakhi": "lbj",
"Ladin": "lld",
"Ladino": "lad",
"Ladji-Ladji": "llj",
"Laeko-Libuat": "lkl",
"Lafofa": "laf",
"Laghu": "lgb",
"Laghuu": "lgh",
"Lagwan": "kot",
"Laha (Indonesia)": "lhh",
"Laha (Vietnam)": "lha",
"Lahanan": "lhn",
"Lahnda": "lah",
"Lahta Karen": "kvt",
"Lahu": "lhu",
"Lahu Shi": "lhi",
"Lahul Lohar": "lhl",
"Lai": "cnh",
"Laimbue": "lmx",
"Laitu Chin": "clj",
"Laiyolo": "lji",
"Lak": "lbe",
"Laka": "lak",
"Lakalei": "lka",
"Lake Miwok": "lmw",
"Lakha": "lkh",
"Laki": "lki",
"Lakkia": "lbc",
"Lakon": "lkn",
"Lakondê": "lkd",
"Lakota": "lkt",
"Lakota Dida": "dic",
"Lala (New Guinea)": "nrz",
"Lala (South Africa)": "bnt-lal",
"Lala-Bisa": "leb",
"Lala-Roba": "lla",
"Lalana Chinantec": "cnl",
"Lama Bai": "lay",
"Lamaholot": "slp",
"Lamalera": "lmr",
"Lamang": "hia",
"Lamatuka": "lmq",
"Lamba": "lam",
"Lambadi": "lmn",
"Lambichhong": "lmh",
"Lambya": "lai",
"Lame": "bma",
"Lamenu": "lmu",
"Lamet": "lbn",
"Lamja-Dengsa-Tola": "ldh",
"Lamkang": "lmk",
"Lamma": "lev",
"Lamnso'": "lns",
"Lamogai": "lmg",
"Lampung Api": "ljp",
"Lamu": "llh",
"Lamu-Lamu": "lby",
"Lanas Lobu": "ruu",
"Landoma": "ldm",
"Lang'e": "yne",
"Langbashe": "lna",
"Langi": "lag",
"Langnian Buyang": "yln",
"Lango (Sudan)": "lno",
"Lango (Uganda)": "laj",
"Lanima": "lnw",
"Lanoh": "lnh",
"Lao": "lo",
"Lao Naga": "nlq",
"Laomian": "lwm",
"Laopang": "lbg",
"Laos Sign Language": "lso",
"Lapaguía-Guivini Zapotec": "ztl",
"Lapine": "art-lap",
"Lapuyan Subanun": "laa",
"Laragia": "lrg",
"Larantuka Malay": "lrt",
"Lardil": "lbz",
"Larestani": "lrl",
"Larike-Wakasihu": "alo",
"Laro": "lro",
"Larteh": "lar",
"Laru": "lan",
"Larëvat": "lrv",
"Lasalimu": "llm",
"Lasgerdi": "lsa",
"Lashi": "lsi",
"Lasi": "lss",
"Latgalian": "ltg",
"Latin": "la",
"Latu": "ltu",
"Latundê": "ltn",
"Latvian": "lv",
"Latvian Sign Language": "lsl",
"Lau": "llu",
"Laua": "luf",
"Lauan": "llx",
"Lauje": "law",
"Laura": "lur",
"Laurentian": "lre",
"Lavatbura-Lamusong": "lbv",
"Lave": "brb",
"Laven": "lbo",
"Lavukaleve": "lvk",
"Lawangan": "lbx",
"Lawi": "lvi",
"Lawu": "lwu",
"Lawunuia": "tgi",
"Layakha": "lya",
"Laz": "lzz",
"Laze": "tbq-laz",
"Lealao Chinantec": "cle",
"Leco": "lec",
"Ledo Kaili": "lew",
"Leelau": "ldk",
"Lefa": "lfa",
"Lega-Mwenga": "lgm",
"Lega-Shabunda": "lea",
"Legbo": "agb",
"Legenyem": "lcc",
"Lehali": "tql",
"Leinong Naga": "lzn",
"Leipon": "lek",
"Leitre": "paa-lei",
"Leizhou Min": "luh",
"Lela": "dri",
"Lelak": "llk",
"Lele (Chad)": "lln",
"Lele (Congo)": "lel",
"Lele (Guinea)": "llc",
"Lele (New Guinea)": "lle",
"Lelemi": "lef",
"Lelepa": "lpa",
"Lembena": "leq",
"Lemerig": "lrz",
"Lemio": "lei",
"Lemnian": "xle",
"Lemolang": "ley",
"Lemoro": "ldj",
"Lenakel": "tnl",
"Lendu": "led",
"Lengilu": "lgi",
"Lengo": "lgr",
"Lengola": "lej",
"Lenje": "leh",
"Lenkau": "ler",
"Lenyima": "ldg",
"Leonese": "roa-leo",
"Lepcha": "lep",
"Lepki": "lpe",
"Lepontic": "xlp",
"Lere": "gnh",
"Lese": "les",
"Lesing-Gelimi": "let",
"Letemboi": "nms",
"Leti": "lti",
"Levuka": "lvu",
"Lewo": "lww",
"Lewo Eleng": "lwe",
"Lewotobi": "lwt",
"Leyigha": "ayi",
"Lezgi": "lez",
"Lhao Vo": "mhx",
"Lhokpu": "lhp",
"Li'o": "ljl",
"Liabuku": "lix",
"Liana-Seti": "ste",
"Liangmai Naga": "njn",
"Liberia Kpelle": "xpe",
"Liberian Kreyol": "lir",
"Libido": "liq",
"Libinza": "liz",
"Libon Bikol": "lbl",
"Liburnian": "xli",
"Libyan Arabic": "ayl",
"Libyan Sign Language": "lbs",
"Ligbi": "lig",
"Ligenza": "lgz",
"Ligurian": "lij",
"Lihir": "lih",
"Lika": "lik",
"Liki": "lio",
"Likila": "lie",
"Likuba": "kxx",
"Likum": "lib",
"Likwala": "kwc",
"Lilau": "lll",
"Lillooet": "lil",
"Limassa": "bme",
"Limbu": "lif",
"Limbum": "lmp",
"Limburgish": "li",
"Limi": "ylm",
"Limilngan": "lmc",
"Limos Kalinga": "kmk",
"Lindu": "klw",
"Linear A": "lab",
"Lingala": "ln",
"Lingao": "onb",
"Lingkhim": "lii",
"Lingua Franca Nova": "lfn",
"Linngithigh": "lnj",
"Lipan": "apl",
"Lipo": "lpo",
"Lisabata-Nuniali": "lcs",
"Lisela": "lcl",
"Lish": "lsh",
"Lishana Deni": "lsd",
"Lishanid Noshan": "aij",
"Lishán Didán": "trg",
"Lisu": "lis",
"Lithuanian": "lt",
"Lithuanian Sign Language": "lls",
"Little Swanport": "aus-lsw",
"Litzlitz": "lzl",
"Livonian": "liv",
"Livvi": "olo",
"Lizu": "sit-liz",
"Lo-Toga": "lht",
"Loarki": "lrk",
"Lobala": "loq",
"Lobi": "lob",
"Lodhi": "lbm",
"Logba": "lgq",
"Logo": "log",
"Logol": "lof",
"Logooli": "rag",
"Logorik": "liu",
"Lojban": "jbo",
"Lokaa": "yaz",
"Loko": "lok",
"Lokono": "arw",
"Lokoya": "lky",
"Lola": "lcd",
"Lolak": "llq",
"Lole": "llg",
"Lolo": "llb",
"Loloda": "loa",
"Lolopo": "ycl",
"Loma": "lom",
"Lomaiviti": "lmv",
"Lomakka": "loi",
"Lomavren": "rmi",
"Lombard": "lmo",
"Lombi": "lmi",
"Lombo": "loo",
"Lomwe": "ngl",
"Loncong": "lce",
"Long Phuri Naga": "lpn",
"Long Wat": "ttw",
"Longgu": "lgu",
"Longjia": "sit-lnj",
"Longto": "wok",
"Longuda": "lnu",
"Longyan Min": "nan-lnx",
"Loniu": "los",
"Lonwolwol": "crc",
"Loo": "ldo",
"Lopa": "lop",
"Lope": "yiu",
"Lopi": "lov",
"Lopit": "lpx",
"Lorang": "lrn",
"Lorediakarkar": "lnn",
"Lorrain": "roa-lor",
"Lote": "uvl",
"Lotha Naga": "njh",
"Lotud": "dtr",
"Lotuko": "lot",
"Lou": "loj",
"Louisiana Creole": "lou",
"Loun": "lox",
"Loup A": "xlo",
"Loup B": "xlb",
"Lovono": "vnk",
"Low German": "nds",
"Lower Burdekin": "xbb",
"Lower Chehalis": "cea",
"Lower Grand Valley Dani": "dni",
"Lower Mandobo": "bwp",
"Lower Nossob": "nsb",
"Lower Sorbian": "dsb",
"Lower Southern Aranda": "axl",
"Lower Ta'oih": "tto",
"Lower Tanana": "taa",
"Lowland Oaxaca Chontal": "clo",
"Lowland Tarahumara": "tac",
"Loxicha Zapotec": "ztp",
"Lozi": "loz",
"Luang": "lex",
"Luba-Kasai": "lua",
"Luba-Katanga": "lu",
"Lubila": "kcc",
"Lubu": "lcf",
"Lubuagan Kalinga": "knb",
"Luchazi": "lch",
"Lucumí": "luq",
"Ludian": "lud",
"Lufu": "ldq",
"Luganda": "lg",
"Lugbara": "lgg",
"Luguru": "ruf",
"Luhu": "lcq",
"Luhya": "luy",
"Luimbi": "lum",
"Luiseño": "lui",
"Lukpa": "dop",
"Lule": "ule",
"Lule Sami": "smj",
"Lumba-Yakkha": "luu",
"Lumbu": "lup",
"Lumun": "lmd",
"Lun Bawang": "lnd",
"Luna": "luj",
"Lunanakha": "luk",
"Lunda": "lun",
"Lungga": "lga",
"Luo": "luo",
"Luopohe Hmong": "hml",
"Luren": "sit-lrn",
"Luri (Nigeria)": "ldd",
"Lusengo": "lse",
"Lushootseed": "lut",
"Lusi": "khl",
"Lusitanian": "xls",
"Lutachoni": "lts",
"Lutos": "ndy",
"Lutuv": "clt",
"Luvale": "lue",
"Luwati": "luv",
"Luwian": "xlu",
"Luwo": "lwo",
"Luxembourgish": "lb",
"Luyana": "lyn",
"Lwalu": "lwa",
"Lwel": "lvl",
"Lycian": "xlc",
"Lydian": "xld",
"Lyngngam": "lyg",
"Lyélé": "lee",
"Láadan": "ldn",
"Láá Láá Bwamu": "bwj",
"Löyöp": "urr",
"Lü": "khb",
"Ma": "msj",
"Ma Manda": "skc",
"Ma'anyan": "mhy",
"Ma'di": "mhi",
"Ma'ya": "slz",
"Maaka": "mew",
"Maale": "mdy",
"Maasai": "mas",
"Maay": "ymm",
"Maba": "mqa",
"Mabaale": "mmz",
"Mabaan": "mfz",
"Mabaka Valley Kalinga": "kkg",
"Mabire": "muj",
"Maca": "mca",
"Macaguaje": "mcl",
"Macaguán": "mbn",
"Macanese": "mzs",
"Macau Pidgin Portuguese": "crp-mpp",
"Macedonian": "mk",
"Machame": "jmc",
"Machiguenga": "mcb",
"Machinere": "mpd",
"Machinga": "mvw",
"Macoris": "nai-mac",
"Macuna": "myy",
"Macushi": "mbc",
"Mada (Cameroon)": "mxu",
"Mada (Nigeria)": "mda",
"Madagascar Sign Language": "mzc",
"Madak": "mmx",
"Maden": "xmx",
"Madhi Madhi": "dmd",
"Madi": "grg",
"Madngele": "zml",
"Madukayang Kalinga": "kmd",
"Madurese": "mad",
"Maeng Itneg": "itt",
"Mafa": "maf",
"Mag-Anchi Ayta": "sgb",
"Mag-Indi Ayta": "blx",
"Magahat": "mtw",
"Magahi": "mag",
"Magdalena Peñasco Mixtec": "xtm",
"Magi": "gkd",
"Magiyi": "gmg",
"Magoma": "gmx",
"Magori": "zgr",
"Maguindanao": "mdh",
"Mahali": "mjx",
"Mahasu Pahari": "bfz",
"Mahican": "mjy",
"Mahongwe": "mhb",
"Mahou": "mxx",
"Maia": "sks",
"Maiadomu": "mzz",
"Maiani": "tnh",
"Maii": "mmm",
"Mailu": "mgu",
"Maindo": "cwb",
"Maipure": "awd-mpr",
"Mairasi": "zrs",
"Maisin": "mbq",
"Maithili": "mai",
"Maiwa (Indonesia)": "wmm",
"Maiwa (New Guinea)": "mti",
"Maiwala": "mum",
"Majang": "mpe",
"Majera": "xmj",
"Majhi": "mjz",
"Majhwar": "mmj",
"Mak (China)": "mkg",
"Mak (Nigeria)": "pbl",
"Makaa": "mcp",
"Makah": "myh",
"Makalero": "mjb",
"Makasae": "mkz",
"Makasar": "mak",
"Makassar Malay": "mfp",
"Makayam": "aup",
"Makhuwa": "vmw",
"Makhuwa-Marrevone": "xmc",
"Makhuwa-Meetto": "mgh",
"Makhuwa-Moniga": "mhm",
"Makhuwa-Saka": "xsq",
"Makhuwa-Shirima": "vmk",
"Maklew": "mgf",
"Makolkol": "zmh",
"Makonde": "kde",
"Maku": "xak",
"Maku'a": "lva",
"Makuri Naga": "jmn",
"Makuráp": "mpu",
"Makwe": "ymk",
"Makyan Naga": "umn",
"Mal": "mlf",
"Mal Paharia": "mkb",
"Mala (New Guinea)": "ped",
"Mala (Nigeria)": "ruy",
"Mala Malasar": "ima",
"Malaccan Creole Malay": "ccm",
"Malagasy": "mg",
"Malalamai": "mmt",
"Malalí": "sai-mal",
"Malango": "mln",
"Malankuravan": "mjo",
"Malapandaram": "mjp",
"Malaryan": "mjq",
"Malas": "mkr",
"Malasanga": "mqz",
"Malasar": "ymr",
"Malavedan": "mjr",
"Malawi Lomwe": "lon",
"Malawian Sign Language": "lws",
"Malay": "ms",
"Malayalam": "ml",
"Malayic Dayak": "xdy",
"Malaynon": "mlz",
"Malaysian Sign Language": "xml",
"Malba Birifor": "bfo",
"Male": "mdc",
"Malecite-Passamaquoddy": "pqm",
"Maleng": "pkt",
"Maleu-Kilenge": "mgl",
"Malfaxal": "mlx",
"Malgana": "vml",
"Malgbe": "mxf",
"Mali": "gcc",
"Malibu": "sai-mlb",
"Malila": "mgq",
"Malimba": "mzd",
"Malimpung": "mli",
"Malinaltepec Tlapanec": "tcf",
"Malol": "mbk",
"Maltese": "mt",
"Maltese Sign Language": "mdl",
"Malua Bay": "mll",
"Malvi": "mup",
"Maléku Jaíka": "gut",
"Mam": "mam",
"Mama": "mma",
"Mamaa": "mhf",
"Mamaindé": "wmd",
"Mamanwa": "mmn",
"Mamara": "myk",
"Mamasa": "mqj",
"Mambae": "mgm",
"Mambai": "mcs",
"Mamboru": "mvd",
"Mambwe-Lungu": "mgr",
"Mampruli": "maw",
"Mamuju": "mqx",
"Mamulique": "emm",
"Mamusi": "kdf",
"Mamvu": "mdi",
"Man Met": "mml",
"Manado Malay": "xmm",
"Manam": "mva",
"Manambu": "mle",
"Manangba": "nmm",
"Manangkari": "znk",
"Manao": "awd-man",
"Manat": "pmr",
"Manchu": "mnc",
"Manda (Australia)": "zma",
"Manda (India)": "mha",
"Manda (Tanzania)": "mgs",
"Mandahuaca": "mht",
"Mandaic": "mid",
"Mandailing Batak": "btm",
"Mandalorian": "art-man",
"Mandan": "mhq",
"Mandandanyi": "zmk",
"Mandar": "mdr",
"Mandara": "tbf",
"Mandari": "mqu",
"Mandarin": "cmn",
"Mandeali": "mjl",
"Mander": "mqr",
"Mandingo": "man",
"Mandinka": "mnk",
"Mandjak": "mfv",
"Manem": "jet",
"Mang": "zng",
"Mangala": "mem",
"Mangarayi": "mpc",
"Mangarevan": "mrv",
"Mangas": "zns",
"Mangayat": "myj",
"Mangbetu": "mdj",
"Mangbutu": "mdk",
"Mangerr": "zme",
"Mangga Buang": "mmo",
"Manggarai": "mqy",
"Mangghuer": "mjg-mgr",
"Mango": "mge",
"Mangole": "mqc",
"Mangseng": "mbh",
"Manigri-Kambolé Ede Nago": "xkb",
"Manipa": "mqp",
"Manipuri": "mni",
"Mankanya": "knf",
"Mankiyali": "nlm",
"Manna-Dora": "mju",
"Mannan": "mjv",
"Mano": "mev",
"Manombai": "woo",
"Mansaka": "msk",
"Mansoanka": "msw",
"Manta": "myg",
"Mantsi": "nty",
"Manumanaw Karen": "kxf",
"Manus Koro": "kxr",
"Manusela": "wha",
"Manx": "gv",
"Manya": "mzj",
"Manyawa": "mny",
"Manza": "mzv",
"Mao Naga": "nbi",
"Maonan": "mmd",
"Maore Comorian": "swb",
"Mape": "mlh",
"Mapena": "mnm",
"Mapia": "mpy",
"Mapidian": "mpw",
"Mapos Buang": "bzh",
"Mapoyo": "mcg",
"Mapudungun": "arn",
"Mapun": "sjm",
"Mara": "mec",
"Mara Chin": "mrh",
"Marachi": "lri",
"Maraghei": "vmh",
"Maragus": "mrs",
"Maram Naga": "nma",
"Marama": "lrm",
"Maranao": "mrw",
"Maranungku": "zmr",
"Mararit": "mgb",
"Marathi": "mr",
"Maratino": "sai-mar",
"Marau": "mvr",
"Marawan": "awd-mar",
"Marba": "mpg",
"Marenje": "vmr",
"Marfa": "mvu",
"Margany": "zmc",
"Marghi South": "mfm",
"Margi": "mrt",
"Mari (Austronesian)": "hob",
"Mari (Sepik)": "mbx",
"Maria": "mds",
"Mariaté": "awd-mrt",
"Maricopa": "mrc",
"Maridan": "zmd",
"Maridjabin": "zmj",
"Marik": "dad",
"Marimanindji": "zmm",
"Marind": "mrz",
"Maring": "mbw",
"Maring Naga": "nng",
"Maringarr": "zmt",
"Mariri": "mqi",
"Maritime Sign Language": "nsr",
"Maritsauá": "msp",
"Mariupol Greek": "grk-mar",
"Mariyedi": "zmy",
"Marka": "rkm",
"Markweeta": "enb",
"Marma": "rmz",
"Maroon Spirit Language": "crp-mar",
"Marovo": "mvo",
"Marrgu": "mhg",
"Marriammu": "xru",
"Marrithiyel": "mfr",
"Marrucinian": "umc",
"Marshallese": "mh",
"Marsian": "ims",
"Martha's Vineyard Sign Language": "mre",
"Marti Ke": "zmg",
"Martu Wangka": "mpj",
"Martuthunira": "vma",
"Marwari": "mwr",
"Marúbo": "mzr",
"Masaba": "myx",
"Masadiit Itneg": "tis",
"Masakará": "sai-msk",
"Masalit": "mls",
"Masana": "mcn",
"Masbate Sorsogon": "bks",
"Masbatenyo": "msb",
"Mashco Piro": "cuj",
"Mashi": "mho",
"Masimasi": "ism",
"Masiwang": "bnf",
"Maskelynes": "klv",
"Maslam": "msv",
"Masmaje": "mes",
"Massachusett": "wam",
"Massalat": "mdg",
"Massep": "mvs",
"Matagalpa": "mtn",
"Matal": "mfh",
"Matanawi": "sai-mat",
"Matbat": "xmt",
"Matengo": "mgv",
"Matepi": "mqe",
"Matigsalug Manobo": "mbt",
"Matipuhy": "mzo",
"Matlatzinca": "mat",
"Mato": "met",
"Mator": "mtm",
"Matsés": "mcf",
"Mattole": "mvb",
"Matukar": "mjk",
"Matumbi": "mgw",
"Matya Samo": "stj",
"Matís": "mpq",
"Maung": "mph",
"Mauritian Creole": "mfe",
"Mauritian Sign Language": "lsy",
"Mauwake": "mhl",
"Mav̋ea": "mkv",
"Mawa": "mcw",
"Mawak": "mjj",
"Mawan": "mcz",
"Mawayana": "mzx",
"Mawchi": "mke",
"Mawes": "mgk",
"Maxakalí": "mbl",
"Maxi Gbe": "mxl",
"Maya Samo": "sym",
"Mayaguduna": "xmy",
"Mayangna": "yan",
"Mayawali": "yxa",
"Maybrat": "ayz",
"Mayeka": "myc",
"Mayi-Thakurti": "xyt",
"Maykulan": "mnt",
"Maynas": "sai-mys",
"Mayo": "mfy",
"Mayogo": "mdm",
"Mayoyao Ifugao": "ifu",
"Mazagway": "dkx",
"Mazaltepec Zapotec": "zpy",
"Mazanderani": "mzn",
"Mazatlán Mazatec": "vmz",
"Mazatlán Mixe": "mzl",
"Mba": "mfc",
"Mbabaram": "vmb",
"Mbala": "mdp",
"Mbalanhu": "lnb",
"Mbandja": "zmz",
"Mbangala": "mxg",
"Mbangi": "mgn",
"Mbangwe": "zmn",
"Mbara (Australia)": "mvl",
"Mbara (Chad)": "mpk",
"Mbariman-Gudhinma": "zmv",
"Mbati": "mdn",
"Mbato": "gwa",
"Mbay": "myb",
"Mbe": "mfo",
"Mbe'": "mtk",
"Mbelime": "mql",
"Mbere": "mdt",
"Mbesa": "zms",
"Mbiywom": "aus-mbi",
"Mbo (Cameroon)": "mbo",
"Mbo (Congo)": "zmw",
"Mboi": "moi",
"Mboko": "mdu",
"Mbole": "mdq",
"Mbonga": "xmb",
"Mbongno": "bgu",
"Mbosi": "mdw",
"Mbowe": "mxo",
"Mbre": "mka",
"Mbu'": "muc",
"Mbudum": "xmd",
"Mbugu": "mhd",
"Mbugwe": "mgz",
"Mbuko": "mqb",
"Mbukushu": "mhw",
"Mbula": "mna",
"Mbula-Bwazza": "mbu",
"Mbule": "mlb",
"Mbulungish": "mbv",
"Mbum": "mdd",
"Mbunda": "mck",
"Mbunga": "mgy",
"Mburku": "bbt",
"Mbuun": "zmp",
"Mbwela": "mfu",
"Mbya Guarani": "gun",
"Me'en": "mym",
"Mebu": "mjn",
"Mecayapan Nahuatl": "nhx",
"Medebur": "mjm",
"Medefaidrin": "dmf",
"Media Lengua": "mue",
"Mednyj Aleut": "mud",
"Medumba": "byv",
"Mefele": "mfj",
"Megam": "mef",
"Megleno-Romanian": "ruq",
"Mehek": "nux",
"Mehináku": "mmh",
"Mehri": "gdq",
"Mekeo": "mek",
"Mekmek": "mvk",
"Mekwei": "msf",
"Mekéns": "skf",
"Mel-Khaonh": "hkn",
"Mele-Fila": "mxe",
"Melo": "mfx",
"Melpa": "med",
"Memoni": "mby",
"Mendalam Kayan": "xkd",
"Mendankwe-Nkwen": "mfd",
"Mende (New Guinea)": "sim",
"Mende (Sierra Leone)": "men",
"Mengaka": "xmg",
"Mengen": "mee",
"Mengisa": "leo",
"Menien": "sai-men",
"Menka": "mea",
"Menominee": "mez",
"Mentawai": "mwv",
"Menya": "mcr",
"Meoswar": "mvx",
"Mer": "mnu",
"Meramera": "mxm",
"Merei": "lmb",
"Merey": "meq",
"Meriam": "ulk",
"Meroitic": "xmr",
"Meru": "mer",
"Mesaka": "iyo",
"Mese": "mci",
"Mesme": "zim",
"Mesmes": "mys",
"Mesqan": "mvz",
"Messapic": "cms",
"Meta'": "mgo",
"Metlatónoc Mixtec": "mxv",
"Mewari": "mtr",
"Mewati": "wtm",
"Mexican Sign Language": "mfs",
"Meyah": "mej",
"Mezontla Popoloca": "pbe",
"Mezquital Otomi": "ote",
"Meänkieli": "fit",
"Mfinu": "zmf",
"Mfumte": "nfu",
"Mgbo": "gmz",
"Mi'kmaq": "mic",
"Miami": "mia",
"Mian": "mpt",
"Miani": "pla",
"Micha": "yiq",
"Michif": "crg",
"Michigamea": "cmm",
"Michoacán Mazahua": "mmc",
"Michoacán Nahuatl": "ncl",
"Mid Grand Valley Dani": "dnt",
"Mid-Southern Banda": "bjo",
"Middle Armenian": "axm",
"Middle Assamese": "inc-mas",
"Middle Bengali": "inc-mbn",
"Middle Breton": "xbm",
"Middle Chinese": "ltc",
"Middle Cornish": "cnx",
"Middle Dutch": "dum",
"Middle English": "enm",
"Middle French": "frm",
"Middle Gujarati": "inc-mgu",
"Middle High German": "gmh",
"Middle Irish": "mga",
"Middle Kannada": "dra-mkn",
"Middle Khmer": "xhm",
"Middle Korean": "okm",
"Middle Low German": "gml",
"Middle Median": "xme-mid",
"Middle Mon": "mkh-mmn",
"Middle Mongol": "xng",
"Middle Newar": "nwx",
"Middle Norwegian": "gmq-mno",
"Middle Odia": "inc-mor",
"Middle Persian": "pal",
"Middle Scots": "gmw-msc",
"Middle Vietnamese": "mkh-mvi",
"Middle Watut": "mpl",
"Middle Welsh": "wlm",
"Midob": "mei",
"Migaama": "mmy",
"Migabac": "mpp",
"Miji": "sjl",
"Miju": "mxj",
"Mikasuki": "mik",
"Milang": "tbq-mil",
"Mili": "ymh",
"Millcayac": "sai-mil",
"Miltu": "mlj",
"Miluk": "iml",
"Milyan": "imy",
"Mimi of Decorse": "mis-mmd",
"Mimi of Nachtigal": "mis-mmn",
"Min Nan": "nan",
"Mina": "hna",
"Minaean": "inm",
"Minang": "xrg",
"Minangkabau": "min",
"Minaveha": "mvn",
"Minderico": "drc",
"Mindiri": "mpn",
"Mingang Doso": "mko",
"Mingo": "iro-min",
"Mingrelian": "xmf",
"Minica Huitoto": "hto",
"Minidien": "wii",
"Minjungbal": "xjb",
"Minkin": "xxm",
"Minoan": "omn",
"Minokok": "mqq",
"Minriq": "mnq",
"Mintil": "mzt",
"Mirandese": "mwl",
"Mire": "mvh",
"Mirgan": "zrg",
"Miriti": "mmv",
"Miriwoong": "mep",
"Miriwoong Sign Language": "rsm",
"Mirning": "gmr",
"Mirpur Panjabi": "pmu",
"Misantla Totonac": "tlc",
"Miship": "mjs",
"Misima-Paneati": "mpx",
"Mising": "mrg",
"Miskito": "miq",
"Mitla Zapotec": "zaw",
"Mitlatongo Mixtec": "vmm",
"Mittu": "mwu",
"Mituku": "zmq",
"Miu": "mpo",
"Miwa": "vmi",
"Mixed Great Andamanese": "gac",
"Mixifore": "mfg",
"Mixtepec Mixtec": "mix",
"Mixtepec Zapotec": "zpm",
"Miya": "mkf",
"Miyako": "mvi",
"Miyobe": "soy",
"Mizo": "lus",
"Mlabri": "mra",
"Mlahsö": "lhs",
"Mlap": "kja",
"Mlomp": "mlo",
"Mmaala": "mmu",
"Mmani": "buy",
"Mmen": "bfm",
"Mo": "wkd",
"Mo'da": "gbn",
"Moabite": "obm",
"Moba": "mfq",
"Mobilian": "mod",
"Mobu": "ahm",
"Mocana": "sai-mcn",
"Mochi": "old",
"Mochica": "omc",
"Mocho": "mhc",
"Mocoví": "moc",
"Modang": "mxd",
"Modole": "mqo",
"Moere": "mvq",
"Mofu-Gudur": "mif",
"Mogholi": "mhj",
"Mogum": "mou",
"Mohawk": "moh",
"Mohegan-Pequot": "xpq",
"Moi (Congo)": "mow",
"Moi (Indonesia)": "mxn",
"Moikodi": "mkp",
"Moingi": "mwz",
"Mojave": "mov",
"Moji": "ymi",
"Mok": "mqt",
"Mokati": "wnb",
"Moken": "mwt",
"Mokerang": "mft",
"Mokilese": "mkj",
"Moklen": "mkm",
"Mokole": "mkl",
"Mokpwe": "bri",
"Moksha": "mdf",
"Molale": "mbe",
"Molbog": "pwm",
"Moldova Sign Language": "vsi",
"Molengue": "bxc",
"Molima": "mox",
"Molmo One": "aun",
"Molo": "zmo",
"Molof": "msl",
"Moloko": "mlw",
"Mom Jango": "ver",
"Moma": "myl",
"Momare": "msz",
"Mombo Dogon": "dmb",
"Mombum": "mso",
"Momina": "mmb",
"Momuna": "mqf",
"Mon": "mnw",
"Monastic Sign Language": "mzg",
"Mondropolon": "npn",
"Mondé": "mnd",
"Mongghul": "mjg-mgl",
"Mongo": "lol",
"Mongolian": "mn",
"Mongolian Sign Language": "msr",
"Mongondow": "mog",
"Moni": "mnz",
"Monimbo": "mom",
"Mono (California)": "mnr",
"Mono (Cameroon)": "mru",
"Mono (Congo)": "mnh",
"Monom": "moo",
"Monsang Naga": "nmh",
"Montagnais": "moe",
"Montana Salish": "fla",
"Montol": "mtl",
"Monumbo": "mxk",
"Monzombo": "moj",
"Moo": "gwg",
"Moore": "mos",
"Moose Cree": "crm",
"Mopan Maya": "mop",
"Mor (Austronesian)": "mhz",
"Mor (Papuan)": "moq",
"Moraid": "msg",
"Moran": "tbq-mor",
"Morawa": "mze",
"Morelos Nahuatl": "nhm",
"Morerebi": "xmo",
"Moresada": "msx",
"Mori Atas": "mzq",
"Mori Bawah": "xmz",
"Morigi": "mdb",
"Moriori": "rrm",
"Moro": "mor",
"Moroccan Amazigh": "zgh",
"Moroccan Arabic": "ary",
"Moroccan Sign Language": "xms",
"Morokodo": "mgc",
"Morom": "bdo",
"Moronene": "mqn",
"Morori": "mok",
"Morouas": "mrp",
"Mortlockese": "mrl",
"Moru": "mgd",
"Mosimo": "mqv",
"Moskona": "mtj",
"Mota": "mtt",
"Motembo": "tmv",
"Motu": "meu",
"Mouk-Aria": "mwh",
"Mount Iraya Agta": "atl",
"Mount Iriga Agta": "agz",
"Mountain Koiari": "kpx",
"Mouwase": "jmw",
"Movima": "mzp",
"Moyadan Itneg": "ity",
"Moyon Naga": "nmo",
"Mozambican Sign Language": "mzy",
"Mozarabic": "mxi",
"Mpade": "mpi",
"Mpalitjanh": "xpj",
"Mpi": "mpz",
"Mpiemo": "mcx",
"Mpiin": "bnt-mpi",
"Mpinda": "pnd",
"Mpongmpong": "mgg",
"Mpoto": "mpa",
"Mpotovoro": "mvt",
"Mpuono": "bnt-mpu",
"Mpur": "akc",
"Mro Chin": "cmr",
"Mru": "mro",
"Mser": "kqx",
"Muak Sa-aak": "ukk",
"Mualang": "mtd",
"Mubami": "tsx",
"Mubi": "mub",
"Mucuchí": "sai-muc",
"Muda": "ymd",
"Mudburra": "dmw",
"Mudu Koraga": "vmd",
"Muduapa": "wiv",
"Muduga": "udg",
"Muellama": "sai-mue",
"Mufian": "aoj",
"Muher": "sem-mhr",
"Muinane": "bmr",
"Mukha-Dora": "mmk",
"Mukulu": "moz",
"Mulaha": "mfw",
"Mulam": "mlm",
"Mulao": "giu",
"Mullu Kurumba": "kpb",
"Mullukmulluk": "mpb",
"Muluridyi": "vmu",
"Mum": "kqa",
"Mumuye": "mzm",
"Muna": "mnb",
"Munda": "unx",
"Mundabli": "boe",
"Mundang": "mua",
"Mundani": "mnf",
"Mundari": "unr",
"Mundat": "mmf",
"Mundolinco": "art-mun",
"Mundurukú": "myu",
"Mungaka": "mhk",
"Mungbam": "mij",
"Munggui": "mth",
"Mungkip": "mpv",
"Muniche": "myr",
"Munit": "mtc",
"Munji": "mnj",
"Munsee": "umu",
"Muong": "mtq",
"Mur Pano": "tkv",
"Muratayak": "asx",
"Murik (Malaysia)": "mxr",
"Murik (New Guinea)": "mtf",
"Murkim": "rmh",
"Murle": "mur",
"Murrinh-Patha": "mwf",
"Mursi": "muz",
"Murui Huitoto": "huu",
"Murupi": "mqw",
"Muruwari": "zmu",
"Musan": "mmp",
"Musasa": "smm",
"Musey": "mse",
"Musgu": "mug",
"Musi": "mui",
"Muskum": "mje",
"Musom": "msu",
"Mussau-Emira": "emi",
"Muthuvan": "muv",
"Mutu": "tuc",
"Muya": "mvm",
"Muyang": "muy",
"Muyuw": "myw",
"Muzi": "ymz",
"Muzo": "sai-muz",
"Mvanip": "mcj",
"Mvuba": "mxh",
"Mwaghavul": "sur",
"Mwakai": "mgt",
"Mwali Comorian": "wlc",
"Mwan": "moa",
"Mwani": "wmw",
"Mwatebu": "mwa",
"Mwera": "mwe",
"Mwerlap": "mrm",
"Mwimbi-Muthambi": "mws",
"Mwotlap": "mlv",
"Mycenaean Greek": "gmy",
"Myene": "mye",
"Mysian": "yms",
"Mzieme Naga": "nme",
"Mághdì": "gmd",
"Mòcheno": "mhn",
"Mün Chin": "mwq",
"Mündü": "muh",
"Māori": "mi",
"Mạ": "cma",
"N'Ko": "nqo",
"Na": "nbt",
"Naaba": "nao",
"Naba": "mne",
"Nabak": "naf",
"Nabi": "mty",
"Nachering": "ncd",
"Nadruvian": "ndf",
"Nadëb": "mbj",
"Nafaanra": "nfr",
"Nafi": "srf",
"Nafri": "nxx",
"Naga Pidgin": "nag",
"Nagarchal": "nbg",
"Nage": "nxe",
"Nagtipunan Agta": "phi-nag",
"Nagu": "ngr",
"Nagumi": "ngv",
"Nahali": "nlx",
"Nahari": "nhh",
"Nahavaq": "sns",
"Nahuatl": "nah",
"Nai": "bio",
"Najdi Arabic": "ars",
"Naka'ela": "nae",
"Nakai": "nkj",
"Nakame": "nib",
"Nakanai": "nak",
"Nakara": "nck",
"Nake": "nbk",
"Naki": "mff",
"Nakwi": "nax",
"Nalca": "nlc",
"Nali": "nss",
"Nalik": "nal",
"Nalu": "naj",
"Nalögo": "nlz",
"Nama": "nmx",
"Namakura": "nmk",
"Namat": "nkm",
"Nambikwara": "nab",
"Nambo": "ncm",
"Nambya": "nmq",
"Namia": "nnm",
"Namiae": "nvm",
"Namibian Sign Language": "nbs",
"Namla": "naa",
"Namo": "mxw",
"Namonuito": "nmt",
"Namosi-Naitasiri-Serua": "bwb",
"Namuyi": "nmy",
"Nanai": "gld",
"Nancere": "nnc",
"Nande": "nnb",
"Nandi": "niq",
"Nanerige": "sen",
"Nanga Dama Dogon": "nzz",
"Nankina": "nnk",
"Nanti": "cox",
"Nanticoke": "nnt",
"Nanubae": "afk",
"Naolan": "nai-nao",
"Napu": "npy",
"Nar Phu": "npa",
"Nara": "nrb",
"Narak": "nac",
"Narango": "nrg",
"Narim": "loh",
"Naro": "nhr",
"Narom": "nrm",
"Narragansett": "xnt",
"Narua": "nru",
"Narungga": "nnr",
"Naruo": "ylo",
"Nasal": "nsy",
"Nasarian": "nvh",
"Nasioi": "nas",
"Naskapi": "nsk",
"Nasu": "ywq",
"Natagaimas": "nts",
"Natchez": "ncz",
"Nateni": "ntm",
"Natioro": "nti",
"Natú": "sai-nat",
"Natügu": "ntu",
"Nauete": "nxa",
"Naukanski": "ynk",
"Nauna": "ncn",
"Nauo": "nwo",
"Nauruan": "na",
"Navajo": "nv",
"Navut": "nsw",
"Nawaru": "nwr",
"Nawathinehena": "nwa",
"Nawdm": "nmz",
"Nawuri": "naw",
"Naxi": "nxq",
"Nayi": "noz",
"Naʼvi": "art-nav",
"Ncane": "ncr",
"Nchumbulu": "nlu",
"Nda'nda'": "nnz",
"Ndai": "gke",
"Ndaka": "ndk",
"Ndali": "ndh",
"Ndam": "ndm",
"Ndamba": "ndj",
"Ndambomo": "nxo",
"Ndasa": "nda",
"Ndau": "ndc",
"Nde-Gbite": "ned",
"Nde-Nsele-Nta": "ndd",
"Ndemli": "nml",
"Ndendeule": "dne",
"Ndengereko": "ndg",
"Nding": "eli",
"Ndjébbana": "djj",
"Ndo": "ndp",
"Ndobo": "ndw",
"Ndoe": "nbb",
"Ndogo": "ndz",
"Ndolo": "ndl",
"Ndom": "nqm",
"Ndombe": "ndq",
"Ndonga": "ng",
"Ndoola": "ndr",
"Ndrulo": "dno",
"Nduga": "ndx",
"Ndumu": "nmd",
"Ndunda": "nuh",
"Ndunga": "ndt",
"Ndut": "ndv",
"Ndyuka-Trio Pidgin": "njt",
"Ndzwani Comorian": "wni",
"Neapolitan": "nap",
"Nedebang": "nec",
"Nefamese": "nef",
"Nefusa": "jbn",
"Negerhollands": "dcr",
"Negeri Sembilan Malay": "zmi",
"Negidal": "neg",
"Nehan": "nsn",
"Nek": "nif",
"Nekgini": "nkg",
"Neko": "nej",
"Neku": "nek",
"Neme": "nex",
"Nemi": "nem",
"Nen": "nqn",
"Nend": "anh",
"Nengone": "nen",
"Neo": "neu",
"Nepalese Sign Language": "nsp",
"Nepali": "ne",
"Nete": "net",
"Neve'ei": "vnm",
"Neverver": "lgk",
"New Caledonian Javanese": "jas",
"New River Shasta": "nai-nrs",
"New Zealand Sign Language": "nzs",
"Newar": "new",
"Neyo": "ney",
"Nez Perce": "nez",
"Nga La": "hlt",
"Ngaanyatjarra": "ntj",
"Ngadha": "nxg",
"Ngadjunmaya": "nju",
"Ngadjuri": "jui",
"Ngaiawang": "nwg",
"Ngaing": "nnf",
"Ngaju": "nij",
"Ngala": "nud",
"Ngalakan": "nig",
"Ngalkbun": "ngk",
"Ngalum": "szb",
"Ngam": "nmc",
"Ngamambo": "nbv",
"Ngambay": "sba",
"Ngamini": "nmv",
"Ngamo": "nbh",
"Ngan'gityemerri": "nam",
"Nganakarti": "xnk",
"Nganasan": "nio",
"Ngandi": "nid",
"Ngando (Central African Republic)": "ngd",
"Ngando (Congo)": "nxd",
"Ngandyera": "nne",
"Ngangam": "gng",
"Ngantangarra": "ntg",
"Nganyaywana": "nyx",
"Ngardi": "rxd",
"Ngarigu": "xni",
"Ngarinman": "nbj",
"Ngarinyin": "ung",
"Ngarla": "nrk",
"Ngarluma": "nrl",
"Ngarrindjeri": "nay",
"Ngas": "anc",
"Ngasa": "nsg",
"Ngatik Men's Creole": "ngm",
"Ngawn Chin": "cnw",
"Ngawun": "nxn",
"Ngazidja Comorian": "zdj",
"Ngbaka": "nga",
"Ngbaka Ma'bo": "nbm",
"Ngbaka Manza": "ngg",
"Ngbee": "jgb",
"Ngbinda": "nbd",
"Ngbundu": "nuu",
"Ngelima": "agh",
"Ngemba": "nge",
"Ngen of Djonkro": "gnj",
"Ngendelengo": "nql",
"Ngeq": "ngt",
"Ngete": "nnn",
"Nggem": "nbq",
"Nggwahyi": "ngx",
"Ngie": "ngj",
"Ngiemboon": "nnh",
"Ngile": "jle",
"Ngindo": "nnq",
"Ngiti": "niy",
"Ngiyambaa": "wyb",
"Ngizim": "ngi",
"Ngkoth": "aus-ngk",
"Ngkâlmpw Kanum": "kcd",
"Ngochang": "tbq-ngo",
"Ngom": "nra",
"Ngomba": "jgo",
"Ngombale": "nla",
"Ngombe (Central African Republic)": "nmj",
"Ngombe (Congo)": "ngc",
"Ngong": "nnx",
"Ngongo": "noq",
"Ngoni": "ngo",
"Ngoreme": "ngq",
"Ngoshie": "nsh",
"Ngul": "nlo",
"Ngulu": "ngp",
"Nguluwan": "nuw",
"Ngumbi": "nui",
"Ngunawal": "xul",
"Ngundi": "ndn",
"Ngundu": "nue",
"Ngungwel": "ngz",
"Ngurmbur": "nrx",
"Nguôn": "nuo",
"Ngwaba": "ngw",
"Ngwe": "nwe",
"Ngwo": "ngn",
"Ngäbere": "gym",
"Nhanda": "nha",
"Nheengatu": "yrl",
"Nhirrpi": "hrp",
"Nhuwala": "nhf",
"Nias": "nia",
"Nicaraguan Creole": "bzk",
"Nicaraguan Sign Language": "ncs",
"Nicola": "ath-nic",
"Niellim": "nie",
"Nigeria Mambila": "mzk",
"Nigerian Pidgin": "pcm",
"Nigerian Sign Language": "nsi",
"Nihali": "nll",
"Nii": "nii",
"Niksek": "gbe",
"Nila": "nil",
"Nilamba": "nim",
"Nimadi": "noe",
"Nimanbur": "nmp",
"Nimbari": "nmr",
"Nimboran": "nir",
"Nimi": "nis",
"Nimo": "niw",
"Nimoa": "nmw",
"Ninam": "shb",
"Nindi": "nxi",
"Ningera": "nby",
"Ninggerum": "nxr",
"Ningil": "niz",
"Ninia Yali": "nlk",
"Ninzo": "nin",
"Nipsan": "nps",
"Nisa": "njs",
"Nisenan": "nsz",
"Nisga'a": "ncg",
"Nisi": "yso",
"Niuafo'ou": "num",
"Niuatoputapu": "nkp",
"Niuean": "niu",
"Nivaclé": "cag",
"Nivkh": "niv",
"Niwer Mil": "hrc",
"Niya Prakrit": "pra-niy",
"Njalgulgule": "njl",
"Njebi": "nzb",
"Njen": "njj",
"Njerep": "njr",
"Njyem": "njy",
"Nkami": "nkq",
"Nkangala": "nkn",
"Nkari": "nkz",
"Nkem-Nkum": "isi",
"Nkhumbi": "khu",
"Nkongho": "nkc",
"Nkonya": "nko",
"Nkoroo": "nkx",
"Nkoya": "nka",
"Nkukoli": "nbo",
"Nkutu": "nkw",
"Nnam": "nbp",
"Noakhali": "oak",
"Nobiin": "fia",
"Nobonob": "gaw",
"Nocamán": "nom",
"Nocte": "njb",
"Nogai": "nog",
"Noipä": "npx",
"Noiri": "noi",
"Nokuku": "nkk",
"Nomaande": "lem",
"Nomane": "nof",
"Nomatsiguenga": "not",
"Nomlaki": "nol",
"Nomu": "noh",
"Nong Zhuang": "zhn",
"Nonuya": "noj",
"Nooksack": "nok",
"Noon": "snf",
"Noone": "nhu",
"Nootka": "nuk",
"Nopala Chatino": "cya",
"Noric": "nrc",
"Norman": "nrf",
"Norn": "nrn",
"Norra": "nrr",
"North Ambrym": "mmg",
"North Asmat": "nks",
"North Awyu": "yir",
"North Babar": "bcd",
"North Boma": "boh",
"North Central Mixe": "neq",
"North Efate": "llp",
"North Fali": "fll",
"North Frisian": "frr",
"North Giziga": "gis",
"North Levantine Arabic": "apc",
"North Marquesan": "mrq",
"North Mesopotamian Arabic": "ayp",
"North Mofu": "mfk",
"North Moluccan Malay": "max",
"North Muyu": "kti",
"North Nuaulu": "nni",
"North Picene": "nrp",
"North Slavey": "scs",
"North Tairora": "tbg",
"North Tanna": "tnn",
"North Tukang Besi": "khc",
"North Wahgi": "whg",
"North Watut": "una",
"Northeast Kiwai": "kiw",
"Northeast Maidu": "nmu",
"Northeast Malakula": "upv",
"Northeast Pashayi": "aee",
"Northeastern Dinka": "dip",
"Northeastern Pomo": "pef",
"Northern Alta": "aqn",
"Northern Altai": "atv",
"Northern Amami Ōshima": "ryn",
"Northern Bontoc": "rbk",
"Northern Catanduanes Bikol": "cts",
"Northern Dagara": "dgi",
"Northern East Cree": "crl",
"Northern Emberá": "emp",
"Northern Ghale": "ghh",
"Northern Grebo": "gbo",
"Northern Guiyang Hmong": "huj",
"Northern Haida": "hdn",
"Northern Hindko": "hno",
"Northern Huishui Hmong": "hmi",
"Northern Kalapuya": "nrt",
"Northern Kam": "doc",
"Northern Khanty": "kca-nor",
"Northern Khmer": "kxm",
"Northern Kissi": "kqs",
"Northern Kurdish": "kmr",
"Northern Lorung": "lbr",
"Northern Luri": "lrc",
"Northern Mansi": "mns-nor",
"Northern Mashan Hmong": "hmp",
"Northern Min": "mnp",
"Northern Muji": "ymx",
"Northern Ndebele": "nd",
"Northern Ngbandi": "ngb",
"Northern Nisu": "yiv",
"Northern Nuni": "nuv",
"Northern Oaxaca Nahuatl": "nhy",
"Northern Ohlone": "cst",
"Northern One": "onr",
"Northern Paiute": "pao",
"Northern Pame": "pmq",
"Northern Pinghua": "cnp",
"Northern Pomo": "pej",
"Northern Puebla Nahuatl": "ncj",
"Northern Pumi": "pmi",
"Northern Pwo": "pww",
"Northern Qiandong Miao": "hea",
"Northern Qiang": "cng",
"Northern Rengma Naga": "nnl",
"Northern Roglai": "rog",
"Northern Saharan Berber": "mzb",
"Northern Sami": "se",
"Northern Selkup": "sel-nor",
"Northern Sierra Miwok": "nsq",
"Northern Sotho": "nso",
"Northern Subanen": "stb",
"Northern Tarahumara": "thh",
"Northern Tepehuan": "ntp",
"Northern Thai": "nod",
"Northern Tidung": "ntd",
"Northern Tlaxiaco Mixtec": "xtn",
"Northern Toussian": "tsp",
"Northern Tujia": "tji",
"Northern Tutchone": "ttm",
"Northern Valley Yokuts": "yok-nvy",
"Northern Yukaghir": "ykg",
"Northwest Gbaya": "gya",
"Northwest Maidu": "mjd",
"Northwest Oaxaca Mixtec": "mxa",
"Northwest Pashayi": "glh",
"Northwestern Dinka": "diw",
"Northwestern Fars": "faz",
"Northwestern Ojibwa": "ojb",
"Norwegian": "no",
"Norwegian Bokmål": "nb",
"Norwegian Nynorsk": "nn",
"Norwegian Sign Language": "nsl",
"Notre": "bly",
"Notsi": "ncf",
"Nottoway": "ntw",
"Nottoway-Meherrin": "nwy",
"Novial": "nov",
"Noy": "noy",
"Nsari": "asj",
"Nsenga": "nse",
"Nshi": "nsc",
"Nsong": "soo",
"Nsongo": "nsx",
"Ntcham": "bud",
"Ntomba": "nto",
"Ntra'ngith": "dgt",
"Nubaca": "baf",
"Nubi": "kcn",
"Nuer": "nus",
"Nuguria": "nur",
"Nuk": "noc",
"Nukak Makú": "mbr",
"Nukna": "klt",
"Nukuini": "nuc",
"Nukumanu": "nuq",
"Nukunu": "nnv",
"Nukunul": "xnu",
"Nukuoro": "nkr",
"Numana": "nbr",
"Numanggang": "nop",
"Numbami": "sij",
"Nume": "tgs",
"Numidian": "nxm",
"Numèè": "kdk",
"Nungali": "nug",
"Nunggubuyu": "nuy",
"Nungon": "paa-nun",
"Nungu": "rin",
"Nuosu": "ii",
"Nupbikha": "npb",
"Nupe": "nup",
"Nusa Laut": "nul",
"Nusu": "nuf",
"Nutabe": "cba-nut",
"Nyabwa": "nwb",
"Nyagrong Minyag": "ero-nya",
"Nyah Kur": "cbn",
"Nyaheun": "nev",
"Nyakyusa": "nyy",
"Nyali": "nlj",
"Nyam": "nmi",
"Nyamal": "nly",
"Nyambo": "now",
"Nyamusa-Molo": "nwm",
"Nyamwanga": "mwn",
"Nyamwezi": "nym",
"Nyaneka": "nyk",
"Nyang'i": "nyp",
"Nyanga (Congo)": "nyj",
"Nyanga (Togo)": "ayg",
"Nyanga-li": "nyc",
"Nyangatom": "nnj",
"Nyangbo": "nyb",
"Nyangga": "nny",
"Nyangumarta": "nna",
"Nyankole": "nyn",
"Nyarafolo": "sev",
"Nyaturu": "rim",
"Nyaw": "nyw",
"Nyawaygi": "nyt",
"Nyelâyu": "yly",
"Nyemba": "nba",
"Nyengo": "nye",
"Nyenkha": "neh",
"Nyeu": "nyl",
"Nyigina": "nyh",
"Nyiha": "nih",
"Nyika": "nkt",
"Nyimang": "nyi",
"Nyindrou": "lid",
"Nyindu": "nyg",
"Nyishi": "njz",
"Nyiyaparli": "xny",
"Nyokon": "nvo",
"Nyole (Kenya)": "nyd",
"Nyole (Uganda)": "nuj",
"Nyong": "muo",
"Nyoro": "nyo",
"Nyulnyul": "nyv",
"Nyunga": "nys",
"Nyungwe": "nyu",
"Nzadi": "nzd",
"Nzakambay": "nzy",
"Nzakara": "nzk",
"Nzanyi": "nja",
"Nzima": "nzi",
"Ná-Meo": "neo",
"Nùng": "nut",
"Nüpode Huitoto": "hux",
"Nǀuu": "ngh",
"O'chi'chi'": "xoc",
"O'du": "tyh",
"O'odham": "ood",
"Obanliku": "bzy",
"Obispeño": "obi",
"Oblo": "obl",
"Obo Manobo": "obo",
"Obokuitai": "afz",
"Obolo": "ann",
"Obulom": "obu",
"Ocaina": "oca",
"Occitan": "oc",
"Ocotepec Mixtec": "mie",
"Ocotlán Zapotec": "zac",
"Od": "odk",
"Odia": "or",
"Odiai": "bhf",
"Odoodee": "kkc",
"Odual": "odu",
"Odut": "oda",
"Ofayé": "opy",
"Ofo": "ofo",
"Ogbah": "ogc",
"Ogbia": "ogb",
"Ogbogolo": "ogg",
"Ogbronuagum": "ogu",
"Ogea": "eri",
"Oirata": "oia",
"Ojibwe": "oj",
"Ojitlán Chinantec": "chj",
"Okanagan": "oka",
"Okiek": "oki",
"Okinawan": "ryu",
"Okinoerabu": "okn",
"Oko-Eni-Osayen": "oks",
"Oko-Juwoi": "okj",
"Okobo": "okb",
"Okodia": "okd",
"Okolod": "kqv",
"Okpamheri": "opa",
"Okpe (Northwestern Edo)": "okx",
"Okpe (Southwestern Edo)": "oke",
"Okpela": "atg",
"Oksapmin": "opm",
"Oku": "oku",
"Okwanuchu": "nai-okw",
"Old Anatolian Turkish": "trk-oat",
"Old Armenian": "xcl",
"Old Avar": "oav",
"Old Awadhi": "inc-oaw",
"Old Bengali": "inc-obn",
"Old Breton": "obt",
"Old Burmese": "obr",
"Old Catalan": "roa-oca",
"Old Chinese": "och",
"Old Church Slavonic": "cu",
"Old Cornish": "oco",
"Old Czech": "zlw-ocs",
"Old Danish": "gmq-oda",
"Old Dutch": "odt",
"Old East Slavic": "orv",
"Old English": "ang",
"Old French": "fro",
"Old Frisian": "ofs",
"Old Galician-Portuguese": "roa-opt",
"Old Georgian": "oge",
"Old Gujarati": "inc-ogu",
"Old Gutnish": "gmq-ogt",
"Old High German": "goh",
"Old Hindi": "inc-ohi",
"Old Hungarian": "ohu",
"Old Irish": "sga",
"Old Japanese": "ojp",
"Old Javanese": "kaw",
"Old Kannada": "dra-okn",
"Old Kentish Sign Language": "okl",
"Old Khmer": "okz",
"Old Komi": "urj-koo",
"Old Korean": "oko",
"Old Leonese": "roa-ole",
"Old Lithuanian": "olt",
"Old Manipuri": "omp",
"Old Marathi": "omr",
"Old Median": "xme-old",
"Old Mon": "omx",
"Old Navarro-Aragonese": "roa-ona",
"Old Norse": "non",
"Old Novgorodian": "zle-ono",
"Old Nubian": "onw",
"Old Occitan": "pro",
"Old Odia": "inc-oor",
"Old Persian": "peo",
"Old Polish": "zlw-opl",
"Old Prussian": "prg",
"Old Punjabi": "inc-opa",
"Old Ruthenian": "zle-ort",
"Old Saxon": "osx",
"Old Slovak": "zlw-osk",
"Old South Arabian": "sem-srb",
"Old Spanish": "osp",
"Old Sundanese": "osn",
"Old Swedish": "gmq-osw",
"Old Tamil": "oty",
"Old Tati": "xme-ott",
"Old Telugu": "dra-ote",
"Old Tibetan": "otb",
"Old Tupi": "tpw",
"Old Turkic": "otk",
"Old Uyghur": "oui",
"Old Welsh": "owl",
"Olekha": "ole",
"Ollari": "gdb",
"Olo": "ong",
"Oloma": "olm",
"Olrat": "olr",
"Olu'bo": "lul",
"Olukumi": "ulb",
"Olulumo-Ikom": "iko",
"Oluta Popoluca": "plo",
"Olutsotso": "lto",
"Omagua": "omg",
"Omaha-Ponca": "oma",
"Omani Arabic": "acx",
"Omba": "omb",
"Ombamba": "mbm",
"Ombo": "oml",
"Ometepec Nahuatl": "nht",
"Omi": "omi",
"Omok": "omk",
"Omotik": "omt",
"Omurano": "omu",
"Oneida": "one",
"Ong": "oog",
"Ongota": "bxe",
"Onin": "oni",
"Onjob": "onj",
"Ono": "ons",
"Onobasulu": "onn",
"Onondaga": "ono",
"Ontong Java": "ojv",
"Oorlams": "oor",
"Opao": "opo",
"Opata": "opt",
"Opuuo": "lgn",
"Opón": "sai-opo",
"Oraon Sadri": "sdr",
"Orejón": "ore",
"Oring": "org",
"Orizaba Nahuatl": "nlv",
"Orléanais": "roa-orl",
"Ormu": "orz",
"Ormuri": "oru",
"Oro": "orx",
"Oro Win": "orw",
"Oroch": "oac",
"Oroha": "ora",
"Orok": "oaa",
"Orokaiva": "okv",
"Oroko": "bdu",
"Orokolo": "oro",
"Oromo": "om",
"Oroqen": "orh",
"Orowe": "bpk",
"Oruma": "orr",
"Orya": "ury",
"Osage": "osa",
"Osamayi": "syx",
"Osatu": "ost",
"Oscan": "osc",
"Osing": "osi",
"Ososo": "oso",
"Ossetian": "os",
"Ot Danum": "otd",
"Otank": "uta",
"Oti": "oti",
"Otomaco": "sai-oto",
"Otoro": "otr",
"Ottawa": "otw",
"Ottoman Turkish": "ota",
"Otuke": "otu",
"Ouma": "oum",
"Oune": "oue",
"Owa": "stn",
"Owenia": "wsr",
"Owiniga": "owi",
"Oy": "oyb",
"Oya'oya": "oyy",
"Oyda": "oyd",
"Ozolotepec Zapotec": "zao",
"Ozumacín Chinantec": "chz",
"Pa": "ppt",
"Pa Di": "pdi",
"Pa'a": "pqa",
"Pa'o Karen": "blk",
"Pa-Hng": "pha",
"Paamese": "pma",
"Paasaal": "sig",
"Pacahuara": "pcp",
"Pacoh": "pac",
"Padoe": "pdo",
"Paelignian": "pgn",
"Paeonian": "ine-pae",
"Pagi": "pgi",
"Pagibete": "pae",
"Pagu": "pgu",
"Pahanan Agta": "apf",
"Pahari-Potwari": "phr",
"Pahi": "lgt",
"Pahlavani": "phv",
"Pai Tavytera": "pta",
"Paicî": "pri",
"Paikoneka": "awd-pai",
"Paipai": "ppi",
"Paite": "pck",
"Paiwan": "pwn",
"Pajapan Nahuatl": "nhp",
"Pajonal Ashéninka": "cjo",
"Pak-Tong": "pkg",
"Pakanha": "pkn",
"Pakistan Sign Language": "pks",
"Paku": "pku",
"Paku Karen": "jkp",
"Pal": "abw",
"Palaic": "plq",
"Palaka": "plr",
"Palantla Chinantec": "cpa",
"Palauan": "pau",
"Palawan Batak": "bya",
"Paleni": "pnl",
"Palenquero": "pln",
"Palewyami Yokuts": "yok-ply",
"Pali": "pi",
"Palikur": "plu",
"Paliyan": "pcf",
"Pallanganmiddang": "pmd",
"Palor": "fap",
"Palta": "sai-pal",
"Palu'e": "ple",
"Paluan": "plz",
"Palula": "phl",
"Palya Bareli": "bpx",
"Pam": "pmn",
"Pambia": "pmb",
"Pamigua": "sai-pam",
"Pamlico": "pmk",
"Pamona": "pmf",
"Pamosu": "hih",
"Pamplona Atta": "att",
"Pana (Central Africa)": "pnz",
"Pana (West Africa)": "pnq",
"Panamanian Sign Language": "lsp",
"Panamint": "par",
"Panare": "pbh",
"Panará": "kre",
"Panasuan": "psn",
"Panawa": "pwb",
"Pancana": "pnp",
"Panchpargania": "tdb",
"Pande": "bkj",
"Pangasinan": "pag",
"Pangseng": "pgs",
"Pangutaran Sama": "slm",
"Pangwa": "pbr",
"Pangwali": "pgg",
"Panim": "pnr",
"Paniya": "pcg",
"Pankararé": "pax",
"Pankararú": "paz",
"Pankhu": "pkh",
"Pannei": "pnc",
"Pannonian Rusyn": "rsk",
"Panobo": "pno",
"Panyi Bai": "bfc",
"Panyjima": "pnw",
"Panzaleo": "sai-pnz",
"Pao": "ppa",
"Papantla Totonac": "top",
"Papapana": "ppn",
"Papar": "dpp",
"Papasena": "pas",
"Papel": "pbo",
"Papi": "ppe",
"Papiamentu": "pap",
"Papora": "ppu",
"Papua New Guinean Sign Language": "pgz",
"Papuan Malay": "pmy",
"Papuma": "ppm",
"Para Naga": "pzn",
"Parachi": "prc",
"Paraguayan Guarani": "gug",
"Paraguayan Sign Language": "pys",
"Parakanã": "pak",
"Paranan": "prf",
"Paranawát": "paf",
"Paratió": "sai-par",
"Paraujano": "pbg",
"Parauk": "prk",
"Parawen": "prw",
"Pardhan": "pch",
"Pardhi": "pcl",
"Pare": "asa",
"Pareci": "pab",
"Paredarerme": "xpd",
"Parenga": "pcj",
"Parkari Koli": "kvx",
"Parthian": "xpr",
"Parya": "paq",
"Pará Gavião": "gvp",
"Pashto": "ps",
"Pasi": "psq",
"Pass Valley Yali": "yac",
"Pasé": "awd-pas",
"Patagón": "sai-ptg",
"Patamona": "pbc",
"Patani": "ptn",
"Pataxó Hã-Ha-Hãe": "pth",
"Patep": "ptp",
"Pathiya": "pty",
"Patpatar": "gfk",
"Pattani": "lae",
"Pattani Malay": "mfa",
"Pattapu": "ptq",
"Patwin": "pwi",
"Paulohi": "plh",
"Paumarí": "pad",
"Paunaka": "pnk",
"Pauri Bareli": "bfb",
"Pauserna": "psm",
"Pawaia": "pwa",
"Pawnee": "paw",
"Payaguá": "sai-pyg",
"Pazeh": "pzh",
"Pe": "pai",
"Pear": "pcb",
"Peba": "sai-peb",
"Pech": "pay",
"Pecheneg": "xpc",
"Peerapper": "xpw",
"Peere": "pfe",
"Pei": "ppq",
"Pekal": "pel",
"Pela": "bxd",
"Pele-Ata": "ata",
"Pemon": "aoc",
"Penang Sign Language": "psg",
"Penchal": "pek",
"Pendau": "ums",
"Pengo": "peg",
"Pennsylvania German": "pdc",
"Penobscot": "aaq",
"Penrhyn": "pnh",
"Pentlatch": "ptw",
"Perai": "wet",
"Peranakan Indonesian": "pea",
"Perema": "wom",
"Perené Ashéninka": "prq",
"Pericú": "nai-per",
"Pero": "pip",
"Persian": "fa",
"Persian Sign Language": "psc",
"Peruvian Sign Language": "prl",
"Petapa Zapotec": "zpe",
"Petats": "pex",
"Petjo": "pey",
"Peñoles Mixtec": "mil",
"Phai": "prt",
"Phake": "phk",
"Phala": "ypa",
"Phana'": "phq",
"Phangduwali": "phw",
"Phende": "pem",
"Philippine Sign Language": "psp",
"Philistine": "mis-phi",
"Phimbi": "phm",
"Phoenician": "phn",
"Phola": "ypg",
"Pholo": "yip",
"Phom": "nph",
"Phong-Kniang": "pnx",
"Phrae Pwo": "kjt",
"Phrygian": "xpg",
"Phu Thai": "pht",
"Phuan": "phu",
"Phudagi": "phd",
"Phuie": "pug",
"Phukha": "phh",
"Phuma": "ypm",
"Phunoi": "pho",
"Phuong": "phg",
"Phupa": "ypp",
"Phupha": "yph",
"Phuthi": "bnt-phu",
"Phuza": "ypz",
"Piamatsina": "ptr",
"Piame": "pin",
"Piapoco": "pio",
"Piaroa": "pid",
"Picard": "pcd",
"Pichinglis": "fpe",
"Pichis Ashéninka": "cpu",
"Pictish": "xpi",
"Picuris": "nai-pic",
"Pidgin Delaware": "dep",
"Pidgin Iha": "ihb",
"Pidgin Onin": "onx",
"Piedmontese": "pms",
"Pijao": "pij",
"Pije": "piz",
"Pijin": "pis",
"Pilagá": "plg",
"Pileni": "piv",
"Pima Bajo": "pia",
"Pimbwe": "piw",
"Pinai-Hagahai": "pnn",
"Pingelapese": "pif",
"Pini": "pii",
"Pinigura": "pnv",
"Pinjarup": "pnj",
"Pinji": "pic",
"Pinotepa Nacional Mixtec": "mio",
"Pintiini": "pti",
"Pintupi-Luritja": "piu",
"Pinyin": "pny",
"Pipil": "ppl",
"Pirahã": "myp",
"Piratapuyo": "pir",
"Pirlatapa": "bxi",
"Piro": "pie",
"Pirriya": "xpa",
"Pisabo": "pig",
"Pisaflores Tepehua": "tpp",
"Piscataway": "psy",
"Pisidian": "xps",
"Pitcairn-Norfolk": "pih",
"Pite Sami": "sje",
"Piti": "pcn",
"Pitjantjatjara": "pjt",
"Pitta-Pitta": "pit",
"Piu": "pix",
"Piya-Kwonci": "piy",
"Plains Apache": "apk",
"Plains Cree": "crk",
"Plains Indian Sign Language": "psd",
"Plains Miwok": "pmw",
"Plapo Krumen": "ktj",
"Plautdietsch": "pdt",
"Playero": "gob",
"Pnar": "pbv",
"Pochuri Naga": "npo",
"Pochutec": "xpo",
"Podoko": "pbi",
"Pogali": "hkh",
"Pogolo": "poy",
"Pohnpeian": "pon",
"Poitevin-Saintongeais": "roa-poi",
"Pokangá": "pok",
"Poke": "pof",
"Pol": "pmm",
"Polabian": "pox",
"Polci": "plj",
"Polish": "pl",
"Polish Sign Language": "pso",
"Polonombauk": "plb",
"Pom": "pmo",
"Ponam": "ncc",
"Pondi": "lnm",
"Pongu": "png",
"Ponosakan": "pns",
"Pontic Greek": "pnt",
"Ponyo": "npg",
"Poqomam": "poc",
"Poqomchi'": "poh",
"Porohanon": "prh",
"Port Sandwich": "psw",
"Port Sorell": "xpl",
"Port Vato": "ptv",
"Portuguese": "pt",
"Portuguese Sign Language": "psr",
"Potawatomi": "pot",
"Potiguára": "pog",
"Poumei Naga": "pmx",
"Pouye": "bye",
"Powari": "pwr",
"Powhatan": "pim",
"Poyanáwa": "pyn",
"Prakrit": "pra",
"Prasuni": "prn",
"Pre-Samnite": "itc-psa",
"Primitive Irish": "pgl",
"Principense": "pre",
"Proto-Abkhaz-Abaza": "cau-abz-pro",
"Proto-Afroasiatic": "afa-pro",
"Proto-Albanian": "sqj-pro",
"Proto-Algic": "aql-pro",
"Proto-Algonquian": "alg-pro",
"Proto-Amuesha-Chamicuro": "awd-amc-pro",
"Proto-Anatolian": "ine-ana-pro",
"Proto-Andian": "cau-and-pro",
"Proto-Apachean": "apa-pro",
"Proto-Arawa": "auf-pro",
"Proto-Arawak": "awd-pro",
"Proto-Armenian": "hyx-pro",
"Proto-Arnhem": "aus-arn-pro",
"Proto-Aroid": "omv-aro-pro",
"Proto-Aslian": "mkh-asl-pro",
"Proto-Atayalic": "map-ata-pro",
"Proto-Athabaskan": "ath-pro",
"Proto-Atlantic-Congo": "alv-pro",
"Proto-Austroasiatic": "aav-pro",
"Proto-Austronesian": "map-pro",
"Proto-Avaro-Andian": "cau-ava-pro",
"Proto-Bahnaric": "mkh-ban-pro",
"Proto-Bai": "sit-bai-pro",
"Proto-Balto-Slavic": "ine-bsl-pro",
"Proto-Bantoid": "nic-bod-pro",
"Proto-Bantu": "bnt-pro",
"Proto-Basque": "euq-pro",
"Proto-Batak": "btk-pro",
"Proto-Be": "qfa-onb-pro",
"Proto-Be-Tai": "qfa-bet-pro",
"Proto-Benue-Congo": "nic-bco-pro",
"Proto-Berber": "ber-pro",
"Proto-Binanderean": "ngf-bin-pro",
"Proto-Bodish": "sit-bdi-pro",
"Proto-Bodo-Garo": "tbq-bdg-pro",
"Proto-Bongo-Bagirmi": "csu-bba-pro",
"Proto-Boran": "sai-bor-pro",
"Proto-Brythonic": "cel-bry-pro",
"Proto-Bua": "alv-bua-pro",
"Proto-Bungku-Tolaki": "poz-btk-pro",
"Proto-Burmish": "tbq-brm-pro",
"Proto-Caddoan": "cdd-pro",
"Proto-Cangin": "alv-cng-pro",
"Proto-Cariban": "sai-car-pro",
"Proto-Celtic": "cel-pro",
"Proto-Central Chadic": "cdc-cbm-pro",
"Proto-Central Dravidian": "dra-cen-pro",
"Proto-Central Jê": "sai-cje-pro",
"Proto-Central Naga": "sit-aao-pro",
"Proto-Central New South Wales": "aus-cww-pro",
"Proto-Central Sudanic": "csu-pro",
"Proto-Central Togo": "alv-gtm-pro",
"Proto-Central-Eastern Malayo-Polynesian": "poz-cet-pro",
"Proto-Cerrado": "sai-cer-pro",
"Proto-Chadic": "cdc-pro",
"Proto-Chamic": "cmc-pro",
"Proto-Chatino": "omq-cha-pro",
"Proto-Chibchan": "cba-pro",
"Proto-Chimakuan": "chi-pro",
"Proto-Chinookan": "nai-ckn-pro",
"Proto-Chukotko-Kamchatkan": "qfa-cka-pro",
"Proto-Chumash": "nai-chu-pro",
"Proto-Circassian": "cau-cir-pro",
"Proto-Cupan": "azc-cup-pro",
"Proto-Cushitic": "cus-pro",
"Proto-Daju": "sdv-daj-pro",
"Proto-Daly": "aus-dal-pro",
"Proto-Dangari": "inc-dng-pro",
"Proto-Dargwa": "cau-drg-pro",
"Proto-Dizoid": "omv-diz-pro",
"Proto-Dravidian": "dra-pro",
"Proto-Eastern Jebel": "sdv-eje-pro",
"Proto-Eastern Malayo-Polynesian": "pqe-pro",
"Proto-Eastern Oti-Volta": "nic-eov-pro",
"Proto-Eastern Polynesian": "poz-pep-pro",
"Proto-Edekiri": "alv-edk-pro",
"Proto-Edoid": "alv-edo-pro",
"Proto-Ersuic": "sit-ers-pro",
"Proto-Eskimo": "esx-esk-pro",
"Proto-Eskimo-Aleut": "esx-pro",
"Proto-Fali": "alv-fli-pro",
"Proto-Finnic": "urj-fin-pro",
"Proto-Gbaya": "gba-pro",
"Proto-Gbe": "alv-gbe-pro",
"Proto-Georgian-Zan": "ccs-gzn-pro",
"Proto-Germanic": "gem-pro",
"Proto-Grassfields": "nic-grf-pro",
"Proto-Great Andamanese": "qfa-adm-pro",
"Proto-Guang": "alv-gng-pro",
"Proto-Gur": "nic-gur-pro",
"Proto-Gurunsi": "nic-gns-pro",
"Proto-Halmahera-Cenderawasih": "poz-hce-pro",
"Proto-Heiban": "alv-hei-pro",
"Proto-Hellenic": "grk-pro",
"Proto-Highland East Cushitic": "cus-hec-pro",
"Proto-Hlai": "qfa-lic-pro",
"Proto-Hmong-Mien": "hmx-pro",
"Proto-Hmongic": "hmn-pro",
"Proto-Hrusish": "sit-hrs-pro",
"Proto-Hurro-Urartian": "qfa-hur-pro",
"Proto-Idomoid": "alv-ido-pro",
"Proto-Igboid": "alv-igb-pro",
"Proto-Ijoid": "ijo-pro",
"Proto-Indo-Aryan": "inc-pro",
"Proto-Indo-European": "ine-pro",
"Proto-Indo-Iranian": "iir-pro",
"Proto-Inuit": "esx-inu-pro",
"Proto-Iranian": "ira-pro",
"Proto-Iroquoian": "iro-pro",
"Proto-Italic": "itc-pro",
"Proto-Iwaidjan": "aus-wdj-pro",
"Proto-Japonic": "jpx-pro",
"Proto-Jukunoid": "nic-jkn-pro",
"Proto-Jê": "sai-jee-pro",
"Proto-Kadu": "qfa-kad-pro",
"Proto-Kalamian": "phi-kal-pro",
"Proto-Kalapuyan": "nai-klp-pro",
"Proto-Kam-Sui": "qfa-kms-pro",
"Proto-Kampa": "awd-kmp-pro",
"Proto-Kamta": "inc-krd-pro",
"Proto-Karen": "kar-pro",
"Proto-Kartvelian": "ccs-pro",
"Proto-Katuic": "mkh-kat-pro",
"Proto-Kham": "sit-kha-pro",
"Proto-Khanty": "kca-pro",
"Proto-Khasian": "aav-khs-pro",
"Proto-Khmeric": "mkh-kmr-pro",
"Proto-Khmuic": "mkh-khm-pro",
"Proto-Kho-Bwa": "sit-khb-pro",
"Proto-Khoe": "khi-kho-pro",
"Proto-Koman": "ssa-kom-pro",
"Proto-Komisenian": "ira-kms-pro",
"Proto-Koreanic": "qfa-kor-pro",
"Proto-Kra": "qfa-kra-pro",
"Proto-Kra-Dai": "qfa-tak-pro",
"Proto-Kru": "kro-pro",
"Proto-Kuki-Chin": "tbq-kuk-pro",
"Proto-Kuliak": "ssa-klk-pro",
"Proto-Kurdish": "ku-pro",
"Proto-Kwa": "alv-kwa-pro",
"Proto-Lalo": "tbq-lal-pro",
"Proto-Lampungic": "poz-lgx-pro",
"Proto-Lezghian": "cau-lzg-pro",
"Proto-Lolo-Burmese": "tbq-lob-pro",
"Proto-Loloish": "tbq-lol-pro",
"Proto-Lower Cross River": "nic-lcr-pro",
"Proto-Luish": "sit-luu-pro",
"Proto-Maidun": "nai-mdu-pro",
"Proto-Malayic": "poz-mly-pro",
"Proto-Malayo-Chamic": "poz-mcm-pro",
"Proto-Malayo-Polynesian": "poz-pro",
"Proto-Malayo-Sumbawan": "poz-msa-pro",
"Proto-Mande": "dmn-pro",
"Proto-Mangbetu": "csu-maa-pro",
"Proto-Mansi": "mns-pro",
"Proto-Mari": "chm-pro",
"Proto-Masa": "cdc-mas-pro",
"Proto-Mayan": "myn-pro",
"Proto-Mazatec": "omq-maz-pro",
"Proto-Medo-Parthian": "ira-mpr-pro",
"Proto-Micronesian": "poz-mic-pro",
"Proto-Mienic": "hmx-mie-pro",
"Proto-Min": "zhx-min-pro",
"Proto-Mixe-Zoque": "nai-miz-pro",
"Proto-Mixtec": "omq-mxt-pro",
"Proto-Mixtecan": "omq-mix-pro",
"Proto-Mon-Khmer": "mkh-pro",
"Proto-Mongolic": "xgn-pro",
"Proto-Monic": "mkh-mnc-pro",
"Proto-Mordvinic": "urj-mdv-pro",
"Proto-Mumuye": "alv-mum-pro",
"Proto-Munda": "mun-pro",
"Proto-Munji-Yidgha": "ira-mny-pro",
"Proto-Muskogean": "nai-mus-pro",
"Proto-Na-Dene": "xnd-pro",
"Proto-Nahuan": "azc-nah-pro",
"Proto-Naish": "sit-nas-pro",
"Proto-Nakh": "cau-nkh-pro",
"Proto-Nawiki": "awd-nwk-pro",
"Proto-Nguni": "bnt-ngu-pro",
"Proto-Nicobarese": "aav-nic-pro",
"Proto-Niger-Congo": "nic-pro",
"Proto-Nilo-Saharan": "ssa-pro",
"Proto-Nilotic": "sdv-nil-pro",
"Proto-Norse": "gmq-pro",
"Proto-North Dravidian": "dra-nor-pro",
"Proto-North Halmahera": "paa-nha-pro",
"Proto-North Iroquoian": "iro-nor-pro",
"Proto-North Sarawak": "poz-swa-pro",
"Proto-Northeast Caucasian": "cau-nec-pro",
"Proto-Northern Jê": "sai-nje-pro",
"Proto-Northern Naga": "sit-kon-pro",
"Proto-Northwest Caucasian": "cau-nwc-pro",
"Proto-Nubian": "nub-pro",
"Proto-Nuclear Polynesian": "poz-pnp-pro",
"Proto-Numic": "azc-num-pro",
"Proto-Nupoid": "alv-nup-pro",
"Proto-Nuristani": "iir-nur-pro",
"Proto-Nyima": "sdv-nyi-pro",
"Proto-Nyulnyulan": "aus-nyu-pro",
"Proto-Oceanic": "poz-oce-pro",
"Proto-Ogoni": "nic-ogo-pro",
"Proto-Omotic": "omv-pro",
"Proto-Ongan": "qfa-ong-pro",
"Proto-Oti-Volta": "nic-ovo-pro",
"Proto-Oto-Manguean": "omq-pro",
"Proto-Oto-Pamean": "omq-otp-pro",
"Proto-Otomi": "oto-otm-pro",
"Proto-Otomian": "oto-pro",
"Proto-Pakanic": "mkh-pkn-pro",
"Proto-Palaungic": "mkh-pal-pro",
"Proto-Pama-Nyungan": "aus-pam-pro",
"Proto-Paresi-Waura": "awd-prw-pro",
"Proto-Pathan": "ira-pat-pro",
"Proto-Pearic": "mkh-pea-pro",
"Proto-Permic": "urj-prm-pro",
"Proto-Philippine": "phi-pro",
"Proto-Plateau": "nic-plt-pro",
"Proto-Plateau Penutian": "nai-plp-pro",
"Proto-Pnar-Khasi-Lyngngam": "aav-pkl-pro",
"Proto-Polynesian": "poz-pol-pro",
"Proto-Pomo": "nai-pom-pro",
"Proto-Puroik": "sit-khp-pro",
"Proto-Quechuan": "qwe-pro",
"Proto-Rukai": "dru-pro",
"Proto-Ryukyuan": "jpx-ryu-pro",
"Proto-Sabaki": "bnt-sab-pro",
"Proto-Saka": "xsc-sak-pro",
"Proto-Saka-Wakhi": "xsc-skw-pro",
"Proto-Salish": "sal-pro",
"Proto-Samic": "smi-pro",
"Proto-Samoyedic": "syd-pro",
"Proto-Sanglechi-Ishkashimi": "ira-sgi-pro",
"Proto-Sara": "csu-sar-pro",
"Proto-Sarmatian": "xsc-sar-pro",
"Proto-Scythian": "xsc-pro",
"Proto-Selkup": "sel-pro",
"Proto-Semitic": "sem-pro",
"Proto-Shughni-Roshani": "ira-shr-pro",
"Proto-Shughni-Yazghulami": "ira-shy-pro",
"Proto-Shughni-Yazghulami-Munji": "ira-sym-pro",
"Proto-Sino-Tibetan": "sit-pro",
"Proto-Siouan": "sio-pro",
"Proto-Siouan-Catawban": "nai-sca-pro",
"Proto-Slavic": "sla-pro",
"Proto-Sogdic": "ira-sgc-pro",
"Proto-Somaloid": "cus-som-pro",
"Proto-Songhay": "son-pro",
"Proto-Sotho-Tswana": "bnt-sts-pro",
"Proto-South Cushitic": "cus-sou-pro",
"Proto-South Dravidian": "dra-sou-pro",
"Proto-South Dravidian I": "dra-sdo-pro",
"Proto-South Dravidian II": "dra-sdt-pro",
"Proto-South Sulawesi": "poz-ssw-pro",
"Proto-Southern Jê": "sai-sje-pro",
"Proto-Southwestern Tai": "tai-swe-pro",
"Proto-Ta-Arawak": "awd-taa-pro",
"Proto-Tai": "tai-pro",
"Proto-Takic": "azc-tak-pro",
"Proto-Taman": "sdv-tmn-pro",
"Proto-Tamangic": "sit-tam-pro",
"Proto-Tangkhulic": "sit-tng-pro",
"Proto-Tani": "sit-tan-pro",
"Proto-Taranoan": "sai-tar-pro",
"Proto-Tatic": "xme-ttc-pro",
"Proto-Tocharian": "ine-toc-pro",
"Proto-Totozoquean": "nai-tot-pro",
"Proto-Trans-New Guinea": "ngf-pro",
"Proto-Triqui": "omq-tri-pro",
"Proto-Tsezian": "cau-tsz-pro",
"Proto-Tsimshianic": "nai-tsi-pro",
"Proto-Tungusic": "tuw-pro",
"Proto-Tupi-Guarani": "tup-gua-pro",
"Proto-Tupian": "tup-pro",
"Proto-Turkic": "trk-pro",
"Proto-Ubangian": "nic-ubg-pro",
"Proto-Ugric": "urj-ugr-pro",
"Proto-Upper Cross River": "nic-ucr-pro",
"Proto-Uralic": "urj-pro",
"Proto-Utian": "nai-utn-pro",
"Proto-Uto-Aztecan": "azc-pro",
"Proto-Vietic": "mkh-vie-pro",
"Proto-Volta-Congo": "nic-vco-pro",
"Proto-Volta-Niger": "alv-von-pro",
"Proto-West Germanic": "gmw-pro",
"Proto-West Semitic": "sem-wes-pro",
"Proto-Western Kho-Bwa": "sit-khw-pro",
"Proto-Western Mande": "dmn-mdw-pro",
"Proto-Witotoan": "sai-wit-pro",
"Proto-Yeniseian": "qfa-yen-pro",
"Proto-Yoruba": "alv-yor-pro",
"Proto-Yoruboid": "alv-yrd-pro",
"Proto-Yukaghir": "qfa-yuk-pro",
"Proto-Yupik": "ypk-pro",
"Proto-Zapotec": "omq-zpc-pro",
"Proto-Zapotecan": "omq-zap-pro",
"Proto-Zaza-Gorani": "ira-zgr-pro",
"Providencia Sign Language": "prz",
"Psikye": "kvj",
"Puare": "pux",
"Pudtol Atta": "atp",
"Puebla Mazatec": "pbm",
"Puelche": "pue",
"Puerto Rican Sign Language": "psl",
"Puimei Naga": "npu",
"Puinave": "pui",
"Puiron": "sit-prn",
"Pukapukan": "pkp",
"Pulabu": "pup",
"Puluwat": "puw",
"Puma": "pum",
"Pumpokol": "xpm",
"Pumé": "yae",
"Punan Aput": "pud",
"Punan Bah-Biau": "pna",
"Punan Batu": "pnm",
"Punan Merah": "puf",
"Punan Merap": "puc",
"Punan Tubu": "puj",
"Punic": "xpu",
"Punjabi": "pa",
"Punu": "puu",
"Puoc": "puo",
"Puquina": "puq",
"Puragi": "pru",
"Purari": "iar",
"Purepecha": "pua",
"Puri": "prr",
"Purik": "prx",
"Purisimeño": "puy",
"Puroik": "suv",
"Puruborá": "pur",
"Puruhá": "sai-prh",
"Purukotó": "sai-pur",
"Purum": "pub",
"Putai": "mfl",
"Putoh": "put",
"Putukwam": "afe",
"Puxian Min": "cpx",
"Puyo-Paekche": "xpp",
"Puyuma": "pyu",
"Pwaamèi": "pme",
"Pwapwâ": "pop",
"Pyapun": "pcw",
"Pye Krumen": "pye",
"Pyemmairre": "xpb",
"Pyen": "pyy",
"Pykobjê": "sai-pyk",
"Pyu (Myanmar)": "pyx",
"Pyu (New Guinea)": "pby",
"Páez": "pbb",
"Pááfang": "pfa",
"Päri": "lkr",
"Pémono": "pev",
"Pévé": "lme",
"Pökoot": "pko",
"Q'anjob'al": "kjb",
"Q'eqchi": "kek",
"Qabiao": "laq",
"Qaqet": "byx",
"Qatabanian": "xqt",
"Qau": "gqu",
"Qila Muji": "ymq",
"Qimant": "ahg",
"Quapaw": "qua",
"Quebec Sign Language": "fcs",
"Quechua": "qu",
"Quenya": "qya",
"Querétaro Otomi": "otq",
"Quetzaltepec Mixe": "pxm",
"Queyu": "qvy",
"Quiavicuzas Zapotec": "zpj",
"Quileute": "qui",
"Quimbaya": "sai-qmb",
"Quinault": "qun",
"Quinigua": "nai-qng",
"Quinqui": "quq",
"Quioquitani-Quierí Zapotec": "ztq",
"Quiotepec Chinantec": "chq",
"Quiripi": "qyp",
"Quitemo": "sai-qtm",
"Rabha": "rah",
"Rabona": "sai-rab",
"Rade": "rad",
"Raetic": "xrr",
"Raga": "lml",
"Rahambuu": "raz",
"Rajah Kabunsuwan Manobo": "mqk",
"Rajbanshi": "rjs",
"Raji": "rji",
"Rajong": "rjg",
"Rajput Garasia": "gra",
"Rakahanga-Manihiki": "rkh",
"Rakhine": "rki",
"Ralte": "ral",
"Rama": "rma",
"Ramandi": "tks",
"Ramanos": "sai-ram",
"Ramoaaina": "rai",
"Ramopa": "kjx",
"Rampi": "lje",
"Rana Tharu": "thr",
"Rang": "rax",
"Rangkas": "rgk",
"Ranglong": "rnl",
"Rao": "rao",
"Rapa": "ray",
"Rapa Nui": "rap",
"Rapoisi": "kyx",
"Rapting": "rpt",
"Rara Bakati'": "lra",
"Rarotongan": "rar",
"Rasawa": "rac",
"Ratagnon": "btn",
"Ratahan": "rth",
"Rathawi": "rtw",
"Rathwi Bareli": "bgd",
"Raute": "rau",
"Ravula": "yea",
"Rawa": "rwo",
"Rawang": "raw",
"Rawat": "jnl",
"Rawo": "rwa",
"Rayón Zoque": "zor",
"Razajerdi": "rat",
"Razihi": "rzh",
"Reang": "ria",
"Red Gelao": "gir",
"Reel": "atu",
"Rejang": "rej",
"Rejang Kayan": "ree",
"Reli": "rei",
"Rema": "bow",
"Rembarunga": "rmb",
"Rembong": "reb",
"Remo": "rem",
"Remontado Agta": "agv",
"Rempi": "rmp",
"Remun": "lkj",
"Rendille": "rel",
"Rengao": "ren",
"Rennellese": "mnv",
"Repanbitip": "rpn",
"Rer Bare": "rer",
"Rerau": "rea",
"Rerep": "pgk",
"Reshe": "res",
"Resígaro": "rgr",
"Retta": "ret",
"Reyesano": "rey",
"Rhine Franconian": "gmw-rfr",
"Riang": "ril",
"Riantana": "ran",
"Ribun": "rir",
"Rigwe": "iri",
"Rikbaktsa": "rkb",
"Rinconada Bikol": "bto",
"Rincón Zapotec": "zar",
"Ringgou": "rgu",
"Ririo": "rri",
"Ritarungo": "rit",
"Riung": "riu",
"Riverain Sango": "snj",
"Rogo": "rod",
"Rohingya": "rhg",
"Roma": "rmm",
"Romagnol": "rgn",
"Romam": "rmx",
"Romani": "rom",
"Romanian": "ro",
"Romanian Sign Language": "rms",
"Romano-Greek": "rge",
"Romano-Serbian": "rsb",
"Romanova": "rmv",
"Romansh": "rm",
"Romblomanon": "rol",
"Rombo": "rof",
"Romkun": "rmk",
"Ron": "cla",
"Ronga": "rng",
"Rongga": "ror",
"Rongmei Naga": "nbu",
"Rongpo": "rnp",
"Ronji": "roe",
"Roon": "rnn",
"Roria": "rga",
"Roro": "rro",
"Rotokas": "roo",
"Rotuman": "rtm",
"Rouran": "mis-rou",
"Roviana": "rug",
"Ruching Palaung": "pce",
"Rudbari": "rdb",
"Rufiji": "rui",
"Ruga": "ruh",
"Rukai": "dru",
"Rukiga": "cgg",
"Ruma": "ruz",
"Rumai Palaung": "rbb",
"Rumu": "klq",
"Runga": "rou",
"Rungtu": "rtc",
"Rungus": "drg",
"Rungwa": "rnw",
"Russenorsk": "crp-rsn",
"Russian": "ru",
"Russian Sign Language": "rsl",
"Rutul": "rut",
"Ruuli": "ruc",
"Ruwund": "rnd",
"Rwa": "rwk",
"Rwanda-Rundi": "rw",
"Rwandan Sign Language": "rsn",
"Réunion Creole French": "rcf",
"S'gaw Karen": "ksw",
"Sa": "sax",
"Sa'a": "apb",
"Sa'ban": "snv",
"Sa'och": "scq",
"Saafi-Saafi": "sav",
"Saam": "raq",
"Saamia": "lsm",
"Saanich": "str",
"Saare": "uss",
"Saaroa": "sxr",
"Saba": "saa",
"Sabaean": "xsa",
"Sabah Bisaya": "bsy",
"Sabah Malay": "msi",
"Sabanê": "sae",
"Sabaot": "spy",
"Sabine": "sbv",
"Sabir": "pml",
"Sabu": "hvn",
"Sabüm": "sbo",
"Sacapulteco": "quv",
"Sadri": "sck",
"Saek": "skb",
"Saep": "spd",
"Safaitic": "sem-saf",
"Safaliba": "saf",
"Safeyoka": "apz",
"Safwa": "sbk",
"Sagala": "sbm",
"Sagalla": "tga",
"Sahaptin": "nai-spt",
"Saho": "ssy",
"Sahu": "saj",
"Saisiyat": "xsy",
"Sajau Basap": "sjb",
"Sakachep": "sch",
"Sakam": "skm",
"Sakao": "sku",
"Sakata": "skt",
"Sake": "sak",
"Sakizaya": "szy",
"Sala": "shq",
"Salampasu": "slx",
"Salar": "slr",
"Salas": "sgu",
"Saleman": "sau",
"Saliba (Colombia)": "slc",
"Saliba (New Guinea)": "sbe",
"Salinan": "sln",
"Salt-Yui": "sll",
"Saluan": "loe",
"Salumá": "slj",
"Salvadoran Lenca": "nai-sln",
"Salvadoran Sign Language": "esn",
"Sam": "snx",
"Sama": "smd",
"Samalian": "sem-sam",
"Samaritan Aramaic": "sam",
"Samaritan Hebrew": "smp",
"Samarokena": "tmj",
"Samatao": "ysd",
"Samba": "smx",
"Sambali": "xsb",
"Sambalpuri": "spv",
"Sambe": "xab",
"Samberigi": "ssx",
"Samburu": "saq",
"Samei": "smh",
"Samo": "smq",
"Samoan": "sm",
"Samoan Plantation Pidgin": "crp-spp",
"Samogitian": "sgs",
"Samosa": "swm",
"Sampang": "rav",
"Samre": "sxm",
"Samtao": "stu",
"Samvedi": "smv",
"San Agustín Mixtepec Zapotec": "ztm",
"San Baltazar Loxicha Zapotec": "zpx",
"San Felipe Otlaltepec Popoloca": "pow",
"San Jerónimo Tecóatl Mazatec": "maa",
"San Juan Atzingo Popoloca": "poe",
"San Juan Colorado Mixtec": "mjc",
"San Juan Guelavía Zapotec": "zab",
"San Juan Quiahije Chatino": "omq-sjq",
"San Juan Teita Mixtec": "xtj",
"San Luís Temalacayuca Popoloca": "pps",
"San Marcos Tlalcoyalco Popoloca": "pls",
"San Martín Itunyoso Triqui": "trq",
"San Miguel Creole French": "scf",
"San Miguel Piedras Mixtec": "xtp",
"San Miguel el Grande Mixtec": "mig",
"San Pablo Güilá Zapotec": "ztu",
"San Pedro Amuzgos Amuzgo": "azg",
"San Pedro Quiatoni Zapotec": "zpf",
"San Vicente Coatlán Zapotec": "zpt",
"Sanapaná": "spn",
"Sanaviron": "sai-san",
"Sandawe": "sad",
"Sanga (Congo)": "sng",
"Sanga (Nigeria)": "xsn",
"Sanggau": "scg",
"Sangil": "snl",
"Sangir": "sxn",
"Sangisari": "sgr",
"Sangkong": "sgk",
"Sanglechi": "sgy",
"Sango": "sg",
"Sangtam Naga": "nsa",
"Sangu (Gabon)": "snq",
"Sangu (Tanzania)": "sbp",
"Sani": "ysn",
"Sanie": "ysy",
"Saniyo-Hiyewe": "sny",
"Sankaran Maninka": "msc",
"Sansi": "ssi",
"Sanskrit": "sa",
"Santa Catarina Albarradas Zapotec": "ztn",
"Santa Inés Ahuatempan Popoloca": "pca",
"Santa Inés Yatzechi Zapotec": "zpn",
"Santa Lucía Monteverde Mixtec": "mdv",
"Santa María La Alta Nahuatl": "nhz",
"Santa María Quiegolani Zapotec": "zpi",
"Santa María Zacatepec Mixtec": "mza",
"Santa Teresa Cora": "cok",
"Santali": "sat",
"Santiago Xanica Zapotec": "zpr",
"Santo Domingo Albarradas Zapotec": "zas",
"Sanumá": "xsu",
"Sanxiang Min": "nan-zsh",
"Sapa": "tys",
"Saparua": "spr",
"Sapará": "sai-sap",
"Sapo": "krn",
"Saponi": "spi",
"Saposa": "sps",
"Sapuan": "spu",
"Sapé": "spc",
"Sar": "mwm",
"Sara": "sre",
"Sara Kaba": "sbz",
"Sara Kaba Deme": "kwg",
"Sara Kaba Náà": "kwv",
"Saraiki": "skr",
"Saramaccan": "srm",
"Sarangani Blaan": "bps",
"Sarangani Manobo": "mbs",
"Sarasira": "zsa",
"Saraveca": "sar",
"Sarawak Malay": "poz-sml",
"Sardinian": "sc",
"Sarikoli": "srh",
"Sarli": "sdf",
"Sartang": "onp",
"Sarua": "swy",
"Sarudu": "sdu",
"Saruga": "sra",
"Sasak": "sas",
"Sasaru": "sxs",
"Sassarese": "sdc",
"Satawalese": "stw",
"Saterland Frisian": "stq",
"Sateré-Mawé": "mav",
"Sathmar Swabian": "gmw-stm",
"Saudi Arabian Sign Language": "sdl",
"Saurashtra": "saz",
"Sauri": "srt",
"Sause": "sao",
"Sausi": "ssj",
"Savi": "sdg",
"Savosavo": "svs",
"Sawai": "szw",
"Saweru": "swr",
"Sawi": "saw",
"Sawila": "swt",
"Sawriya Paharia": "mjt",
"Saxwe Gbe": "sxw",
"Saya": "say",
"Sayula Popoluca": "pos",
"Scanian": "gmq-scy",
"Scots": "sco",
"Scottish Gaelic": "gd",
"Seba": "kdg",
"Sebat Bet Gurage": "sgw",
"Seberuang": "sbx",
"Sebop": "sib",
"Sebuyau": "snb",
"Sechelt": "sec",
"Sechura": "sai-sec",
"Secoya": "sey",
"Sedang": "sed",
"Sedoa": "tvw",
"Seenku": "sos",
"Segai": "sge",
"Segeju": "seg",
"Seget": "sbg",
"Sehwi": "sfw",
"Seimat": "ssg",
"Seit-Kaitetu": "hik",
"Sekani": "sek",
"Sekapan": "skp",
"Sekar": "skz",
"Seke": "skj",
"Sekele": "vaj",
"Seki": "syi",
"Seko Padang": "skx",
"Seko Tengah": "sko",
"Sekpele": "lip",
"Selangor Sign Language": "kgi",
"Selaru": "slu",
"Selayar": "sly",
"Selee": "snw",
"Selepet": "spl",
"Selk'nam": "ona",
"Selonian": "sxl",
"Selungai Murut": "slg",
"Seluwasan": "sws",
"Sema": "nsm",
"Semai": "sea",
"Semandang": "sdm",
"Semaq Beri": "szc",
"Sembakung Murut": "sbr",
"Semelai": "sza",
"Semigallian": "xzm",
"Semimi": "etz",
"Semnam": "ssm",
"Semnani": "smy",
"Sempan": "xse",
"Sena": "seh",
"Senara": "seq",
"Senaya": "syn",
"Sene": "sej",
"Seneca": "see",
"Sengele": "szg",
"Senggi": "snu",
"Sengo": "spk",
"Sengseng": "ssz",
"Senhaja de Srair": "sjs",
"Sensi": "sni",
"Sentani": "set",
"Senthang Chin": "sez",
"Sentinelese": "std",
"Sepa (Indonesia)": "spb",
"Sepa (New Guinea)": "spe",
"Sepen": "spm",
"Sepik Iwam": "iws",
"Sera": "sry",
"Serbo-Croatian": "sh",
"Sere": "swf",
"Serer": "srr",
"Seri": "sei",
"Serili": "sve",
"Seroa": "kqu",
"Serrano": "ser",
"Seru": "szd",
"Serua": "srw",
"Serudung Murut": "srk",
"Serui-Laut": "seu",
"Seta": "stf",
"Setaman": "stm",
"Seti": "sbi",
"Severn Ojibwa": "ojs",
"Sewa Bay": "sew",
"Seychelles Sign Language": "lsw",
"Seychellois Creole": "crs",
"Seze": "sze",
"Sha": "scw",
"Shabak": "sdb",
"Shabo": "sbf",
"Shahmirzadi": "srz",
"Shahrudi": "shm",
"Shall-Zwall": "sha",
"Shama-Sambuga": "sqa",
"Shamang": "xsh",
"Shambala": "ksb",
"Shan": "shn",
"Shanenawa": "swo",
"Shanga": "sho",
"Shangzhai": "jih",
"Shaojiang Min": "sjc",
"Shaozhou Tuhua": "zhx-sht",
"Sharanahua": "mcd",
"Shark Bay": "ssv",
"Sharwa": "swq",
"Shasta": "sht",
"Shatt": "shj",
"Shau": "sqh",
"Shawnee": "sjw",
"She": "shx",
"Shebayo": "awd-she",
"Shehri": "shv",
"Shekkacho": "moy",
"Sheko": "she",
"Shelta": "sth",
"Shendu": "shl",
"Sheni": "scv",
"Sherbro": "bun",
"Sherdukpen": "sdp",
"Sherpa": "xsr",
"Sheshi Kham": "kip",
"Shetland": "scz",
"Shi": "shr",
"Shihhi Arabic": "ssh",
"Shiki": "gua",
"Shilluk": "shk",
"Shina": "scl",
"Shinasha": "bwo",
"Shipibo-Conibo": "shp",
"Shixing": "sxg",
"Sholaga": "sle",
"Shom Peng": "sii",
"Shona": "sn",
"Shoo-Minda-Nye": "bcv",
"Shor": "cjs",
"Shoshone": "shh",
"Shua": "shg",
"Shuar": "jiv",
"Shughni": "sgh",
"Shumashti": "sts",
"Shumcho": "scu",
"Shuswap": "shs",
"Shuwa-Zamani": "ksa",
"Shwai": "shw",
"Shwe Palaung": "pll",
"Sialum": "slw",
"Siamou": "sif",
"Sian": "spg",
"Siane": "snp",
"Siang": "sya",
"Siar-Lak": "sjr",
"Sibe": "nco",
"Siberian Tatar": "sty",
"Sibu Melanau": "sdx",
"Sicanian": "sxc",
"Sicel": "scx",
"Sichuanese": "zhx-sic",
"Sicilian": "scn",
"Sicite": "sep",
"Siculo-Arabic": "sqr",
"Sidamo": "sid",
"Sidetic": "xsd",
"Sie": "erg",
"Sierra Leone Sign Language": "sgx",
"Sierra Negra Nahuatl": "nsu",
"Sierra de Juárez Zapotec": "zaa",
"Sighu": "sxe",
"Sihan": "snr",
"Sika": "ski",
"Sikaiana": "sky",
"Sikaritai": "tty",
"Sikiana": "sik",
"Sikkimese": "sip",
"Sikule": "skh",
"Sila": "slt",
"Silacayoapan Mixtec": "mks",
"Silesian": "szl",
"Silimo": "wul",
"Siliput": "mkc",
"Silopi": "xsp",
"Silt'e": "stv",
"Simaa": "sie",
"Simalungun Batak": "bts",
"Simba": "sbw",
"Simbali": "smg",
"Simbari": "smb",
"Simbo": "sbb",
"Simeku": "smz",
"Simeulue": "smr",
"Simte": "smt",
"Sinacantán": "nai-sin",
"Sinagen": "siu",
"Sinasina": "sst",
"Sinaugoro": "snc",
"Sindarin": "sjn",
"Sindhi": "sd",
"Sindhi Bhil": "sbn",
"Sindihui Mixtec": "xts",
"Singa": "sgm",
"Singapore Sign Language": "sls",
"Singpho": "sgp",
"Sinhalese": "si",
"Sinicahua Mixtec": "xti",
"Sininkere": "skq",
"Sinte Romani": "rmo",
"Sinyar": "sys",
"Sinúfana": "sai-sin",
"Sio": "xsi",
"Siona": "snn",
"Sipakapense": "qum",
"Sira": "swj",
"Siraya": "fos",
"Sirenik": "ysr",
"Siri": "sir",
"Siriano": "sri",
"Sirionó": "srq",
"Sirmauri": "srx",
"Siroi": "ssd",
"Sirva": "sbq",
"Sisaala": "sld",
"Sissano": "sso",
"Situ": "sit-sit",
"Siuslaw": "sis",
"Sivandi": "siy",
"Sivia Sign Language": "lsv",
"Siwai": "siw",
"Siwi": "siz",
"Siwu": "akp",
"Siyin Chin": "csy",
"Skalvian": "svx",
"Ske": "ske",
"Skepi Creole Dutch": "skw",
"Skolt Sami": "sms",
"Skou": "skv",
"Slavomolisano": "svm",
"Slovak": "sk",
"Slovakian Sign Language": "svk",
"Slovene": "sl",
"Slovincian": "zlw-slv",
"Small Flowery Miao": "sfm",
"Smärky Kanum": "kxq",
"So'a": "ssq",
"Sobei": "sob",
"Sochiapam Chinantec": "cso",
"Soga": "xog",
"Sogdian": "sog",
"Sok": "skk",
"Sokna": "swn",
"Soko": "soc",
"Sokoro": "sok",
"Solano": "xso",
"Soli": "sby",
"Solombala English": "crp-slb",
"Solon": "tuw-sol",
"Solong": "aaw",
"Solos": "sol",
"Som": "smc",
"Somali": "so",
"Somba-Siawari": "bmu",
"Somra": "ntx",
"Somrai": "sor",
"Somray": "smu",
"Somyev": "kgt",
"Sonaga": "ysg",
"Sonde": "shc",
"Songe": "sop",
"Songlai Chin": "csj",
"Songomeno": "soe",
"Songoora": "sod",
"Sonha": "soi",
"Sonia": "siq",
"Soninke": "snk",
"Sonsorolese": "sov",
"Soo": "teu",
"Sop": "urw",
"Soqotri": "sqt",
"Sora": "srb",
"Sori-Harengan": "sbh",
"Sorkhei": "sqo",
"Sorothaptic": "sxo",
"Sorsogon Ayta": "ays",
"Sos Kundi": "sdk",
"Sota Kanum": "krz",
"Sotho": "st",
"Sou": "sqq",
"Sougb": "mnx",
"South African Sign Language": "sfs",
"South Awyu": "aws",
"South Boma": "bnt-sbo",
"South Central Banda": "lnl",
"South Central Dinka": "dib",
"South Efate": "erk",
"South Fali": "fal",
"South Giziga": "giz",
"South Lembata": "lmf",
"South Levantine Arabic": "ajp",
"South Marquesan": "mqm",
"South Muyu": "kts",
"South Nuaulu": "nxl",
"South Picene": "spx",
"South Slavey": "xsl",
"South Tairora": "omw",
"South Tukang Besi": "bhq",
"South Ucayali Ashéninka": "cpy",
"South Watut": "mcy",
"Southeast Ambrym": "tvk",
"Southeast Babar": "vbb",
"Southeast Ijo": "ijs",
"Southeast Pashayi": "psi",
"Southeast Tasmanian": "xpf",
"Southeastern Dinka": "dks",
"Southeastern Ixtlán Zapotec": "zpd",
"Southeastern Kolami": "nit",
"Southeastern Nochixtlán Mixtec": "mxy",
"Southeastern Pomo": "pom",
"Southeastern Puebla Nahuatl": "npl",
"Southeastern Tarahumara": "tcu",
"Southeastern Tepehuan": "stp",
"Southern Alta": "agy",
"Southern Altai": "alt",
"Southern Amami Ōshima": "ams",
"Southern Bai": "bfs",
"Southern Birifor": "biv",
"Southern Bobo": "bwq",
"Southern Bontoc": "obk",
"Southern Carrier": "caf",
"Southern Catanduanes Bikol": "bln",
"Southern Dagaare": "dga",
"Southern East Cree": "crj",
"Southern Ghale": "ghe",
"Southern Grebo": "grj",
"Southern Guiyang Hmong": "hmy",
"Southern Haida": "hax",
"Southern Hindko": "hnd",
"Southern Kalapuya": "sxk",
"Southern Kalinga": "ksc",
"Southern Kam": "kmc",
"Southern Khanty": "kca-sou",
"Southern Kissi": "kss",
"Southern Kiwai": "kjd",
"Southern Kurdish": "sdh",
"Southern Lolopo": "ysp",
"Southern Lorung": "lrr",
"Southern Luri": "luz",
"Southern Ma'di": "snm",
"Southern Mansi": "mns-sou",
"Southern Mashan Hmong": "hma",
"Southern Mnong": "mnn",
"Southern Muji": "ymc",
"Southern Ndebele": "nr",
"Southern Ngbandi": "nbw",
"Southern Nicobarese": "nik",
"Southern Nisu": "nsd",
"Southern Nuni": "nnw",
"Southern Ohlone": "css",
"Southern One": "osu",
"Southern Pame": "pmz",
"Southern Pinghua": "csp",
"Southern Pomo": "peq",
"Southern Puebla Mixtec": "mit",
"Southern Pumi": "pmj",
"Southern Qiandong Miao": "hms",
"Southern Qiang": "qxs",
"Southern Rengma Naga": "nre",
"Southern Rincon Zapotec": "zsr",
"Southern Roglai": "rgs",
"Southern Sama": "ssb",
"Southern Sami": "sma",
"Southern Samo": "sbd",
"Southern Selkup": "sel-sou",
"Southern Sierra Miwok": "skd",
"Southern Thai": "sou",
"Southern Tidung": "itd",
"Southern Tiwa": "tix",
"Southern Toussian": "wib",
"Southern Tujia": "tjs",
"Southern Tutchone": "tce",
"Southern Valley Yokuts": "yok-svy",
"Southern Yukaghir": "yux",
"Southwest Gbaya": "gso",
"Southwest Palawano": "plv",
"Southwest Pashayi": "psh",
"Southwest Tanna": "nwi",
"Southwestern Bontoc": "vbk",
"Southwestern Dinka": "dik",
"Southwestern Fars": "fay",
"Southwestern Guiyang Hmong": "hmg",
"Southwestern Huishui Hmong": "hmh",
"Southwestern Nisu": "nsv",
"Southwestern Tarahumara": "twr",
"Southwestern Tepehuan": "tla",
"Southwestern Tlaxiaco Mixtec": "meh",
"Sowa": "sww",
"Sowanda": "sow",
"Soyaltepec Mazatec": "vmp",
"Soyaltepec Mixtec": "vmq",
"Spanish": "es",
"Spanish Sign Language": "ssp",
"Spiti Bhoti": "spt",
"Spokane": "spo",
"Squamish": "squ",
"Sranan Tongo": "srn",
"Sri Lankan Creole Malay": "sci",
"Sri Lankan Sign Language": "sqs",
"Stau": "ero-tau",
"Stod Bhoti": "sbu",
"Stoney": "sto",
"Suabo": "szp",
"Suau": "swp",
"Suba": "sxb",
"Suba-Simbiti": "ssc",
"Subi": "xsj",
"Subiya": "sbs",
"Subtiaba": "sut",
"Sudanese Arabic": "apd",
"Sudest": "tgo",
"Sudovian": "xsv",
"Suena": "sue",
"Suga": "sgi",
"Suganga": "sug",
"Sugut Dusun": "kzs",
"Sui": "swi",
"Suki": "sui",
"Suku": "sub",
"Sukuma": "suk",
"Sukur": "syk",
"Sukurum": "zsu",
"Sula": "szn",
"Sulka": "sua",
"Sulod": "srg",
"Suma": "sqm",
"Sumariup": "siv",
"Sumau": "six",
"Sumbawa": "smw",
"Sumbwa": "suw",
"Sumerian": "sux",
"Sumtu Chin": "csv",
"Sunam": "ssk",
"Sundanese": "su",
"Sungwadaga": "mwo",
"Sungwadia": "mrb",
"Sunum": "ymn",
"Sunwar": "suz",
"Suoy": "syo",
"Supyire": "spp",
"Sur": "tdl",
"Surbakhal": "sbj",
"Suri": "suq",
"Surigaonon": "sgd",
"Surjapuri": "sjp",
"Sursurunga": "sgz",
"Suruahá": "swx",
"Surubu": "sde",
"Suruí": "sru",
"Suruí Do Pará": "mdz",
"Susquehannock": "sqn",
"Susu": "sus",
"Susuami": "ssu",
"Suundi": "sdj",
"Suwawa": "swu",
"Suyá": "suy",
"Svan": "sva",
"Swabian": "swg",
"Swahili": "sw",
"Swampy Cree": "csw",
"Swazi": "ss",
"Swedish": "sv",
"Swedish Sign Language": "swl",
"Swiss-French Sign Language": "ssr",
"Swiss-German Sign Language": "sgg",
"Swiss-Italian Sign Language": "slf",
"Swo": "sox",
"Syenara": "shz",
"Sylheti": "syl",
"Sácata": "sai-sac",
"São Paulo Kaingáng": "zkp",
"Sãotomense": "cri",
"Sîshëë": "sih",
"Sô": "sss",
"T'en": "tct",
"Taabwa": "tap",
"Taba": "mky",
"Tabaa Zapotec": "zat",
"Tabancale": "sai-tab",
"Tabaru": "tby",
"Tabasaran": "tab",
"Tabasco Nahuatl": "nhc",
"Tabasco Zoque": "zoq",
"Tabla": "tnm",
"Tabo": "knv",
"Tabriak": "tzx",
"Tacahua Mixtec": "xtt",
"Tacana": "tna",
"Tachawit": "shy",
"Tadaksahak": "dsq",
"Tadyawan": "tdy",
"Tae'": "rob",
"Tafi": "tcd",
"Tafreshi": "xme-taf",
"Tagabawa": "bgs",
"Tagakaulu Kalagan": "klg",
"Tagal Murut": "mvv",
"Tagalog": "tl",
"Tagbu": "tbm",
"Tagdal": "tda",
"Tagish": "tgx",
"Tagoi": "tag",
"Tagwana": "tgw",
"Tahitian": "ty",
"Tahltan": "tht",
"Tai": "taw",
"Tai Daeng": "tyr",
"Tai Dam": "blt",
"Tai Do": "tyj",
"Tai Dón": "twh",
"Tai Hang Tong": "thc",
"Tai Hongjin": "tiz",
"Tai Laing": "tjl",
"Tai Loi": "tlq",
"Tai Long": "thi",
"Tai Nüa": "tdd",
"Tai Pao": "tpo",
"Tai Thanh": "tmm",
"Tai Ya": "cuu",
"Taiap": "gpn",
"Taikat": "aos",
"Taimyr Pidgin Russian": "crp-tpr",
"Tainae": "ago",
"Tairuma": "uar",
"Taishanese": "zhx-tai",
"Taita": "dav",
"Taivoan": "tvx",
"Taiwan Sign Language": "tss",
"Taje": "pee",
"Tajik": "tg",
"Tajiki Arabic": "abh",
"Tajio": "tdj",
"Tajuasohn": "tja",
"Takelma": "tkm",
"Takia": "tbc",
"Takua": "tkz",
"Takuu": "nho",
"Takwane": "tke",
"Tal": "tal",
"Tala": "tak",
"Talaud": "tld",
"Taliabu": "tlv",
"Talieng": "tdf",
"Talinga-Bwisi": "tlj",
"Talise": "tlr",
"Tallán": "sai-tal",
"Talodi": "tlo",
"Taloki": "tlk",
"Talondo'": "tln",
"Talossan": "tzl",
"Talu": "yta",
"Talysh": "tly",
"Tama (Chad)": "tma",
"Tama (Colombia)": "ten",
"Tamagario": "tcg",
"Tamambo": "mla",
"Taman (Indonesia)": "tmn",
"Taman (Myanmar)": "tcl",
"Tamanaku": "tmz",
"Tamazola Mixtec": "vmx",
"Tambas": "tdk",
"Tambora": "xxt",
"Tambotalo": "tls",
"Tambunan Dusun": "kzt",
"Tami": "tmy",
"Tamil": "ta",
"Tamki": "tax",
"Tamnim Citak": "tml",
"Tampias Lobu": "low",
"Tampuan": "tpu",
"Tampulma": "tpm",
"Tanacross": "tcb",
"Tanahmerah": "tcm",
"Tanapag": "tpv",
"Tanchangya": "tnv",
"Tandaganon": "tgn",
"Tandia": "tni",
"Tanema": "tnx",
"Tangale": "tan",
"Tangam": "sit-tgm",
"Tanggu": "tgu",
"Tangkhul Naga": "nmf",
"Tangko": "tkx",
"Tangoa": "tgp",
"Tangsa": "nst",
"Tanguat": "tbs",
"Tangut": "txg",
"Tangwang": "mis-tnw",
"Tanimbili": "tbe",
"Tanimuca-Retuarã": "tnc",
"Tanjijili": "uji",
"Tanudan Kalinga": "kml",
"Tanzanian Sign Language": "tza",
"Taos": "twf",
"Tapachultec": "nai-tap",
"Taparita": "sai-tpr",
"Tapayuna": "sai-tap",
"Tapei": "afp",
"Tapieté": "tpj",
"Tapirapé": "taf",
"Tar Gula": "kcm",
"Tara Baka": "bdh",
"Tarairiú": "sai-trr",
"Tarantino": "roa-tar",
"Tarao": "tro",
"Taraon": "mhu",
"Tareng": "tgr",
"Tariana": "tae",
"Tarifit": "rif",
"Tarjumo": "txj",
"Tarok": "yer",
"Taroko": "trv",
"Tarpia": "tpf",
"Tartessian": "txr",
"Taruma": "tdm",
"Tasawaq": "twq",
"Tashelhit": "shi",
"Tasmate": "tmt",
"Tat": "ttt",
"Tataltepec Chatino": "cta",
"Tatana": "txx",
"Tatar": "tt",
"Tataviam": "azc-tat",
"Tatuyo": "tav",
"Tauade": "ttd",
"Taulil": "tuh",
"Taungyo": "tco",
"Taupota": "tpa",
"Tause": "tad",
"Taushiro": "trr",
"Tausug": "tsg",
"Tauya": "tya",
"Taveta": "tvs",
"Tavoyan": "tvn",
"Tavringer Romani": "rmu",
"Tawala": "tbo",
"Tawandê": "xtw",
"Tawang Monpa": "twm",
"Tawasa": "nai-taw",
"Taworta": "tbp",
"Tawoyan": "twy",
"Tawr Chin": "tcp",
"Tay Khang": "tnu",
"Taymanitic": "sem-tay",
"Tayo": "cks",
"Taíno": "tnq",
"Tboli": "tbl",
"Tchitchege": "tck",
"Tchumbuli": "bqa",
"Te'un": "tve",
"Teanu": "tkw",
"Tebul Sign Language": "tsy",
"Tebul Ure Dogon": "dtu",
"Tecpatlán Totonac": "tcw",
"Tedaga": "tuq",
"Tedim Chin": "ctd",
"Tee": "tkq",
"Tefaro": "tfo",
"Tegali": "ras",
"Tehit": "kps",
"Tehuelche": "teh",
"Teiwa": "twe",
"Tejalapan Zapotec": "ztt",
"Teke-Fuumu": "ifm",
"Teke-Kukuya": "kkw",
"Teke-Laali": "lli",
"Teke-Tege": "teg",
"Teke-Tsaayi": "tyi",
"Teke-Tyee": "tyx",
"Tektiteko": "ttc",
"Tela-Masbuar": "tvm",
"Telefol": "tlf",
"Telugu": "te",
"Teluti": "tlt",
"Tem": "kdh",
"Temascaltepec Nahuatl": "nhv",
"Tembé": "tqb",
"Teme": "tdo",
"Temein": "teq",
"Temi": "soz",
"Temiar": "tea",
"Temne": "tem",
"Temoaya Otomi": "ott",
"Temoq": "tmo",
"Tempasuk Dusun": "tdu",
"Ten'edn": "tnz",
"Tenango Otomi": "otn",
"Tene Kan Dogon": "dtk",
"Tenggarong Kutai Malay": "vkt",
"Tengger": "tes",
"Tenharim": "pah",
"Tenino": "tqn",
"Tenis": "tns",
"Tennet": "tex",
"Teochew": "nan-tws",
"Teojomulco Chatino": "omq-teo",
"Teop": "tio",
"Teor": "tev",
"Tepecano": "tep",
"Tepetotutla Chinantec": "cnt",
"Tepeuxila Cuicatec": "cux",
"Tepinapa Chinantec": "cte",
"Tepo Krumen": "ted",
"Teposcolula Mixtec": "omq-tel",
"Tequistlatec": "nai-teq",
"Ter Sami": "sjt",
"Tera": "ttr",
"Terebu": "trb",
"Terei": "buo",
"Terengganu Malay": "poz-ter",
"Tereno": "ter",
"Teressa": "tef",
"Tereweng": "twg",
"Teribe": "tfr",
"Terik": "tec",
"Termanu": "twu",
"Ternate": "tft",
"Ternateño": "tmg",
"Tese": "keg",
"Teshenawa": "twc",
"Tetela": "tll",
"Tetelcingo Nahuatl": "nhg",
"Tetete": "teb",
"Tetserret": "tez",
"Tetum": "tet",
"Tetun Dili": "tdt",
"Teushen": "sai-teu",
"Teutila Cuicatec": "cut",
"Tewa": "tew",
"Texcatepec Otomi": "otx",
"Texistepec Popoluca": "poq",
"Texmelucan Zapotec": "zpz",
"Tezoatlán Mixtec": "mxb",
"Tha": "thy",
"Thachanadan": "thn",
"Thado Chin": "tcz",
"Thai": "th",
"Thai Mon": "mnw-tha",
"Thai Sign Language": "tsq",
"Thai Song": "soa",
"Thaiphum Chin": "cth",
"Thakali": "ths",
"Thamudic": "sem-tha",
"Thangal Naga": "nki",
"Thangmi": "thf",
"Thao": "ssf",
"Tharaka": "thk",
"Tharrgari": "dhr",
"Thavung": "thm",
"Thawa": "xtv",
"Tho": "tou",
"Thompson": "thp",
"Thopho": "ytp",
"Thracian": "txh",
"Thu Lao": "tyl",
"Thulung": "tdh",
"Thurawal": "tbh",
"Thuri": "thu",
"Tiagba": "ahi",
"Tiale": "mnl",
"Tiang": "tbj",
"Tibea": "ngy",
"Tibetan": "bo",
"Tibetan Sign Language": "lsn",
"Ticuna": "tca",
"Tidaá Mixtec": "mtx",
"Tidore": "tvo",
"Tiemacèwè Bozo": "boo",
"Tiene": "tii",
"Tifal": "tif",
"Tigak": "tgc",
"Tigon Mbembe": "nza",
"Tigre": "tig",
"Tigrinya": "ti",
"Tii": "txq",
"Tijaltepec Mixtec": "xtl",
"Tikar": "tik",
"Tikopia": "tkp",
"Tilapa Otomi": "otl",
"Tillamook": "til",
"Tilquiapan Zapotec": "zts",
"Tilung": "tij",
"Tima": "tms",
"Timbe": "tim",
"Timor Pidgin": "tvy",
"Timote": "sai-tim",
"Timucua": "tjm",
"Timugon Murut": "tih",
"Tinani": "lbf",
"Tindi": "tin",
"Tingui-Boto": "tgv",
"Tinigua": "tit",
"Tinoc Kallahan": "tne",
"Tinputz": "tpz",
"Tinrin": "cir",
"Tipai": "nai-tip",
"Tippera": "tpe",
"Tira": "tic",
"Tirahi": "tra",
"Tiranige Diga Dogon": "tde",
"Tirax": "mme",
"Tiruray": "tiy",
"Tita": "tdq",
"Titan": "ttv",
"Tiv": "tiv",
"Tiwa": "lax",
"Tiwi": "tiw",
"Tiéfo": "tiq",
"Tiéyaxo Bozo": "boz",
"Tjurruru": "tju",
"Tlachichilco Tepehua": "tpt",
"Tlacoapa Me'phaa": "tpl",
"Tlacoatzintepec Chinantec": "ctl",
"Tlacolulita Zapotec": "zpk",
"Tlahuica": "ocu",
"Tlahuitoltepec Mixe": "mxp",
"Tlamacazapa Nahuatl": "nuz",
"Tlazoyaltepec Mixtec": "mqh",
"Tlingit": "tli",
"To": "toz",
"To'abaita": "mlu",
"Toaripi": "tqo",
"Toba": "tob",
"Toba Batak": "bbc",
"Toba-Maskoy": "tmf",
"Tobagonian Creole English": "tgh",
"Tobanga": "tng",
"Tobati": "tti",
"Tobelo": "tlb",
"Tobian": "tox",
"Tobilung": "tgb",
"Tobo": "tbv",
"Tocantins Asurini": "asu",
"Tocharian A": "xto",
"Tocharian B": "txb",
"Tocho": "taz",
"Toda": "tcx",
"Todrah": "tdr",
"Tofa": "kim",
"Tofanma": "tlg",
"Tofin Gbe": "tfi",
"Togbo-Vara Banda": "tor",
"Togoyo": "tgy",
"Tojolabal": "toj",
"Tok Pisin": "tpi",
"Toka-Leya": "dov",
"Tokano": "zuh",
"Tokelauan": "tkl",
"Toki Pona": "tok",
"Tokunoshima": "tkn",
"Tol": "jic",
"Tolai": "ksd",
"Tolaki": "lbw",
"Tolomako": "tlm",
"Tolowa": "tol",
"Toloza": "ytl",
"Toma": "tod",
"Tomadino": "tdi",
"Tombelala": "ttp",
"Tombonuo": "txa",
"Tombulu": "tom",
"Tomini": "txm",
"Tommeginne": "xpv",
"Tommo So": "dto",
"Tomo Kan Dogon": "dtm",
"Tomoip": "tqp",
"Tondano": "tdn",
"Tonga (Malawi)": "tog",
"Tonga (Mozambique)": "toh",
"Tonga (Zambia)": "toi",
"Tongan": "to",
"Tongwe": "tny",
"Tonjon": "tjn",
"Tonkawa": "tqw",
"Tonsawang": "tnw",
"Tonsea": "txs",
"Tontemboan": "tnt",
"Toogee": "xpx",
"Tooro": "ttj",
"Topoiyo": "toy",
"Toposa": "toq",
"Toraja-Sa'dan": "sda",
"Toram": "trj",
"Torau": "ttu",
"Toro": "tdv",
"Toro So Dogon": "dts",
"Toro Tegu Dogon": "dtt",
"Toromono": "tno",
"Torona": "tqr",
"Torres Strait Creole": "tcs",
"Torricelli": "tei",
"Torwali": "trw",
"Torá": "trz",
"Tosu": "sit-tos",
"Totela": "ttl",
"Toto": "txo",
"Totoli": "txe",
"Totomachapan Zapotec": "zph",
"Totontepec Mixe": "mto",
"Totoro": "ttk",
"Touo": "tqu",
"Toura": "neb",
"Towei": "ttn",
"Translingual": "mul",
"Transylvanian Saxon": "gmw-tsx",
"Traveller Danish": "rmd",
"Traveller Norwegian": "rmg",
"Traveller Scottish": "trl",
"Tregami": "trm",
"Tremembé": "tme",
"Trieng": "stg",
"Trimuris": "tip",
"Tring": "tgq",
"Tringgus": "trx",
"Trinidad and Tobago Sign Language": "lst",
"Trinidadian Creole English": "trf",
"Trinitario": "trn",
"Trió": "tri",
"Truká": "tka",
"Trumai": "tpy",
"Ts'ün-Lao": "tsl",
"Tsaangi": "tsa",
"Tsafiki": "cof",
"Tsakhur": "tkr",
"Tsakonian": "tsd",
"Tsakwambo": "kvz",
"Tsamai": "tsb",
"Tsat": "huq",
"Tsetsaut": "txc",
"Tsez": "ddo",
"Tshangla": "tsj",
"Tshobdun": "sit-tsh",
"Tshwa": "hio",
"Tsikimba": "kdl",
"Tsimané": "cas",
"Tsimshian": "tsi",
"Tsishingini": "tsw",
"Tso": "ldp",
"Tsogo": "tsv",
"Tsonga": "ts",
"Tsotsitaal": "fly",
"Tsou": "tsu",
"Tsucuba": "cbq",
"Tsum": "ttz",
"Tsuut'ina": "srs",
"Tsuvadi": "tvd",
"Tsuvan": "tsh",
"Tswa": "tsc",
"Tswana": "tn",
"Tswapong": "two",
"Tuamotuan": "pmt",
"Tuareg": "tmh",
"Tubar": "tbu",
"Tucano": "tuo",
"Tugen": "tuy",
"Tugun": "tzn",
"Tugutil": "tuj",
"Tuki": "bag",
"Tukpa": "tpq",
"Tukudede": "tkd",
"Tukumanféd": "tkf",
"Tula": "tul",
"Tule-Kaweah Yokuts": "yok-tky",
"Tulehu": "tlu",
"Tulishi": "tey",
"Tulu": "tcy",
"Tulu-Bohuai": "rak",
"Tulua": "aus-tul",
"Tuma-Irumu": "iou",
"Tumak": "tmc",
"Tumbuka": "tum",
"Tumi": "kku",
"Tumleo": "tmq",
"Tumshuqese": "xtq",
"Tumtum": "tbr",
"Tumulung Sisaala": "sil",
"Tundra Enets": "enh",
"Tundra Nenets": "yrk-tun",
"Tunen": "tvu",
"Tungag": "lcm",
"Tunggare": "trt",
"Tunia": "tug",
"Tunica": "tun",
"Tunisian Arabic": "aeb",
"Tunisian Berber": "sds",
"Tunisian Sign Language": "tse",
"Tunjung": "tjg",
"Tunni": "tqq",
"Tunumiisut": "esx-tut",
"Tunzu": "dza",
"Tuoba": "mis-tuo",
"Tuotomb": "ttf",
"Tuparí": "tpr",
"Tupinambá": "tpn",
"Tupinikin": "tpk",
"Tupuri": "tui",
"Turaka": "trh",
"Turdetanian": "mis-tdt",
"Turdulian": "mis-tdl",
"Turi": "trd",
"Turiwára": "twt",
"Turka": "tuz",
"Turkana": "tuv",
"Turkish": "tr",
"Turkish Sign Language": "tsm",
"Turkmen": "tk",
"Turks and Caicos Creole English": "tch",
"Turoyo": "tru",
"Turumsa": "tqm",
"Turung": "try",
"Tuscarora": "tus",
"Tutelo": "tta",
"Tutong": "ttg",
"Tutsa Naga": "tvt",
"Tutuba": "tmi",
"Tututepec Mixtec": "mtu",
"Tututni": "tuu",
"Tuvaluan": "tvl",
"Tuvan": "tyv",
"Tuwali Ifugao": "ifk",
"Tuwari": "tww",
"Tuwuli": "bov",
"Tuxináwa": "tux",
"Tuxá": "tud",
"Tuyuca": "tue",
"Tuyuhun": "mis-tuh",
"Twana": "twa",
"Twendi": "twn",
"Tyap": "kcg",
"Tyaraity": "woa",
"Tyerrernotepanner": "xph",
"Tz'utujil": "tzj",
"Tzeltal": "tzh",
"Tzotzil": "tzo",
"Tày": "tyz",
"Tày Tac": "tyt",
"Tây Bồi": "tas",
"Téén": "lor",
"Tübatulabal": "tub",
"U": "uuu",
"Uab Meto": "aoz",
"Uamué": "uam",
"Uare": "ksj",
"Ubaghara": "byc",
"Ubang": "uba",
"Ubi": "ubi",
"Ubir": "ubr",
"Ubykh": "uby",
"Ucayali-Yurúa Ashéninka": "cpb",
"Uda": "uda",
"Udi": "udi",
"Udihe": "ude",
"Udmurt": "udm",
"Uduk": "udu",
"Ufim": "ufi",
"Ugandan Sign Language": "ugn",
"Ugaritic": "uga",
"Ughele": "uge",
"Uhami": "uha",
"Uisai": "uis",
"Ujir": "udj",
"Ukaan": "kcf",
"Ukhwejo": "ukh",
"Ukit": "umi",
"Ukpe-Bayobiri": "ukp",
"Ukpet-Ehom": "akd",
"Ukrainian": "uk",
"Ukrainian Sign Language": "ukl",
"Ukue": "uku",
"Ukuriguma": "ukg",
"Ukwa": "ukq",
"Ukwuani-Aboh-Ndoni": "ukw",
"Ulau-Suain": "svb",
"Ulch": "ulc",
"Uldeme": "udl",
"Ulithian": "uli",
"Ullatan": "ull",
"Ulumanda'": "ulm",
"Ulwa (New Guinea)": "yla",
"Ulwa (Nicaragua)": "ulw",
"Uma": "ppk",
"Uma' Lasan": "xky",
"Uma' Lung": "ulu",
"Umanakaina": "gdn",
"Umatilla": "uma",
"Umbindhamu": "umd",
"Umbrian": "xum",
"Umbu-Ungu": "ubu",
"Umbugarla": "umr",
"Umbundu": "umb",
"Umbuygamu": "umg",
"Ume Sami": "sju",
"Umeda": "upi",
"Umiida": "xud",
"Umiray Dumaget Agta": "due",
"Umon": "umm",
"Umotína": "umo",
"Umpila": "ump",
"Una": "mtg",
"Unami": "unm",
"Unde Kaili": "unz",
"Undetermined": "und",
"Uneapa": "bbn",
"Uneme": "une",
"Unggaranggu": "xun",
"Unggumi": "xgu",
"Uni": "uni",
"Unserdeutsch": "uln",
"Unua": "onu",
"Unubahe": "unu",
"Uokha": "uok",
"Upper Chehalis": "cjh",
"Upper Grand Valley Dani": "dna",
"Upper Kinabatangan": "dmg",
"Upper Kuskokwim": "kuu",
"Upper Mandobo": "aax",
"Upper Necaxa Totonac": "tku",
"Upper Sorbian": "hsb",
"Upper Ta'oih": "tth",
"Upper Tanana": "tau",
"Upper Taromi": "tov",
"Upper Umpqua": "xup",
"Ura (New Guinea)": "uro",
"Ura (Vanuatu)": "uur",
"Uradhi": "urf",
"Urak Lawoi'": "urk",
"Urali": "url",
"Urapmin": "urm",
"Urarina": "ura",
"Urartian": "xur",
"Urat": "urt",
"Urdu": "ur",
"Urhobo": "urh",
"Uri": "uvh",
"Urigina": "urg",
"Urim": "uri",
"Urimo": "urx",
"Urningangg": "urc",
"Uru": "ure",
"Uru-Eu-Wau-Wau": "urz",
"Uru-Pa-In": "urp",
"Uruangnirin": "urn",
"Uruava": "urv",
"Urubú-Kaapor": "urb",
"Uruguayan Sign Language": "ugy",
"Urum": "uum",
"Urumi": "uru",
"Usaghade": "usk",
"Usan": "wnu",
"Usarufa": "usa",
"Ushojo": "ush",
"Usila Chinantec": "cuc",
"Uspanteco": "usp",
"Usui": "usi",
"Utarmbung": "omo",
"Ute": "ute",
"Utu": "utu",
"Uvbie": "evh",
"Uwinymil": "aus-uwi",
"Uya": "usu",
"Uyajitaya": "duk",
"Uyghur": "ug",
"Uzbek": "uz",
"Uzbeki Arabic": "auz",
"Uzekwe": "eze",
"Vaagri Booli": "vaa",
"Vaghri": "vgr",
"Vaghua": "tva",
"Vagla": "vag",
"Vai": "vai",
"Vaiphei": "vap",
"Vale": "vae",
"Valencian Sign Language": "vsv",
"Valle Nacional Chinantec": "cvn",
"Valley Maidu": "vmv",
"Valman": "van",
"Valpei": "vlp",
"Vamale": "mkt",
"Vame": "mlr",
"Vandalic": "xvn",
"Vangunu": "mpr",
"Vanimo": "vam",
"Vanji": "ira-wnj",
"Vanuma": "vau",
"Vao": "vao",
"Varhadi": "vah",
"Varisi": "vrs",
"Varli": "vav",
"Vasavi": "vas",
"Vayu": "vay",
"Veddah": "ved",
"Vehes": "val",
"Vemgo-Mabas": "vem",
"Venda": "ve",
"Venetan": "vec",
"Venetic": "xve",
"Venezuelan Sign Language": "vsl",
"Ventureño": "veo",
"Veps": "vep",
"Vera'a": "vra",
"Vestinian": "xvs",
"Vidunda": "vid",
"Viemo": "vig",
"Vietnamese": "vi",
"Vilamovian": "wym",
"Vilela": "vil",
"Vili": "vif",
"Vincentian Creole English": "svc",
"Vinitiri": "vmg",
"Virgin Islands Creole": "vic",
"Vishavan": "vis",
"Viti": "vit",
"Vitou": "vto",
"Viya": "gev",
"Vlax Romani": "rmy",
"Volapük": "vo",
"Volga German": "gmw-vog",
"Volscian": "xvo",
"Vono": "kch",
"Voro": "vor",
"Votic": "vot",
"Vumbu": "vum",
"Vunapu": "vnp",
"Vunjo": "vun",
"Vurës": "msn",
"Vute": "vut",
"Võro": "vro",
"Wa": "wbm",
"Wa'ema": "wag",
"Waama": "wwa",
"Waamwang": "wmn",
"Wab": "wab",
"Wabo": "wbb",
"Waboda": "kmx",
"Waci Gbe": "wci",
"Wadaginam": "wdg",
"Waddar": "wbq",
"Wadi Wadi": "xwd",
"Wadiyara Koli": "kxp",
"Wadjabangayi": "wdy",
"Wadjiginy": "wdj",
"Wadjigu": "wdu",
"Wae Rana": "wrx",
"Waffa": "waj",
"Wagawaga": "wgb",
"Wagaya": "wga",
"Wagdi": "wbr",
"Wageman": "waq",
"Wagi": "fad",
"Wahau Kayan": "whu",
"Wahau Kenyah": "whk",
"Wahgi": "wgi",
"Waigali": "wbk",
"Waigeo": "wgo",
"Waikuri": "nai-wai",
"Wailaki": "wlk",
"Wailapa": "wlr",
"Waima'a": "wmh",
"Waimaha": "bao",
"Waimiri-Atroari": "atr",
"Wainumá": "awd-wai",
"Waioli": "wli",
"Waitaká": "sai-wai",
"Waiwai": "waw",
"Waja": "wja",
"Wajarri": "wbv",
"Wajuk": "xwj",
"Waka": "wav",
"Wakawaka": "wkw",
"Wakhi": "wbl",
"Wakoná": "waf",
"Wala": "lgl",
"Walak": "wlw",
"Walangama": "nlw",
"Wali (Ghana)": "wlx",
"Wali (Sudan)": "wll",
"Waling": "wly",
"Walio": "wla",
"Walla Walla": "waa",
"Wallisian": "wls",
"Walloon": "wa",
"Walmajarri": "wmt",
"Wam": "wmo",
"Wamas": "wmc",
"Wambaya": "wmb",
"Wambon": "wms",
"Wambule": "wme",
"Wamey": "cou",
"Wamin": "wmi",
"Wampar": "lbq",
"Wampur": "waz",
"Wan": "wan",
"Wanap": "wnp",
"Wancho": "nnp",
"Wanda": "wbh",
"Wandala": "mfi",
"Wandamen": "wad",
"Wandarang": "wnd",
"Wandji": "wdd",
"Waneci": "wne",
"Wanga": "lwg",
"Wanggamala": "wnm",
"Wangganguru": "wgg",
"Wanggom": "wng",
"Wangkayutyuru": "wky",
"Wangkumara": "xwk",
"Wanham": "sai-wnm",
"Wanji": "wbi",
"Wanman": "wbt",
"Wannu": "jub",
"Wano": "wno",
"Wantoat": "wnc",
"Wanukaka": "wnk",
"Wanyi": "wny",
"Wané": "hwa",
"Wapan": "juk",
"Wapishana": "wap",
"Wappo": "wao",
"War-Jaintia": "aml",
"Wara": "wbf",
"Warao": "wba",
"Warapu": "wra",
"Waray Sorsogon": "srv",
"Waray-Waray": "war",
"Wardaman": "wrr",
"Wardandi": "wxw",
"Warekena": "gae",
"Warembori": "wsa",
"Wari'": "pav",
"Waris": "wrs",
"Waritai": "wbe",
"Wariyangga": "wri",
"Warji": "wji",
"Warkay-Bipim": "bgv",
"Warlmanpa": "wrl",
"Warlpiri": "wbp",
"Warluwara": "wrb",
"Warnang": "wrn",
"Waropen": "wrp",
"Warray": "wrz",
"Warrgamay": "wgy",
"Warrwa": "wwr",
"Waru": "wru",
"Warumungu": "wrm",
"Waruna": "wrv",
"Warungu": "wrg",
"Warwar Feni": "hrw",
"Wasco-Wishram": "wac",
"Wasembo": "gsp",
"Washo": "was",
"Waskia": "wsk",
"Wastek": "hus",
"Wasu": "wsu",
"Watakataui": "wtk",
"Watam": "wax",
"Wathaurong": "wth",
"Watiwa": "wtf",
"Watubela": "wah",
"Waube": "kop",
"Wauja": "wau",
"Wauyai": "wuy",
"Wawa": "www",
"Wawonii": "wow",
"Waxiang": "wxa",
"Wayampi": "oym",
"Wayana": "way",
"Wayanad Chetti": "ctt",
"Wayoró": "wyr",
"Wayumara": "sai-way",
"Wayuu": "guc",
"Wedau": "wed",
"Weh": "weh",
"Welaung": "weu",
"Weliki": "klh",
"Welsh": "cy",
"Welsh Romani": "rmw",
"Wemale": "weo",
"Wemba-Wemba": "xww",
"Weme Gbe": "wem",
"Wendat": "wdt",
"Weri": "wer",
"Wersing": "kvw",
"West Ambae": "nnd",
"West Central Banda": "bbp",
"West Circassian": "ady",
"West Coast Bajau": "bdr",
"West Damar": "drn",
"West Flemish": "vls",
"West Frisian": "fy",
"West Greenlandic Pidgin": "crp-gep",
"West Lembata": "lmj",
"West Makian": "mqs",
"West Masela": "mss",
"West Miraya Bikol": "fbl",
"West Tarangan": "txn",
"West Uvean": "uve",
"West-Central Limba": "lia",
"Western Apache": "apw",
"Western Arrernte": "are",
"Western Bolivian Guarani": "gnw",
"Western Bru": "brv",
"Western Bukidnon Manobo": "mbb",
"Western Cham": "cja",
"Western Dani": "dnw",
"Western Durango Nahuatl": "azn",
"Western Fijian": "wyy",
"Western Highland Chatino": "ctp",
"Western Huasteca Nahuatl": "nhw",
"Western Jicaque": "nai-wji",
"Western Juxtlahuaca Mixtec": "jmx",
"Western Karaboro": "kza",
"Western Katu": "kuf",
"Western Kayah": "kyu",
"Western Keres": "kjq",
"Western Krahn": "krw",
"Western Lalu": "ywl",
"Western Lawa": "lcp",
"Western Magar": "mrd",
"Western Maninkakan": "mlq",
"Western Mari": "mrj",
"Western Mashan Hmong": "hmw",
"Western Meohang": "raf",
"Western Muria": "mut",
"Western Neo-Aramaic": "amw",
"Western Ojibwa": "ojw",
"Western Parbate Kham": "kjl",
"Western Penan": "pne",
"Western Pwo": "pwo",
"Western Sisaala": "ssl",
"Western Subanon": "suc",
"Western Tamang": "tdg",
"Western Tawbuid": "twb",
"Western Totonac": "tqt",
"Western Tunebo": "tnb",
"Western Xiangxi Miao": "mmr",
"Western Xwla Gbe": "xwl",
"Western Yugur": "ybe",
"Wewaw": "wea",
"Weyewa": "wew",
"Weyto": "woy",
"White Gelao": "giw",
"White Hmong": "mww",
"White Lachi": "lwh",
"Whitesands": "tnp",
"Wiarumus": "tua",
"Wichita": "wic",
"Wichí Lhamtés Güisnay": "mzh",
"Wichí Lhamtés Nocten": "mtp",
"Wichí Lhamtés Vejoz": "wlv",
"Wik-Epa": "wie",
"Wik-Iiyanh": "wij",
"Wik-Keyangan": "wif",
"Wik-Me'anha": "wih",
"Wik-Mungkan": "wim",
"Wik-Ngathana": "wig",
"Wikalkan": "wik",
"Wikngenchera": "wua",
"Wilawila": "wil",
"Winnebago": "win",
"Wintu": "wnw",
"Winyé": "kst",
"Wipi": "gdr",
"Wiradjuri": "wrh",
"Wiraféd": "wir",
"Wirangu": "wgu",
"Wiru": "wiu",
"Wirö": "wpc",
"Wiwa": "mbp",
"Wiyot": "wiy",
"Wobé": "wob",
"Woccon": "xwc",
"Wogamusin": "wog",
"Wogeo": "woc",
"Woi": "wbw",
"Woiwurrung": "wyi",
"Wojenaka": "jod",
"Wolane": "wle",
"Wolani": "wod",
"Wolaytta": "wal",
"Woleaian": "woe",
"Wolio": "wlo",
"Wolof": "wo",
"Womo": "wmx",
"Wong-gie": "aus-won",
"Wongo": "won",
"Woods Cree": "cwd",
"Woria": "wor",
"Worimi": "kda",
"Worodougou": "jud",
"Worora": "wro",
"Wotapuri-Katarqalai": "wsv",
"Wotu": "wtw",
"Woun Meu": "noa",
"Written Oirat": "xwo",
"Wu": "wuu",
"Wudu": "wud",
"Wuhuan": "mis-wuh",
"Wulguru": "aus-wul",
"Wuliwuli": "wlu",
"Wulna": "wux",
"Wumboko": "bqm",
"Wumbvu": "wum",
"Wumeng": "ywu",
"Wunai Bunu": "bwn",
"Wunambal": "wub",
"Wurrugu": "wur",
"Wusa": "yig",
"Wushi": "bse",
"Wusi": "wsi",
"Wutung": "wut",
"Wutunhua": "wuh",
"Wuvulu-Aua": "wuv",
"Wyandot": "wya",
"Wára": "tci",
"Wãpha": "juw",
"Wè Southern": "gxx",
"Wè Western": "wec",
"Xadani Zapotec": "zax",
"Xakriabá": "xkr",
"Xamtanga": "xan",
"Xanaguía Zapotec": "ztg",
"Xavante": "xav",
"Xerénte": "xer",
"Xetá": "xet",
"Xhosa": "xh",
"Xianbei": "mis-xbi",
"Xiang": "hsn",
"Xibe": "sjo",
"Xicotepec de Juárez Totonac": "too",
"Xinca": "xin",
"Xingú Asuriní": "asn",
"Xiongnu": "mis-xnu",
"Xipaya": "xiy",
"Xiri": "xii",
"Xiriâna": "xir",
"Xishanba Lalo": "ywt",
"Xocó": "sai-xoc",
"Xokleng": "xok",
"Xukurú": "xoo",
"Xwela Gbe": "xwe",
"Xârâcùù": "ane",
"Xârâgurè": "axx",
"Yaa": "iyx",
"Yaaku": "muu",
"Yabarana": "yar",
"Yabaâna": "ybn",
"Yaben": "ybm",
"Yabong": "ybo",
"Yabula Yabula": "yxy",
"Yace": "ekr",
"Yaeyama": "rys",
"Yafi": "wfg",
"Yagara": "yxg",
"Yagaria": "ygr",
"Yaghnobi": "yai",
"Yagomi": "ygm",
"Yagua": "yad",
"Yagwoia": "ygw",
"Yahadian": "ner",
"Yahang": "rhp",
"Yahuna": "ynu",
"Yaka": "yaf",
"Yakaikeke": "ykk",
"Yakan": "yka",
"Yakima": "yak",
"Yakkha": "ybh",
"Yakoma": "yky",
"Yakut": "sah",
"Yala": "yba",
"Yalahatan": "jal",
"Yalakalore": "xyl",
"Yalarnnga": "ylr",
"Yale": "nce",
"Yaleba": "ylb",
"Yalunka": "yal",
"Yalálag Zapotec": "zpu",
"Yamap": "ymp",
"Yamba": "yam",
"Yamben": "ynb",
"Yambes": "ymb",
"Yambeta": "yat",
"Yamdena": "jmd",
"Yameo": "yme",
"Yami": "tao",
"Yaminahua": "yaa",
"Yamongeri": "ymg",
"Yamphu": "ybi",
"Yan-nhangu": "jay",
"Yana": "ynn",
"Yanda": "yda",
"Yanda Dogon": "dym",
"Yandjibara": "xyb",
"Yandruwandha": "ynd",
"Yanesha'": "ame",
"Yangben": "yav",
"Yangkaal": "aus-ynk",
"Yangkam": "bsx",
"Yangman": "jng",
"Yango": "yng",
"Yangulam": "ynl",
"Yangum Dey": "yde",
"Yangum Gel": "ygl",
"Yangum Mon": "ymo",
"Yankunytjatjara": "kdd",
"Yanomam": "wca",
"Yanomamö": "guu",
"Yansi": "yns",
"Yanyuwa": "jao",
"Yao (Africa)": "yao",
"Yao (South America)": "sai-yao",
"Yaosakor Asmat": "asy",
"Yaouré": "yre",
"Yapese": "yap",
"Yaqay": "jaq",
"Yaqui": "yaq",
"Yarawata": "yrw",
"Yareba": "yrb",
"Yareni Zapotec": "zae",
"Yarli": "yxl",
"Yarluyandi": "yry",
"Yarumá": "sai-yar",
"Yarí": "yri",
"Yasa": "yko",
"Yatay": "yty",
"Yatee Zapotec": "zty",
"Yatzachi Zapotec": "zav",
"Yau (Finisterre)": "yuw",
"Yau (Torricelli)": "yyu",
"Yaur": "jau",
"Yautepec Zapotec": "zpb",
"Yavitero": "yvt",
"Yawa": "yva",
"Yawalapití": "yaw",
"Yawanawa": "ywn",
"Yawarawarga": "yww",
"Yaweyuha": "yby",
"Yawijibaya": "jbw",
"Yawiyo": "ybx",
"Yawuru": "ywr",
"Yaygir": "xya",
"Yazghulami": "yah",
"Ye'kwana": "mch",
"Yei": "jei",
"Yekhee": "ets",
"Yekora": "ykr",
"Yele": "yle",
"Yelmek": "jel",
"Yelogu": "ylg",
"Yemaek": "hmk",
"Yemba": "ybb",
"Yemeni Arabic": "ayn",
"Yemsa": "jnj",
"Yendang": "yen",
"Yeni": "yei",
"Yenish": "yec",
"Yerakai": "yra",
"Yeretuar": "gop",
"Yeri": "yev",
"Yerong": "yrn",
"Yerukula": "yeu",
"Yeskwa": "yes",
"Yessan-Mayo": "yss",
"Yetfa": "yet",
"Yevanic": "yej",
"Yeyi": "yey",
"Yiddish": "yi",
"Yidgha": "ydg",
"Yidiny": "yii",
"Yil": "yll",
"Yilan Creole": "ycr",
"Yimas": "yee",
"Yimchungru Naga": "yim",
"Yinbaw Karen": "kvu",
"Yinchia": "yin",
"Yindjibarndi": "yij",
"Yindjilandji": "yil",
"Yine": "pib",
"Yinggarda": "yia",
"Yinhawangka": "ywg",
"Yiningayi": "ygi",
"Yintale Karen": "kvy",
"Yinwum": "yxm",
"Yir-Yoront": "yiy",
"Yirandali": "ljw",
"Yis": "yis",
"Yitha Yitha": "xth",
"Yoba": "yob",
"Yocoboué Dida": "gud",
"Yogad": "yog",
"Yoidik": "ydk",
"Yoke": "yki",
"Yola": "yol",
"Yolmo": "scp",
"Yolngu Sign Language": "ygs",
"Yoloxochitl Mixtec": "xty",
"Yom": "pil",
"Yombe": "yom",
"Yonaguni": "yoi",
"Yong": "yno",
"Yongkom": "yon",
"Yopno": "yut",
"Yora": "mts",
"Yoron": "yox",
"Yorta Yorta": "xyy",
"Yoruba": "yo",
"Yosondúa Mixtec": "mpm",
"Youle Jino": "jiu",
"Younuo Bunu": "buh",
"Yout Wam": "ytw",
"Yoy": "yoy",
"Yuanga": "nua",
"Yucatec Maya": "yua",
"Yucatec Maya Sign Language": "msd",
"Yuchi": "yuc",
"Yucuañe Mixtec": "mvg",
"Yucuna": "ycn",
"Yug": "yug",
"Yugambal": "yub",
"Yugoslavian Sign Language": "ysl",
"Yugul": "ygu",
"Yuhup": "yab",
"Yuki": "yuk",
"Yukpa": "yup",
"Yukuben": "ybl",
"Yulu": "yul",
"Yuma": "yum",
"Yumana": "awd-yum",
"Yup'ik": "esu",
"Yupiltepeque": "nai-yup",
"Yupua": "sai-yup",
"Yuqui": "yuq",
"Yuracare": "yuz",
"Yuri": "sai-yri",
"Yurok": "yur",
"Yuru": "ljx",
"Yurumanguí": "sai-yur",
"Yurutí": "yui",
"Yutanduchi Mixtec": "mab",
"Yuwana": "yau",
"Yuyu": "yxu",
"Yámana": "yag",
"Zaachila Zapotec": "ztx",
"Zabana": "kji",
"Zacatepec Chatino": "ctz",
"Zacatlán-Ahuacatlán-Tepetzintla Nahuatl": "nhi",
"Zaghawa": "zag",
"Zaiwa": "atb",
"Zakhring": "zkr",
"Zambian Sign Language": "zsl",
"Zan Gula": "zna",
"Zanaki": "zak",
"Zande": "zne",
"Zangskari": "zau",
"Zangwal": "zah",
"Zaniza Zapotec": "zpw",
"Zapotec": "zap",
"Zaramo": "zaj",
"Zari": "zaz",
"Zarma": "dje",
"Zauzou": "zal",
"Zay": "zwa",
"Zayein Karen": "kxk",
"Zayse-Zergulla": "zay",
"Zazaki": "zza",
"Zazao": "jaj",
"Zbu": "sit-zbu",
"Zealandic": "zea",
"Zeem": "zua",
"Zemba": "dhm",
"Zeme Naga": "nzm",
"Zenag": "zeg",
"Zenaga": "zen",
"Zenzontepec Chatino": "czn",
"Zhaba": "zhb",
"Zhang-Zhung": "xzh",
"Zhenan Min": "nan-zhe",
"Zhire": "zhi",
"Zhoa": "zhw",
"Zhuang": "za",
"Zhár": "jjr",
"Zia": "zia",
"Zialo": "zil",
"Zigula": "ziw",
"Zimakani": "zik",
"Zimba": "zmb",
"Zimbabwe Sign Language": "zib",
"Zinza": "zin",
"Zipser German": "gmw-zps",
"Zirenkel": "zrn",
"Ziriya": "zir",
"Zizilivakan": "ziz",
"Zo'é": "pto",
"Zokhuo": "yzk",
"Zoogocho Zapotec": "zpq",
"Zotung Chin": "czt",
"Zou": "zom",
"Zulgo-Gemzek": "gnd",
"Zulu": "zu",
"Zumaya": "zuy",
"Zumbun": "jmb",
"Zuni": "zun",
"Zuojiang Zhuang": "zzj",
"Zuwara": "ber-zuw",
"Zyphe": "zyp",
"Záparo": "zro",
"Àhàn": "ahn",
"Áncá": "acb",
"Äiwoo": "nfl",
"Äynu": "aib",
"Ömie": "aom",
"Önge": "oon",
"ǀXam": "xam",
"ǁAni": "hnh",
"ǁGana": "gnk",
"ǁXegwi": "xeg",
"ǂHoan": "huc",
"ǃKung": "khi-kun",
"ǃXóõ": "nmn",
"Ỹaroamë": "yro"
}
78ssl5g8cxn19hwb1vhkx7zznsf586i
မဳဒဳယာဝဳကဳ:Gadget-linkLanguageHeaders.js/documentation
8
295697
396515
2026-06-07T15:01:28Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "{{documentation subpage}} See also: [[Special:Gadgets]]. <includeonly> [[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]] </includeonly>"
396515
wikitext
text/x-wiki
{{documentation subpage}}
See also: [[Special:Gadgets]].
<includeonly>
[[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]]
</includeonly>
g0pto8y4htz6e0eccg6fcqtlxv07sce
မဝ်ဂျူ:languages/code to canonical name.json
828
295698
396516
2026-06-07T15:18:47Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "{ "aa": "Afar", "aaa": "Ghotuo", "aab": "Alumu-Tesu", "aac": "Ari", "aad": "Amal", "aaf": "Aranadan", "aag": "Ambrak", "aah": "Abu'", "aai": "Arifama-Miniafia", "aak": "Ankave", "aal": "Afade", "aan": "Anambé", "aap": "Arára (Pará)", "aaq": "Penobscot", "aas": "Aasax", "aau": "Abau", "aav-khs-pro": "Proto-Khasian", "aav-nic-pro": "Proto-Nicobarese", "aav-pkl-pro": "Proto-Pnar-Khasi-Lyngngam", "a..."
396516
json
application/json
{
"aa": "Afar",
"aaa": "Ghotuo",
"aab": "Alumu-Tesu",
"aac": "Ari",
"aad": "Amal",
"aaf": "Aranadan",
"aag": "Ambrak",
"aah": "Abu'",
"aai": "Arifama-Miniafia",
"aak": "Ankave",
"aal": "Afade",
"aan": "Anambé",
"aap": "Arára (Pará)",
"aaq": "Penobscot",
"aas": "Aasax",
"aau": "Abau",
"aav-khs-pro": "Proto-Khasian",
"aav-nic-pro": "Proto-Nicobarese",
"aav-pkl-pro": "Proto-Pnar-Khasi-Lyngngam",
"aav-pro": "Proto-Austroasiatic",
"aaw": "Solong",
"aax": "Upper Mandobo",
"aaz": "Amarasi",
"ab": "Abkhaz",
"aba": "Abé",
"abb": "Bankon",
"abc": "Ambala Ayta",
"abd": "Camarines Norte Agta",
"abe": "Abenaki",
"abf": "Abai Sungai",
"abg": "Abaga",
"abh": "Tajiki Arabic",
"abi": "Abidji",
"abj": "Aka-Bea",
"abl": "Abung",
"abm": "Abanyom",
"abn": "Abua",
"abo": "Abon",
"abp": "Abenlen Ayta",
"abq": "Abaza",
"abs": "Ambonese Malay",
"abt": "Ambulas",
"abu": "Abure",
"abv": "Baharna Arabic",
"abw": "Pal",
"abx": "Inabaknon",
"aby": "Aneme Wake",
"abz": "Abui",
"aca": "Achagua",
"acb": "Áncá",
"acd": "Gikyode",
"ace": "Acehnese",
"ach": "Acholi",
"aci": "Aka-Cari",
"ack": "Aka-Kora",
"acl": "Akar-Bale",
"acm": "Iraqi Arabic",
"acn": "Achang",
"acp": "Eastern Acipa",
"acr": "Achi",
"acs": "Acroá",
"acu": "Achuar",
"acv": "Achumawi",
"acw": "Hijazi Arabic",
"acx": "Omani Arabic",
"acy": "Cypriot Arabic",
"acz": "Acheron",
"ada": "Adangme",
"adb": "Atauran",
"add": "Dzodinka",
"ade": "Adele",
"adf": "Dhofari Arabic",
"adg": "Andegerebinha",
"adh": "Adhola",
"adi": "Adi",
"adj": "Adioukrou",
"adl": "Galo",
"adn": "Adang",
"ado": "Abu",
"adp": "Adap",
"adq": "Adangbe",
"adr": "Adonara",
"ads": "Adamorobe Sign Language",
"adt": "Adnyamathanha",
"adu": "Aduge",
"adw": "Amondawa",
"ady": "West Circassian",
"adz": "Adzera",
"ae": "Avestan",
"aea": "Areba",
"aeb": "Tunisian Arabic",
"aed": "Argentine Sign Language",
"aee": "Northeast Pashayi",
"aek": "Haeke",
"ael": "Ambele",
"aem": "Arem",
"aen": "Armenian Sign Language",
"aeq": "Aer",
"aer": "Eastern Arrernte",
"aes": "Alsea",
"aeu": "Akeu",
"aew": "Ambakich",
"aey": "Amele",
"aez": "Aeka",
"af": "Afrikaans",
"afa-pro": "Proto-Afroasiatic",
"afb": "Gulf Arabic",
"afd": "Andai",
"afe": "Putukwam",
"afg": "Afghan Sign Language",
"afh": "Afrihili",
"afi": "Akrukay",
"afk": "Nanubae",
"afn": "Defaka",
"afo": "Eloyi",
"afp": "Tapei",
"afs": "Afro-Seminole Creole",
"aft": "Afitti",
"afu": "Awutu",
"afz": "Obokuitai",
"aga": "Aguano",
"agb": "Legbo",
"agc": "Agatu",
"agd": "Agarabi",
"age": "Angal",
"agf": "Arguni",
"agg": "Angor",
"agh": "Ngelima",
"agi": "Agariya",
"agj": "Argobba",
"agk": "Isarog Agta",
"agl": "Fembe",
"agm": "Angaataha",
"agn": "Agutaynen",
"ago": "Tainae",
"agq": "Aghem",
"agr": "Aguaruna",
"ags": "Esimbi",
"agt": "Central Cagayan Agta",
"agu": "Aguacateca",
"agv": "Remontado Agta",
"agw": "Kahua",
"agx": "Aghul",
"agy": "Southern Alta",
"agz": "Mount Iriga Agta",
"aha": "Ahanta",
"ahb": "Axamb",
"ahg": "Qimant",
"ahh": "Aghu",
"ahi": "Tiagba",
"ahk": "Akha",
"ahl": "Igo",
"ahm": "Mobu",
"ahn": "Àhàn",
"aho": "Ahom",
"ahp": "Apro",
"ahr": "Ahirani",
"ahs": "Ashe",
"aht": "Ahtna",
"aia": "Arosi",
"aib": "Äynu",
"aic": "Ainbai",
"aid": "Alngith",
"aie": "Amara",
"aif": "Agi",
"aig": "Antigua and Barbuda Creole English",
"aih": "Ai-Cham",
"aii": "Assyrian Neo-Aramaic",
"aij": "Lishanid Noshan",
"aik": "Ake",
"ail": "Aimele",
"aim": "Aimol",
"ain": "Ainu",
"aio": "Aiton",
"aip": "Burumakok",
"air": "Airoran",
"ait": "Arikem",
"aiw": "Aari",
"aix": "Aighon",
"aiy": "Ali",
"aja": "Aja (East Africa)",
"ajg": "Aja (West Africa)",
"aji": "Ajië",
"ajn": "Andajin",
"ajp": "South Levantine Arabic",
"ajw": "Ajawa",
"ajz": "Amri Karbi",
"ak": "Akan",
"akb": "Angkola Batak",
"akc": "Mpur",
"akd": "Ukpet-Ehom",
"ake": "Akawaio",
"akf": "Akpa",
"akg": "Anakalangu",
"akh": "Angal Heneng",
"aki": "Aiome",
"akj": "Jeru",
"akk": "Akkadian",
"akl": "Aklanon",
"akm": "Aka-Bo",
"ako": "Akurio",
"akp": "Siwu",
"akq": "Ak",
"akr": "Araki",
"aks": "Akaselem",
"akt": "Akolet",
"aku": "Akum",
"akv": "Akhvakh",
"akw": "Akwa",
"akx": "Aka-Kede",
"aky": "Aka-Kol",
"akz": "Alabama",
"ala": "Alago",
"alc": "Kawésqar",
"ald": "Alladian",
"ale": "Aleut",
"alf": "Alege",
"alg-aga": "Agawam",
"alg-pro": "Proto-Algonquian",
"alh": "Alawa",
"ali": "Amaimon",
"alj": "Alangan",
"alk": "Alak",
"all": "Allar",
"alm": "Amblong",
"alo": "Larike-Wakasihu",
"alp": "Alune",
"alq": "Algonquin",
"alr": "Alutor",
"alt": "Southern Altai",
"alu": "'Are'are",
"alv-ama": "Amasi",
"alv-bgu": "Bainouk Gubeeher",
"alv-bua-pro": "Proto-Bua",
"alv-cng-pro": "Proto-Cangin",
"alv-edk-pro": "Proto-Edekiri",
"alv-edo-pro": "Proto-Edoid",
"alv-fli-pro": "Proto-Fali",
"alv-gbe-pro": "Proto-Gbe",
"alv-gng-pro": "Proto-Guang",
"alv-gtm-pro": "Proto-Central Togo",
"alv-gwa": "Gwara",
"alv-hei-pro": "Proto-Heiban",
"alv-ido-pro": "Proto-Idomoid",
"alv-igb-pro": "Proto-Igboid",
"alv-kwa-pro": "Proto-Kwa",
"alv-mum-pro": "Proto-Mumuye",
"alv-nup-pro": "Proto-Nupoid",
"alv-pro": "Proto-Atlantic-Congo",
"alv-von-pro": "Proto-Volta-Niger",
"alv-yor-pro": "Proto-Yoruba",
"alv-yrd-pro": "Proto-Yoruboid",
"alw": "Alaba",
"alx": "Amol",
"aly": "Alyawarr",
"alz": "Alur",
"am": "Amharic",
"ama": "Amanayé",
"amb": "Ambo",
"amc": "Amahuaca",
"ame": "Yanesha'",
"amf": "Hamer-Banna",
"amg": "Amurdag",
"ami": "Amis",
"amj": "Amdang",
"amk": "Ambai",
"aml": "War-Jaintia",
"amm": "Ama",
"amn": "Amanab",
"amo": "Amo",
"amp": "Alamblak",
"amq": "Amahai",
"amr": "Amarakaeri",
"ams": "Southern Amami Ōshima",
"amt": "Amto",
"amu": "Guerrero Amuzgo",
"amv": "Ambelau",
"amw": "Western Neo-Aramaic",
"amx": "Anmatyerre",
"amy": "Ami",
"amz": "Atampaya",
"an": "Aragonese",
"ana": "Andaqui",
"anb": "Andoa",
"anc": "Ngas",
"and": "Ansus",
"ane": "Xârâcùù",
"anf": "Animere",
"ang": "Old English",
"anh": "Nend",
"ani": "Andi",
"anj": "Anor",
"ank": "Goemai",
"anl": "Anu",
"anm": "Anāl",
"ann": "Obolo",
"ano": "Andoque",
"anp": "Angika",
"anq": "Jarawa",
"anr": "Andh",
"ans": "Anserma",
"ant": "Antakarinya",
"anu": "Anuak",
"anv": "Denya",
"anw": "Anaang",
"anx": "Andra-Hus",
"any": "Anyi",
"anz": "Anem",
"aoa": "Angolar",
"aob": "Abom",
"aoc": "Pemon",
"aod": "Andarum",
"aoe": "Angal Enen",
"aof": "Bragat",
"aog": "Angoram",
"aoi": "Anindilyakwa",
"aoj": "Mufian",
"aok": "Arhö",
"aol": "Alorese",
"aom": "Ömie",
"aon": "Bumbita Arapesh",
"aor": "Aore",
"aos": "Taikat",
"aot": "Atong (India)",
"aou": "A'ou",
"aox": "Atorada",
"aoz": "Uab Meto",
"apa-pro": "Proto-Apachean",
"apb": "Sa'a",
"apc": "North Levantine Arabic",
"apd": "Sudanese Arabic",
"ape": "Bukiyip",
"apf": "Pahanan Agta",
"apg": "Ampanang",
"aph": "Athpare",
"api": "Apiaká",
"apj": "Jicarilla",
"apk": "Plains Apache",
"apl": "Lipan",
"apm": "Chiricahua",
"apn": "Apinayé",
"apo": "Ambul",
"app": "Apma",
"apq": "A-Pucikwar",
"apr": "Arop-Lokep",
"aps": "Arop-Sissano",
"apt": "Apatani",
"apu": "Apurinã",
"apv": "Alapmunte",
"apw": "Western Apache",
"apx": "Aputai",
"apy": "Apalaí",
"apz": "Safeyoka",
"aqc": "Archi",
"aqd": "Ampari Dogon",
"aqg": "Arigidi",
"aql-pro": "Proto-Algic",
"aqm": "Atohwaim",
"aqn": "Northern Alta",
"aqp": "Atakapa",
"aqr": "Arhâ",
"aqt": "Angaité",
"aqz": "Akuntsu",
"ar": "Arabic",
"arc": "Aramaic",
"ard": "Arabana",
"are": "Western Arrernte",
"arh": "Arhuaco",
"ari": "Arikara",
"arj": "Arapaso",
"ark": "Arikapú",
"arl": "Arabela",
"arn": "Mapudungun",
"aro": "Araona",
"arp": "Arapaho",
"arq": "Algerian Arabic",
"arr": "Arara-Karo",
"ars": "Najdi Arabic",
"art-adu": "Adûni",
"art-bel": "Belter Creole",
"art-blk": "Bolak",
"art-bsp": "Black Speech",
"art-com": "Communicationssprache",
"art-dtk": "Dothraki",
"art-elo": "Eloi",
"art-gld": "Goa'uld",
"art-lap": "Lapine",
"art-man": "Mandalorian",
"art-mun": "Mundolinco",
"art-nav": "Naʼvi",
"art-vlh": "High Valyrian",
"aru": "Arua",
"arv": "Arbore",
"arw": "Lokono",
"arx": "Aruá",
"ary": "Moroccan Arabic",
"arz": "Egyptian Arabic",
"as": "Assamese",
"asa": "Pare",
"asb": "Assiniboine",
"asc": "Casuarina Coast Asmat",
"ase": "American Sign Language",
"asf": "Auslan",
"asg": "Cishingini",
"ash": "Abishira",
"asi": "Buruwai",
"asj": "Nsari",
"ask": "Ashkun",
"asl": "Asilulu",
"asn": "Xingú Asuriní",
"aso": "Dano",
"asp": "Algerian Sign Language",
"asq": "Austrian Sign Language",
"asr": "Asuri",
"ass": "Ipulo",
"ast": "Asturian",
"asu": "Tocantins Asurini",
"asv": "Asoa",
"asw": "Australian Aboriginal Sign Language",
"asx": "Muratayak",
"asy": "Yaosakor Asmat",
"asz": "As",
"ata": "Pele-Ata",
"atb": "Zaiwa",
"atc": "Atsahuaca",
"atd": "Ata Manobo",
"ate": "Atemble",
"atg": "Okpela",
"ath-nic": "Nicola",
"ath-pro": "Proto-Athabaskan",
"ati": "Attié",
"atj": "Atikamekw",
"atk": "Ati",
"atl": "Mount Iraya Agta",
"atm": "Ata",
"ato": "Atong (Cameroon)",
"atp": "Pudtol Atta",
"atq": "Aralle-Tabulahan",
"atr": "Waimiri-Atroari",
"ats": "Gros Ventre",
"att": "Pamplona Atta",
"atu": "Reel",
"atv": "Northern Altai",
"atw": "Atsugewi",
"atx": "Arutani",
"aty": "Aneityum",
"atz": "Arta",
"aua": "Asumboa",
"aub": "Alugu",
"auc": "Huaorani",
"aud": "Anuta",
"auf-pro": "Proto-Arawa",
"aug": "Aguna",
"auh": "Aushi",
"aui": "Anuki",
"auj": "Awjila",
"auk": "Heyo",
"aul": "Aulua",
"aum": "Asu",
"aun": "Molmo One",
"auo": "Auyokawa",
"aup": "Makayam",
"auq": "Anus",
"aur": "Aruek",
"aus-alu": "Alungul",
"aus-and": "Andjingith",
"aus-ang": "Angkula",
"aus-arn-pro": "Proto-Arnhem",
"aus-bra": "Barranbinya",
"aus-brm": "Barunggam",
"aus-cww-pro": "Proto-Central New South Wales",
"aus-dal-pro": "Proto-Daly",
"aus-guw": "Guwar",
"aus-lsw": "Little Swanport",
"aus-mbi": "Mbiywom",
"aus-ngk": "Ngkoth",
"aus-nyu-pro": "Proto-Nyulnyulan",
"aus-pam-pro": "Proto-Pama-Nyungan",
"aus-tul": "Tulua",
"aus-uwi": "Uwinymil",
"aus-wdj-pro": "Proto-Iwaidjan",
"aus-won": "Wong-gie",
"aus-wul": "Wulguru",
"aus-ynk": "Yangkaal",
"aut": "Austral",
"auu": "Auye",
"auw": "Awyi",
"aux": "Aurá",
"auy": "Auyana",
"auz": "Uzbeki Arabic",
"av": "Avar",
"avb": "Avau",
"avd": "Alviri-Vidari",
"avi": "Avikam",
"avk": "Kotava",
"avm": "Angkamuthi",
"avn": "Avatime",
"avo": "Agavotaguerra",
"avs": "Aushiri",
"avt": "Au",
"avu": "Avokaya",
"avv": "Avá-Canoeiro",
"awa": "Awadhi",
"awb": "Awa (New Guinea)",
"awc": "Cicipu",
"awd-ama": "Amarizana",
"awd-amc-pro": "Proto-Amuesha-Chamicuro",
"awd-ana": "Anauyá",
"awd-apo": "Apolista",
"awd-cab": "Cabre",
"awd-gnu": "Guinau",
"awd-kar": "Cariay",
"awd-kaw": "Kawishana",
"awd-kmp-pro": "Proto-Kampa",
"awd-kus": "Kustenau",
"awd-man": "Manao",
"awd-mar": "Marawan",
"awd-mpr": "Maipure",
"awd-mrt": "Mariaté",
"awd-nwk-pro": "Proto-Nawiki",
"awd-pai": "Paikoneka",
"awd-pas": "Pasé",
"awd-pro": "Proto-Arawak",
"awd-prw-pro": "Proto-Paresi-Waura",
"awd-she": "Shebayo",
"awd-taa-pro": "Proto-Ta-Arawak",
"awd-wai": "Wainumá",
"awd-yum": "Yumana",
"awe": "Awetí",
"awg": "Anguthimri",
"awh": "Awbono",
"awi": "Aekyom",
"awk": "Awabakal",
"awm": "Arawum",
"awn": "Awngi",
"awo": "Awak",
"awr": "Awera",
"aws": "South Awyu",
"awt": "Araweté",
"awu": "Central Awyu",
"awv": "Jair Awyu",
"aww": "Awun",
"awx": "Awara",
"awy": "Edera Awyu",
"axb": "Abipón",
"axe": "Ayerrerenge",
"axg": "Arára (Mato Grosso)",
"axk": "Aka (Central Africa)",
"axl": "Lower Southern Aranda",
"axm": "Middle Armenian",
"axx": "Xârâgurè",
"ay": "Aymara",
"aya": "Awar",
"ayb": "Ayizo",
"ayd": "Ayabadhu",
"aye": "Ayere",
"ayg": "Nyanga (Togo)",
"ayi": "Leyigha",
"ayk": "Akuku",
"ayl": "Libyan Arabic",
"ayn": "Yemeni Arabic",
"ayo": "Ayoreo",
"ayp": "North Mesopotamian Arabic",
"ayq": "Ayi",
"ays": "Sorsogon Ayta",
"ayt": "Bataan Ayta",
"ayu": "Ayu",
"ayz": "Maybrat",
"az": "Azerbaijani",
"aza": "Azha",
"azc-caz": "Cazcan",
"azc-cup-pro": "Proto-Cupan",
"azc-ktn": "Kitanemuk",
"azc-nah-pro": "Proto-Nahuan",
"azc-num-pro": "Proto-Numic",
"azc-pro": "Proto-Uto-Aztecan",
"azc-tak-pro": "Proto-Takic",
"azc-tat": "Tataviam",
"azd": "Eastern Durango Nahuatl",
"azg": "San Pedro Amuzgos Amuzgo",
"azm": "Ipalapa Amuzgo",
"azn": "Western Durango Nahuatl",
"azo": "Awing",
"azt": "Faire Atta",
"azz": "Highland Puebla Nahuatl",
"ba": "Bashkir",
"baa": "Babatana",
"bab": "Bainouk-Gunyuño",
"bac": "Baduy",
"bae": "Baré",
"baf": "Nubaca",
"bag": "Tuki",
"bah": "Bahamian Creole",
"baj": "Barakai",
"bal": "Baluchi",
"ban": "Balinese",
"bao": "Waimaha",
"bap": "Bantawa",
"bar": "Bavarian",
"bas": "Basaa",
"bau": "Badanchi",
"bav": "Babungo",
"baw": "Bambili-Bambui",
"bax": "Bamum",
"bay": "Batuley",
"bba": "Baatonum",
"bbb": "Barai",
"bbc": "Toba Batak",
"bbd": "Bau",
"bbe": "Bangba",
"bbf": "Baibai",
"bbg": "Barama",
"bbh": "Bugan",
"bbi": "Barombi",
"bbj": "Ghomala'",
"bbk": "Babanki",
"bbl": "Bats",
"bbm": "Babango",
"bbn": "Uneapa",
"bbo": "Konabéré",
"bbp": "West Central Banda",
"bbq": "Bamali",
"bbr": "Girawa",
"bbs": "Bakpinka",
"bbt": "Mburku",
"bbu": "Bakulung",
"bbv": "Karnai",
"bbw": "Baba",
"bbx": "Bubia",
"bby": "Befang",
"bca": "Central Bai",
"bcb": "Bainouk-Samik",
"bcd": "North Babar",
"bce": "Bamenyam",
"bcf": "Bamu",
"bcg": "Baga Pokur",
"bch": "Bariai",
"bci": "Baoule",
"bcj": "Bardi",
"bck": "Bunaba",
"bcl": "Central Bikol",
"bcm": "Banoni",
"bcn": "Bibaali",
"bco": "Kaluli",
"bcp": "Bali",
"bcq": "Bench",
"bcr": "Babine-Witsuwit'en",
"bcs": "Kohumono",
"bct": "Bendi",
"bcu": "Biliau",
"bcv": "Shoo-Minda-Nye",
"bcw": "Bana",
"bcy": "Bacama",
"bcz": "Bainouk-Gunyaamolo",
"bda": "Bayot",
"bdb": "Basap",
"bdc": "Emberá-Baudó",
"bdd": "Bunama",
"bde": "Bade",
"bdf": "Biage",
"bdg": "Bonggi",
"bdh": "Tara Baka",
"bdi": "Burun",
"bdj": "Bai (South Sudan)",
"bdk": "Budukh",
"bdl": "Indonesian Bajau",
"bdm": "Buduma",
"bdn": "Baldemu",
"bdo": "Morom",
"bdp": "Bende",
"bdq": "Bahnar",
"bdr": "West Coast Bajau",
"bds": "Burunge",
"bdt": "Bokoto",
"bdu": "Oroko",
"bdv": "Bodo Parja",
"bdw": "Baham",
"bdx": "Budong-Budong",
"bdy": "Bandjalang",
"bdz": "Badeshi",
"be": "Belarusian",
"bea": "Beaver",
"beb": "Bebele",
"bec": "Iceve-Maci",
"bed": "Bedoanas",
"bee": "Byangsi",
"bef": "Benabena",
"beg": "Belait",
"beh": "Biali",
"bei": "Bekati'",
"bej": "Beja",
"bek": "Bebeli",
"bem": "Bemba",
"beo": "Beami",
"bep": "Besoa",
"beq": "Beembe",
"ber-fog": "Fogaha",
"ber-pro": "Proto-Berber",
"ber-zuw": "Zuwara",
"bes": "Besme",
"bet": "Guiberoua Bété",
"beu": "Blagar",
"bev": "Daloa Bété",
"bew": "Betawi",
"bex": "Jur Modo",
"bey": "Beli (New Guinea)",
"bez": "Kibena",
"bfa": "Bari",
"bfb": "Pauri Bareli",
"bfc": "Panyi Bai",
"bfd": "Bafut",
"bfe": "Betaf",
"bff": "Bofi",
"bfg": "Busang Kayan",
"bfh": "Blafe",
"bfi": "British Sign Language",
"bfj": "Bafanji",
"bfk": "Ban Khor Sign Language",
"bfl": "Banda-Ndélé",
"bfm": "Mmen",
"bfn": "Bunak",
"bfo": "Malba Birifor",
"bfp": "Beba",
"bfq": "Badaga",
"bfr": "Bazigar",
"bfs": "Southern Bai",
"bft": "Balti",
"bfu": "Gahri",
"bfw": "Bondo",
"bfx": "Bantayanon",
"bfy": "Bagheli",
"bfz": "Mahasu Pahari",
"bg": "Bulgarian",
"bga": "Gwamhi-Wuri",
"bgb": "Bobongko",
"bgc": "Haryanvi",
"bgd": "Rathwi Bareli",
"bge": "Bauria",
"bgf": "Bangandu",
"bgg": "Bugun",
"bgi": "Giangan",
"bgj": "Bangolan",
"bgk": "Bit",
"bgl": "Bo",
"bgo": "Baga Koga",
"bgq": "Bagri",
"bgr": "Bawm Chin",
"bgs": "Tagabawa",
"bgt": "Bughotu",
"bgu": "Mbongno",
"bgv": "Warkay-Bipim",
"bgw": "Bhatri",
"bgx": "Balkan Gagauz Turkish",
"bgy": "Benggoi",
"bgz": "Banggai",
"bh": "Bihari",
"bha": "Bharia",
"bhb": "Bhili",
"bhc": "Biga",
"bhd": "Bhadrawahi",
"bhe": "Bhaya",
"bhf": "Odiai",
"bhg": "Binandere",
"bhh": "Bukhari",
"bhi": "Bhilali",
"bhj": "Bahing",
"bhl": "Bimin",
"bhm": "Bathari",
"bhn": "Bohtan Neo-Aramaic",
"bho": "Bhojpuri",
"bhp": "Bima",
"bhq": "South Tukang Besi",
"bhs": "Buwal",
"bht": "Bhattiyali",
"bhu": "Bhunjia",
"bhv": "Bahau",
"bhw": "Biak",
"bhx": "Bhalay",
"bhy": "Bhele",
"bhz": "Bada",
"bi": "Bislama",
"bia": "Badimaya",
"bib": "Bissa",
"bid": "Bidiyo",
"bie": "Bepour",
"bif": "Biafada",
"big": "Biangai",
"bij": "Kwanka",
"bil": "Bile",
"bim": "Bimoba",
"bin": "Edo",
"bio": "Nai",
"bip": "Bila",
"biq": "Bipi",
"bir": "Bisorio",
"bit": "Berinomo",
"biu": "Biete",
"biv": "Southern Birifor",
"biw": "Kol (Cameroon)",
"bix": "Bijori",
"biy": "Birhor",
"biz": "Baloi",
"bja": "Budza",
"bjb": "Barngarla",
"bjc": "Bariji",
"bje": "Biao-Jiao Mien",
"bjf": "Barzani Jewish Neo-Aramaic",
"bjg": "Bidyogo",
"bjh": "Bahinemo",
"bji": "Burji",
"bjj": "Kannauji",
"bjk": "Barok",
"bjl": "Bulu (New Guinea)",
"bjm": "Bajelani",
"bjn": "Banjarese",
"bjo": "Mid-Southern Banda",
"bjp": "Fanamaket",
"bjr": "Binumarien",
"bjs": "Bajan",
"bjt": "Balanta-Ganja",
"bju": "Busuu",
"bjv": "Bedjond",
"bjw": "Bakwé",
"bjx": "Banao Itneg",
"bjy": "Bayali",
"bjz": "Baruga",
"bka": "Kyak",
"bkc": "Baka",
"bkd": "Binukid",
"bkf": "Beeke",
"bkg": "Buraka",
"bkh": "Bakoko",
"bki": "Baki",
"bkj": "Pande",
"bkk": "Brokskat",
"bkl": "Berik",
"bkm": "Kom (Cameroon)",
"bkn": "Bukitan",
"bko": "Kwa'",
"bkp": "Iboko",
"bkq": "Bakairí",
"bkr": "Bakumpai",
"bks": "Masbate Sorsogon",
"bkt": "Boloki",
"bku": "Buhid",
"bkv": "Bekwarra",
"bkw": "Bekwel",
"bkx": "Baikeno",
"bky": "Bokyi",
"bkz": "Bungku",
"bla": "Blackfoot",
"blb": "Bilua",
"blc": "Bella Coola",
"bld": "Bolango",
"ble": "Balanta-Kentohe",
"blf": "Buol",
"blg": "Balau",
"blh": "Kuwaa",
"bli": "Bolia",
"blj": "Bulungan",
"blk": "Pa'o Karen",
"bll": "Biloxi",
"blm": "Beli (South Sudan)",
"bln": "Southern Catanduanes Bikol",
"blo": "Anii",
"blp": "Blablanga",
"blq": "Baluan-Pam",
"blr": "Blang",
"bls": "Balaesang",
"blt": "Tai Dam",
"blv": "Kibala",
"blw": "Balangao",
"blx": "Mag-Indi Ayta",
"bly": "Notre",
"blz": "Balantak",
"bm": "Bambara",
"bma": "Lame",
"bmb": "Bembe",
"bmc": "Biem",
"bmd": "Baga Manduri",
"bme": "Limassa",
"bmf": "Bom",
"bmg": "Bamwe",
"bmh": "Kein",
"bmi": "Bagirmi",
"bmj": "Bote-Majhi",
"bmk": "Ghayavi",
"bml": "Bomboli",
"bmn": "Bina",
"bmo": "Bambalang",
"bmp": "Bulgebi",
"bmq": "Bomu",
"bmr": "Muinane",
"bmt": "Biao Mon",
"bmu": "Somba-Siawari",
"bmv": "Bum",
"bmw": "Bomwali",
"bmx": "Baimak",
"bmz": "Baramu",
"bn": "Bengali",
"bna": "Bonerate",
"bnb": "Bookan",
"bnd": "Banda",
"bne": "Bintauna",
"bnf": "Masiwang",
"bng": "Benga",
"bni": "Bangi",
"bnj": "Eastern Tawbuid",
"bnk": "Bierebo",
"bnl": "Boon",
"bnm": "Batanga",
"bnn": "Bunun",
"bno": "Asi",
"bnp": "Bola",
"bnq": "Bantik",
"bnr": "Butmas-Tur",
"bns": "Bundeli",
"bnt-bal": "Balong",
"bnt-bon": "Boma Nkuu",
"bnt-boy": "Boma Yumu",
"bnt-bwa": "Bwala",
"bnt-cmw": "Chimwiini",
"bnt-ind": "Indanga",
"bnt-lal": "Lala (South Africa)",
"bnt-mpi": "Mpiin",
"bnt-mpu": "Mpuono",
"bnt-ngu-pro": "Proto-Nguni",
"bnt-phu": "Phuthi",
"bnt-pro": "Proto-Bantu",
"bnt-sab-pro": "Proto-Sabaki",
"bnt-sbo": "South Boma",
"bnt-sts-pro": "Proto-Sotho-Tswana",
"bnu": "Bentong",
"bnv": "Beneraf",
"bnw": "Bisis",
"bnx": "Bangubangu",
"bny": "Bintulu",
"bnz": "Beezen",
"bo": "Tibetan",
"boa": "Bora",
"bob": "Aweer",
"boe": "Mundabli",
"bof": "Bolon",
"bog": "Bamako Sign Language",
"boh": "North Boma",
"boi": "Barbareño",
"boj": "Anjam",
"bok": "Bonjo",
"bol": "Bole",
"bom": "Berom",
"bon": "Bine",
"boo": "Tiemacèwè Bozo",
"bop": "Bonkiman",
"boq": "Bogaya",
"bor": "Borôro",
"bot": "Bongo",
"bou": "Bondei",
"bov": "Tuwuli",
"bow": "Rema",
"box": "Buamu",
"boy": "Bodo (Central Africa)",
"boz": "Tiéyaxo Bozo",
"bpa": "Daakaka",
"bpd": "Banda-Banda",
"bpe": "Bauni",
"bpg": "Bonggo",
"bph": "Botlikh",
"bpi": "Bagupi",
"bpj": "Binji",
"bpk": "Orowe",
"bpl": "Broome Pearling Lugger Pidgin",
"bpm": "Biyom",
"bpn": "Dzao Min",
"bpo": "Anasi",
"bpp": "Kaure",
"bpq": "Banda Malay",
"bpr": "Koronadal Blaan",
"bps": "Sarangani Blaan",
"bpt": "Barrow Point",
"bpu": "Bongu",
"bpv": "Bian Marind",
"bpx": "Palya Bareli",
"bpy": "Bishnupriya Manipuri",
"bpz": "Bilba",
"bqa": "Tchumbuli",
"bqb": "Bagusa",
"bqc": "Boko",
"bqd": "Bung",
"bqf": "Baga Kaloum",
"bqg": "Bago-Kusuntu",
"bqh": "Baima",
"bqi": "Bakhtiari",
"bqj": "Bandial",
"bqk": "Banda-Mbrès",
"bql": "Karian",
"bqm": "Wumboko",
"bqn": "Bulgarian Sign Language",
"bqo": "Balo",
"bqp": "Busa",
"bqq": "Biritai",
"bqr": "Burusu",
"bqs": "Bosngun",
"bqt": "Bamukumbit",
"bqu": "Boguru",
"bqv": "Begbere-Ejar",
"bqw": "Buru (Nigeria)",
"bqx": "Baangi",
"bqy": "Bengkala Sign Language",
"bqz": "Bakaka",
"br": "Breton",
"bra": "Braj",
"brb": "Lave",
"brc": "Berbice Creole Dutch",
"brd": "Baraamu",
"brf": "Bera",
"brg": "Baure",
"brh": "Brahui",
"bri": "Mokpwe",
"brj": "Bieria",
"brk": "Birgid",
"brl": "Birwa",
"brm": "Barambu",
"brn": "Boruca",
"bro": "Brokkat",
"brp": "Barapasi",
"brq": "Breri",
"brr": "Birao",
"brs": "Baras",
"brt": "Bitare",
"bru": "Eastern Bru",
"brv": "Western Bru",
"brw": "Bellari",
"brx": "Bodo (India)",
"bry": "Burui",
"brz": "Bilbil",
"bsa": "Abinomn",
"bsb": "Brunei Bisaya",
"bsc": "Bassari",
"bse": "Wushi",
"bsf": "Bauchi",
"bsg": "Bashkardi",
"bsh": "Kamkata-viri",
"bsi": "Bassossi",
"bsj": "Bangwinji",
"bsk": "Burushaski",
"bsl": "Basa-Gumna",
"bsm": "Busami",
"bsn": "Barasana",
"bso": "Buso",
"bsp": "Baga Sitemu",
"bsq": "Bassa",
"bsr": "Bassa-Kontagora",
"bss": "Akoose",
"bst": "Basketo",
"bsu": "Bahonsuai",
"bsv": "Baga Sobané",
"bsw": "Baiso",
"bsx": "Yangkam",
"bsy": "Sabah Bisaya",
"bta": "Bata",
"btc": "Bati (Cameroon)",
"btd": "Dairi Batak",
"bte": "Gamo-Ningi",
"btf": "Birgit",
"btg": "Gagnoa Bété",
"bth": "Biatah Bidayuh",
"bti": "Burate",
"btj": "Bacanese Malay",
"btk-pro": "Proto-Batak",
"btm": "Mandailing Batak",
"btn": "Ratagnon",
"bto": "Rinconada Bikol",
"btp": "Budibud",
"btq": "Batek",
"btr": "Baetora",
"bts": "Simalungun Batak",
"btt": "Bete-Bendi",
"btu": "Batu",
"btv": "Bateri",
"btw": "Butuanon",
"btx": "Karo Batak",
"bty": "Bobot",
"btz": "Alas-Kluet Batak",
"bua": "Buryat",
"bub": "Bua",
"bud": "Ntcham",
"bue": "Beothuk",
"buf": "Bushoong",
"bug": "Buginese",
"buh": "Younuo Bunu",
"bui": "Bongili",
"buj": "Basa-Gurmana",
"buk": "Bukawa",
"bum": "Bulu (Cameroon)",
"bun": "Sherbro",
"buo": "Terei",
"bup": "Busoa",
"buq": "Brem",
"bus": "Bokobaru",
"but": "Bungain",
"buu": "Budu",
"buv": "Bun",
"buw": "Bubi",
"bux": "Boghom",
"buy": "Mmani",
"bva": "Barein",
"bvb": "Bube",
"bvc": "Baelelea",
"bvd": "Baeggu",
"bve": "Berau Malay",
"bvf": "Boor",
"bvg": "Bonkeng",
"bvh": "Bure",
"bvi": "Belanda Viri",
"bvj": "Baan",
"bvk": "Bukat",
"bvl": "Bolivian Sign Language",
"bvm": "Bamunka",
"bvn": "Buna",
"bvo": "Bolgo",
"bvp": "Bumang",
"bvq": "Birri",
"bvr": "Burarra",
"bvt": "Bati (Indonesia)",
"bvu": "Bukit Malay",
"bvv": "Baniva",
"bvw": "Boga",
"bvx": "Babole",
"bvy": "Baybayanon",
"bvz": "Bauzi",
"bwa": "Bwatoo",
"bwb": "Namosi-Naitasiri-Serua",
"bwc": "Bwile",
"bwd": "Bwaidoka",
"bwe": "Bwe Karen",
"bwf": "Boselewa",
"bwg": "Barwe",
"bwh": "Bishuo",
"bwi": "Baniwa",
"bwj": "Láá Láá Bwamu",
"bwk": "Bauwaki",
"bwl": "Bwela",
"bwm": "Biwat",
"bwn": "Wunai Bunu",
"bwo": "Shinasha",
"bwp": "Lower Mandobo",
"bwq": "Southern Bobo",
"bwr": "Bura",
"bws": "Bomboma",
"bwt": "Bafaw",
"bwu": "Buli (Ghana)",
"bww": "Bwa",
"bwx": "Bu-Nao Bunu",
"bwy": "Cwi Bwamu",
"bwz": "Bwisi",
"bxa": "Bauro",
"bxb": "Belanda Bor",
"bxc": "Molengue",
"bxd": "Pela",
"bxe": "Ongota",
"bxf": "Bilur",
"bxg": "Bangala",
"bxh": "Buhutu",
"bxi": "Pirlatapa",
"bxj": "Bayungu",
"bxk": "Bukusu",
"bxl": "Jalkunan",
"bxn": "Burduna",
"bxo": "Barikanchi",
"bxp": "Bebil",
"bxq": "Beele",
"bxs": "Busam",
"bxv": "Berakou",
"bxw": "Banka",
"bxz": "Binahari",
"bya": "Palawan Batak",
"byb": "Bikya",
"byc": "Ubaghara",
"byd": "Benyadu'",
"bye": "Pouye",
"byf": "Bete",
"byg": "Baygo",
"byh": "Bujhyal",
"byi": "Buyu",
"byj": "Binawa",
"byk": "Biao",
"byl": "Bayono",
"bym": "Bidyara",
"byn": "Blin",
"byo": "Biyo",
"byp": "Bumaji",
"byq": "Basay",
"byr": "Baruya",
"bys": "Burak",
"byt": "Berti",
"byv": "Medumba",
"byw": "Belhariya",
"byx": "Qaqet",
"byz": "Banaro",
"bza": "Bandi",
"bzb": "Andio",
"bzd": "Bribri",
"bze": "Jenaama Bozo",
"bzf": "Boikin",
"bzg": "Babuza",
"bzh": "Mapos Buang",
"bzi": "Bisu",
"bzj": "Belizean Creole",
"bzk": "Nicaraguan Creole",
"bzl": "Boano (Sulawesi)",
"bzm": "Bolondo",
"bzn": "Boano (Maluku)",
"bzo": "Bozaba",
"bzp": "Kemberano",
"bzq": "Buli (Indonesia)",
"bzr": "Biri",
"bzs": "Brazilian Sign Language",
"bzu": "Burmeso",
"bzv": "Bebe",
"bzw": "Basa",
"bzx": "Hainyaxo Bozo",
"bzy": "Obanliku",
"bzz": "Evant",
"ca": "Catalan",
"caa": "Ch'orti'",
"cab": "Garifuna",
"cac": "Chuj",
"cad": "Caddo",
"cae": "Laalaa",
"caf": "Southern Carrier",
"cag": "Nivaclé",
"cah": "Cahuarano",
"caj": "Chané",
"cak": "Kaqchikel",
"cal": "Carolinian",
"cam": "Cèmuhî",
"can": "Chambri",
"cao": "Chácobo",
"cap": "Chipaya",
"caq": "Car Nicobarese",
"car": "Kari'na",
"cas": "Tsimané",
"cau-abz-pro": "Proto-Abkhaz-Abaza",
"cau-and-pro": "Proto-Andian",
"cau-ava-pro": "Proto-Avaro-Andian",
"cau-cir-pro": "Proto-Circassian",
"cau-drg-pro": "Proto-Dargwa",
"cau-lzg-pro": "Proto-Lezghian",
"cau-nec-pro": "Proto-Northeast Caucasian",
"cau-nkh-pro": "Proto-Nakh",
"cau-nwc-pro": "Proto-Northwest Caucasian",
"cau-tsz-pro": "Proto-Tsezian",
"cav": "Cavineña",
"caw": "Kallawaya",
"cax": "Chiquitano",
"cay": "Cayuga",
"caz": "Canichana",
"cba-ata": "Atanques",
"cba-cat": "Catío Chibcha",
"cba-dor": "Dorasque",
"cba-dui": "Duit",
"cba-hue": "Huetar",
"cba-nut": "Nutabe",
"cba-pro": "Proto-Chibchan",
"cbb": "Cabiyarí",
"cbc": "Carapana",
"cbd": "Carijona",
"cbg": "Chimila",
"cbi": "Chachi",
"cbj": "Ede Cabe",
"cbk": "Chavacano",
"cbl": "Bualkhaw Chin",
"cbn": "Nyah Kur",
"cbo": "Izora",
"cbq": "Tsucuba",
"cbr": "Cashibo-Cacataibo",
"cbs": "Cashinahua",
"cbt": "Chayahuita",
"cbu": "Candoshi-Shapra",
"cbv": "Cacua",
"cbw": "Kinabalian",
"cby": "Carabayo",
"cca": "Cauca",
"ccc": "Chamicuro",
"ccd": "Cafundó",
"cce": "Chopi",
"ccg": "Chamba Daka",
"cch": "Atsam",
"ccj": "Kasanga",
"ccl": "Cutchi-Swahili",
"ccm": "Malaccan Creole Malay",
"cco": "Comaltepec Chinantec",
"ccp": "Chakma",
"ccr": "Cacaopera",
"ccs-gzn-pro": "Proto-Georgian-Zan",
"ccs-pro": "Proto-Kartvelian",
"cda": "Choni",
"cdc-cbm-pro": "Proto-Central Chadic",
"cdc-mas-pro": "Proto-Masa",
"cdc-pro": "Proto-Chadic",
"cdd-pro": "Proto-Caddoan",
"cde": "Chenchu",
"cdf": "Chiru",
"cdh": "Chambeali",
"cdi": "Chodri",
"cdj": "Churahi",
"cdm": "Chepang",
"cdn": "Chaudangsi",
"cdo": "Eastern Min",
"cdr": "Cinda-Regi-Tiyal",
"cds": "Chadian Sign Language",
"cdy": "Chadong",
"cdz": "Koda",
"ce": "Chechen",
"cea": "Lower Chehalis",
"ceb": "Cebuano",
"ceg": "Chamacoco",
"cel-bry-pro": "Proto-Brythonic",
"cel-gal": "Gallaecian",
"cel-gau": "Gaulish",
"cel-pro": "Proto-Celtic",
"cen": "Cen",
"cet": "Centúúm",
"cfa": "Dijim-Bwilim",
"cfd": "Cara",
"cfg": "Como Karim",
"cfm": "Falam Chin",
"cga": "Changriwa",
"cgc": "Kagayanen",
"cgg": "Rukiga",
"cgk": "Chocangaca",
"ch": "Chamorro",
"chb": "Chibcha",
"chc": "Catawba",
"chd": "Highland Oaxaca Chontal",
"chf": "Chontal Maya",
"chg": "Chagatai",
"chh": "Chinook",
"chi-pro": "Proto-Chimakuan",
"chj": "Ojitlán Chinantec",
"chk": "Chuukese",
"chl": "Cahuilla",
"chm-pro": "Proto-Mari",
"chn": "Chinook Jargon",
"cho": "Choctaw",
"chp": "Chipewyan",
"chq": "Quiotepec Chinantec",
"chr": "Cherokee",
"cht": "Cholón",
"chw": "Chuabo",
"chx": "Chantyal",
"chy": "Cheyenne",
"chz": "Ozumacín Chinantec",
"cia": "Cia-Cia",
"cib": "Ci Gbe",
"cic": "Chickasaw",
"cid": "Chimariko",
"cie": "Cineni",
"cih": "Chinali",
"cik": "Chitkuli Kinnauri",
"cim": "Cimbrian",
"cin": "Cinta Larga",
"cip": "Chiapanec",
"cir": "Tinrin",
"ciy": "Chaima",
"cja": "Western Cham",
"cje": "Chru",
"cjh": "Upper Chehalis",
"cji": "Chamalal",
"cjk": "Chokwe",
"cjm": "Eastern Cham",
"cjn": "Chenapian",
"cjo": "Pajonal Ashéninka",
"cjp": "Cabécar",
"cjs": "Shor",
"cjv": "Chuave",
"cjy": "Jin",
"ckb": "Central Kurdish",
"ckh": "Chak",
"ckl": "Cibak",
"ckn": "Kaang Chin",
"cko": "Anufo",
"ckq": "Kajakse",
"ckr": "Kairak",
"cks": "Tayo",
"ckt": "Chukchi",
"cku": "Koasati",
"ckv": "Kavalan",
"ckx": "Caka",
"cky": "Cakfem-Mushere",
"ckz": "Kaqchikel-K'iche' Mixed Language",
"cla": "Ron",
"clc": "Chilcotin",
"cld": "Chaldean Neo-Aramaic",
"cle": "Lealao Chinantec",
"clh": "Chilisso",
"cli": "Chakali",
"clj": "Laitu Chin",
"clk": "Idu",
"cll": "Chala",
"clm": "Klallam",
"clo": "Lowland Oaxaca Chontal",
"clt": "Lutuv",
"clu": "Caluyanun",
"clw": "Chulym",
"cly": "Eastern Highland Chatino",
"cma": "Mạ",
"cmc-pro": "Proto-Chamic",
"cme": "Cerma",
"cmg": "Classical Mongolian",
"cmi": "Emberá-Chamí",
"cml": "Campalagian",
"cmm": "Michigamea",
"cmn": "Mandarin",
"cmo": "Central Mnong",
"cmr": "Mro Chin",
"cms": "Messapic",
"cmt": "Camtho",
"cna": "Changthang",
"cnb": "Chinbon Chin",
"cnc": "Cốông",
"cng": "Northern Qiang",
"cnh": "Lai",
"cni": "Asháninka",
"cnk": "Khumi Chin",
"cnl": "Lalana Chinantec",
"cno": "Con",
"cnp": "Northern Pinghua",
"cns": "Central Asmat",
"cnt": "Tepetotutla Chinantec",
"cnu": "Chenoua",
"cnw": "Ngawn Chin",
"cnx": "Middle Cornish",
"co": "Corsican",
"coa": "Cocos Islands Malay",
"cob": "Chicomuceltec",
"coc": "Cocopa",
"cod": "Cocama",
"coe": "Koreguaje",
"cof": "Tsafiki",
"cog": "Chong",
"coh": "Chichonyi-Chidzihana-Chikauma",
"coj": "Cochimi",
"cok": "Santa Teresa Cora",
"col": "Columbia-Wenatchi",
"com": "Comanche",
"con": "Cofán",
"coo": "Comox",
"cop": "Coptic",
"coq": "Coquille",
"cot": "Caquinte",
"cou": "Wamey",
"cov": "Cao Miao",
"cow": "Cowlitz",
"cox": "Nanti",
"coy": "Coyaima",
"coz": "Chochotec",
"cpa": "Palantla Chinantec",
"cpb": "Ucayali-Yurúa Ashéninka",
"cpc": "Apurucayali Ashéninka",
"cpg": "Cappadocian Greek",
"cpi": "Chinese Pidgin English",
"cpn": "Cherepon",
"cpo": "Kpee",
"cps": "Capiznon",
"cpu": "Pichis Ashéninka",
"cpx": "Puxian Min",
"cpy": "South Ucayali Ashéninka",
"cqd": "Chuanqiandian Cluster Miao",
"cr": "Cree",
"cra": "Chara",
"crb": "Kalinago",
"crc": "Lonwolwol",
"crd": "Coeur d'Alene",
"crf": "Caramanta",
"crg": "Michif",
"crh": "Crimean Tatar",
"cri": "Sãotomense",
"crj": "Southern East Cree",
"crk": "Plains Cree",
"crl": "Northern East Cree",
"crm": "Moose Cree",
"crn": "Cora",
"cro": "Crow",
"crp-bip": "Basque-Icelandic Pidgin",
"crp-gep": "West Greenlandic Pidgin",
"crp-kia": "Kiautschou German Pidgin",
"crp-mar": "Maroon Spirit Language",
"crp-mpp": "Macau Pidgin Portuguese",
"crp-rsn": "Russenorsk",
"crp-slb": "Solombala English",
"crp-spp": "Samoan Plantation Pidgin",
"crp-tpr": "Taimyr Pidgin Russian",
"crq": "Iyo'wujwa Chorote",
"crr": "Carolina Algonquian",
"crs": "Seychellois Creole",
"crt": "Iyojwa'ja Chorote",
"crv": "Chaura",
"crw": "Chrau",
"crx": "Carrier",
"cry": "Cori",
"crz": "Cruzeño",
"cs": "Czech",
"csa": "Chiltepec Chinantec",
"csb": "Kashubian",
"csc": "Catalan Sign Language",
"csd": "Chiangmai Sign Language",
"cse": "Czech Sign Language",
"csf": "Cuban Sign Language",
"csg": "Chilean Sign Language",
"csh": "Asho Chin",
"csi": "Coast Miwok",
"csj": "Songlai Chin",
"csk": "Jola-Kasa",
"csl": "Chinese Sign Language",
"csm": "Central Sierra Miwok",
"csn": "Colombian Sign Language",
"cso": "Sochiapam Chinantec",
"csp": "Southern Pinghua",
"csq": "Croatian Sign Language",
"csr": "Costa Rican Sign Language",
"css": "Southern Ohlone",
"cst": "Northern Ohlone",
"csu-bba-pro": "Proto-Bongo-Bagirmi",
"csu-maa-pro": "Proto-Mangbetu",
"csu-pro": "Proto-Central Sudanic",
"csu-sar-pro": "Proto-Sara",
"csv": "Sumtu Chin",
"csw": "Swampy Cree",
"csx": "Cambodian Sign Language",
"csy": "Siyin Chin",
"csz": "Coos",
"cta": "Tataltepec Chatino",
"ctc": "Chetco-Tolowa",
"ctd": "Tedim Chin",
"cte": "Tepinapa Chinantec",
"ctg": "Chittagonian",
"cth": "Thaiphum Chin",
"ctl": "Tlacoatzintepec Chinantec",
"ctm": "Chitimacha",
"ctn": "Chhintange",
"cto": "Emberá-Catío",
"ctp": "Western Highland Chatino",
"cts": "Northern Catanduanes Bikol",
"ctt": "Wayanad Chetti",
"ctu": "Chol",
"ctz": "Zacatepec Chatino",
"cu": "Old Church Slavonic",
"cua": "Cua",
"cub": "Cubeo",
"cuc": "Usila Chinantec",
"cug": "Cung",
"cuh": "Chuka",
"cui": "Cuiba",
"cuj": "Mashco Piro",
"cuk": "Kuna",
"cul": "Culina",
"cuo": "Cumanagoto",
"cup": "Cupeño",
"cuq": "Cun",
"cur": "Chhulung",
"cus-ash": "Ashraaf",
"cus-hec-pro": "Proto-Highland East Cushitic",
"cus-pro": "Proto-Cushitic",
"cus-som-pro": "Proto-Somaloid",
"cus-sou-pro": "Proto-South Cushitic",
"cut": "Teutila Cuicatec",
"cuu": "Tai Ya",
"cuv": "Cuvok",
"cuw": "Chukwa",
"cux": "Tepeuxila Cuicatec",
"cuy": "Cuitlatec",
"cv": "Chuvash",
"cvg": "Chug",
"cvn": "Valle Nacional Chinantec",
"cwa": "Kabwa",
"cwb": "Maindo",
"cwd": "Woods Cree",
"cwe": "Kwere",
"cwg": "Chewong",
"cwt": "Kuwaataay",
"cy": "Welsh",
"cya": "Nopala Chatino",
"cyb": "Cayubaba",
"cyo": "Cuyunon",
"czh": "Huizhou",
"czk": "Knaanic",
"czn": "Zenzontepec Chatino",
"czo": "Central Min",
"czt": "Zotung Chin",
"da": "Danish",
"daa": "Dangaléat",
"dac": "Dambi",
"dad": "Marik",
"dae": "Duupa",
"dag": "Dagbani",
"dah": "Gwahatike",
"dai": "Day",
"daj": "Dar Fur Daju",
"dak": "Dakota",
"dal": "Dahalo",
"dam": "Damakawa",
"dao": "Daai Chin",
"daq": "Dandami Maria",
"dar": "Dargwa",
"das": "Daho-Doo",
"dau": "Dar Sila Daju",
"dav": "Taita",
"daw": "Davawenyo",
"dax": "Dayi",
"daz": "Dao",
"dba": "Bangime",
"dbb": "Deno",
"dbd": "Dadiya",
"dbe": "Dabe",
"dbf": "Edopi",
"dbg": "Dogul Dom",
"dbi": "Doka",
"dbj": "Ida'an",
"dbl": "Dyirbal",
"dbm": "Duguri",
"dbn": "Duriankere",
"dbo": "Dulbu",
"dbp": "Duwai",
"dbq": "Daba",
"dbr": "Dabarre",
"dbt": "Ben Tey",
"dbu": "Bondum Dom Dogon",
"dbv": "Dungu",
"dbw": "Bankan Tey Dogon",
"dby": "Dibiyaso",
"dcc": "Deccani",
"dcr": "Negerhollands",
"dda": "Dadi Dadi",
"ddd": "Dongotono",
"dde": "Doondo",
"ddg": "Fataluku",
"ddi": "Diodio",
"ddj": "Jaru",
"ddn": "Dendi",
"ddo": "Tsez",
"ddr": "Dhudhuroa",
"dds": "Donno So Dogon",
"ddw": "Dawera-Daweloor",
"de": "German",
"dec": "Dagik",
"ded": "Dedua",
"dee": "Dewoin",
"def": "Dezfuli",
"deg": "Degema",
"deh": "Dehwari",
"dei": "Demisa",
"dem": "Dem",
"dep": "Pidgin Delaware",
"der": "Deori",
"des": "Desano",
"dev": "Domung",
"dez": "Dengese",
"dga": "Southern Dagaare",
"dgb": "Bunoge",
"dgc": "Casiguran Dumagat Agta",
"dgd": "Dagaari Dioula",
"dge": "Degenan",
"dgg": "Doga",
"dgh": "Dghwede",
"dgi": "Northern Dagara",
"dgk": "Dagba",
"dgn": "Dagoman",
"dgo": "Hindi Dogri",
"dgr": "Dogrib",
"dgs": "Dogoso",
"dgt": "Ntra'ngith",
"dgw": "Daungwurrung",
"dgx": "Doghoro",
"dgz": "Daga",
"dhd": "Dhundhari",
"dhg": "Dhangu",
"dhi": "Dhimal",
"dhl": "Dhalandji",
"dhm": "Zemba",
"dhn": "Dhanki",
"dho": "Dhodia",
"dhr": "Tharrgari",
"dhs": "Dhaiso",
"dhu": "Dhurga",
"dhv": "Drehu",
"dhw": "Danuwar",
"dhx": "Dhungaloo",
"dia": "Dia",
"dib": "South Central Dinka",
"dic": "Lakota Dida",
"did": "Didinga",
"dif": "Dieri",
"dig": "Digo",
"dii": "Dimbong",
"dij": "Dai",
"dik": "Southwestern Dinka",
"dil": "Dilling",
"dim": "Dime",
"din": "Dinka",
"dio": "Dibo",
"dip": "Northeastern Dinka",
"dir": "Dirim",
"dis": "Dimasa",
"diu": "Gciriku",
"diw": "Northwestern Dinka",
"dix": "Dixon Reef",
"diy": "Diuwe",
"diz": "Ding",
"dja": "Djadjawurrung",
"djb": "Djinba",
"djc": "Dar Daju Daju",
"djd": "Jaminjung",
"dje": "Zarma",
"djf": "Djangun",
"dji": "Djinang",
"djj": "Ndjébbana",
"djk": "Aukan",
"djl": "Djiwarli",
"djm": "Jamsay",
"djn": "Djauan",
"djo": "Jangkang",
"djr": "Djambarrpuyngu",
"dju": "Kapriman",
"djw": "Djawi",
"dka": "Dakpa",
"dkk": "Dakka",
"dkr": "Kuijau",
"dks": "Southeastern Dinka",
"dkx": "Mazagway",
"dlg": "Dolgan",
"dlk": "Dahalik",
"dlm": "Dalmatian",
"dln": "Darlong",
"dma": "Duma",
"dmb": "Mombo Dogon",
"dmc": "Gavak",
"dmd": "Madhi Madhi",
"dme": "Dugwor",
"dmf": "Medefaidrin",
"dmg": "Upper Kinabatangan",
"dmk": "Domaaki",
"dml": "Dameli",
"dmm": "Dama (Nigeria)",
"dmn-dam": "Dama (Sierra Leone)",
"dmn-mdw-pro": "Proto-Western Mande",
"dmn-pro": "Proto-Mande",
"dmo": "Kemezung",
"dmr": "East Damar",
"dms": "Dampelas",
"dmu": "Dubu",
"dmv": "Dumpas",
"dmw": "Mudburra",
"dmx": "Dema",
"dmy": "Demta",
"dna": "Upper Grand Valley Dani",
"dnd": "Daonda",
"dne": "Ndendeule",
"dng": "Dungan",
"dni": "Lower Grand Valley Dani",
"dnj": "Dan",
"dnk": "Dengka",
"dnn": "Dzuun",
"dno": "Ndrulo",
"dnr": "Danaru",
"dnt": "Mid Grand Valley Dani",
"dnu": "Danau",
"dnv": "Danu",
"dnw": "Western Dani",
"dny": "Dení",
"doa": "Dom",
"dob": "Dobu",
"doc": "Northern Kam",
"doe": "Doe",
"dof": "Domu",
"doh": "Dong",
"doi": "Dogri",
"dok": "Dondo",
"dol": "Doso",
"don": "Doura",
"doo": "Dongo",
"dop": "Lukpa",
"doq": "Dominican Sign Language",
"dor": "Dori'o",
"dos": "Dogosé",
"dot": "Dass",
"dov": "Toka-Leya",
"dow": "Doyayo",
"dox": "Bussa",
"doy": "Dompo",
"doz": "Dorze",
"dpp": "Papar",
"dra-bry": "Beary",
"dra-cen-pro": "Proto-Central Dravidian",
"dra-mkn": "Middle Kannada",
"dra-nor-pro": "Proto-North Dravidian",
"dra-okn": "Old Kannada",
"dra-ote": "Old Telugu",
"dra-pro": "Proto-Dravidian",
"dra-sdo-pro": "Proto-South Dravidian I",
"dra-sdt-pro": "Proto-South Dravidian II",
"dra-sou-pro": "Proto-South Dravidian",
"drb": "Dair",
"drc": "Minderico",
"drd": "Darmiya",
"drg": "Rungus",
"dri": "Lela",
"drl": "Baagandji",
"drn": "West Damar",
"dro": "Daro-Matu Melanau",
"drq": "Dura",
"drs": "Gedeo",
"dru": "Rukai",
"dru-pro": "Proto-Rukai",
"dry": "Darai",
"dsb": "Lower Sorbian",
"dse": "Dutch Sign Language",
"dsh": "Daasanach",
"dsi": "Disa",
"dsl": "Danish Sign Language",
"dsn": "Dusner",
"dso": "Desiya",
"dsq": "Tadaksahak",
"dta": "Daur",
"dtb": "Labuk-Kinabatangan Kadazan",
"dtd": "Ditidaht",
"dth": "Adithinngithigh",
"dti": "Ana Tinga Dogon",
"dtk": "Tene Kan Dogon",
"dtm": "Tomo Kan Dogon",
"dto": "Tommo So",
"dtp": "Central Dusun",
"dtr": "Lotud",
"dts": "Toro So Dogon",
"dtt": "Toro Tegu Dogon",
"dtu": "Tebul Ure Dogon",
"dty": "Doteli",
"dua": "Duala",
"dub": "Dubli",
"duc": "Duna",
"due": "Umiray Dumaget Agta",
"duf": "Dumbea",
"dug": "Chiduruma",
"duh": "Dungra Bhil",
"dui": "Dumun",
"duk": "Uyajitaya",
"dul": "Alabat Island Agta",
"dum": "Middle Dutch",
"dun": "Dusun Deyah",
"duo": "Dupaningan Agta",
"dup": "Duano",
"duq": "Dusun Malang",
"dur": "Dii",
"dus": "Dumi",
"duu": "Drung",
"duv": "Duvle",
"duw": "Dusun Witu",
"dux": "Duun",
"duy": "Dicamay Agta",
"duz": "Duli",
"dv": "Dhivehi",
"dva": "Duau",
"dwa": "Diri",
"dwr": "Dawro",
"dwu": "Dhuwal",
"dww": "Dawawa",
"dwy": "Dhuwaya",
"dwz": "Dewas Rai",
"dya": "Dyan",
"dyb": "Dyaberdyaber",
"dyd": "Dyugun",
"dyi": "Djimini",
"dym": "Yanda Dogon",
"dyn": "Dyangadi",
"dyo": "Jola-Fonyi",
"dyu": "Dyula",
"dyy": "Dyaabugay",
"dz": "Dzongkha",
"dza": "Tunzu",
"dzg": "Dazaga",
"dzl": "Dzala",
"dzn": "Dzando",
"ebg": "Ebughu",
"ebk": "Eastern Bontoc",
"ebr": "Ebrié",
"ebu": "Embu",
"ecr": "Eteocretan",
"ecs": "Ecuadorian Sign Language",
"ecy": "Eteocypriot",
"ee": "Ewe",
"eee": "E",
"efa": "Efai",
"efe": "Efe",
"efi": "Efik",
"ega": "Ega",
"egl": "Emilian",
"ego": "Eggon",
"egx-dem": "Demotic Egyptian",
"egy": "Egyptian",
"ehu": "Ehueun",
"eip": "Eipomek",
"eit": "Eitiep",
"eiv": "Askopan",
"eja": "Ejamat",
"eka": "Ekajuk",
"eke": "Ekit",
"ekg": "Ekari",
"eki": "Eki",
"ekl": "Kolhe",
"ekm": "Elip",
"eko": "Koti",
"ekp": "Ekpeye",
"ekr": "Yace",
"eky": "Eastern Kayah",
"el": "Greek",
"ele": "Elepi",
"elh": "El Hugeirat",
"eli": "Nding",
"elk": "Elkei",
"elm": "Eleme",
"elo": "El Molo",
"elu": "Elu",
"elx": "Elamite",
"ema": "Emai",
"emb": "Embaloh",
"eme": "Emerillon",
"emg": "Eastern Meohang",
"emi": "Mussau-Emira",
"emk": "Eastern Maninkakan",
"emm": "Mamulique",
"emn": "Eman",
"emp": "Northern Emberá",
"ems": "Alutiiq",
"emu": "Eastern Muria",
"emw": "Emplawas",
"emx": "Erromintxela",
"emy": "Epigraphic Mayan",
"en": "English",
"ena": "Apali",
"enb": "Markweeta",
"enc": "En",
"end": "Ende",
"enf": "Forest Enets",
"enh": "Tundra Enets",
"enl": "Enlhet",
"enm": "Middle English",
"enn": "Engenni",
"eno": "Enggano",
"enq": "Enga",
"enr": "Emem",
"enu": "Enu",
"env": "Enwan",
"enw": "Enwang",
"enx": "Enxet",
"eo": "Esperanto",
"eot": "Eotile",
"epi": "Epie",
"era": "Eravallan",
"erg": "Sie",
"erh": "Eruwa",
"eri": "Ogea",
"erk": "South Efate",
"ero-gsz": "Geshiza",
"ero-nya": "Nyagrong Minyag",
"ero-tau": "Stau",
"err": "Erre",
"ers": "Ersu",
"ert": "Eritai",
"erw": "Erokwanas",
"es": "Spanish",
"ese": "Ese Ejja",
"esh": "Eshtehardi",
"esl": "Egyptian Sign Language",
"esm": "Esuma",
"esn": "Salvadoran Sign Language",
"eso": "Estonian Sign Language",
"esq": "Esselen",
"ess": "Central Siberian Yupik",
"esu": "Yup'ik",
"esx-esk-pro": "Proto-Eskimo",
"esx-ink": "Inuktun",
"esx-inq": "Inuinnaqtun",
"esx-inu-pro": "Proto-Inuit",
"esx-pro": "Proto-Eskimo-Aleut",
"esx-tut": "Tunumiisut",
"esy": "Eskayan",
"et": "Estonian",
"etb": "Etebi",
"etc": "Etchemin",
"eth": "Ethiopian Sign Language",
"etn": "Eton (Vanuatu)",
"eto": "Eton (Cameroon)",
"etr": "Edolo",
"ets": "Yekhee",
"ett": "Etruscan",
"etu": "Ejagham",
"etx": "Eten",
"etz": "Semimi",
"eu": "Basque",
"euq-pro": "Proto-Basque",
"eve": "Even",
"evh": "Uvbie",
"evn": "Evenki",
"ewo": "Ewondo",
"ext": "Extremaduran",
"eya": "Eyak",
"eyo": "Keiyo",
"eza": "Ezaa",
"eze": "Uzekwe",
"fa": "Persian",
"faa": "Fasu",
"fab": "Annobonese",
"fad": "Wagi",
"faf": "Fagani",
"fag": "Finongan",
"fah": "Baissa Fali",
"fai": "Faiwol",
"faj": "Kursav",
"fak": "Fang (Beboid)",
"fal": "South Fali",
"fam": "Fam",
"fan": "Fang (Bantu)",
"fap": "Palor",
"far": "Fataleka",
"fau": "Fayu",
"fax": "Fala",
"fay": "Southwestern Fars",
"faz": "Northwestern Fars",
"fbl": "West Miraya Bikol",
"fcs": "Quebec Sign Language",
"fer": "Feroge",
"ff": "Fula",
"ffi": "Foia Foia",
"fgr": "Fongoro",
"fi": "Finnish",
"fia": "Nobiin",
"fie": "Fyer",
"fif": "Faifi",
"fip": "Fipa",
"fir": "Firan",
"fit": "Meänkieli",
"fiw": "Fiwaga",
"fj": "Fijian",
"fkk": "Kirya-Konzel",
"fkv": "Kven",
"fla": "Montana Salish",
"flh": "Foau",
"fli": "Fali",
"fll": "North Fali",
"fln": "Flinders Island",
"flr": "Fuliiru",
"fly": "Tsotsitaal",
"fmp": "Fe'fe'",
"fmu": "Far Western Muria",
"fng": "Fanagalo",
"fni": "Fania",
"fo": "Faroese",
"fod": "Foodo",
"foi": "Foi",
"fom": "Foma",
"fon": "Fon",
"for": "Fore",
"fos": "Siraya",
"fpe": "Pichinglis",
"fqs": "Fas",
"fr": "French",
"frd": "Fordata",
"frm": "Middle French",
"fro": "Old French",
"frp": "Franco-Provençal",
"frq": "Forak",
"frr": "North Frisian",
"frt": "Fortsenal",
"fse": "Finnish Sign Language",
"fsl": "French Sign Language",
"fss": "Finnish-Swedish Sign Language",
"fud": "East Futuna",
"fuj": "Ko",
"fum": "Fum",
"fun": "Fulniô",
"fur": "Friulian",
"fut": "Futuna-Aniwa",
"fuu": "Furu",
"fuy": "Fuyug",
"fvr": "Fur",
"fwa": "Fwâi",
"fwe": "Fwe",
"fy": "West Frisian",
"ga": "Irish",
"gaa": "Ga",
"gab": "Gabri",
"gac": "Mixed Great Andamanese",
"gad": "Gaddang",
"gae": "Warekena",
"gaf": "Gende",
"gag": "Gagauz",
"gah": "Alekano",
"gai": "Borei",
"gaj": "Gadsup",
"gak": "Gamkonora",
"gal": "Galoli",
"gam": "Kandawo",
"gan": "Gan",
"gao": "Gants",
"gap": "Gal",
"gaq": "Gata'",
"gar": "Galeya",
"gas": "Adiwasi Garasia",
"gat": "Kenati",
"gau": "Kondekor",
"gaw": "Nobonob",
"gay": "Gayo",
"gba-pro": "Proto-Gbaya",
"gbb": "Kaytetye",
"gbd": "Karadjeri",
"gbe": "Niksek",
"gbf": "Gaikundi",
"gbg": "Gbanziri",
"gbh": "Defi Gbe",
"gbi": "Galela",
"gbj": "Bodo Gadaba",
"gbk": "Gaddi",
"gbl": "Gamit",
"gbm": "Garhwali",
"gbn": "Mo'da",
"gbo": "Northern Grebo",
"gbp": "Gbaya-Bossangoa",
"gbq": "Gbaya-Bozoum",
"gbr": "Gbagyi",
"gbs": "Gbesi Gbe",
"gbu": "Gagadu",
"gbv": "Gbanu",
"gbw": "Gabi",
"gbx": "Eastern Xwla Gbe",
"gby": "Gbari",
"gcc": "Mali",
"gcd": "Ganggalida",
"gce": "Galice",
"gcf": "Antillean Creole",
"gcl": "Grenadian Creole English",
"gcn": "Gaina",
"gcr": "Guianese Creole",
"gct": "Colonia Tovar German",
"gd": "Scottish Gaelic",
"gdb": "Ollari",
"gdc": "Gugu Badhun",
"gdd": "Gedaged",
"gde": "Gude",
"gdf": "Guduf-Gava",
"gdg": "Ga'dang",
"gdh": "Gadjerawang",
"gdi": "Gundi",
"gdj": "Kurtjar",
"gdk": "Gadang",
"gdl": "Dirasha",
"gdm": "Laal",
"gdn": "Umanakaina",
"gdo": "Godoberi",
"gdq": "Mehri",
"gdr": "Wipi",
"gds": "Ghandruk Sign Language",
"gdt": "Kungardutyi",
"gdu": "Gudu",
"gdx": "Godwari",
"gea": "Geruma",
"geb": "Kire",
"gec": "Gboloo Grebo",
"ged": "Gade",
"geg": "Gengle",
"geh": "Hutterisch",
"gei": "Gebe",
"gej": "Gen",
"gek": "Gerka",
"gel": "Fakkanci",
"gem-pro": "Proto-Germanic",
"geq": "Geme",
"ges": "Geser-Gorom",
"gev": "Viya",
"gew": "Gera",
"gex": "Garre",
"gey": "Enya",
"gez": "Ge'ez",
"gfk": "Patpatar",
"gft": "Gafat",
"gga": "Gao",
"ggb": "Gbii",
"ggd": "Gugadj",
"gge": "Guragone",
"ggg": "Gurgula",
"ggk": "Kungarakany",
"ggl": "Ganglau",
"ggt": "Gitua",
"ggu": "Gban",
"ggw": "Gogodala",
"gha": "Ghadames",
"ghc": "Classical Gaelic",
"ghe": "Southern Ghale",
"ghh": "Northern Ghale",
"ghk": "Geko Karen",
"ghl": "Ghulfan",
"ghn": "Ghanongga",
"gho": "Ghomara",
"ghr": "Ghera",
"ghs": "Guhu-Samane",
"ght": "Kutang Ghale",
"gia": "Kitja",
"gib": "Gibanawa",
"gid": "Gidar",
"gie": "Guébie",
"gig": "Goaria",
"gih": "Githabul",
"gii": "Girirra",
"gil": "Gilbertese",
"gim": "Gimi (Papuan)",
"gin": "Hinukh",
"gip": "Gimi (Austronesian)",
"giq": "Green Gelao",
"gir": "Red Gelao",
"gis": "North Giziga",
"git": "Gitxsan",
"giu": "Mulao",
"giw": "White Gelao",
"gix": "Gilima",
"giy": "Giyug",
"giz": "South Giziga",
"gji": "Geji",
"gjk": "Kachi Koli",
"gjm": "Gunditjmara",
"gjn": "Gonja",
"gjr": "Gurindji Kriol",
"gju": "Gojri",
"gka": "Guya",
"gkd": "Magi",
"gke": "Ndai",
"gkn": "Gokana",
"gko": "Kok-Nar",
"gkp": "Guinea Kpelle",
"gl": "Galician",
"glc": "Bon Gula",
"gld": "Nanai",
"glh": "Northwest Pashayi",
"glj": "Kulaal",
"glk": "Gilaki",
"glo": "Galambu",
"glr": "Glaro-Twabo",
"glu": "Gula",
"glw": "Glavda",
"gly": "Gule",
"gma": "Gambera",
"gmb": "Gula'alaa",
"gmd": "Mághdì",
"gme-bur": "Burgundian",
"gme-cgo": "Crimean Gothic",
"gmg": "Magiyi",
"gmh": "Middle High German",
"gml": "Middle Low German",
"gmm": "Gbaya-Mbodomo",
"gmn": "Gimnime",
"gmq-gut": "Gutnish",
"gmq-jmk": "Jamtish",
"gmq-mno": "Middle Norwegian",
"gmq-oda": "Old Danish",
"gmq-ogt": "Old Gutnish",
"gmq-osw": "Old Swedish",
"gmq-pro": "Proto-Norse",
"gmq-scy": "Scanian",
"gmr": "Mirning",
"gmu": "Gumalu",
"gmv": "Gamo",
"gmw-bgh": "Bergish",
"gmw-cfr": "Central Franconian",
"gmw-ecg": "East Central German",
"gmw-fin": "Fingallian",
"gmw-gts": "Gottscheerish",
"gmw-jdt": "Jersey Dutch",
"gmw-msc": "Middle Scots",
"gmw-pro": "Proto-West Germanic",
"gmw-rfr": "Rhine Franconian",
"gmw-stm": "Sathmar Swabian",
"gmw-tsx": "Transylvanian Saxon",
"gmw-vog": "Volga German",
"gmw-zps": "Zipser German",
"gmx": "Magoma",
"gmy": "Mycenaean Greek",
"gmz": "Mgbo",
"gn-cls": "Classical Guarani",
"gna": "Kaansa",
"gnb": "Gangte",
"gnc": "Guanche",
"gnd": "Zulgo-Gemzek",
"gne": "Ganang",
"gng": "Ngangam",
"gnh": "Lere",
"gni": "Gooniyandi",
"gnj": "Ngen of Djonkro",
"gnk": "ǁGana",
"gnl": "Gangulu",
"gnm": "Ginuman",
"gnn": "Gumatj",
"gnq": "Gana",
"gnr": "Gureng Gureng",
"gnt": "Guntai",
"gnu": "Gnau",
"gnw": "Western Bolivian Guarani",
"gnz": "Ganzi",
"goa": "Guro",
"gob": "Playero",
"goc": "Gorakor",
"god": "Godié",
"goe": "Gongduk",
"gof": "Gofa",
"gog": "Gogo",
"goh": "Old High German",
"goi": "Gobasi",
"goj": "Gowlan",
"gol": "Gola",
"gon": "Gondi",
"goo": "Gone Dau",
"gop": "Yeretuar",
"goq": "Gorap",
"gor": "Gorontalo",
"got": "Gothic",
"gou": "Gavar",
"gov": "Goo",
"gow": "Gorwaa",
"gox": "Gobu",
"goy": "Goundo",
"goz": "Gozarkhani",
"gpa": "Gupa-Abawa",
"gpn": "Taiap",
"gqa": "Ga'anda",
"gqi": "Guiqiong",
"gqn": "Kinikinao",
"gqr": "Gor",
"gqu": "Qau",
"gra": "Rajput Garasia",
"grc": "Ancient Greek",
"grd": "Guruntum",
"grg": "Madi",
"grh": "Gbiri-Niragu",
"gri": "Ghari",
"grj": "Southern Grebo",
"grk-cal": "Calabrian Greek",
"grk-ita": "Italiot Greek",
"grk-mar": "Mariupol Greek",
"grk-pro": "Proto-Hellenic",
"grm": "Kota Marudu Talantang",
"gro": "Groma",
"grq": "Gorovu",
"grs": "Gresi",
"grt": "Garo",
"gru": "Kistane",
"grv": "Central Grebo",
"grw": "Gweda",
"grx": "Guriaso",
"gry": "Barclayville Grebo",
"grz": "Guramalum",
"gse": "Ghanaian Sign Language",
"gsg": "German Sign Language",
"gsl": "Gusilay",
"gsm": "Guatemalan Sign Language",
"gsn": "Gusan",
"gso": "Southwest Gbaya",
"gsp": "Wasembo",
"gss": "Greek Sign Language",
"gsw": "Alemannic German",
"gta": "Guató",
"gtu": "Aghu Tharrnggala",
"gu": "Gujarati",
"gua": "Shiki",
"gub": "Guajajára",
"guc": "Wayuu",
"gud": "Yocoboué Dida",
"gue": "Gurindji",
"guf": "Gupapuyngu",
"gug": "Paraguayan Guarani",
"guh": "Guahibo",
"gui": "Eastern Bolivian Guarani",
"guk": "Gumuz",
"gul": "Gullah",
"gum": "Guambiano",
"gun": "Mbya Guarani",
"guo": "Guayabero",
"gup": "Gunwinggu",
"guq": "Aché",
"gur": "Farefare",
"gus": "Guinean Sign Language",
"gut": "Maléku Jaíka",
"guu": "Yanomamö",
"guv": "Gey",
"guw": "Gun",
"gux": "Gourmanchéma",
"guz": "Gusii",
"gv": "Manx",
"gva": "Kaskihá",
"gvc": "Guanano",
"gve": "Duwet",
"gvf": "Golin",
"gvj": "Guajá",
"gvl": "Gulay",
"gvm": "Gurmana",
"gvn": "Kuku-Yalanji",
"gvo": "Gavião do Jiparaná",
"gvp": "Pará Gavião",
"gvr": "Gurung",
"gvs": "Gumawana",
"gvy": "Guyani",
"gwa": "Mbato",
"gwb": "Gwa",
"gwc": "Kalami",
"gwd": "Gawwada",
"gwe": "Gweno",
"gwf": "Gowro",
"gwg": "Moo",
"gwi": "Gwich'in",
"gwj": "Gcwi",
"gwm": "Awngthim",
"gwn": "Gwandara",
"gwr": "Gwere",
"gwt": "Gawar-Bati",
"gwu": "Guwamu",
"gww": "Kwini",
"gwx": "Gua",
"gxx": "Wè Southern",
"gya": "Northwest Gbaya",
"gyb": "Garus",
"gyd": "Kayardild",
"gye": "Gyem",
"gyf": "Gungabula",
"gyg": "Gbayi",
"gyi": "Gyele",
"gyl": "Gayil",
"gym": "Ngäbere",
"gyn": "Guyanese Creole English",
"gyo": "Gyalsumdo",
"gyr": "Guarayu",
"gyy": "Gunya",
"gza": "Ganza",
"gzn": "Gane",
"ha": "Hausa",
"haa": "Hän",
"hab": "Hanoi Sign Language",
"hac": "Gurani",
"had": "Hatam",
"haf": "Haiphong Sign Language",
"hag": "Hanga",
"hah": "Hahon",
"hai": "Haida",
"haj": "Hajong",
"hak": "Hakka",
"hal": "Halang",
"ham": "Hewa",
"hao": "Hakö",
"hap": "Hupla",
"har": "Harari",
"has": "Haisla",
"hav": "Havu",
"haw": "Hawaiian",
"hax": "Southern Haida",
"hay": "Haya",
"hba": "Hamba",
"hbb": "Huba",
"hbn": "Heiban",
"hbu": "Habu",
"hca": "Andaman Creole Hindi",
"hch": "Huichol",
"hdn": "Northern Haida",
"hds": "Honduras Sign Language",
"hdy": "Hadiyya",
"he": "Hebrew",
"hea": "Northern Qiandong Miao",
"hed": "Herdé",
"heg": "Helong",
"heh": "Hehe",
"hei": "Heiltsuk",
"hem": "Hemba",
"hgm": "Haiǁom",
"hgw": "Haigwai",
"hhi": "Hoia Hoia",
"hhr": "Kerak",
"hhy": "Hoyahoya",
"hi": "Hindi",
"hia": "Lamang",
"hib": "Hibito",
"hid": "Hidatsa",
"hif": "Fiji Hindi",
"hig": "Kamwe",
"hih": "Pamosu",
"hii": "Hinduri",
"hij": "Hijuk",
"hik": "Seit-Kaitetu",
"hil": "Hiligaynon",
"hio": "Tshwa",
"hir": "Himarimã",
"hit": "Hittite",
"hiw": "Hiw",
"hix": "Hixkaryana",
"hji": "Haji",
"hka": "Kahe",
"hke": "Hunde",
"hkh": "Pogali",
"hkk": "Hunjara-Kaina Ke",
"hkn": "Mel-Khaonh",
"hks": "Hong Kong Sign Language",
"hla": "Halia",
"hlb": "Halbi",
"hld": "Halang Doan",
"hle": "Hlersu",
"hlt": "Nga La",
"hma": "Southern Mashan Hmong",
"hmb": "Humburi Senni",
"hmc": "Central Huishui Hmong",
"hmd": "A-Hmao",
"hme": "Eastern Huishui Hmong",
"hmf": "Hmong Don",
"hmg": "Southwestern Guiyang Hmong",
"hmh": "Southwestern Huishui Hmong",
"hmi": "Northern Huishui Hmong",
"hmj": "Ge",
"hmk": "Yemaek",
"hml": "Luopohe Hmong",
"hmm": "Central Mashan Hmong",
"hmn-pro": "Proto-Hmongic",
"hmp": "Northern Mashan Hmong",
"hmq": "Eastern Qiandong Miao",
"hmr": "Hmar",
"hms": "Southern Qiandong Miao",
"hmt": "Hamtai",
"hmu": "Hamap",
"hmv": "Hmong Dô",
"hmw": "Western Mashan Hmong",
"hmx-mie-pro": "Proto-Mienic",
"hmx-pro": "Proto-Hmong-Mien",
"hmy": "Southern Guiyang Hmong",
"hmz": "Hmong Shua",
"hna": "Mina",
"hnd": "Southern Hindko",
"hne": "Chhattisgarhi",
"hnh": "ǁAni",
"hni": "Hani",
"hnj": "Green Hmong",
"hnm": "Hainanese",
"hnn": "Hanunoo",
"hno": "Northern Hindko",
"hns": "Caribbean Hindustani",
"hnu": "Hung",
"ho": "Hiri Motu",
"hoa": "Hoava",
"hob": "Mari (Austronesian)",
"hoc": "Ho",
"hod": "Holma",
"hoe": "Horom",
"hoh": "Hobyót",
"hoi": "Holikachuk",
"hoj": "Hadoti",
"hol": "Holu",
"hom": "Homa",
"hoo": "Holoholo",
"hop": "Hopi",
"hor": "Horo",
"hos": "Ho Chi Minh City Sign Language",
"hot": "Hote",
"hov": "Hovongan",
"how": "Honi",
"hoy": "Holiya",
"hoz": "Hozo",
"hpo": "Hpon",
"hps": "Hawai'i Pidgin Sign Language",
"hra": "Hrangkhol",
"hrc": "Niwer Mil",
"hre": "Hrê",
"hrk": "Haruku",
"hrm": "Horned Miao",
"hro": "Haroi",
"hrp": "Nhirrpi",
"hrt": "Hértevin",
"hru": "Hruso",
"hrw": "Warwar Feni",
"hrx": "Hunsrik",
"hrz": "Harzani",
"hsb": "Upper Sorbian",
"hsh": "Hungarian Sign Language",
"hsl": "Hausa Sign Language",
"hsn": "Xiang",
"hss": "Harsusi",
"ht": "Haitian Creole",
"hti": "Hoti",
"hto": "Minica Huitoto",
"hts": "Hadza",
"htu": "Hitu",
"hu": "Hungarian",
"hub": "Huambisa",
"huc": "ǂHoan",
"hud": "Huaulu",
"huf": "Humene",
"hug": "Huachipaeri",
"huh": "Huilliche",
"hui": "Huli",
"huj": "Northern Guiyang Hmong",
"huk": "Hulung",
"hul": "Hula",
"hum": "Hungana",
"huo": "Hu",
"hup": "Hupa",
"huq": "Tsat",
"hur": "Halkomelem",
"hus": "Wastek",
"huu": "Murui Huitoto",
"huv": "Huave",
"huw": "Hukumina",
"hux": "Nüpode Huitoto",
"huy": "Hulaulá",
"huz": "Hunzib",
"hvc": "Haitian Vodoun Culture Language",
"hvk": "Haveke",
"hvn": "Sabu",
"hwa": "Wané",
"hwc": "Hawaiian Creole",
"hwo": "Hwana",
"hy": "Armenian",
"hya": "Hya",
"hyx-pro": "Proto-Armenian",
"hz": "Herero",
"ia": "Interlingua",
"iai": "Iaai",
"ian": "Iatmul",
"iar": "Purari",
"iba": "Iban",
"ibb": "Ibibio",
"ibd": "Iwaidja",
"ibe": "Akpes",
"ibg": "Ibanag",
"ibh": "Bih",
"ibl": "Ibaloi",
"ibm": "Agoi",
"ibn": "Ibino",
"ibr": "Ibuoro",
"ibu": "Ibu",
"iby": "Ibani",
"ica": "Ede Ica",
"ich": "Etkywan",
"icl": "Icelandic Sign Language",
"icr": "Islander Creole English",
"id": "Indonesian",
"ida": "Idakho-Isukha-Tiriki",
"idb": "Indo-Portuguese",
"idc": "Idon",
"idd": "Ede Idaca",
"ide": "Idere",
"idi": "Idi",
"idr": "Indri",
"ids": "Idesa",
"idt": "Idaté",
"idu": "Idoma",
"ie": "Interlingue",
"ifa": "Amganad Ifugao",
"ifb": "Batad Ifugao",
"ife": "Ifè",
"iff": "Ifo",
"ifk": "Tuwali Ifugao",
"ifm": "Teke-Fuumu",
"ifu": "Mayoyao Ifugao",
"ify": "Keley-I Kallahan",
"ig": "Igbo",
"igb": "Ebira",
"ige": "Igede",
"igg": "Igana",
"igl": "Igala",
"igm": "Kanggape",
"ign": "Ignaciano",
"igo": "Isebe",
"igs": "Glosa",
"igw": "Igwe",
"ihb": "Pidgin Iha",
"ihi": "Ihievbe",
"ihp": "Iha",
"ii": "Nuosu",
"iir-nur-pro": "Proto-Nuristani",
"iir-pro": "Proto-Indo-Iranian",
"ijc": "Izon",
"ije": "Biseni",
"ijj": "Ede Ije",
"ijn": "Kalabari",
"ijo-pro": "Proto-Ijoid",
"ijs": "Southeast Ijo",
"ik": "Inupiaq",
"ike": "Eastern Canadian Inuktitut",
"iki": "Iko",
"ikk": "Ika",
"ikl": "Ikulu",
"iko": "Olulumo-Ikom",
"ikp": "Ikpeshi",
"ikr": "Ikaranggal",
"iks": "Inuit Sign Language",
"ikt": "Inuvialuktun",
"ikv": "Iku-Gora-Ankwa",
"ikw": "Ikwere",
"ikx": "Ik",
"ikz": "Ikizu",
"ila": "Ile Ape",
"ilb": "Ila",
"ilg": "Ilgar",
"ili": "Ili Turki",
"ilk": "Ilongot",
"ill": "Iranun",
"ilo": "Ilocano",
"ils": "International Sign",
"ilu": "Ili'uun",
"ilv": "Ilue",
"ima": "Mala Malasar",
"imi": "Anamgura",
"iml": "Miluk",
"imn": "Imonda",
"imo": "Imbongu",
"imr": "Imroing",
"ims": "Marsian",
"imy": "Milyan",
"inb": "Inga",
"inc-apa": "Apabhramsa",
"inc-ash": "Ashokan Prakrit",
"inc-dng-pro": "Proto-Dangari",
"inc-kam": "Kamarupi Prakrit",
"inc-kho": "Kholosi",
"inc-krd-pro": "Proto-Kamta",
"inc-mas": "Middle Assamese",
"inc-mbn": "Middle Bengali",
"inc-mgu": "Middle Gujarati",
"inc-mor": "Middle Odia",
"inc-oas": "Early Assamese",
"inc-oaw": "Old Awadhi",
"inc-obn": "Old Bengali",
"inc-ogu": "Old Gujarati",
"inc-ohi": "Old Hindi",
"inc-oor": "Old Odia",
"inc-opa": "Old Punjabi",
"inc-pro": "Proto-Indo-Aryan",
"ine-ana-pro": "Proto-Anatolian",
"ine-bsl-pro": "Proto-Balto-Slavic",
"ine-kal": "Kalašma",
"ine-pae": "Paeonian",
"ine-pro": "Proto-Indo-European",
"ine-toc-pro": "Proto-Tocharian",
"ing": "Deg Xinag",
"inh": "Ingush",
"inj": "Jungle Inga",
"inl": "Indonesian Sign Language",
"inm": "Minaean",
"inn": "Isinai",
"ino": "Inoke-Yate",
"inp": "Iñapari",
"ins": "Indian Sign Language",
"int": "Intha",
"inz": "Ineseño",
"io": "Ido",
"ior": "Inor",
"iou": "Tuma-Irumu",
"iow": "Chiwere",
"ipi": "Ipili",
"ipo": "Ipiko",
"iqu": "Iquito",
"iqw": "Ikwo",
"ira-kms-pro": "Proto-Komisenian",
"ira-mny-pro": "Proto-Munji-Yidgha",
"ira-mpr-pro": "Proto-Medo-Parthian",
"ira-pat-pro": "Proto-Pathan",
"ira-pro": "Proto-Iranian",
"ira-sgc-pro": "Proto-Sogdic",
"ira-sgi-pro": "Proto-Sanglechi-Ishkashimi",
"ira-shr-pro": "Proto-Shughni-Roshani",
"ira-shy-pro": "Proto-Shughni-Yazghulami",
"ira-sym-pro": "Proto-Shughni-Yazghulami-Munji",
"ira-wnj": "Vanji",
"ira-zgr-pro": "Proto-Zaza-Gorani",
"ire": "Iresim",
"irh": "Irarutu",
"iri": "Rigwe",
"irk": "Iraqw",
"irn": "Irantxe",
"iro-ere": "Erie",
"iro-min": "Mingo",
"iro-nor-pro": "Proto-North Iroquoian",
"iro-pro": "Proto-Iroquoian",
"irr": "Ir",
"iru": "Irula",
"irx": "Kamberau",
"iry": "Iraya",
"is": "Icelandic",
"isa": "Isabi",
"isc": "Isconahua",
"isd": "Isnag",
"ise": "Italian Sign Language",
"isg": "Irish Sign Language",
"ish": "Esan",
"isi": "Nkem-Nkum",
"isk": "Ishkashimi",
"ism": "Masimasi",
"isn": "Isanzu",
"iso": "Isoko",
"isr": "Israeli Sign Language",
"ist": "Istriot",
"isu": "Isu",
"isv": "Interslavic",
"it": "Italian",
"itb": "Binongan Itneg",
"itc-pro": "Proto-Italic",
"itc-psa": "Pre-Samnite",
"itd": "Southern Tidung",
"ite": "Itene",
"iti": "Inlaod Itneg",
"itk": "Judeo-Italian",
"itl": "Itelmen",
"itm": "Itu Mbon Uzo",
"ito": "Itonama",
"itr": "Iteri",
"its": "Itsekiri",
"itt": "Maeng Itneg",
"itv": "Itawit",
"itw": "Ito",
"itx": "Itik",
"ity": "Moyadan Itneg",
"itz": "Itza'",
"iu": "Inuktitut",
"ium": "Iu Mien",
"ivb": "Ibatan",
"ivv": "Ivatan",
"iwk": "I-Wak",
"iwm": "Iwam",
"iwo": "Iwur",
"iws": "Sepik Iwam",
"ixc": "Ixcatec",
"ixl": "Ixil",
"iya": "Iyayu",
"iyo": "Mesaka",
"iyx": "Yaa",
"izh": "Ingrian",
"izi": "Izi-Ezaa-Ikwo-Mgbo",
"izr": "Izere",
"izz": "Izi",
"ja": "Japanese",
"jaa": "Jamamadí",
"jab": "Hyam",
"jac": "Jakaltek",
"jad": "Jahanka",
"jae": "Jabem",
"jaf": "Jara",
"jah": "Jah Hut",
"jaj": "Zazao",
"jal": "Yalahatan",
"jam": "Jamaican Creole",
"jan": "Janday",
"jao": "Yanyuwa",
"jaq": "Yaqay",
"jas": "New Caledonian Javanese",
"jat": "Jakati",
"jau": "Yaur",
"jax": "Jambi Malay",
"jay": "Yan-nhangu",
"jaz": "Jawe",
"jbi": "Badjiri",
"jbj": "Arandai",
"jbk": "Barikewa",
"jbn": "Nefusa",
"jbo": "Lojban",
"jbr": "Jofotek-Bromnya",
"jbt": "Jabutí",
"jbu": "Jukun Takum",
"jbw": "Yawijibaya",
"jcs": "Jamaican Country Sign Language",
"jct": "Krymchak",
"jda": "Jad",
"jdg": "Jadgali",
"jdt": "Judeo-Tat",
"jeb": "Jebero",
"jee": "Jerung",
"jeg": "Jeng",
"jeh": "Jeh",
"jei": "Yei",
"jek": "Jeri Kuo",
"jel": "Yelmek",
"jen": "Dza",
"jer": "Jere",
"jet": "Manem",
"jeu": "Jonkor Bourmataguil",
"jgb": "Ngbee",
"jgk": "Gwak",
"jgo": "Ngomba",
"jhi": "Jehai",
"jhs": "Jhankot Sign Language",
"jia": "Jina",
"jib": "Jibu",
"jic": "Tol",
"jid": "Bu",
"jie": "Jilbe",
"jig": "Jingulu",
"jih": "Shangzhai",
"jii": "Jiiddu",
"jil": "Jilim",
"jim": "Jimjimen",
"jio": "Jiamao",
"jiq": "Khroskyabs",
"jit": "Jita",
"jiu": "Youle Jino",
"jiv": "Shuar",
"jiy": "Buyuan Jino",
"jje": "Jeju",
"jjr": "Zhár",
"jka": "Kaera",
"jko": "Kubo",
"jkp": "Paku Karen",
"jkr": "Koro (India)",
"jku": "Labir",
"jle": "Ngile",
"jls": "Jamaican Sign Language",
"jma": "Dima",
"jmb": "Zumbun",
"jmc": "Machame",
"jmd": "Yamdena",
"jmi": "Jimi",
"jml": "Jumli",
"jmn": "Makuri Naga",
"jmr": "Kamara",
"jmw": "Mouwase",
"jmx": "Western Juxtlahuaca Mixtec",
"jna": "Jangshung",
"jnd": "Jandavra",
"jng": "Yangman",
"jni": "Janji",
"jnj": "Yemsa",
"jnl": "Rawat",
"jns": "Jaunsari",
"job": "Joba",
"jod": "Wojenaka",
"jor": "Jorá",
"jos": "Jordanian Sign Language",
"jow": "Jowulu",
"jpr": "Judeo-Persian",
"jpx-hcj": "Hachijō",
"jpx-pro": "Proto-Japonic",
"jpx-ryu-pro": "Proto-Ryukyuan",
"jqr": "Jaqaru",
"jra": "Jarai",
"jrr": "Jiru",
"jru": "Japrería",
"jsl": "Japanese Sign Language",
"jua": "Júma",
"jub": "Wannu",
"juc": "Jurchen",
"jud": "Worodougou",
"juh": "Hone",
"jui": "Ngadjuri",
"juk": "Wapan",
"jul": "Jirel",
"jum": "Jumjum",
"jun": "Juang",
"juo": "Jiba",
"jup": "Hupdë",
"jur": "Jurúna",
"jus": "Jumla Sign Language",
"jut": "Jutish",
"juu": "Ju",
"juw": "Wãpha",
"juy": "Juray",
"jv": "Javanese",
"jvd": "Javindo",
"jvn": "Caribbean Javanese",
"jwi": "Jwira-Pepesa",
"jyy": "Jaya",
"ka": "Georgian",
"kaa": "Karakalpak",
"kab": "Kabyle",
"kac": "Jingpho",
"kad": "Kadara",
"kae": "Ketangalan",
"kaf": "Katso",
"kag": "Kajaman",
"kah": "Fer",
"kai": "Karekare",
"kaj": "Jju",
"kak": "Kayapa Kallahan",
"kam": "Kamba",
"kao": "Kassonke",
"kap": "Bezhta",
"kaq": "Capanahua",
"kar-pro": "Proto-Karen",
"kaw": "Old Javanese",
"kax": "Kao",
"kay": "Kamayurá",
"kba": "Kalarko",
"kbb": "Kaxuyana",
"kbc": "Kadiwéu",
"kbd": "East Circassian",
"kbe": "Kanju",
"kbh": "Camsá",
"kbi": "Kaptiau",
"kbj": "Kari",
"kbk": "Grass Koiari",
"kbm": "Iwal",
"kbn": "Kare (Central Africa)",
"kbo": "Keliko",
"kbp": "Kabiye",
"kbq": "Kamano",
"kbr": "Kafa",
"kbs": "Kande",
"kbt": "Gabadi",
"kbu": "Kabutra",
"kbv": "Kamberataro",
"kbw": "Kaiep",
"kbx": "Ap Ma",
"kbz": "Duhwa",
"kca-eas": "Eastern Khanty",
"kca-nor": "Northern Khanty",
"kca-pro": "Proto-Khanty",
"kca-sou": "Southern Khanty",
"kcb": "Kawacha",
"kcc": "Lubila",
"kcd": "Ngkâlmpw Kanum",
"kce": "Kaivi",
"kcf": "Ukaan",
"kcg": "Tyap",
"kch": "Vono",
"kci": "Kamantan",
"kcj": "Kobiana",
"kck": "Kalanga",
"kcl": "Kala",
"kcm": "Tar Gula",
"kcn": "Nubi",
"kco": "Kinalakna",
"kcp": "Kanga",
"kcq": "Kamo",
"kcr": "Katla",
"kcs": "Koenoem",
"kct": "Kaian",
"kcu": "Kikami",
"kcv": "Kete",
"kcw": "Kabwari",
"kcx": "Kachama-Ganjule",
"kcy": "Korandje",
"kcz": "Konongo",
"kda": "Worimi",
"kdc": "Kutu",
"kdd": "Yankunytjatjara",
"kde": "Makonde",
"kdf": "Mamusi",
"kdg": "Seba",
"kdh": "Tem",
"kdi": "Kumam",
"kdj": "Karamojong",
"kdk": "Numèè",
"kdl": "Tsikimba",
"kdm": "Kagoma",
"kdn": "Kunda",
"kdp": "Kaningdon-Nindem",
"kdq": "Koch",
"kdr": "Karaim",
"kdt": "Kuy",
"kdu": "Kadaru",
"kdv": "Kado",
"kdw": "Koneraw",
"kdx": "Kam",
"kdy": "Keder",
"kdz": "Kwaja",
"kea": "Kabuverdianu",
"keb": "Kélé",
"kec": "Keiga",
"ked": "Kerewe",
"kee": "Eastern Keres",
"kef": "Kpessi",
"keg": "Tese",
"keh": "Keak",
"kei": "Kei",
"kej": "Kadar",
"kek": "Q'eqchi",
"kel": "Kela-Yela",
"kem": "Kemak",
"ken": "Kenyang",
"keo": "Kakwa",
"kep": "Kaikadi",
"keq": "Kamar",
"ker": "Kera",
"kes": "Kugbo",
"ket": "Ket",
"keu": "Akebu",
"kev": "Kanikkaran",
"kew": "Kewa",
"kex": "Kukna",
"key": "Kupia",
"kez": "Kukele",
"kfa": "Kodava",
"kfb": "Kolami",
"kfc": "Konda-Dora",
"kfd": "Korra Koraga",
"kfe": "Kota (India)",
"kff": "Koya",
"kfg": "Kudiya",
"kfh": "Kurichiya",
"kfi": "Kannada Kurumba",
"kfj": "Kemiehua",
"kfk": "Kinnauri",
"kfl": "Kung",
"kfn": "Kuk",
"kfo": "Koro (West Africa)",
"kfp": "Korwa",
"kfq": "Korku",
"kfr": "Kachchi",
"kfs": "Bilaspuri",
"kft": "Kanjari",
"kfu": "Katkari",
"kfv": "Kurmukar",
"kfw": "Kharam Naga",
"kfx": "Kullu Pahari",
"kfy": "Kumaoni",
"kfz": "Koromfé",
"kg": "Kongo",
"kga": "Koyaga",
"kgb": "Kawe",
"kgd": "Kataang",
"kge": "Komering",
"kgf": "Kube",
"kgg": "Kusunda",
"kgi": "Selangor Sign Language",
"kgj": "Gamale Kham",
"kgk": "Kaiwá",
"kgl": "Kunggari",
"kgn": "Karingani",
"kgo": "Krongo",
"kgp": "Kaingang",
"kgq": "Kamoro",
"kgr": "Abun",
"kgs": "Kumbainggar",
"kgt": "Somyev",
"kgu": "Kobol",
"kgv": "Karas",
"kgw": "Karon Dori",
"kgx": "Kamaru",
"kgy": "Kyerung",
"kha": "Khasi",
"khb": "Lü",
"khc": "North Tukang Besi",
"khd": "Bädi Kanum",
"khe": "Korowai",
"khf": "Khuen",
"khh": "Kehu",
"khi-kho-pro": "Proto-Khoe",
"khi-kun": "ǃKung",
"khj": "Kuturmi",
"khl": "Lusi",
"khn": "Khandeshi",
"kho": "Khotanese",
"khp": "Kapauri",
"khq": "Koyra Chiini",
"khr": "Kharia",
"khs": "Kasua",
"kht": "Khamti",
"khu": "Nkhumbi",
"khv": "Khvarshi",
"khw": "Khowar",
"khx": "Kanu",
"khy": "Ekele",
"khz": "Keapara",
"ki": "Kikuyu",
"kia": "Kim",
"kib": "Koalib",
"kic": "Kickapoo",
"kid": "Koshin",
"kie": "Kibet",
"kif": "Eastern Parbate Kham",
"kig": "Kimaama",
"kih": "Kilmeri",
"kii": "Kitsai",
"kij": "Kilivila",
"kil": "Kariya",
"kim": "Tofa",
"kio": "Kiowa",
"kip": "Sheshi Kham",
"kiq": "Kosadle",
"kis": "Kis",
"kit": "Agob",
"kiv": "Kimbu",
"kiw": "Northeast Kiwai",
"kix": "Khiamniungan Naga",
"kiy": "Kirikiri",
"kiz": "Kisi",
"kj": "Kwanyama",
"kja": "Mlap",
"kjb": "Q'anjob'al",
"kjc": "Coastal Konjo",
"kjd": "Southern Kiwai",
"kje": "Kisar",
"kjg": "Khmu",
"kjh": "Khakas",
"kji": "Zabana",
"kjj": "Khinalug",
"kjk": "Highland Konjo",
"kjl": "Western Parbate Kham",
"kjm": "Kháng",
"kjn": "Kunjen",
"kjo": "Harijan Kinnauri",
"kjp": "Eastern Pwo",
"kjq": "Western Keres",
"kjr": "Kurudu",
"kjs": "East Kewa",
"kjt": "Phrae Pwo",
"kju": "Kashaya",
"kjx": "Ramopa",
"kjy": "Erave",
"kjz": "Bumthangkha",
"kk": "Kazakh",
"kka": "Kakanda",
"kkb": "Kwerisa",
"kkc": "Odoodee",
"kkd": "Kinuku",
"kke": "Kakabe",
"kkf": "Kalaktang Monpa",
"kkg": "Mabaka Valley Kalinga",
"kkh": "Khün",
"kki": "Kagulu",
"kkj": "Kako",
"kkk": "Kokota",
"kkl": "Kosarek Yale",
"kkm": "Kiong",
"kkn": "Kon Keu",
"kko": "Karko",
"kkp": "Koko-Bera",
"kkq": "Kaiku",
"kkr": "Kir-Balar",
"kks": "Kirfi",
"kkt": "Koi",
"kku": "Tumi",
"kkv": "Kangean",
"kkw": "Teke-Kukuya",
"kkx": "Kohin",
"kky": "Guugu Yimidhirr",
"kkz": "Kaska",
"kl": "Greenlandic",
"kla": "Klamath-Modoc",
"klb": "Kiliwa",
"klc": "Kolbila",
"kld": "Gamilaraay",
"kle": "Kulung",
"klf": "Kendeje",
"klg": "Tagakaulu Kalagan",
"klh": "Weliki",
"kli": "Kalumpang",
"klj": "Khalaj",
"klk": "Kono (Nigeria)",
"kll": "Kagan Kalagan",
"klm": "Kolom",
"kln": "Kalenjin",
"klo": "Kapya",
"klp": "Kamasa",
"klq": "Rumu",
"klr": "Khaling",
"kls": "Kalasha",
"klt": "Nukna",
"klu": "Klao",
"klv": "Maskelynes",
"klw": "Lindu",
"klx": "Koluwawa",
"kly": "Kalao",
"klz": "Kabola",
"km": "Khmer",
"kma": "Konni",
"kmb": "Kimbundu",
"kmc": "Southern Kam",
"kmd": "Madukayang Kalinga",
"kme": "Bakole",
"kmf": "Kare (New Guinea)",
"kmg": "Kâte",
"kmh": "Kalam",
"kmi": "Kami",
"kmj": "Kumarbhag Paharia",
"kmk": "Limos Kalinga",
"kml": "Tanudan Kalinga",
"kmm": "Kom (India)",
"kmn": "Awtuw",
"kmo": "Kwoma",
"kmp": "Gimme",
"kmq": "Kwama",
"kmr": "Northern Kurdish",
"kms": "Kamasau",
"kmt": "Kemtuik",
"kmu": "Kanite",
"kmv": "Karipúna Creole French",
"kmw": "Kumu",
"kmx": "Waboda",
"kmy": "Koma",
"kmz": "Khorasani Turkish",
"kn": "Kannada",
"kna": "Kanakuru",
"knb": "Lubuagan Kalinga",
"knd": "Konda",
"kne": "Kankanaey",
"knf": "Mankanya",
"kni": "Kanufi",
"knj": "Akatek",
"knk": "Kuranko",
"knl": "Keninjal",
"knm": "Kanamari",
"kno": "Kono (Sierra Leone)",
"knp": "Kwanja",
"knq": "Kintaq",
"knr": "Kaningra",
"kns": "Kensiu",
"knt": "Katukina",
"knu": "Kono (Guinea)",
"knv": "Tabo",
"knx": "Kendayan",
"kny": "Kanyok",
"knz": "Kalamsé",
"ko": "Korean",
"ko-ear": "Early Modern Korean",
"koa": "Konomala",
"koc": "Kpati",
"kod": "Kodi",
"koe": "Kacipo-Balesi",
"kof": "Kubi",
"kog": "Cogui",
"koh": "Koyo",
"koi": "Komi-Permyak",
"kok": "Konkani",
"kol": "Kol (New Guinea)",
"koo": "Konzo",
"kop": "Waube",
"koq": "Kota (Gabon)",
"kos": "Kosraean",
"kot": "Lagwan",
"kou": "Koke",
"kov": "Kudu-Camo",
"kow": "Kugama",
"koy": "Koyukon",
"koz": "Korak",
"kpa": "Kutto",
"kpb": "Mullu Kurumba",
"kpc": "Curripaco",
"kpd": "Koba",
"kpe": "Kpelle",
"kpf": "Komba",
"kpg": "Kapingamarangi",
"kph": "Kplang",
"kpi": "Kofei",
"kpj": "Karajá",
"kpk": "Kpan",
"kpl": "Kpala",
"kpm": "Koho",
"kpn": "Kepkiriwát",
"kpo": "Ikposo",
"kpq": "Korupun-Sela",
"kpr": "Korafe-Yegha",
"kps": "Tehit",
"kpt": "Karata",
"kpu": "Kafoa",
"kpv": "Komi-Zyrian",
"kpw": "Kobon",
"kpx": "Mountain Koiari",
"kpy": "Koryak",
"kpz": "Kupsabiny",
"kqa": "Mum",
"kqb": "Kovai",
"kqc": "Doromu-Koki",
"kqd": "Koy Sanjaq Surat",
"kqe": "Kalagan",
"kqf": "Kakabai",
"kqg": "Khe",
"kqh": "Kisankasa",
"kqi": "Koitabu",
"kqj": "Koromira",
"kqk": "Kotafon Gbe",
"kql": "Kyenele",
"kqm": "Khisa",
"kqn": "Kaonde",
"kqo": "Eastern Krahn",
"kqp": "Kimré",
"kqq": "Krenak",
"kqr": "Kimaragang",
"kqs": "Northern Kissi",
"kqt": "Klias River Kadazan",
"kqu": "Seroa",
"kqv": "Okolod",
"kqw": "Kandas",
"kqx": "Mser",
"kqy": "Koorete",
"kqz": "Korana",
"kr": "Kanuri",
"kra": "Kumhali",
"krb": "Karkin",
"krc": "Karachay-Balkar",
"krd": "Kairui-Midiki",
"kre": "Panará",
"krf": "Koro (Vanuatu)",
"krh": "Kurama",
"kri": "Krio",
"krj": "Kinaray-a",
"krk": "Kerek",
"krl": "Karelian",
"krm": "Krim",
"krn": "Sapo",
"kro-pro": "Proto-Kru",
"krp": "Korop",
"krr": "Kru'ng",
"krs": "Kresh",
"kru": "Kurux",
"krv": "Kavet",
"krw": "Western Krahn",
"krx": "Karon",
"kry": "Kryts",
"krz": "Sota Kanum",
"ks": "Kashmiri",
"ksa": "Shuwa-Zamani",
"ksb": "Shambala",
"ksc": "Southern Kalinga",
"ksd": "Tolai",
"kse": "Kuni",
"ksf": "Bafia",
"ksg": "Kusaghe",
"ksi": "Krisa",
"ksj": "Uare",
"ksk": "Kansa",
"ksl": "Kumalu",
"ksm": "Kumba",
"ksn": "Kasiguranin",
"kso": "Kofa",
"ksp": "Kaba",
"ksq": "Kwaami",
"ksr": "Borong",
"kss": "Southern Kissi",
"kst": "Winyé",
"ksu": "Khamyang",
"ksv": "Kusu",
"ksw": "S'gaw Karen",
"ksx": "Kedang",
"ksy": "Kharia Thar",
"ksz": "Kodaku",
"kta": "Katua",
"ktb": "Kambaata",
"ktc": "Kholok",
"ktd": "Kokata",
"ktf": "Kwami",
"ktg": "Kalkatungu",
"kth": "Karanga",
"kti": "North Muyu",
"ktj": "Plapo Krumen",
"ktk": "Kaniet",
"ktl": "Koroshi",
"ktm": "Kurti",
"ktn": "Karitiâna",
"kto": "Kuot",
"ktp": "Kaduo",
"ktq": "Katabaga",
"ktr": "Kota Marudu Tinagas",
"kts": "South Muyu",
"ktt": "Ketum",
"ktu": "Kituba",
"ktv": "Eastern Katu",
"ktw": "Kato",
"ktx": "Kaxararí",
"kty": "Kango",
"ktz": "Juǀ'hoan",
"ku-pro": "Proto-Kurdish",
"kub": "Kutep",
"kuc": "Kwinsu",
"kud": "Auhelawa",
"kue": "Kuman",
"kuf": "Western Katu",
"kug": "Kupa",
"kuh": "Kushi",
"kui": "Kuikúro",
"kuj": "Kuria",
"kuk": "Kepo'",
"kul": "Kulere",
"kum": "Kumyk",
"kun": "Kunama",
"kuo": "Kumukio",
"kup": "Kunimaipa",
"kuq": "Karipuna",
"kus": "Kusaal",
"kut": "Ktunaxa",
"kuu": "Upper Kuskokwim",
"kuv": "Kur",
"kuw": "Kpagua",
"kux": "Kukatja",
"kuy": "Kuuku-Ya'u",
"kuz": "Kunza",
"kva": "Bagvalal",
"kvb": "Kubu",
"kvc": "Kove",
"kvd": "Kui (Indonesia)",
"kve": "Kalabakan",
"kvf": "Kabalai",
"kvg": "Kuni-Boazi",
"kvh": "Komodo",
"kvi": "Kwang",
"kvj": "Psikye",
"kvk": "Korean Sign Language",
"kvl": "Brek Karen",
"kvm": "Kendem",
"kvn": "Border Kuna",
"kvo": "Dobel",
"kvp": "Kompane",
"kvq": "Geba Karen",
"kvr": "Kerinci",
"kvt": "Lahta Karen",
"kvu": "Yinbaw Karen",
"kvv": "Kola",
"kvw": "Wersing",
"kvx": "Parkari Koli",
"kvy": "Yintale Karen",
"kvz": "Tsakwambo",
"kw": "Cornish",
"kwa": "Dâw",
"kwb": "Baa",
"kwc": "Likwala",
"kwd": "Kwaio",
"kwe": "Kwerba",
"kwf": "Kwara'ae",
"kwg": "Sara Kaba Deme",
"kwh": "Kowiai",
"kwi": "Awa-Cuaiquer",
"kwj": "Kwanga",
"kwk": "Kwak'wala",
"kwl": "Kofyar",
"kwm": "Kwambi",
"kwn": "Kwangali",
"kwo": "Kwomtari",
"kwp": "Kodia",
"kwq": "Kwak",
"kwr": "Kwer",
"kws": "Kwese",
"kwt": "Kwesten",
"kwu": "Kwakum",
"kwv": "Sara Kaba Náà",
"kww": "Kwinti",
"kwx": "Khirwar",
"kwz": "Kwadi",
"kxa": "Kairiru",
"kxb": "Krobu",
"kxc": "Konso",
"kxd": "Brunei Malay",
"kxe": "Kakihum",
"kxf": "Manumanaw Karen",
"kxh": "Karo",
"kxi": "Keningau Murut",
"kxj": "Kulfa",
"kxk": "Zayein Karen",
"kxm": "Northern Khmer",
"kxn": "Kanowit",
"kxo": "Kanoé",
"kxp": "Wadiyara Koli",
"kxq": "Smärky Kanum",
"kxr": "Manus Koro",
"kxs": "Kangjia",
"kxt": "Koiwat",
"kxu": "Kui (India)",
"kxv": "Kuvi",
"kxw": "Konai",
"kxx": "Likuba",
"kxy": "Kayong",
"kxz": "Kerewo",
"ky": "Kyrgyz",
"kya": "Kwaya",
"kyb": "Butbut Kalinga",
"kyc": "Kyaka",
"kyd": "Karey",
"kye": "Krache",
"kyf": "Kouya",
"kyg": "Keyagana",
"kyh": "Karok",
"kyi": "Kiput",
"kyj": "Karao",
"kyk": "Kamayo",
"kyl": "Kalapuya",
"kym": "Kpatili",
"kyn": "Karolanos",
"kyo": "Kelon",
"kyp": "Kang",
"kyq": "Kenga",
"kyr": "Kuruáya",
"kys": "Baram Kayan",
"kyt": "Kayagar",
"kyu": "Western Kayah",
"kyv": "Kayort",
"kyw": "Kudmali",
"kyx": "Rapoisi",
"kyy": "Kambaira",
"kyz": "Kayabí",
"kza": "Western Karaboro",
"kzb": "Kaibobo",
"kzc": "Bondoukou Kulango",
"kzd": "Kadai",
"kzf": "Da'a Kaili",
"kzg": "Kikai",
"kzh": "Dongolawi",
"kzi": "Kelabit",
"kzj": "Coastal Kadazan",
"kzk": "Kazukuru",
"kzl": "Kayeli",
"kzm": "Kais",
"kzn": "Kokola",
"kzo": "Kaningi",
"kzp": "Kaidipang",
"kzq": "Kaike",
"kzr": "Karang",
"kzs": "Sugut Dusun",
"kzt": "Tambunan Dusun",
"kzu": "Kayupulau",
"kzv": "Komyandaret",
"kzw": "Kariri",
"kzx": "Kamarian",
"kzy": "Kango-Sua",
"kzz": "Kalabra",
"la": "Latin",
"laa": "Lapuyan Subanun",
"lab": "Linear A",
"lac": "Lacandon",
"lad": "Ladino",
"lae": "Pattani",
"laf": "Lafofa",
"lag": "Langi",
"lah": "Lahnda",
"lai": "Lambya",
"laj": "Lango (Uganda)",
"lak": "Laka",
"lam": "Lamba",
"lan": "Laru",
"lap": "Kabba-Laka",
"laq": "Qabiao",
"lar": "Larteh",
"las": "Gur Lama",
"lau": "Laba",
"law": "Lauje",
"lax": "Tiwa",
"lay": "Lama Bai",
"laz": "Aribwatsa",
"lb": "Luxembourgish",
"lbb": "Label",
"lbc": "Lakkia",
"lbe": "Lak",
"lbf": "Tinani",
"lbg": "Laopang",
"lbi": "La'bi",
"lbj": "Ladakhi",
"lbk": "Central Bontoc",
"lbl": "Libon Bikol",
"lbm": "Lodhi",
"lbn": "Lamet",
"lbo": "Laven",
"lbq": "Wampar",
"lbr": "Northern Lorung",
"lbs": "Libyan Sign Language",
"lbt": "Lachi",
"lbu": "Labu",
"lbv": "Lavatbura-Lamusong",
"lbw": "Tolaki",
"lbx": "Lawangan",
"lby": "Lamu-Lamu",
"lbz": "Lardil",
"lcc": "Legenyem",
"lcd": "Lola",
"lce": "Loncong",
"lcf": "Lubu",
"lch": "Luchazi",
"lcl": "Lisela",
"lcm": "Tungag",
"lcp": "Western Lawa",
"lcq": "Luhu",
"lcs": "Lisabata-Nuniali",
"lda": "Kla",
"ldb": "Idun",
"ldd": "Luri (Nigeria)",
"ldg": "Lenyima",
"ldh": "Lamja-Dengsa-Tola",
"ldj": "Lemoro",
"ldk": "Leelau",
"ldl": "Kaan",
"ldm": "Landoma",
"ldn": "Láadan",
"ldo": "Loo",
"ldp": "Tso",
"ldq": "Lufu",
"lea": "Lega-Shabunda",
"leb": "Lala-Bisa",
"lec": "Leco",
"led": "Lendu",
"lee": "Lyélé",
"lef": "Lelemi",
"leh": "Lenje",
"lei": "Lemio",
"lej": "Lengola",
"lek": "Leipon",
"lel": "Lele (Congo)",
"lem": "Nomaande",
"len": "Honduran Lenca",
"leo": "Mengisa",
"lep": "Lepcha",
"leq": "Lembena",
"ler": "Lenkau",
"les": "Lese",
"let": "Lesing-Gelimi",
"leu": "Kara (New Guinea)",
"lev": "Lamma",
"lew": "Ledo Kaili",
"lex": "Luang",
"ley": "Lemolang",
"lez": "Lezgi",
"lfa": "Lefa",
"lfn": "Lingua Franca Nova",
"lg": "Luganda",
"lga": "Lungga",
"lgb": "Laghu",
"lgg": "Lugbara",
"lgh": "Laghuu",
"lgi": "Lengilu",
"lgk": "Neverver",
"lgl": "Wala",
"lgm": "Lega-Mwenga",
"lgn": "Opuuo",
"lgq": "Logba",
"lgr": "Lengo",
"lgs": "Guinea-Bissau Sign Language",
"lgt": "Pahi",
"lgu": "Longgu",
"lgz": "Ligenza",
"lha": "Laha (Vietnam)",
"lhh": "Laha (Indonesia)",
"lhi": "Lahu Shi",
"lhl": "Lahul Lohar",
"lhn": "Lahanan",
"lhp": "Lhokpu",
"lhs": "Mlahsö",
"lht": "Lo-Toga",
"lhu": "Lahu",
"li": "Limburgish",
"lia": "West-Central Limba",
"lib": "Likum",
"lic": "Hlai",
"lid": "Nyindrou",
"lie": "Likila",
"lif": "Limbu",
"lig": "Ligbi",
"lih": "Lihir",
"lii": "Lingkhim",
"lij": "Ligurian",
"lik": "Lika",
"lil": "Lillooet",
"lio": "Liki",
"lip": "Sekpele",
"liq": "Libido",
"lir": "Liberian Kreyol",
"lis": "Lisu",
"liu": "Logorik",
"liv": "Livonian",
"liw": "Col",
"lix": "Liabuku",
"liy": "Banda-Bambari",
"liz": "Libinza",
"lja": "Golpa",
"lje": "Rampi",
"lji": "Laiyolo",
"ljl": "Li'o",
"ljp": "Lampung Api",
"ljw": "Yirandali",
"ljx": "Yuru",
"lka": "Lakalei",
"lkb": "Kabras",
"lkc": "Kucong",
"lkd": "Lakondê",
"lke": "Kenyi",
"lkh": "Lakha",
"lki": "Laki",
"lkj": "Remun",
"lkl": "Laeko-Libuat",
"lkm": "Kalaamaya",
"lkn": "Lakon",
"lko": "Khayo",
"lkr": "Päri",
"lks": "Kisa",
"lkt": "Lakota",
"lku": "Kungkari",
"lky": "Lokoya",
"lla": "Lala-Roba",
"llb": "Lolo",
"llc": "Lele (Guinea)",
"lld": "Ladin",
"lle": "Lele (New Guinea)",
"llf": "Hermit",
"llg": "Lole",
"llh": "Lamu",
"lli": "Teke-Laali",
"llj": "Ladji-Ladji",
"llk": "Lelak",
"lll": "Lilau",
"llm": "Lasalimu",
"lln": "Lele (Chad)",
"llp": "North Efate",
"llq": "Lolak",
"lls": "Lithuanian Sign Language",
"llu": "Lau",
"llx": "Lauan",
"lma": "East Limba",
"lmb": "Merei",
"lmc": "Limilngan",
"lmd": "Lumun",
"lme": "Pévé",
"lmf": "South Lembata",
"lmg": "Lamogai",
"lmh": "Lambichhong",
"lmi": "Lombi",
"lmj": "West Lembata",
"lmk": "Lamkang",
"lml": "Raga",
"lmn": "Lambadi",
"lmo": "Lombard",
"lmp": "Limbum",
"lmq": "Lamatuka",
"lmr": "Lamalera",
"lmu": "Lamenu",
"lmv": "Lomaiviti",
"lmw": "Lake Miwok",
"lmx": "Laimbue",
"lmy": "Laboya",
"ln": "Lingala",
"lna": "Langbashe",
"lnb": "Mbalanhu",
"lnd": "Lun Bawang",
"lnh": "Lanoh",
"lni": "Daantanai'",
"lnj": "Linngithigh",
"lnl": "South Central Banda",
"lnm": "Pondi",
"lnn": "Lorediakarkar",
"lno": "Lango (Sudan)",
"lns": "Lamnso'",
"lnu": "Longuda",
"lnw": "Lanima",
"lo": "Lao",
"loa": "Loloda",
"lob": "Lobi",
"loc": "Inonhan",
"lod": "Berawan",
"loe": "Saluan",
"lof": "Logol",
"log": "Logo",
"loh": "Narim",
"loi": "Lomakka",
"loj": "Lou",
"lok": "Loko",
"lol": "Mongo",
"lom": "Loma",
"lon": "Malawi Lomwe",
"loo": "Lombo",
"lop": "Lopa",
"loq": "Lobala",
"lor": "Téén",
"los": "Loniu",
"lot": "Lotuko",
"lou": "Louisiana Creole",
"lov": "Lopi",
"low": "Tampias Lobu",
"lox": "Loun",
"loz": "Lozi",
"lpa": "Lelepa",
"lpe": "Lepki",
"lpn": "Long Phuri Naga",
"lpo": "Lipo",
"lpx": "Lopit",
"lra": "Rara Bakati'",
"lrc": "Northern Luri",
"lre": "Laurentian",
"lrg": "Laragia",
"lri": "Marachi",
"lrk": "Loarki",
"lrl": "Larestani",
"lrm": "Marama",
"lrn": "Lorang",
"lro": "Laro",
"lrr": "Southern Lorung",
"lrt": "Larantuka Malay",
"lrv": "Larëvat",
"lrz": "Lemerig",
"lsa": "Lasgerdi",
"lsb": "Burundian Sign Language",
"lsd": "Lishana Deni",
"lse": "Lusengo",
"lsh": "Lish",
"lsi": "Lashi",
"lsl": "Latvian Sign Language",
"lsm": "Saamia",
"lsn": "Tibetan Sign Language",
"lso": "Laos Sign Language",
"lsp": "Panamanian Sign Language",
"lsr": "Aruop",
"lss": "Lasi",
"lst": "Trinidad and Tobago Sign Language",
"lsv": "Sivia Sign Language",
"lsw": "Seychelles Sign Language",
"lsy": "Mauritian Sign Language",
"lt": "Lithuanian",
"ltc": "Middle Chinese",
"ltg": "Latgalian",
"lti": "Leti",
"ltn": "Latundê",
"lto": "Olutsotso",
"lts": "Lutachoni",
"ltu": "Latu",
"lu": "Luba-Katanga",
"lua": "Luba-Kasai",
"luc": "Aringa",
"lud": "Ludian",
"lue": "Luvale",
"luf": "Laua",
"luh": "Leizhou Min",
"lui": "Luiseño",
"luj": "Luna",
"luk": "Lunanakha",
"lul": "Olu'bo",
"lum": "Luimbi",
"lun": "Lunda",
"luo": "Luo",
"lup": "Lumbu",
"luq": "Lucumí",
"lur": "Laura",
"lus": "Mizo",
"lut": "Lushootseed",
"luu": "Lumba-Yakkha",
"luv": "Luwati",
"luy": "Luhya",
"luz": "Southern Luri",
"lv": "Latvian",
"lva": "Maku'a",
"lvi": "Lawi",
"lvk": "Lavukaleve",
"lvl": "Lwel",
"lvu": "Levuka",
"lwa": "Lwalu",
"lwe": "Lewo Eleng",
"lwg": "Wanga",
"lwh": "White Lachi",
"lwl": "Eastern Lawa",
"lwm": "Laomian",
"lwo": "Luwo",
"lws": "Malawian Sign Language",
"lwt": "Lewotobi",
"lwu": "Lawu",
"lww": "Lewo",
"lya": "Layakha",
"lyg": "Lyngngam",
"lyn": "Luyana",
"lzh": "Classical Chinese",
"lzl": "Litzlitz",
"lzn": "Leinong Naga",
"lzz": "Laz",
"maa": "San Jerónimo Tecóatl Mazatec",
"mab": "Yutanduchi Mixtec",
"mad": "Madurese",
"mae": "Bo-Rukul",
"maf": "Mafa",
"mag": "Magahi",
"mai": "Maithili",
"maj": "Jalapa de Díaz Mazatec",
"mak": "Makasar",
"mam": "Mam",
"man": "Mandingo",
"map-ata-pro": "Proto-Atayalic",
"map-bms": "Banyumasan",
"map-pro": "Proto-Austronesian",
"maq": "Chiquihuitlán Mazatec",
"mas": "Maasai",
"mat": "Matlatzinca",
"mau": "Huautla Mazatec",
"mav": "Sateré-Mawé",
"maw": "Mampruli",
"max": "North Moluccan Malay",
"maz": "Central Mazahua",
"mba": "Higaonon",
"mbb": "Western Bukidnon Manobo",
"mbc": "Macushi",
"mbd": "Dibabawon Manobo",
"mbe": "Molale",
"mbf": "Baba Malay",
"mbh": "Mangseng",
"mbi": "Ilianen Manobo",
"mbj": "Nadëb",
"mbk": "Malol",
"mbl": "Maxakalí",
"mbm": "Ombamba",
"mbn": "Macaguán",
"mbo": "Mbo (Cameroon)",
"mbp": "Wiwa",
"mbq": "Maisin",
"mbr": "Nukak Makú",
"mbs": "Sarangani Manobo",
"mbt": "Matigsalug Manobo",
"mbu": "Mbula-Bwazza",
"mbv": "Mbulungish",
"mbw": "Maring",
"mbx": "Mari (Sepik)",
"mby": "Memoni",
"mbz": "Amoltepec Mixtec",
"mca": "Maca",
"mcb": "Machiguenga",
"mcc": "Bitur",
"mcd": "Sharanahua",
"mce": "Itundujia Mixtec",
"mcf": "Matsés",
"mcg": "Mapoyo",
"mch": "Ye'kwana",
"mci": "Mese",
"mcj": "Mvanip",
"mck": "Mbunda",
"mcl": "Macaguaje",
"mcm": "Kristang",
"mcn": "Masana",
"mco": "Coatlán Mixe",
"mcp": "Makaa",
"mcq": "Ese",
"mcr": "Menya",
"mcs": "Mambai",
"mcu": "Cameroon Mambila",
"mcw": "Mawa",
"mcx": "Mpiemo",
"mcy": "South Watut",
"mcz": "Mawan",
"mda": "Mada (Nigeria)",
"mdb": "Morigi",
"mdc": "Male",
"mdd": "Mbum",
"mde": "Bura Mabang",
"mdf": "Moksha",
"mdg": "Massalat",
"mdh": "Maguindanao",
"mdi": "Mamvu",
"mdj": "Mangbetu",
"mdk": "Mangbutu",
"mdl": "Maltese Sign Language",
"mdm": "Mayogo",
"mdn": "Mbati",
"mdp": "Mbala",
"mdq": "Mbole",
"mdr": "Mandar",
"mds": "Maria",
"mdt": "Mbere",
"mdu": "Mboko",
"mdv": "Santa Lucía Monteverde Mixtec",
"mdw": "Mbosi",
"mdx": "Dizin",
"mdy": "Maale",
"mdz": "Suruí Do Pará",
"mea": "Menka",
"meb": "Ikobi-Mena",
"mec": "Mara",
"med": "Melpa",
"mee": "Mengen",
"mef": "Megam",
"meh": "Southwestern Tlaxiaco Mixtec",
"mei": "Midob",
"mej": "Meyah",
"mek": "Mekeo",
"mel": "Central Melanau",
"mem": "Mangala",
"men": "Mende (Sierra Leone)",
"meo": "Kedah Malay",
"mep": "Miriwoong",
"meq": "Merey",
"mer": "Meru",
"mes": "Masmaje",
"met": "Mato",
"meu": "Motu",
"mev": "Mano",
"mew": "Maaka",
"mey": "Hassaniya Arabic",
"mez": "Menominee",
"mfa": "Pattani Malay",
"mfb": "Bangka",
"mfc": "Mba",
"mfd": "Mendankwe-Nkwen",
"mfe": "Mauritian Creole",
"mff": "Naki",
"mfg": "Mixifore",
"mfh": "Matal",
"mfi": "Wandala",
"mfj": "Mefele",
"mfk": "North Mofu",
"mfl": "Putai",
"mfm": "Marghi South",
"mfn": "Cross River Mbembe",
"mfo": "Mbe",
"mfp": "Makassar Malay",
"mfq": "Moba",
"mfr": "Marrithiyel",
"mfs": "Mexican Sign Language",
"mft": "Mokerang",
"mfu": "Mbwela",
"mfv": "Mandjak",
"mfw": "Mulaha",
"mfx": "Melo",
"mfy": "Mayo",
"mfz": "Mabaan",
"mg": "Malagasy",
"mga": "Middle Irish",
"mgb": "Mararit",
"mgc": "Morokodo",
"mgd": "Moru",
"mge": "Mango",
"mgf": "Maklew",
"mgg": "Mpongmpong",
"mgh": "Makhuwa-Meetto",
"mgi": "Jili",
"mgj": "Abureni",
"mgk": "Mawes",
"mgl": "Maleu-Kilenge",
"mgm": "Mambae",
"mgn": "Mbangi",
"mgo": "Meta'",
"mgp": "Eastern Magar",
"mgq": "Malila",
"mgr": "Mambwe-Lungu",
"mgs": "Manda (Tanzania)",
"mgt": "Mwakai",
"mgu": "Mailu",
"mgv": "Matengo",
"mgw": "Matumbi",
"mgy": "Mbunga",
"mgz": "Mbugwe",
"mh": "Marshallese",
"mha": "Manda (India)",
"mhb": "Mahongwe",
"mhc": "Mocho",
"mhd": "Mbugu",
"mhe": "Besisi",
"mhf": "Mamaa",
"mhg": "Marrgu",
"mhi": "Ma'di",
"mhj": "Mogholi",
"mhk": "Mungaka",
"mhl": "Mauwake",
"mhm": "Makhuwa-Moniga",
"mhn": "Mòcheno",
"mho": "Mashi",
"mhp": "Balinese Malay",
"mhq": "Mandan",
"mhr": "Eastern Mari",
"mhs": "Buru (Indonesia)",
"mht": "Mandahuaca",
"mhu": "Taraon",
"mhw": "Mbukushu",
"mhx": "Lhao Vo",
"mhy": "Ma'anyan",
"mhz": "Mor (Austronesian)",
"mi": "Māori",
"mia": "Miami",
"mib": "Atatláhuca Mixtec",
"mic": "Mi'kmaq",
"mid": "Mandaic",
"mie": "Ocotepec Mixtec",
"mif": "Mofu-Gudur",
"mig": "San Miguel el Grande Mixtec",
"mih": "Chayuco Mixtec",
"mii": "Chigmecatitlán Mixtec",
"mij": "Mungbam",
"mik": "Mikasuki",
"mil": "Peñoles Mixtec",
"mim": "Alacatlatzala Mixtec",
"min": "Minangkabau",
"mio": "Pinotepa Nacional Mixtec",
"mip": "Apasco-Apoala Mixtec",
"miq": "Miskito",
"mir": "Isthmus Mixe",
"mis-hkl": "Kelantan Peranakan Hokkien",
"mis-idn": "Idiom Neutral",
"mis-isa": "Isaurian",
"mis-jie": "Jie",
"mis-jzh": "Jizhao",
"mis-kas": "Kassite",
"mis-mmd": "Mimi of Decorse",
"mis-mmn": "Mimi of Nachtigal",
"mis-phi": "Philistine",
"mis-rou": "Rouran",
"mis-tdl": "Turdulian",
"mis-tdt": "Turdetanian",
"mis-tnw": "Tangwang",
"mis-tuh": "Tuyuhun",
"mis-tuo": "Tuoba",
"mis-wuh": "Wuhuan",
"mis-xbi": "Xianbei",
"mis-xnu": "Xiongnu",
"mit": "Southern Puebla Mixtec",
"miu": "Cacaloxtepec Mixtec",
"miw": "Akoye",
"mix": "Mixtepec Mixtec",
"miy": "Ayutla Mixtec",
"miz": "Coatzospan Mixtec",
"mjb": "Makalero",
"mjc": "San Juan Colorado Mixtec",
"mjd": "Northwest Maidu",
"mje": "Muskum",
"mjg-mgl": "Mongghul",
"mjg-mgr": "Mangghuer",
"mji": "Kim Mun",
"mjj": "Mawak",
"mjk": "Matukar",
"mjl": "Mandeali",
"mjm": "Medebur",
"mjn": "Mebu",
"mjo": "Malankuravan",
"mjp": "Malapandaram",
"mjq": "Malaryan",
"mjr": "Malavedan",
"mjs": "Miship",
"mjt": "Sawriya Paharia",
"mju": "Manna-Dora",
"mjv": "Mannan",
"mjw": "Karbi",
"mjx": "Mahali",
"mjy": "Mahican",
"mjz": "Majhi",
"mk": "Macedonian",
"mka": "Mbre",
"mkb": "Mal Paharia",
"mkc": "Siliput",
"mke": "Mawchi",
"mkf": "Miya",
"mkg": "Mak (China)",
"mkh-asl-pro": "Proto-Aslian",
"mkh-ban-pro": "Proto-Bahnaric",
"mkh-kat-pro": "Proto-Katuic",
"mkh-khm-pro": "Proto-Khmuic",
"mkh-kmr-pro": "Proto-Khmeric",
"mkh-mmn": "Middle Mon",
"mkh-mnc-pro": "Proto-Monic",
"mkh-mvi": "Middle Vietnamese",
"mkh-pal-pro": "Proto-Palaungic",
"mkh-pea-pro": "Proto-Pearic",
"mkh-pkn-pro": "Proto-Pakanic",
"mkh-pro": "Proto-Mon-Khmer",
"mkh-vie-pro": "Proto-Vietic",
"mki": "Dhatki",
"mkj": "Mokilese",
"mkk": "Byep",
"mkl": "Mokole",
"mkm": "Moklen",
"mkn": "Kupang Malay",
"mko": "Mingang Doso",
"mkp": "Moikodi",
"mkq": "Bay Miwok",
"mkr": "Malas",
"mks": "Silacayoapan Mixtec",
"mkt": "Vamale",
"mku": "Konyanka Maninka",
"mkv": "Mav̋ea",
"mkx": "Cinamiguin Manobo",
"mky": "Taba",
"mkz": "Makasae",
"ml": "Malayalam",
"mla": "Tamambo",
"mlb": "Mbule",
"mlc": "Caolan",
"mle": "Manambu",
"mlf": "Mal",
"mlh": "Mape",
"mli": "Malimpung",
"mlj": "Miltu",
"mlk": "Ilwana",
"mll": "Malua Bay",
"mlm": "Mulam",
"mln": "Malango",
"mlo": "Mlomp",
"mlp": "Bargam",
"mlq": "Western Maninkakan",
"mlr": "Vame",
"mls": "Masalit",
"mlu": "To'abaita",
"mlv": "Mwotlap",
"mlw": "Moloko",
"mlx": "Malfaxal",
"mlz": "Malaynon",
"mma": "Mama",
"mmb": "Momina",
"mmc": "Michoacán Mazahua",
"mmd": "Maonan",
"mme": "Tirax",
"mmf": "Mundat",
"mmg": "North Ambrym",
"mmh": "Mehináku",
"mmi": "Hember Avu",
"mmj": "Majhwar",
"mmk": "Mukha-Dora",
"mml": "Man Met",
"mmm": "Maii",
"mmn": "Mamanwa",
"mmo": "Mangga Buang",
"mmp": "Musan",
"mmq": "Aisi",
"mmr": "Western Xiangxi Miao",
"mmt": "Malalamai",
"mmu": "Mmaala",
"mmv": "Miriti",
"mmw": "Emae",
"mmx": "Madak",
"mmy": "Migaama",
"mmz": "Mabaale",
"mn": "Mongolian",
"mna": "Mbula",
"mnb": "Muna",
"mnc": "Manchu",
"mnd": "Mondé",
"mne": "Naba",
"mnf": "Mundani",
"mng": "Eastern Mnong",
"mnh": "Mono (Congo)",
"mni": "Manipuri",
"mnj": "Munji",
"mnk": "Mandinka",
"mnl": "Tiale",
"mnm": "Mapena",
"mnn": "Southern Mnong",
"mnp": "Northern Min",
"mnq": "Minriq",
"mnr": "Mono (California)",
"mns-cen": "Central Mansi",
"mns-nor": "Northern Mansi",
"mns-pro": "Proto-Mansi",
"mns-sou": "Southern Mansi",
"mnt": "Maykulan",
"mnu": "Mer",
"mnv": "Rennellese",
"mnw": "Mon",
"mnw-tha": "Thai Mon",
"mnx": "Sougb",
"mny": "Manyawa",
"mnz": "Moni",
"moa": "Mwan",
"moc": "Mocoví",
"mod": "Mobilian",
"moe": "Montagnais",
"mog": "Mongondow",
"moh": "Mohawk",
"moi": "Mboi",
"moj": "Monzombo",
"mok": "Morori",
"mom": "Monimbo",
"moo": "Monom",
"mop": "Mopan Maya",
"moq": "Mor (Papuan)",
"mor": "Moro",
"mos": "Moore",
"mot": "Barí",
"mou": "Mogum",
"mov": "Mojave",
"mow": "Moi (Congo)",
"mox": "Molima",
"moy": "Shekkacho",
"moz": "Mukulu",
"mpa": "Mpoto",
"mpb": "Mullukmulluk",
"mpc": "Mangarayi",
"mpd": "Machinere",
"mpe": "Majang",
"mpg": "Marba",
"mph": "Maung",
"mpi": "Mpade",
"mpj": "Martu Wangka",
"mpk": "Mbara (Chad)",
"mpl": "Middle Watut",
"mpm": "Yosondúa Mixtec",
"mpn": "Mindiri",
"mpo": "Miu",
"mpp": "Migabac",
"mpq": "Matís",
"mpr": "Vangunu",
"mps": "Dadibi",
"mpt": "Mian",
"mpu": "Makuráp",
"mpv": "Mungkip",
"mpw": "Mapidian",
"mpx": "Misima-Paneati",
"mpy": "Mapia",
"mpz": "Mpi",
"mqa": "Maba",
"mqb": "Mbuko",
"mqc": "Mangole",
"mqe": "Matepi",
"mqf": "Momuna",
"mqg": "Kota Bangun Kutai Malay",
"mqh": "Tlazoyaltepec Mixtec",
"mqi": "Mariri",
"mqj": "Mamasa",
"mqk": "Rajah Kabunsuwan Manobo",
"mql": "Mbelime",
"mqm": "South Marquesan",
"mqn": "Moronene",
"mqo": "Modole",
"mqp": "Manipa",
"mqq": "Minokok",
"mqr": "Mander",
"mqs": "West Makian",
"mqt": "Mok",
"mqu": "Mandari",
"mqv": "Mosimo",
"mqw": "Murupi",
"mqx": "Mamuju",
"mqy": "Manggarai",
"mqz": "Malasanga",
"mr": "Marathi",
"mra": "Mlabri",
"mrb": "Sungwadia",
"mrc": "Maricopa",
"mrd": "Western Magar",
"mre": "Martha's Vineyard Sign Language",
"mrf": "Elseng",
"mrg": "Mising",
"mrh": "Mara Chin",
"mrj": "Western Mari",
"mrk": "Hmwaveke",
"mrl": "Mortlockese",
"mrm": "Mwerlap",
"mrn": "Cheke Holo",
"mro": "Mru",
"mrp": "Morouas",
"mrq": "North Marquesan",
"mrr": "Hill Maria",
"mrs": "Maragus",
"mrt": "Margi",
"mru": "Mono (Cameroon)",
"mrv": "Mangarevan",
"mrw": "Maranao",
"mrx": "Dineor",
"mry": "Karaga Mandaya",
"mrz": "Marind",
"ms": "Malay",
"msb": "Masbatenyo",
"msc": "Sankaran Maninka",
"msd": "Yucatec Maya Sign Language",
"mse": "Musey",
"msf": "Mekwei",
"msg": "Moraid",
"msi": "Sabah Malay",
"msj": "Ma",
"msk": "Mansaka",
"msl": "Molof",
"msm": "Agusan Manobo",
"msn": "Vurës",
"mso": "Mombum",
"msp": "Maritsauá",
"msq": "Caac",
"msr": "Mongolian Sign Language",
"mss": "West Masela",
"msu": "Musom",
"msv": "Maslam",
"msw": "Mansoanka",
"msx": "Moresada",
"msy": "Aruamu",
"msz": "Momare",
"mt": "Maltese",
"mta": "Cotabato Manobo",
"mtb": "Anyin Morofo",
"mtc": "Munit",
"mtd": "Mualang",
"mte": "Alu",
"mtf": "Murik (New Guinea)",
"mtg": "Una",
"mth": "Munggui",
"mti": "Maiwa (New Guinea)",
"mtj": "Moskona",
"mtk": "Mbe'",
"mtl": "Montol",
"mtm": "Mator",
"mtn": "Matagalpa",
"mto": "Totontepec Mixe",
"mtp": "Wichí Lhamtés Nocten",
"mtq": "Muong",
"mtr": "Mewari",
"mts": "Yora",
"mtt": "Mota",
"mtu": "Tututepec Mixtec",
"mtv": "Asaro'o",
"mtw": "Magahat",
"mtx": "Tidaá Mixtec",
"mty": "Nabi",
"mua": "Mundang",
"mub": "Mubi",
"muc": "Mbu'",
"mud": "Mednyj Aleut",
"mue": "Media Lengua",
"mug": "Musgu",
"muh": "Mündü",
"mui": "Musi",
"muj": "Mabire",
"mul": "Translingual",
"mum": "Maiwala",
"mun-pro": "Proto-Munda",
"muo": "Nyong",
"mup": "Malvi",
"muq": "Eastern Xiangxi Miao",
"mur": "Murle",
"mus": "Creek",
"mut": "Western Muria",
"muu": "Yaaku",
"muv": "Muthuvan",
"mux": "Bo-Ung",
"muy": "Muyang",
"muz": "Mursi",
"mva": "Manam",
"mvb": "Mattole",
"mvd": "Mamboru",
"mvg": "Yucuañe Mixtec",
"mvh": "Mire",
"mvi": "Miyako",
"mvk": "Mekmek",
"mvl": "Mbara (Australia)",
"mvm": "Muya",
"mvn": "Minaveha",
"mvo": "Marovo",
"mvp": "Duri",
"mvq": "Moere",
"mvr": "Marau",
"mvs": "Massep",
"mvt": "Mpotovoro",
"mvu": "Marfa",
"mvv": "Tagal Murut",
"mvw": "Machinga",
"mvx": "Meoswar",
"mvy": "Indus Kohistani",
"mvz": "Mesqan",
"mwa": "Mwatebu",
"mwb": "Juwal",
"mwc": "Are",
"mwe": "Mwera",
"mwf": "Murrinh-Patha",
"mwg": "Aiklep",
"mwh": "Mouk-Aria",
"mwi": "Labo",
"mwk": "Kita Maninkakan",
"mwl": "Mirandese",
"mwm": "Sar",
"mwn": "Nyamwanga",
"mwo": "Sungwadaga",
"mwp": "Kala Lagaw Ya",
"mwq": "Mün Chin",
"mwr": "Marwari",
"mws": "Mwimbi-Muthambi",
"mwt": "Moken",
"mwu": "Mittu",
"mwv": "Mentawai",
"mww": "White Hmong",
"mwz": "Moingi",
"mxa": "Northwest Oaxaca Mixtec",
"mxb": "Tezoatlán Mixtec",
"mxd": "Modang",
"mxe": "Mele-Fila",
"mxf": "Malgbe",
"mxg": "Mbangala",
"mxh": "Mvuba",
"mxi": "Mozarabic",
"mxj": "Miju",
"mxk": "Monumbo",
"mxl": "Maxi Gbe",
"mxm": "Meramera",
"mxn": "Moi (Indonesia)",
"mxo": "Mbowe",
"mxp": "Tlahuitoltepec Mixe",
"mxq": "Juquila Mixe",
"mxr": "Murik (Malaysia)",
"mxs": "Huitepec Mixtec",
"mxt": "Jamiltepec Mixtec",
"mxu": "Mada (Cameroon)",
"mxv": "Metlatónoc Mixtec",
"mxw": "Namo",
"mxx": "Mahou",
"mxy": "Southeastern Nochixtlán Mixtec",
"mxz": "Central Masela",
"my": "Burmese",
"myb": "Mbay",
"myc": "Mayeka",
"mye": "Myene",
"myf": "Bambassi",
"myg": "Manta",
"myh": "Makah",
"myj": "Mangayat",
"myk": "Mamara",
"myl": "Moma",
"mym": "Me'en",
"myn-chl": "Ch'olti'",
"myn-pro": "Proto-Mayan",
"myo": "Anfillo",
"myp": "Pirahã",
"myr": "Muniche",
"mys": "Mesmes",
"myu": "Mundurukú",
"myv": "Erzya",
"myw": "Muyuw",
"myx": "Masaba",
"myy": "Macuna",
"myz": "Classical Mandaic",
"mza": "Santa María Zacatepec Mixtec",
"mzb": "Northern Saharan Berber",
"mzc": "Madagascar Sign Language",
"mzd": "Malimba",
"mze": "Morawa",
"mzg": "Monastic Sign Language",
"mzh": "Wichí Lhamtés Güisnay",
"mzi": "Ixcatlán Mazatec",
"mzj": "Manya",
"mzk": "Nigeria Mambila",
"mzl": "Mazatlán Mixe",
"mzm": "Mumuye",
"mzn": "Mazanderani",
"mzo": "Matipuhy",
"mzp": "Movima",
"mzq": "Mori Atas",
"mzr": "Marúbo",
"mzs": "Macanese",
"mzt": "Mintil",
"mzu": "Inapang",
"mzv": "Manza",
"mzw": "Deg",
"mzx": "Mawayana",
"mzy": "Mozambican Sign Language",
"mzz": "Maiadomu",
"na": "Nauruan",
"naa": "Namla",
"nab": "Nambikwara",
"nac": "Narak",
"nae": "Naka'ela",
"naf": "Nabak",
"nag": "Naga Pidgin",
"nah": "Nahuatl",
"nai-ala": "Alazapa",
"nai-bay": "Bayogoula",
"nai-cal": "Calusa",
"nai-chi": "Chiquimulilla",
"nai-chu-pro": "Proto-Chumash",
"nai-cig": "Ciguayo",
"nai-ckn-pro": "Proto-Chinookan",
"nai-guz": "Guazacapán",
"nai-hit": "Hitchiti",
"nai-ipa": "Ipai",
"nai-jtp": "Jutiapa",
"nai-jum": "Jumaytepeque",
"nai-kat": "Kathlamet",
"nai-klp-pro": "Proto-Kalapuyan",
"nai-knm": "Konomihu",
"nai-kum": "Kumeyaay",
"nai-mac": "Macoris",
"nai-mdu-pro": "Proto-Maidun",
"nai-miz-pro": "Proto-Mixe-Zoque",
"nai-mus-pro": "Proto-Muskogean",
"nai-nao": "Naolan",
"nai-nrs": "New River Shasta",
"nai-okw": "Okwanuchu",
"nai-per": "Pericú",
"nai-pic": "Picuris",
"nai-plp-pro": "Proto-Plateau Penutian",
"nai-pom-pro": "Proto-Pomo",
"nai-qng": "Quinigua",
"nai-sca-pro": "Proto-Siouan-Catawban",
"nai-sin": "Sinacantán",
"nai-sln": "Salvadoran Lenca",
"nai-spt": "Sahaptin",
"nai-tap": "Tapachultec",
"nai-taw": "Tawasa",
"nai-teq": "Tequistlatec",
"nai-tip": "Tipai",
"nai-tot-pro": "Proto-Totozoquean",
"nai-tsi-pro": "Proto-Tsimshianic",
"nai-utn-pro": "Proto-Utian",
"nai-wai": "Waikuri",
"nai-wji": "Western Jicaque",
"nai-yup": "Yupiltepeque",
"naj": "Nalu",
"nak": "Nakanai",
"nal": "Nalik",
"nam": "Ngan'gityemerri",
"nan": "Min Nan",
"nan-dat": "Datian Min",
"nan-hbl": "Hokkien",
"nan-hlh": "Hailufeng Min",
"nan-lnx": "Longyan Min",
"nan-tws": "Teochew",
"nan-zhe": "Zhenan Min",
"nan-zsh": "Sanxiang Min",
"nao": "Naaba",
"nap": "Neapolitan",
"naq": "Khoekhoe",
"nar": "Iguta",
"nas": "Nasioi",
"nat": "Hungworo",
"naw": "Nawuri",
"nax": "Nakwi",
"nay": "Ngarrindjeri",
"naz": "Coatepec Nahuatl",
"nb": "Norwegian Bokmål",
"nba": "Nyemba",
"nbb": "Ndoe",
"nbc": "Chang",
"nbd": "Ngbinda",
"nbe": "Konyak Naga",
"nbg": "Nagarchal",
"nbh": "Ngamo",
"nbi": "Mao Naga",
"nbj": "Ngarinman",
"nbk": "Nake",
"nbm": "Ngbaka Ma'bo",
"nbn": "Kuri",
"nbo": "Nkukoli",
"nbp": "Nnam",
"nbq": "Nggem",
"nbr": "Numana",
"nbs": "Namibian Sign Language",
"nbt": "Na",
"nbu": "Rongmei Naga",
"nbv": "Ngamambo",
"nbw": "Southern Ngbandi",
"nby": "Ningera",
"nca": "Iyo",
"ncb": "Central Nicobarese",
"ncc": "Ponam",
"ncd": "Nachering",
"nce": "Yale",
"ncf": "Notsi",
"ncg": "Nisga'a",
"nch": "Central Huasteca Nahuatl",
"nci": "Classical Nahuatl",
"ncj": "Northern Puebla Nahuatl",
"nck": "Nakara",
"ncl": "Michoacán Nahuatl",
"ncm": "Nambo",
"ncn": "Nauna",
"nco": "Sibe",
"ncr": "Ncane",
"ncs": "Nicaraguan Sign Language",
"nct": "Chothe Naga",
"ncu": "Chumburung",
"ncx": "Central Puebla Nahuatl",
"ncz": "Natchez",
"nd": "Northern Ndebele",
"nda": "Ndasa",
"ndb": "Kenswei Nsei",
"ndc": "Ndau",
"ndd": "Nde-Nsele-Nta",
"ndf": "Nadruvian",
"ndg": "Ndengereko",
"ndh": "Ndali",
"ndi": "Chamba Leko",
"ndj": "Ndamba",
"ndk": "Ndaka",
"ndl": "Ndolo",
"ndm": "Ndam",
"ndn": "Ngundi",
"ndp": "Ndo",
"ndq": "Ndombe",
"ndr": "Ndoola",
"nds": "Low German",
"ndt": "Ndunga",
"ndu": "Dugun",
"ndv": "Ndut",
"ndw": "Ndobo",
"ndx": "Nduga",
"ndy": "Lutos",
"ndz": "Ndogo",
"ne": "Nepali",
"nea": "Eastern Ngad'a",
"neb": "Toura",
"nec": "Nedebang",
"ned": "Nde-Gbite",
"nee": "Kumak",
"nef": "Nefamese",
"neg": "Negidal",
"neh": "Nyenkha",
"nej": "Neko",
"nek": "Neku",
"nem": "Nemi",
"nen": "Nengone",
"neo": "Ná-Meo",
"neq": "North Central Mixe",
"ner": "Yahadian",
"nes": "Bhoti Kinnauri",
"net": "Nete",
"neu": "Neo",
"nev": "Nyaheun",
"new": "Newar",
"nex": "Neme",
"ney": "Neyo",
"nez": "Nez Perce",
"nfa": "Dhao",
"nfd": "Ahwai",
"nfl": "Äiwoo",
"nfr": "Nafaanra",
"nfu": "Mfumte",
"ng": "Ndonga",
"nga": "Ngbaka",
"ngb": "Northern Ngbandi",
"ngc": "Ngombe (Congo)",
"ngd": "Ngando (Central African Republic)",
"nge": "Ngemba",
"ngf-bin-pro": "Proto-Binanderean",
"ngf-pro": "Proto-Trans-New Guinea",
"ngg": "Ngbaka Manza",
"ngh": "Nǀuu",
"ngi": "Ngizim",
"ngj": "Ngie",
"ngk": "Ngalkbun",
"ngl": "Lomwe",
"ngm": "Ngatik Men's Creole",
"ngn": "Ngwo",
"ngo": "Ngoni",
"ngp": "Ngulu",
"ngq": "Ngoreme",
"ngr": "Nagu",
"ngs": "Gvoko",
"ngt": "Ngeq",
"ngu": "Guerrero Nahuatl",
"ngv": "Nagumi",
"ngw": "Ngwaba",
"ngx": "Nggwahyi",
"ngy": "Tibea",
"ngz": "Ngungwel",
"nha": "Nhanda",
"nhb": "Beng",
"nhc": "Tabasco Nahuatl",
"nhd": "Chiripá",
"nhe": "Eastern Huasteca Nahuatl",
"nhf": "Nhuwala",
"nhg": "Tetelcingo Nahuatl",
"nhh": "Nahari",
"nhi": "Zacatlán-Ahuacatlán-Tepetzintla Nahuatl",
"nhk": "Cosoleacaque Nahuatl",
"nhm": "Morelos Nahuatl",
"nhn": "Central Nahuatl",
"nho": "Takuu",
"nhp": "Pajapan Nahuatl",
"nhq": "Huaxcaleca Nahuatl",
"nhr": "Naro",
"nht": "Ometepec Nahuatl",
"nhu": "Noone",
"nhv": "Temascaltepec Nahuatl",
"nhw": "Western Huasteca Nahuatl",
"nhx": "Mecayapan Nahuatl",
"nhy": "Northern Oaxaca Nahuatl",
"nhz": "Santa María La Alta Nahuatl",
"nia": "Nias",
"nib": "Nakame",
"nic-bco-pro": "Proto-Benue-Congo",
"nic-bod-pro": "Proto-Bantoid",
"nic-eov-pro": "Proto-Eastern Oti-Volta",
"nic-gns-pro": "Proto-Gurunsi",
"nic-grf-pro": "Proto-Grassfields",
"nic-gur-pro": "Proto-Gur",
"nic-jkn-pro": "Proto-Jukunoid",
"nic-lcr-pro": "Proto-Lower Cross River",
"nic-ogo-pro": "Proto-Ogoni",
"nic-ovo-pro": "Proto-Oti-Volta",
"nic-plt-pro": "Proto-Plateau",
"nic-pro": "Proto-Niger-Congo",
"nic-ubg-pro": "Proto-Ubangian",
"nic-ucr-pro": "Proto-Upper Cross River",
"nic-vco-pro": "Proto-Volta-Congo",
"nid": "Ngandi",
"nie": "Niellim",
"nif": "Nek",
"nig": "Ngalakan",
"nih": "Nyiha",
"nii": "Nii",
"nij": "Ngaju",
"nik": "Southern Nicobarese",
"nil": "Nila",
"nim": "Nilamba",
"nin": "Ninzo",
"nio": "Nganasan",
"niq": "Nandi",
"nir": "Nimboran",
"nis": "Nimi",
"nit": "Southeastern Kolami",
"niu": "Niuean",
"niv": "Nivkh",
"niw": "Nimo",
"nix": "Hema",
"niy": "Ngiti",
"niz": "Ningil",
"nja": "Nzanyi",
"njb": "Nocte",
"njh": "Lotha Naga",
"nji": "Gudanji",
"njj": "Njen",
"njl": "Njalgulgule",
"njm": "Angami",
"njn": "Liangmai Naga",
"njo": "Ao",
"njo-jgl": "Chungli Ao",
"njr": "Njerep",
"njs": "Nisa",
"njt": "Ndyuka-Trio Pidgin",
"nju": "Ngadjunmaya",
"njx": "Kunyi",
"njy": "Njyem",
"njz": "Nyishi",
"nka": "Nkoya",
"nkb": "Khoibu Naga",
"nkc": "Nkongho",
"nkd": "Koireng",
"nke": "Duke",
"nkf": "Inpui Naga",
"nkg": "Nekgini",
"nkh": "Khezha Naga",
"nki": "Thangal Naga",
"nkj": "Nakai",
"nkk": "Nokuku",
"nkm": "Namat",
"nkn": "Nkangala",
"nko": "Nkonya",
"nkp": "Niuatoputapu",
"nkq": "Nkami",
"nkr": "Nukuoro",
"nks": "North Asmat",
"nkt": "Nyika",
"nku": "Bouna Kulango",
"nkw": "Nkutu",
"nkx": "Nkoroo",
"nkz": "Nkari",
"nl": "Dutch",
"nla": "Ngombale",
"nlc": "Nalca",
"nle": "East Nyala",
"nlg": "Gela",
"nli": "Grangali",
"nlj": "Nyali",
"nlk": "Ninia Yali",
"nll": "Nihali",
"nlm": "Mankiyali",
"nlo": "Ngul",
"nlq": "Lao Naga",
"nlu": "Nchumbulu",
"nlv": "Orizaba Nahuatl",
"nlw": "Walangama",
"nlx": "Nahali",
"nly": "Nyamal",
"nlz": "Nalögo",
"nma": "Maram Naga",
"nmb": "Big Nambas",
"nmc": "Ngam",
"nmd": "Ndumu",
"nme": "Mzieme Naga",
"nmf": "Tangkhul Naga",
"nmg": "Kwasio",
"nmh": "Monsang Naga",
"nmi": "Nyam",
"nmj": "Ngombe (Central African Republic)",
"nmk": "Namakura",
"nml": "Ndemli",
"nmm": "Manangba",
"nmn": "ǃXóõ",
"nmo": "Moyon Naga",
"nmp": "Nimanbur",
"nmq": "Nambya",
"nmr": "Nimbari",
"nms": "Letemboi",
"nmt": "Namonuito",
"nmu": "Northeast Maidu",
"nmv": "Ngamini",
"nmw": "Nimoa",
"nmx": "Nama",
"nmy": "Namuyi",
"nmz": "Nawdm",
"nn": "Norwegian Nynorsk",
"nna": "Nyangumarta",
"nnb": "Nande",
"nnc": "Nancere",
"nnd": "West Ambae",
"nne": "Ngandyera",
"nnf": "Ngaing",
"nng": "Maring Naga",
"nnh": "Ngiemboon",
"nni": "North Nuaulu",
"nnj": "Nyangatom",
"nnk": "Nankina",
"nnl": "Northern Rengma Naga",
"nnm": "Namia",
"nnn": "Ngete",
"nnp": "Wancho",
"nnq": "Ngindo",
"nnr": "Narungga",
"nnt": "Nanticoke",
"nnu": "Dwang",
"nnv": "Nukunu",
"nnw": "Southern Nuni",
"nnx": "Ngong",
"nny": "Nyangga",
"nnz": "Nda'nda'",
"no": "Norwegian",
"noa": "Woun Meu",
"noc": "Nuk",
"nod": "Northern Thai",
"noe": "Nimadi",
"nof": "Nomane",
"nog": "Nogai",
"noh": "Nomu",
"noi": "Noiri",
"noj": "Nonuya",
"nok": "Nooksack",
"nol": "Nomlaki",
"nom": "Nocamán",
"non": "Old Norse",
"nop": "Numanggang",
"noq": "Ngongo",
"nos": "Eastern Nisu",
"not": "Nomatsiguenga",
"nou": "Ewage-Notu",
"nov": "Novial",
"now": "Nyambo",
"noy": "Noy",
"noz": "Nayi",
"npa": "Nar Phu",
"npb": "Nupbikha",
"npg": "Ponyo",
"nph": "Phom",
"npl": "Southeastern Puebla Nahuatl",
"npn": "Mondropolon",
"npo": "Pochuri Naga",
"nps": "Nipsan",
"npu": "Puimei Naga",
"npx": "Noipä",
"npy": "Napu",
"nqg": "Ede Nago",
"nqk": "Kura Ede Nago",
"nql": "Ngendelengo",
"nqm": "Ndom",
"nqn": "Nen",
"nqo": "N'Ko",
"nqq": "Kyan-Karyaw Naga",
"nqy": "Akyaung Ari",
"nr": "Southern Ndebele",
"nra": "Ngom",
"nrb": "Nara",
"nrc": "Noric",
"nre": "Southern Rengma Naga",
"nrf": "Norman",
"nrg": "Narango",
"nri": "Chokri Naga",
"nrk": "Ngarla",
"nrl": "Ngarluma",
"nrm": "Narom",
"nrn": "Norn",
"nrp": "North Picene",
"nrr": "Norra",
"nrt": "Northern Kalapuya",
"nru": "Narua",
"nrx": "Ngurmbur",
"nrz": "Lala (New Guinea)",
"nsa": "Sangtam Naga",
"nsb": "Lower Nossob",
"nsc": "Nshi",
"nsd": "Southern Nisu",
"nse": "Nsenga",
"nsg": "Ngasa",
"nsh": "Ngoshie",
"nsi": "Nigerian Sign Language",
"nsk": "Naskapi",
"nsl": "Norwegian Sign Language",
"nsm": "Sema",
"nsn": "Nehan",
"nso": "Northern Sotho",
"nsp": "Nepalese Sign Language",
"nsq": "Northern Sierra Miwok",
"nsr": "Maritime Sign Language",
"nss": "Nali",
"nst": "Tangsa",
"nsu": "Sierra Negra Nahuatl",
"nsv": "Southwestern Nisu",
"nsw": "Navut",
"nsx": "Nsongo",
"nsy": "Nasal",
"nsz": "Nisenan",
"ntd": "Northern Tidung",
"ntg": "Ngantangarra",
"nti": "Natioro",
"ntj": "Ngaanyatjarra",
"ntk": "Ikoma",
"ntm": "Nateni",
"nto": "Ntomba",
"ntp": "Northern Tepehuan",
"ntr": "Delo",
"nts": "Natagaimas",
"ntu": "Natügu",
"ntw": "Nottoway",
"ntx": "Somra",
"nty": "Mantsi",
"nua": "Yuanga",
"nub-har": "Haraza",
"nub-pro": "Proto-Nubian",
"nuc": "Nukuini",
"nud": "Ngala",
"nue": "Ngundu",
"nuf": "Nusu",
"nug": "Nungali",
"nuh": "Ndunda",
"nui": "Ngumbi",
"nuj": "Nyole (Uganda)",
"nuk": "Nootka",
"nul": "Nusa Laut",
"num": "Niuafo'ou",
"nun": "Anong",
"nuo": "Nguôn",
"nup": "Nupe",
"nuq": "Nukumanu",
"nur": "Nuguria",
"nus": "Nuer",
"nut": "Nùng",
"nuu": "Ngbundu",
"nuv": "Northern Nuni",
"nuw": "Nguluwan",
"nux": "Mehek",
"nuy": "Nunggubuyu",
"nuz": "Tlamacazapa Nahuatl",
"nv": "Navajo",
"nvh": "Nasarian",
"nvm": "Namiae",
"nvo": "Nyokon",
"nwa": "Nawathinehena",
"nwb": "Nyabwa",
"nwc": "Classical Newar",
"nwe": "Ngwe",
"nwg": "Ngaiawang",
"nwi": "Southwest Tanna",
"nwm": "Nyamusa-Molo",
"nwo": "Nauo",
"nwr": "Nawaru",
"nwx": "Middle Newar",
"nwy": "Nottoway-Meherrin",
"nxa": "Nauete",
"nxd": "Ngando (Congo)",
"nxe": "Nage",
"nxg": "Ngadha",
"nxi": "Nindi",
"nxl": "South Nuaulu",
"nxm": "Numidian",
"nxn": "Ngawun",
"nxo": "Ndambomo",
"nxq": "Naxi",
"nxr": "Ninggerum",
"nxx": "Nafri",
"ny": "Chichewa",
"nyb": "Nyangbo",
"nyc": "Nyanga-li",
"nyd": "Nyole (Kenya)",
"nye": "Nyengo",
"nyf": "Giryama",
"nyg": "Nyindu",
"nyh": "Nyigina",
"nyi": "Nyimang",
"nyj": "Nyanga (Congo)",
"nyk": "Nyaneka",
"nyl": "Nyeu",
"nym": "Nyamwezi",
"nyn": "Nyankole",
"nyo": "Nyoro",
"nyp": "Nyang'i",
"nys": "Nyunga",
"nyt": "Nyawaygi",
"nyu": "Nyungwe",
"nyv": "Nyulnyul",
"nyw": "Nyaw",
"nyx": "Nganyaywana",
"nyy": "Nyakyusa",
"nza": "Tigon Mbembe",
"nzb": "Njebi",
"nzd": "Nzadi",
"nzi": "Nzima",
"nzk": "Nzakara",
"nzm": "Zeme Naga",
"nzs": "New Zealand Sign Language",
"nzu": "Central Teke",
"nzy": "Nzakambay",
"nzz": "Nanga Dama Dogon",
"oaa": "Orok",
"oac": "Oroch",
"oak": "Noakhali",
"oav": "Old Avar",
"obi": "Obispeño",
"obk": "Southern Bontoc",
"obl": "Oblo",
"obm": "Moabite",
"obo": "Obo Manobo",
"obr": "Old Burmese",
"obt": "Old Breton",
"obu": "Obulom",
"oc": "Occitan",
"oca": "Ocaina",
"och": "Old Chinese",
"oco": "Old Cornish",
"ocu": "Tlahuica",
"oda": "Odut",
"odk": "Od",
"odt": "Old Dutch",
"odu": "Odual",
"ofo": "Ofo",
"ofs": "Old Frisian",
"ofu": "Efutop",
"ogb": "Ogbia",
"ogc": "Ogbah",
"oge": "Old Georgian",
"ogg": "Ogbogolo",
"ogo": "Khana",
"ogu": "Ogbronuagum",
"ohu": "Old Hungarian",
"oia": "Oirata",
"oin": "Inebu One",
"oj": "Ojibwe",
"ojb": "Northwestern Ojibwa",
"ojc": "Central Ojibwa",
"ojg": "Eastern Ojibwa",
"ojp": "Old Japanese",
"ojs": "Severn Ojibwa",
"ojv": "Ontong Java",
"ojw": "Western Ojibwa",
"oka": "Okanagan",
"okb": "Okobo",
"okd": "Okodia",
"oke": "Okpe (Southwestern Edo)",
"okg": "Kok-Paponk",
"okh": "Koresh-e Rostam",
"oki": "Okiek",
"okj": "Oko-Juwoi",
"okk": "Kwamtim One",
"okl": "Old Kentish Sign Language",
"okm": "Middle Korean",
"okn": "Okinoerabu",
"oko": "Old Korean",
"okr": "Kirike",
"oks": "Oko-Eni-Osayen",
"oku": "Oku",
"okv": "Orokaiva",
"okx": "Okpe (Northwestern Edo)",
"okz": "Old Khmer",
"old": "Mochi",
"ole": "Olekha",
"olm": "Oloma",
"olo": "Livvi",
"olr": "Olrat",
"olt": "Old Lithuanian",
"olu": "Kuvale",
"om": "Oromo",
"oma": "Omaha-Ponca",
"omb": "Omba",
"omc": "Mochica",
"omg": "Omagua",
"omi": "Omi",
"omk": "Omok",
"oml": "Ombo",
"omn": "Minoan",
"omo": "Utarmbung",
"omp": "Old Manipuri",
"omq-cha-pro": "Proto-Chatino",
"omq-maz-pro": "Proto-Mazatec",
"omq-mix-pro": "Proto-Mixtecan",
"omq-mxt-pro": "Proto-Mixtec",
"omq-otp-pro": "Proto-Oto-Pamean",
"omq-pro": "Proto-Oto-Manguean",
"omq-sjq": "San Juan Quiahije Chatino",
"omq-tel": "Teposcolula Mixtec",
"omq-teo": "Teojomulco Chatino",
"omq-tri-pro": "Proto-Triqui",
"omq-zap-pro": "Proto-Zapotecan",
"omq-zpc-pro": "Proto-Zapotec",
"omr": "Old Marathi",
"omt": "Omotik",
"omu": "Omurano",
"omv-aro-pro": "Proto-Aroid",
"omv-diz-pro": "Proto-Dizoid",
"omv-pro": "Proto-Omotic",
"omw": "South Tairora",
"omx": "Old Mon",
"ona": "Selk'nam",
"onb": "Lingao",
"one": "Oneida",
"ong": "Olo",
"oni": "Onin",
"onj": "Onjob",
"onk": "Kabore One",
"onn": "Onobasulu",
"ono": "Onondaga",
"onp": "Sartang",
"onr": "Northern One",
"ons": "Ono",
"onu": "Unua",
"onw": "Old Nubian",
"onx": "Pidgin Onin",
"ood": "O'odham",
"oog": "Ong",
"oon": "Önge",
"oor": "Oorlams",
"opa": "Okpamheri",
"opk": "Kopkaka",
"opm": "Oksapmin",
"opo": "Opao",
"opt": "Opata",
"opy": "Ofayé",
"or": "Odia",
"ora": "Oroha",
"ore": "Orejón",
"org": "Oring",
"orh": "Oroqen",
"oro": "Orokolo",
"orr": "Oruma",
"ort": "Adivasi Odia",
"oru": "Ormuri",
"orv": "Old East Slavic",
"orw": "Oro Win",
"orx": "Oro",
"orz": "Ormu",
"os": "Ossetian",
"osa": "Osage",
"osc": "Oscan",
"osi": "Osing",
"osn": "Old Sundanese",
"oso": "Ososo",
"osp": "Old Spanish",
"ost": "Osatu",
"osu": "Southern One",
"osx": "Old Saxon",
"ota": "Ottoman Turkish",
"otb": "Old Tibetan",
"otd": "Ot Danum",
"ote": "Mezquital Otomi",
"oti": "Oti",
"otk": "Old Turkic",
"otl": "Tilapa Otomi",
"otm": "Eastern Highland Otomi",
"otn": "Tenango Otomi",
"oto-otm-pro": "Proto-Otomi",
"oto-pro": "Proto-Otomian",
"otq": "Querétaro Otomi",
"otr": "Otoro",
"ots": "Estado de México Otomi",
"ott": "Temoaya Otomi",
"otu": "Otuke",
"otw": "Ottawa",
"otx": "Texcatepec Otomi",
"oty": "Old Tamil",
"otz": "Ixtenco Otomi",
"oub": "Glio-Oubi",
"oue": "Oune",
"oui": "Old Uyghur",
"oum": "Ouma",
"ovd": "Elfdalian",
"owi": "Owiniga",
"owl": "Old Welsh",
"oyb": "Oy",
"oyd": "Oyda",
"oym": "Wayampi",
"oyy": "Oya'oya",
"ozm": "Koonzime",
"pa": "Punjabi",
"paa-kmn": "Kómnzo",
"paa-kwn": "Kuwani",
"paa-lei": "Leitre",
"paa-nha-pro": "Proto-North Halmahera",
"paa-nun": "Nungon",
"pab": "Pareci",
"pac": "Pacoh",
"pad": "Paumarí",
"pae": "Pagibete",
"paf": "Paranawát",
"pag": "Pangasinan",
"pah": "Tenharim",
"pai": "Pe",
"pak": "Parakanã",
"pal": "Middle Persian",
"pam": "Kapampangan",
"pao": "Northern Paiute",
"pap": "Papiamentu",
"paq": "Parya",
"par": "Panamint",
"pas": "Papasena",
"pau": "Palauan",
"pav": "Wari'",
"paw": "Pawnee",
"pax": "Pankararé",
"pay": "Pech",
"paz": "Pankararú",
"pbb": "Páez",
"pbc": "Patamona",
"pbe": "Mezontla Popoloca",
"pbf": "Coyotepec Popoloca",
"pbg": "Paraujano",
"pbh": "Panare",
"pbi": "Podoko",
"pbl": "Mak (Nigeria)",
"pbm": "Puebla Mazatec",
"pbn": "Kpasam",
"pbo": "Papel",
"pbp": "Badyara",
"pbr": "Pangwa",
"pbs": "Central Pame",
"pbv": "Pnar",
"pby": "Pyu (New Guinea)",
"pca": "Santa Inés Ahuatempan Popoloca",
"pcb": "Pear",
"pcc": "Bouyei",
"pcd": "Picard",
"pce": "Ruching Palaung",
"pcf": "Paliyan",
"pcg": "Paniya",
"pch": "Pardhan",
"pci": "Duruwa",
"pcj": "Parenga",
"pck": "Paite",
"pcl": "Pardhi",
"pcm": "Nigerian Pidgin",
"pcn": "Piti",
"pcp": "Pacahuara",
"pcw": "Pyapun",
"pda": "Anam",
"pdc": "Pennsylvania German",
"pdi": "Pa Di",
"pdn": "Fedan",
"pdo": "Padoe",
"pdt": "Plautdietsch",
"pdu": "Kayan",
"pea": "Peranakan Indonesian",
"peb": "Eastern Pomo",
"ped": "Mala (New Guinea)",
"pee": "Taje",
"pef": "Northeastern Pomo",
"peg": "Pengo",
"peh": "Bonan",
"pei": "Chichimeca-Jonaz",
"pej": "Northern Pomo",
"pek": "Penchal",
"pel": "Pekal",
"pem": "Phende",
"peo": "Old Persian",
"pep": "Kunja",
"peq": "Southern Pomo",
"pev": "Pémono",
"pex": "Petats",
"pey": "Petjo",
"pez": "Eastern Penan",
"pfa": "Pááfang",
"pfe": "Peere",
"pga": "Juba Arabic",
"pgd": "Gandhari",
"pgg": "Pangwali",
"pgi": "Pagi",
"pgk": "Rerep",
"pgl": "Primitive Irish",
"pgn": "Paelignian",
"pgs": "Pangseng",
"pgu": "Pagu",
"pgz": "Papua New Guinean Sign Language",
"pha": "Pa-Hng",
"phd": "Phudagi",
"phg": "Phuong",
"phh": "Phukha",
"phi-din": "Dinapigue Agta",
"phi-kal-pro": "Proto-Kalamian",
"phi-nag": "Nagtipunan Agta",
"phi-pro": "Proto-Philippine",
"phk": "Phake",
"phl": "Palula",
"phm": "Phimbi",
"phn": "Phoenician",
"pho": "Phunoi",
"phq": "Phana'",
"phr": "Pahari-Potwari",
"pht": "Phu Thai",
"phu": "Phuan",
"phv": "Pahlavani",
"phw": "Phangduwali",
"pi": "Pali",
"pia": "Pima Bajo",
"pib": "Yine",
"pic": "Pinji",
"pid": "Piaroa",
"pie": "Piro",
"pif": "Pingelapese",
"pig": "Pisabo",
"pih": "Pitcairn-Norfolk",
"pii": "Pini",
"pij": "Pijao",
"pil": "Yom",
"pim": "Powhatan",
"pin": "Piame",
"pio": "Piapoco",
"pip": "Pero",
"pir": "Piratapuyo",
"pis": "Pijin",
"pit": "Pitta-Pitta",
"piu": "Pintupi-Luritja",
"piv": "Pileni",
"piw": "Pimbwe",
"pix": "Piu",
"piy": "Piya-Kwonci",
"piz": "Pije",
"pjt": "Pitjantjatjara",
"pkb": "Kipfokomo",
"pkc": "Baekje",
"pkg": "Pak-Tong",
"pkh": "Pankhu",
"pkn": "Pakanha",
"pko": "Pökoot",
"pkp": "Pukapukan",
"pkr": "Attapady Kurumba",
"pks": "Pakistan Sign Language",
"pkt": "Maleng",
"pku": "Paku",
"pl": "Polish",
"pla": "Miani",
"plb": "Polonombauk",
"plc": "Central Palawano",
"ple": "Palu'e",
"plg": "Pilagá",
"plh": "Paulohi",
"plj": "Polci",
"plk": "Kohistani Shina",
"pll": "Shwe Palaung",
"pln": "Palenquero",
"plo": "Oluta Popoluca",
"plq": "Palaic",
"plr": "Palaka",
"pls": "San Marcos Tlalcoyalco Popoloca",
"plu": "Palikur",
"plv": "Southwest Palawano",
"plw": "Brooke's Point Palawano",
"ply": "Bolyu",
"plz": "Paluan",
"pma": "Paamese",
"pmb": "Pambia",
"pmd": "Pallanganmiddang",
"pme": "Pwaamèi",
"pmf": "Pamona",
"pmi": "Northern Pumi",
"pmj": "Southern Pumi",
"pmk": "Pamlico",
"pml": "Sabir",
"pmm": "Pol",
"pmn": "Pam",
"pmo": "Pom",
"pmq": "Northern Pame",
"pmr": "Manat",
"pms": "Piedmontese",
"pmt": "Tuamotuan",
"pmu": "Mirpur Panjabi",
"pmw": "Plains Miwok",
"pmx": "Poumei Naga",
"pmy": "Papuan Malay",
"pmz": "Southern Pame",
"pna": "Punan Bah-Biau",
"pnc": "Pannei",
"pnd": "Mpinda",
"pne": "Western Penan",
"png": "Pongu",
"pnh": "Penrhyn",
"pni": "Aoheng",
"pnj": "Pinjarup",
"pnk": "Paunaka",
"pnl": "Paleni",
"pnm": "Punan Batu",
"pnn": "Pinai-Hagahai",
"pno": "Panobo",
"pnp": "Pancana",
"pnq": "Pana (West Africa)",
"pnr": "Panim",
"pns": "Ponosakan",
"pnt": "Pontic Greek",
"pnu": "Jiongnai Bunu",
"pnv": "Pinigura",
"pnw": "Panyjima",
"pnx": "Phong-Kniang",
"pny": "Pinyin",
"pnz": "Pana (Central Africa)",
"poc": "Poqomam",
"poe": "San Juan Atzingo Popoloca",
"pof": "Poke",
"pog": "Potiguára",
"poh": "Poqomchi'",
"poi": "Highland Popoluca",
"pok": "Pokangá",
"pom": "Southeastern Pomo",
"pon": "Pohnpeian",
"poo": "Central Pomo",
"pop": "Pwapwâ",
"poq": "Texistepec Popoluca",
"pos": "Sayula Popoluca",
"pot": "Potawatomi",
"pov": "Guinea-Bissau Creole",
"pow": "San Felipe Otlaltepec Popoloca",
"pox": "Polabian",
"poy": "Pogolo",
"poz-abi": "Abai",
"poz-bal": "Baliledo",
"poz-btk-pro": "Proto-Bungku-Tolaki",
"poz-cet-pro": "Proto-Central-Eastern Malayo-Polynesian",
"poz-hce-pro": "Proto-Halmahera-Cenderawasih",
"poz-lgx-pro": "Proto-Lampungic",
"poz-mcm-pro": "Proto-Malayo-Chamic",
"poz-mic-pro": "Proto-Micronesian",
"poz-mly-pro": "Proto-Malayic",
"poz-msa-pro": "Proto-Malayo-Sumbawan",
"poz-oce-pro": "Proto-Oceanic",
"poz-pep-pro": "Proto-Eastern Polynesian",
"poz-pnp-pro": "Proto-Nuclear Polynesian",
"poz-pol-pro": "Proto-Polynesian",
"poz-pro": "Proto-Malayo-Polynesian",
"poz-sml": "Sarawak Malay",
"poz-ssw-pro": "Proto-South Sulawesi",
"poz-swa-pro": "Proto-North Sarawak",
"poz-ter": "Terengganu Malay",
"ppa": "Pao",
"ppe": "Papi",
"ppi": "Paipai",
"ppk": "Uma",
"ppl": "Pipil",
"ppm": "Papuma",
"ppn": "Papapana",
"ppo": "Folopa",
"ppq": "Pei",
"pps": "San Luís Temalacayuca Popoloca",
"ppt": "Pa",
"ppu": "Papora",
"pqa": "Pa'a",
"pqe-pro": "Proto-Eastern Malayo-Polynesian",
"pqm": "Malecite-Passamaquoddy",
"pra": "Prakrit",
"pra-niy": "Niya Prakrit",
"prc": "Parachi",
"pre": "Principense",
"prf": "Paranan",
"prg": "Old Prussian",
"prh": "Porohanon",
"pri": "Paicî",
"prk": "Parauk",
"prl": "Peruvian Sign Language",
"prm": "Kibiri",
"prn": "Prasuni",
"pro": "Old Occitan",
"prq": "Perené Ashéninka",
"prr": "Puri",
"prt": "Phai",
"pru": "Puragi",
"prw": "Parawen",
"prx": "Purik",
"prz": "Providencia Sign Language",
"ps": "Pashto",
"psa": "Asue Awyu",
"psc": "Persian Sign Language",
"psd": "Plains Indian Sign Language",
"pse": "Central Malay",
"psg": "Penang Sign Language",
"psh": "Southwest Pashayi",
"psi": "Southeast Pashayi",
"psl": "Puerto Rican Sign Language",
"psm": "Pauserna",
"psn": "Panasuan",
"pso": "Polish Sign Language",
"psp": "Philippine Sign Language",
"psq": "Pasi",
"psr": "Portuguese Sign Language",
"pss": "Kaulong",
"psw": "Port Sandwich",
"psy": "Piscataway",
"pt": "Portuguese",
"pta": "Pai Tavytera",
"pth": "Pataxó Hã-Ha-Hãe",
"pti": "Pintiini",
"ptn": "Patani",
"pto": "Zo'é",
"ptp": "Patep",
"ptq": "Pattapu",
"ptr": "Piamatsina",
"ptt": "Enrekang",
"ptu": "Bambam",
"ptv": "Port Vato",
"ptw": "Pentlatch",
"pty": "Pathiya",
"pua": "Purepecha",
"pub": "Purum",
"puc": "Punan Merap",
"pud": "Punan Aput",
"pue": "Puelche",
"puf": "Punan Merah",
"pug": "Phuie",
"pui": "Puinave",
"puj": "Punan Tubu",
"pum": "Puma",
"puo": "Puoc",
"pup": "Pulabu",
"puq": "Puquina",
"pur": "Puruborá",
"put": "Putoh",
"puu": "Punu",
"puw": "Puluwat",
"pux": "Puare",
"puy": "Purisimeño",
"pwa": "Pawaia",
"pwb": "Panawa",
"pwg": "Gapapaiwa",
"pwi": "Patwin",
"pwm": "Molbog",
"pwn": "Paiwan",
"pwo": "Western Pwo",
"pwr": "Powari",
"pww": "Northern Pwo",
"pxm": "Quetzaltepec Mixe",
"pye": "Pye Krumen",
"pym": "Fyam",
"pyn": "Poyanáwa",
"pys": "Paraguayan Sign Language",
"pyu": "Puyuma",
"pyx": "Pyu (Myanmar)",
"pyy": "Pyen",
"pzh": "Pazeh",
"pzn": "Para Naga",
"qfa-adm-pro": "Proto-Great Andamanese",
"qfa-bet-pro": "Proto-Be-Tai",
"qfa-cka-pro": "Proto-Chukotko-Kamchatkan",
"qfa-hur-pro": "Proto-Hurro-Urartian",
"qfa-kad-pro": "Proto-Kadu",
"qfa-kms-pro": "Proto-Kam-Sui",
"qfa-kor-pro": "Proto-Koreanic",
"qfa-kra-pro": "Proto-Kra",
"qfa-lic-pro": "Proto-Hlai",
"qfa-onb-pro": "Proto-Be",
"qfa-ong-pro": "Proto-Ongan",
"qfa-tak-pro": "Proto-Kra-Dai",
"qfa-yen-pro": "Proto-Yeniseian",
"qfa-yuk-pro": "Proto-Yukaghir",
"qu": "Quechua",
"qua": "Quapaw",
"quc": "K'iche'",
"qui": "Quileute",
"qum": "Sipakapense",
"qun": "Quinault",
"quq": "Quinqui",
"quv": "Sacapulteco",
"qvy": "Queyu",
"qwc": "Classical Quechua",
"qwe-kch": "Kichwa",
"qwe-pro": "Proto-Quechuan",
"qwm": "Kipchak",
"qwt": "Kwalhioqua-Tlatskanai",
"qxs": "Southern Qiang",
"qya": "Quenya",
"qyp": "Quiripi",
"raa": "Dungmali",
"rab": "Chamling",
"rac": "Rasawa",
"rad": "Rade",
"raf": "Western Meohang",
"rag": "Logooli",
"rah": "Rabha",
"rai": "Ramoaaina",
"rak": "Tulu-Bohuai",
"ral": "Ralte",
"ram": "Canela",
"ran": "Riantana",
"rao": "Rao",
"rap": "Rapa Nui",
"raq": "Saam",
"rar": "Rarotongan",
"ras": "Tegali",
"rat": "Razajerdi",
"rau": "Raute",
"rav": "Sampang",
"raw": "Rawang",
"rax": "Rang",
"ray": "Rapa",
"raz": "Rahambuu",
"rbb": "Rumai Palaung",
"rbk": "Northern Bontoc",
"rbl": "East Miraya Bikol",
"rcf": "Réunion Creole French",
"rdb": "Rudbari",
"rea": "Rerau",
"reb": "Rembong",
"ree": "Rejang Kayan",
"reg": "Kara (Tanzania)",
"rei": "Reli",
"rej": "Rejang",
"rel": "Rendille",
"rem": "Remo",
"ren": "Rengao",
"rer": "Rer Bare",
"res": "Reshe",
"ret": "Retta",
"rey": "Reyesano",
"rga": "Roria",
"rge": "Romano-Greek",
"rgk": "Rangkas",
"rgn": "Romagnol",
"rgr": "Resígaro",
"rgs": "Southern Roglai",
"rgu": "Ringgou",
"rhg": "Rohingya",
"rhp": "Yahang",
"ria": "Reang",
"rif": "Tarifit",
"ril": "Riang",
"rim": "Nyaturu",
"rin": "Nungu",
"rir": "Ribun",
"rit": "Ritarungo",
"riu": "Riung",
"rjg": "Rajong",
"rji": "Raji",
"rjs": "Rajbanshi",
"rka": "Kraol",
"rkb": "Rikbaktsa",
"rkh": "Rakahanga-Manihiki",
"rki": "Rakhine",
"rkm": "Marka",
"rkt": "Kamta",
"rkw": "Arakwal",
"rm": "Romansh",
"rma": "Rama",
"rmb": "Rembarunga",
"rmc": "Carpathian Romani",
"rmd": "Traveller Danish",
"rme": "Angloromani",
"rmf": "Kalo Finnish Romani",
"rmg": "Traveller Norwegian",
"rmh": "Murkim",
"rmi": "Lomavren",
"rmk": "Romkun",
"rml": "Baltic Romani",
"rmm": "Roma",
"rmn": "Balkan Romani",
"rmo": "Sinte Romani",
"rmp": "Rempi",
"rmq": "Caló",
"rms": "Romanian Sign Language",
"rmt": "Domari",
"rmu": "Tavringer Romani",
"rmv": "Romanova",
"rmw": "Welsh Romani",
"rmx": "Romam",
"rmy": "Vlax Romani",
"rmz": "Marma",
"rnd": "Ruwund",
"rng": "Ronga",
"rnl": "Ranglong",
"rnn": "Roon",
"rnp": "Rongpo",
"rnw": "Rungwa",
"ro": "Romanian",
"roa-ang": "Angevin",
"roa-bbn": "Bourbonnais-Berrichon",
"roa-brg": "Bourguignon",
"roa-can": "Cantabrian",
"roa-cha": "Champenois",
"roa-fcm": "Franc-Comtois",
"roa-gal": "Gallo",
"roa-gib": "Gallo-Italic of Basilicata",
"roa-gis": "Gallo-Italic of Sicily",
"roa-leo": "Leonese",
"roa-lor": "Lorrain",
"roa-oca": "Old Catalan",
"roa-ole": "Old Leonese",
"roa-ona": "Old Navarro-Aragonese",
"roa-opt": "Old Galician-Portuguese",
"roa-orl": "Orléanais",
"roa-poi": "Poitevin-Saintongeais",
"roa-tar": "Tarantino",
"rob": "Tae'",
"roc": "Cacgia Roglai",
"rod": "Rogo",
"roe": "Ronji",
"rof": "Rombo",
"rog": "Northern Roglai",
"rol": "Romblomanon",
"rom": "Romani",
"roo": "Rotokas",
"rop": "Australian Kriol",
"ror": "Rongga",
"rou": "Runga",
"row": "Dela-Oenale",
"rpn": "Repanbitip",
"rpt": "Rapting",
"rri": "Ririo",
"rrm": "Moriori",
"rro": "Roro",
"rrt": "Arritinngithigh",
"rsb": "Romano-Serbian",
"rsk": "Pannonian Rusyn",
"rsl": "Russian Sign Language",
"rsm": "Miriwoong Sign Language",
"rsn": "Rwandan Sign Language",
"rtc": "Rungtu",
"rth": "Ratahan",
"rtm": "Rotuman",
"rtw": "Rathawi",
"ru": "Russian",
"rub": "Gungu",
"ruc": "Ruuli",
"rue": "Carpathian Rusyn",
"ruf": "Luguru",
"rug": "Roviana",
"ruh": "Ruga",
"rui": "Rufiji",
"ruk": "Che",
"ruo": "Istro-Romanian",
"rup": "Aromanian",
"ruq": "Megleno-Romanian",
"rut": "Rutul",
"ruu": "Lanas Lobu",
"ruy": "Mala (Nigeria)",
"ruz": "Ruma",
"rw": "Rwanda-Rundi",
"rwa": "Rawo",
"rwk": "Rwa",
"rwm": "Amba",
"rwo": "Rawa",
"rxd": "Ngardi",
"rxw": "Karuwali",
"ryn": "Northern Amami Ōshima",
"rys": "Yaeyama",
"ryu": "Okinawan",
"rzh": "Razihi",
"sa": "Sanskrit",
"saa": "Saba",
"sab": "Buglere",
"sac": "Fox",
"sad": "Sandawe",
"sae": "Sabanê",
"saf": "Safaliba",
"sah": "Yakut",
"sai-all": "Allentiac",
"sai-and": "Andoquero",
"sai-ayo": "Ayomán",
"sai-bae": "Baenan",
"sai-bag": "Bagua",
"sai-bet": "Betoi",
"sai-bor-pro": "Proto-Boran",
"sai-cac": "Cacán",
"sai-caq": "Caranqui",
"sai-car-pro": "Proto-Cariban",
"sai-cat": "Catacao",
"sai-cer-pro": "Proto-Cerrado",
"sai-chi": "Chirino",
"sai-chn": "Chaná",
"sai-chp": "Chapacura",
"sai-chr": "Charrua",
"sai-chu": "Churuya",
"sai-cje-pro": "Proto-Central Jê",
"sai-cmg": "Comechingon",
"sai-cno": "Chono",
"sai-cnr": "Cañari",
"sai-coe": "Coeruna",
"sai-col": "Colán",
"sai-cop": "Copallén",
"sai-crd": "Coroado Puri",
"sai-ctq": "Catuquinaru",
"sai-cul": "Culli",
"sai-cva": "Cueva",
"sai-esm": "Esmeralda",
"sai-ewa": "Ewarhuyana",
"sai-gam": "Gamela",
"sai-gay": "Gayón",
"sai-gmo": "Guamo",
"sai-gua": "Guachí",
"sai-gue": "Güenoa",
"sai-hau": "Haush",
"sai-jee-pro": "Proto-Jê",
"sai-jko": "Jeikó",
"sai-jrj": "Jirajara",
"sai-kat": "Katembri",
"sai-mal": "Malalí",
"sai-mar": "Maratino",
"sai-mat": "Matanawi",
"sai-mcn": "Mocana",
"sai-men": "Menien",
"sai-mil": "Millcayac",
"sai-mlb": "Malibu",
"sai-msk": "Masakará",
"sai-muc": "Mucuchí",
"sai-mue": "Muellama",
"sai-muz": "Muzo",
"sai-mys": "Maynas",
"sai-nat": "Natú",
"sai-nje-pro": "Proto-Northern Jê",
"sai-opo": "Opón",
"sai-oto": "Otomaco",
"sai-pal": "Palta",
"sai-pam": "Pamigua",
"sai-par": "Paratió",
"sai-peb": "Peba",
"sai-pnz": "Panzaleo",
"sai-prh": "Puruhá",
"sai-ptg": "Patagón",
"sai-pur": "Purukotó",
"sai-pyg": "Payaguá",
"sai-pyk": "Pykobjê",
"sai-qmb": "Quimbaya",
"sai-qtm": "Quitemo",
"sai-rab": "Rabona",
"sai-ram": "Ramanos",
"sai-sac": "Sácata",
"sai-san": "Sanaviron",
"sai-sap": "Sapará",
"sai-sec": "Sechura",
"sai-sin": "Sinúfana",
"sai-sje-pro": "Proto-Southern Jê",
"sai-tab": "Tabancale",
"sai-tal": "Tallán",
"sai-tap": "Tapayuna",
"sai-tar-pro": "Proto-Taranoan",
"sai-teu": "Teushen",
"sai-tim": "Timote",
"sai-tpr": "Taparita",
"sai-trr": "Tarairiú",
"sai-wai": "Waitaká",
"sai-way": "Wayumara",
"sai-wit-pro": "Proto-Witotoan",
"sai-wnm": "Wanham",
"sai-xoc": "Xocó",
"sai-yao": "Yao (South America)",
"sai-yar": "Yarumá",
"sai-yri": "Yuri",
"sai-yup": "Yupua",
"sai-yur": "Yurumanguí",
"saj": "Sahu",
"sak": "Sake",
"sal-pro": "Proto-Salish",
"sam": "Samaritan Aramaic",
"sao": "Sause",
"saq": "Samburu",
"sar": "Saraveca",
"sas": "Sasak",
"sat": "Santali",
"sau": "Saleman",
"sav": "Saafi-Saafi",
"saw": "Sawi",
"sax": "Sa",
"say": "Saya",
"saz": "Saurashtra",
"sba": "Ngambay",
"sbb": "Simbo",
"sbc": "Gele'",
"sbd": "Southern Samo",
"sbe": "Saliba (New Guinea)",
"sbf": "Shabo",
"sbg": "Seget",
"sbh": "Sori-Harengan",
"sbi": "Seti",
"sbj": "Surbakhal",
"sbk": "Safwa",
"sbl": "Botolan Sambal",
"sbm": "Sagala",
"sbn": "Sindhi Bhil",
"sbo": "Sabüm",
"sbp": "Sangu (Tanzania)",
"sbq": "Sirva",
"sbr": "Sembakung Murut",
"sbs": "Subiya",
"sbt": "Kimki",
"sbu": "Stod Bhoti",
"sbv": "Sabine",
"sbw": "Simba",
"sbx": "Seberuang",
"sby": "Soli",
"sbz": "Sara Kaba",
"sc": "Sardinian",
"scb": "Chut",
"sce": "Dongxiang",
"scf": "San Miguel Creole French",
"scg": "Sanggau",
"sch": "Sakachep",
"sci": "Sri Lankan Creole Malay",
"sck": "Sadri",
"scl": "Shina",
"scn": "Sicilian",
"sco": "Scots",
"scp": "Yolmo",
"scq": "Sa'och",
"scs": "North Slavey",
"scu": "Shumcho",
"scv": "Sheni",
"scw": "Sha",
"scx": "Sicel",
"scz": "Shetland",
"sd": "Sindhi",
"sda": "Toraja-Sa'dan",
"sdb": "Shabak",
"sdc": "Sassarese",
"sde": "Surubu",
"sdf": "Sarli",
"sdg": "Savi",
"sdh": "Southern Kurdish",
"sdj": "Suundi",
"sdk": "Sos Kundi",
"sdl": "Saudi Arabian Sign Language",
"sdm": "Semandang",
"sdn": "Gallurese",
"sdo": "Bukar-Sadung Bidayuh",
"sdp": "Sherdukpen",
"sdr": "Oraon Sadri",
"sds": "Tunisian Berber",
"sdu": "Sarudu",
"sdv-daj-pro": "Proto-Daju",
"sdv-eje-pro": "Proto-Eastern Jebel",
"sdv-nil-pro": "Proto-Nilotic",
"sdv-nyi-pro": "Proto-Nyima",
"sdv-tmn-pro": "Proto-Taman",
"sdx": "Sibu Melanau",
"se": "Northern Sami",
"sea": "Semai",
"sec": "Sechelt",
"sed": "Sedang",
"see": "Seneca",
"sef": "Cebaara",
"seg": "Segeju",
"seh": "Sena",
"sei": "Seri",
"sej": "Sene",
"sek": "Sekani",
"sel-nor": "Northern Selkup",
"sel-pro": "Proto-Selkup",
"sel-sou": "Southern Selkup",
"sem-amm": "Ammonite",
"sem-amo": "Amorite",
"sem-cha": "Chaha",
"sem-dad": "Dadanitic",
"sem-dum": "Dumaitic",
"sem-has": "Hasaitic",
"sem-his": "Hismaic",
"sem-mhr": "Muher",
"sem-pro": "Proto-Semitic",
"sem-saf": "Safaitic",
"sem-sam": "Samalian",
"sem-srb": "Old South Arabian",
"sem-tay": "Taymanitic",
"sem-tha": "Thamudic",
"sem-wes-pro": "Proto-West Semitic",
"sen": "Nanerige",
"seo": "Asaba",
"sep": "Sicite",
"seq": "Senara",
"ser": "Serrano",
"ses": "Koyraboro Senni",
"set": "Sentani",
"seu": "Serui-Laut",
"sev": "Nyarafolo",
"sew": "Sewa Bay",
"sey": "Secoya",
"sez": "Senthang Chin",
"sfb": "French Belgian Sign Language",
"sfe": "Eastern Subanun",
"sfm": "Small Flowery Miao",
"sfs": "South African Sign Language",
"sfw": "Sehwi",
"sg": "Sango",
"sga": "Old Irish",
"sgb": "Mag-Anchi Ayta",
"sgc": "Kipsigis",
"sgd": "Surigaonon",
"sge": "Segai",
"sgg": "Swiss-German Sign Language",
"sgh": "Shughni",
"sgi": "Suga",
"sgk": "Sangkong",
"sgm": "Singa",
"sgp": "Singpho",
"sgr": "Sangisari",
"sgs": "Samogitian",
"sgt": "Brokpake",
"sgu": "Salas",
"sgw": "Sebat Bet Gurage",
"sgx": "Sierra Leone Sign Language",
"sgy": "Sanglechi",
"sgz": "Sursurunga",
"sh": "Serbo-Croatian",
"sha": "Shall-Zwall",
"shb": "Ninam",
"shc": "Sonde",
"shd": "Kundal Shahi",
"she": "Sheko",
"shg": "Shua",
"shh": "Shoshone",
"shi": "Tashelhit",
"shj": "Shatt",
"shk": "Shilluk",
"shl": "Shendu",
"shm": "Shahrudi",
"shn": "Shan",
"sho": "Shanga",
"shp": "Shipibo-Conibo",
"shq": "Sala",
"shr": "Shi",
"shs": "Shuswap",
"sht": "Shasta",
"shu": "Chadian Arabic",
"shv": "Shehri",
"shw": "Shwai",
"shx": "She",
"shy": "Tachawit",
"shz": "Syenara",
"si": "Sinhalese",
"sia": "Akkala Sami",
"sib": "Sebop",
"sid": "Sidamo",
"sie": "Simaa",
"sif": "Siamou",
"sig": "Paasaal",
"sih": "Sîshëë",
"sii": "Shom Peng",
"sij": "Numbami",
"sik": "Sikiana",
"sil": "Tumulung Sisaala",
"sim": "Mende (New Guinea)",
"sio-pro": "Proto-Siouan",
"sip": "Sikkimese",
"siq": "Sonia",
"sir": "Siri",
"sis": "Siuslaw",
"sit-aao-pro": "Proto-Central Naga",
"sit-bai-pro": "Proto-Bai",
"sit-ban": "Bangru",
"sit-bdi-pro": "Proto-Bodish",
"sit-bok": "Bokar",
"sit-cai": "Caijia",
"sit-cha": "Chairel",
"sit-ers-pro": "Proto-Ersuic",
"sit-hrs-pro": "Proto-Hrusish",
"sit-jap": "Japhug",
"sit-kha-pro": "Proto-Kham",
"sit-khb-pro": "Proto-Kho-Bwa",
"sit-khp-pro": "Proto-Puroik",
"sit-khw-pro": "Proto-Western Kho-Bwa",
"sit-kon-pro": "Proto-Northern Naga",
"sit-liz": "Lizu",
"sit-lnj": "Longjia",
"sit-lrn": "Luren",
"sit-luu-pro": "Proto-Luish",
"sit-nas-pro": "Proto-Naish",
"sit-prn": "Puiron",
"sit-pro": "Proto-Sino-Tibetan",
"sit-sit": "Situ",
"sit-tam-pro": "Proto-Tamangic",
"sit-tan-pro": "Proto-Tani",
"sit-tgm": "Tangam",
"sit-tng-pro": "Proto-Tangkhulic",
"sit-tos": "Tosu",
"sit-tsh": "Tshobdun",
"sit-zbu": "Zbu",
"siu": "Sinagen",
"siv": "Sumariup",
"siw": "Siwai",
"six": "Sumau",
"siy": "Sivandi",
"siz": "Siwi",
"sja": "Epena",
"sjb": "Sajau Basap",
"sjc": "Shaojiang Min",
"sjd": "Kildin Sami",
"sje": "Pite Sami",
"sjg": "Assangori",
"sjk": "Kemi Sami",
"sjl": "Miji",
"sjm": "Mapun",
"sjn": "Sindarin",
"sjo": "Xibe",
"sjp": "Surjapuri",
"sjr": "Siar-Lak",
"sjs": "Senhaja de Srair",
"sjt": "Ter Sami",
"sju": "Ume Sami",
"sjw": "Shawnee",
"sk": "Slovak",
"skb": "Saek",
"skc": "Ma Manda",
"skd": "Southern Sierra Miwok",
"ske": "Ske",
"skf": "Mekéns",
"skh": "Sikule",
"ski": "Sika",
"skj": "Seke",
"skk": "Sok",
"skm": "Sakam",
"skn": "Kolibugan Subanon",
"sko": "Seko Tengah",
"skp": "Sekapan",
"skq": "Sininkere",
"skr": "Saraiki",
"sks": "Maia",
"skt": "Sakata",
"sku": "Sakao",
"skv": "Skou",
"skw": "Skepi Creole Dutch",
"skx": "Seko Padang",
"sky": "Sikaiana",
"skz": "Sekar",
"sl": "Slovene",
"sla-pro": "Proto-Slavic",
"slc": "Saliba (Colombia)",
"sld": "Sisaala",
"sle": "Sholaga",
"slf": "Swiss-Italian Sign Language",
"slg": "Selungai Murut",
"slj": "Salumá",
"sll": "Salt-Yui",
"slm": "Pangutaran Sama",
"sln": "Salinan",
"slp": "Lamaholot",
"slr": "Salar",
"sls": "Singapore Sign Language",
"slt": "Sila",
"slu": "Selaru",
"slw": "Sialum",
"slx": "Salampasu",
"sly": "Selayar",
"slz": "Ma'ya",
"sm": "Samoan",
"sma": "Southern Sami",
"smb": "Simbari",
"smc": "Som",
"smd": "Sama",
"smf": "Auwe",
"smg": "Simbali",
"smh": "Samei",
"smi-pro": "Proto-Samic",
"smj": "Lule Sami",
"smk": "Bolinao",
"sml": "Central Sama",
"smm": "Musasa",
"smn": "Inari Sami",
"smp": "Samaritan Hebrew",
"smq": "Samo",
"smr": "Simeulue",
"sms": "Skolt Sami",
"smt": "Simte",
"smu": "Somray",
"smv": "Samvedi",
"smw": "Sumbawa",
"smx": "Samba",
"smy": "Semnani",
"smz": "Simeku",
"sn": "Shona",
"snb": "Sebuyau",
"snc": "Sinaugoro",
"sne": "Bau Bidayuh",
"snf": "Noon",
"sng": "Sanga (Congo)",
"sni": "Sensi",
"snj": "Riverain Sango",
"snk": "Soninke",
"snl": "Sangil",
"snm": "Southern Ma'di",
"snn": "Siona",
"snp": "Siane",
"snq": "Sangu (Gabon)",
"snr": "Sihan",
"sns": "Nahavaq",
"snu": "Senggi",
"snv": "Sa'ban",
"snw": "Selee",
"snx": "Sam",
"sny": "Saniyo-Hiyewe",
"snz": "Kou",
"so": "Somali",
"soa": "Thai Song",
"sob": "Sobei",
"soc": "Soko",
"sod": "Songoora",
"soe": "Songomeno",
"sog": "Sogdian",
"soh": "Aka (Sudan)",
"soi": "Sonha",
"sok": "Sokoro",
"sol": "Solos",
"son-pro": "Proto-Songhay",
"soo": "Nsong",
"sop": "Songe",
"soq": "Kanasi",
"sor": "Somrai",
"sos": "Seenku",
"sou": "Southern Thai",
"sov": "Sonsorolese",
"sow": "Sowanda",
"sox": "Swo",
"soy": "Miyobe",
"soz": "Temi",
"spb": "Sepa (Indonesia)",
"spc": "Sapé",
"spd": "Saep",
"spe": "Sepa (New Guinea)",
"spg": "Sian",
"spi": "Saponi",
"spk": "Sengo",
"spl": "Selepet",
"spm": "Sepen",
"spn": "Sanapaná",
"spo": "Spokane",
"spp": "Supyire",
"spr": "Saparua",
"sps": "Saposa",
"spt": "Spiti Bhoti",
"spu": "Sapuan",
"spv": "Sambalpuri",
"spx": "South Picene",
"spy": "Sabaot",
"sq": "Albanian",
"sqa": "Shama-Sambuga",
"sqh": "Shau",
"sqj-pro": "Proto-Albanian",
"sqk": "Albanian Sign Language",
"sqm": "Suma",
"sqn": "Susquehannock",
"sqo": "Sorkhei",
"sqq": "Sou",
"sqr": "Siculo-Arabic",
"sqs": "Sri Lankan Sign Language",
"sqt": "Soqotri",
"squ": "Squamish",
"sra": "Saruga",
"srb": "Sora",
"sre": "Sara",
"srf": "Nafi",
"srg": "Sulod",
"srh": "Sarikoli",
"sri": "Siriano",
"srk": "Serudung Murut",
"srl": "Isirawa",
"srm": "Saramaccan",
"srn": "Sranan Tongo",
"srq": "Sirionó",
"srr": "Serer",
"srs": "Tsuut'ina",
"srt": "Sauri",
"sru": "Suruí",
"srv": "Waray Sorsogon",
"srw": "Serua",
"srx": "Sirmauri",
"sry": "Sera",
"srz": "Shahmirzadi",
"ss": "Swazi",
"ssa-klk-pro": "Proto-Kuliak",
"ssa-kom-pro": "Proto-Koman",
"ssa-pro": "Proto-Nilo-Saharan",
"ssb": "Southern Sama",
"ssc": "Suba-Simbiti",
"ssd": "Siroi",
"sse": "Balangingi",
"ssf": "Thao",
"ssg": "Seimat",
"ssh": "Shihhi Arabic",
"ssi": "Sansi",
"ssj": "Sausi",
"ssk": "Sunam",
"ssl": "Western Sisaala",
"ssm": "Semnam",
"sso": "Sissano",
"ssp": "Spanish Sign Language",
"ssq": "So'a",
"ssr": "Swiss-French Sign Language",
"sss": "Sô",
"sst": "Sinasina",
"ssu": "Susuami",
"ssv": "Shark Bay",
"ssx": "Samberigi",
"ssy": "Saho",
"ssz": "Sengseng",
"st": "Sotho",
"stb": "Northern Subanen",
"std": "Sentinelese",
"ste": "Liana-Seti",
"stf": "Seta",
"stg": "Trieng",
"sth": "Shelta",
"sti": "Bulo Stieng",
"stj": "Matya Samo",
"stk": "Arammba",
"stm": "Setaman",
"stn": "Owa",
"sto": "Stoney",
"stp": "Southeastern Tepehuan",
"stq": "Saterland Frisian",
"str": "Saanich",
"sts": "Shumashti",
"stt": "Budeh Stieng",
"stu": "Samtao",
"stv": "Silt'e",
"stw": "Satawalese",
"sty": "Siberian Tatar",
"su": "Sundanese",
"sua": "Sulka",
"sub": "Suku",
"suc": "Western Subanon",
"sue": "Suena",
"sug": "Suganga",
"sui": "Suki",
"suk": "Sukuma",
"suo": "Bouni",
"suq": "Suri",
"sur": "Mwaghavul",
"sus": "Susu",
"sut": "Subtiaba",
"suv": "Puroik",
"suw": "Sumbwa",
"sux": "Sumerian",
"suy": "Suyá",
"suz": "Sunwar",
"sv": "Swedish",
"sva": "Svan",
"svb": "Ulau-Suain",
"svc": "Vincentian Creole English",
"sve": "Serili",
"svk": "Slovakian Sign Language",
"svm": "Slavomolisano",
"svs": "Savosavo",
"svx": "Skalvian",
"sw": "Swahili",
"swb": "Maore Comorian",
"swf": "Sere",
"swg": "Swabian",
"swi": "Sui",
"swj": "Sira",
"swl": "Swedish Sign Language",
"swm": "Samosa",
"swn": "Sokna",
"swo": "Shanenawa",
"swp": "Suau",
"swq": "Sharwa",
"swr": "Saweru",
"sws": "Seluwasan",
"swt": "Sawila",
"swu": "Suwawa",
"sww": "Sowa",
"swx": "Suruahá",
"swy": "Sarua",
"sxb": "Suba",
"sxc": "Sicanian",
"sxe": "Sighu",
"sxg": "Shixing",
"sxk": "Southern Kalapuya",
"sxl": "Selonian",
"sxm": "Samre",
"sxn": "Sangir",
"sxo": "Sorothaptic",
"sxr": "Saaroa",
"sxs": "Sasaru",
"sxw": "Saxwe Gbe",
"sya": "Siang",
"syb": "Central Subanen",
"syc": "Classical Syriac",
"syd-pro": "Proto-Samoyedic",
"syi": "Seki",
"syk": "Sukur",
"syl": "Sylheti",
"sym": "Maya Samo",
"syn": "Senaya",
"syo": "Suoy",
"sys": "Sinyar",
"syw": "Kagate",
"syx": "Osamayi",
"syy": "Al-Sayyid Bedouin Sign Language",
"sza": "Semelai",
"szb": "Ngalum",
"szc": "Semaq Beri",
"szd": "Seru",
"sze": "Seze",
"szg": "Sengele",
"szl": "Silesian",
"szn": "Sula",
"szp": "Suabo",
"szv": "Isubu",
"szw": "Sawai",
"szy": "Sakizaya",
"ta": "Tamil",
"taa": "Lower Tanana",
"tab": "Tabasaran",
"tac": "Lowland Tarahumara",
"tad": "Tause",
"tae": "Tariana",
"taf": "Tapirapé",
"tag": "Tagoi",
"tai-pro": "Proto-Tai",
"tai-swe-pro": "Proto-Southwestern Tai",
"taj": "Eastern Tamang",
"tak": "Tala",
"tal": "Tal",
"tan": "Tangale",
"tao": "Yami",
"tap": "Taabwa",
"tar": "Central Tarahumara",
"tas": "Tây Bồi",
"tau": "Upper Tanana",
"tav": "Tatuyo",
"taw": "Tai",
"tax": "Tamki",
"tay": "Atayal",
"taz": "Tocho",
"tba": "Aikanã",
"tbc": "Takia",
"tbd": "Kaki Ae",
"tbe": "Tanimbili",
"tbf": "Mandara",
"tbg": "North Tairora",
"tbh": "Thurawal",
"tbi": "Gaam",
"tbj": "Tiang",
"tbk": "Calamian Tagbanwa",
"tbl": "Tboli",
"tbm": "Tagbu",
"tbn": "Barro Negro Tunebo",
"tbo": "Tawala",
"tbp": "Taworta",
"tbq-bdg-pro": "Proto-Bodo-Garo",
"tbq-blg": "Bailang",
"tbq-brm-pro": "Proto-Burmish",
"tbq-gkh": "Gokhy",
"tbq-kuk-pro": "Proto-Kuki-Chin",
"tbq-lal-pro": "Proto-Lalo",
"tbq-laz": "Laze",
"tbq-lob-pro": "Proto-Lolo-Burmese",
"tbq-lol-pro": "Proto-Loloish",
"tbq-mil": "Milang",
"tbq-mor": "Moran",
"tbq-ngo": "Ngochang",
"tbr": "Tumtum",
"tbs": "Tanguat",
"tbt": "Kitembo",
"tbu": "Tubar",
"tbv": "Tobo",
"tbw": "Aborlan Tagbanwa",
"tbx": "Kapin",
"tby": "Tabaru",
"tbz": "Ditammari",
"tca": "Ticuna",
"tcb": "Tanacross",
"tcc": "Datooga",
"tcd": "Tafi",
"tce": "Southern Tutchone",
"tcf": "Malinaltepec Tlapanec",
"tcg": "Tamagario",
"tch": "Turks and Caicos Creole English",
"tci": "Wára",
"tck": "Tchitchege",
"tcl": "Taman (Myanmar)",
"tcm": "Tanahmerah",
"tco": "Taungyo",
"tcp": "Tawr Chin",
"tcq": "Kaiy",
"tcs": "Torres Strait Creole",
"tct": "T'en",
"tcu": "Southeastern Tarahumara",
"tcw": "Tecpatlán Totonac",
"tcx": "Toda",
"tcy": "Tulu",
"tcz": "Thado Chin",
"tda": "Tagdal",
"tdb": "Panchpargania",
"tdc": "Emberá-Tadó",
"tdd": "Tai Nüa",
"tde": "Tiranige Diga Dogon",
"tdf": "Talieng",
"tdg": "Western Tamang",
"tdh": "Thulung",
"tdi": "Tomadino",
"tdj": "Tajio",
"tdk": "Tambas",
"tdl": "Sur",
"tdm": "Taruma",
"tdn": "Tondano",
"tdo": "Teme",
"tdq": "Tita",
"tdr": "Todrah",
"tds": "Doutai",
"tdt": "Tetun Dili",
"tdu": "Tempasuk Dusun",
"tdv": "Toro",
"tdy": "Tadyawan",
"te": "Telugu",
"tea": "Temiar",
"teb": "Tetete",
"tec": "Terik",
"ted": "Tepo Krumen",
"tee": "Huehuetla Tepehua",
"tef": "Teressa",
"teg": "Teke-Tege",
"teh": "Tehuelche",
"tei": "Torricelli",
"tek": "Ibali Teke",
"tem": "Temne",
"ten": "Tama (Colombia)",
"teo": "Ateso",
"tep": "Tepecano",
"teq": "Temein",
"ter": "Tereno",
"tes": "Tengger",
"tet": "Tetum",
"teu": "Soo",
"tev": "Teor",
"tew": "Tewa",
"tex": "Tennet",
"tey": "Tulishi",
"tez": "Tetserret",
"tfi": "Tofin Gbe",
"tfn": "Dena'ina",
"tfo": "Tefaro",
"tfr": "Teribe",
"tft": "Ternate",
"tg": "Tajik",
"tga": "Sagalla",
"tgb": "Tobilung",
"tgc": "Tigak",
"tgd": "Ciwogai",
"tge": "Eastern Gorkha Tamang",
"tgf": "Chali",
"tgh": "Tobagonian Creole English",
"tgi": "Lawunuia",
"tgn": "Tandaganon",
"tgo": "Sudest",
"tgp": "Tangoa",
"tgq": "Tring",
"tgr": "Tareng",
"tgs": "Nume",
"tgt": "Central Tagbanwa",
"tgu": "Tanggu",
"tgv": "Tingui-Boto",
"tgw": "Tagwana",
"tgx": "Tagish",
"tgy": "Togoyo",
"th": "Thai",
"thc": "Tai Hang Tong",
"thd": "Kuuk Thaayorre",
"the": "Chitwania Tharu",
"thf": "Thangmi",
"thh": "Northern Tarahumara",
"thi": "Tai Long",
"thk": "Tharaka",
"thl": "Dangaura Tharu",
"thm": "Thavung",
"thn": "Thachanadan",
"thp": "Thompson",
"thq": "Kochila Tharu",
"thr": "Rana Tharu",
"ths": "Thakali",
"tht": "Tahltan",
"thu": "Thuri",
"thy": "Tha",
"ti": "Tigrinya",
"tic": "Tira",
"tif": "Tifal",
"tig": "Tigre",
"tih": "Timugon Murut",
"tii": "Tiene",
"tij": "Tilung",
"tik": "Tikar",
"til": "Tillamook",
"tim": "Timbe",
"tin": "Tindi",
"tio": "Teop",
"tip": "Trimuris",
"tiq": "Tiéfo",
"tis": "Masadiit Itneg",
"tit": "Tinigua",
"tiu": "Adasen",
"tiv": "Tiv",
"tiw": "Tiwi",
"tix": "Southern Tiwa",
"tiy": "Tiruray",
"tiz": "Tai Hongjin",
"tja": "Tajuasohn",
"tjg": "Tunjung",
"tji": "Northern Tujia",
"tjl": "Tai Laing",
"tjm": "Timucua",
"tjn": "Tonjon",
"tjs": "Southern Tujia",
"tju": "Tjurruru",
"tjw": "Chaap Wuurong",
"tk": "Turkmen",
"tka": "Truká",
"tkb": "Buksa",
"tkd": "Tukudede",
"tke": "Takwane",
"tkf": "Tukumanféd",
"tkl": "Tokelauan",
"tkm": "Takelma",
"tkn": "Tokunoshima",
"tkp": "Tikopia",
"tkq": "Tee",
"tkr": "Tsakhur",
"tks": "Ramandi",
"tkt": "Kathoriya Tharu",
"tku": "Upper Necaxa Totonac",
"tkv": "Mur Pano",
"tkw": "Teanu",
"tkx": "Tangko",
"tkz": "Takua",
"tl": "Tagalog",
"tla": "Southwestern Tepehuan",
"tlb": "Tobelo",
"tlc": "Misantla Totonac",
"tld": "Talaud",
"tlf": "Telefol",
"tlg": "Tofanma",
"tlh": "Klingon",
"tli": "Tlingit",
"tlj": "Talinga-Bwisi",
"tlk": "Taloki",
"tll": "Tetela",
"tlm": "Tolomako",
"tln": "Talondo'",
"tlo": "Talodi",
"tlp": "Filomena Mata-Coahuitlán Totonac",
"tlq": "Tai Loi",
"tlr": "Talise",
"tls": "Tambotalo",
"tlt": "Teluti",
"tlu": "Tulehu",
"tlv": "Taliabu",
"tlx": "Khehek",
"tly": "Talysh",
"tma": "Tama (Chad)",
"tmb": "Avava",
"tmc": "Tumak",
"tmd": "Haruai",
"tme": "Tremembé",
"tmf": "Toba-Maskoy",
"tmg": "Ternateño",
"tmh": "Tuareg",
"tmi": "Tutuba",
"tmj": "Samarokena",
"tml": "Tamnim Citak",
"tmm": "Tai Thanh",
"tmn": "Taman (Indonesia)",
"tmo": "Temoq",
"tmq": "Tumleo",
"tms": "Tima",
"tmt": "Tasmate",
"tmu": "Iau",
"tmv": "Motembo",
"tmy": "Tami",
"tmz": "Tamanaku",
"tn": "Tswana",
"tna": "Tacana",
"tnb": "Western Tunebo",
"tnc": "Tanimuca-Retuarã",
"tnd": "Angosturas Tunebo",
"tne": "Tinoc Kallahan",
"tng": "Tobanga",
"tnh": "Maiani",
"tni": "Tandia",
"tnk": "Kwamera",
"tnl": "Lenakel",
"tnm": "Tabla",
"tnn": "North Tanna",
"tno": "Toromono",
"tnp": "Whitesands",
"tnq": "Taíno",
"tnr": "Bedik",
"tns": "Tenis",
"tnt": "Tontemboan",
"tnu": "Tay Khang",
"tnv": "Tanchangya",
"tnw": "Tonsawang",
"tnx": "Tanema",
"tny": "Tongwe",
"tnz": "Ten'edn",
"to": "Tongan",
"tob": "Toba",
"toc": "Coyutla Totonac",
"tod": "Toma",
"tof": "Gizrra",
"tog": "Tonga (Malawi)",
"toh": "Tonga (Mozambique)",
"toi": "Tonga (Zambia)",
"toj": "Tojolabal",
"tok": "Toki Pona",
"tol": "Tolowa",
"tom": "Tombulu",
"too": "Xicotepec de Juárez Totonac",
"top": "Papantla Totonac",
"toq": "Toposa",
"tor": "Togbo-Vara Banda",
"tos": "Highland Totonac",
"tou": "Tho",
"tov": "Upper Taromi",
"tow": "Jemez",
"tox": "Tobian",
"toy": "Topoiyo",
"toz": "To",
"tpa": "Taupota",
"tpc": "Azoyú Me'phaa",
"tpe": "Tippera",
"tpf": "Tarpia",
"tpg": "Kula",
"tpi": "Tok Pisin",
"tpj": "Tapieté",
"tpk": "Tupinikin",
"tpl": "Tlacoapa Me'phaa",
"tpm": "Tampulma",
"tpn": "Tupinambá",
"tpo": "Tai Pao",
"tpp": "Pisaflores Tepehua",
"tpq": "Tukpa",
"tpr": "Tuparí",
"tpt": "Tlachichilco Tepehua",
"tpu": "Tampuan",
"tpv": "Tanapag",
"tpw": "Old Tupi",
"tpx": "Acatepec Me'phaa",
"tpy": "Trumai",
"tpz": "Tinputz",
"tqb": "Tembé",
"tql": "Lehali",
"tqm": "Turumsa",
"tqn": "Tenino",
"tqo": "Toaripi",
"tqp": "Tomoip",
"tqq": "Tunni",
"tqr": "Torona",
"tqt": "Western Totonac",
"tqu": "Touo",
"tqw": "Tonkawa",
"tr": "Turkish",
"tra": "Tirahi",
"trb": "Terebu",
"trc": "Copala Triqui",
"trd": "Turi",
"tre": "East Tarangan",
"trf": "Trinidadian Creole English",
"trg": "Lishán Didán",
"trh": "Turaka",
"tri": "Trió",
"trj": "Toram",
"trk-dkh": "Dukhan",
"trk-eog": "Early Old Oghuz",
"trk-oat": "Old Anatolian Turkish",
"trk-pro": "Proto-Turkic",
"trl": "Traveller Scottish",
"trm": "Tregami",
"trn": "Trinitario",
"tro": "Tarao",
"trp": "Kokborok",
"trq": "San Martín Itunyoso Triqui",
"trr": "Taushiro",
"trs": "Chicahuaxtla Triqui",
"trt": "Tunggare",
"tru": "Turoyo",
"trv": "Taroko",
"trw": "Torwali",
"trx": "Tringgus",
"try": "Turung",
"trz": "Torá",
"ts": "Tsonga",
"tsa": "Tsaangi",
"tsb": "Tsamai",
"tsc": "Tswa",
"tsd": "Tsakonian",
"tse": "Tunisian Sign Language",
"tsg": "Tausug",
"tsh": "Tsuvan",
"tsi": "Tsimshian",
"tsj": "Tshangla",
"tsl": "Ts'ün-Lao",
"tsm": "Turkish Sign Language",
"tsp": "Northern Toussian",
"tsq": "Thai Sign Language",
"tsr": "Akei",
"tss": "Taiwan Sign Language",
"tsu": "Tsou",
"tsv": "Tsogo",
"tsw": "Tsishingini",
"tsx": "Mubami",
"tsy": "Tebul Sign Language",
"tt": "Tatar",
"tta": "Tutelo",
"ttb": "Gaa",
"ttc": "Tektiteko",
"ttd": "Tauade",
"tte": "Bwanabwana",
"ttf": "Tuotomb",
"ttg": "Tutong",
"tth": "Upper Ta'oih",
"tti": "Tobati",
"ttj": "Tooro",
"ttk": "Totoro",
"ttl": "Totela",
"ttm": "Northern Tutchone",
"ttn": "Towei",
"tto": "Lower Ta'oih",
"ttp": "Tombelala",
"ttr": "Tera",
"tts": "Isan",
"ttt": "Tat",
"ttu": "Torau",
"ttv": "Titan",
"ttw": "Long Wat",
"tty": "Sikaritai",
"ttz": "Tsum",
"tua": "Wiarumus",
"tub": "Tübatulabal",
"tuc": "Mutu",
"tud": "Tuxá",
"tue": "Tuyuca",
"tuf": "Central Tunebo",
"tug": "Tunia",
"tuh": "Taulil",
"tui": "Tupuri",
"tuj": "Tugutil",
"tul": "Tula",
"tum": "Tumbuka",
"tun": "Tunica",
"tuo": "Tucano",
"tup-gua-pro": "Proto-Tupi-Guarani",
"tup-kab": "Kabishiana",
"tup-pro": "Proto-Tupian",
"tuq": "Tedaga",
"tus": "Tuscarora",
"tuu": "Tututni",
"tuv": "Turkana",
"tuw-alk": "Alchuka",
"tuw-bal": "Bala",
"tuw-kkl": "Kyakala",
"tuw-kli": "Kili",
"tuw-pro": "Proto-Tungusic",
"tuw-sol": "Solon",
"tux": "Tuxináwa",
"tuy": "Tugen",
"tuz": "Turka",
"tva": "Vaghua",
"tvd": "Tsuvadi",
"tve": "Te'un",
"tvk": "Southeast Ambrym",
"tvl": "Tuvaluan",
"tvm": "Tela-Masbuar",
"tvn": "Tavoyan",
"tvo": "Tidore",
"tvs": "Taveta",
"tvt": "Tutsa Naga",
"tvu": "Tunen",
"tvw": "Sedoa",
"tvx": "Taivoan",
"tvy": "Timor Pidgin",
"twa": "Twana",
"twb": "Western Tawbuid",
"twc": "Teshenawa",
"twe": "Teiwa",
"twf": "Taos",
"twg": "Tereweng",
"twh": "Tai Dón",
"twm": "Tawang Monpa",
"twn": "Twendi",
"two": "Tswapong",
"twp": "Ere",
"twq": "Tasawaq",
"twr": "Southwestern Tarahumara",
"twt": "Turiwára",
"twu": "Termanu",
"tww": "Tuwari",
"twy": "Tawoyan",
"txa": "Tombonuo",
"txb": "Tocharian B",
"txc": "Tsetsaut",
"txe": "Totoli",
"txg": "Tangut",
"txh": "Thracian",
"txi": "Ikpeng",
"txj": "Tarjumo",
"txm": "Tomini",
"txn": "West Tarangan",
"txo": "Toto",
"txq": "Tii",
"txr": "Tartessian",
"txs": "Tonsea",
"txt": "Citak",
"txu": "Kayapó",
"txx": "Tatana",
"ty": "Tahitian",
"tya": "Tauya",
"tye": "Kyenga",
"tyh": "O'du",
"tyi": "Teke-Tsaayi",
"tyj": "Tai Do",
"tyl": "Thu Lao",
"tyn": "Kombai",
"typ": "Kuku-Thaypan",
"tyr": "Tai Daeng",
"tys": "Sapa",
"tyt": "Tày Tac",
"tyu": "Kua",
"tyv": "Tuvan",
"tyx": "Teke-Tyee",
"tyz": "Tày",
"tza": "Tanzanian Sign Language",
"tzh": "Tzeltal",
"tzj": "Tz'utujil",
"tzl": "Talossan",
"tzm": "Central Atlas Tamazight",
"tzn": "Tugun",
"tzo": "Tzotzil",
"tzx": "Tabriak",
"uam": "Uamué",
"uan": "Kuan",
"uar": "Tairuma",
"uba": "Ubang",
"ubi": "Ubi",
"ubl": "Buhi'non Bikol",
"ubr": "Ubir",
"ubu": "Umbu-Ungu",
"uby": "Ubykh",
"uda": "Uda",
"ude": "Udihe",
"udg": "Muduga",
"udi": "Udi",
"udj": "Ujir",
"udl": "Uldeme",
"udm": "Udmurt",
"udu": "Uduk",
"ues": "Kioko",
"ufi": "Ufim",
"ug": "Uyghur",
"uga": "Ugaritic",
"ugb": "Kuku-Ugbanh",
"uge": "Ughele",
"ugn": "Ugandan Sign Language",
"ugo": "Gong",
"ugy": "Uruguayan Sign Language",
"uha": "Uhami",
"uhn": "Damal",
"uis": "Uisai",
"uiv": "Iyive",
"uji": "Tanjijili",
"uk": "Ukrainian",
"uka": "Kaburi",
"ukg": "Ukuriguma",
"ukh": "Ukhwejo",
"ukk": "Muak Sa-aak",
"ukl": "Ukrainian Sign Language",
"ukp": "Ukpe-Bayobiri",
"ukq": "Ukwa",
"uks": "Kaapor Sign Language",
"uku": "Ukue",
"ukw": "Ukwuani-Aboh-Ndoni",
"uky": "Kuuk Yak",
"ula": "Fungwa",
"ulb": "Olukumi",
"ulc": "Ulch",
"ule": "Lule",
"ulf": "Afra",
"uli": "Ulithian",
"ulk": "Meriam",
"ull": "Ullatan",
"ulm": "Ulumanda'",
"uln": "Unserdeutsch",
"ulu": "Uma' Lung",
"ulw": "Ulwa (Nicaragua)",
"uma": "Umatilla",
"umb": "Umbundu",
"umc": "Marrucinian",
"umd": "Umbindhamu",
"umg": "Umbuygamu",
"umi": "Ukit",
"umm": "Umon",
"umn": "Makyan Naga",
"umo": "Umotína",
"ump": "Umpila",
"umr": "Umbugarla",
"ums": "Pendau",
"umu": "Munsee",
"una": "North Watut",
"und": "Undetermined",
"une": "Uneme",
"ung": "Ngarinyin",
"uni": "Uni",
"unk": "Enawené-Nawé",
"unm": "Unami",
"unn": "Kurnai",
"unr": "Mundari",
"unu": "Unubahe",
"unx": "Munda",
"unz": "Unde Kaili",
"uok": "Uokha",
"uon": "Kulon",
"upi": "Umeda",
"upv": "Northeast Malakula",
"ur": "Urdu",
"ura": "Urarina",
"urb": "Urubú-Kaapor",
"urc": "Urningangg",
"ure": "Uru",
"urf": "Uradhi",
"urg": "Urigina",
"urh": "Urhobo",
"uri": "Urim",
"urj-fin-pro": "Proto-Finnic",
"urj-koo": "Old Komi",
"urj-kuk": "Kukkuzi",
"urj-kya": "Komi-Yazva",
"urj-mdv-pro": "Proto-Mordvinic",
"urj-prm-pro": "Proto-Permic",
"urj-pro": "Proto-Uralic",
"urj-ugr-pro": "Proto-Ugric",
"urk": "Urak Lawoi'",
"url": "Urali",
"urm": "Urapmin",
"urn": "Uruangnirin",
"uro": "Ura (New Guinea)",
"urp": "Uru-Pa-In",
"urr": "Löyöp",
"urt": "Urat",
"uru": "Urumi",
"urv": "Uruava",
"urw": "Sop",
"urx": "Urimo",
"ury": "Orya",
"urz": "Uru-Eu-Wau-Wau",
"usa": "Usarufa",
"ush": "Ushojo",
"usi": "Usui",
"usk": "Usaghade",
"usp": "Uspanteco",
"uss": "Saare",
"usu": "Uya",
"uta": "Otank",
"ute": "Ute",
"uth": "Hun",
"utp": "Aba",
"utr": "Etulo",
"utu": "Utu",
"uum": "Urum",
"uur": "Ura (Vanuatu)",
"uuu": "U",
"uve": "West Uvean",
"uvh": "Uri",
"uvl": "Lote",
"uwa": "Kuku-Uwanh",
"uya": "Doko-Uyanga",
"uz": "Uzbek",
"vaa": "Vaagri Booli",
"vae": "Vale",
"vag": "Vagla",
"vah": "Varhadi",
"vai": "Vai",
"vaj": "Sekele",
"val": "Vehes",
"vam": "Vanimo",
"van": "Valman",
"vao": "Vao",
"vap": "Vaiphei",
"var": "Huarijio",
"vas": "Vasavi",
"vau": "Vanuma",
"vav": "Varli",
"vay": "Vayu",
"vbb": "Southeast Babar",
"vbk": "Southwestern Bontoc",
"ve": "Venda",
"vec": "Venetan",
"ved": "Veddah",
"vem": "Vemgo-Mabas",
"veo": "Ventureño",
"vep": "Veps",
"ver": "Mom Jango",
"vgr": "Vaghri",
"vgt": "Flemish Sign Language",
"vi": "Vietnamese",
"vic": "Virgin Islands Creole",
"vid": "Vidunda",
"vif": "Vili",
"vig": "Viemo",
"vil": "Vilela",
"vis": "Vishavan",
"vit": "Viti",
"viv": "Iduna",
"vjk": "Bajjika",
"vka": "Kariyarra",
"vki": "Ija-Zuba",
"vkj": "Kujarge",
"vkk": "Kaur",
"vkl": "Kulisusu",
"vkm": "Kamakan",
"vko": "Kodeoha",
"vkp": "Korlai Creole Portuguese",
"vkt": "Tenggarong Kutai Malay",
"vku": "Kurrama",
"vlp": "Valpei",
"vls": "West Flemish",
"vma": "Martuthunira",
"vmb": "Mbabaram",
"vmc": "Juxtlahuaca Mixtec",
"vmd": "Mudu Koraga",
"vme": "East Masela",
"vmf": "East Franconian",
"vmg": "Vinitiri",
"vmh": "Maraghei",
"vmi": "Miwa",
"vmj": "Ixtayutla Mixtec",
"vmk": "Makhuwa-Shirima",
"vml": "Malgana",
"vmm": "Mitlatongo Mixtec",
"vmp": "Soyaltepec Mazatec",
"vmq": "Soyaltepec Mixtec",
"vmr": "Marenje",
"vmu": "Muluridyi",
"vmv": "Valley Maidu",
"vmw": "Makhuwa",
"vmx": "Tamazola Mixtec",
"vmy": "Ayautla Mazatec",
"vmz": "Mazatlán Mazatec",
"vnk": "Lovono",
"vnm": "Neve'ei",
"vnp": "Vunapu",
"vo": "Volapük",
"vor": "Voro",
"vot": "Votic",
"vra": "Vera'a",
"vro": "Võro",
"vrs": "Varisi",
"vrt": "Burmbar",
"vsi": "Moldova Sign Language",
"vsl": "Venezuelan Sign Language",
"vsv": "Valencian Sign Language",
"vto": "Vitou",
"vum": "Vumbu",
"vun": "Vunjo",
"vut": "Vute",
"vwa": "Awa (China)",
"wa": "Walloon",
"waa": "Walla Walla",
"wab": "Wab",
"wac": "Wasco-Wishram",
"wad": "Wandamen",
"waf": "Wakoná",
"wag": "Wa'ema",
"wah": "Watubela",
"waj": "Waffa",
"wal": "Wolaytta",
"wam": "Massachusett",
"wan": "Wan",
"wao": "Wappo",
"wap": "Wapishana",
"waq": "Wageman",
"war": "Waray-Waray",
"was": "Washo",
"wat": "Kaninuwa",
"wau": "Wauja",
"wav": "Waka",
"waw": "Waiwai",
"wax": "Watam",
"way": "Wayana",
"waz": "Wampur",
"wba": "Warao",
"wbb": "Wabo",
"wbe": "Waritai",
"wbf": "Wara",
"wbh": "Wanda",
"wbi": "Wanji",
"wbj": "Alagwa",
"wbk": "Waigali",
"wbl": "Wakhi",
"wbm": "Wa",
"wbp": "Warlpiri",
"wbq": "Waddar",
"wbr": "Wagdi",
"wbt": "Wanman",
"wbv": "Wajarri",
"wbw": "Woi",
"wca": "Yanomam",
"wci": "Waci Gbe",
"wdd": "Wandji",
"wdg": "Wadaginam",
"wdj": "Wadjiginy",
"wdt": "Wendat",
"wdu": "Wadjigu",
"wdy": "Wadjabangayi",
"wea": "Wewaw",
"wec": "Wè Western",
"wed": "Wedau",
"weh": "Weh",
"wei": "Kiunum",
"wem": "Weme Gbe",
"weo": "Wemale",
"wer": "Weri",
"wes": "Cameroon Pidgin",
"wet": "Perai",
"weu": "Welaung",
"wew": "Weyewa",
"wfg": "Yafi",
"wga": "Wagaya",
"wgb": "Wagawaga",
"wgg": "Wangganguru",
"wgi": "Wahgi",
"wgo": "Waigeo",
"wgu": "Wirangu",
"wgy": "Warrgamay",
"wha": "Manusela",
"whg": "North Wahgi",
"whk": "Wahau Kenyah",
"whu": "Wahau Kayan",
"wib": "Southern Toussian",
"wic": "Wichita",
"wie": "Wik-Epa",
"wif": "Wik-Keyangan",
"wig": "Wik-Ngathana",
"wih": "Wik-Me'anha",
"wii": "Minidien",
"wij": "Wik-Iiyanh",
"wik": "Wikalkan",
"wil": "Wilawila",
"wim": "Wik-Mungkan",
"win": "Winnebago",
"wir": "Wiraféd",
"wiu": "Wiru",
"wiv": "Muduapa",
"wiy": "Wiyot",
"wja": "Waja",
"wji": "Warji",
"wka": "Kw'adza",
"wkb": "Kumbaran",
"wkd": "Mo",
"wkl": "Kalanadi",
"wku": "Kunduvadi",
"wkw": "Wakawaka",
"wky": "Wangkayutyuru",
"wla": "Walio",
"wlc": "Mwali Comorian",
"wle": "Wolane",
"wlg": "Kunbarlang",
"wli": "Waioli",
"wlk": "Wailaki",
"wll": "Wali (Sudan)",
"wlm": "Middle Welsh",
"wlo": "Wolio",
"wlr": "Wailapa",
"wls": "Wallisian",
"wlu": "Wuliwuli",
"wlv": "Wichí Lhamtés Vejoz",
"wlw": "Walak",
"wlx": "Wali (Ghana)",
"wly": "Waling",
"wmb": "Wambaya",
"wmc": "Wamas",
"wmd": "Mamaindé",
"wme": "Wambule",
"wmh": "Waima'a",
"wmi": "Wamin",
"wmm": "Maiwa (Indonesia)",
"wmn": "Waamwang",
"wmo": "Wam",
"wms": "Wambon",
"wmt": "Walmajarri",
"wmw": "Mwani",
"wmx": "Womo",
"wnb": "Mokati",
"wnc": "Wantoat",
"wnd": "Wandarang",
"wne": "Waneci",
"wng": "Wanggom",
"wni": "Ndzwani Comorian",
"wnk": "Wanukaka",
"wnm": "Wanggamala",
"wno": "Wano",
"wnp": "Wanap",
"wnu": "Usan",
"wnw": "Wintu",
"wny": "Wanyi",
"wo": "Wolof",
"woa": "Tyaraity",
"wob": "Wobé",
"woc": "Wogeo",
"wod": "Wolani",
"woe": "Woleaian",
"wog": "Wogamusin",
"woi": "Kamang",
"wok": "Longto",
"wom": "Perema",
"won": "Wongo",
"woo": "Manombai",
"wor": "Woria",
"wos": "Hanga Hundi",
"wow": "Wawonii",
"woy": "Weyto",
"wpc": "Wirö",
"wra": "Warapu",
"wrb": "Warluwara",
"wrg": "Warungu",
"wrh": "Wiradjuri",
"wri": "Wariyangga",
"wrk": "Garawa",
"wrl": "Warlmanpa",
"wrm": "Warumungu",
"wrn": "Warnang",
"wro": "Worora",
"wrp": "Waropen",
"wrr": "Wardaman",
"wrs": "Waris",
"wru": "Waru",
"wrv": "Waruna",
"wrw": "Gugu Warra",
"wrx": "Wae Rana",
"wrz": "Warray",
"wsa": "Warembori",
"wsi": "Wusi",
"wsk": "Waskia",
"wsr": "Owenia",
"wsu": "Wasu",
"wsv": "Wotapuri-Katarqalai",
"wtf": "Watiwa",
"wth": "Wathaurong",
"wti": "Berta",
"wtk": "Watakataui",
"wtm": "Mewati",
"wtw": "Wotu",
"wua": "Wikngenchera",
"wub": "Wunambal",
"wud": "Wudu",
"wuh": "Wutunhua",
"wul": "Silimo",
"wum": "Wumbvu",
"wun": "Bungu",
"wur": "Wurrugu",
"wut": "Wutung",
"wuu": "Wu",
"wuv": "Wuvulu-Aua",
"wux": "Wulna",
"wuy": "Wauyai",
"wwa": "Waama",
"wwo": "Dorig",
"wwr": "Warrwa",
"www": "Wawa",
"wxa": "Waxiang",
"wxw": "Wardandi",
"wya": "Wyandot",
"wyb": "Ngiyambaa",
"wyi": "Woiwurrung",
"wym": "Vilamovian",
"wyr": "Wayoró",
"wyy": "Western Fijian",
"xaa": "Andalusian Arabic",
"xab": "Sambe",
"xac": "Kachari",
"xad": "Adai",
"xae": "Aequian",
"xag": "Aghwan",
"xai": "Kaimbé",
"xaj": "Ararandewára",
"xak": "Maku",
"xal": "Kalmyk",
"xam": "ǀXam",
"xan": "Xamtanga",
"xao": "Khao",
"xap": "Apalachee",
"xar": "Karami",
"xas": "Kamassian",
"xat": "Katawixi",
"xau": "Kauwera",
"xav": "Xavante",
"xaw": "Kawaiisu",
"xay": "Kayan Mahakam",
"xbb": "Lower Burdekin",
"xbc": "Bactrian",
"xbd": "Bindal",
"xbe": "Bigambal",
"xbg": "Bunganditj",
"xbi": "Kombio",
"xbj": "Birrpayi",
"xbm": "Middle Breton",
"xbn": "Kenaboi",
"xbo": "Bulgar",
"xbp": "Bibbulman",
"xbr": "Kambera",
"xbw": "Kambiwá",
"xby": "Butchulla",
"xcb": "Cumbric",
"xcc": "Camunic",
"xce": "Celtiberian",
"xch": "Chemakum",
"xcl": "Old Armenian",
"xcm": "Comecrudo",
"xcn": "Cotoname",
"xco": "Khwarezmian",
"xcr": "Carian",
"xct": "Classical Tibetan",
"xcu": "Curonian",
"xcv": "Chuvan",
"xcw": "Coahuilteco",
"xcy": "Cayuse",
"xda": "Darkinjung",
"xdc": "Dacian",
"xdk": "Dharug",
"xdm": "Edomite",
"xdq": "Kaitag",
"xdy": "Malayic Dayak",
"xeb": "Eblaite",
"xed": "Hdi",
"xeg": "ǁXegwi",
"xel": "Kelo",
"xem": "Kembayan",
"xep": "Epi-Olmec",
"xer": "Xerénte",
"xes": "Koromu",
"xet": "Xetá",
"xeu": "Keoru-Ahia",
"xfa": "Faliscan",
"xga": "Galatian",
"xgb": "Gbin",
"xgd": "Gudang",
"xgf": "Gabrielino-Fernandeño",
"xgg": "Goreng",
"xgi": "Garingbal",
"xgl": "Galindian",
"xgm": "Darumbal",
"xgn-pro": "Proto-Mongolic",
"xgr": "Garza",
"xgu": "Unggumi",
"xgw": "Guwa",
"xh": "Xhosa",
"xha": "Harami",
"xhc": "Hunnic",
"xhd": "Hadrami",
"xhe": "Khetrani",
"xhm": "Middle Khmer",
"xhr": "Hernican",
"xht": "Hattic",
"xhu": "Hurrian",
"xhv": "Khua",
"xib": "Iberian",
"xii": "Xiri",
"xil": "Illyrian",
"xin": "Xinca",
"xir": "Xiriâna",
"xis": "Kisan",
"xiv": "Harappan",
"xiy": "Xipaya",
"xjb": "Minjungbal",
"xka": "Kalkoti",
"xkb": "Manigri-Kambolé Ede Nago",
"xkc": "Khoini",
"xkd": "Mendalam Kayan",
"xke": "Kereho",
"xkf": "Khengkha",
"xkg": "Kagoro",
"xki": "Kenyan Sign Language",
"xkj": "Kajali",
"xkk": "Kaco'",
"xkl": "Bakung",
"xkn": "Kayan River Kayan",
"xko": "Kiorr",
"xkp": "Kabatei",
"xkq": "Koroni",
"xkr": "Xakriabá",
"xks": "Kumbewaha",
"xkt": "Kantosi",
"xku": "Kaamba",
"xkv": "Kgalagadi",
"xkw": "Kembra",
"xkx": "Karore",
"xky": "Uma' Lasan",
"xkz": "Kurtöp",
"xla": "Kamula",
"xlb": "Loup B",
"xlc": "Lycian",
"xld": "Lydian",
"xle": "Lemnian",
"xlg": "Ancient Ligurian",
"xli": "Liburnian",
"xln": "Alanic",
"xlo": "Loup A",
"xlp": "Lepontic",
"xls": "Lusitanian",
"xlu": "Luwian",
"xly": "Elymian",
"xmb": "Mbonga",
"xmc": "Makhuwa-Marrevone",
"xmd": "Mbudum",
"xme-ker": "Kermanic",
"xme-kls": "Kalasuri",
"xme-klt": "Kilit",
"xme-mid": "Middle Median",
"xme-old": "Old Median",
"xme-ott": "Old Tati",
"xme-taf": "Tafreshi",
"xme-ttc-pro": "Proto-Tatic",
"xmf": "Mingrelian",
"xmg": "Mengaka",
"xmh": "Kugu-Muminh",
"xmj": "Majera",
"xmk": "Ancient Macedonian",
"xml": "Malaysian Sign Language",
"xmm": "Manado Malay",
"xmo": "Morerebi",
"xmp": "Kuku-Mu'inh",
"xmq": "Kuku-Mangk",
"xmr": "Meroitic",
"xms": "Moroccan Sign Language",
"xmt": "Matbat",
"xmu": "Kamu",
"xmx": "Maden",
"xmy": "Mayaguduna",
"xmz": "Mori Bawah",
"xna": "Ancient North Arabian",
"xnb": "Kanakanabu",
"xnd-pro": "Proto-Na-Dene",
"xng": "Middle Mongol",
"xnh": "Kuanhua",
"xni": "Ngarigu",
"xnk": "Nganakarti",
"xnr": "Kangri",
"xns": "Kanashi",
"xnt": "Narragansett",
"xnu": "Nukunul",
"xny": "Nyiyaparli",
"xoc": "O'chi'chi'",
"xod": "Kokoda",
"xog": "Soga",
"xoi": "Kominimung",
"xok": "Xokleng",
"xom": "Komo",
"xon": "Konkomba",
"xoo": "Xukurú",
"xop": "Kopar",
"xor": "Korubo",
"xow": "Kowaki",
"xpa": "Pirriya",
"xpb": "Pyemmairre",
"xpc": "Pecheneg",
"xpd": "Paredarerme",
"xpe": "Liberia Kpelle",
"xpf": "Southeast Tasmanian",
"xpg": "Phrygian",
"xph": "Tyerrernotepanner",
"xpi": "Pictish",
"xpj": "Mpalitjanh",
"xpk": "Kulina",
"xpl": "Port Sorell",
"xpm": "Pumpokol",
"xpn": "Kapinawá",
"xpo": "Pochutec",
"xpp": "Puyo-Paekche",
"xpq": "Mohegan-Pequot",
"xpr": "Parthian",
"xps": "Pisidian",
"xpu": "Punic",
"xpv": "Tommeginne",
"xpw": "Peerapper",
"xpx": "Toogee",
"xpy": "Buyeo",
"xpz": "Bruny Island",
"xqa": "Karakhanid",
"xqt": "Qatabanian",
"xra": "Krahô",
"xrb": "Eastern Karaboro",
"xrd": "Gundungurra",
"xre": "Kreye",
"xrg": "Minang",
"xri": "Krikati-Timbira",
"xrm": "Armazic",
"xrn": "Arin",
"xrq": "Karranga",
"xrr": "Raetic",
"xrt": "Aranama-Tamique",
"xru": "Marriammu",
"xrw": "Karawa",
"xsa": "Sabaean",
"xsb": "Sambali",
"xsc-pro": "Proto-Scythian",
"xsc-sak-pro": "Proto-Saka",
"xsc-sar-pro": "Proto-Sarmatian",
"xsc-skw-pro": "Proto-Saka-Wakhi",
"xsd": "Sidetic",
"xse": "Sempan",
"xsh": "Shamang",
"xsi": "Sio",
"xsj": "Subi",
"xsl": "South Slavey",
"xsm": "Kasem",
"xsn": "Sanga (Nigeria)",
"xso": "Solano",
"xsp": "Silopi",
"xsq": "Makhuwa-Saka",
"xsr": "Sherpa",
"xss": "Assan",
"xsu": "Sanumá",
"xsv": "Sudovian",
"xsy": "Saisiyat",
"xta": "Alcozauca Mixtec",
"xtb": "Chazumba Mixtec",
"xtc": "Kadugli",
"xtd": "Diuxi-Tilantongo Mixtec",
"xte": "Ketengban",
"xth": "Yitha Yitha",
"xti": "Sinicahua Mixtec",
"xtj": "San Juan Teita Mixtec",
"xtl": "Tijaltepec Mixtec",
"xtm": "Magdalena Peñasco Mixtec",
"xtn": "Northern Tlaxiaco Mixtec",
"xto": "Tocharian A",
"xtp": "San Miguel Piedras Mixtec",
"xtq": "Tumshuqese",
"xtr": "Early Tripuri",
"xts": "Sindihui Mixtec",
"xtt": "Tacahua Mixtec",
"xtu": "Cuyamecalco Mixtec",
"xtv": "Thawa",
"xtw": "Tawandê",
"xty": "Yoloxochitl Mixtec",
"xua": "Alu Kurumba",
"xub": "Betta Kurumba",
"xud": "Umiida",
"xug": "Kunigami",
"xuj": "Jennu Kurumba",
"xul": "Ngunawal",
"xum": "Umbrian",
"xun": "Unggaranggu",
"xuo": "Kuo",
"xup": "Upper Umpqua",
"xur": "Urartian",
"xut": "Kuthant",
"xuu": "Khwe",
"xve": "Venetic",
"xvn": "Vandalic",
"xvo": "Volscian",
"xvs": "Vestinian",
"xwa": "Kwaza",
"xwc": "Woccon",
"xwd": "Wadi Wadi",
"xwe": "Xwela Gbe",
"xwg": "Kwegu",
"xwj": "Wajuk",
"xwk": "Wangkumara",
"xwl": "Western Xwla Gbe",
"xwo": "Written Oirat",
"xwr": "Kwerba Mamberamo",
"xww": "Wemba-Wemba",
"xxb": "Boro",
"xxk": "Ke'o",
"xxm": "Minkin",
"xxr": "Koropó",
"xxt": "Tambora",
"xya": "Yaygir",
"xyb": "Yandjibara",
"xyl": "Yalakalore",
"xyt": "Mayi-Thakurti",
"xyy": "Yorta Yorta",
"xzh": "Zhang-Zhung",
"xzm": "Semigallian",
"xzp": "Ancient Zapotec",
"yaa": "Yaminahua",
"yab": "Yuhup",
"yac": "Pass Valley Yali",
"yad": "Yagua",
"yae": "Pumé",
"yaf": "Yaka",
"yag": "Yámana",
"yah": "Yazghulami",
"yai": "Yaghnobi",
"yaj": "Banda-Yangere",
"yak": "Yakima",
"yal": "Yalunka",
"yam": "Yamba",
"yan": "Mayangna",
"yao": "Yao (Africa)",
"yap": "Yapese",
"yaq": "Yaqui",
"yar": "Yabarana",
"yas": "Gunu",
"yat": "Yambeta",
"yau": "Yuwana",
"yav": "Yangben",
"yaw": "Yawalapití",
"yay": "Agwagwune",
"yaz": "Lokaa",
"yba": "Yala",
"ybb": "Yemba",
"ybe": "Western Yugur",
"ybh": "Yakkha",
"ybi": "Yamphu",
"ybj": "Hasha",
"ybk": "Bokha",
"ybl": "Yukuben",
"ybm": "Yaben",
"ybn": "Yabaâna",
"ybo": "Yabong",
"ybx": "Yawiyo",
"yby": "Yaweyuha",
"ych": "Chesu",
"ycl": "Lolopo",
"ycn": "Yucuna",
"ycp": "Chepya",
"ycr": "Yilan Creole",
"yda": "Yanda",
"yde": "Yangum Dey",
"ydg": "Yidgha",
"ydk": "Yoidik",
"yea": "Ravula",
"yec": "Yenish",
"yee": "Yimas",
"yei": "Yeni",
"yej": "Yevanic",
"yen": "Yendang",
"yer": "Tarok",
"yes": "Yeskwa",
"yet": "Yetfa",
"yeu": "Yerukula",
"yev": "Yeri",
"yey": "Yeyi",
"ygi": "Yiningayi",
"ygl": "Yangum Gel",
"ygm": "Yagomi",
"ygp": "Gepo",
"ygr": "Yagaria",
"ygs": "Yolngu Sign Language",
"ygu": "Yugul",
"ygw": "Yagwoia",
"yha": "Baha",
"yhl": "Hlepho Phowa",
"yi": "Yiddish",
"yia": "Yinggarda",
"yif": "Ache",
"yig": "Wusa",
"yii": "Yidiny",
"yij": "Yindjibarndi",
"yik": "Dongshanba Lalo",
"yil": "Yindjilandji",
"yim": "Yimchungru Naga",
"yin": "Yinchia",
"yip": "Pholo",
"yiq": "Micha",
"yir": "North Awyu",
"yis": "Yis",
"yit": "Eastern Lalu",
"yiu": "Lope",
"yiv": "Northern Nisu",
"yix": "Axi",
"yiy": "Yir-Yoront",
"yiz": "Azhe",
"yka": "Yakan",
"ykg": "Northern Yukaghir",
"ykh": "Khamnigan Mongol",
"yki": "Yoke",
"ykk": "Yakaikeke",
"ykl": "Khlula",
"ykm": "Kap",
"ykn": "Kua-nsi",
"yko": "Yasa",
"ykr": "Yekora",
"ykt": "Kathu",
"yku": "Kuamasi",
"yky": "Yakoma",
"yla": "Ulwa (New Guinea)",
"ylb": "Yaleba",
"yle": "Yele",
"ylg": "Yelogu",
"yli": "Angguruk Yali",
"yll": "Yil",
"ylm": "Limi",
"yln": "Langnian Buyang",
"ylo": "Naruo",
"ylr": "Yalarnnga",
"ylu": "Aribwaung",
"yly": "Nyelâyu",
"ymb": "Yambes",
"ymc": "Southern Muji",
"ymd": "Muda",
"yme": "Yameo",
"ymg": "Yamongeri",
"ymh": "Mili",
"ymi": "Moji",
"ymk": "Makwe",
"yml": "Iamalele",
"ymm": "Maay",
"ymn": "Sunum",
"ymo": "Yangum Mon",
"ymp": "Yamap",
"ymq": "Qila Muji",
"ymr": "Malasar",
"yms": "Mysian",
"ymx": "Northern Muji",
"ymz": "Muzi",
"yna": "Aluo",
"ynb": "Yamben",
"ynd": "Yandruwandha",
"yne": "Lang'e",
"yng": "Yango",
"ynk": "Naukanski",
"ynl": "Yangulam",
"ynn": "Yana",
"yno": "Yong",
"yns": "Yansi",
"ynu": "Yahuna",
"yo": "Yoruba",
"yob": "Yoba",
"yog": "Yogad",
"yoi": "Yonaguni",
"yok-bvy": "Buena Vista Yokuts",
"yok-dly": "Delta Yokuts",
"yok-gsy": "Gashowu Yokuts",
"yok-kry": "Kings River Yokuts",
"yok-nvy": "Northern Valley Yokuts",
"yok-ply": "Palewyami Yokuts",
"yok-svy": "Southern Valley Yokuts",
"yok-tky": "Tule-Kaweah Yokuts",
"yol": "Yola",
"yom": "Yombe",
"yon": "Yongkom",
"yox": "Yoron",
"yoy": "Yoy",
"ypa": "Phala",
"ypb": "Labo Phowa",
"ypg": "Phola",
"yph": "Phupha",
"ypk-pro": "Proto-Yupik",
"ypm": "Phuma",
"ypn": "Ani Phowa",
"ypo": "Alo Phola",
"ypp": "Phupa",
"ypz": "Phuza",
"yra": "Yerakai",
"yrb": "Yareba",
"yre": "Yaouré",
"yri": "Yarí",
"yrk-for": "Forest Nenets",
"yrk-tun": "Tundra Nenets",
"yrl": "Nheengatu",
"yrn": "Yerong",
"yro": "Ỹaroamë",
"yrw": "Yarawata",
"yry": "Yarluyandi",
"ysc": "Jassic",
"ysd": "Samatao",
"ysg": "Sonaga",
"ysl": "Yugoslavian Sign Language",
"ysn": "Sani",
"yso": "Nisi",
"ysp": "Southern Lolopo",
"ysr": "Sirenik",
"yss": "Yessan-Mayo",
"ysy": "Sanie",
"yta": "Talu",
"ytl": "Toloza",
"ytp": "Thopho",
"ytw": "Yout Wam",
"yty": "Yatay",
"yua": "Yucatec Maya",
"yub": "Yugambal",
"yuc": "Yuchi",
"yue": "Cantonese",
"yuf": "Havasupai-Walapai-Yavapai",
"yug": "Yug",
"yui": "Yurutí",
"yuj": "Karkar-Yuri",
"yuk": "Yuki",
"yul": "Yulu",
"yum": "Yuma",
"yun": "Bena",
"yup": "Yukpa",
"yuq": "Yuqui",
"yur": "Yurok",
"yut": "Yopno",
"yuw": "Yau (Finisterre)",
"yux": "Southern Yukaghir",
"yuy": "East Yugur",
"yuz": "Yuracare",
"yva": "Yawa",
"yvt": "Yavitero",
"ywa": "Kalou",
"ywg": "Yinhawangka",
"ywl": "Western Lalu",
"ywn": "Yawanawa",
"ywq": "Nasu",
"ywr": "Yawuru",
"ywt": "Xishanba Lalo",
"ywu": "Wumeng",
"yww": "Yawarawarga",
"yxa": "Mayawali",
"yxg": "Yagara",
"yxl": "Yarli",
"yxm": "Yinwum",
"yxu": "Yuyu",
"yxy": "Yabula Yabula",
"yyu": "Yau (Torricelli)",
"yyz": "Ayizi",
"yzg": "E'ma Buyang",
"yzk": "Zokhuo",
"za": "Zhuang",
"zaa": "Sierra de Juárez Zapotec",
"zab": "San Juan Guelavía Zapotec",
"zac": "Ocotlán Zapotec",
"zad": "Cajonos Zapotec",
"zae": "Yareni Zapotec",
"zaf": "Ayoquesco Zapotec",
"zag": "Zaghawa",
"zah": "Zangwal",
"zai": "Isthmus Zapotec",
"zaj": "Zaramo",
"zak": "Zanaki",
"zal": "Zauzou",
"zam": "Central Mahuatlán Zapotec",
"zao": "Ozolotepec Zapotec",
"zap": "Zapotec",
"zaq": "Aloápam Zapotec",
"zar": "Rincón Zapotec",
"zas": "Santo Domingo Albarradas Zapotec",
"zat": "Tabaa Zapotec",
"zau": "Zangskari",
"zav": "Yatzachi Zapotec",
"zaw": "Mitla Zapotec",
"zax": "Xadani Zapotec",
"zay": "Zayse-Zergulla",
"zaz": "Zari",
"zbt": "Batui",
"zca": "Coatecas Altas Zapotec",
"zdj": "Ngazidja Comorian",
"zea": "Zealandic",
"zeg": "Zenag",
"zen": "Zenaga",
"zga": "Kinga",
"zgh": "Moroccan Amazigh",
"zgr": "Magori",
"zh": "Chinese",
"zhb": "Zhaba",
"zhi": "Zhire",
"zhn": "Nong Zhuang",
"zhw": "Zhoa",
"zhx-min-pro": "Proto-Min",
"zhx-sht": "Shaozhou Tuhua",
"zhx-sic": "Sichuanese",
"zhx-tai": "Taishanese",
"zia": "Zia",
"zib": "Zimbabwe Sign Language",
"zik": "Zimakani",
"zil": "Zialo",
"zim": "Mesme",
"zin": "Zinza",
"zir": "Ziriya",
"ziw": "Zigula",
"ziz": "Zizilivakan",
"zka": "Kaimbulawa",
"zkb": "Koibal",
"zkd": "Kadu (Myanmar)",
"zkg": "Goguryeo",
"zkh": "Khorezmian Turkic",
"zkk": "Karankawa",
"zko": "Kott",
"zkp": "São Paulo Kaingáng",
"zkr": "Zakhring",
"zkt": "Khitan",
"zku": "Kaurna",
"zkv": "Krevinian",
"zkz": "Khazar",
"zle-ono": "Old Novgorodian",
"zle-ort": "Old Ruthenian",
"zls-chs": "Church Slavonic",
"zlw-ocs": "Old Czech",
"zlw-opl": "Old Polish",
"zlw-osk": "Old Slovak",
"zlw-slv": "Slovincian",
"zma": "Manda (Australia)",
"zmb": "Zimba",
"zmc": "Margany",
"zmd": "Maridan",
"zme": "Mangerr",
"zmf": "Mfinu",
"zmg": "Marti Ke",
"zmh": "Makolkol",
"zmi": "Negeri Sembilan Malay",
"zmj": "Maridjabin",
"zmk": "Mandandanyi",
"zml": "Madngele",
"zmm": "Marimanindji",
"zmn": "Mbangwe",
"zmo": "Molo",
"zmp": "Mbuun",
"zmq": "Mituku",
"zmr": "Maranungku",
"zms": "Mbesa",
"zmt": "Maringarr",
"zmu": "Muruwari",
"zmv": "Mbariman-Gudhinma",
"zmw": "Mbo (Congo)",
"zmx": "Bomitaba",
"zmy": "Mariyedi",
"zmz": "Mbandja",
"zna": "Zan Gula",
"zne": "Zande",
"zng": "Mang",
"znk": "Manangkari",
"zns": "Mangas",
"zoc": "Copainalá Zoque",
"zoh": "Chimalapa Zoque",
"zom": "Zou",
"zoo": "Asunción Mixtepec Zapotec",
"zoq": "Tabasco Zoque",
"zor": "Rayón Zoque",
"zos": "Francisco León Zoque",
"zpa": "Lachiguiri Zapotec",
"zpb": "Yautepec Zapotec",
"zpc": "Choapan Zapotec",
"zpd": "Southeastern Ixtlán Zapotec",
"zpe": "Petapa Zapotec",
"zpf": "San Pedro Quiatoni Zapotec",
"zpg": "Guevea de Humboldt Zapotec",
"zph": "Totomachapan Zapotec",
"zpi": "Santa María Quiegolani Zapotec",
"zpj": "Quiavicuzas Zapotec",
"zpk": "Tlacolulita Zapotec",
"zpl": "Lachixío Zapotec",
"zpm": "Mixtepec Zapotec",
"zpn": "Santa Inés Yatzechi Zapotec",
"zpo": "Amatlán Zapotec",
"zpp": "El Alto Zapotec",
"zpq": "Zoogocho Zapotec",
"zpr": "Santiago Xanica Zapotec",
"zps": "Coatlán Zapotec",
"zpt": "San Vicente Coatlán Zapotec",
"zpu": "Yalálag Zapotec",
"zpv": "Chichicapan Zapotec",
"zpw": "Zaniza Zapotec",
"zpx": "San Baltazar Loxicha Zapotec",
"zpy": "Mazaltepec Zapotec",
"zpz": "Texmelucan Zapotec",
"zra": "Gaya",
"zrg": "Mirgan",
"zrn": "Zirenkel",
"zro": "Záparo",
"zrs": "Mairasi",
"zsa": "Sarasira",
"zsk": "Kaskean",
"zsl": "Zambian Sign Language",
"zsr": "Southern Rincon Zapotec",
"zsu": "Sukurum",
"zte": "Elotepec Zapotec",
"ztg": "Xanaguía Zapotec",
"ztl": "Lapaguía-Guivini Zapotec",
"ztm": "San Agustín Mixtepec Zapotec",
"ztn": "Santa Catarina Albarradas Zapotec",
"ztp": "Loxicha Zapotec",
"ztq": "Quioquitani-Quierí Zapotec",
"zts": "Tilquiapan Zapotec",
"ztt": "Tejalapan Zapotec",
"ztu": "San Pablo Güilá Zapotec",
"ztx": "Zaachila Zapotec",
"zty": "Yatee Zapotec",
"zu": "Zulu",
"zua": "Zeem",
"zuh": "Tokano",
"zum": "Kumzari",
"zun": "Zuni",
"zuy": "Zumaya",
"zwa": "Zay",
"zyp": "Zyphe",
"zza": "Zazaki",
"zzj": "Zuojiang Zhuang"
}
afzxtv8rek0m3v77my6jv2kbd3nmvsn
မဳဒဳယာဝဳကဳ:Gadget-HotCat.js
8
295699
396517
2026-06-07T15:33:48Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "// {{documentation}} // See also: MediaWiki:Gadget-HotCat.js/local defaults //window.hotcat_translations_from_commons = true; // Make HotCat load its interface from the Commons (the default setting so no need to set it here) /** HotCat V2.43 en.wiktionary fork (with better support for en.wiktionary templates) upstream: https://commons.wikimedia.org/w/index.php?title=MediaWiki:Gadget-HotCat.js&oldid=818790002 The ori..."
396517
javascript
text/javascript
// {{documentation}}
// See also: MediaWiki:Gadget-HotCat.js/local defaults
//window.hotcat_translations_from_commons = true; // Make HotCat load its interface from the Commons (the default setting so no need to set it here)
/**
HotCat V2.43
en.wiktionary fork (with better support for en.wiktionary templates)
upstream: https://commons.wikimedia.org/w/index.php?title=MediaWiki:Gadget-HotCat.js&oldid=818790002
The original script is hosted at [https://commons.wikimedia.org/wiki/MediaWiki:Gadget-HotCat.js].
See it for more information.
License: Quadruple licensed, any of the following:
GFDL
GPL
LGPL
Creative Commons Attribution 3.0 (CC-BY-3.0)
Requires MediaWiki >= MW 1.27.
*/
// <nowiki>
/* eslint-disable vars-on-top, one-var, camelcase, no-alert, curly */
/* global jQuery, mediaWiki, UFUI, JSconfig, UploadForm */
/* jslint strict:false, nonew:false, bitwise:true */
( function ( $, mw ) {
// BEGINNING OF en.wiktionary EXTRA CODE
var MNW_WIKTIONARY_CONTENT_NAMESPACE = [0, 100, 118].indexOf(mw.config.get("wgNamespaceNumber")) >= 0;
var MNW_WIKTIONARY_TOPICS = ["C", "c", "topics", "top", "topic", "catlangcode"];
var MNW_WIKTIONARY_CATLANGNAME = ["cln", "catlangname", "Cln"];
var MNW_WIKTIONARY_CATEGORIZE = ["categorize", "cat"];
var MNW_WIKTIONARY_PREFER_TOPICS_FOR_NEW_ADDITIONS = true;
var MNW_WIKTIONARY_PREFER_CATLANGNAME_FOR_NEW_ADDITIONS = true;
function regexpEscape(string) {
return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}
function cleanUpText(wikitext) {
return wikitext
.replace( /<!--(\s|\S)*?-->/g, replaceByBlanks )
.replace( /<nowiki>(\s|\S)*?<\/nowiki>/g, replaceByBlanks );
}
function mnw_wiktionary_find_category_allMatches(regexp, wikitext, data_key, data) {
var copiedtext = cleanUpText(wikitext);
var result = [];
var curr_match = null;
while ((curr_match = regexp.exec(copiedtext)) !== null) {
var obj = { match: curr_match };
obj[data_key] = data;
result.push(obj);
}
result.re = regexp;
return result.length ? result : null;
}
function en_wiktionary_codeToCanonicalName(callback) {
$.get({
"url": mw.util.wikiScript('index'),
"data": {
"action": "raw",
"title": "Module:languages/code to canonical name.json"
},
"cache": true
}).then(function(r) {
var parsedData;
try {
parsedData = JSON.parse(r);
callback({ ok: true, data: parsedData });
} catch (e) {
console.error(e);
callback({ ok: false });
}
});
}
function en_wiktionary_populate_langdata(data) {
data.langnames = [];
data.name_to_code = {};
for (var key in data.code_to_name) {
if (data.code_to_name.hasOwnProperty(key)) {
var value = data.code_to_name[key];
data.langnames.push(value);
data.name_to_code[value] = key;
}
}
var escaped = data.langnames.map(regexpEscape);
// sort from longest to shortest to ensure greedy alternation
escaped.sort(function (a, b) { return b.length - a.length; });
data.catlangnameRegexp = "^(" + escaped.join("|") + ") (.+)";
}
var MNW_WIKTIONARY_LANGUAGE_DATA_STORAGE_KEY = "HotCat-mnw-wiktionary-language-data";
function mnw_wiktionary_cache_langdata(callback) {
if (!window.sessionStorage) {
callback();
return;
}
if (window.sessionStorage.hasOwnProperty(EN_WIKTIONARY_LANGUAGE_DATA_STORAGE_KEY)) {
try {
var timeNow = new Date().getTime();
var data = JSON.parse(window.sessionStorage.getItem(MNW_WIKTIONARY_LANGUAGE_DATA_STORAGE_KEY));
if (timeNow < data.expiry) {
MNW_WIKTIONARY_LANGDATA = data.data;
MNW_WIKTIONARY_LANGDATA_READY = true;
MNW_WIKTIONARY_LANGDATA.catlangnameRegexpCompiled = null;
callback();
return;
}
} catch (e) { }
}
mnw_wiktionary_codeToCanonicalName(function (r) {
if (r.ok) {
MNW_WIKTIONARY_LANGDATA = {};
MNW_WIKTIONARY_LANGDATA.code_to_name = r.data;
mnw_wiktionary_populate_langdata(EN_WIKTIONARY_LANGDATA);
}
MNW_WIKTIONARY_LANGDATA_READY = true;
if (window.sessionStorage)
window.sessionStorage.setItem(EN_WIKTIONARY_LANGUAGE_DATA_STORAGE_KEY, JSON.stringify({
expiry: new Date().getTime() + 30 * 60 * 1000, // expire in 30 minutes
data: MNW_WIKTIONARY_LANGDATA
}));
callback();
});
}
var MNW_WIKTIONARY_LANGDATA = null;
var MNW_WIKTIONARY_LANGDATA_READY = false;
var MNW_WIKTIONARY_LANGDATA_LOCK = false;
var MNW_WIKTIONARY_LANGDATA_QUEUE = [];
function mnw_wiktionary_get_langdata(callback) {
if (MNW_WIKTIONARY_LANGDATA_READY) {
callback();
} else {
if (!MNW_WIKTIONARY_LANGDATA_LOCK) {
MNW_WIKTIONARY_LANGDATA_LOCK = true;
mnw_wiktionary_cache_langdata(function() {
callback();
MNW_WIKTIONARY_LANGDATA_QUEUE.forEach(function(fn) { fn(); });
});
return;
}
MNW_WIKTIONARY_LANGDATA_QUEUE.push(callback);
}
}
function MNW_WIKTIONARY_TOPICS_START(lang, bar_required) {
return "\\{\\{\\s*(?:" + EN_WIKTIONARY_TOPICS.join("|") + ")\\s*\\|\\s*" + lang + "\\s*\\|" + (bar_required ? "" : "?");
}
function MNW_WIKTIONARY_CATLANGNAME_START(lang, bar_required) {
return "\\{\\{\\s*(?:" + EN_WIKTIONARY_CATLANGNAME.join("|") + ")\\s*\\|\\s*" + lang + "\\s*\\|" + (bar_required ? "" : "?");
}
function EN_WIKTIONARY_CATEGORIZE_START(lang, bar_required) {
return "\\{\\{\\s*(?:" + EN_WIKTIONARY_CATEGORIZE.join("|") + ")\\s*\\|\\s*" + (lang == true ? "(" : "") + "[a-z0-9-]+" + (lang == true ? ")" : "") + "\\s*\\|" + (bar_required ? "" : "?");
}
var MNW_WIKTIONARY_TEMPLATE_ANY_PARAMETERS = "[^}]*";
var MNW_WIKTIONARY_TEMPLATE_START_OF_PARAMETER = "?(?<=\\|)";
var MNW_WIKTIONARY_TEMPLATE_END_OF_PARAMETER = "(?=\\s*[\|\}])";
var MNW_WIKTIONARY_TEMPLATE_END = "\\}\\}";
var MNW_WIKTIONARY_TEMPLATE_END_WITH_WHITESPACE = "\\s*\\}\\}\n?";
function mnw_wiktionary_category_name_detect(category) {
var topicsMatch = category.match(/^([a-z-]+):(.+)$/);
if (topicsMatch) {
return { type: "topics", lang: topicsMatch[1], topic: topicsMatch[2] };
}
if (MNW_WIKTIONARY_LANGDATA) {
var data = MNW_WIKTIONARY_LANGDATA;
if (!data.catlangnameRegexpCompiled) {
data.catlangnameRegexpCompiled = new RegExp(data.catlangnameRegexp);
}
var catlangnameMatch = category.match(data.catlangnameRegexpCompiled);
if (catlangnameMatch && data.name_to_code[catlangnameMatch[1]]) {
return { type: "catlangname",
lang: data.name_to_code[catlangnameMatch[1]],
topic: catlangnameMatch[2] };
}
}
return null;
}
function mnw_wiktionary_find_category(wikitext, category, once) {
if (!EN_WIKTIONARY_CONTENT_NAMESPACE) return null;
var catname = mnw_wiktionary_category_name_detect(category);
var data, matched;
if (catname) {
if (catname.type == "topics") {
var topicTemplateRegex = new RegExp(
MNW_WIKTIONARY_TOPICS_START(catname.lang, true) +
MNW_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
MNW_WIKTIONARY_TEMPLATE_START_OF_PARAMETER +
regexpEscape(catname.topic) +
MNW_WIKTIONARY_TEMPLATE_END_OF_PARAMETER +
MNW_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
MNW_WIKTIONARY_TEMPLATE_END, "g");
data = { lang: catname.lang, name: catname.topic };
if (once) {
matched = topicTemplateRegex.exec(wikitext);
if (matched) {
matched.mnwWiktTopics = data;
}
return matched;
}
return mnw_wiktionary_find_category_allMatches(topicTemplateRegex, wikitext, "mnwWiktTopics", data);
} else if (catname.type == "catlangname") {
var clnTemplateRegex = new RegExp(
MNW_WIKTIONARY_CATLANGNAME_START(catname.lang, true) +
MNW_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
MNW_WIKTIONARY_TEMPLATE_START_OF_PARAMETER +
regexpEscape(catname.topic) +
MNW_WIKTIONARY_TEMPLATE_END_OF_PARAMETER +
MNW_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
MNW_WIKTIONARY_TEMPLATE_END, "g");
data = { lang: catname.lang, name: catname.topic };
if (once) {
matched = clnTemplateRegex.exec(wikitext);
if (matched) {
matched.enWiktCatlangname = data;
}
return matched;
}
return en_wiktionary_find_category_allMatches(clnTemplateRegex, wikitext, "mnwWiktCatlangname", data);
}
}
var catTemplateRegex = new RegExp(EN_WIKTIONARY_CATEGORIZE_START(true, true) +
MNW_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
MNW_WIKTIONARY_TEMPLATE_START_OF_PARAMETER +
regexpEscape(category) +
MNW_WIKTIONARY_TEMPLATE_END_OF_PARAMETER +
MNW_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
MNW_WIKTIONARY_TEMPLATE_END, "g");
var catTemplateMatches = wikitext.match(catTemplateRegex);
if (catTemplateMatches) {
data = { lang: catTemplateMatches[1], name: category };
if (once) {
if (catTemplateMatches) {
catTemplateMatches.enWiktCategorize = data;
}
return catTemplateMatches;
}
catTemplateRegex.lastIndex = 0;
return mnw_wiktionary_find_category_allMatches(catTemplateRegex, wikitext, "mnwWiktCategorize", data);
}
return null;
}
function mnw_wiktionary_is_special_match(match) {
return !!(match.enWiktTopics || match.enWiktCatlangname || match.mnwWiktCategorize);
}
function en_wiktionary_three_to_two_split(wikitext, match) {
var before, after, data, removeTopic;
if (match.enWiktTopics) { // {{topics}}?
data = match.enWiktTopics;
before = wikitext.substring(0, match.match.index);
after = wikitext.substring(match.match.index);
var topicTemplateRegex = new RegExp(
MNW_WIKTIONARY_TOPICS_START(data.lang, true) +
MNW_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
MNW_WIKTIONARY_TEMPLATE_START_OF_PARAMETER +
regexpEscape(data.name) +
MNW_WIKTIONARY_TEMPLATE_END_OF_PARAMETER +
MNW_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
MNW_WIKTIONARY_TEMPLATE_END + "\\s*?\n?");
removeTopic = function(inner) {
// remove parameter from {{topics}}
var remaining = inner.replace(new RegExp("\\|" +
regexpEscape(data.name) +
MNW_WIKTIONARY_TEMPLATE_END_OF_PARAMETER), "");
// if it is now empty, remove it
remaining = remaining.replace(new RegExp(
MNW_WIKTIONARY_TOPICS_START(data.lang, false) +
MNW_WIKTIONARY_TEMPLATE_END_WITH_WHITESPACE), "");
return remaining;
};
return [before, after.replace(topicTemplateRegex, removeTopic)];
} else if (match.enWiktCatlangname) { // {{cln}}?
data = match.enWiktCatlangname;
before = wikitext.substring(0, match.match.index);
after = wikitext.substring(match.match.index);
var clnTemplateRegex = new RegExp(
MNW_WIKTIONARY_CATLANGNAME_START(data.lang, true) +
MNW_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
MNW_WIKTIONARY_TEMPLATE_START_OF_PARAMETER +
regexpEscape(data.name) +
MNW_WIKTIONARY_TEMPLATE_END_OF_PARAMETER +
MNW_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
MNW_WIKTIONARY_TEMPLATE_END + "\\s*?\n?");
removeTopic = function(inner) {
// remove parameter from {{cln}}
var remaining = inner.replace(new RegExp("\\|" +
regexpEscape(data.name) +
MNW_WIKTIONARY_TEMPLATE_END_OF_PARAMETER), "");
// if it is now empty, remove it
remaining = remaining.replace(new RegExp(
MNW_WIKTIONARY_CATLANGNAME_START(data.lang, false) +
MNW_WIKTIONARY_TEMPLATE_END_WITH_WHITESPACE), "");
return remaining;
};
return [before, after.replace(clnTemplateRegex, removeTopic)];
} else if (match.enWiktCategorize) { // {{cat}}?
data = match.enWiktCategorize;
before = wikitext.substring(0, match.match.index);
after = wikitext.substring(match.match.index);
var catTemplateRegex = new RegExp(
MNW_WIKTIONARY_CATEGORIZE_START(data.lang, true) +
MNW_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
MNW_WIKTIONARY_TEMPLATE_START_OF_PARAMETER +
regexpEscape(data.name) +
MNW_WIKTIONARY_TEMPLATE_END_OF_PARAMETER +
MNW_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
MNW_WIKTIONARY_TEMPLATE_END + "\\s*?\n?");
removeTopic = function(inner) {
// remove parameter from {{cln}}
var remaining = inner.replace(new RegExp("\\|" +
regexpEscape(data.name) +
EN_WIKTIONARY_TEMPLATE_END_OF_PARAMETER), "");
// if it is now empty, remove it
remaining = remaining.replace(new RegExp(
MNW_WIKTIONARY_CATEGORIZE_START(data.lang, false) +
MNW_WIKTIONARY_TEMPLATE_END_WITH_WHITESPACE), "");
return remaining;
};
return [before, after.replace(catTemplateRegex, removeTopic)];
}
before = wikitext.substring(0, match.match.index);
after = wikitext.substring(match.match.index + match.match[0].length);
return [before, after];
}
function mnw_wiktionary_remove_category(wikitext, match) {
var before_after = mnw_wiktionary_three_to_two_split(wikitext, match);
return before_after[0] + before_after[1];
}
function mnw_wiktionary_find_insertionpoint(wikitext, category) {
var catname = mnw_wiktionary_category_name_detect(category);
if (catname) {
if (catname.lang) {
var langname = catname.langname || (MNW_WIKTIONARY_LANGDATA ? MNW_WIKTIONARY_LANGDATA.code_to_name[catname.lang] : null);
if (langname) {
// try to put category under the language L2
var l2_heading_this = "^==\\s*" + regexpEscape(langname) + "\\s*==$";
var end_of_l2 = "\\n*(?:^----$)?\\n*^==(?!=)\\s*.*\\s*==$";
var clean_wikitext = cleanUpText(wikitext);
var finalRegexp = "(" + l2_heading_this + "[\\s\\S]+?)" + end_of_l2;
var match = new RegExp(finalRegexp, "m").exec(clean_wikitext);
if (match) {
var rawPoint = match.index + match[1].length;
var noPreLine = false;
if (wikitext.substring(rawPoint - 2, rawPoint) == "]]") {
return {
idx: rawPoint,
onCat: true
};
}
var cookedPoint = rawPoint;
if (wikitext.substring(cookedPoint - 3, cookedPoint) == "}}\n") {
// if the preceding line has a topics/cln template, add noPreLine
var i = cookedPoint - 2;
while (i >= 0 && wikitext[i] !== '\n') {
i--;
}
i += 2;
var previousLine = wikitext.substring(i, cookedPoint - 1);
if (previousLine.match(new RegExp("^\\{\\{(?:" + MNW_WIKTIONARY_TOPICS.join("|") + "|" + MNW_WIKTIONARY_CATLANGNAME.join("|") + ")\\|"))) {
noPreLine = true;
}
}
// hotfix: fix newline placement
if (!noPreLine && wikitext.charAt(cookedPoint - 1) != "\n" && wikitext.substring(cookedPoint, cookedPoint + 2) == "\n\n") {
++cookedPoint;
}
return {
idx: cookedPoint,
onCat: false,
noPreLine: noPreLine
};
} else {
var l2match = new RegExp(l2_heading_this, "m").exec(clean_wikitext);
if (l2match) {
// assuming there are no more L2s...
var l2_regex = new RegExp("^==(?!=)\\s*.*\\s*==$", "gm");
l2_regex.lastIndex = l2match.index + l2match[0].length;
var l2nextmatch = l2_regex.exec(clean_wikitext);
if (!l2nextmatch) {
// no more L2s
return {
idx: -1,
onCat: false
};
}
}
}
}
}
}
return null; // fallback
}
function en_wiktionary_add_category_hook(wikitext, category, cat_point) {
var catname = en_wiktionary_category_name_detect(category);
if (catname) {
var match, splitPoint;
if (catname.type == "topics") {
// try to find the first language template that matches and
// add the category there
var topicTemplateRegex = new RegExp("(?:^|(?<=\\n))(" +
EN_WIKTIONARY_TOPICS_START(catname.lang, true) +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
")" + EN_WIKTIONARY_TEMPLATE_END);
match = topicTemplateRegex.exec(cleanUpText(wikitext));
if (match) {
splitPoint = match.index + match[1].length;
return wikitext.substring(0, splitPoint) + "|" + catname.topic + wikitext.substring(splitPoint);
}
}
if (catname.type == "catlangname") {
// try to find the first language template that matches and
// add the category there
var clnTemplateRegex = new RegExp("(?:^|(?<=\\n))(" +
EN_WIKTIONARY_CATLANGNAME_START(catname.lang, true) +
EN_WIKTIONARY_TEMPLATE_ANY_PARAMETERS +
")" + EN_WIKTIONARY_TEMPLATE_END);
match = clnTemplateRegex.exec(cleanUpText(wikitext));
if (match) {
splitPoint = match.index + match[1].length;
return wikitext.substring(0, splitPoint) + "|" + catname.topic + wikitext.substring(splitPoint);
}
}
}
return null; // fallback
}
function en_wiktionary_add_category_string(category, onCat) {
var catname = en_wiktionary_category_name_detect(category);
if (catname) {
if (catname.type == "topics" && EN_WIKTIONARY_PREFER_TOPICS_FOR_NEW_ADDITIONS && !onCat) {
return "{{" + EN_WIKTIONARY_TOPICS[0] + "|" + catname.lang + "|" + catname.topic + "}}";
}
if (catname.type == "catlangname" && EN_WIKTIONARY_PREFER_CATLANGNAME_FOR_NEW_ADDITIONS && !onCat) {
return "{{" + EN_WIKTIONARY_CATLANGNAME[0] + "|" + catname.lang + "|" + catname.topic + "}}";
}
}
return null; // fallback
}
// END OF en.wiktionary EXTRA CODE
// Don't use mw.config.get() as that takes a copy of the config, and so doesn't
// account for values changing, e.g. wgCurRevisionId after a VE edit
var
conf = mw.config.values,
// when running on mobile domain - do not use wgServer.
wgServer = window.location.host.indexOf('.m.') > -1 ?
'//' + window.location.host : mw.config.get( 'wgServer' );
// Guard against double inclusions (in old IE/Opera element ids become window properties)
if ( ( window.HotCat && !window.HotCat.nodeName ) ||
conf.wgAction === 'edit' ) // Not on edit mode
return;
// Configuration stuff.
var HC = window.HotCat = {
// Localize these messages to the main language of your wiki.
messages: {
cat_removed: 'removed [[Category:$1]]',
template_removed: 'removed {{[[Category:$1]]}}',
cat_added: 'added [[Category:$1]]',
cat_keychange: 'new key for [[Category:$1]]: "$2"', // $2 is the new key
cat_notFound: 'Category "$1" not found',
cat_exists: 'Category "$1" already exists; not added.',
cat_resolved: ' (redirect [[Category:$1]] resolved)',
uncat_removed: 'removed {{uncategorized}}',
separator: '; ',
// Some text to prefix to the edit summary.
prefix: '',
// Some text to append to the edit summary. Named 'using' for historical reasons. If you prefer
// to have a marker at the front, use prefix and set this to the empty string.
using: ' using [[Help:Gadget-HotCat|HotCat]]',
// $1 is replaced by a number. If your language has several plural forms (c.f. [[:en:Dual (grammatical form)]]),
// you can set this to an array of strings suitable for passing to mw.language.configPlural().
// If that function doesn't exist, HotCat will simply fall back to using the last
// entry in the array.
multi_change: '$1 categories',
// Button text. Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
// see localization hook below.
commit: 'Save',
// Button text. Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
// see localization hook below.
ok: 'OK',
// Button text. Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
// see localization hook below.
cancel: 'Cancel',
// Localize to wgContentLanguage here; localize to wgUserLanguage in a subpage,
// see localization hook below.
multi_error: 'Could not retrieve the page text from the server. Therefore, your category changes ' +
'cannot be saved. We apologize for the inconvenience.',
// Defaults to '[[' + category_canonical + ':$1]]'. Can be overridden if in the short edit summaries
// not the standard category name should be used but, say, a shorter namespace alias. $1 is replaced
// by a category name.
short_catchange: null
},
// Plural of category_canonical.
categories: 'Categories',
// Any category in this category is deemed a disambiguation category; i.e., a category that should not contain
// any items, but that contains links to other categories where stuff should be categorized. If you don't have
// that concept on your wiki, set it to null. Use blanks, not underscores.
disambig_category: 'Disambiguation',
// Any category in this category is deemed a (soft) redirect to some other category defined by a link
// to another non-blacklisted category. If your wiki doesn't have soft category redirects, set this to null.
// If a soft-redirected category contains more than one link to another non-blacklisted category, it's considered
// a disambiguation category instead.
redir_category: 'Category redirects',
// The little modification links displayed after category names. U+2212 is a minus sign; U+2193 and U+2191 are
// downward and upward pointing arrows. Do not use ↓ and ↑ in the code!
links: {
change: '(±)',
remove: '(\u2212)',
add: '(+)',
restore: '(×)',
undo: '(×)',
down: '(\u2193)',
up: '(\u2191)'
},
changeTag: conf.wgUserName ? 'HotCat' : '', // if tag is missing, edit is rejected
// The tooltips for the above links
tooltips: {
change: 'Modify',
remove: 'Remove',
add: 'Add a new category',
restore: 'Undo changes',
undo: 'Undo changes',
down: 'Open for modifying and display subcategories',
up: 'Open for modifying and display parent categories'
},
// The HTML content of the "enter multi-mode" link at the front.
addmulti: '<span>+<sup>+</sup></span>',
// Tooltip for the "enter multi-mode" link
multi_tooltip: 'Modify several categories',
// Return true to disable HotCat.
disable: function () {
var ns = conf.wgNamespaceNumber;
var nsIds = conf.wgNamespaceIds;
return (
ns < 0 || // Special pages; Special:Upload is handled differently
ns === 10 || // Templates
ns === 828 || // Module (Lua)
ns === 8 || // MediaWiki
ns === 6 && !conf.wgArticleId || // Non-existing file pages
ns === 2 && /\.(js|css)$/.test( conf.wgTitle ) || // User scripts
nsIds &&
( ns === nsIds.creator ||
ns === nsIds.timedtext ||
ns === nsIds.institution ) );
},
// A regexp matching a templates used to mark uncategorized pages, if your wiki does have that.
// If not, set it to null.
uncat_regexp: /\{\{\s*[Uu]ncategorized\s*[^}]*\}\}\s*(<!--.*?-->\s*)?/g,
// The images used for the little indication icon. Should not need changing.
existsYes: '//upload.wikimedia.org/wikipedia/commons/thumb/b/be/P_yes.svg/20px-P_yes.svg.png',
existsNo: '//upload.wikimedia.org/wikipedia/commons/thumb/4/42/P_no.svg/20px-P_no.svg.png',
// a list of categories which can be removed by removing a template
// key: the category without namespace
// value: A regexp matching the template name, again without namespace
// If you don't have this at your wiki, or don't want this, set it to an empty object {}.
template_categories: {},
// Names for the search engines
engine_names: {
searchindex: 'Search index',
pagelist: 'Page list',
combined: 'Combined search',
subcat: 'Subcategories',
parentcat: 'Parent categories'
},
// Override the decision of whether HotCat should help users by automatically
// capitalising the title in the user input text if the wiki has case-sensitive page names.
// Basically, this will make an API query to check the MediaWiki configuration and HotCat then sets
// this to true for most wikis, and to false on Wiktionary.
//
// You can set this directly if there is a problem with it. For example, Georgian Wikipedia (kawiki),
// is known to have different capitalisation logic between MediaWiki PHP and JavaScript. As such, automatic
// case changes in JavaScript by HotCat would be wrong.
capitalizePageNames: null,
// If upload_disabled is true, HotCat will not be used on the Upload form.
upload_disabled: false,
// Single regular expression matching blacklisted categories that cannot be changed or
// added using HotCat. For instance /\bstubs?$/ (any category ending with the word "stub"
// or "stubs"), or /(\bstubs?$)|\bmaintenance\b/ (stub categories and any category with the
// word "maintenance" in its title.
blacklist: null,
// Stuff changeable by users:
// Background for changed categories in multi-edit mode. Default is a very light salmon pink.
bg_changed: '#FCA',
// If true, HotCat will never automatically submit changes. HotCat will only open an edit page with
// the changes; users must always save explicitly.
no_autocommit: false,
// If true, the "category deletion" link "(-)" will never save automatically but always show an
// edit page where the user has to save the edit manually. Is false by default because that's the
// traditional behavior. This setting overrides no_autocommit for "(-)" links.
del_needs_diff: false,
// Time, in milliseconds, that HotCat waits after a keystroke before making a request to the
// server to get suggestions.
suggest_delay: 100,
// Default width, in characters, of the text input field.
editbox_width: 40,
// One of the engine_names above, to be used as the default suggestion engine.
suggestions: 'combined',
// If true, always use the default engine, and never display a selector.
fixed_search: false,
// If false, do not display the "up" and "down" links
use_up_down: true,
// Default list size
listSize: 10,
// If true, single category changes are marked as minor edits. If false, they're not.
single_minor: true,
// If true, never add a page to the user's watchlist. If false, pages get added to the watchlist if
// the user has the "Add pages I edit to my watchlist" or the "Add pages I create to my watchlist"
// options in his or her preferences set.
dont_add_to_watchlist: false,
shortcuts: null,
addShortcuts: function ( map ) {
if ( !map ) return;
window.HotCat.shortcuts = window.HotCat.shortcuts || {};
for ( var k in map ) {
if ( !map.hasOwnProperty( k ) || typeof k !== 'string' ) continue;
var v = map[ k ];
if ( typeof v !== 'string' ) continue;
k = k.replace( /^\s+|\s+$/g, '' );
v = v.replace( /^\s+|\s+$/g, '' );
if ( !k.length || !v.length ) continue;
window.HotCat.shortcuts[ k ] = v;
}
}
};
// More backwards compatibility. We have a few places where we test for the browser: once for
// Safari < 3.0, and twice for WebKit (Chrome or Safari, any versions)
var ua = navigator.userAgent.toLowerCase();
var is_webkit = /applewebkit\/\d+/.test( ua ) && ua.indexOf( 'spoofer' ) < 0;
var cat_prefix = null;
var noSuggestions = false;
function LoadTrigger( needed ) {
// Define methods in a closure so that self reference is available,
// also allows method calls to be detached.
var self = this;
self.queue = [];
self.needed = needed;
self.register = function ( callback ) {
if ( self.needed <= 0 ) callback(); // Execute directly
else self.queue.push( callback );
};
self.loaded = function () {
self.needed--;
if ( self.needed === 0 ) {
// Run queued callbacks once
for ( var i = 0; i < self.queue.length; i++ ) self.queue[ i ]();
self.queue = [];
}
};
}
// Used to delay running the HotCat setup until /local_defaults and localizations have been loaded.
var loadTrigger = new LoadTrigger( 2 );
function load( uri, callback ) {
var s = document.createElement( 'script' );
s.src = uri;
var called = false;
s.onload = s.onerror = function () {
if ( !called && callback ) {
called = true;
callback();
}
if ( s.parentNode ) {
s.parentNode.removeChild( s );
}
};
document.head.appendChild( s );
}
function loadJS( page, callback ) {
load( wgServer + conf.wgScript + '?title=' + encodeURIComponent( page ) + '&action=raw&ctype=text/javascript', callback );
}
function loadURI( href, callback ) {
var url = href;
if ( url.substring( 0, 2 ) === '//' ) url = window.location.protocol + url; else if ( url.substring( 0, 1 ) === '/' ) url = wgServer + url;
load( url, callback );
}
// Load local configurations, overriding the pre-set default values in the HotCat object above. This is always loaded
// from the wiki where this script is executing, even if this script itself is hotlinked from Commons. This can
// be used to change the default settings, or to provide localized interface texts for edit summaries and so on.
loadJS( 'MediaWiki:Gadget-HotCat.js/local_defaults', loadTrigger.loaded );
// Load localized UI texts. These are the texts that HotCat displays on the page itself. Texts shown in edit summaries
// should be localized in /local_defaults above.
if ( conf.wgUserLanguage !== 'en' ) {
// Lupo: somebody thought it would be a good idea to add this. So the default is true, and you have to set it to false
// explicitly if you're not on Commons and don't want that.
if ( window.hotcat_translations_from_commons === undefined ) window.hotcat_translations_from_commons = true;
// Localization hook to localize HotCat messages, tooltips, and engine names for wgUserLanguage.
if ( window.hotcat_translations_from_commons && wgServer.indexOf( '//commons' ) < 0 ) {
loadURI( '//commons.wikimedia.org/w/index.php?title=' +
'MediaWiki:Gadget-HotCat.js/' + conf.wgUserLanguage +
'&action=raw&ctype=text/javascript', loadTrigger.loaded );
} else {
// Load translations locally
loadJS( 'MediaWiki:Gadget-HotCat.js/' + conf.wgUserLanguage, loadTrigger.loaded );
}
} else {
loadTrigger.loaded();
}
// No further changes should be necessary here.
// The following regular expression strings are used when searching for categories in wikitext.
var wikiTextBlank = '[\\t _\\xA0\\u1680\\u180E\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000]+';
var wikiTextBlankRE = new RegExp( wikiTextBlank, 'g' );
// Regexp for handling blanks inside a category title or namespace name.
// See http://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/includes/Title.php?revision=104051&view=markup#l2722
// See also http://www.fileformat.info/info/unicode/category/Zs/list.htm
// MediaWiki collapses several contiguous blanks inside a page title to one single blank. It also replace a
// number of special whitespace characters by simple blanks. And finally, blanks are treated as underscores.
// Therefore, when looking for page titles in wikitext, we must handle all these cases.
// Note: we _do_ include the horizontal tab in the above list, even though the MediaWiki software for some reason
// appears to not handle it. The zero-width space \u200B is _not_ handled as a space inside titles by MW.
var wikiTextBlankOrBidi = '[\\t _\\xA0\\u1680\\u180E\\u2000-\\u200B\\u200E\\u200F\\u2028-\\u202F\\u205F\\u3000]*';
// Whitespace regexp for handling whitespace between link components. Including the horizontal tab, but not \n\r\f\v:
// a link must be on one single line.
// MediaWiki also removes Unicode bidi override characters in page titles (and namespace names) completely.
// This is *not* handled, as it would require us to allow any of [\u200E\u200F\u202A-\u202E] between any two
// characters inside a category link. It _could_ be done though... We _do_ handle strange spaces, including the
// zero-width space \u200B, and bidi overrides between the components of a category link (adjacent to the colon,
// or adjacent to and inside of "[[" and "]]").
// First auto-localize the regexps for the category and the template namespaces.
var formattedNamespaces = conf.wgFormattedNamespaces;
var namespaceIds = conf.wgNamespaceIds;
function autoLocalize( namespaceNumber, fallback ) {
function createRegexpStr( name ) {
if ( !name || !name.length ) return '';
var regex_name = '';
for ( var i = 0; i < name.length; i++ ) {
var initial = name.charAt( i ),
ll = initial.toLowerCase(),
ul = initial.toUpperCase();
if ( ll === ul ) regex_name += initial; else regex_name += '[' + ll + ul + ']';
}
return regex_name
.replace( /([\\^$.?*+()])/g, '\\$1' )
.replace( wikiTextBlankRE, wikiTextBlank );
}
fallback = fallback.toLowerCase();
var canonical = formattedNamespaces[ String( namespaceNumber ) ].toLowerCase();
var regexp = createRegexpStr( canonical );
if ( fallback && canonical !== fallback ) regexp += '|' + createRegexpStr( fallback );
if ( namespaceIds ) {
for ( var cat_name in namespaceIds ) {
if (
typeof cat_name === 'string' &&
cat_name.toLowerCase() !== canonical &&
cat_name.toLowerCase() !== fallback &&
namespaceIds[ cat_name ] === namespaceNumber
) {
regexp += '|' + createRegexpStr( cat_name );
}
}
}
return regexp;
}
HC.category_canonical = formattedNamespaces[ '14' ];
HC.category_regexp = autoLocalize( 14, 'category' );
if ( formattedNamespaces[ '10' ] ) HC.template_regexp = autoLocalize( 10, 'template' );
// Utility functions. Yes, this duplicates some functionality that also exists in other places, but
// to keep this whole stuff in a single file not depending on any other on-wiki JavaScripts, we re-do
// these few operations here.
function make( arg, literal ) {
if ( !arg ) return null;
return literal ? document.createTextNode( arg ) : document.createElement( arg );
}
function param( name, uri ) {
uri = uri || document.location.href;
var re = new RegExp( '[&?]' + name + '=([^&#]*)' );
var m = re.exec( uri );
if ( m && m.length > 1 ) return decodeURIComponent( m[ 1 ] );
return null;
}
function title( href ) {
if ( !href ) return null;
var script = conf.wgScript + '?';
if ( href.indexOf( script ) === 0 || href.indexOf( wgServer + script ) === 0 || wgServer.substring( 0, 2 ) === '//' && href.indexOf( document.location.protocol + wgServer + script ) === 0 ) {
// href="/w/index.php?title=..."
return param( 'title', href );
} else {
// href="/wiki/..."
var prefix = conf.wgArticlePath.replace( '$1', '' );
if ( href.indexOf( prefix ) ) prefix = wgServer + prefix; // Fully expanded URL?
if ( href.indexOf( prefix ) && prefix.substring( 0, 2 ) === '//' ) prefix = document.location.protocol + prefix; // Protocol-relative wgServer?
if ( href.indexOf( prefix ) === 0 ) return decodeURIComponent( href.substring( prefix.length ) );
}
return null;
}
function hasClass( elem, name ) {
return ( ' ' + elem.className + ' ' ).indexOf( ' ' + name + ' ' ) >= 0;
}
function capitalize( str ) {
if ( !str || !str.length ) return str;
return str.substr( 0, 1 ).toUpperCase() + str.substr( 1 );
}
function wikiPagePath( pageName ) {
// Note: do not simply use encodeURI, it doesn't encode '&', which might break if wgArticlePath actually has the $1 in
// a query parameter.
return conf.wgArticlePath.replace( '$1', encodeURIComponent( pageName ).replace( /%3A/g, ':' ).replace( /%2F/g, '/' ) );
}
function escapeRE( str ) {
return str.replace( /([\\^$.?*+()[\]])/g, '\\$1' );
}
function substituteFactory( options ) {
options = options || {};
var lead = options.indicator || '$';
var indicator = escapeRE( lead );
var lbrace = escapeRE( options.lbrace || '{' );
var rbrace = escapeRE( options.rbrace || '}' );
var re;
re = new RegExp(
// $$
'(?:' + indicator + '(' + indicator + '))|' +
// $0, $1
'(?:' + indicator + '(\\d+))|' +
// ${key}
'(?:' + indicator + '(?:' + lbrace + '([^' + lbrace + rbrace + ']+)' + rbrace + '))|' +
// $key (only if first char after $ is not $, digit, or { )
'(?:' + indicator + '(?!(?:[' + indicator + lbrace + ']|\\d))(\\S+?)\\b)',
'g'
);
// Replace $1, $2, or ${key1}, ${key2}, or $key1, $key2 by values from map. $$ is replaced by a single $.
return function ( str, map ) {
if ( !map ) return str;
return str.replace( re, function ( match, prefix, idx, key, alpha ) {
if ( prefix === lead ) return lead;
var k = alpha || key || idx;
var replacement = typeof map[ k ] === 'function' ? map[ k ]( match, k ) : map[ k ];
return typeof replacement === 'string' ? replacement : ( replacement || match );
} );
};
}
var substitute = substituteFactory();
var replaceShortcuts = ( function () {
var replaceHash = substituteFactory( {
indicator: '#',
lbrace: '[',
rbrace: ']'
} );
return function ( str, map ) {
var s = replaceHash( str, map );
return HC.capitalizePageNames ? capitalize( s ) : s;
};
}() );
// Text modification
var findCatsRE =
new RegExp( '\\[\\[' + wikiTextBlankOrBidi + '(?:' + HC.category_regexp + ')' + wikiTextBlankOrBidi + ':[^\\]]+\\]\\]', 'g' );
function replaceByBlanks( match ) {
return match.replace( /(\s|\S)/g, ' ' ); // /./ doesn't match linebreaks. /(\s|\S)/ does.
}
function find_category( wikitext, category, once ) {
var cat_regex = null;
var result_hook = en_wiktionary_find_category(wikitext, category, once);
if (result_hook) {
return result_hook;
}
if ( HC.template_categories[ category ] ) {
cat_regex = new RegExp(
'\\{\\{' + wikiTextBlankOrBidi + '(' + HC.template_regexp + '(?=' + wikiTextBlankOrBidi + ':))?' + wikiTextBlankOrBidi +
'(?:' + HC.template_categories[ category ] + ')' +
wikiTextBlankOrBidi + '(\\|.*?)?\\}\\}',
'g'
);
} else {
var cat_name = escapeRE( category );
var initial = cat_name.substr( 0, 1 );
cat_regex = new RegExp(
'\\[\\[' + wikiTextBlankOrBidi + '(' + HC.category_regexp + ')' + wikiTextBlankOrBidi + ':' + wikiTextBlankOrBidi +
( initial === '\\' || !HC.capitalizePageNames ?
initial :
'[' + initial.toUpperCase() + initial.toLowerCase() + ']' ) +
cat_name.substring( 1 ).replace( wikiTextBlankRE, wikiTextBlank ) +
wikiTextBlankOrBidi + '(\\|.*?)?\\]\\]',
'g'
);
}
if ( once ) return cat_regex.exec( wikitext );
var copiedtext = wikitext
.replace( /<!--(\s|\S)*?-->/g, replaceByBlanks )
.replace( /<nowiki>(\s|\S)*?<\/nowiki>/g, replaceByBlanks );
var result = [];
var curr_match = null;
while ( ( curr_match = cat_regex.exec( copiedtext ) ) !== null ) {
result.push( {
match: curr_match
} );
}
result.re = cat_regex;
return result; // An array containing all matches, with positions, in result[ i ].match
}
var interlanguageRE = null;
function change_category( wikitext, toRemove, toAdd, key, is_hidden ) {
function find_insertionpoint( wikitext, category ) {
var copiedtext = wikitext
.replace( /<!--(\s|\S)*?-->/g, replaceByBlanks )
.replace( /<nowiki>(\s|\S)*?<\/nowiki>/g, replaceByBlanks );
// Search in copiedtext to avoid that we insert inside an HTML comment or a nowiki "element".
var result = en_wiktionary_find_insertionpoint( wikitext, category );
if (result) {
return result;
}
var index = -1;
findCatsRE.lastIndex = 0;
while ( findCatsRE.exec( copiedtext ) !== null ) index = findCatsRE.lastIndex;
if ( index < 0 ) {
// Find the index of the first interlanguage link...
var match = null;
if ( !interlanguageRE ) {
// Approximation without API: interlanguage links start with 2 to 3 lower case letters, optionally followed by
// a sequence of groups consisting of a dash followed by one or more lower case letters. Exceptions are "simple"
// and "tokipona".
match = /((^|\n\r?)(\[\[\s*(([a-z]{2,3}(-[a-z]+)*)|simple|tokipona)\s*:[^\]]+\]\]\s*))+$/.exec( copiedtext );
} else {
match = interlanguageRE.exec( copiedtext );
}
if ( match ) index = match.index;
return {
idx: index,
onCat: false
};
}
return {
idx: index,
onCat: index >= 0
};
}
var summary = [],
nameSpace = HC.category_canonical,
cat_point = -1, // Position of removed category;
keyChange = ( toRemove && toAdd && toRemove === toAdd && toAdd.length ),
matches,
noOnCatOverride = false;
if ( key ) key = '|' + key;
// Remove
if ( toRemove && toRemove.length ) {
matches = find_category( wikitext, toRemove );
if ( !matches || !matches.length ) {
return {
text: wikitext,
summary: summary,
error: HC.messages.cat_notFound.replace( /\$1/g, toRemove )
};
} else {
var after = wikitext;
for (var i = matches.length - 1; i > 0; i--) {
after = en_wiktionary_remove_category(after, matches[i]);
}
var before_after = en_wiktionary_three_to_two_split(after, matches[0]);
noOnCatOverride = en_wiktionary_is_special_match(matches[0]);
var before = before_after[0];
after = before_after[1];
if ( toAdd ) {
// nameSpace = matches[ 0 ].match[ 1 ] || nameSpace; Canonical namespace should be always preferred
if ( key === null ) key = matches[ 0 ].match[ 2 ];
// Remember the category key, if any.
}
// Remove whitespace (properly): strip whitespace, but only up to the next line feed.
// If we then have two linefeeds in a row, remove one. Otherwise, if we have two non-
// whitespace characters, insert a blank.
var i = before.length - 1;
while ( i >= 0 && before.charAt( i ) !== '\n' && before.substr( i, 1 ).search( /\s/ ) >= 0 ) i--;
var j = 0;
while ( j < after.length && after.charAt( j ) !== '\n' && after.substr( j, 1 ).search( /\s/ ) >= 0 ) j++;
if ( i >= 0 && before.charAt( i ) === '\n' && ( !after.length || j < after.length && after.charAt( j ) === '\n' ) ) i--;
if ( i >= 0 ) before = before.substring( 0, i + 1 ); else before = '';
if ( j < after.length ) after = after.substring( j ); else after = '';
if (
before.length && before.substring( before.length - 1 ).search( /\S/ ) >= 0 &&
after.length && after.substr( 0, 1 ).search( /\S/ ) >= 0
) {
before += ' ';
}
cat_point = before.length;
if ( cat_point === 0 && after.length && after.substr( 0, 1 ) === '\n' ) after = after.substr( 1 );
wikitext = before + after;
if ( !keyChange ) {
if ( HC.template_categories[ toRemove ] ) { summary.push( HC.messages.template_removed.replace( /\$1/g, toRemove ) ); } else { summary.push( HC.messages.cat_removed.replace( /\$1/g, toRemove ) ); }
}
}
}
// Add
if ( toAdd && toAdd.length ) {
matches = find_category( wikitext, toAdd );
if ( matches && matches.length ) {
// Already exists
return {
text: wikitext,
summary: summary,
error: HC.messages.cat_exists.replace( /\$1/g, toAdd )
};
} else {
var hooked = en_wiktionary_add_category_hook( wikitext, toAdd, cat_point );
if (hooked) {
wikitext = hooked;
summary.push( HC.messages.cat_added.replace( /\$1/g, toAdd ) );
} else {
var onCat = false;
var noPreLine = false;
if ( cat_point < 0 ) {
var point = find_insertionpoint ( wikitext, toAdd );
cat_point = point.idx;
onCat = point.onCat;
noPreLine = point.noPreLine;
} else {
onCat = true;
}
var newcatstring = en_wiktionary_add_category_string(toAdd, onCat && !noOnCatOverride) || ('[[' + nameSpace + ':' + toAdd + ( key || '' ) + ']]');
if ( cat_point >= 0 ) {
var suffix = wikitext.substring( cat_point );
wikitext = wikitext.substring( 0, cat_point ) + ( cat_point > 0 && !noPreLine ? '\n' : '' ) + newcatstring + ( !onCat ? '\n' : '' );
if ( suffix.length && suffix.substr( 0, 1 ) !== '\n' ) wikitext += '\n' + suffix; else wikitext += suffix;
} else {
if ( wikitext.length && wikitext.substr( wikitext.length - 1, 1 ) !== '\n' ) wikitext += '\n';
wikitext += ( wikitext.length ? '\n' : '' ) + newcatstring;
}
if ( keyChange ) {
var k = key || '';
if ( k.length ) k = k.substr( 1 );
summary.push( substitute( HC.messages.cat_keychange, [ null, toAdd, k ] ) );
} else {
summary.push( HC.messages.cat_added.replace( /\$1/g, toAdd ) );
}
if ( HC.uncat_regexp && !is_hidden ) {
var txt = wikitext.replace( HC.uncat_regexp, '' ); // Remove "uncat" templates
if ( txt.length !== wikitext.length ) {
wikitext = txt;
summary.push( HC.messages.uncat_removed );
}
}
}
}
}
return {
text: wikitext,
summary: summary,
error: null
};
}
// The real HotCat UI
function evtKeys( e ) {
/* eslint-disable no-bitwise */
var code = 0;
if ( e.ctrlKey ) { // All modern browsers
// Ctrl-click seems to be overloaded in FF/Mac (it opens a pop-up menu), so treat cmd-click
// as a ctrl-click, too.
if ( e.ctrlKey || e.metaKey ) code |= 1;
if ( e.shiftKey ) code |= 2;
}
return code;
}
function evtKill( e ) {
if ( e.preventDefault ) {
e.preventDefault();
e.stopPropagation();
} else {
e.cancelBubble = true;
}
return false;
}
var catLine = null,
onUpload = false,
editors = [],
commitButton = null,
commitForm = null,
multiSpan = null,
pageText = null,
pageTime = null,
pageWatched = false,
watchCreate = false,
watchEdit = false,
minorEdits = false,
editToken = null,
is_rtl = false,
serverTime = null,
lastRevId = null,
pageTextRevId = null,
conflictingUser = null,
newDOM = false; // true if MediaWiki serves the new UL-LI DOM for categories
function CategoryEditor() {
this.initialize.apply( this, arguments );
}
function setPage( json ) {
var startTime = null;
if ( json && json.query ) {
if ( json.query.pages ) {
var page = json.query.pages[ !conf.wgArticleId ? '-1' : String( conf.wgArticleId ) ];
if ( page ) {
if ( page.revisions && page.revisions.length ) {
// Revisions are sorted by revision ID, hence [ 0 ] is the one we asked for, and possibly there's a [ 1 ] if we're
// not on the latest revision (edit conflicts and such).
pageText = page.revisions[ 0 ][ '*' ];
if ( page.revisions[ 0 ].timestamp ) pageTime = page.revisions[ 0 ].timestamp.replace( /\D/g, '' );
if ( page.revisions[ 0 ].revid ) pageTextRevId = page.revisions[ 0 ].revid;
if ( page.revisions.length > 1 ) conflictingUser = page.revisions[ 1 ].user;
}
if ( page.lastrevid ) lastRevId = page.lastrevid;
if ( page.starttimestamp ) startTime = page.starttimestamp.replace( /\D/g, '' );
pageWatched = typeof page.watched === 'string';
if ( json.query.tokens ) editToken = json.query.tokens.csrftoken;
if ( page.langlinks && ( !json[ 'query-continue' ] || !json[ 'query-continue' ].langlinks ) ) {
// We have interlanguage links, and we got them all.
var re = '';
for ( var i = 0; i < page.langlinks.length; i++ ) re += ( i > 0 ? '|' : '' ) + page.langlinks[ i ].lang.replace( /([\\^$.?*+()])/g, '\\$1' );
if ( re.length ) interlanguageRE = new RegExp( '((^|\\n\\r?)(\\[\\[\\s*(' + re + ')\\s*:[^\\]]+\\]\\]\\s*))+$' );
}
}
}
// Siteinfo
if ( json.query.general ) {
if ( json.query.general.time && !startTime ) startTime = json.query.general.time.replace( /\D/g, '' );
if ( HC.capitalizePageNames === null ) {
// ResourceLoader's JSParser doesn't like .case, so override eslint.
// eslint-disable-next-line dot-notation
HC.capitalizePageNames = ( json.query.general[ 'case' ] === 'first-letter' );
}
}
serverTime = startTime;
// Userinfo
if ( json.query.userinfo && json.query.userinfo.options ) {
watchCreate = !HC.dont_add_to_watchlist && json.query.userinfo.options.watchcreations === '1';
watchEdit = !HC.dont_add_to_watchlist && json.query.userinfo.options.watchdefault === '1';
minorEdits = json.query.userinfo.options.minordefault === 1;
// If the user has the "All edits are minor" preference enabled, we should honor that
// for single category changes, no matter what the site configuration is.
if ( minorEdits ) HC.single_minor = true;
}
}
}
var saveInProgress = false;
function initiateEdit( doEdit, failure ) {
if ( saveInProgress ) return;
saveInProgress = true;
var oldButtonState;
if ( commitButton ) {
oldButtonState = commitButton.disabled;
commitButton.disabled = true;
}
function fail() {
saveInProgress = false;
if ( commitButton ) commitButton.disabled = oldButtonState;
failure.apply( this, arguments );
}
// Must use Ajax here to get the user options and the edit token.
$.getJSON(
wgServer + conf.wgScriptPath + '/api.php?' +
'format=json&action=query&rawcontinue=&titles=' + encodeURIComponent( conf.wgPageName ) +
'&prop=info%7Crevisions%7Clanglinks&inprop=watched&rvprop=content%7Ctimestamp%7Cids%7Cuser&lllimit=500' +
'&rvlimit=2&rvdir=newer&rvstartid=' + conf.wgCurRevisionId + '&meta=siteinfo%7Cuserinfo%7Ctokens&type=csrf&uiprop=options',
function ( json ) {
setPage( json );
doEdit( fail );
}
).fail( function ( req ) {
fail( req.status + ' ' + req.statusText );
} );
}
function multiChangeMsg( count ) {
var msg = HC.messages.multi_change;
if ( typeof msg !== 'string' && msg.length )
if ( mw.language && mw.language.convertPlural ) { msg = mw.language.convertPlural( count, msg ); } else { msg = msg[ msg.length - 1 ]; }
return substitute( msg, [ null, String( count ) ] );
}
function currentTimestamp() {
var now = new Date();
var ts = String( now.getUTCFullYear() );
function two( s ) {
return s.substr( s.length - 2 );
}
ts +=
two( '0' + ( now.getUTCMonth() + 1 ) ) +
two( '0' + now.getUTCDate() ) +
two( '00' + now.getUTCHours() ) +
two( '00' + now.getUTCMinutes() ) +
two( '00' + now.getUTCSeconds() );
return ts;
}
function performChanges( failure, singleEditor ) {
if ( pageText === null ) {
failure( HC.messages.multi_error );
return;
}
// Backwards compatibility after message change (added $2 to cat_keychange)
if ( HC.messages.cat_keychange.indexOf( '$2' ) < 0 ) HC.messages.cat_keychange += '"$2"';
// More backwards-compatibility with earlier HotCat versions:
if ( !HC.messages.short_catchange ) HC.messages.short_catchange = '[[' + HC.category_canonical + ':$1]]';
// Create a form and submit it. We don't use the edit API (api.php?action=edit) because
// (a) sensibly reporting back errors like edit conflicts is always a hassle, and
// (b) we want to show a diff for multi-edits anyway, and
// (c) we want to trigger onsubmit events, allowing user code to intercept the edit.
// Using the form, we can do (b) and (c), and we get (a) for free. And, of course, using the form
// automatically reloads the page with the updated categories on a successful submit, which
// we would have to do explicitly if we used the edit API.
var action;
// Normally, we don't have to care about edit conflicts. If some other user edited the page in the meantime, the
// server will take care of it and merge the edit automatically or present an edit conflict screen. However, the
// server suppresses edit conflicts with oneself. Hence, if we have a conflict, and the conflicting user is the
// current user, then we set the "oldid" value and switch to diff, which gives the "you are editing an old version;
// if you save, any more recent changes will be lost" screen.
var selfEditConflict = ( lastRevId !== null && lastRevId !== conf.wgCurRevisionId || pageTextRevId !== null &&
pageTextRevId !== conf.wgCurRevisionId ) && conflictingUser && conflictingUser === conf.wgUserName;
if ( singleEditor && !singleEditor.noCommit && !HC.no_autocommit && editToken && !selfEditConflict ) {
// If we do have an edit conflict, but not with ourself, that's no reason not to attempt to save: the server side may actually be able to
// merge the changes. We just need to make sure that we do present a diff view if it's a self edit conflict.
commitForm.wpEditToken.value = editToken;
action = commitForm.wpDiff;
if ( action ) action.name = action.value = 'wpSave';
} else {
action = commitForm.wpSave;
if ( action ) action.name = action.value = 'wpDiff';
}
var result = {
text: pageText
},
changed = [],
added = [],
deleted = [],
changes = 0,
toEdit = singleEditor ? [ singleEditor ] : editors,
error = null,
edit,
i;
for ( i = 0; i < toEdit.length; i++ ) {
edit = toEdit[ i ];
if ( edit.state === CategoryEditor.CHANGED ) {
result = change_category(
result.text,
edit.originalCategory,
edit.currentCategory,
edit.currentKey,
edit.currentHidden );
if ( !result.error ) {
changes++;
if ( !edit.originalCategory || !edit.originalCategory.length ) {
added.push( edit.currentCategory );
} else {
changed.push( {
from: edit.originalCategory,
to: edit.currentCategory
} );
}
} else if ( error === null ) {
error = result.error;
}
} else if (
edit.state === CategoryEditor.DELETED && edit.originalCategory && edit.originalCategory.length ) {
result = change_category(
result.text,
edit.originalCategory,
null, null, false );
if ( !result.error ) {
changes++;
deleted.push( edit.originalCategory );
} else if ( error === null ) {
error = result.error;
}
}
}
if ( error !== null ) { // Do not commit if there were errors
action = commitForm.wpSave;
if ( action ) action.name = action.value = 'wpDiff';
}
// Fill in the form and submit it
commitForm.wpMinoredit.checked = minorEdits;
commitForm.wpWatchthis.checked = !conf.wgArticleId && watchCreate || watchEdit || pageWatched;
if ( conf.wgArticleId || !!singleEditor ) {
// Prepare change-tag save
if ( action && action.value === 'wpSave' ) {
if ( HC.changeTag ) {
commitForm.wpChangeTags.value = HC.changeTag;
HC.messages.using = '';
HC.messages.prefix = '';
}
} else {
commitForm.wpAutoSummary.value = HC.changeTag;
}
if ( changes === 1 ) {
if ( result.summary && result.summary.length ) commitForm.wpSummary.value = HC.messages.prefix + result.summary.join( HC.messages.separator ) + HC.messages.using;
commitForm.wpMinoredit.checked = HC.single_minor || minorEdits;
} else if ( changes ) {
var summary = [];
var shortSummary = [];
// Deleted
for ( i = 0; i < deleted.length; i++ ) summary.push( '−' + substitute( HC.messages.short_catchange, [ null, deleted[ i ] ] ) );
if ( deleted.length === 1 ) shortSummary.push( '−' + substitute( HC.messages.short_catchange, [ null, deleted[ 0 ] ] ) ); else if ( deleted.length ) shortSummary.push( '− ' + multiChangeMsg( deleted.length ) );
// Added
for ( i = 0; i < added.length; i++ ) summary.push( '+' + substitute( HC.messages.short_catchange, [ null, added[ i ] ] ) );
if ( added.length === 1 ) shortSummary.push( '+' + substitute( HC.messages.short_catchange, [ null, added[ 0 ] ] ) ); else if ( added.length ) shortSummary.push( '+ ' + multiChangeMsg( added.length ) );
// Changed
var arrow = is_rtl ? '\u2190' : '\u2192'; // left and right arrows. Don't use ← and → in the code.
for ( i = 0; i < changed.length; i++ ) {
if ( changed[ i ].from !== changed[ i ].to ) {
summary.push(
'±' + substitute( HC.messages.short_catchange, [ null, changed[ i ].from ] ) + arrow +
substitute( HC.messages.short_catchange, [ null, changed[ i ].to ] )
);
} else {
summary.push( '±' + substitute( HC.messages.short_catchange, [ null, changed[ i ].from ] ) );
}
}
if ( changed.length === 1 ) {
if ( changed[ 0 ].from !== changed[ 0 ].to ) {
shortSummary.push(
'±' + substitute( HC.messages.short_catchange, [ null, changed[ 0 ].from ] ) + arrow +
substitute( HC.messages.short_catchange, [ null, changed[ 0 ].to ] )
);
} else {
shortSummary.push( '±' + substitute( HC.messages.short_catchange, [ null, changed[ 0 ].from ] ) );
}
} else if ( changed.length ) {
shortSummary.push( '± ' + multiChangeMsg( changed.length ) );
}
if ( summary.length ) {
summary = summary.join( HC.messages.separator );
if ( summary.length > 200 - HC.messages.prefix.length - HC.messages.using.length ) summary = shortSummary.join( HC.messages.separator );
commitForm.wpSummary.value = HC.messages.prefix + summary + HC.messages.using;
}
}
}
commitForm.wpTextbox1.value = result.text;
commitForm.wpStarttime.value = serverTime || currentTimestamp();
commitForm.wpEdittime.value = pageTime || commitForm.wpStarttime.value;
if ( selfEditConflict ) commitForm.oldid.value = String( pageTextRevId || conf.wgCurRevisionId );
// Submit the form in a way that triggers onsubmit events: commitForm.submit() doesn't.
commitForm.hcCommit.click();
}
function resolveOne( page, toResolve ) {
var cats = page.categories,
lks = page.links,
is_dab = false,
is_redir = typeof page.redirect === 'string', // Hard redirect?
is_hidden = page.categoryinfo && typeof page.categoryinfo.hidden === 'string',
is_missing = typeof page.missing === 'string',
i;
for ( i = 0; i < toResolve.length; i++ ) {
if ( i && toResolve[ i ].dabInputCleaned !== page.title.substring( page.title.indexOf( ':' ) + 1 ) ) continue;
// Note: the server returns in page an NFC normalized Unicode title. If our input was not NFC normalized, we may not find
// any entry here. If we have only one editor to resolve (the most common case, I presume), we may simply skip the check.
toResolve[ i ].currentHidden = is_hidden;
toResolve[ i ].inputExists = !is_missing;
toResolve[ i ].icon.src = ( is_missing ? HC.existsNo : HC.existsYes );
}
if ( is_missing ) return;
if ( !is_redir && cats && ( HC.disambig_category || HC.redir_category ) ) {
for ( var c = 0; c < cats.length; c++ ) {
var cat = cats[ c ].title;
// Strip namespace prefix
if ( cat ) {
cat = cat.substring( cat.indexOf( ':' ) + 1 ).replace( /_/g, ' ' );
if ( cat === HC.disambig_category ) {
is_dab = true;
break;
} else if ( cat === HC.redir_category ) {
is_redir = true;
break;
}
}
}
}
if ( !is_redir && !is_dab ) return;
if ( !lks || !lks.length ) return;
var titles = [];
for ( i = 0; i < lks.length; i++ ) {
if (
// Category namespace -- always true since we ask only for the category links
lks[ i ].ns === 14 &&
// Name not empty
lks[ i ].title && lks[ i ].title.length
) {
// Internal link to existing thingy. Extract the page name and remove the namespace.
var match = lks[ i ].title;
match = match.substring( match.indexOf( ':' ) + 1 );
// Exclude blacklisted categories.
if ( !HC.blacklist || !HC.blacklist.test( match ) ) titles.push( match );
}
}
if ( !titles.length ) return;
for ( i = 0; i < toResolve.length; i++ ) {
if ( i && toResolve[ i ].dabInputCleaned !== page.title.substring( page.title.indexOf( ':' ) + 1 ) ) continue;
toResolve[ i ].inputExists = true; // Might actually be wrong if it's a redirect pointing to a non-existing category
toResolve[ i ].icon.src = HC.existsYes;
if ( titles.length > 1 ) {
toResolve[ i ].dab = titles;
} else {
toResolve[ i ].text.value =
titles[ 0 ] + ( toResolve[ i ].currentKey !== null ? '|' + toResolve[ i ].currentKey : '' );
}
}
}
function resolveRedirects( toResolve, params ) {
if ( !params || !params.query || !params.query.pages ) return;
for ( var p in params.query.pages ) resolveOne( params.query.pages[ p ], toResolve );
}
function resolveMulti( toResolve, callback ) {
var i;
for ( i = 0; i < toResolve.length; i++ ) {
toResolve[ i ].dab = null;
toResolve[ i ].dabInput = toResolve[ i ].lastInput;
}
if ( noSuggestions ) {
callback( toResolve );
return;
}
// Use %7C instead of |, otherwise Konqueror insists on re-encoding the arguments, resulting in doubly encoded
// category names. (That is a bug in Konqueror. Other browsers don't have this problem.)
var args = 'action=query&prop=info%7Clinks%7Ccategories%7Ccategoryinfo&plnamespace=14' +
'&pllimit=' + ( toResolve.length * 10 ) +
'&cllimit=' + ( toResolve.length * 10 ) +
'&format=json&titles=';
for ( i = 0; i < toResolve.length; i++ ) {
var v = toResolve[ i ].dabInput;
v = replaceShortcuts( v, HC.shortcuts );
toResolve[ i ].dabInputCleaned = v;
args += encodeURIComponent( 'Category:' + v );
if ( i + 1 < toResolve.length ) args += '%7C';
}
$.getJSON( wgServer + conf.wgScriptPath + '/api.php?' + args,
function ( json ) {
resolveRedirects( toResolve, json );
callback( toResolve );
} ).fail( function ( req ) {
if ( !req ) noSuggestions = true;
callback( toResolve );
} );
}
function makeActive( which ) {
if ( which.is_active ) return;
for ( var i = 0; i < editors.length; i++ )
if ( editors[ i ] !== which ) editors[ i ].inactivate();
which.is_active = true;
if ( which.dab ) {
// eslint-disable-next-line no-use-before-define
showDab( which );
} else {
// Check for programmatic value changes.
var expectedInput = which.lastRealInput || which.lastInput || '';
var actualValue = which.text.value || '';
if ( !expectedInput.length && actualValue.length || expectedInput.length && actualValue.indexOf( expectedInput ) ) {
// Somehow the field's value appears to have changed, and which.lastSelection therefore is no longer valid. Try to set the
// cursor at the end of the category, and do not display the old suggestion list.
which.showsList = false;
var v = actualValue.split( '|' );
which.lastRealInput = which.lastInput = v[ 0 ];
if ( v.length > 1 ) which.currentKey = v[ 1 ];
if ( which.lastSelection ) {
which.lastSelection = {
start: v[ 0 ].length,
end: v[ 0 ].length
};
}
}
if ( which.showsList ) which.displayList();
if ( which.lastSelection ) {
if ( is_webkit ) {
// WebKit (Safari, Chrome) has problems selecting inside focus()
// See http://code.google.com/p/chromium/issues/detail?id=32865#c6
window.setTimeout(
function () {
which.setSelection( which.lastSelection.start, which.lastSelection.end );
},
1 );
} else {
which.setSelection( which.lastSelection.start, which.lastSelection.end );
}
}
}
}
function showDab( which ) {
if ( !which.is_active ) {
makeActive( which );
} else {
which.showSuggestions( which.dab, false, null, null ); // do autocompletion, no key, no engine selector
which.dab = null;
}
}
function multiSubmit() {
var toResolve = [];
for ( var i = 0; i < editors.length; i++ )
if ( editors[ i ].state === CategoryEditor.CHANGE_PENDING || editors[ i ].state === CategoryEditor.OPEN ) toResolve.push( editors[ i ] );
if ( !toResolve.length ) {
initiateEdit( function ( failure ) {
performChanges( failure );
}, function ( msg ) {
alert( msg );
} );
return;
}
resolveMulti( toResolve, function ( resolved ) {
var firstDab = null;
var dontChange = false;
for ( var i = 0; i < resolved.length; i++ ) {
if ( resolved[ i ].lastInput !== resolved[ i ].dabInput ) {
// We didn't disable all the open editors, but we did asynchronous calls. It is
// theoretically possible that the user changed something...
dontChange = true;
} else {
if ( resolved[ i ].dab ) {
if ( !firstDab ) firstDab = resolved[ i ];
} else {
if ( resolved[ i ].acceptCheck( true ) ) resolved[ i ].commit();
}
}
}
if ( firstDab ) {
showDab( firstDab );
} else if ( !dontChange ) {
initiateEdit( function ( failure ) {
performChanges( failure );
}, function ( msg ) {
alert( msg );
} );
}
} );
}
function setMultiInput() {
if ( commitButton || onUpload ) return;
commitButton = make( 'input' );
commitButton.type = 'button';
commitButton.value = HC.messages.commit;
commitButton.onclick = multiSubmit;
if ( multiSpan ) multiSpan.parentNode.replaceChild( commitButton, multiSpan ); else catLine.appendChild( commitButton );
}
function checkMultiInput() {
if ( !commitButton ) return;
var hasChanges = false;
for ( var i = 0; i < editors.length; i++ ) {
if ( editors[ i ].state !== CategoryEditor.UNCHANGED ) {
hasChanges = true;
break;
}
}
commitButton.disabled = !hasChanges;
}
var suggestionEngines = {
opensearch: {
uri: '/api.php?format=json&action=opensearch&namespace=14&limit=30&search=Category:$1', // $1 = search term
// Function to convert result of uri into an array of category names
handler: function ( queryResult, queryKey ) {
if ( queryResult && queryResult.length >= 2 ) {
var key = queryResult[ 0 ].substring( queryResult[ 0 ].indexOf( ':' ) + 1 );
var titles = queryResult[ 1 ];
var exists = false;
if ( !cat_prefix ) cat_prefix = new RegExp( '^(' + HC.category_regexp + '):' );
for ( var i = 0; i < titles.length; i++ ) {
cat_prefix.lastIndex = 0;
var m = cat_prefix.exec( titles[ i ] );
if ( m && m.length > 1 ) {
titles[ i ] = titles[ i ].substring( titles[ i ].indexOf( ':' ) + 1 ); // rm namespace
if ( key === titles[ i ] ) exists = true;
} else {
titles.splice( i, 1 ); // Nope, it's not a category after all.
i--;
}
}
titles.exists = exists;
if ( queryKey !== key ) titles.normalized = key;
// Remember the NFC normalized key we got back from the server
return titles;
}
return null;
}
},
internalsearch: {
uri: '/api.php?format=json&action=query&list=allpages&apnamespace=14&aplimit=30&apfrom=$1&apprefix=$1',
handler: function ( queryResult ) {
if ( queryResult && queryResult.query && queryResult.query.allpages ) {
var titles = queryResult.query.allpages;
for ( var i = 0; i < titles.length; i++ ) titles[ i ] = titles[ i ].title.substring( titles[ i ].title.indexOf( ':' ) + 1 ); // rm namespace
return titles;
}
return null;
}
},
exists: {
uri: '/api.php?format=json&action=query&prop=info&titles=Category:$1',
handler: function ( queryResult, queryKey ) {
if ( queryResult && queryResult.query && queryResult.query.pages && !queryResult.query.pages[ -1 ] ) {
// Should have exactly 1
for ( var p in queryResult.query.pages ) {
var title = queryResult.query.pages[ p ].title;
title = title.substring( title.indexOf( ':' ) + 1 );
var titles = [ title ];
titles.exists = true;
if ( queryKey !== title ) titles.normalized = title;
// NFC
return titles;
}
}
return null;
}
},
subcategories: {
uri: '/api.php?format=json&action=query&list=categorymembers&cmtype=subcat&cmlimit=max&cmtitle=Category:$1',
handler: function ( queryResult ) {
if ( queryResult && queryResult.query && queryResult.query.categorymembers ) {
var titles = queryResult.query.categorymembers;
for ( var i = 0; i < titles.length; i++ ) titles[ i ] = titles[ i ].title.substring( titles[ i ].title.indexOf( ':' ) + 1 ); // rm namespace
return titles;
}
return null;
}
},
parentcategories: {
uri: '/api.php?format=json&action=query&prop=categories&titles=Category:$1&cllimit=max',
handler: function ( queryResult ) {
if ( queryResult && queryResult.query && queryResult.query.pages ) {
for ( var p in queryResult.query.pages ) {
if ( queryResult.query.pages[ p ].categories ) {
var titles = queryResult.query.pages[ p ].categories;
for ( var i = 0; i < titles.length; i++ ) titles[ i ] = titles[ i ].title.substring( titles[ i ].title.indexOf( ':' ) + 1 ); // rm namespace
return titles;
}
}
}
return null;
}
}
};
var suggestionConfigs = {
searchindex: {
name: 'Search index',
engines: [ 'opensearch' ],
cache: {},
show: true,
temp: false,
noCompletion: false
},
pagelist: {
name: 'Page list',
engines: [ 'internalsearch', 'exists' ],
cache: {},
show: true,
temp: false,
noCompletion: false
},
combined: {
name: 'Combined search',
engines: [ 'opensearch', 'internalsearch' ],
cache: {},
show: true,
temp: false,
noCompletion: false
},
subcat: {
name: 'Subcategories',
engines: [ 'subcategories' ],
cache: {},
show: true,
temp: true,
noCompletion: true
},
parentcat: {
name: 'Parent categories',
engines: [ 'parentcategories' ],
cache: {},
show: true,
temp: true,
noCompletion: true
}
};
CategoryEditor.UNCHANGED = 0;
CategoryEditor.OPEN = 1; // Open, but no input yet
CategoryEditor.CHANGE_PENDING = 2; // Open, some input made
CategoryEditor.CHANGED = 3;
CategoryEditor.DELETED = 4;
// Support: IE6
// IE6 sometimes forgets to redraw the list when editors are opened or closed.
// Adding/removing a dummy element helps, at least when opening editors.
var dummyElement = make( '\xa0', true );
function forceRedraw() {
if ( dummyElement.parentNode ) document.body.removeChild( dummyElement ); else document.body.appendChild( dummyElement );
}
// Event keyCodes that we handle in the text input field/suggestion list.
var BS = 8,
TAB = 9,
RET = 13,
ESC = 27,
SPACE = 32,
PGUP = 33,
PGDOWN = 34,
UP = 38,
DOWN = 40,
DEL = 46,
IME = 229;
CategoryEditor.prototype = {
initialize: function ( line, span, after, key, is_hidden ) {
// If a span is given, 'after' is the category title, otherwise it may be an element after which to
// insert the new span. 'key' is likewise overloaded; if a span is given, it is the category key (if
// known), otherwise it is a boolean indicating whether a bar shall be prepended.
if ( !span ) {
this.isAddCategory = true;
// Create add span and append to catLinks
this.originalCategory = '';
this.originalKey = null;
this.originalExists = false;
if ( !newDOM ) {
span = make( 'span' );
span.className = 'noprint';
if ( key ) {
span.appendChild( make( ' | ', true ) );
if ( after ) {
after.parentNode.insertBefore( span, after.nextSibling );
after = after.nextSibling;
} else if (line) {
line.appendChild( span );
}
} else if ( line && line.firstChild ) {
span.appendChild( make( ' ', true ) );
line.appendChild( span );
}
}
this.linkSpan = make( 'span' );
this.linkSpan.className = 'noprint nopopups hotcatlink';
var lk = make( 'a' );
lk.href = '#catlinks';
lk.onclick = this.open.bind( this );
lk.appendChild( make( HC.links.add, true ) );
lk.title = HC.tooltips.add;
this.linkSpan.appendChild( lk );
span = make( newDOM ? 'li' : 'span' );
span.className = 'noprint';
if ( is_rtl ) span.dir = 'rtl';
span.appendChild( this.linkSpan );
if ( after ) {
after.parentNode.insertBefore( span, after.nextSibling );
} else if ( line ) {
line.appendChild( span );
}
this.normalLinks = null;
this.undelLink = null;
this.catLink = null;
} else {
if ( is_rtl ) span.dir = 'rtl';
this.isAddCategory = false;
this.catLink = span.firstChild;
this.originalCategory = after;
this.originalKey = ( key && key.length > 1 ) ? key.substr( 1 ) : null; // > 1 because it includes the leading bar
this.originalExists = !hasClass( this.catLink, 'new' );
// Create change and del links
this.makeLinkSpan();
if ( !this.originalExists && this.upDownLinks ) this.upDownLinks.style.display = 'none';
span.appendChild( this.linkSpan );
}
this.originalHidden = is_hidden;
this.line = line;
this.engine = HC.suggestions;
this.span = span;
this.currentCategory = this.originalCategory;
this.currentExists = this.originalExists;
this.currentHidden = this.originalHidden;
this.currentKey = this.originalKey;
this.state = CategoryEditor.UNCHANGED;
this.lastSavedState = CategoryEditor.UNCHANGED;
this.lastSavedCategory = this.originalCategory;
this.lastSavedKey = this.originalKey;
this.lastSavedExists = this.originalExists;
this.lastSavedHidden = this.originalHidden;
if ( this.catLink && this.currentKey ) this.catLink.title = this.currentKey;
editors[ editors.length ] = this;
},
makeLinkSpan: function () {
this.normalLinks = make( 'span' );
var lk = null;
if ( this.originalCategory && this.originalCategory.length ) {
lk = make( 'a' );
lk.href = '#catlinks';
lk.onclick = this.remove.bind( this );
lk.appendChild( make( HC.links.remove, true ) );
lk.title = HC.tooltips.remove;
this.normalLinks.appendChild( make( ' ', true ) );
this.normalLinks.appendChild( lk );
}
if ( !HC.template_categories[ this.originalCategory ] ) {
lk = make( 'a' );
lk.href = '#catlinks';
lk.onclick = this.open.bind( this );
lk.appendChild( make( HC.links.change, true ) );
lk.title = HC.tooltips.change;
this.normalLinks.appendChild( make( ' ', true ) );
this.normalLinks.appendChild( lk );
if ( !noSuggestions && HC.use_up_down ) {
this.upDownLinks = make( 'span' );
lk = make( 'a' );
lk.href = '#catlinks';
lk.onclick = this.down.bind( this );
lk.appendChild( make( HC.links.down, true ) );
lk.title = HC.tooltips.down;
this.upDownLinks.appendChild( make( ' ', true ) );
this.upDownLinks.appendChild( lk );
lk = make( 'a' );
lk.href = '#catlinks';
lk.onclick = this.up.bind( this );
lk.appendChild( make( HC.links.up, true ) );
lk.title = HC.tooltips.up;
this.upDownLinks.appendChild( make( ' ', true ) );
this.upDownLinks.appendChild( lk );
this.normalLinks.appendChild( this.upDownLinks );
}
}
this.linkSpan = make( 'span' );
this.linkSpan.className = 'noprint nopopups hotcatlink';
this.linkSpan.appendChild( this.normalLinks );
this.undelLink = make( 'span' );
this.undelLink.className = 'nopopups hotcatlink';
this.undelLink.style.display = 'none';
lk = make( 'a' );
lk.href = '#catlinks';
lk.onclick = this.restore.bind( this );
lk.appendChild( make( HC.links.restore, true ) );
lk.title = HC.tooltips.restore;
this.undelLink.appendChild( make( ' ', true ) );
this.undelLink.appendChild( lk );
this.linkSpan.appendChild( this.undelLink );
},
invokeSuggestions: function ( dont_autocomplete ) {
if ( this.engine && suggestionConfigs[ this.engine ] && suggestionConfigs[ this.engine ].temp && !dont_autocomplete ) this.engine = HC.suggestions; // Reset to a search upon input
this.state = CategoryEditor.CHANGE_PENDING;
var self = this;
window.setTimeout( function () {
self.textchange( dont_autocomplete );
}, HC.suggest_delay );
},
makeForm: function () {
var form = make( 'form' );
form.method = 'POST';
form.onsubmit = this.accept.bind( this );
this.form = form;
var self = this;
var text = make( 'input' );
text.type = 'text';
text.size = HC.editbox_width;
if ( !noSuggestions ) {
// Be careful here to handle IME input. This is browser/OS/IME dependent, but basically there are two mechanisms:
// - Modern (DOM Level 3) browsers use compositionstart/compositionend events to signal composition; if the
// composition is not canceled, there'll be a textInput event following. During a composition key events are
// either all suppressed (FF/Gecko), or otherwise have keyDown === IME for all keys (Webkit).
// - Webkit sends a textInput followed by keyDown === IME and a keyUp with the key that ended composition.
// - Gecko doesn't send textInput but just a keyUp with the key that ended composition, without sending keyDown
// first. Gecko doesn't send any keydown while IME is active.
// - Older browsers signal composition by keyDown === IME for the first and subsequent keys for a composition. The
// first keyDown !== IME is certainly after the end of the composition. Typically, composition end can also be
// detected by a keyDown IME with a keyUp of space, tab, escape, or return.
text.onkeyup = function ( evt ) {
var key = evt.keyCode || 0;
if ( self.ime && self.lastKey === IME && !self.usesComposition && ( key === TAB || key === RET || key === ESC || key === SPACE ) ) self.ime = false;
if ( self.ime ) return true;
if ( key === UP || key === DOWN || key === PGUP || key === PGDOWN ) {
// In case a browser doesn't generate keypress events for arrow keys...
if ( self.keyCount === 0 ) return self.processKey( evt );
} else {
if ( key === ESC && self.lastKey !== IME ) {
if ( !self.resetKeySelection() ) {
// No undo of key selection: treat ESC as "cancel".
self.cancel();
return;
}
}
// Also do this for ESC as a workaround for Firefox bug 524360
// https://bugzilla.mozilla.org/show_bug.cgi?id=524360
self.invokeSuggestions( key === BS || key === DEL || key === ESC );
}
return true;
};
text.onkeydown = function ( evt ) {
var key = evt.keyCode || 0;
self.lastKey = key;
self.keyCount = 0;
// DOM Level < 3 IME input
if ( !self.ime && key === IME && !self.usesComposition ) {
// self.usesComposition catches browsers that may emit spurious keydown IME after a composition has ended
self.ime = true;
} else if ( self.ime && key !== IME && !( key >= 16 && key <= 20 || key >= 91 && key <= 93 || key === 144 ) ) {
// Ignore control keys: ctrl, shift, alt, alt gr, caps lock, windows/apple cmd keys, num lock. Only the windows keys
// terminate IME (apple cmd doesn't), but they also cause a blur, so it's OK to ignore them here.
// Note: Safari 4 (530.17) propagates ESC out of an IME composition (observed at least on Win XP).
self.ime = false;
}
if ( self.ime ) return true;
// Handle return explicitly, to override the default form submission to be able to check for ctrl
if ( key === RET ) return self.accept( evt );
// Inhibit default behavior of ESC (revert to last real input in FF: we do that ourselves)
return ( key === ESC ) ? evtKill( evt ) : true;
};
// And handle continued pressing of arrow keys
text.onkeypress = function ( evt ) {
self.keyCount++;
return self.processKey( evt );
};
$( text ).on( 'focus', function () {
makeActive( self );
} );
// On IE, blur events are asynchronous, and may thus arrive after the element has lost the focus. Since IE
// can get the selection only while the element is active (has the focus), we may not always get the selection.
// Therefore, use an IE-specific synchronous event on IE...
// Don't test for text.selectionStart being defined;
$( text ).on(
( text.onbeforedeactivate !== undefined && text.createTextRange ) ? 'beforedeactivate' : 'blur',
this.saveView.bind( this ) );
// DOM Level 3 IME handling
try {
// Setting lastKey = IME provides a fake keyDown for Gecko's single keyUp after a cmposition. If we didn't do this,
// cancelling a composition via ESC would also cancel and close the whole category input editor.
$( text ).on( 'compositionstart', function () {
self.lastKey = IME;
self.usesComposition = true;
self.ime = true;
} );
$( text ).on( 'compositionend', function () {
self.lastKey = IME;
self.usesComposition = true;
self.ime = false;
} );
$( text ).on( 'textInput', function () {
self.ime = false;
self.invokeSuggestions( false );
} );
} catch ( any ) {
// Just in case some browsers might produce exceptions with these DOM Level 3 events
}
$( text ).on( 'blur', function () {
self.usesComposition = false;
self.ime = false;
} );
}
this.text = text;
this.icon = make( 'img' );
var list = null;
if ( !noSuggestions ) {
list = make( 'select' );
list.onclick = function () {
if ( self.highlightSuggestion( 0 ) ) self.textchange( false, true );
};
list.ondblclick = function ( e ) {
if ( self.highlightSuggestion( 0 ) ) self.accept( e );
};
list.onchange = function () {
self.highlightSuggestion( 0 );
self.text.focus();
};
list.onkeyup = function ( evt ) {
if ( evt.keyCode === ESC ) {
self.resetKeySelection();
self.text.focus();
window.setTimeout( function () {
self.textchange( true );
}, HC.suggest_delay );
} else if ( evt.keyCode === RET ) {
self.accept( evt );
}
};
if ( !HC.fixed_search ) {
var engineSelector = make( 'select' );
for ( var key in suggestionConfigs ) {
if ( suggestionConfigs[ key ].show ) {
var opt = make( 'option' );
opt.value = key;
if ( key === this.engine ) opt.selected = true;
opt.appendChild( make( suggestionConfigs[ key ].name, true ) );
engineSelector.appendChild( opt );
}
}
engineSelector.onchange = function () {
self.engine = self.engineSelector.options[ self.engineSelector.selectedIndex ].value;
self.text.focus();
self.textchange( true, true ); // Don't autocomplete, force re-display of list
};
this.engineSelector = engineSelector;
}
}
this.list = list;
function button_label( id, defaultText ) {
var label = null;
if (
onUpload &&
window.UFUI !== undefined &&
window.UIElements !== undefined &&
UFUI.getLabel instanceof Function
) {
try {
label = UFUI.getLabel( id, true );
// Extract the plain text. IE doesn't know that Node.TEXT_NODE === 3
while ( label && label.nodeType !== 3 ) label = label.firstChild;
} catch ( ex ) {
label = null;
}
}
if ( !label || !label.data ) return defaultText;
return label.data;
}
// Do not use type 'submit'; we cannot detect modifier keys if we do
var OK = make( 'input' );
OK.type = 'button';
OK.value = button_label( 'wpOkUploadLbl', HC.messages.ok );
OK.onclick = this.accept.bind( this );
this.ok = OK;
var cancel = make( 'input' );
cancel.type = 'button';
cancel.value = button_label( 'wpCancelUploadLbl', HC.messages.cancel );
cancel.onclick = this.cancel.bind( this );
this.cancelButton = cancel;
var span = make( 'span' );
span.className = 'hotcatinput';
span.style.position = 'relative';
span.appendChild( text );
// Support: IE8, IE9
// Put some text into this span (a0 is nbsp) and make sure it always stays on the same
// line as the input field, otherwise, IE8/9 miscalculates the height of the span and
// then the engine selector may overlap the input field.
span.appendChild( make( '\xa0', true ) );
span.style.whiteSpace = 'nowrap';
if ( list ) span.appendChild( list );
if ( this.engineSelector ) span.appendChild( this.engineSelector );
if ( !noSuggestions ) span.appendChild( this.icon );
span.appendChild( OK );
span.appendChild( cancel );
form.appendChild( span );
form.style.display = 'none';
this.span.appendChild( form );
},
display: function ( evt ) {
if ( this.isAddCategory && !onUpload && this.line ) {
// eslint-disable-next-line no-new
new CategoryEditor( this.line, null, this.span, true ); // Create a new one
}
if ( !commitButton && !onUpload ) {
for ( var i = 0; i < editors.length; i++ ) {
if ( editors[ i ].state !== CategoryEditor.UNCHANGED ) {
setMultiInput();
break;
}
}
}
if ( !this.form ) this.makeForm();
if ( this.list ) this.list.style.display = 'none';
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
this.currentCategory = this.lastSavedCategory;
this.currentExists = this.lastSavedExists;
this.currentHidden = this.lastSavedHidden;
this.currentKey = this.lastSavedKey;
this.icon.src = ( this.currentExists ? HC.existsYes : HC.existsNo );
this.text.value = this.currentCategory + ( this.currentKey !== null ? '|' + this.currentKey : '' );
this.originalState = this.state;
this.lastInput = this.currentCategory;
this.inputExists = this.currentExists;
this.state = this.state === CategoryEditor.UNCHANGED ? CategoryEditor.OPEN : CategoryEditor.CHANGE_PENDING;
this.lastSelection = {
start: this.currentCategory.length,
end: this.currentCategory.length
};
this.showsList = false;
// Display the form
if ( this.catLink ) this.catLink.style.display = 'none';
this.linkSpan.style.display = 'none';
this.form.style.display = 'inline';
this.ok.disabled = false;
// Kill the event before focussing, otherwise IE will kill the onfocus event!
var result = evtKill( evt );
this.text.focus();
this.text.readOnly = false;
checkMultiInput();
return result;
},
show: function ( evt, engine, readOnly ) {
var result = this.display( evt );
var v = this.lastSavedCategory;
if ( !v.length ) return result;
this.text.readOnly = !!readOnly;
this.engine = engine;
this.textchange( false, true ); // do autocompletion, force display of suggestions
forceRedraw();
return result;
},
open: function ( evt ) {
return this.show( evt, ( this.engine && suggestionConfigs[ this.engine ].temp ) ? HC.suggestions : this.engine );
},
down: function ( evt ) {
return this.show( evt, 'subcat', true );
},
up: function ( evt ) {
return this.show( evt, 'parentcat' );
},
cancel: function () {
if ( this.isAddCategory && !onUpload ) {
this.removeEditor(); // We added a new adder when opening
return;
}
// Close, re-display link
this.inactivate();
this.form.style.display = 'none';
if ( this.catLink ) this.catLink.style.display = '';
this.linkSpan.style.display = '';
this.state = this.originalState;
this.currentCategory = this.lastSavedCategory;
this.currentKey = this.lastSavedKey;
this.currentExists = this.lastSavedExists;
this.currentHidden = this.lastSavedHidden;
if ( this.catLink )
if ( this.currentKey && this.currentKey.length ) { this.catLink.title = this.currentKey; } else { this.catLink.title = ''; }
if ( this.state === CategoryEditor.UNCHANGED ) {
if ( this.catLink ) this.catLink.style.backgroundColor = 'transparent';
} else {
if ( !onUpload ) {
try {
this.catLink.style.backgroundColor = HC.bg_changed;
} catch ( ex ) {}
}
}
checkMultiInput();
forceRedraw();
},
removeEditor: function () {
if ( !newDOM ) {
var next = this.span.nextSibling;
if ( next ) next.parentNode.removeChild( next );
}
if (this.span && this.span.parentNode) {
this.span.parentNode.removeChild( this.span );
}
for ( var i = 0; i < editors.length; i++ ) {
if ( editors[ i ] === this ) {
editors.splice( i, 1 );
break;
}
}
checkMultiInput();
},
rollback: function ( evt ) {
this.undoLink.parentNode.removeChild( this.undoLink );
this.undoLink = null;
this.currentCategory = this.originalCategory;
this.currentKey = this.originalKey;
this.currentExists = this.originalExists;
this.currentHidden = this.originalHidden;
this.lastSavedCategory = this.originalCategory;
this.lastSavedKey = this.originalKey;
this.lastSavedExists = this.originalExists;
this.lastSavedHidden = this.originalHidden;
this.state = CategoryEditor.UNCHANGED;
if ( !this.currentCategory || !this.currentCategory.length ) {
// It was a newly added category. Remove the whole editor.
this.removeEditor();
} else {
// Redisplay the link...
this.catLink.removeChild( this.catLink.firstChild );
this.catLink.appendChild( make( this.currentCategory, true ) );
this.catLink.href = wikiPagePath( HC.category_canonical + ':' + this.currentCategory );
this.catLink.title = this.currentKey || '';
this.catLink.className = this.currentExists ? '' : 'new';
this.catLink.style.backgroundColor = 'transparent';
if ( this.upDownLinks ) this.upDownLinks.style.display = this.currentExists ? '' : 'none';
checkMultiInput();
}
return evtKill( evt );
},
inactivate: function () {
if ( this.list ) this.list.style.display = 'none';
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
this.is_active = false;
},
acceptCheck: function ( dontCheck ) {
this.sanitizeInput();
var value = this.text.value.split( '|' );
var key = null;
if ( value.length > 1 ) key = value[ 1 ];
var v = value[ 0 ].replace( /_/g, ' ' ).replace( /^\s+|\s+$/g, '' );
if ( HC.capitalizePageNames ) v = capitalize( v );
this.lastInput = v;
v = replaceShortcuts( v, HC.shortcuts );
if ( !v.length ) {
this.cancel();
return false;
}
if ( !dontCheck && (
conf.wgNamespaceNumber === 14 && v === conf.wgTitle || HC.blacklist && HC.blacklist.test( v ) ) ) {
this.cancel();
return false;
}
this.currentCategory = v;
this.currentKey = key;
this.currentExists = this.inputExists;
return true;
},
accept: function ( evt ) {
// eslint-disable-next-line no-bitwise
this.noCommit = ( evtKeys( evt ) & 1 ) !== 0;
var result = evtKill( evt );
if ( this.acceptCheck() ) {
var toResolve = [ this ];
var original = this.currentCategory;
resolveMulti( toResolve, function ( resolved ) {
if ( resolved[ 0 ].dab ) {
showDab( resolved[ 0 ] );
} else {
if ( resolved[ 0 ].acceptCheck( true ) ) {
resolved[ 0 ].commit(
( resolved[ 0 ].currentCategory !== original ) ?
HC.messages.cat_resolved.replace( /\$1/g, original ) :
null );
}
}
} );
}
return result;
},
close: function () {
if ( !this.catLink ) {
// Create a catLink
this.catLink = make( 'a' );
this.catLink.appendChild( make( 'foo', true ) );
this.catLink.style.display = 'none';
this.span.insertBefore( this.catLink, this.span.firstChild.nextSibling );
}
this.catLink.removeChild( this.catLink.firstChild );
this.catLink.appendChild( make( this.currentCategory, true ) );
this.catLink.href = wikiPagePath( HC.category_canonical + ':' + this.currentCategory );
this.catLink.className = this.currentExists ? '' : 'new';
this.lastSavedCategory = this.currentCategory;
this.lastSavedKey = this.currentKey;
this.lastSavedExists = this.currentExists;
this.lastSavedHidden = this.currentHidden;
// Close form and redisplay category
this.inactivate();
this.form.style.display = 'none';
this.catLink.title = this.currentKey || '';
this.catLink.style.display = '';
if ( this.isAddCategory ) {
if ( onUpload && this.line ) {
// eslint-disable-next-line no-new
new CategoryEditor( this.line, null, this.span, true ); // Create a new one
}
this.isAddCategory = false;
this.linkSpan.parentNode.removeChild( this.linkSpan );
this.makeLinkSpan();
this.span.appendChild( this.linkSpan );
}
if ( !this.undoLink ) {
// Append an undo link.
var span = make( 'span' );
var lk = make( 'a' );
lk.href = '#catlinks';
lk.onclick = this.rollback.bind( this );
lk.appendChild( make( HC.links.undo, true ) );
lk.title = HC.tooltips.undo;
span.appendChild( make( ' ', true ) );
span.appendChild( lk );
this.normalLinks.appendChild( span );
this.undoLink = span;
if ( !onUpload ) {
try {
this.catLink.style.backgroundColor = HC.bg_changed;
} catch ( ex ) {}
}
}
if ( this.upDownLinks ) this.upDownLinks.style.display = this.lastSavedExists ? '' : 'none';
this.linkSpan.style.display = '';
this.state = CategoryEditor.CHANGED;
checkMultiInput();
forceRedraw();
},
commit: function () {
// Check again to catch problem cases after redirect resolution
if (
(
this.currentCategory === this.originalCategory &&
(
this.currentKey === this.originalKey ||
this.currentKey === null && !this.originalKey.length
)
) ||
conf.wgNamespaceNumber === 14 && this.currentCategory === conf.wgTitle ||
HC.blacklist && HC.blacklist.test( this.currentCategory )
) {
this.cancel();
return;
}
this.close();
if ( !commitButton && !onUpload ) {
var self = this;
initiateEdit( function ( failure ) {
performChanges( failure, self );
}, function ( msg ) {
alert( msg );
} );
}
},
remove: function ( evt ) {
// eslint-disable-next-line no-bitwise
this.doRemove( evtKeys( evt ) & 1 );
return evtKill( evt );
},
doRemove: function ( noCommit ) {
if ( this.isAddCategory ) { // Empty input on adding a new category
this.cancel();
return;
}
if ( !commitButton && !onUpload ) {
for ( var i = 0; i < editors.length; i++ ) {
if ( editors[ i ].state !== CategoryEditor.UNCHANGED ) {
setMultiInput();
break;
}
}
}
if ( commitButton ) {
this.catLink.title = '';
this.catLink.style.cssText += '; text-decoration : line-through !important;';
try {
this.catLink.style.backgroundColor = HC.bg_changed;
} catch ( ex ) {}
this.originalState = this.state;
this.state = CategoryEditor.DELETED;
this.normalLinks.style.display = 'none';
this.undelLink.style.display = '';
checkMultiInput();
} else {
if ( onUpload ) {
// Remove this editor completely
this.removeEditor();
} else {
this.originalState = this.state;
this.state = CategoryEditor.DELETED;
this.noCommit = noCommit || HC.del_needs_diff;
var self = this;
initiateEdit(
function ( failure ) {
performChanges( failure, self );
},
function ( msg ) {
self.state = self.originalState;
alert( msg );
} );
}
}
},
restore: function ( evt ) {
// Can occur only if we do have a commit button and are not on the upload form
this.catLink.title = this.currentKey || '';
this.catLink.style.textDecoration = '';
this.state = this.originalState;
if ( this.state === CategoryEditor.UNCHANGED ) {
this.catLink.style.backgroundColor = 'transparent';
} else {
try {
this.catLink.style.backgroundColor = HC.bg_changed;
} catch ( ex ) {}
}
this.normalLinks.style.display = '';
this.undelLink.style.display = 'none';
checkMultiInput();
return evtKill( evt );
},
// Internal operations
selectEngine: function ( engineName ) {
if ( !this.engineSelector ) return;
for ( var i = 0; i < this.engineSelector.options.length; i++ ) this.engineSelector.options[ i ].selected = this.engineSelector.options[ i ].value === engineName;
},
sanitizeInput: function () {
var v = this.text.value || '';
v = v.replace( /^(\s|_)+/, '' ); // Trim leading blanks and underscores
var re = new RegExp( '^(' + HC.category_regexp + '):' );
if ( re.test( v ) ) v = v.substring( v.indexOf( ':' ) + 1 ).replace( /^(\s|_)+/, '' );
v = v.replace(/\u200E$/, ''); // Trim ending left-to-right mark
if ( HC.capitalizePageNames ) v = capitalize( v );
// Update the input field if needed, manually restoring the cursor afterwards
// (but not the whole selection, so that further typing does not overwrite anything)
if ( this.text.value !== null && this.text.value !== v ) {
var cursorPos = this.text.selectionStart;
this.text.value = v;
this.text.setSelectionRange( cursorPos, cursorPos );
}
},
makeCall: function ( url, callbackObj, engine, queryKey, cleanKey ) {
var cb = callbackObj,
e = engine,
v = queryKey,
z = cleanKey,
thisObj = this;
function done() {
cb.callsMade++;
if ( cb.callsMade === cb.nofCalls ) {
if ( cb.exists ) cb.allTitles.exists = true;
if ( cb.normalized ) cb.allTitles.normalized = cb.normalized;
if ( !cb.dontCache && !suggestionConfigs[ cb.engineName ].cache[ z ] ) suggestionConfigs[ cb.engineName ].cache[ z ] = cb.allTitles;
thisObj.text.readOnly = false;
if ( !cb.cancelled ) thisObj.showSuggestions( cb.allTitles, cb.noCompletion, v, cb.engineName );
if ( cb === thisObj.callbackObj ) thisObj.callbackObj = null;
cb = undefined;
}
}
$.getJSON( url, function ( json ) {
var titles = e.handler( json, z );
if ( titles && titles.length ) {
if ( cb.allTitles === null ) cb.allTitles = titles; else cb.allTitles = cb.allTitles.concat( titles );
if ( titles.exists ) cb.exists = true;
if ( titles.normalized ) cb.normalized = titles.normalized;
}
done();
} ).fail( function ( req ) {
if ( !req ) noSuggestions = true;
cb.dontCache = true;
done();
} );
},
callbackObj: null,
textchange: function ( dont_autocomplete, force ) {
// Hide all other lists
makeActive( this );
// Get input value, omit sort key, if any
this.sanitizeInput();
var v = this.text.value;
// Disregard anything after a pipe.
var pipe = v.indexOf( '|' );
if ( pipe >= 0 ) {
this.currentKey = v.substring( pipe + 1 );
v = v.substring( 0, pipe );
} else {
this.currentKey = null;
}
if ( this.lastInput === v && !force ) return; // No change
if ( this.lastInput !== v ) checkMultiInput();
this.lastInput = v;
this.lastRealInput = v;
// Mark blacklisted inputs.
this.ok.disabled = v.length && HC.blacklist && HC.blacklist.test( v );
if ( noSuggestions ) {
// No Ajax: just make sure the list is hidden
if ( this.list ) this.list.style.display = 'none';
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
if ( this.icon ) this.icon.style.display = 'none';
return;
}
if ( !v.length ) {
this.showSuggestions( [] );
return;
}
var cleanKey = v.replace( /[\u200E\u200F\u202A-\u202E]/g, '' ).replace( wikiTextBlankRE, ' ' );
cleanKey = replaceShortcuts( cleanKey, HC.shortcuts );
cleanKey = cleanKey.replace( /^\s+|\s+$/g, '' );
if ( !cleanKey.length ) {
this.showSuggestions( [] );
return;
}
if ( this.callbackObj ) this.callbackObj.cancelled = true;
var engineName = suggestionConfigs[ this.engine ] ? this.engine : 'combined';
dont_autocomplete = dont_autocomplete || suggestionConfigs[ engineName ].noCompletion;
if ( suggestionConfigs[ engineName ].cache[ cleanKey ] ) {
this.showSuggestions( suggestionConfigs[ engineName ].cache[ cleanKey ], dont_autocomplete, v, engineName );
return;
}
var engines = suggestionConfigs[ engineName ].engines;
this.callbackObj = {
allTitles: null,
callsMade: 0,
nofCalls: engines.length,
noCompletion: dont_autocomplete,
engineName: engineName
};
this.makeCalls( engines, this.callbackObj, v, cleanKey );
},
makeCalls: function ( engines, cb, v, cleanKey ) {
for ( var j = 0; j < engines.length; j++ ) {
var engine = suggestionEngines[ engines[ j ] ];
var url = wgServer + conf.wgScriptPath + engine.uri.replace( /\$1/g, encodeURIComponent( cleanKey ) );
this.makeCall( url, cb, engine, v, cleanKey );
}
},
showSuggestions: function ( titles, dontAutocomplete, queryKey, engineName ) {
this.text.readOnly = false;
this.dab = null;
this.showsList = false;
if ( !this.list ) return;
if ( noSuggestions ) {
if ( this.list ) this.list.style.display = 'none';
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
if ( this.icon ) this.icon.style.display = 'none';
this.inputExists = true; // Default...
return;
}
this.engineName = engineName;
if ( engineName ) {
if ( !this.engineSelector ) this.engineName = null;
} else {
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
}
if ( queryKey ) {
if ( this.lastInput.indexOf( queryKey ) ) return;
if ( this.lastQuery && this.lastInput.indexOf( this.lastQuery ) === 0 && this.lastQuery.length > queryKey.length ) return;
}
this.lastQuery = queryKey;
// Get current input text
var v = this.text.value.split( '|' );
var key = v.length > 1 ? '|' + v[ 1 ] : '';
v = ( HC.capitalizePageNames ? capitalize( v[ 0 ] ) : v[ 0 ] );
var vNormalized = v;
var knownToExist = titles && titles.exists;
var i;
if ( titles ) {
if ( titles.normalized && v.indexOf( queryKey ) === 0 ) {
// We got back a different normalization than what is in the input field
vNormalized = titles.normalized + v.substring( queryKey.length );
}
var vLow = vNormalized.toLowerCase();
// Strip blacklisted categories
if ( HC.blacklist ) {
for ( i = 0; i < titles.length; i++ ) {
if ( HC.blacklist.test( titles[ i ] ) ) {
titles.splice( i, 1 );
i--;
}
}
}
titles.sort(
function ( a, b ) {
if ( a === b ) return 0;
if ( a.indexOf( b ) === 0 ) return 1;
// a begins with b: a > b
if ( b.indexOf( a ) === 0 ) return -1;
// b begins with a: a < b
// Opensearch may return stuff not beginning with the search prefix!
var prefixMatchA = ( a.indexOf( vNormalized ) === 0 ? 1 : 0 );
var prefixMatchB = ( b.indexOf( vNormalized ) === 0 ? 1 : 0 );
if ( prefixMatchA !== prefixMatchB ) return prefixMatchB - prefixMatchA;
// Case-insensitive prefix match!
var aLow = a.toLowerCase(),
bLow = b.toLowerCase();
prefixMatchA = ( aLow.indexOf( vLow ) === 0 ? 1 : 0 );
prefixMatchB = ( bLow.indexOf( vLow ) === 0 ? 1 : 0 );
if ( prefixMatchA !== prefixMatchB ) return prefixMatchB - prefixMatchA;
if ( a < b ) return -1;
if ( b < a ) return 1;
return 0;
} );
// Remove duplicates and self-references
for ( i = 0; i < titles.length; i++ ) {
if (
i + 1 < titles.length && titles[ i ] === titles[ i + 1 ] ||
conf.wgNamespaceNumber === 14 && titles[ i ] === conf.wgTitle
) {
titles.splice( i, 1 );
i--;
}
}
}
if ( !titles || !titles.length ) {
if ( this.list ) this.list.style.display = 'none';
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
if ( engineName && suggestionConfigs[ engineName ] && !suggestionConfigs[ engineName ].temp ) {
if ( this.icon ) this.icon.src = HC.existsNo;
this.inputExists = false;
}
return;
}
var firstTitle = titles[ 0 ];
var completed = this.autoComplete( firstTitle, v, vNormalized, key, dontAutocomplete );
var existing = completed || knownToExist || firstTitle === replaceShortcuts( v, HC.shortcuts );
if ( engineName && suggestionConfigs[ engineName ] && !suggestionConfigs[ engineName ].temp ) {
this.icon.src = ( existing ? HC.existsYes : HC.existsNo );
this.inputExists = existing;
}
if ( completed ) {
this.lastInput = firstTitle;
if ( titles.length === 1 ) {
this.list.style.display = 'none';
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
return;
}
}
// (Re-)fill the list
while ( this.list.firstChild ) this.list.removeChild( this.list.firstChild );
for ( i = 0; i < titles.length; i++ ) {
var opt = make( 'option' );
opt.appendChild( make( titles[ i ], true ) );
opt.selected = completed && ( i === 0 );
this.list.appendChild( opt );
}
this.displayList();
},
displayList: function () {
this.showsList = true;
if ( !this.is_active ) {
this.list.style.display = 'none';
if ( this.engineSelector ) this.engineSelector.style.display = 'none';
return;
}
var nofItems = ( this.list.options.length > HC.listSize ? HC.listSize : this.list.options.length );
if ( nofItems <= 1 ) nofItems = 2;
this.list.size = nofItems;
this.list.style.align = is_rtl ? 'right' : 'left';
this.list.style.zIndex = 5;
this.list.style.position = 'absolute';
// Compute initial list position. First the height.
var anchor = is_rtl ? 'right' : 'left';
var listh = 0;
if ( this.list.style.display === 'none' ) {
// Off-screen display to get the height
this.list.style.top = this.text.offsetTop + 'px';
this.list.style[ anchor ] = '-10000px';
this.list.style.display = '';
listh = this.list.offsetHeight;
this.list.style.display = 'none';
} else {
listh = this.list.offsetHeight;
}
// Approximate calculation of maximum list size
var maxListHeight = listh;
if ( nofItems < HC.listSize ) maxListHeight = ( listh / nofItems ) * HC.listSize;
function viewport( what ) {
if ( is_webkit && !document.evaluate ) {
// Safari < 3.0
return window[ 'inner' + what ];
}
var s = 'client' + what;
if ( window.opera ) return document.body[ s ];
return ( document.documentElement ? document.documentElement[ s ] : 0 ) || document.body[ s ] || 0;
}
function scroll_offset( what ) {
var s = 'scroll' + what;
var result = ( document.documentElement ? document.documentElement[ s ] : 0 ) || document.body[ s ] || 0;
if ( is_rtl && what === 'Left' ) {
// RTL inconsistencies.
// FF: 0 at the far right, then increasingly negative values.
// IE >= 8: 0 at the far right, then increasingly positive values.
// Webkit: scrollWidth - clientWidth at the far right, then down to zero.
// Opera: don't know...
if ( result < 0 ) result = -result;
if ( !is_webkit ) result = scroll_offset( 'Width' ) - viewport( 'Width' ) - result;
// Now all have webkit behavior, i.e. zero if at the leftmost edge.
}
return result;
}
function position( node ) {
// Stripped-down simplified position function. It's good enough for our purposes.
if ( node.getBoundingClientRect ) {
var box = node.getBoundingClientRect();
return {
x: Math.round( box.left + scroll_offset( 'Left' ) ),
y: Math.round( box.top + scroll_offset( 'Top' ) )
};
}
var t = 0,
l = 0;
do {
t += ( node.offsetTop || 0 );
l += ( node.offsetLeft || 0 );
node = node.offsetParent;
} while ( node );
return {
x: l,
y: t
};
}
var textPos = position( this.text ),
nl = 0,
nt = 0,
offset = 0,
// Opera 9.5 somehow has offsetWidth = 0 here?? Use the next best value...
textBoxWidth = this.text.offsetWidth || this.text.clientWidth;
if ( this.engineName ) {
this.engineSelector.style.zIndex = 5;
this.engineSelector.style.position = 'absolute';
this.engineSelector.style.width = textBoxWidth + 'px';
// Figure out the height of this selector: display it off-screen, then hide it again.
if ( this.engineSelector.style.display === 'none' ) {
this.engineSelector.style[ anchor ] = '-10000px';
this.engineSelector.style.top = '0';
this.engineSelector.style.display = '';
offset = this.engineSelector.offsetHeight;
this.engineSelector.style.display = 'none';
} else {
offset = this.engineSelector.offsetHeight;
}
this.engineSelector.style[ anchor ] = nl + 'px';
}
if ( textPos.y < maxListHeight + offset + 1 ) {
// The list might extend beyond the upper border of the page. Let's avoid that by placing it
// below the input text field.
nt = this.text.offsetHeight + offset + 1;
if ( this.engineName ) this.engineSelector.style.top = this.text.offsetHeight + 'px';
} else {
nt = -listh - offset - 1;
if ( this.engineName ) this.engineSelector.style.top = -( offset + 1 ) + 'px';
}
this.list.style.top = nt + 'px';
this.list.style.width = ''; // No fixed width (yet)
this.list.style[ anchor ] = nl + 'px';
if ( this.engineName ) {
this.selectEngine( this.engineName );
this.engineSelector.style.display = '';
}
this.list.style.display = 'block';
// Set the width of the list
if ( this.list.offsetWidth < textBoxWidth ) {
this.list.style.width = textBoxWidth + 'px';
return;
}
// If the list is wider than the textbox: make sure it fits horizontally into the browser window
var scroll = scroll_offset( 'Left' );
var view_w = viewport( 'Width' );
var w = this.list.offsetWidth;
var l_pos = position( this.list );
var left = l_pos.x;
var right = left + w;
if ( left < scroll || right > scroll + view_w ) {
if ( w > view_w ) {
w = view_w;
this.list.style.width = w + 'px';
if ( is_rtl ) left = right - w; else right = left + w;
}
var relative_offset = 0;
if ( left < scroll ) relative_offset = scroll - left; else if ( right > scroll + view_w ) relative_offset = -( right - scroll - view_w );
if ( is_rtl ) relative_offset = -relative_offset;
if ( relative_offset ) this.list.style[ anchor ] = ( nl + relative_offset ) + 'px';
}
},
autoComplete: function ( newVal, actVal, normalizedActVal, key, dontModify ) {
if ( newVal === actVal ) return true;
if ( dontModify || this.ime || !this.canSelect() ) return false;
// If we can't select properly or an IME composition is ongoing, autocompletion would be a major annoyance to the user.
if ( newVal.indexOf( actVal ) ) {
// Maybe it'll work with the normalized value (NFC)?
if ( normalizedActVal && newVal.indexOf( normalizedActVal ) === 0 ) {
if ( this.lastRealInput === actVal ) this.lastRealInput = normalizedActVal;
actVal = normalizedActVal;
} else {
return false;
}
}
// Actual input is a prefix of the new text. Fill in new text, selecting the newly added suffix
// such that it can be easily removed by typing backspace if the suggestion is unwanted.
this.text.focus();
this.text.value = newVal + key;
this.setSelection( actVal.length, newVal.length );
return true;
},
canSelect: function () {
return this.text.setSelectionRange ||
this.text.createTextRange ||
this.text.selectionStart !== undefined &&
this.text.selectionEnd !== undefined;
},
setSelection: function ( from, to ) {
// this.text must be focused (at least on IE)
if ( !this.text.value ) return;
if ( this.text.setSelectionRange ) { // e.g. khtml
this.text.setSelectionRange( from, to );
} else if ( this.text.selectionStart !== undefined ) {
if ( from > this.text.selectionStart ) {
this.text.selectionEnd = to;
this.text.selectionStart = from;
} else {
this.text.selectionStart = from;
this.text.selectionEnd = to;
}
} else if ( this.text.createTextRange ) { // IE
var new_selection = this.text.createTextRange();
new_selection.move( 'character', from );
new_selection.moveEnd( 'character', to - from );
new_selection.select();
}
},
getSelection: function () {
var from = 0,
to = 0;
// this.text must be focused (at least on IE)
if ( !this.text.value ) {
// No text.
} else if ( this.text.selectionStart !== undefined ) {
from = this.text.selectionStart;
to = this.text.selectionEnd;
} else if ( document.selection && document.selection.createRange ) { // IE
var rng = document.selection.createRange().duplicate();
if ( rng.parentElement() === this.text ) {
try {
var textRng = this.text.createTextRange();
textRng.move( 'character', 0 );
textRng.setEndPoint( 'EndToEnd', rng );
// We're in a single-line input box: no need to care about IE's strange
// handling of line ends
to = textRng.text.length;
textRng.setEndPoint( 'EndToStart', rng );
from = textRng.text.length;
} catch ( notFocused ) {
from = this.text.value.length;
to = from; // At end of text
}
}
}
return {
start: from,
end: to
};
},
saveView: function () {
this.lastSelection = this.getSelection();
},
processKey: function ( evt ) {
var dir = 0;
switch ( this.lastKey ) {
case UP:
dir = -1;
break;
case DOWN:
dir = 1;
break;
case PGUP:
dir = -HC.listSize;
break;
case PGDOWN:
dir = HC.listSize;
break;
case ESC: // Inhibit default behavior (revert to last real input in FF: we do that ourselves)
return evtKill( evt );
}
if ( dir ) {
if ( this.list.style.display !== 'none' ) {
// List is visible, so there are suggestions
this.highlightSuggestion( dir );
// Kill the event, otherwise some browsers (e.g., Firefox) may additionally treat an up-arrow
// as "place the text cursor at the front", which we don't want here.
return evtKill( evt );
} else if (
this.keyCount <= 1 &&
( !this.callbackObj || this.callbackObj.callsMade === this.callbackObj.nofCalls )
) {
// If no suggestions displayed, get them, unless we're already getting them.
this.textchange();
}
}
return true;
},
highlightSuggestion: function ( dir ) {
if ( noSuggestions || !this.list || this.list.style.display === 'none' ) return false;
var curr = this.list.selectedIndex;
var tgt = -1;
if ( dir === 0 ) {
if ( curr < 0 || curr >= this.list.options.length ) return false;
tgt = curr;
} else {
tgt = curr < 0 ? 0 : curr + dir;
tgt = tgt < 0 ? 0 : tgt;
if ( tgt >= this.list.options.length ) tgt = this.list.options.length - 1;
}
if ( tgt !== curr || dir === 0 ) {
if ( curr >= 0 && curr < this.list.options.length && dir !== 0 ) this.list.options[ curr ].selected = false;
this.list.options[ tgt ].selected = true;
// Get current input text
var v = this.text.value.split( '|' );
var key = v.length > 1 ? '|' + v[ 1 ] : '';
var completed = this.autoComplete( this.list.options[ tgt ].text, this.lastRealInput, null, key, false );
if ( !completed || this.list.options[ tgt ].text === this.lastRealInput ) {
this.text.value = this.list.options[ tgt ].text + key;
if ( this.canSelect() ) this.setSelection( this.list.options[ tgt ].text.length, this.list.options[ tgt ].text.length );
}
this.lastInput = this.list.options[ tgt ].text;
this.inputExists = true; // Might be wrong if from a dab list...
if ( this.icon ) this.icon.src = HC.existsYes;
this.state = CategoryEditor.CHANGE_PENDING;
}
return true;
},
resetKeySelection: function () {
if ( noSuggestions || !this.list || this.list.style.display === 'none' ) return false;
var curr = this.list.selectedIndex;
if ( curr >= 0 && curr < this.list.options.length ) {
this.list.options[ curr ].selected = false;
// Get current input text
var v = this.text.value.split( '|' );
var key = v.length > 1 ? '|' + v[ 1 ] : '';
// ESC is handled strangely by some browsers (e.g., FF); somehow it resets the input value before
// our event handlers ever get a chance to run.
var result = v[ 0 ] !== this.lastInput;
if ( v[ 0 ] !== this.lastRealInput ) {
this.text.value = this.lastRealInput + key;
result = true;
}
this.lastInput = this.lastRealInput;
return result;
}
return false;
}
}; // end CategoryEditor.prototype
function initialize() {
// User configurations: Do this here, called from the onload handler, so that users can
// override it easily in their own user script files by just declaring variables. JSconfig
// is some feature used at Wikimedia Commons.
var config = ( window.JSconfig !== undefined && JSconfig.keys ) ? JSconfig.keys : {};
HC.dont_add_to_watchlist = ( window.hotcat_dont_add_to_watchlist !== undefined ?
!!window.hotcat_dont_add_to_watchlist :
( config.HotCatDontAddToWatchlist !== undefined ? config.HotCatDontAddToWatchlist :
HC.dont_add_to_watchlist ) );
HC.no_autocommit = ( window.hotcat_no_autocommit !== undefined ?
!!window.hotcat_no_autocommit : ( config.HotCatNoAutoCommit !== undefined ?
config.HotCatNoAutoCommit :
// On talk namespace default autocommit off
( conf.wgNamespaceNumber % 2 ?
true : HC.no_autocommit ) ) );
HC.del_needs_diff = ( window.hotcat_del_needs_diff !== undefined ?
!!window.hotcat_del_needs_diff :
( config.HotCatDelNeedsDiff !== undefined ?
config.HotCatDelNeedsDiff :
HC.del_needs_diff ) );
HC.suggest_delay = window.hotcat_suggestion_delay || config.HotCatSuggestionDelay || HC.suggest_delay;
HC.editbox_width = window.hotcat_editbox_width || config.HotCatEditBoxWidth || HC.editbox_width;
HC.suggestions = window.hotcat_suggestions || config.HotCatSuggestions || HC.suggestions;
if ( typeof HC.suggestions !== 'string' || !suggestionConfigs[ HC.suggestions ] ) HC.suggestions = 'combined';
HC.fixed_search = ( window.hotcat_suggestions_fixed !== undefined ?
!!window.hotcat_suggestions_fixed : ( config.HotCatFixedSuggestions !== undefined ?
config.HotCatFixedSuggestions : HC.fixed_search ) );
HC.single_minor = ( window.hotcat_single_changes_are_minor !== undefined ?
!!window.hotcat_single_changes_are_minor :
( config.HotCatMinorSingleChanges !== undefined ?
config.HotCatMinorSingleChanges :
HC.single_minor ) );
HC.bg_changed = window.hotcat_changed_background || config.HotCatChangedBackground || HC.bg_changed;
HC.use_up_down = ( window.hotcat_use_category_links !== undefined ?
!!window.hotcat_use_category_links :
( config.HotCatUseCategoryLinks !== undefined ?
config.HotCatUseCategoryLinks :
HC.use_up_down ) );
HC.listSize = window.hotcat_list_size || config.HotCatListSize || HC.listSize;
if ( conf.wgDBname !== 'commonswiki' ) HC.changeTag = config.HotCatChangeTag || '';
// The next whole shebang is needed, because manual tags get not submitted except of save
if ( HC.changeTag ) {
var eForm = document.editform,
catRegExp = new RegExp( '^\\[\\[(' + HC.category_regexp + '):' ),
oldTxt;
// Returns true if minor change
var isMinorChange = function () {
var newTxt = eForm.wpTextbox1;
if ( !newTxt ) return;
newTxt = newTxt.value;
var oldLines = oldTxt.match( /^.*$/gm ),
newLines = newTxt.match( /^.*$/gm ),
cArr; // changes
var except = function ( aArr, bArr ) {
var result = [],
lArr, // larger
sArr; // smaller
if ( aArr.length < bArr.length ) {
lArr = bArr;
sArr = aArr;
} else {
lArr = aArr;
sArr = bArr;
}
for ( var i = 0; i < lArr.length; i++ ) {
var item = lArr[ i ];
var ind = $.inArray( item, sArr );
if ( ind === -1 ) result.push( item );
else sArr.splice( ind, 1 ); // don't check this item again
}
return result.concat( sArr );
};
cArr = except( oldLines, newLines );
if ( cArr.length ) {
cArr = $.grep( cArr, function ( c ) {
c = $.trim( c );
return ( c && !catRegExp.test( c ) );
} );
}
if ( !cArr.length ) {
oldTxt = newTxt;
return true;
}
};
if ( conf.wgAction === 'submit' && conf.wgArticleId && eForm && eForm.wpSummary && document.getElementById( 'wikiDiff' ) ) {
var sum = eForm.wpSummary,
sumA = eForm.wpAutoSummary;
if ( sum.value && sumA.value === HC.changeTag ) { // HotCat diff
// MD5 hash of the empty string, as HotCat edit is based on empty sum
sumA.value = sumA.value.replace( HC.changeTag, 'd41d8cd98f00b204e9800998ecf8427e' );
// Attr creation and event handling is not same in all (old) browsers so use $
var $ct = $( '<input type="ပၞုက်" name="wpChangeTags">' ).val( HC.changeTag );
$( eForm ).append( $ct );
oldTxt = eForm.wpTextbox1.value;
$( '#wpSave' ).one( 'click', function () {
if ( $ct.val() )
sum.value = sum.value.replace( ( HC.messages.using || HC.messages.prefix ), '' );
} );
var removeChangeTag = function () {
$( eForm.wpTextbox1 ).add( sum ).one( 'input', function () {
window.setTimeout( function () {
if ( !isMinorChange() ) $ct.val( '' );
else removeChangeTag();
}, 500 );
} );
};
removeChangeTag();
}
}
}
// Numeric input, make sure we have a numeric value
HC.listSize = parseInt( HC.listSize, 10 );
if ( isNaN( HC.listSize ) || HC.listSize < 5 ) HC.listSize = 5;
HC.listSize = Math.min( HC.listSize, 30 ); // Max size
// Localize search engine names
if ( HC.engine_names ) {
for ( var key in HC.engine_names )
if ( suggestionConfigs[ key ] && HC.engine_names[ key ] ) suggestionConfigs[ key ].name = HC.engine_names[ key ];
}
// Catch both native RTL and "faked" RTL through [[MediaWiki:Rtl.js]]
is_rtl = hasClass( document.body, 'rtl' );
if ( !is_rtl ) {
if ( document.defaultView && document.defaultView.getComputedStyle ) { // Gecko etc.
is_rtl = document.defaultView.getComputedStyle( document.body, null ).getPropertyValue( 'direction' );
} else if ( document.body.currentStyle ) { // IE, has subtle differences to getComputedStyle
is_rtl = document.body.currentStyle.direction;
} else { // Not exactly right, but best effort
is_rtl = document.body.style.direction;
}
is_rtl = ( is_rtl === 'rtl' );
}
}
function can_edit() {
var container = null;
switch ( mw.config.get( 'skin' ) ) {
case 'cologneblue':
container = document.getElementById( 'quickbar' );
/* fall through */
case 'standard':
case 'nostalgia':
if ( !container ) container = document.getElementById( 'topbar' );
var lks = container.getElementsByTagName( 'a' );
for ( var i = 0; i < lks.length; i++ ) {
if (
param( 'title', lks[ i ].href ) === conf.wgPageName &&
param( 'action', lks[ i ].href ) === 'ပလေဝ်ဒါန်'
) {
return true;
}
}
return false;
default:
// all modern skins:
return document.getElementById( 'ca-edit' ) !== null;
}
}
// Legacy stuff
function closeForm() {
// Close all open editors without redirect resolution and other asynchronous stuff.
for ( var i = 0; i < editors.length; i++ ) {
var edit = editors[ i ];
if ( edit.state === CategoryEditor.OPEN ) {
edit.cancel();
} else if ( edit.state === CategoryEditor.CHANGE_PENDING ) {
edit.sanitizeInput();
var value = edit.text.value.split( '|' );
var key = null;
if ( value.length > 1 ) key = value[ 1 ];
var v = value[ 0 ].replace( /_/g, ' ' ).replace( /^\s+|\s+$/g, '' );
if ( !v.length ) {
edit.cancel();
} else {
edit.currentCategory = v;
edit.currentKey = key;
edit.currentExists = this.inputExists;
edit.close();
}
}
}
}
function setup_upload() {
onUpload = true;
// Add an empty category bar at the end of the table containing the description, and change the onsubmit handler.
var ip = document.getElementById( 'mw-htmlform-description' ) || document.getElementById( 'wpDestFile' );
if ( !ip ) {
ip = document.getElementById( 'wpDestFile' );
while ( ip && ip.nodeName.toLowerCase() !== 'table' ) ip = ip.parentNode;
}
if ( !ip ) return;
var reupload = document.getElementById( 'wpForReUpload' );
var destFile = document.getElementById( 'wpDestFile' );
if (
( reupload && !!reupload.value ) ||
( destFile && ( destFile.disabled || destFile.readOnly ) )
) {
return; // re-upload form...
}
// Insert a table row with two fields (label and empty category bar)
var labelCell = make( 'td' );
var lineCell = make( 'td' );
// Create the category line
catLine = make( 'div' );
catLine.className = 'catlinks';
catLine.id = 'catlinks';
catLine.style.textAlign = is_rtl ? 'right' : 'left';
// We'll be inside a table row. Make sure that we don't have margins or strange borders.
catLine.style.margin = '0';
catLine.style.border = 'none';
lineCell.appendChild( catLine );
// Create the label
var label = null;
if ( window.UFUI && window.UIElements && UFUI.getLabel instanceof Function ) {
try {
label = UFUI.getLabel( 'wpCategoriesUploadLbl' );
} catch ( ex ) {
label = null;
}
}
if ( !label ) {
labelCell.id = 'hotcatLabel';
labelCell.appendChild( make( HC.categories, true ) );
} else {
labelCell.id = 'hotcatLabelTranslated';
labelCell.appendChild( label );
}
labelCell.className = 'mw-label';
labelCell.style.textAlign = 'right';
labelCell.style.verticalAlign = 'middle';
// Change the onsubmit handler
var form = document.getElementById( 'upload' ) || document.getElementById( 'mw-upload-form' );
if ( form && ip && ip.insertRow ) {
var newRow = ip.insertRow( -1 );
newRow.appendChild( labelCell );
newRow.appendChild( lineCell );
form.onsubmit = ( function ( oldSubmit ) {
return function () {
var do_submit = true;
if ( oldSubmit ) {
if ( typeof oldSubmit === 'string' ) {
// eslint-disable-next-line no-eval
do_submit = eval( oldSubmit );
} else if ( oldSubmit instanceof Function ) {
do_submit = oldSubmit.apply( form, arguments );
}
}
if ( !do_submit ) return false;
closeForm();
// Copy the categories
var eb = document.getElementById( 'wpUploadDescription' ) || document.getElementById( 'wpDesc' );
var addedOne = false;
for ( var i = 0; i < editors.length; i++ ) {
var t = editors[ i ].currentCategory;
if ( !t ) continue;
var key = editors[ i ].currentKey;
var new_cat = '[[' + HC.category_canonical + ':' + t + ( key ? '|' + key : '' ) + ']]';
// Only add if not already present
var cleanedText = eb.value
.replace( /<!--(\s|\S)*?-->/g, '' )
.replace( /<nowiki>(\s|\S)*?<\/nowiki>/g, '' );
if ( !find_category( cleanedText, t, true ) ) {
eb.value += '\n' + new_cat;
addedOne = true;
}
}
if ( addedOne ) {
// Remove "subst:unc" added by Flinfo if it didn't find categories
eb.value = eb.value.replace( /\{\{subst:unc\}\}/g, '' );
}
return true;
};
}( form.onsubmit ) );
}
}
var cleanedText = null;
function isOnPage( span ) {
if ( span.firstChild.nodeType !== Node.ELEMENT_NODE ) return null;
var catTitle = title( span.firstChild.getAttribute( 'href' ) );
if ( !catTitle ) return null;
catTitle = catTitle.substr( catTitle.indexOf( ':' ) + 1 ).replace( /_/g, ' ' );
if ( HC.blacklist && HC.blacklist.test( catTitle ) ) return null;
var result = {
title: catTitle,
match: [ '', '', '' ]
};
if ( pageText === null ) return result;
if ( cleanedText === null ) {
cleanedText = pageText
.replace( /<!--(\s|\S)*?-->/g, '' )
.replace( /<nowiki>(\s|\S)*?<\/nowiki>/g, '' );
}
result.match = find_category( cleanedText, catTitle, true );
return result;
}
var initialized = false;
var setupTimeout = null;
function findByClass( scope, tag, className ) {
var result = $( scope ).find( tag + '.' + className );
return ( result && result.length ) ? result[ 0 ] : null;
}
function errorAMC() {
alert( 'An error occurred. Unable to setup HotCat' );
}
function enableAMC() {
var api = new mw.Api();
return api.saveOption( 'mf_amc_optin', '1' ).then( function ( r ) {
if ( !r || r.options !== 'success' ) {
errorAMC();
return;
}
if ( window.confirm( 'Please reload your page to use hotcat.' ) ) {
location.reload();
}
}, function () {
errorAMC();
} );
}
function showWarning( text ) {
var warning = document.createElement( 'div' );
warning.setAttribute( 'style', 'padding: 20px; background: var(--wikt-palette-amber-0,orange); color: var(--wikt-palette-black,#333); font-weight: bold; margin-top: 20px;' );
warning.textContent = text;
var btn = document.createElement( 'button' );
btn.classList.add( 'mw-ui-button', 'cdx-button' );
btn.style.display = 'block';
btn.textContent = 'Enable HotCat and AMC mode on this page';
btn.addEventListener( 'click', function () {
enableAMC();
} );
warning.appendChild( btn );
document.getElementById( 'mw-content-text' ).appendChild( warning );
}
function setup( additionalWork ) {
if ( initialized ) return;
initialized = true;
if ( setupTimeout ) {
window.clearTimeout( setupTimeout );
setupTimeout = null;
}
// Find the category bar, or create an empty one if there isn't one. Then add -/+- links after
// each category, and add the + link.
catLine =
// Special:Upload
catLine ||
document.getElementById( 'mw-normal-catlinks' );
var hiddenCats = document.getElementById( 'mw-hidden-catlinks' );
if ( !catLine ) {
// Workaround for T24660
if ( mw.config.get('skin') === 'minerva' ) {
if ( document.body.classList.contains('mw-mf-amc-disabled') ) {
showWarning( 'HotCat requires AMC mode.' );
}
}
var footer = null;
if ( !hiddenCats ) {
footer = findByClass( document, 'div', 'printfooter' );
if ( !footer ) return; // Don't know where to insert the category line
}
catLine = make( 'div' );
catLine.id = 'mw-normal-catlinks';
catLine.style.textAlign = is_rtl ? 'right' : 'left';
// Add a label
var label = make( 'a' );
label.href = conf.wgArticlePath.replace( '$1', 'Special:Categories' );
label.title = HC.categories;
label.appendChild( make( HC.categories, true ) );
catLine.appendChild( label );
catLine.appendChild( make( ':', true ) );
// Insert the new category line
var container = ( hiddenCats ? hiddenCats.parentNode : document.getElementById( 'catlinks' ) );
if ( !container ) {
container = make( 'div' );
container.id = 'catlinks';
footer.parentNode.insertBefore( container, footer.nextSibling );
}
container.className = 'catlinks noprint';
container.style.display = '';
if ( !hiddenCats ) container.appendChild( catLine ); else container.insertBefore( catLine, hiddenCats );
} // end if catLine exists
if ( is_rtl ) catLine.dir = 'rtl';
// Create editors for all existing categories
function createEditors( line, is_hidden ) {
var i;
var cats = line.getElementsByTagName( 'li' );
if ( cats.length ) {
newDOM = true;
line = cats[ 0 ].parentNode;
} else {
cats = line.getElementsByTagName( 'span' );
}
// Copy cats, otherwise it'll also magically contain our added spans as it is a live collection!
var copyCats = new Array( cats.length );
for ( i = 0; i < cats.length; i++ ) copyCats[ i ] = cats[ i ];
for ( i = 0; i < copyCats.length; i++ ) {
var test = isOnPage( copyCats[ i ] );
if ( test !== null && test.match !== null && line ) {
// eslint-disable-next-line no-new
new CategoryEditor( line, copyCats[ i ], test.title, test.match[ 2 ], is_hidden );
}
}
return copyCats.length ? copyCats[ copyCats.length - 1 ] : null;
}
var lastSpan = createEditors( catLine, false );
// Create one to add a new category
// eslint-disable-next-line no-new
new CategoryEditor( newDOM ? catLine.getElementsByTagName( 'ul' )[ 0 ] : catLine, null, null, lastSpan !== null, false );
if ( !onUpload ) {
if ( pageText !== null && hiddenCats ) {
if ( is_rtl ) hiddenCats.dir = 'rtl';
createEditors( hiddenCats, true );
}
// And finally add the "multi-mode" span. (Do this at the end, otherwise it ends up in the list above.)
var enableMulti = make( 'span' );
enableMulti.className = 'noprint';
if ( is_rtl ) enableMulti.dir = 'rtl';
catLine.insertBefore( enableMulti, catLine.firstChild.nextSibling );
enableMulti.appendChild( make( '\xa0', true ) ); // nbsp
multiSpan = make( 'span' );
enableMulti.appendChild( multiSpan );
multiSpan.innerHTML = '(<a>' + HC.addmulti + '</a>)';
var lk = multiSpan.getElementsByTagName( 'a' )[ 0 ];
lk.onclick = function ( evt ) {
setMultiInput();
checkMultiInput();
return evtKill( evt );
};
lk.title = HC.multi_tooltip;
lk.style.cursor = 'pointer';
}
cleanedText = null;
if ( additionalWork instanceof Function ) additionalWork();
mw.hook( 'hotcat.ready' ).fire(); // Execute registered callback functions
$( 'body' ).trigger( 'hotcatSetupCompleted' );
}
function createCommitForm() {
if ( commitForm ) return;
var formContainer = make( 'div' );
formContainer.style.display = 'none';
document.body.appendChild( formContainer );
formContainer.innerHTML =
'<form id="hotcatCommitForm" method="post" enctype="multipart/form-data" action="' +
conf.wgScript + '?title=' + encodeURIComponent( conf.wgPageName ) + '&action=submit">' +
'<input type="hidden" name="wpTextbox1">' +
'<input type="hidden" name="model" value="' + conf.wgPageContentModel + '">' +
'<input type="hidden" name="format" value="text/x-wiki">' +
'<input type="hidden" name="wpSummary" value="">' +
'<input type="checkbox" name="wpMinoredit" value="1">' +
'<input type="checkbox" name="wpWatchthis" value="1">' +
'<input type="hidden" name="wpAutoSummary" value="d41d8cd98f00b204e9800998ecf8427e">' +
'<input type="hidden" name="wpEdittime">' +
'<input type="hidden" name="wpStarttime">' +
'<input type="hidden" name="wpDiff" value="wpDiff">' +
'<input type="hidden" name="oldid" value="0">' +
'<input type="hidden" name="wpIgnoreBlankSummary" value="1">' +
'<input type="submit" name="hcCommit" value="hcCommit">' +
'<input type="hidden" name="wpEditToken">' +
'<input type="hidden" name="wpUltimateParam" value="1">' +
'<input type="hidden" name="wpChangeTags">' +
'<input type="hidden" value="ℳ𝒲♥𝓊𝓃𝒾𝒸ℴ𝒹ℯ" name="wpUnicodeCheck">' +
'</form>';
commitForm = document.getElementById( 'hotcatCommitForm' );
}
function getPage() {
// We know we have an article here.
if ( !conf.wgArticleId ) {
// Doesn't exist yet. Disable on non-existing User pages -- might be a global user page.
if ( conf.wgNamespaceNumber === 2 ) return;
pageText = '';
pageTime = null;
setup( createCommitForm );
} else {
var url = wgServer + conf.wgScriptPath + '/api.php?format=json&callback=HotCat.start&action=query&rawcontinue=&titles=' +
encodeURIComponent( conf.wgPageName ) +
'&prop=info%7Crevisions&rvprop=content%7Ctimestamp%7Cids&meta=siteinfo&rvlimit=1&rvstartid=' +
conf.wgCurRevisionId;
var s = make( 'script' );
s.src = url;
HC.start = function ( json ) {
setPage( json );
setup( createCommitForm );
};
document.getElementsByTagName( 'head' )[ 0 ].appendChild( s );
setupTimeout = window.setTimeout( function () {
setup( createCommitForm );
}, 4000 ); // 4 sec, just in case getting the wikitext takes longer.
}
}
function setState( state ) {
var cats = state.split( '\n' );
if ( !cats.length ) return null;
if ( initialized && editors.length === 1 && editors[ 0 ].isAddCategory ) {
// Insert new spans and create new editors for them.
var newSpans = [];
var before = editors.length === 1 ? editors[ 0 ].span : null;
var i;
for ( i = 0; i < cats.length; i++ ) {
if ( !cats[ i ].length ) continue;
var cat = cats[ i ].split( '|' );
var key = cat.length > 1 ? cat[ 1 ] : null;
cat = cat[ 0 ];
var lk = make( 'a' );
lk.href = wikiPagePath( HC.category_canonical + ':' + cat );
lk.appendChild( make( cat, true ) );
lk.title = cat;
var span = make( 'span' );
span.appendChild( lk );
if ( !i ) catLine.insertBefore( make( ' ', true ), before );
catLine.insertBefore( span, before );
if ( before && i + 1 < cats.length ) parent.insertBefore( make( ' | ', true ), before );
newSpans.push( {
element: span,
title: cat,
key: key
} );
}
// And change the last one...
if ( before ) before.parentNode.insertBefore( make( ' | ', true ), before );
for ( i = 0; i < newSpans.length; i++ ) {
// eslint-disable-next-line no-new
new CategoryEditor( catLine, newSpans[ i ].element, newSpans[ i ].title, newSpans[ i ].key );
}
}
return null;
}
function getState() {
var result = null;
for ( var i = 0; i < editors.length; i++ ) {
var text = editors[ i ].currentCategory;
var key = editors[ i ].currentKey;
if ( text && text.length ) {
if ( key !== null ) text += '|' + key;
if ( result === null ) result = text; else result += '\n' + text;
}
}
return result;
}
function really_run() {
mnw_wiktionary_get_langdata(function() {
initialize();
if ( !HC.upload_disabled && conf.wgNamespaceNumber === -1 && conf.wgCanonicalSpecialPageName === 'Upload' && conf.wgUserName ) {
setup_upload();
setup( function () {
// Check for state restoration once the setup is done otherwise, but before signalling setup completion
if ( window.UploadForm && UploadForm.previous_hotcat_state ) UploadForm.previous_hotcat_state = setState( UploadForm.previous_hotcat_state );
} );
} else {
if ( !conf.wgIsArticle || conf.wgAction !== 'view' || param( 'diff' ) !== null || param( 'oldid' ) !== null || !can_edit() || HC.disable() ) return;
getPage();
}
});
}
function run() {
if ( HC.started ) return;
HC.started = true;
loadTrigger.register( really_run );
}
// Export legacy functions
window.hotcat_get_state = function () {
return getState();
};
window.hotcat_set_state = function ( state ) {
return setState( state );
};
window.hotcat_close_form = function () {
closeForm();
};
HC.runWhenReady = function ( callback ) {
// run user-registered code once HotCat is fully set up and ready.
mw.hook( 'hotcat.ready' ).add( callback );
};
// Make sure we don't get conflicts with AjaxCategories (core development that should one day
// replace HotCat).
mw.config.set( 'disableAJAXCategories', true );
// Run as soon as possible. This varies depending on MediaWiki version;
// window's 'load' event is always safe, but usually we can do better than that.
if ( conf.wgCanonicalSpecialPageName !== 'Upload' ) {
// Reload HotCat after (VE) edits (bug T103285)
mw.hook( 'postEdit' ).add( function () {
// Reset HotCat in case this is a soft reload (e.g. VisualEditor edit), unless the categories
// were not re-rendered and our interface is still there (e.g. DiscussionTools edit)
if ( document.querySelector( '#catlinks .hotcatlink' ) ) {
return;
}
catLine = null;
editors = [];
initialized = false;
HC.started = false;
run();
} );
}
// We can safely trigger just after user configuration is loaded.
// Use always() instead of then() to also start HotCat if the user module has problems.
$.when( mw.loader.using( 'ညးလွပ်' ), $.ready ).always( run );
}( jQuery, mediaWiki ) );
// </nowiki>
04ejv5tocf2geeai0gzu3u7j8wrk2x9
မဳဒဳယာဝဳကဳ:Gadget-HotCat.js/documentation
8
295700
396518
2026-06-07T15:35:14Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "{{documentation subpage}} <!--Add description here. This subpage initially added just for categorization.--> See also: [[MediaWiki:Gadget-HotCat.js/local defaults]]. See also: [[Special:Gadgets]]. <includeonly> [[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]] </includeonly>"
396518
wikitext
text/x-wiki
{{documentation subpage}}
<!--Add description here. This subpage initially added just for categorization.-->
See also: [[MediaWiki:Gadget-HotCat.js/local defaults]].
See also: [[Special:Gadgets]].
<includeonly>
[[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]]
</includeonly>
fx0tcn3mha6sd63t021170fg5y6swh5
မဳဒဳယာဝဳကဳ:Gadget-HotCat.js/local defaults
8
295701
396519
2026-06-07T15:35:58Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "//<source lang="javascript"> HotCat.capitalizePageNames = false; //</source>"
396519
wikitext
text/x-wiki
//<source lang="javascript">
HotCat.capitalizePageNames = false;
//</source>
aaafpazmwyq74ivkwgv72tucldzhu8u
မဳဒဳယာဝဳကဳ:Gadget-DotsSyntaxHighlighter.js
8
295702
396520
2026-06-07T15:39:04Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "// {{documentation}} mw.loader.load('//www.mediawiki.org/w/index.php?title=MediaWiki:Gadget-DotsSyntaxHighlighter.js&action=raw&ctype=text/javascript&smaxage=21600&maxage=86400');"
396520
javascript
text/javascript
// {{documentation}}
mw.loader.load('//www.mediawiki.org/w/index.php?title=MediaWiki:Gadget-DotsSyntaxHighlighter.js&action=raw&ctype=text/javascript&smaxage=21600&maxage=86400');
erifp41x2kc6dejtdqketyrzpihcwtm
မဳဒဳယာဝဳကဳ:Gadget-DotsSyntaxHighlighter.js/documentation
8
295703
396521
2026-06-07T15:39:50Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "{{documentation subpage}} <!--Add description here. This subpage initially added just for categorization.--> See also: [[Special:Gadgets]]. <includeonly> [[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]] </includeonly>"
396521
wikitext
text/x-wiki
{{documentation subpage}}
<!--Add description here. This subpage initially added just for categorization.-->
See also: [[Special:Gadgets]].
<includeonly>
[[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]]
</includeonly>
nh2z4mxstdhoy5k1d14op93dv5v8194
မဳဒဳယာဝဳကဳ:Gadget-DeveloperEditorTweaks.js
8
295704
396522
2026-06-07T15:42:38Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "'use strict'; /*jshint undef:true */ // {{documentation}} /*global mw, jQuery */ if ((mw.config.get('wgAction') === 'ပလေဝ်ဒါန်') || (mw.config.get('wgAction') === 'submit')) jQuery(document).ready(function () { var isCoding = { 'javascript': true, 'css': true, 'Scribunto': true }; var wgPageContentModel = mw.config.get('wgPageContentModel'); var wpTextbox1 = jQuery('#wpTextbox1'); if (!wpTextbox1.l..."
396522
javascript
text/javascript
'use strict'; /*jshint undef:true */
// {{documentation}}
/*global mw, jQuery */
if ((mw.config.get('wgAction') === 'ပလေဝ်ဒါန်') || (mw.config.get('wgAction') === 'submit'))
jQuery(document).ready(function () {
var isCoding = { 'javascript': true, 'css': true, 'Scribunto': true };
var wgPageContentModel = mw.config.get('wgPageContentModel');
var wpTextbox1 = jQuery('#wpTextbox1');
if (!wpTextbox1.length) {
console.error("det: failed to find WikiEditor entry point");
return;
}
if (!wpTextbox1.wikiEditor) {
console.error("det: WikiEditor not loaded?");
return;
}
if (wgPageContentModel !== 'wikitext') {
// remove irrelevant buttons
// XXX: this should be reported to Bugzilla, really
wpTextbox1.wikiEditor('removeFromToolbar', {
'section': 'main',
'group': 'insert'
});
wpTextbox1.wikiEditor('removeFromToolbar', {
'section': 'main',
'group': 'format'
});
wpTextbox1.wikiEditor('removeFromToolbar', {
'section': 'help'
});
wpTextbox1.wikiEditor('removeFromToolbar', {
'section': 'advanced',
'group': 'heading'
});
wpTextbox1.wikiEditor('removeFromToolbar', {
'section': 'advanced',
'group': 'format'
});
wpTextbox1.wikiEditor('removeFromToolbar', {
'section': 'advanced',
'group': 'size'
});
wpTextbox1.wikiEditor('removeFromToolbar', {
'section': 'advanced',
'group': 'insert'
});
}
// workaround [[gerrit:118993]]
mw.util.addCSS(
".group-codeeditor-tools," +
".codeEditor-ui-toolbar .group-insert," +
".codeEditor-ui-toolbar .group-format," +
".codeEditor-ui-toolbar .tabs span.tab-advanced," +
".codeEditor-ui-toolbar .tabs span.tab-characters," +
".codeEditor-ui-toolbar .tabs span.tab-help," +
".codeEditor-ui-toolbar .sections {" +
" display: block !important;" +
"}");
if (isCoding[wgPageContentModel]) {
wpTextbox1.wikiEditor('addToToolbar', {
'section': 'main',
'groups': {
'codefmt': {
'tools': {
'normws': {
'type': 'button',
'label': 'Normalise whitespace',
'icon': '//upload.wikimedia.org/wikipedia/commons/thumb/0/00/VisualEditor_-_Icon_-_Indent-list-ltr.svg/24px-VisualEditor_-_Icon_-_Indent-list-ltr.svg.png', // XXX
'action': {
'type': 'callback',
'execute': function (context) {
function normws(text, lineStart, lineEnd) {
text = text.replace(lineEnd ? /[ \t]+(\n|$)/g : /[ \t]+(\n)/g, '$1');
var step = 4; // XXX: guess it by inspecting the source?
text = text.replace(lineStart ? /(^|\n)([\t ]+)/g : /(\n)([\t ]+)/g, function (_, nl, indent) {
indent = indent.replace(/ /g, '\t').replace(/ +\t/g, '\t').replace(/\t/g, ' ');
var steps = Math.floor(indent.length / step); indent = '';
for (var i = 0; i < steps; ++i)
indent += '\t';
return nl + indent;
});
return text;
}
if (!context.$textarea.textSelection('getSelection')) {
if (context.codeEditor)
context.codeEditor.selectAll();
else
context.$textarea[0].select();
}
context.$textarea.textSelection('encapsulateSelection', {
replace: true, selectPeri: true,
peri: normws(context.$textarea[0].value, true, true)
});
}
}
}
}
}
}
});
}
if ((wgPageContentModel === 'javascript') || (mw.config.get('wgPageName') === 'MediaWiki:Gadgets-definition')) {
var moduleDescriptions = {
'mediawiki.Title': "<code>mw.Title</code> object",
'mediawiki.Uri' : "<code>mw.Uri</code> object",
'mediawiki.util' : "<code>mw.util</code>",
'mediawiki.user' : "<code>mw.user</code>",
'moment' : "Moment.js",
'site' : "Site-specific scripts: [[MediaWiki:Common.js]] and per-skin JS",
'es5-shim' : "ECMAScript 5 shim for older browsers",
'':""
};
wpTextbox1.wikiEditor('addToToolbar', {
'sections': {
'js': {
'type': 'booklet',
'label': "JavaScript reference",
'pages': {
'modules': {
'layout': 'table',
'label': "ResourceLoader modules",
'headings': [
{ 'text': "Identifier" },
{ 'text': "Description" }
],
'rows': mw.loader.getModuleNames().sort().filter(function (modname) {
// they are quite dull modules, but they are there
if (/^ext\.geshi\.language\./.test(modname))
return false;
if (/^ext\.math\.mathjax\.jax\.output\./.test(modname))
return false;
return true;
}).map(function (modname) {
return {
'id': {
'html': '<code>' + modname + '</code>',
},
'desc': {
'html': moduleDescriptions[modname] || '<small style="color:gray;">(no description)</small>'
}
};
})
}
}
}
}
});
}
if (wgPageContentModel === 'javascript') {
var jshintDescriptions = {
'bitwise' : "Prohibit bitwise operators",
'camelcase' : "Mandate <code>javaCamelCase</code> (deprecated)",
'curly' : "Mandate curly brackets in control structures",
'eqeqeq' : "Prohibit <code>==</code> and <code>!=</code>",
'es3' : "Mandate ECMAScript 3 compatibility (deprecated; use <code>esversion:3</code>)",
'es5' : "Mandate ECMAScript 5 compatibility (deprecated; use <code>esversion:5</code>)",
'esversion:<var>n</var>' : 'Mandate compatibility with an ECMAScript version (3, 5<span class="serial-comma">,</span> or 6)',
'forin' : "Mandate <code>hasOwnProperty</code> in <code>for..in</code> loops",
'freeze' : "Prohibit changing prototypes of built-in objects",
'futurehostile' : "Warn about identifiers defined in future versions of JavaScript",
'globals' : "Disable warnings for these globals",
'immed' : "Mandate parentheses around immediately-executed function expressions (deprecated)",
'indent:<var>n</var>' : "Set tab width (deprecated)",
'latedef' : "Prohibit using variables before their declaration",
'newcap' : "Mandate capitalisation of constructors (deprecated)",
'noarg' : "Prohibit use of <code>arguments.caller</code> and <code>arguments.callee</code>",
'noempty' : "Warn about empty blocks (deprecated)",
'nonbsp' : "Warn about non-breaking space",
'nonew' : "Prohibit constructor usage for side effects",
'plusplus' : "Prohibit unary increment and decrement operators",
'quotmark:true' : "Mandate consistency of quotation marks (deprecated)",
'quotmark:single' : "Mandate single quotation marks only (deprecated)",
'quotmark:double' : "Mandata double quotation marks only (deprecated)",
'undef' : "Prohibit use of variables not explicitly declared",
'unused' : "Warn about unused variables",
'varstmt' : "Prohibit <code>var</code> (use <code>let</code> or <code>const</code>)",
'strict' : "Mandate strict mode compatibility",
'maxparams:<var>n</var>' : "Enforce maximum number of formal parameters",
'maxdepth:<var>n</var>' : "Enforce maximum depth of nested blocks",
'maxstatements' : "Enforce maximum number of statements per function",
'maxcomplexity:<var>n</var>': "Enforce maximum cyclomatic complexity",
'maxlen' : "Enforce maximum length of a line (deprecated)",
'asi' : "Allow semicolon insertion",
'boss' : "Allow assignment-expressions",
'debug' : "Allow <code>debugger</code> statements",
'eqnull' : "Allow <code>== null</code> comparisons",
'esnext' : "Allow ES.next syntax (deprecated; use <code>esversion:6</code>)",
'evil' : "Allow <code>eval</code>",
'expr' : "Allow expressions where statements are expected",
'funcscope' : "Allow using variables outside the block where they are declared",
'globalstrict' : "Allow global strict mode (deprecated)",
'iterator' : "Allow using <code>__iterator__</code>",
'lastsemic' : "Allow omitting semicolons last statement",
'laxbreak' : "Allow lax line breaks (deprecated)",
'laxcomma' : "Allow comma-first coding style (deprecated)",
'loopfunc' : "Allow making functions within a loop",
'maxerr:<var>n</var>' : "Set maximum number of warnings",
'moz' : "Allow Mozilla extensions",
'multistr' : "Allow multi-line string literals (deprecated)",
'notypeof' : "Allow lax usage of <code>typeof</code>",
'proto' : "Allow using <code>__proto__</code>",
'scripturl' : "Allow <code>javascript:</code> URLs",
'shadow' : "Allow variable shadowing",
'sub' : "Allow bracket notation for object member access (deprecated)",
'supernew' : "Allow atypical constructor usage",
'validthis' : "<code>this</code> is valid in this function",
'noyield' : "Allow generators without <code>yield</code>",
'browser' : "Assume a browser environment",
'devel' : "Assume <code>console</code> is available",
'jquery' : "Assume jQuery is available",
'nonstandard' : "Assume non-standard global functions are available",
'worker' : "Assume Web Worker globals are available",
/* probably not useful: assume a framework is available
'couch' : "",
'dojo' : "",
'mootools' : "",
'node' : "",
'phantom' : "",
'prototypejs' : "",
'rhino' : "",
'wsh' : "",
'yui' : "",
*/
};
wpTextbox1.wikiEditor('addToToolbar', {
'section': 'js',
'pages': {
'jshint': {
'layout': 'table',
'label': "JSHint options",
'headings': [
{ 'text': "Option" },
{ 'text': "Description" }
],
'rows': Object.keys(jshintDescriptions).sort().map(function (option) {
return {
'option': {
'html': '<code>'
+ option.replace(
/^[^:]+/,
'<a href="https://jshint.com/docs/options/#$&" target="_blank">$&</a>')
+ '</code>'
},
'description': {
'html': jshintDescriptions[option]
}
};
})
}
}
});
}
if (wgPageContentModel === 'Scribunto') {
// XXX: move console upwards
}
});
qanern4yutj9hflid49sc87wyemhx02
မဳဒဳယာဝဳကဳ:Gadget-DeveloperEditorTweaks.js/documentation
8
295705
396523
2026-06-07T15:43:47Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "{{documentation subpage}} <!--Add description here. This subpage initially added just for categorization.--> See also: [[Special:Gadgets]]. <includeonly> [[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]] </includeonly>"
396523
wikitext
text/x-wiki
{{documentation subpage}}
<!--Add description here. This subpage initially added just for categorization.-->
See also: [[Special:Gadgets]].
<includeonly>
[[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]]
</includeonly>
nh2z4mxstdhoy5k1d14op93dv5v8194
မဳဒဳယာဝဳကဳ:Gadget-catfixRegrouper.js
8
295706
396524
2026-06-07T15:52:15Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "// {{documentation}} // <nowiki> // implicit dependencies : mediawiki.Title, ext.gadget.catfixRegrouper-Data /* jshint maxerr:1048576, strict:true, undef:true, latedef:true, esversion:6 */ /* global $, mw, RegrouperMetadata */ /* data is in [[MediaWiki:Gadget-catfixRegrouper-Data.js]] */ // characters to ignore when looking for the initial in a title. // this is embedded inside a RegExp character class const REGROUP..."
396524
javascript
text/javascript
// {{documentation}}
// <nowiki>
// implicit dependencies : mediawiki.Title, ext.gadget.catfixRegrouper-Data
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, esversion:6 */
/* global $, mw, RegrouperMetadata */
/* data is in [[MediaWiki:Gadget-catfixRegrouper-Data.js]] */
// characters to ignore when looking for the initial in a title.
// this is embedded inside a RegExp character class
const REGROUPER_INITIALS = "-";
function safeUpperCase(text, dottedDotlessI) {
if (dottedDotlessI)
return text.replace(/i/g, "İ").toUpperCase();
else
return text.toUpperCase();
}
function safeLowerCase(text, dottedDotlessI) {
if (dottedDotlessI)
return text.replace(/I/g, "ı").toLowerCase();
else
return text.toLowerCase();
}
function titleCase(text, lang, sc) {
return safeUpperCase(text.charAt(0), this.dottedDotlessI)
+ safeLowerCase(text.substring(1), this.dottedDotlessI);
}
function makeRemoveExceptionsMap(chars) {
const pairs = [];
let code = 0x100000;
for (let char of chars) {
pairs.push([char, String.fromCodePoint(code++)]);
}
return pairs;
}
function escapeRegexp(text) {
return text.replace(/[/\-\\^$*+?.()|[\]{}]/g, "\\$&");
}
function replaceAll(text, source, destination) {
return text.replace(new RegExp(escapeRegexp(source), "g"), destination);
}
function removeDiacritics(text, diacritics, exceptions) {
if (exceptions != null) {
for (let pair of exceptions) {
text = replaceAll(text, pair[0], pair[1]);
}
}
text = text.normalize("NFD");
text = text.replace(diacritics, "");
if (exceptions != null) {
for (let pair of exceptions) {
text = replaceAll(text, pair[1], pair[0]);
}
}
return text.normalize("NFC");
}
function defaultGroup(title, lang, sc) {
if (title.length < 1) return undefined;
let cleaned = title.replace(this._clean_regex, "");
if (this.removeDiacritics) {
cleaned = removeDiacritics(cleaned, this.removeDiacritics, this._removeExceptions_map);
}
if (this.initials) {
const initialMatch = cleaned.match(this._initials_regex);
if (initialMatch) {
return titleCase(initialMatch[0], lang, sc);
}
if (!this.initialFallback) return undefined;
}
title = cleaned || title;
return titleCase(title.charAt(0), lang, sc);
}
function makeGroup(groupText) {
const groupDiv = document.createElement("div");
groupDiv.className = "mw-category-group";
const groupH3 = document.createElement("h3");
groupH3.textContent = groupText;
groupDiv.append(groupH3);
const groupUl = document.createElement("ul");
groupDiv.append(groupUl);
return [groupDiv, groupUl];
}
function getLiText(el) {
const child = $(el).find("a, span").first();
const rawText = el.textContent || el.innerText;
return (child.length > 0 && child.text()) || rawText;
}
jQuery(() => {
'use strict';
let catfix;
// Apply only to pages in the Category namespace
// containing an element with the id "catfix".
// Set window.disableCatfixRegrouper to true to prevent this script from running.
if (!(!window.disableCatfixRegrouper
&& mw.config.get('wgNamespaceNumber') === 14
&& (catfix = $(".catfix")).length))
return;
catfix = catfix[0];
const catfixSpan = catfix.querySelector("span");
if (!catfixSpan) return;
const regrouperMeta = new RegrouperMetadata();
// Get the language name and script catfix.
const langName = decodeURIComponent(catfix.getAttribute("data-anchor").replace(/_/g, " "));
catfix = catfix.getElementsByTagName("*")[0] || document.createElement("span");
const lang = catfixSpan.getAttribute("lang");
const defaultSc = catfixSpan.classList[0] || "None";
const cachedScriptData = {};
if (!lang)
return;
const UNPREFIXED_NAMESPACES = ["", "ဓရီုကျာ", "Citations"];
const PREFIXED_NAMESPACES = ["အဆက်လက္ကရဴ", "အဆက်လက္ကရဴ ဓရီုကျာ", "ဗီုပြၚ်သိုၚ်တၟိ", "ဗီုပြၚ်သိုၚ်တၟိ ဓရီုကျာ"];
function isEntry(namespaceName, pageName) {
// main, Talk, Citations,
// Reconstruction/Appendix (Talk) if it starts with language name and "/"
return UNPREFIXED_NAMESPACES.indexOf(namespaceName) !== -1
|| (PREFIXED_NAMESPACES.indexOf(namespaceName) !== -1
&& pageName.slice(0, langName.length + 1) === langName + "/");
}
const formattedNamespaces = mw.config.get("wgFormattedNamespaces");
const regrouperData = regrouperMeta.getByLang(lang);
if (!regrouperData) return;
// set up stuff for the default regrouper
regrouperData._clean_regex = new RegExp("^[" + ((regrouperData.ignoreAdd || "") + (regrouperData.ignore || REGROUPER_INITIALS)) + "]+");
if (regrouperData.initials)
regrouperData._initials_regex = new RegExp("^" + regrouperData.initials.source, regrouperData.initials.flags);
if (regrouperData.removeExceptions)
regrouperData._removeExceptions_map = makeRemoveExceptionsMap(regrouperData.removeExceptions);
const groupFunction = regrouperData.group;
const detectScriptFunction = regrouperData.detectScript;
function getGroup(pageTitle, oldGroup) {
const titleobj = new mw.Title(pageTitle);
const namespaceId = titleobj.getNamespaceId();
const namespaceName = formattedNamespaces[namespaceId];
const pageName = titleobj.getMainText();
if (!isEntry(namespaceName, pageName))
return oldGroup;
// verify language prefix if the namespace should have one
let formattedTitle = pageName;
const langPrefix = langName + "/";
if (PREFIXED_NAMESPACES.indexOf(namespaceName) !== -1) {
if (formattedTitle.startsWith(langPrefix)) {
formattedTitle = formattedTitle.substring(langPrefix.length);
} else {
return oldGroup;
}
}
// ignore unsupported titles unless the language data requests otherwise
if (formattedTitle.startsWith("Unsupported titles/") && !regrouperData.unsupported)
return oldGroup;
// script detection
let sc = defaultSc;
if (detectScriptFunction)
sc = detectScriptFunction.call(regrouperData, formattedTitle, lang, sc, namespaceId) || sc;
let scData = cachedScriptData[sc];
if (!scData)
scData = cachedScriptData[sc] = regrouperMeta.getBySc(sc) || {};
let newGroup = true;
// the language group function may return true to fall back
if (groupFunction)
newGroup = groupFunction.call(regrouperData, formattedTitle, lang, sc, namespaceId);
if (newGroup === true && scData.group)
newGroup = scData.group.call(regrouperData, formattedTitle, lang, sc, namespaceId);
if (newGroup === true)
newGroup = defaultGroup.call(regrouperData, formattedTitle, lang, sc, namespaceId);
return newGroup || oldGroup;
}
const GROUP_QUERY = "#mw-pages > .mw-content-ltr .mw-category-group";
let regroupOk = true;
const regroupData = new Map();
// Process each group in the category listing.
jQuery(GROUP_QUERY)
.each(function () {
// Get the existing group.
const group = $(this).find("h3").first().text();
if (!group) {
// Failed to get group -- something has gone wrong.
regroupOk = false;
return;
}
$(this).find("li")
.each(function () {
try {
const liText = getLiText(this);
const newGroup = getGroup(liText, group);
regroupData.set(liText, newGroup);
} catch (e) {
console.error(e);
regroupOk = false;
}
});
});
// Find the existing groups, which we will delete.
const groups = jQuery(GROUP_QUERY);
// Cannot regroup if there are no groups.
if (!groups.length) return;
const parent = groups.first().parent()[0];
if (!parent) return;
const fragment = document.createDocumentFragment();
if (regroupOk) {
let lastGroup, groupUl;
jQuery(GROUP_QUERY + " li")
.each(function () {
const liText = getLiText(this);
const newGroup = regroupData.get(liText) || "";
if (lastGroup !== newGroup) {
const elements = makeGroup(newGroup);
const groupDiv = elements[0];
fragment.appendChild(groupDiv);
groupUl = elements[1];
lastGroup = newGroup;
}
groupUl.appendChild(this);
});
groups.remove();
parent.appendChild(fragment);
}
});
// </nowiki>
nwi46rqk10hxaymx4nluzjb1nd0etbj
မဳဒဳယာဝဳကဳ:Gadget-catfixRegrouper.js/documentation
8
295707
396525
2026-06-07T15:53:18Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "{{documentation subpage}} Fixes subheadings within category lists. Uses data in [[MediaWiki:Gadget-catfixRegrouper-Data.js]]. <includeonly> [[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]] </includeonly>"
396525
wikitext
text/x-wiki
{{documentation subpage}}
Fixes subheadings within category lists.
Uses data in [[MediaWiki:Gadget-catfixRegrouper-Data.js]].
<includeonly>
[[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]]
</includeonly>
p5ekmugb8tqgi0novgbqu6o738242ab
မဳဒဳယာဝဳကဳ:Gadget-catfixRegrouper-Data.js
8
295708
396526
2026-06-07T15:54:16Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "// {{documentation}} // <nowiki> /* jshint maxerr:1048576, strict:true, undef:true, latedef:true, es5:true */ /* global mw */ // Configuration and data for [[MediaWiki:Gadget-catfixRegrouper.js]]. /** REGROUPER_DATA_LANG Data format: * IMPORTANT! The regrouper already assumes that the language sort keys * are set up correctly. It does not protect against the same group * heading being repeated *AT..."
396526
javascript
text/javascript
// {{documentation}}
// <nowiki>
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, es5:true */
/* global mw */
// Configuration and data for [[MediaWiki:Gadget-catfixRegrouper.js]].
/** REGROUPER_DATA_LANG Data format:
* IMPORTANT! The regrouper already assumes that the language sort keys
* are set up correctly. It does not protect against the same group
* heading being repeated *AT ALL*. Do not enable the regrouper for
* a language until sort keys are set up for it first (so that
* each would-be group is contiguous).
* The data is organized by language code.
* If the code is missing or the value is undefined, the
* regrouper is disabled. This is the default.
* If it is ``true``, default values are used.
* Otherwise it is an object, with the following fields:
* group
* A function which is given the following parameters,
* in this order:
* - page title (minus namespace and language prefixes),
* - language code,
* - script code,
* - namespace number.
* ``this`` will be the language data object (so that you
* can add your own variables, functions, etc.; but if you
* do so, please prefix their names with x_).
* It should return the group, i.e. the heading that the
* page should be categorized under. If the value returned
* is undefined, null, ``false`` or an empty string,
* its existing group will be kept.
* If ``true``, the group falls back to the
* default grouping function.
* The default is the default grouping function, either of the
* script, or of ``defaultGroup``;
* see below for a description thereof.
* detectScript
* A function which is given the following parameters,
* in this order:
* - page title (minus namespace and language prefixes),
* - language code,
* - (default) script code,
* - namespace number.
* ``this`` will be the language data object (so that you
* can add your own variables, functions, etc.; but if you
* do so, please prefix their names with x_).
* It should return a script code, or undefined to use
* the default script.
* initials
* If the default grouping function is used, this can be a RegExp
* of initial letters/digraphs/etc. which are automatically mapped
* to that respective group (with titlecase conversion).
* initialFallback
* Only applies for the default grouping function and if initials
* is defined. If ``true``, the default grouping function falls
* back to the default logic when the initial doesn't match any
* specified in initials; if ``false`` (default), it just returns
* ``undefined`` (i.e. keeps the existing group).
* ignore
* Preceding characters to ignore instead of the default ones.
* This is in RegExp character class syntax.
* ignoreAdd
* Preceding characters to ignore in addition to the default ones.
* This is in RegExp character class syntax.
* removeDiacritics
* If specified, decomposition is applied, diacritics matching
* this pattern are removed, and then the remaining text is
* recomposed. Corresponds to remove_diacritics in language data.
* This only applies with the default grouping function.
* removeExceptions
* If specified, these precomposed characters are retained,
* even if the diacritic would otherwise be subject to removal
* as per removeDiacritics. Corresponds to remove_exceptions
* in language data. This must be a string containing characters,
* not a regex pattern.
* This only applies with the default grouping function.
* unsupported
* If ``true``, unsupported titles are passed directly to
* ``group``. If ``false`` (default), they are ignored, and their
* existing groups are kept.
* dottedDotlessI
* Used in case conversion; ``true`` means the language has both
* dotted and dotless I as separate letters (like in Turkish),
* and ``false`` (default) means it doesn't.
*
* REGROUPER_DATA_SC data format:
* This data is organized by script. Note that these configurations are
* still only considered for languages that have regrouping enabled.
*
* If missing, defaults are used. Else, an object may override:
* group
* A function that works just like group in REGROUPER_DATA_LANG.
* If missing, the default grouping function is used.
* The priority of grouping functions is:
* - group in language data,
* - group in script data,
* - default grouping.
*
* The default grouping function:
* Checks initials and initialFallback.
* If there are initials, it matches them first.
* Initial matching ignores certain preceding characters,
* e.g. hyphens.
* If an initial is found, the matching portion is converted
* to title case with ``titleCase`` and returned.
* Otherwise, we fall back to 'fallback' logic only if
* `initialFallback` is `true`, and else return `undefined`
* to keep the existing group.
* We may fall into the fallback logic:
* remove preceding characters (e.g. hyphens),
* take the first remaining Unicode character
* (or the first character in general if none would remain
* from the previous step),
* convert it to title case with ``titleCase`` and return it.
*/
window.RegrouperMetadata = function() {
const REGROUPER_COMMON_DIACRITICS = /[\u0300-\u034E\u0350-\u036F\u1AB0-\u1ACE\u1DC0-\u1DFF\u20D0-\u20F0\uFE20-\uFE2F]/g;
const REGROUPER_DATA_LANG = {
"da": {
initials: /[ÆØÅ]/i,
removeDiacritics: /[\u0300\u0301\u0302\u0303\u0304\u030B\u030C\u0327]/g,
removeExceptions: "Åå",
},
"et": {
initials: /[ŠZŽÕÄÖÜ]/i,
},
"fi": {
initials: /[Å]/i,
removeDiacritics: REGROUPER_COMMON_DIACRITICS,
removeExceptions: "ÅÄÖåäö",
},
// TODO check with editors.
// "hu": {
// initials: /(?:[ÁÉÍÓÖŐÚÜŰ]|C[sz]|Dzs?|[GLNT]y|Sz)/i,
// },
"liv": {
initials: /[ĀÄǞḐĒĪĻŅŌȮȰÕȬÖȪŖŠȚŪÜṺȲŽ]/i,
},
"lud": {
initials: /[ČŠZŽÜÄÖʹ]/i,
},
"mt": {
initials: /(?:Għ|Ie|[ĊĠĦŻCGHIZ])/i,
},
"sv": {
initials: /[Å]/i,
removeDiacritics: /[\u0300\u0301\u0302\u0303\u0304\u030B\u030C\u0327]/g,
removeExceptions: "Åå",
},
"vot": {
initials: /(?:[ČŠZŽÕÄÖÜ]|Tš)/i,
},
};
const REGROUPER_DATA_SC = {
};
this.getByLang = lang => REGROUPER_DATA_LANG[lang];
this.getBySc = sc => REGROUPER_DATA_SC[sc];
}
//</nowiki>
23l97ycefmpgfemndxsnvstxjvvb4j4
မဳဒဳယာဝဳကဳ:Gadget-categoryTreeLanguageNames.js
8
295709
396527
2026-06-07T16:00:42Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု ""use strict"; // {{documentation}} // <nowiki> /* global mw */ (function categoryTreeLanguageNamesGadget() { const ALLOWED_NAMESPACES = [ 0, // မုက်လိက်တမ် 14, // ကဏ္ဍ 100, // အဆက်လက္ကရဴ 118 // ဗီုပြၚ်သိုၚ်တၟိ ]; const CATEGORY_PREFIX = mw.config.get("wgFormattedNamespaces")[14] + ":"; function getLanguageCo..."
396527
javascript
text/javascript
"use strict";
// {{documentation}}
// <nowiki>
/* global mw */
(function categoryTreeLanguageNamesGadget() {
const ALLOWED_NAMESPACES = [
0, // မုက်လိက်တမ်
14, // ကဏ္ဍ
100, // အဆက်လက္ကရဴ
118 // ဗီုပြၚ်သိုၚ်တၟိ
];
const CATEGORY_PREFIX = mw.config.get("wgFormattedNamespaces")[14] + ":";
function getLanguageCodeToCanonicalName() {
const CACHE_DURATION = 24 * 60 * 60; // 24 hours
const KEY = "mnwwiktLanguageCodeToCanonicalNameJson";
let timeNow = new Date().getTime() * 1e-3;
try {
let cachedData = JSON.parse(localStorage.getItem(KEY));
if (timeNow - cachedData.timestamp < CACHE_DURATION)
return Promise.resolve(cachedData.data);
} catch (e) { }
const actionAPI = new mw.Api({ ajax: { headers: { "Api-User-Agent": "Gadget developed by [[User:Surjection]]" } } });
return actionAPI.get({
"action": "parse",
"page": "Module:languages/code to canonical name.json",
"prop": "wikitext",
"formatversion": "2",
"format": "json"
})
.then(response => {
let languageData = JSON.parse(response.parse.wikitext);
localStorage.setItem(KEY, JSON.stringify({
timestamp: timeNow,
data: languageData
}));
return languageData;
});
}
function addCategoryTreeLanguageName(codeToCanonicalName, catElement, doNotTagLanguageCode) {
// not a category link/name
if (!catElement.href.includes(CATEGORY_PREFIX))
return;
// do not duplicate
if (catElement.querySelector(".wikt-category-tree-language-name"))
return;
let firstTextNode = Array.from(catElement.childNodes).find(node => node.nodeType === Node.TEXT_NODE);
if (!firstTextNode)
return;
let categoryName = firstTextNode.textContent;
let parseCategoryTreeName = categoryName.match(/^([a-z-]+):(.+)/);
if (!parseCategoryTreeName)
return;
let languageCode = parseCategoryTreeName[1];
let subcategoryName = parseCategoryTreeName[2];
let canonicalName = codeToCanonicalName[languageCode];
if (!canonicalName)
return;
if (catElement.title === categoryName || catElement.title === CATEGORY_PREFIX + categoryName)
catElement.title += ` [${canonicalName}: ${subcategoryName}]`;
if (languageCode !== doNotTagLanguageCode) {
let langElement = document.createElement("span");
langElement.className = "wikt-category-tree-language-name";
langElement.textContent = ` [${canonicalName}]`;
catElement.append(langElement);
}
}
mw.hook("wikipage.content").add(() => {
if (mw.config.get("wgAction") === "ဗီုပြၚ်လညာတ်" &&
ALLOWED_NAMESPACES.includes(mw.config.get("wgNamespaceNumber"))) {
mw.util.addCSS(`
.wikt-category-tree-language-name {
font-size: 85%;
}
`);
let thisLanguageCode = null;
if (mw.config.get("wgNamespaceNumber") === 14) {
let categoryName = mw.config.get("wgTitle");
let parseCategoryTreeName = categoryName.match(/^([a-z-]+):(.+)/);
if (parseCategoryTreeName)
thisLanguageCode = parseCategoryTreeName[1];
}
getLanguageCodeToCanonicalName().then(codeToCanonicalName => {
for (let catElement of document.querySelectorAll("#catlinks ul > li a"))
addCategoryTreeLanguageName(codeToCanonicalName, catElement, thisLanguageCode);
if (mw.config.get("wgNamespaceNumber") === 14) {
for (let catElement of document.querySelectorAll("#mw-subcategories a"))
addCategoryTreeLanguageName(codeToCanonicalName, catElement, thisLanguageCode);
}
});
}
});
})();
// </nowiki>
28qxdkgpomu09ca2nib9pp1abbxdmih
မဳဒဳယာဝဳကဳ:Gadget-categoryTreeLanguageNames.js/documentation
8
295710
396528
2026-06-07T16:01:40Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "{{documentation subpage}} See also: [[Special:Gadgets]]. <includeonly> [[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]] </includeonly>"
396528
wikitext
text/x-wiki
{{documentation subpage}}
See also: [[Special:Gadgets]].
<includeonly>
[[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]]
</includeonly>
g0pto8y4htz6e0eccg6fcqtlxv07sce
မဳဒဳယာဝဳကဳ:Gadget-VisibilityToggles.js
8
295711
396529
2026-06-07T16:10:29Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "/* eslint-env es5, browser, jquery */ /* eslint semi: "error" */ /* jshint esversion: 5, eqeqeq: true */ /* globals $, mw */ /* requires mw.cookie, mw.storage */ (function VisibilityTogglesIIFE () { "use strict"; // Toggle object that is constructed so that `toggle.status = !toggle.status` // automatically calls either `toggle.show()` or `toggle.hide()` as appropriate. // Creating toggle also automatically calls eith..."
396529
javascript
text/javascript
/* eslint-env es5, browser, jquery */
/* eslint semi: "error" */
/* jshint esversion: 5, eqeqeq: true */
/* globals $, mw */
/* requires mw.cookie, mw.storage */
(function VisibilityTogglesIIFE () {
"use strict";
// Toggle object that is constructed so that `toggle.status = !toggle.status`
// automatically calls either `toggle.show()` or `toggle.hide()` as appropriate.
// Creating toggle also automatically calls either the show or the hide function.
function Toggle (showFunction, hideFunction) {
this.show = showFunction, this.hide = hideFunction;
}
Toggle.prototype = {
get status () {
return this._status;
},
set status (newStatus) {
if (typeof newStatus !== "boolean")
throw new TypeError("Value of 'status' must be a boolean.");
if (newStatus === this._status)
return;
this._status = newStatus;
if (this._status !== this.toggleCategory.status)
this.toggleCategory.updateToggle(this._status);
if (this._status)
this.show();
else
this.hide();
},
};
/*
* Handles storing a boolean value associated with a `name` stored in
* localStorage under `key`.
*
* The `get` method returns `true`, `false`, or `undefined` (if the storage
* hasn't been tampered with).
* The `set` method only allows setting `true` or `false`.
*/
function BooleanStorage(key, name) {
if (typeof key !== "string")
throw new TypeError("Expected string");
if (!(typeof name === "string" && name !== "")) {
throw new TypeError("Expected non-empty string");
}
this.key = key; // key for localStorage
this.name = name; // name of toggle category
function convertOldCookie(cookie) {
return cookie.split(';')
.filter(function(e) { return e !== ''; })
.reduce(function(memo, currentValue) {
var match = /(.+?)=(\d)/.exec(currentValue); // only to test for temporary =[01] format
if (match) {
memo[match[1]] = Boolean(Number(match[2]));
} else {
memo[currentValue] = true;
}
return memo;
}, {});
}
// Look for cookie in old format.
var cookie = mw.cookie.get(key);
if (cookie !== null) {
this.obj = $.extend(this.obj, convertOldCookie(cookie));
mw.cookie.set(key, null); // Remove cookie.
}
}
BooleanStorage.prototype = {
get: function () {
return this.obj[this.name];
},
set: function (value) {
if (typeof value !== "boolean")
throw new TypeError("Expected boolean");
var obj = this.obj;
if (obj[this.name] !== value) {
obj[this.name] = value;
this.obj = obj;
}
},
// obj allows getting and setting the object version of the stored value.
get obj() {
if (typeof this.rawValue !== "string")
return {};
try {
return JSON.parse(this.rawValue);
} catch (e) {
if (e instanceof SyntaxError) {
return {};
} else {
throw e;
}
}
},
set obj(value) {
// throws TypeError ("cyclic object value")
this.rawValue = JSON.stringify(value);
},
// rawValue allows simple getting and setting of the stringified object.
get rawValue () {
return mw.storage.get(this.key);
},
set rawValue (value) {
return mw.storage.set(this.key, value);
},
};
// This is a version of the actual CSS identifier syntax (described here:
// https://stackoverflow.com/a/2812097), with only ASCII and that must begin
// with an alphabetic character.
var asciiCssIdentifierRegex = /^[a-zA-Z][a-zA-Z0-9_-]+$/;
function ToggleCategory (name, defaultStatus) {
this.name = name;
this.sidebarToggle = this.newSidebarToggle();
this.storage = new BooleanStorage("Visibility", name);
this.status = this.getInitialStatus(defaultStatus);
}
// Have toggle category inherit array methods.
ToggleCategory.prototype = [];
ToggleCategory.prototype.addToggle = function (showFunction, hideFunction) {
var toggle = new Toggle(showFunction, hideFunction);
toggle.toggleCategory = this;
this.push(toggle);
toggle.status = this.status;
return toggle;
};
// Generate an identifier consisting of a lowercase ASCII letter and a random integer.
function randomAsciiCssIdentifier() {
var digits = 9;
var lowCodepoint = "a".codePointAt(0), highCodepoint = "z".codePointAt(0);
return String.fromCodePoint(
lowCodepoint + Math.floor(Math.random() * (highCodepoint - lowCodepoint)))
+ String(Math.floor(Math.random() * Math.pow(10, digits)));
}
function getCssIdentifier(name) {
name = name.replace(/\s+/g, "-");
// Generate a valid ASCII CSS identifier.
if (!asciiCssIdentifierRegex.test(name)) {
// Remove characters that are invalid in an ASCII CSS identifier.
name = name.replace(/^[^a-zA-Z]+/, "").replace(/[^a-zA-Z_-]+/g, "");
if (!asciiCssIdentifierRegex.test(name))
name = randomAsciiCssIdentifier();
}
return name;
}
// Add a new global toggle to the sidebar.
ToggleCategory.prototype.newSidebarToggle = function () {
var name = getCssIdentifier(this.name);
var id = "p-visibility-" + name;
var sidebarToggle = $("#" + id);
if (sidebarToggle.length > 0)
return sidebarToggle;
var listEntry = $("<li>");
if (mw.config.get("skin") === "vector-2022")
listEntry.addClass("mw-list-item");
sidebarToggle = $("<a>", {
id: id,
href: "#visibility-" + this.name,
})
.click((function () {
this.status = !this.status;
this.storage.set(this.status);
return false;
}).bind(this));
listEntry.append(sidebarToggle).appendTo(this.buttons);
return sidebarToggle;
};
// Update the status of the sidebar toggle for the category when all of its
// toggles on the page are toggled one way.
ToggleCategory.prototype.updateToggle = function (status) {
if (this.length > 0 && this.every(function (toggle) { return toggle.status === status; }))
this.status = status;
};
// getInitialStatus is only called when a category is first created.
ToggleCategory.prototype.getInitialStatus = function (defaultStatus) {
function isFragmentSet(name) {
return location.hash.toLowerCase().split("_")[0] === "#" + name.toLowerCase();
}
function isHideCatsSet(name) {
var match = /^.+?\?(?:.*?&)*?hidecats=(.+?)(?:&.*)?$/.exec(location.href);
if (match !== null) {
var hidecats = match[1].split(",");
for (var i = 0; i < hidecats.length; ++i) {
switch (hidecats[i]) {
case name: case "all":
return false;
case "!" + name: case "none":
return true;
}
}
}
return false;
}
function isWiktionaryPreferencesCookieSet() {
return mw.cookie.get("WiktionaryPreferencesShowNav") === "true";
}
// TODO check category-specific cookies
return isFragmentSet(this.name)
|| isHideCatsSet(this.name)
|| isWiktionaryPreferencesCookieSet()
|| (function(storedValue) {
return storedValue !== undefined ? storedValue : Boolean(defaultStatus);
}(this.storage.get()));
};
Object.defineProperties(ToggleCategory.prototype, {
status: {
get: function () {
return this._status;
},
set: function (status) {
if (typeof status !== "boolean")
throw new TypeError("Value of 'status' must be a boolean.");
if (status === this._status)
return;
this._status = status;
// Change the state of all Toggles in the ToggleCategory.
for (var i = 0; i < this.length; i++)
this[i].status = status;
this.sidebarToggle.text((status ? "ပၞုက်" : "ထ္ၜး") + this.name);
},
},
buttons: {
get: function () {
var buttons = $("#p-visibility ul");
if (buttons.length > 0)
return buttons;
buttons = $("<ul>");
// unused var collapsed = mw.cookie.get("vector-nav-p-visibility") === "false";
if (mw.config.get("skin") === "vector-2022") {
/* add to right-hand side ('tools') bar */
var toolbox = $("<div>", {
"class": "vector-menu mw-portlet mw-portlet-visibility",
"id": "p-visibility"
})
.append($('<div id="p-visibility-label" aria-label="" class="vector-menu-heading">Visibility</div>'))
.append($("<div>", { class: "vector-menu-content" }).append(buttons.addClass("vector-menu-content-list")));
$('#vector-page-tools').append(toolbox);
} else {
var toolbox = $("<div>", {
"class": "vector-menu vector-menu-portal portal portlet",
"id": "p-visibility"
})
.append($('<label id="p-visibility-label" aria-label="" class="vector-menu-heading"><span class="vector-menu-heading-label">Visibility</span></label>'))
.append($("<div>", { class: "pBody body vector-menu-content" }).append(buttons));
var insert = document.getElementById("p-lang") || document.getElementById("p-feedback");
if (insert) {
$(insert).before(toolbox);
} else {
var sidebar = document.getElementById("mw-panel") || document.getElementById("column-one");
$(sidebar).append(toolbox);
}
}
return buttons;
}
}
});
function VisibilityToggles () {
// table containing ToggleCategories
this.togglesByCategory = {};
}
// Add a new toggle, adds a Show/Hide category button in the toolbar.
// Returns a function that when called, calls showFunction and hideFunction
// alternately and updates the sidebar toggle for the category if necessary.
VisibilityToggles.prototype.register = function (category, showFunction, hideFunction, defaultStatus) {
if (!(typeof category === "string" && category !== ""))
return;
var toggle = this.addToggleCategory(category, defaultStatus)
.addToggle(showFunction, hideFunction);
return function () {
toggle.status = !toggle.status;
};
};
VisibilityToggles.prototype.addToggleCategory = function (name, defaultStatus) {
return (this.togglesByCategory[name] = this.togglesByCategory[name] || new ToggleCategory(name, defaultStatus));
};
window.alternativeVisibilityToggles = new VisibilityToggles();
window.VisibilityToggles = window.alternativeVisibilityToggles;
})();
9cywdtza86wqtr1wvurcyzow6fufrbj
မဳဒဳယာဝဳကဳ:Gadget-DocTabs.css
8
295712
396530
2026-06-07T16:12:36Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "body.skin-monobook li#ca-talk { margin-right: 0.3em; } body.skin-monobook li#ca-edit { margin-left: 1.6em; }"
396530
css
text/css
body.skin-monobook li#ca-talk {
margin-right: 0.3em;
}
body.skin-monobook li#ca-edit {
margin-left: 1.6em;
}
1qr2q49pcyypwjhp4zhxahgmt90m3f1
မဳဒဳယာဝဳကဳ:Gadget-VectorClassic.css
8
295713
396531
2026-06-07T16:15:04Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "html, body { font-family: Pyidaungsu; } div#content { color: black; } div#content h1, div#content h2, div#content #firstHeading, div#content .mw-editsection { font-family: inherit; } div#content h1, div#content h2, div#content h3, div#content h4, div#content h5, div#content h6 { line-height: inherit; margin: 0; padding-top: 0.5em; padding-bottom: 0.17em; } div#content #firstHeading { fo..."
396531
css
text/css
html, body {
font-family: Pyidaungsu;
}
div#content {
color: black;
}
div#content h1,
div#content h2,
div#content #firstHeading,
div#content .mw-editsection {
font-family: inherit;
}
div#content h1,
div#content h2,
div#content h3,
div#content h4,
div#content h5,
div#content h6 {
line-height: inherit;
margin: 0;
padding-top: 0.5em;
padding-bottom: 0.17em;
}
div#content #firstHeading {
font-size: 1.6em;
line-height: 1.2em;
margin-bottom: 0.1em;
padding-bottom: 0;
}
div#content h1 {
font-size: 188%;
margin-bottom: 0.6em;
}
div#content h2 {
font-size: 150%;
margin-bottom: 0.6em;
}
div#content h3 {
font-size: 132%;
}
div#content h4 {
font-size: 116%;
}
div#content h5 {
font-size: 108%;
}
div#content h6 {
font-size: 100%;
}
.mw-body-content,
#bodyContent {
font-size: 0.8125em;
line-height: 1.5em;
}
e407fb2f50lf01w13y9heyeii2lzxev
မဳဒဳယာဝဳကဳ:Gadget-legacy.js
8
295714
396532
2026-06-07T16:18:19Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် ပလး
396532
javascript
text/javascript
phoiac9h4m842xq45sp7s6u21eteeq1
မဳဒဳယာဝဳကဳ:Gadget-legacy.js/documentation
8
295715
396533
2026-06-07T16:19:04Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "{{documentation subpage}} <!--Add description here. This subpage initially added just for categorization.--> See also: [[Special:Gadgets]]. <includeonly> [[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]] </includeonly>"
396533
wikitext
text/x-wiki
{{documentation subpage}}
<!--Add description here. This subpage initially added just for categorization.-->
See also: [[Special:Gadgets]].
<includeonly>
[[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]]
</includeonly>
nh2z4mxstdhoy5k1d14op93dv5v8194
ထာမ်ပလိက်:hsb-noun
10
295716
396535
2026-06-07T16:27:40Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "{{#invoke:hsb-dsb-headword|show|nouns|lang=hsb}}<!-- --><noinclude>{{hwcat}}</noinclude>"
396535
wikitext
text/x-wiki
{{#invoke:hsb-dsb-headword|show|nouns|lang=hsb}}<!--
--><noinclude>{{hwcat}}</noinclude>
7ahsxr8o3kamsr6u4o3loda87f8d54d
396536
396535
2026-06-07T16:28:39Z
咽頭べさ
33
396536
wikitext
text/x-wiki
{{#invoke:hsb-dsb-headword|show|နာမ်|lang=hsb}}<!--
--><noinclude>{{hwcat}}</noinclude>
keijddnd5acgix3jnhqgnsv4gecastw
ကဏ္ဍ:ထာမ်ပလိက်လာၚ်က္ဍိုပ်မအရေဝ်သဝ်ဗဳယျာ လ္ပာ်သၠုၚ်ကျာဂမၠိုၚ်
14
295717
396537
2026-06-07T16:29:29Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "[[ကဏ္ဍ:ထာမ်ပလိက်သဝ်ဗဳယျာ လ္ပာ်သၠုၚ်ကျာဂမၠိုၚ်]][[ကဏ္ဍ:လာၚ်က္ဍိုပ်မအရေဝ်ဗက်အလိုက်အရေဝ်ဘာသာဂမၠိုၚ်|သ]]"
396537
wikitext
text/x-wiki
[[ကဏ္ဍ:ထာမ်ပလိက်သဝ်ဗဳယျာ လ္ပာ်သၠုၚ်ကျာဂမၠိုၚ်]][[ကဏ္ဍ:လာၚ်က္ဍိုပ်မအရေဝ်ဗက်အလိုက်အရေဝ်ဘာသာဂမၠိုၚ်|သ]]
gz4m5s29nr7c8l3f40bzcy1sme4h60f
ထာမ်ပလိက်:hsb-ndecl
10
295718
396538
2026-06-07T16:32:11Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "{{#invoke:hsb-noun|show}}<!-- --><noinclude>{{documentation}}</noinclude>"
396538
wikitext
text/x-wiki
{{#invoke:hsb-noun|show}}<!--
--><noinclude>{{documentation}}</noinclude>
czhfiq4u8c5lzoupyofq9psy1gaouvg
မဝ်ဂျူ:hsb-noun
828
295719
396539
2026-06-07T16:49:26Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "local export = {} --[=[ Authorship: Zhnka, heavily based on [[Module:cs-noun]] by Benwing ]=] --[=[ TERMINOLOGY: -- "slot" = A particular combination of case/number. Example slot names for nouns are "gen_s" (genitive singular) and "voc_p" (vocative plural). Each slot is filled with zero or more forms. -- "form" = The declined form representing the value of a given slot. -- "lemma" = The dictionary form. G..."
396539
Scribunto
text/plain
local export = {}
--[=[
Authorship: Zhnka, heavily based on [[Module:cs-noun]] by Benwing
]=]
--[=[
TERMINOLOGY:
-- "slot" = A particular combination of case/number.
Example slot names for nouns are "gen_s" (genitive singular) and
"voc_p" (vocative plural). Each slot is filled with zero or more forms.
-- "form" = The declined form representing the value of a given slot.
-- "lemma" = The dictionary form. Generally the nominative
masculine singular, but may occasionally be another form if the nominative
masculine singular is missing.
]=]
local lang = require("Module:languages").getByCode("hsb")
local m_table = require("Module:table")
local m_links = require("Module:links")
local m_string_utilities = require("Module:string utilities")
local iut = require("Module:inflection utilities")
local put = require("Module:parse utilities")
local m_para = require("Module:parameters")
local com = require("Module:hsb-common")
local en_utilities_module = "Module:en-utilities"
local current_title = mw.title.getCurrentTitle()
local NAMESPACE = current_title.nsText
local PAGENAME = current_title.text
local u = mw.ustring.char
local rsplit = mw.text.split
local rfind = mw.ustring.find
local rmatch = mw.ustring.match
local rgmatch = mw.ustring.gmatch
local rsubn = mw.ustring.gsub
local ulen = mw.ustring.len
local usub = mw.ustring.sub
local uupper = mw.ustring.upper
local ulower = mw.ustring.lower
local force_cat = false -- set to true to make categories appear in non-mainspace pages, for testing
-- 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
-- version of rsubn() that returns a 2nd argument boolean indicating whether
-- a substitution was made.
local function rsubb(term, foo, bar)
local retval, nsubs = rsubn(term, foo, bar)
return retval, nsubs > 0
end
local function track(track_id)
require("Module:debug/track")("hsb-noun/" .. track_id)
return true
end
local output_noun_slots = {
nom_s = "nom|s",
gen_s = "gen|s",
dat_s = "dat|s",
acc_s = "acc|s",
voc_s = "voc|s",
loc_s = "loc|s",
ins_s = "ins|s",
nom_d = "nom|d",
gen_d = "gen|d",
dat_d = "dat|d",
acc_d = "acc|d",
voc_d = "voc|d",
loc_d = "loc|d",
ins_d = "ins|d",
nom_p = "nom|p",
gen_p = "gen|p",
dat_p = "dat|p",
acc_p = "acc|p",
voc_p = "voc|p",
loc_p = "loc|p",
ins_p = "ins|p",
}
local function get_output_noun_slots(alternant_multiword_spec)
-- FIXME: To save memory we modify the table in-place. This won't work if we ever end up with multiple calls to
-- this module in the same Lua invocation, and we would need to clone the table.
if alternant_multiword_spec.actual_number ~= "allthree" then
for slot, accel_form in pairs(output_noun_slots) do
output_noun_slots[slot] = accel_form:gsub("|[sp]$", "")
end
end
return output_noun_slots
end
local potential_lemma_slots = {"nom_s", "nom_p", "gen_s"}
local cases = {
nom = true,
gen = true,
dat = true,
acc = true,
voc = true,
loc = true,
ins = true,
}
local clitic_cases = {
gen = true,
dat = true,
acc = true,
}
local function dereduce(base, stem)
local dereduced_stem = com.dereduce(base, stem)
if not dereduced_stem then
error("Unable to dereduce stem '" .. stem .. "'")
end
return dereduced_stem
end
local function skip_slot(number, slot)
return number == "sg" and rfind(slot, "_p$") or
number == "pl" and rfind(slot, "_s$")
end
-- Basic function to combine stem(s) and ending(s) and insert the result into the appropriate slot. `stems` is either
-- the `stems` object passed into the declension functions (containing the various stems; see below) or a string to
-- override the stem. (NOTE: If you pass a string in as `stems`, you should pass the value of `stems.footnotes` as the
-- value of `footnotes` as it will be lost otherwise. If you need to supply your own footnote in addition, use
-- iut.combine_footnotes() to combine any user-specified footnote(s) with your footnote(s).) `endings` is either a
-- string specifying a single ending or a list of endings. If `endings` is nil, no forms are inserted. If an ending is
-- "-", the value of `stems` is ignored and the lemma is used instead as the stem; this is important in case the user
-- used `decllemma:` to specify a declension lemma different from the actual lemma, or specified '.foreign' (which has
-- a similar effect).
local function add(base, slot, stems, endings, footnotes)
if not endings then
return
end
-- Call skip_slot() based on the declined number; if the actual number is different, we correct this in
-- decline_noun() at the end.
if skip_slot(base.number, slot) then
return
end
local stems_footnotes = type(stems) == "table" and stems.footnotes or nil
footnotes = iut.combine_footnotes(iut.combine_footnotes(base.footnotes, stems_footnotes), footnotes)
if type(endings) == "string" then
endings = {endings}
end
for _, ending in ipairs(endings) do
-- Compute the stem. If ending is "-", use the lemma regardless. Otherwise if `stems` is a string, use it.
-- Otherwise `stems` is an object containing four stems (vowel-vs-non-vowel cross regular-vs-oblique);
-- compute the appropriate stem based on the slot and whether the ending begins with a vowel.
local stem
if ending == "-" then
stem = base.actual_lemma
ending = ""
elseif type(stems) == "string" then
stem = stems
else
local is_vowel_ending = rfind(ending, "^" .. com.vowel_c)
if stems.oblique_slots == "all" then
if is_vowel_ending then
stem = stems.oblique_vowel_stem
else
stem = stems.oblique_nonvowel_stem
end
elseif is_vowel_ending then
stem = stems.vowel_stem
else
stem = stems.nonvowel_stem
end
end
ending = iut.combine_form_and_footnotes(ending, footnotes)
local function combine_stem_ending(stem, ending)
return com.combine_stem_ending(base, slot, stem, ending)
end
iut.add_forms(base.forms, slot, stem, ending, combine_stem_ending)
end
end
local function process_slot_overrides(base, do_slot)
for slot, overrides in pairs(base.overrides) do
-- Call skip_slot() based on the declined number; if the actual number is different, we correct this in
-- decline_noun() at the end.
if skip_slot(base.number, slot) then
error("Override specified for invalid slot '" .. slot .. "' due to '" .. base.number .. "' number restriction")
end
if do_slot(slot) then
base.slot_overridden[slot] = true
base.forms[slot] = nil
for _, override in ipairs(overrides) do
for _, value in ipairs(override.values) do
local form = value.form
local combined_notes = iut.combine_footnotes(base.footnotes, value.footnotes)
if override.full then
if form ~= "" then
iut.insert_form(base.forms, slot, {form = form, footnotes = combined_notes})
end
else
-- Convert a null ending to "-" in the acc/voc sg slots so that e.g. [[Kerberos]] declared as
-- <m.sg.foreign.gena:u.acc-:a> works correctly and generates accusative 'Kerberos/Kerbera' not
-- #'Kerber/Kerbera'.
if (slot == "acc_s" or slot == "voc_s") and form == "" then
form = "-"
end
for _, stems in ipairs(base.stem_sets) do
add(base, slot, stems, form, combined_notes)
end
end
end
end
end
end
end
local function add_decl(base, stems,
gen_s, dat_s, acc_s, voc_s, loc_s, ins_s,
nom_d, gen_d, dat_d,
nom_p, gen_p, dat_p, acc_p, loc_p, ins_p, nom_s, footnotes
)
add(base, "nom_s", stems, "-", footnotes)
add(base, "gen_s", stems, gen_s, footnotes)
add(base, "dat_s", stems, dat_s, footnotes)
add(base, "acc_s", stems, acc_s, footnotes)
add(base, "voc_s", stems, voc_s, footnotes)
add(base, "loc_s", stems, loc_s, footnotes)
add(base, "ins_s", stems, ins_s, footnotes)
add(base, "nom_d", stems, nom_d, footnotes)
add(base, "gen_d", stems, gen_d, footnotes)
add(base, "dat_d", stems, dat_d, footnotes)
if base.number == "pl" then
-- If this is a plurale tantum noun and we're processing the nominative plural, use the user-specified lemma
-- rather than generating the plural from the synthesized singular, which may not match the specified lemma
-- (e.g. [[tvargle]] "Olomouc cheese" using <m.pl.mixed> would try to generate 'tvargle/tvargly', and [[peníze]]
-- "money" using <m.pl.#ě.genpl-> would try to generate 'peněze').
local acc_p_like_nom = m_table.deepEquals(nom_p, acc_p)
nom_p = "-"
if acc_p_like_nom then
acc_p = "-"
end
end
add(base, "nom_p", stems, nom_p, footnotes)
add(base, "gen_p", stems, gen_p, footnotes)
add(base, "dat_p", stems, dat_p, footnotes)
add(base, "acc_p", stems, acc_p, footnotes)
add(base, "loc_p", stems, loc_p, footnotes)
add(base, "ins_p", stems, ins_p, footnotes)
add(base, "nom_s", stems, nom_s, footnotes)
end
local function add_sg_decl(base, stems,
gen_s, dat_s, acc_s, voc_s, loc_s, ins_s, footnotes
)
add_decl(base, stems, gen_s, dat_s, acc_s, voc_s, loc_s, ins_s,
nil, nil, nil,
nil, nil, nil, nil, nil, nil, footnotes)
end
local function add_du_only_decl(base, stems,
gen_d, dat_d, footnotes
)
add_decl(base, stems, nil, nil, nil, nil, nil, nil,
"-", gen_d, dat_d,
nil, nil, nil, nil, nil, nil, footnotes)
end
local function add_pl_only_decl(base, stems,
gen_p, dat_p, acc_p, loc_p, ins_p, footnotes
)
add_decl(base, stems, nil, nil, nil, nil, nil, nil,
nil, nil, nil,
"-", gen_p, dat_p, acc_p, loc_p, ins_p, footnotes)
end
local function handle_derived_slots_and_overrides(base)
local function is_non_derived_slot(slot)
return slot ~= "voc_p" and slot ~= "acc_s" and slot ~= "clitic_acc_s"
end
local function is_derived_slot(slot)
return not is_non_derived_slot(slot)
end
base.slot_overridden = {}
-- Handle overrides for the non-derived slots. Do this before generating the derived
-- slots so overrides of the source slots (e.g. nom_p) propagate to the derived slots.
process_slot_overrides(base, is_non_derived_slot)
-- Generate the remaining slots that are derived from other slots.
if not base.pron and not base.det then
-- Pronouns don't have a vocative (singular or plural).
iut.insert_forms(base.forms, "voc_p", base.forms.nom_p)
end
if not base.forms.acc_s and not base.slot_overridden.acc_s then
iut.insert_forms(base.forms, "acc_s", base.forms[base.animacy == "inan" and "nom_s" or base.animacy == "pr" and "gen_s" or base.animacy == "anml" and "gen_s"])
end
if not base.forms.acc_d and not base.slot_overridden.acc_d then
iut.insert_forms(base.forms, "acc_d", base.forms[base.animacy == "inan" and "nom_d" or base.animacy == "pr" and "gen_d" or base.animacy == "anml" and "nom_d"])
end
if not base.forms.acc_p and not base.slot_overridden.acc_p then
iut.insert_forms(base.forms, "acc_p", base.forms[base.animacy == "inan" and "nom_p" or base.animacy == "pr" and "gen_p" or base.animacy == "anml" and "nom_p"])
end
if not base.forms.clitic_acc_s and not base.slot_overridden.clitic_acc_s then
iut.insert_forms(base.forms, "clitic_acc_s", base.forms[base.animacy == "inan" and "nom_s" or "clitic_gen_s"])
end
-- Handle overrides for derived slots, to allow them to be overridden.
process_slot_overrides(base, is_derived_slot)
-- Compute linked versions of potential lemma slots, for use in {{hsb-noun}}.
-- We substitute the original lemma (before removing links) for forms that
-- are the same as the lemma, if the original lemma has links.
for _, slot in ipairs(potential_lemma_slots) do
iut.insert_forms(base.forms, slot .. "_linked", iut.map_forms(base.forms[slot], function(form)
if form == base.orig_lemma_no_links and rfind(base.orig_lemma, "%[%[") then
return base.orig_lemma
else
return form
end
end))
end
end
-- Table mapping declension types to functions to decline the noun. The function takes two arguments, `base` and
-- `stems`; the latter specifies the computed stems (vowel vs. non-vowel, singular vs. plural) and whether the noun
-- is reducible and/or has vowel alternations in the stem. Most of the specifics of determining which stem to use
-- and how to modify it for the given ending are handled in add_decl(); the declension functions just need to generate
-- the appropriate endings.
local decls = {}
-- Table specifying additional properties for declension types. Every declension type must have such a table, which
-- specifies which category or categories to add and what annotation to show in the title bar of the declension table.
--
-- * Only the `cat` property of this table is mandatory; there is also a `desc` property to specify the annotation, but
-- this can be omitted and the annotation will then be computed from the `cat` property. The `cat` property is either
-- a string, a list of strings or a function (of two arguments, `base` and `stems` as above) returning a string or
-- list of strings. The string can contain the keywords GENDER to substitute the gender (and animacy for masculine
-- nouns) and POS (to substitute the pluralized part of speech). The keyword GENPOS is equivalent to 'GENDER POS'. If
-- no keyword is present, ' GENPOS' is added onto the end. If only GENDER is present, ' POS' is added onto the end.
-- In all cases, the language name is added onto the beginning to form the full category name.
-- * The `desc` property is of the same form as the `cat` property and specifies the annotation to display in the title
-- bar (which may have the same format as the category minus the part of speech, or may be abbreviated). The value
-- may not be a list of strings, as only one annotation is displayed. If omitted, it is derived from the category
-- spec(s) by taking the last category (if more than one is given) and removing ' POS' before keyword substitution.
local declprops = {}
decls["hard-m"] = function(base, stems)
local gen_s = base.animacy == "in" and {"a", "u"} or "a"
local nom_p = base.animacy == "pr" and "ojo" or "y"
local voc_s = not rmatch(base.lemma, ".*tr$") and "o"
add_decl(base, stems, gen_s, "ej", acc_s, voc_s, "u", "om",
"aj", "ow", "omaj",
nom_p, "ow", "am", nil, "ach", "ami")
add_decl(base, com.convert_paired_plain_to_palatal(com.apply_palatalization(stems.oblique_vowel_stem)), nil, nil, nil, "e", "e")
end
declprops["hard-m"] = {
desc = function(base, stems)
return "masculine hard stem"
end,
cat = function(base, stems)
return "masculine hard stem"
end
}
decls["soft-m"] = function(base, stems)
local gen_s = base.animacy == "in" and {"a", "u"} or "a"
local nom_p = base.animacy == "pr" and "ojo" or "e"
add_decl(base, com.addj(stems.oblique_vowel_stem), gen_s, "ej", acc_s, "o", "u", "om",
"ej", "ow", "omaj",
nom_p, "ow", "am", nil, "ach", "emi")
end
declprops["soft-m"] = {
desc = function(base, stems)
return "masculine soft stem"
end,
cat = function(base, stems)
return "masculine soft stem"
end
}
decls["czs-m"] = function(base, stems)
local gen_s = base.animacy == "in" and {"a", "u"} or "a"
local nom_p = base.animacy == "pr" and "ojo" or "y"
add_decl(base, stems, gen_s, "ej", acc_s, "o", "u", "om",
"aj", "ow", "omaj",
nom_p, "ow", "am", nil, "ach", "ami")
end
declprops["czs-m"] = {
desc = function(base, stems)
return "masculine hard hissing stem"
end,
cat = function(base, stems)
return "masculine hard stem"
end
}
decls["velar-m"] = function(base, stems)
local gen_s = base.animacy == "in" and {"a", "u"} or "a"
local nom_p = base.animacy == "pr" and "ojo" or "i"
add_decl(base, stems, gen_s, "ej", acc_s, "o", "u", "om",
"aj", "ow", "omaj",
nom_p, "ow", "am", nil, "ach", "ami")
add_decl(base, com.convert_paired_plain_to_palatal(com.apply_palatalization(stems.oblique_vowel_stem)), nil, nil, nil, nil, "e")
end
declprops["velar-m"] = {
desc = function(base, stems)
return "masculine velar stem"
end,
cat = function(base, stems)
return "masculine velar stem"
end
}
decls["adje-m"] = function(base, stems)
if rmatch(base.lemma, "^.*" .. com.velar_c .. "i$") then
add_decl(base, stems, "eho", "emu", nil, "-", "im", "im",
nom_d, "eju", "imaj",
nom_p, "ich", "im", nil, "ich", "imi")
if base.animacy == "pr" then
add_decl(base, com.apply_palatalization(stems.oblique_vowel_stem), nil, nil, nil, nil, nil, nil, nil, nil, nil, "y")
add_decl(base, stems, nil, nil, nil, nil, nil, nil, "aj")
else
add_decl(base, stems, nil, nil, nil, nil, nil, nil, "ej", nil, nil, "e")
end
elseif rmatch(base.lemma, "^.*" .. com.inherently_soft_c .. "i$") then
local nom_p = base.animacy == "pr" and "i" or "e"
add_decl(base, stems, "eho", "emu", nil, "-", "im", "im",
"ej", "eju", "imaj",
nom_p, "ich", "im", nil, "ich", "imi")
elseif rmatch(base.lemma, "^.*[czs]e$") then
local nom_p = base.animacy == "pr" and "y" or "e"
local nom_d = base.animacy == "pr" and "aj" or "ej"
add_decl(base, stems, "eho", "emu", nil, "-", "ym", "ym",
nom_d, "eju", "ymaj",
nom_p, "ych", "ym", nil, "ych", "ymi")
else
add_decl(base, stems, "eho", "emu", nil, "-", "ym", "ym",
nom_d, "eju", "ymaj",
nom_p, "ych", "ym", nil, "ych", "ymi")
if base.animacy == "pr" then
add_decl(base, com.apply_palatalization(stems.oblique_vowel_stem), nil, nil, nil, nil, nil, nil, nil, nil, nil, "i")
add_decl(base, stems, nil, nil, nil, nil, nil, nil, "aj")
else
add_decl(base, stems, nil, nil, nil, nil, nil, nil, "ej", nil, nil, "e")
end
end
end
declprops["adje-m"] = {
desc = function(base, stems)
return ""
end,
cat = function(base, stems)
return "masculine adjectival"
end
}
decls["hard-f"] = function(base, stems)
add_decl(base, stems, "y", nil, "u", "-", nil, "u",
nil, "ow", "omaj",
"y", "ow", "am", "y", "ach", "ami")
add_decl(base, com.convert_paired_plain_to_palatal(com.apply_palatalization(stems.oblique_vowel_stem)), nil, "e", nil, nil, "e", nil, "e")
end
declprops["hard-f"] = {
desc = function(base, stems)
return "feminine hard stem"
end,
cat = function(base, stems)
return "feminine hard stem"
end
}
decls["soft-f"] = function(base, stems)
if rmatch(base.lemma, "^(.*" .. com.cons_c .. ")$") then
add_decl(base, com.addj(stems.oblique_vowel_stem), "e", nil, "-", "-", nil, "u",
nil, "ow", "omaj",
"e", "ow", "am", "e", "ach", "emi")
else
add_decl(base, stems, "e", nil, "u", "-", nil, "u",
nil, "ow", "omaj",
"e", "ow", "am", "e", "ach", "emi")
end
add_decl(base, com.removej(com.addj(stems.oblique_vowel_stem)), nil, "i", nil, nil, "i", nil, "i", nil, nil, nil, "i")
end
declprops["soft-f"] = {
desc = function(base, stems)
return "feminine soft stem"
end,
cat = function(base, stems)
return "feminine soft stem"
end
}
decls["czs-f"] = function(base, stems)
if rmatch(base.lemma, "^(.*" .. com.cons_c .. ")$") then
add_decl(base, stems, "y", "y", "-", "-", "y", "u",
"y", "ow", "omaj",
"y", "ow", "am", "y", "ach", "ami")
else
add_decl(base, stems, "y", "y", "u", "-", "y", "u",
"y", "ow", "omaj",
"y", "ow", "am", "y", "ach", "ami")
end
end
declprops["czs-f"] = {
desc = function(base, stems)
return "feminine hard hissing stem"
end,
cat = function(base, stems)
return "feminine hard stem"
end
}
decls["velar-f"] = function(base, stems)
add_decl(base, stems, "i", nil, "u", "-", nil, "u",
nil, "ow", "omaj",
"i", "ow", "am", "i", "ach", "ami")
add_decl(base, com.convert_paired_plain_to_palatal(com.apply_palatalization(stems.oblique_vowel_stem)), nil, "e", nil, nil, "e", nil, "e")
end
declprops["velar-f"] = {
desc = function(base, stems)
return "feminine velar stem"
end,
cat = function(base, stems)
return "feminine velar stem"
end
}
decls["v-f"] = function(base, stems)
add_decl(base, stems, "wje", "wi", "-", "-", "wi", "wju",
"wi", "wjow", "wjomaj",
"wje", "wjow", "wjam", "wje", "wjach", "wjemi")
end
declprops["v-f"] = {
desc = function(base, stems)
return "feminine v-stem"
end,
cat = function(base, stems)
return "feminine v-stem"
end
}
decls["adje-f"] = function(base, stems)
if rmatch(base.lemma, "^.*" .. com.velar_c .. "a$") or rmatch(base.lemma, "^.*" .. com.inherently_soft_c .. "a$") then
add_decl(base, stems, "eje", "ej", "u", "-", "ej", "ej",
"ej", "eju", "imaj",
"e", "ich", "im", "e", "ich", "imi")
else
add_decl(base, stems, "eje", "ej", "u", "-", "ej", "ej",
"ej", "eju", "ymaj",
"e", "ych", "ym", "e", "ych", "ymi")
end
end
declprops["adje-f"] = {
desc = function(base, stems)
return ""
end,
cat = function(base, stems)
return "feminine adjectival"
end
}
decls["hard-n"] = function(base, stems)
add_decl(base, stems, "a", "u", "-", "-", nil, "om",
nil, "ow", "omaj",
"a", "ow", "am", "a", "ach", "ami")
add_decl(base, com.convert_paired_plain_to_palatal(com.apply_palatalization(stems.oblique_vowel_stem)), nil, nil, nil, nil, "e", nil, "e")
end
declprops["hard-n"] = {
desc = function(base, stems)
return "neuter hard stem"
end,
cat = function(base, stems)
return "neuter hard stem"
end
}
decls["soft-n"] = function(base, stems)
add_decl(base, stems, "a", "u", "-", "-", "u", "om",
nil, "ow", "omaj",
"a", "ow", "am", "a", "ach", "emi")
add_decl(base, com.removej(stems.oblique_vowel_stem), nil, nil, nil, nil, nil, nil, "i")
end
declprops["soft-n"] = {
desc = function(base, stems)
return "neuter soft stem"
end,
cat = function(base, stems)
return "neuter soft stem"
end
}
decls["czs-n"] = function(base, stems)
add_decl(base, stems, "a", "u", "-", "-", "u", "om",
"y", "ow", "omaj",
"a", "ow", "am", "a", "ach", "ami")
end
declprops["czs-n"] = {
desc = function(base, stems)
return "neuter hard hissing stem"
end,
cat = function(base, stems)
return "neuter hard stem"
end
}
decls["velar-n"] = function(base, stems)
add_decl(base, stems, "a", "u", "-", "-", "u", "om",
nil, "ow", "omaj",
"a", "ow", "am", "a", "ach", "ami")
add_decl(base, com.convert_paired_plain_to_palatal(com.apply_palatalization(stems.oblique_vowel_stem)), nil, nil, nil, nil, "e", nil, "e")
end
declprops["velar-n"] = {
desc = function(base, stems)
return "neuter velar stem"
end,
cat = function(base, stems)
return "neuter velar stem"
end
}
decls["adje-n"] = function(base, stems)
if rmatch(base.lemma, "^.*" .. com.velar_c .. "e$") or rmatch(base.lemma, "^.*" .. com.inherently_soft_c .. "e$") then
add_decl(base, stems, "eho", "emu", "-", "-", "im", "im",
"ej", "eju", "imaj",
"e", "ich", "im", "e", "ich", "imi")
else
add_decl(base, stems, "eje", "ej", "u", "-", "ym", "ym",
"ej", "eju", "ymaj",
"e", "ych", "ym", "e", "ych", "ymi")
end
end
declprops["adje-n"] = {
desc = function(base, stems)
return ""
end,
cat = function(base, stems)
return "neuter adjectival"
end
}
decls["tstem-n"] = function(base, stems)
add_decl(base, stems, "eća", "eću", "-", "-", "eću", "ećom",
"eći", "ećow", "ećomaj",
"ata", "atow", "atam", "ata", "atach", "atami")
end
declprops["tstem-n"] = {
desc = function(base, stems)
return "neuter t-stem"
end,
cat = function(base, stems)
return "neuter t-stem"
end
}
decls["nstem-n"] = function(base, stems)
add_decl(base, stems, "enja", "enju", "-", "-", "enju", "enjom",
"eni", "enjow", "enjomaj",
"enja", "enjow", "enjam", "enja", "enjach", "enjami")
end
declprops["nstem-n"] = {
desc = function(base, stems)
return "neuter n-stem"
end,
cat = function(base, stems)
return "neuter n-stem"
end
}
decls["adj"] = function(base, stems)
local props = {}
local propspec = table.concat(props, ".")
if propspec ~= "" then
propspec = "<" .. propspec .. ">"
end
local adj_alternant_multiword_spec = require("Module:zlw-ocs-adjective").do_generate_forms({base.lemma .. propspec})
local function copy(from_slot, to_slot)
base.forms[to_slot] = adj_alternant_multiword_spec.forms[from_slot]
end
if base.number ~= "pl" then
if base.gender == "m" then
copy("nom_m", "nom_s")
copy("gen_mn", "gen_s")
copy("dat_mn", "dat_s")
copy("loc_mn", "loc_s")
copy("ins_mn", "ins_s")
elseif base.gender == "f" then
copy("nom_f", "nom_s")
copy("gen_f", "gen_s")
copy("dat_f", "dat_s")
copy("acc_f", "acc_s")
copy("loc_f", "loc_s")
copy("ins_f", "ins_s")
else
copy("nom_n", "nom_s")
copy("gen_mn", "gen_s")
copy("dat_mn", "dat_s")
copy("acc_n", "acc_s")
copy("loc_mn", "loc_s")
copy("ins_mn", "ins_s")
end
if not base.forms.voc_s then
iut.insert_forms(base.forms, "voc_s", base.forms.nom_s)
end
end
if base.number ~= "sg" then
if base.gender == "m" then
copy("nom_mp", "nom_p")
copy("acc_mfp", "acc_p")
copy("nom_md", "nom_d")
elseif base.gender == "f" then
copy("nom_fp", "nom_p")
copy("acc_mfp", "acc_p")
copy("nom_fnd", "nom_d")
else
copy("nom_np", "nom_p")
copy("acc_np", "acc_p")
copy("nom_fnd", "nom_d")
end
copy("gen_p", "gen_p")
copy("dat_p", "dat_p")
copy("ins_p", "ins_p")
copy("loc_p", "loc_p")
copy("gen_d", "gen_d")
copy("dat_d", "dat_d")
end
end
local function get_stemtype(base)
if rfind(base.lemma, "ý$") then
return "hard"
elseif rfind(base.lemma, "í$") then
return "soft"
else
return "possessive"
end
end
declprops["adj"] = {
cat = function(base, stems)
return {"adjectival POS", get_stemtype(base) .. " GENDER adjectival POS"}
end,
}
decls["indecl"] = function(base, stems)
-- Indeclinable. Note that fully indeclinable nouns should not have a table at all rather than one all of whose forms
-- are the same; but having an indeclinable declension is useful for nouns that may or may not be indeclinable, e.g.
-- [[desatero]] "group of ten" or the plural of [[peso]], which may be indeclinable 'pesos'.
add_decl(base, stems, "-", "-", "-", "-", "-", "-",
"-", "-", "-", "-", "-", "-", "-", "-", "-")
end
declprops["indecl"] = {
cat = function(base, stems)
if base.adj then
return {"adjectival POS", "indeclinable adjectival POS", "indeclinable GENDER adjectival POS"}
else
return {"indeclinable POS", "indeclinable GENPOS"}
end
end
}
decls["manual"] = function(base, stems)
-- Anything declined manually using overrides. We don't set any declensions except the nom_s (or nom_p if plurale
-- tantum).
add(base, base.number == "pl" and "nom_p" or "nom_s", stems, "-")
end
declprops["manual"] = {
desc = "GENDER",
cat = {},
}
local function set_pron_defaults(base)
if base.gender or base.lemma ~= "ona" and base.number or base.animacy then
error("Can't specify gender, number or animacy for pronouns")
end
local function pron_props()
-- Return values are GENDER, NUMBER, ANIMACY, HAS_CLITIC.
if base.lemma == "štó" then
return "none", "sg", "pr", false
elseif base.lemma == "što" then
return "none", "sg", "inan", false
else
error(("Unrecognized pronoun '%s'"):format(base.lemma))
end
end
local gender, number, animacy, has_clitic = pron_props()
base.gender = gender
base.actual_gender = gender
base.number = number
base.actual_number = number
base.animacy = animacy
base.actual_animacy = animacy
base.has_clitic = has_clitic
end
local function determine_pronoun_stems(base)
if base.stem_sets then
error("Reducible and vowel alternation specs cannot be given with pronouns")
end
base.stem_sets = {{reducible = false, vowel_stem = "", nonvowel_stem = ""}}
base.decl = "pron"
end
decls["pron"] = function(base, stems)
if base.lemma == "štó" then
add_decl(base, stems, "koho", "komu", nil, nil, "kim", "kim")
elseif base.lemma == "što" then
add_decl(base, stems, "čeho", "čemu", nil, nil, "čim", "čim")
else
error(("Internal error: Unrecognized pronoun lemma '%s'"):format(base.lemma))
end
end
declprops["pron"] = {
desc = "GENDER pronoun",
cat = {},
}
local function set_num_defaults(base)
if base.gender or base.animacy then
error("Can't specify gender, number or animacy for numeral")
end
local function num_props()
-- Return values are GENDER, NUMBER, ANIMACY, HAS_CLITIC.
return "none", "pl", "none", false
end
local gender, number, animacy, has_clitic = num_props()
base.gender = gender
base.actual_gender = gender
base.number = number
base.actual_number = number
base.animacy = animacy
base.actual_animacy = animacy
base.has_clitic = has_clitic
end
local function set_det_defaults(base)
if base.gender or base.number or base.animacy then
error("Can't specify gender, number or animacy for determiner")
end
local function det_props()
-- Return values are GENDER, NUMBER, ANIMACY, HAS_CLITIC.
return "none", "none", "none", false
end
local gender, number, animacy, has_clitic = det_props()
base.gender = gender
base.actual_gender = gender
base.number = number
base.actual_number = number
base.animacy = animacy
base.actual_animacy = animacy
base.has_clitic = has_clitic
end
local function determine_determiner_stems(base)
if base.stem_sets then
error("Reducible and vowel alternation specs cannot be given with determiners")
end
local stem = rmatch(base.lemma, "^(.*)" .. com.vowel_c .. "$") or base.lemma
base.stem_sets = {{reducible = false, vowel_stem = stem, nonvowel_stem = stem}}
base.decl = "det"
end
decls["det"] = function(base, stems)
add_sg_decl(base, stems, "a", "a", "-", nil, "a", "a")
end
declprops["det"] = {
desc = "GENDER determiner",
cat = {},
}
local function fetch_footnotes(separated_group)
local footnotes
for j = 2, #separated_group - 1, 2 do
if separated_group[j + 1] ~= "" then
error("Extraneous text after bracketed footnotes: '" .. table.concat(separated_group) .. "'")
end
if not footnotes then
footnotes = {}
end
table.insert(footnotes, separated_group[j])
end
return footnotes
end
local function parse_override(segments)
local retval = {values = {}}
local part = segments[1]
local slots = {}
while true do
local case = usub(part, 1, 3)
if cases[case] then
-- ok
else
error(("Unrecognized case '%s' in override: '%s'"):format(case, table.concat(segments)))
end
part = usub(part, 4)
local slot
if rfind(part, "^pl") then
part = usub(part, 3)
slot = case .. "_p"
elseif rfind(part, "^du") then
part = usub(part, 3)
slot = case .. "_d"
else
slot = case .. "_s"
end
table.insert(slots, slot)
if rfind(part, "^%+") then
part = usub(part, 2)
else
break
end
end
if rfind(part, "^:") then
retval.full = true
part = usub(part, 2)
end
segments[1] = part
local colon_separated_groups = put.split_alternating_runs_and_strip_spaces(segments, ":")
for i, colon_separated_group in ipairs(colon_separated_groups) do
local value = {}
local form = colon_separated_group[1]
if form == "" then
error(("Use - to indicate an empty ending for slot%s '%s': '%s'"):format(#slots > 1 and "s" or "", table.concat(slots), table.concat(segments)))
elseif form == "-" then
value.form = ""
else
value.form = form
end
value.footnotes = fetch_footnotes(colon_separated_group)
table.insert(retval.values, value)
end
return slots, retval
end
--[=[
Parse an indicator spec (text consisting of angle brackets and zero or more
dot-separated indicators within them). Return value is an object of the form
{
overrides = {
SLOT = {OVERRIDE, OVERRIDE, ...}, -- as returned by parse_override()
...
},
forms = {}, -- forms for a single spec alternant; see `forms` below
footnotes = {"FOOTNOTE", "FOOTNOTE", ...}, -- may be missing
stems = { -- may be missing
{
reducible = TRUE_OR_FALSE,
footnotes = {"FOOTNOTE", "FOOTNOTE", ...}, -- may be missing
-- The following fields are filled in by determine_stems()
vowel_stem = "STEM",
nonvowel_stem = "STEM",
oblique_slots = "all",
oblique_vowel_stem = "STEM" or nil (only needs to be set if oblique_slots is non-nil),
oblique_nonvowel_stem = "STEM" or nil (only needs to be set if oblique_slots is non-nil),
},
...
},
gender = "GENDER", -- "m", "f", "n"
number = "NUMBER", -- "sg", "pl"; may be missing
animacy = "ANIMACY", -- "inan", "an"; may be missing
hard = true, -- may be missing
soft = true, -- may be missing
mixed = true, -- may be missing
surname = true, -- may be missing
istem = true, -- may be missing
["-istem"] = true, -- may be missing
tstem = true, -- may be missing
nstem = true, -- may be missing
tech = true, -- may be missing
foreign = true, -- may be missing
mostlyindecl = true, -- may be missing
indecl = true, -- may be missing
manual = true, -- may be missing
adj = true, -- may be missing
decllemma = "DECLENSION-LEMMA", -- may be missing
declgender = "DECLENSION-GENDER", -- may be missing
declnumber = "DECLENSION-NUMBER", -- may be missing
-- The following additional fields are added by other functions:
orig_lemma = "ORIGINAL-LEMMA", -- as given by the user
orig_lemma_no_links = "ORIGINAL-LEMMA-NO-LINKS", -- links removed
lemma = "LEMMA", -- `orig_lemma_no_links`, converted to singular form if plural and lowercase if all-uppercase
forms = {
SLOT = {
{
form = "FORM",
footnotes = {"FOOTNOTE", "FOOTNOTE", ...} -- may be missing
},
...
},
...
},
decl = "DECL", -- declension, e.g. "hard-m"
vowel_stem = "VOWEL-STEM", -- derived from vowel-ending lemmas
nonvowel_stem = "NONVOWEL-STEM", -- derived from non-vowel-ending lemmas
}
]=]
local function parse_indicator_spec(angle_bracket_spec)
local inside = rmatch(angle_bracket_spec, "^<(.*)>$")
assert(inside)
local base = {overrides = {}, forms = {}}
if inside ~= "" then
local segments = put.parse_balanced_segment_run(inside, "[", "]")
local dot_separated_groups = put.split_alternating_runs_and_strip_spaces(segments, "%.")
for i, dot_separated_group in ipairs(dot_separated_groups) do
local part = dot_separated_group[1]
local case_prefix = usub(part, 1, 3)
if cases[case_prefix] then
local slots, override = parse_override(dot_separated_group)
for _, slot in ipairs(slots) do
if base.overrides[slot] then
error(("Two overrides specified for slot '%s'"):format(slot))
else
base.overrides[slot] = {override}
end
end
elseif part == "" then
if #dot_separated_group == 1 then
error("Blank indicator: '" .. inside .. "'")
end
base.footnotes = fetch_footnotes(dot_separated_group)
elseif rfind(part, "^[-*#ě]*$") or rfind(part, "^[-*#ě]*,") then
if base.stem_sets then
error("Can't specify reducible/vowel-alternant indicator twice: '" .. inside .. "'")
end
local comma_separated_groups = put.split_alternating_runs_and_strip_spaces(dot_separated_group, ",")
local stem_sets = {}
for i, comma_separated_group in ipairs(comma_separated_groups) do
local pattern = comma_separated_group[1]
local orig_pattern = pattern
local reducible, vowelalt, oblique_slots
if pattern == "-" then
-- default reducible, no vowel alt
else
local before, after
before, reducible, after = rmatch(pattern, "^(.-)(%-?%*)(.-)$")
if before then
pattern = before .. after
reducible = reducible == "*"
end
if pattern ~= "" then
if not rfind(pattern, "^##?ě?$") then
error("Unrecognized vowel-alternation pattern '" .. pattern .. "', should be one of #, ##, #ě or ##ě: '" .. inside .. "'")
end
if pattern == "#ě" or pattern == "##ě" then
vowelalt = "quant-ě"
else
vowelalt = "quant"
end
-- `oblique_slots` will be later changed to "all" if the lemma ends in a consonant.
oblique_slots = "all"
end
end
table.insert(stem_sets, {
reducible = reducible,
vowelalt = vowelalt,
oblique_slots = oblique_slots,
footnotes = fetch_footnotes(comma_separated_group)
})
end
base.stem_sets = stem_sets
elseif #dot_separated_group > 1 then
error("Footnotes only allowed with slot overrides, reducible or vowel alternation specs or by themselves: '" .. table.concat(dot_separated_group) .. "'")
elseif part == "m" or part == "f" or part == "n" then
if base.gender then
error("Can't specify gender twice: '" .. inside .. "'")
end
base.gender = part
elseif part == "sg" or part == "du" or part == "pl" then
if base.number then
error("Can't specify number twice: '" .. inside .. "'")
end
base.number = part
elseif part == "pr" or part == "anml" or part == "inan" then
if base.animacy then
error("Can't specify animacy twice: '" .. inside .. "'")
end
base.animacy = part
elseif part == "hard" or part == "soft" or part == "istem" or part == "tstem" or part == "nstem" or
part == "indecl" or part == "pron" or part == "det" or part == "velar" or part == "vstem" or part == "adje" then
if base[part] then
error("Can't specify '" .. part .. "' twice: '" .. inside .. "'")
end
base[part] = true
elseif part == "+" then
if base.adj then
error("Can't specify '+' twice: '" .. inside .. "'")
end
base.adj = true
elseif part == "!" then
if base.manual then
error("Can't specify '!' twice: '" .. inside .. "'")
end
base.manual = true
elseif rfind(part, "^mixedistem:") then
if base.mixedistem then
error("Can't specify 'mixedistem:' twice: '" .. inside .. "'")
end
base.mixedistem = rsub(part, "^mixedistem:", "")
elseif rfind(part, "^decllemma:") then
if base.decllemma then
error("Can't specify 'decllemma:' twice: '" .. inside .. "'")
end
base.decllemma = rsub(part, "^decllemma:", "")
elseif rfind(part, "^declgender:") then
if base.declgender then
error("Can't specify 'declgender:' twice: '" .. inside .. "'")
end
base.declgender = rsub(part, "^declgender:", "")
elseif rfind(part, "^declnumber:") then
if base.declnumber then
error("Can't specify 'declnumber:' twice: '" .. inside .. "'")
end
base.declnumber = rsub(part, "^declnumber:", "")
else
error("Unrecognized indicator '" .. part .. "': '" .. inside .. "'")
end
end
end
return base
end
local function is_regular_noun(base)
return not base.adj and not base.pron and not base.det and not base.num
end
local function process_declnumber(base)
base.actual_number = base.number
if base.declnumber then
if base.declnumber == "sg" or base.declnumber == "du" or base.declnumber == "pl" then
base.number = base.declnumber
else
error(("Unrecognized value '%s' for 'declnumber', should be 'sg' or 'pl'"):format(base.declnumber))
end
end
end
local function set_defaults_and_check_bad_indicators(base)
-- Set default values.
local regular_noun = is_regular_noun(base)
if base.pron then
set_pron_defaults(base)
elseif base.det then
set_det_defaults(base)
elseif base.num then
set_num_defaults(base)
elseif not base.adj then
if not base.gender then
if base.manual then
base.gender = "none"
else
error("For nouns, gender must be specified")
end
end
base.number = base.number or "allthree"
process_declnumber(base)
base.animacy = base.animacy or "inan"
base.actual_gender = base.gender
base.actual_animacy = base.animacy
if base.declgender then
if base.declgender == "m-an" then
base.gender = "m"
base.animacy = "pr"
elseif base.declgender == "m-in" then
base.gender = "m"
base.animacy = "inan"
elseif base.declgender == "f" or base.declgender == "n" then
base.gender = base.declgender
else
error(("Unrecognized value '%s' for 'declgender', should be 'm-an', 'm-in', 'f' or 'n'"):format(base.declgender))
end
end
end
-- Check for bad indicator combinations.
if (base.hard and 1 or 0) + (base.soft and 1 or 0) > 1 then
error("At most one of 'hard' or 'soft' can be specified")
end
if base.istem and base["-istem"] then
error("'istem' and '-istem' cannot be specified together")
end
if (base.istem or base["-istem"]) then
if not regular_noun then
error("'istem' and '-istem' can only be specified with regular nouns")
end
end
if base.declgender and not regular_noun then
error("'declgender' can only be specified with regular nouns")
end
end
local function set_all_defaults_and_check_bad_indicators(alternant_multiword_spec)
local is_multiword = #alternant_multiword_spec.alternant_or_word_specs > 1
iut.map_word_specs(alternant_multiword_spec, function(base)
set_defaults_and_check_bad_indicators(base)
base.multiword = is_multiword -- FIXME: not currently used; consider deleting
alternant_multiword_spec.has_clitic = alternant_multiword_spec.has_clitic or base.has_clitic
if base.pron then
alternant_multiword_spec.saw_pron = true
else
alternant_multiword_spec.saw_non_pron = true
end
if base.det then
alternant_multiword_spec.saw_det = true
else
alternant_multiword_spec.saw_non_det = true
end
if base.num then
alternant_multiword_spec.saw_num = true
else
alternant_multiword_spec.saw_non_num = true
end
end)
end
local function undo_second_palatalization(base, word, is_adjective)
local function try(from, to)
local stem = rmatch(word, "^(.*)" .. from .. "$")
if stem then
return stem .. to
end
return nil
end
return is_adjective and try("št", "sk") or
is_adjective and try("čt", "ck") or
try("c", "k") or -- FIXME, this could be wrong and c correct
try("ř", "r") or
try("z", "h") or -- FIXME, this could be wrong and z or g correct
try("š", "ch") or
word
end
-- For a plural-only lemma, synthesize a likely singular lemma. It doesn't have to be
-- theoretically correct as long as it generates all the correct plural forms.
local function synthesize_singular_lemma(base)
if not base.stem_sets then
base.stem_sets = {{}}
end
local lemma_determined
-- Loop over all stem sets in case the user specified multiple ones (e.g. '*,-*'). If we try to reconstruct
-- different lemmas for different stem sets, we'll throw an error below.
for _, stems in ipairs(base.stem_sets) do
local stem, lemma
while true do
if base.indecl then
-- If specified as indeclinable, leave it alone; e.g. 'pesos' indeclinable plural of [[peso]].
lemma = base.lemma
break
elseif base.gender == "m" then
stem = rmatch(base.lemma, "^(.*)ojo$")
if stem then
lemma = undo_second_palatalization(base, stem)
else
stem = rmatch(base.lemma, "^(.*)[oyie]$")
if stem then
lemma = stem
else
error(("Masculine plural-only lemma '%s' should end in -ojo, -o, -y, -i or -e"):format(base.lemma))
end
end
if stems.reducible == nil then
if rfind(lemma, com.cons_c .. "[ck]$") and not com.is_monosyllabic(base.lemma) then
stems.reducible = true
end
if stems.reducible then
lemma = dereduce(base, lemma)
end
end
break
elseif base.gender == "f" then
stem = rmatch(base.lemma, "^(.*)y$")
if stem then
lemma = stem .. "a"
break
end
stem = rmatch(base.lemma, "^(.*)[eě]$")
if stem then
-- Singular like the plural. Cons-stem feminines like [[dlaň]] "palm (of the hand)" have identical
-- plurals to soft-stem feminines like [[růže]] (modulo e/ě differences), so we don't need to
-- reconstruct the former type.
lemma = base.lemma
break
end
stem = rmatch(base.lemma, "^(.*)i$")
if stem then
-- i-stems.
lemma = stem
base.istem = true
break
end
error(("Feminine plural-only lemma '%s' should end in -y, -ě, -e or -i"):format(base.lemma))
elseif base.gender == "n" then
-- -ata nouns like [[slůně]] "baby elephant" nom pl 'slůňata' are declined in the plural same as if
-- the singular were 'slůňato' so we don't have to worry about them.
stem = rmatch(base.lemma, "^(.*)a$")
if stem then
lemma = stem .. "o"
break
end
stem = rmatch(base.lemma, "^(.*)[eěí]$")
if stem then
-- singular lemma also in -e, -ě or -í; e.g. [[věčná loviště]] "[[happy hunting ground]]"
lemma = base.lemma
break
end
error(("Neuter plural-only lemma '%s' should end in -a, -í, -ě or -e"):format(base.lemma))
else
error(("Internal error: Unrecognized gender '%s'"):format(base.gender))
end
end
if lemma_determined and lemma_determined ~= lemma then
error(("Attempt to set two different singular lemmas '%s' and '%s'"):format(lemma_determined, lemma))
end
lemma_determined = lemma
end
base.lemma = lemma_determined
end
-- For an adjectival lemma, synthesize the masc singular form.
local function synthesize_adj_lemma(base)
local stem
if base.indecl then
base.decl = "indecl"
stem = base.lemma
else
local gender, number
local function sub_ov(stem)
stem = stem:gsub("ov$", "ův")
return stem
end
while true do
if base.number == "pl" then
if base.gender == "m" then
stem = rmatch(base.lemma, "^(.*)í$")
if stem then
if base.soft then
-- nothing to do
else
if base.animacy ~= "pr" then
error(("Masculine plural-only adjectival lemma '%s' ending in -í can only be animate unless '.soft' is specified"):
format(base.lemma))
end
base.lemma = undo_second_palatalization(base, stem, "is adjective") .. "ý"
end
break
end
stem = rmatch(base.lemma, "^(.*)é$")
if stem then
if base.animacy == "pr" then
error(("Masculine plural-only adjectival lemma '%s' ending in -é must be inanimate"):
format(base.lemma))
end
base.lemma = stem .. "ý"
break
end
stem = rmatch(base.lemma, "^(.*ov)i$") or rmatch(base.lemma, "^(.*in)i$")
if stem then
if base.animacy ~= "pr" then
error(("Masculine plural-only possessive adjectival lemma '%s' ending in -i must be animate"):
format(base.lemma))
end
base.lemma = sub_ov(stem)
break
end
stem = rmatch(base.lemma, "^(.*ov)y$") or rmatch(base.lemma, "^(.*in)y$")
if stem then
if base.animacy == "pr" then
error(("Masculine plural-only possessive adjectival lemma '%s' ending in -y must be inanimate"):
format(base.lemma))
end
base.lemma = sub_ov(stem)
break
end
if base.animacy == "pr" then
error(("Animate masculine plural-only adjectival lemma '%s' should end in -í, -ovi or -ini"):
format(base.lemma))
elseif base.soft then
error(("Soft masculine plural-only adjectival lemma '%s' should end in -í"):format(base.lemma))
else
error(("Inanimate masculine plural-only adjectival lemma '%s' should end in -é, -ovy or -iny"):
format(base.lemma))
end
elseif base.gender == "f" then
stem = rmatch(base.lemma, "^(.*)é$") -- hard adjective
if stem then
base.lemma = stem .. "ý"
break
end
stem = rmatch(base.lemma, "^(.*)í$") -- soft adjective
if stem then
break
end
stem = rmatch(base.lemma, "^(.*ov)y$") or rmatch(base.lemma, "^(.*in)y$") -- possessive adjective
if stem then
base.lemma = sub_ov(stem)
break
end
error(("Feminine plural-only adjectival lemma '%s' should end in -é, -í, -ovy or -iny"):format(base.lemma))
else
stem = rmatch(base.lemma, "^(.*)á$") -- hard adjective
if stem then
base.lemma = stem .. "ý"
break
end
stem = rmatch(base.lemma, "^(.*)í$") -- soft adjective
if stem then
break
end
stem = rmatch(base.lemma, "^(.*ov)a$") or rmatch(base.lemma, "^(.*in)a$") -- possessive adjective
if stem then
base.lemma = sub_ov(stem)
break
end
error(("Neuter plural-only adjectival lemma '%s' should end in -á, -í, -ova or -ina"):format(base.lemma))
end
else
if base.gender == "m" then
stem = rmatch(base.lemma, "^(.*)[ýí]$") or rmatch(base.lemma, "^(.*)ův$") or rmatch(base.lemma, "^(.*)in$")
if stem then
break
end
error(("Masculine adjectival lemma '%s' should end in -ý, -í, -ův or -in"):format(base.lemma))
elseif base.gender == "f" then
stem = rmatch(base.lemma, "^(.*)á$")
if stem then
base.lemma = stem .. "ý"
break
end
stem = rmatch(base.lemma, "^(.*)í$")
if stem then
break
end
stem = rmatch(base.lemma, "^(.*ov)a$") or rmatch(base.lemma, "^(.*in)a$")
if stem then
base.lemma = sub_ov(stem)
break
end
error(("Feminine adjectival lemma '%s' should end in -á, -í, -ova or -ina"):format(base.lemma))
else
stem = rmatch(base.lemma, "^(.*)í$")
if stem then
break
end
stem = rmatch(base.lemma, "^(.*ov)o$") or rmatch(base.lemma, "^(.*in)o$")
if stem then
base.lemma = sub_ov(stem)
break
end
error(("Neuter adjectival lemma '%s' should end in -é, -í, -ovo or -ino"):format(base.lemma))
end
end
end
base.decl = "adj"
end
-- Now set the stem sets if not given.
-- Now set the stem sets if not given.
if not base.stem_sets then
base.stem_sets = {{reducible = false}}
end
for _, stems in ipairs(base.stem_sets) do
-- Set the stems.
stems.vowel_stem = stem
stems.nonvowel_stem = stem
end
end
-- Determine the declension based on the lemma, gender and number. The declension is set in base.decl. In the process,
-- we set either base.vowel_stem (if the lemma ends in a vowel) or base.nonvowel_stem (if the lemma does not end in a
-- vowel), which is used by determine_stems(). In some cases (specifically with certain foreign nouns), we set
-- base.lemma to a new value; this is as if the user specified 'decllemma:'.
local function determine_declension(base)
if base.indecl then
base.decl = "indecl"
base.nonvowel_stem = base.lemma
return
end
-- Determine declension
stem = rmatch(base.lemma, "^(.*)a$")
if stem then
if base.gender == "m" then
if base.animacy ~= "pr" then
error("Masculine lemma in -a must be animate")
end
base.decl = "a-m"
elseif base.gender == "f" then
if base.hard then
base.decl = "hard-f"
elseif base.soft then
base.decl = "soft-f"
elseif base.adje then
base.decl = "adje-f"
elseif rfind(base.lemma, com.velar_c .. "a$") then
base.decl = "velar-f"
elseif rfind(base.lemma, "[czs]" .. "a$") then
base.decl = "czs-f"
elseif rfind(base.lemma, com.inherently_soft_c .. "a$") then
base.decl = "soft-f"
else
base.decl = "hard-f"
end
elseif base.gender == "n" then
if rfind(stem, "m$") then
base.decl = "ma-n"
else
error("Lemma ending in -a and neuter must end in -ma")
end
end
base.vowel_stem = stem
return
end
local ending
stem, ending = rmatch(base.lemma, "^(.*)e$")
if stem then
if base.tstem then
base.decl = "tstem-n"
elseif base.adje then
base.decl = "adje-n"
else
base.decl = "soft-n"
end
base.vowel_stem = stem
return
end
stem = rmatch(base.lemma, "^(.*)o$")
if stem then
if base.gender == "m" then
-- Cf. [[maestro]] m.
base.decl = "o-m"
elseif base.gender == "f" then
-- [[zoo]]; [[Žemaitsko]]?
error("Feminine nouns in -o are indeclinable; use '.indecl' if needed")
elseif base.hard then
base.decl = "hard-n"
elseif base.tstem then
base.decl = "tstem-n"
elseif base.nstem then
base.decl = "nstem-n"
elseif rfind(base.lemma, "[czs]" .. "o$") then
base.decl = "czs-n"
elseif rfind(base.lemma, com.inherently_soft_c .. "o$") then
base.decl = "soft-n"
elseif rfind(base.lemma, com.velar_c .. "o$") then
base.decl = "velar-n"
else
base.decl = "hard-n"
end
base.vowel_stem = stem
return
end
stem = rmatch(base.lemma, "^(.*)[iy]$")
if stem then
if base.gender == "m" then
if base.adje then
base.decl = "adje-m"
end
end
base.vowel_stem = stem
return
end
stem = rmatch(base.lemma, "^(.*" .. com.cons_c .. ")$")
if stem then
if base.gender == "m" then
if base.hard then
base.decl = "hard-m"
elseif base.soft then
base.decl = "soft-m"
elseif rfind(base.lemma, com.velar_c .. "$") then
base.decl = "velar-m"
elseif rfind(base.lemma, "[czs]" .. "$") then
base.decl = "czs-m"
elseif rfind(base.lemma, com.inherently_soft_c .. "$") then
base.decl = "soft-m"
else
base.decl = "hard-m"
end
elseif base.gender == "f" then
if base.vstem then
base.decl = "v-f"
stem = rmatch(base.lemma, "^(.*)ej$")
elseif base.soft then
base.decl = "soft-f"
elseif rfind(base.lemma, "[czs]" .. "$") then
base.decl = "czs-f"
else
base.decl = "soft-f"
end
elseif base.gender == "n" then
if base.foreign then
stem = rmatch(base.lemma, "^(.*)um$") or rmatch(base.lemma, "^(.*)on$")
if not stem then
error("Unrecognized neuter foreign ending, should be -um or -on")
end
if base.hard then
base.decl = "hard-n"
elseif rfind(stem, "[eiuy]$") then
base.decl = "semisoft-n"
else
base.decl = "hard-n"
end
-- set the lemma here as if decllemma: were given
base.lemma = stem .. "o"
base.vowel_stem = stem
return
else
error("Neuter nouns ending in a consonant should use '.foreign' or '.decllemma:...'")
end
end
base.nonvowel_stem = stem
return
end
error("Unrecognized ending for lemma: '" .. base.lemma .. "'")
end
-- Determine the default value for the 'reducible' flag.
local function determine_default_reducible(base)
-- Nouns in vowels other than -a/o as well as masculine nouns ending in all vowels don't have null endings so not
-- reducible. Note, we are never called on adjectival nouns.
if rfind(base.lemma, "[iyuíeě]$") or base.gender == "m" and rfind(base.lemma, "[ao]$") or base.tstem then
base.default_reducible = false
return
end
local stem
stem = rmatch(base.lemma, "^(.*" .. com.cons_c .. ")$")
if stem then
if base.gender == "m" and rfind(stem, "e[ck]$") and not com.is_monosyllabic(stem) then
base.default_reducible = true
elseif base.gender == "f" and rfind(stem, "eń$") then
-- pěseń
base.default_reducible = true
else
base.default_reducible = false
end
return
end
base.default_reducible = false
end
-- Determine the stems to use for each stem set: vowel and nonvowel stems, for singular
-- and plural. We assume that one of base.vowel_stem or base.nonvowel_stem has been
-- set in determine_declension(), depending on whether the lemma ends in
-- a vowel. We construct all the rest given the reducibility, vowel alternation spec and
-- any explicit stems given. We store the determined stems inside of the stem-set objects
-- in `base.stem_sets`, meaning that if the user gave multiple reducible or vowel-alternation
-- patterns, we will compute multiple sets of stems. The reason is that the stems may vary
-- depending on the reducibility and vowel alternation.
local function determine_stems(base)
if not base.stem_sets then
base.stem_sets = {{}}
end
-- Set default reducible and check for default mixed reducible, which needs to be expanded into two entries.
local default_mixed_reducible = false
for _, stems in ipairs(base.stem_sets) do
if stems.reducible == nil then
stems.reducible = base.default_reducible
end
end
if default_mixed_reducible then
local new_stem_sets = {}
for _, stems in ipairs(base.stem_sets) do
table.insert(new_stem_sets, stems)
end
base.stem_sets = new_stem_sets
end
-- Now determine all the stems for each stem set.
for _, stems in ipairs(base.stem_sets) do
local lemma_is_vowel_stem = not not base.vowel_stem
if base.vowel_stem then
stems.vowel_stem = base.vowel_stem
stems.nonvowel_stem = stems.vowel_stem
-- Apply vowel alternation first in cases like jádro -> jader; apply_vowel_alternation() will throw an error
-- if the vowel being modified isn't the last vowel in the stem.
stems.oblique_nonvowel_stem = com.apply_vowel_alternation(stems.vowelalt, stems.nonvowel_stem)
if stems.reducible then
stems.nonvowel_stem = dereduce(base, stems.nonvowel_stem)
stems.oblique_nonvowel_stem = dereduce(base, stems.oblique_nonvowel_stem)
end
else
stems.nonvowel_stem = base.nonvowel_stem
-- The user specified #. E.g. nóc nocy
if stems.oblique_slots then
stems.oblique_slots = "all"
end
stems.oblique_nonvowel_stem = com.apply_vowel_alternation(stems.vowelalt, stems.nonvowel_stem)
if stems.reducible then
stems.vowel_stem = com.reduce(base.nonvowel_stem)
if not stems.vowel_stem then
error("Unable to reduce stem '" .. base.nonvowel_stem .. "'")
end
else
stems.vowel_stem = base.nonvowel_stem
end
end
stems.oblique_vowel_stem = com.apply_vowel_alternation(stems.vowelalt, stems.vowel_stem)
end
end
local function detect_indicator_spec(base)
if base.pron then
determine_pronoun_stems(base)
elseif base.det then
determine_determiner_stems(base)
elseif base.num then
determine_numeral_stems(base)
elseif base.adj then
process_declnumber(base)
synthesize_adj_lemma(base)
elseif base.manual then
if base.stem_sets then
-- FIXME, maybe this should be allowed?
error("Reducible and vowel alternation specs cannot be given with manual declensions")
end
base.stem_sets = {{reducible = false, vowel_stem = "", nonvowel_stem = ""}}
base.decl = "manual"
else
if base.number == "pl" then
synthesize_singular_lemma(base)
end
determine_declension(base)
determine_default_reducible(base)
determine_stems(base)
end
end
local function detect_all_indicator_specs(alternant_multiword_spec)
alternant_multiword_spec.sg_genders = {}
alternant_multiword_spec.pl_genders = {}
iut.map_word_specs(alternant_multiword_spec, function(base)
detect_indicator_spec(base)
if base.number ~= "pl" then
alternant_multiword_spec.sg_genders[base.actual_gender] = true
end
if base.number ~= "sg" then
-- All t-stem masculines are neuter in the plural.
local plgender
plgender = base.actual_gender
alternant_multiword_spec.pl_genders[plgender] = true
end
end)
if (alternant_multiword_spec.saw_pron and 1 or 0) + (alternant_multiword_spec.saw_det and 1 or 0) + (alternant_multiword_spec.saw_num and 1 or 0) > 1 then
error("Can't combine pronouns, determiners and/or numerals")
end
end
local propagate_multiword_properties
local function propagate_alternant_properties(alternant_spec, property, mixed_value, nouns_only)
local seen_property
for _, multiword_spec in ipairs(alternant_spec.alternants) do
propagate_multiword_properties(multiword_spec, property, mixed_value, nouns_only)
if seen_property == nil then
seen_property = multiword_spec[property]
elseif multiword_spec[property] and seen_property ~= multiword_spec[property] then
seen_property = mixed_value
end
end
alternant_spec[property] = seen_property
end
propagate_multiword_properties = function(multiword_spec, property, mixed_value, nouns_only)
local seen_property = nil
local last_seen_nounal_pos = 0
local word_specs = multiword_spec.alternant_or_word_specs or multiword_spec.word_specs
for i = 1, #word_specs do
local is_nounal
if word_specs[i].alternants then
propagate_alternant_properties(word_specs[i], property, mixed_value)
is_nounal = not not word_specs[i][property]
elseif nouns_only then
is_nounal = is_regular_noun(word_specs[i])
else
is_nounal = not not word_specs[i][property]
end
if is_nounal then
if not word_specs[i][property] then
error("Internal error: noun-type word spec without " .. property .. " set")
end
for j = last_seen_nounal_pos + 1, i - 1 do
word_specs[j][property] = word_specs[j][property] or word_specs[i][property]
end
last_seen_nounal_pos = i
if seen_property == nil then
seen_property = word_specs[i][property]
elseif seen_property ~= word_specs[i][property] then
seen_property = mixed_value
end
end
end
if last_seen_nounal_pos > 0 then
for i = last_seen_nounal_pos + 1, #word_specs do
word_specs[i][property] = word_specs[i][property] or word_specs[last_seen_nounal_pos][property]
end
end
multiword_spec[property] = seen_property
end
local function propagate_properties_downward(alternant_multiword_spec, property, default_propval)
local function set_and_fetch(obj, default)
local retval
if obj[property] then
retval = obj[property]
else
obj[property] = default
retval = default
end
if not obj["actual_" .. property] then
obj["actual_" .. property] = retval
end
return retval
end
local propval1 = set_and_fetch(alternant_multiword_spec, default_propval)
for _, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do
local propval2 = set_and_fetch(alternant_or_word_spec, propval1)
if alternant_or_word_spec.alternants then
for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do
local propval3 = set_and_fetch(multiword_spec, propval2)
for _, word_spec in ipairs(multiword_spec.word_specs) do
local propval4 = set_and_fetch(word_spec, propval3)
if propval4 == "mixed" then
-- FIXME, use clearer error message.
error("Attempt to assign mixed " .. property .. " to word")
end
set_and_fetch(word_spec, propval4)
end
end
else
if propval2 == "mixed" then
-- FIXME, use clearer error message.
error("Attempt to assign mixed " .. property .. " to word")
end
set_and_fetch(alternant_or_word_spec, propval2)
end
end
end
--[=[
Propagate `property` (one of "animacy", "gender" or "number") from nouns to adjacent
adjectives. We proceed as follows:
1. We assume the properties in question are already set on all nouns. This should happen in
set_defaults_and_check_bad_indicators().
2. We first propagate properties upwards and sideways. We recurse downwards from the top. When we encounter a multiword
spec, we proceed left to right looking for a noun. When we find a noun, we fetch its property (recursing if the noun
is an alternant), and propagate it to any adjectives to its left, up to the next noun to the left. When we have
processed the last noun, we also propagate its property value to any adjectives to the right (to handle e.g.
[[anděl strážný]] "guardian angel", where the adjective [[strážný]] should inherit the 'masculine' and 'animate'
properties of [[anděl]]). Finally, we set the property value for the multiword spec itself by combining all the
non-nil properties of the individual elements. If all non-nil properties have the same value, the result is that
value, otherwise it is `mixed_value` (which is "mixed" for animacy and gender, but "allthree" for number).
3. When we encounter an alternant spec in this process, we recursively process each alternant (which is a multiword
spec) using the previous step, and combine any non-nil properties we encounter the same way as for multiword specs.
4. The effect of steps 2 and 3 is to set the property of each alternant and multiword spec based on its children or its
neighbors.
]=]
local function propagate_properties(alternant_multiword_spec, property, default_propval, mixed_value)
propagate_multiword_properties(alternant_multiword_spec, property, mixed_value, "nouns only")
propagate_multiword_properties(alternant_multiword_spec, property, mixed_value, false)
propagate_properties_downward(alternant_multiword_spec, property, default_propval)
end
local function determine_noun_status(alternant_multiword_spec)
for i, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do
if alternant_or_word_spec.alternants then
local is_noun = false
for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do
for j, word_spec in ipairs(multiword_spec.word_specs) do
if is_regular_noun(word_spec) then
multiword_spec.first_noun = j
is_noun = true
break
end
end
end
if is_noun then
alternant_multiword_spec.first_noun = i
end
elseif is_regular_noun(alternant_or_word_spec) then
alternant_multiword_spec.first_noun = i
return
end
end
end
-- Set the part of speech based on properties of the individual words.
local function set_pos(alternant_multiword_spec)
if alternant_multiword_spec.args.pos then
alternant_multiword_spec.pos = alternant_multiword_spec.args.pos
elseif alternant_multiword_spec.saw_pron and not alternant_multiword_spec.saw_non_pron then
alternant_multiword_spec.pos = "သဗ္ဗနာမ်"
elseif alternant_multiword_spec.saw_det and not alternant_multiword_spec.saw_non_det then
alternant_multiword_spec.pos = "ဖျေံလဝ်သန္နိဋ္ဌာန်"
elseif alternant_multiword_spec.saw_num and not alternant_multiword_spec.saw_non_num then
alternant_multiword_spec.pos = "ဂၞန်သၚ်္ချာ"
else
alternant_multiword_spec.pos = "နာမ်"
end
alternant_multiword_spec.plpos = require(en_utilities_module).pluralize(alternant_multiword_spec.pos)
end
local function normalize_all_lemmas(alternant_multiword_spec, pagename)
iut.map_word_specs(alternant_multiword_spec, function(base)
if base.lemma == "" then
base.lemma = pagename
end
base.orig_lemma = base.lemma
base.orig_lemma_no_links = m_links.remove_links(base.lemma)
local lemma = base.orig_lemma_no_links
-- If the lemma is all-uppercase, lowercase it but note this, so that later in combine_stem_ending() we convert it
-- back to uppercase. This allows us to handle all-uppercase acronyms without a lot of extra complexity.
-- FIXME: This may not make sense at all.
if uupper(lemma) == lemma then
base.all_uppercase = true
lemma = ulower(lemma)
end
base.actual_lemma = lemma
base.lemma = base.decllemma or lemma
end)
end
local function decline_noun(base)
for _, stems in ipairs(base.stem_sets) do
if not decls[base.decl] then
error("Internal error: Unrecognized declension type '" .. base.decl .. "'")
end
decls[base.decl](base, stems)
end
handle_derived_slots_and_overrides(base)
local function copy(from_slot, to_slot)
base.forms[to_slot] = base.forms[from_slot]
end
if base.gender ~= "m" then
copy("nom_d", "acc_d")
end
copy("nom_d", "voc_d")
copy("dat_d", "loc_d")
copy("dat_d", "ins_d")
if base.actual_number ~= base.number then
local source_num = base.number == "sg" and "_s" or base.number == "du" and "_d" or "_p"
local dest_num = base.number == "sg" and {"_p", "_d"} or base.number == "du" and {"_s", "_p"} or {"_s", "_d"}
for case, _ in pairs(cases) do
copy(case .. source_num, case .. dest_num)
copy("nom" .. source_num .. "_linked", "nom" .. dest_num .. "_linked")
end
if base.actual_number ~= "allthree" then
local erase_num = base.actual_number == "sg" and {"_d", "_p"} or base.actual_number == "du" and {"_s", "_p"} or {"_s", "_d"}
for case, _ in pairs(cases) do
base.forms[case .. erase_num] = nil
end
base.forms["nom" .. erase_num .. "_linked"] = nil
end
end
end
local function get_variants(form)
return nil
--[=[
FIXME
return
form:find(com.VAR1) and "var1" or
form:find(com.VAR2) and "var2" or
form:find(com.VAR3) and "var3" or
nil
]=]
end
-- Compute the categories to add the noun to, as well as the annotation to display in the
-- declension title bar. We combine the code to do these functions as both categories and
-- title bar contain similar information.
local function compute_categories_and_annotation(alternant_multiword_spec)
local all_cats = {}
local function insert(cattype)
m_table.insertIfNot(all_cats, "Upper Sorbian " .. cattype)
end
if alternant_multiword_spec.pos == "နာမ်" then
if alternant_multiword_spec.actual_number == "sg" then
-- insert("uncountable nouns")
elseif alternant_multiword_spec.actual_number == "du" then
-- insert("dualia tantum")
elseif alternant_multiword_spec.actual_number == "pl" then
-- insert("pluralia tantum")
end
end
local annotation
local annparts = {}
local decldescs = {}
local vowelalts = {}
local foreign = {}
local irregs = {}
local stemspecs = {}
local reducible = nil
local function get_genanim(gender, animacy)
local gender_code_to_desc = {
m = "ပုလ္လိၚ်",
f = "ဣတ္တိလိၚ်",
n = "နပုလ္လိၚ်",
none = nil,
}
local animacy_code_to_desc = {
pr = "ပူဂဵု",
anml = "ဆေၚ်စပ်ကဵုသတ်",
inan = "မသက္ကုဟၟဲကဵုလမျီု",
none = nil,
}
local descs = {}
table.insert(descs, gender_code_to_desc[gender])
if gender ~= "f" and gender ~= "n" then
-- masculine or "none" (e.g. certain pronouns and numerals)
table.insert(descs, animacy_code_to_desc[animacy])
end
return table.concat(descs, " ")
end
local function trim(text)
text = text:gsub(" +", " ")
return mw.text.trim(text)
end
local function do_word_spec(base)
local actual_genanim = get_genanim(base.actual_gender, base.actual_animacy)
local declined_genanim = get_genanim(base.gender, base.animacy)
local genanim
genanim = actual_genanim
if base.actual_gender == "m" then
insert(actual_genanim .. " " .. alternant_multiword_spec.plpos)
end
for _, stems in ipairs(base.stem_sets) do
local props = declprops[base.decl]
local cats = props.cat
if type(cats) == "function" then
cats = cats(base, stems)
end
if type(cats) == "string" then
cats = {cats}
end
local default_desc
for i, cat in ipairs(cats) do
if not cat:find("GENDER") and not cat:find("GENPOS") and not cat:find("POS") then
cat = cat
end
cat = cat:gsub("GENPOS", "GENDER POS")
if not cat:find("POS") then
cat = cat .. " POS"
end
if i == #cats then
default_desc = cat:gsub(" POS", "")
end
cat = cat:gsub("GENDER", actual_genanim)
cat = cat:gsub("POS", alternant_multiword_spec.plpos)
-- Need to trim `cat` because actual_genanim may be an empty string.
insert(trim(cat))
end
local desc = props.desc
if type(desc) == "function" then
desc = desc(base, stems)
end
desc = desc or default_desc
desc = desc:gsub("GENDER", genanim)
-- Need to trim `desc` because genanim may be an empty string.
m_table.insertIfNot(decldescs, trim(desc))
local vowelalt
if stems.vowelalt == "quant" then
vowelalt = "quant-alt"
-- insert("nouns with quantitative vowel alternation")
elseif stems.vowelalt == "quant-ě" then
vowelalt = "í-ě-alt"
-- insert("nouns with í-ě alternation")
end
if vowelalt then
m_table.insertIfNot(vowelalts, vowelalt)
end
if reducible == nil then
reducible = stems.reducible
end
if stems.reducible then
-- insert("nouns with reducible stem")
end
if base.foreign then
m_table.insertIfNot(foreign, "foreign")
if not base.decllemma then
-- NOTE: there are nouns that use both 'foreign' and 'decllemma', e.g. [[Zeus]].
-- insert("nouns with regular foreign declension")
end
end
-- User-specified 'decllemma:' indicates irregular stem. Don't consider foreign nouns in -us/-os/-es, -um/-on or
-- silent -e (e.g. [[software]]) where this ending is simply dropped in oblique and plural forms as irregular;
-- there are too many of these and they are already categorized above as 'nouns with regular foreign declension'.
if base.decllemma then
m_table.insertIfNot(irregs, "irreg-stem")
-- insert("nouns with irregular stem")
end
m_table.insertIfNot(stemspecs, stems.vowel_stem)
end
end
local key_entry = alternant_multiword_spec.first_noun or 1
if #alternant_multiword_spec.alternant_or_word_specs >= key_entry then
local alternant_or_word_spec = alternant_multiword_spec.alternant_or_word_specs[key_entry]
if alternant_or_word_spec.alternants then
for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do
key_entry = multiword_spec.first_noun or 1
if #multiword_spec.word_specs >= key_entry then
do_word_spec(multiword_spec.word_specs[key_entry])
end
end
else
do_word_spec(alternant_or_word_spec)
end
end
if alternant_multiword_spec.actual_number == "sg" or alternant_multiword_spec.actual_number == "pl" or alternant_multiword_spec.actual_number == "du" then
-- not "allthree" or "none" (for [[sebe]])
table.insert(annparts, alternant_multiword_spec.actual_number == "sg" and "sg-only" or alternant_multiword_spec.actual_number == "du" and "du-only" or "pl-only")
end
if #decldescs == 0 then
table.insert(annparts, "indecl")
else
table.insert(annparts, table.concat(decldescs, " // "))
end
if #vowelalts > 0 then
table.insert(annparts, table.concat(vowelalts, "/"))
end
if reducible == "mixed" then
table.insert(annparts, "mixed-reducible")
elseif reducible then
table.insert(annparts, "reducible")
end
if #foreign > 0 then
table.insert(annparts, table.concat(foreign, " // "))
end
if #irregs > 0 then
table.insert(annparts, table.concat(irregs, " // "))
end
alternant_multiword_spec.annotation = table.concat(annparts, " ")
if #stemspecs > 1 then
insert("nouns with multiple stems")
end
if alternant_multiword_spec.actual_number == "allthree" and not m_table.deepEquals(alternant_multiword_spec.sg_genders, alternant_multiword_spec.pl_genders) then
insert("nouns that change gender in the plural")
end
alternant_multiword_spec.categories = all_cats
end
local function show_forms(alternant_multiword_spec)
local lemmas = {}
for _, slot in ipairs(potential_lemma_slots) do
if alternant_multiword_spec.forms[slot] then
for _, formobj in ipairs(alternant_multiword_spec.forms[slot]) do
-- FIXME, now can support footnotes as qualifiers in headwords?
table.insert(lemmas, formobj.form)
end
break
end
end
local props = {
lemmas = lemmas,
slot_table = alternant_multiword_spec.output_noun_slots,
lang = lang,
canonicalize = function(form)
-- return com.remove_variant_codes(form)
return form
end,
}
iut.show_forms(alternant_multiword_spec.forms, props)
end
local function make_table(alternant_multiword_spec)
local forms = alternant_multiword_spec.forms
local function template_prelude(min_width)
return rsub([=[
<div>
<div class="NavFrame" style="max-width: MINWIDTHem">
<div class="NavHead" style="background:var(--wikt-palette-lighterblue, #ebf4ff)">{title}{annotation}</div>
<div class="NavContent">
{\op}| class="inflection-table inflection" style="width: 100%; margin: 0; text-align:center;"
|-
]=], "MINWIDTH", min_width)
end
local function template_postlude()
return [=[
|{\cl}{notes_clause}</div></div></div>]=]
end
local table_spec_allthree = template_prelude("45") .. [=[
|- class="rowgroup"
! style="background:var(--wikt-palette-lightblue, #d9ebff)" |
! scope="col" style="background:var(--wikt-palette-lightblue, #d9ebff)" | ကိုန်ဨကဝုစ်
! scope="col" style="background:var(--wikt-palette-lightblue, #d9ebff)" | ၜါလ္ပာ်
! scope="col" style="background:var(--wikt-palette-lightblue, #d9ebff)" | ကိုန်ဗဟုဝစ်
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | မဒုၚ်ယၟု
| {nom_s}
| {nom_d}
| {nom_p}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ဗဳဇဂကူ
| {gen_s}
| {gen_d}
| {gen_p}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ပြကမ္မကာရက
| {dat_s}
| {dat_d}
| {dat_p}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ကမ္မကာရက
| {acc_s}
| {acc_d}
| {acc_p}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | တိၚ်တိုက်ကပေါတ်ကွိၚ်ကွိုက်
| {ins_s}
| {ins_d}
| {ins_p}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ခၞံဗဒှ်ဌာန်မတန်တဴ
| {loc_s}
| {loc_d}
| {loc_p}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ပရေၚ်ဂယိုၚ်လမျီု
| {voc_s}
| {voc_d}
| {voc_p}
]=] .. template_postlude()
local function get_table_spec_one_number(number, numcode)
local table_spec_one_number = [=[
! style="width:33%;background:var(--wikt-palette-lightblue)" |
! style="background:var(--wikt-palette-lightblue)" | NUMBER
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | မဒုၚ်ယၟု
| {nom_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ဗဳဇဂကူ
| {gen_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ပြကမ္မကာရက
| {dat_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ကမ္မကာရက
| {acc_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ပရေၚ်ဂယိုၚ်လမျီု
| {voc_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ခၞံဗဒှ်ဌာန်မတန်တဴ
| {loc_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | တိၚ်တိုက်ကပေါတ်ကွိၚ်ကွိုက်
| {ins_CODE}
]=]
return template_prelude("30") .. table_spec_one_number:gsub("NUMBER", number):gsub("CODE", numcode) ..
template_postlude()
end
local function get_table_spec_one_number_clitic(number, numcode)
local table_spec_one_number_clitic = [=[
! rowspan=2 style="background:var(--wikt-palette-lightblue, #d9ebff)" |
! style="background:var(--wikt-palette-lightblue, #d9ebff)" |NUMBER
|-
! style="background:var(--wikt-palette-lightblue, #d9ebff)" |stressed
! style="background:var(--wikt-palette-lightblue, #d9ebff)" |clitic
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | မဒုၚ်ယၟု
| colspan=2 | {nom_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ဗဳဇဂကူ
| {gen_CODE}
| {clitic_gen_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ပြကမ္မကာရက
| {dat_CODE}
| {clitic_dat_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ကမ္မကာရက
| {acc_CODE}
| {clitic_acc_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ပရေၚ်ဂယိုၚ်လမျီု
| colspan=2 | {voc_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ခၞံဗဒှ်ဌာန်မတန်တဴ
| colspan=2 | {loc_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | တိၚ်တိုက်ကပေါတ်ကွိၚ်ကွိုက်
| colspan=2 | {ins_CODE}
]=]
return template_prelude("40") .. table_spec_one_number_clitic:gsub("NUMBER", number):gsub("CODE", numcode) ..
template_postlude()
end
local notes_template = [=[
<div style="width:100%;text-align:left;background:var(--wikt-palette-lightblue, #d9ebff)">
<div style="display:inline-block;text-align:left;padding-left:1em;padding-right:1em">
{footnote}
</div></div>
]=]
if alternant_multiword_spec.title then
forms.title = alternant_multiword_spec.title
else
forms.title = 'Declension of <i lang="hsb">' .. forms.lemma .. '</i>'
end
local annotation = alternant_multiword_spec.annotation
if annotation == "" then
forms.annotation = ""
else
forms.annotation = " (<span style=\"font-size: smaller;\">" .. annotation .. "</span>)"
end
local number, numcode
if alternant_multiword_spec.actual_number == "sg" then
number, numcode = "singular", "s"
elseif alternant_multiword_spec.actual_number == "du" then
number, numcode = "dual", "d"
elseif alternant_multiword_spec.actual_number == "pl" then
number, numcode = "plural", "p"
elseif alternant_multiword_spec.actual_number == "none" then -- used for [[sebe]]
number, numcode = "", "s"
end
local table_spec =
alternant_multiword_spec.actual_number == "allthree" and table_spec_allthree or
alternant_multiword_spec.has_clitic and get_table_spec_one_number_clitic(number, numcode) or
get_table_spec_one_number(number, numcode)
forms.notes_clause = forms.footnote ~= "" and
m_string_utilities.format(notes_template, forms) or ""
return m_string_utilities.format(table_spec, forms)
end
local function compute_headword_genders(alternant_multiword_spec)
local genders = {}
local number
if alternant_multiword_spec.actual_number == "pl" then
number = "-p"
elseif alternant_multiword_spec.actual_number == "du" then
number = "-d"
else
number = ""
end
iut.map_word_specs(alternant_multiword_spec, function(base)
local animacy = base.animacy
if animacy == "inan" then
animacy = "in"
end
m_table.insertIfNot(genders, base.gender .. "-" .. animacy .. number)
end)
return genders
end
-- Externally callable function to parse and decline a noun given user-specified arguments.
-- Return value is ALTERNANT_MULTIWORD_SPEC, an object where the declined forms are in
-- `ALTERNANT_MULTIWORD_SPEC.forms` for each slot. If there are no values for a slot, the
-- slot key will be missing. The value for a given slot is a list of objects
-- {form=FORM, footnotes=FOOTNOTES}.
function export.do_generate_forms(parent_args, from_headword)
local params = {
[1] = {required = true, default = "žona<f>"},
title = {},
pagename = {},
json = {type = "boolean"},
pos = {},
}
if from_headword then
params["head"] = {list = true}
params["lemma"] = {list = true}
params["g"] = {list = true}
params["f"] = {list = true}
params["m"] = {list = true}
params["adj"] = {list = true}
params["dim"] = {list = true}
params["id"] = {}
end
local args = m_para.process(parent_args, params)
local parse_props = {
parse_indicator_spec = parse_indicator_spec,
angle_brackets_omittable = true,
allow_blank_lemma = true,
}
local alternant_multiword_spec = iut.parse_inflected_text(args[1], parse_props)
alternant_multiword_spec.title = args.title
alternant_multiword_spec.args = args
local pagename = args.pagename or from_headword and args.head[1] or mw.loadData("Module:headword/data").pagename
normalize_all_lemmas(alternant_multiword_spec, pagename)
set_all_defaults_and_check_bad_indicators(alternant_multiword_spec)
-- These need to happen before detect_all_indicator_specs() so that adjectives get their genders and numbers set
-- appropriately, which are needed to correctly synthesize the adjective lemma.
propagate_properties(alternant_multiword_spec, "animacy", "inan", "mixed")
propagate_properties(alternant_multiword_spec, "number", "allthree", "allthree")
-- FIXME, the default value (third param) used to be 'm' with a comment indicating that this applied only to
-- plural adjectives, where it didn't matter; but here, plural adjectives are distinguished for gender and
-- animacy. Make sure 'mixed' works.
propagate_properties(alternant_multiword_spec, "gender", "mixed", "mixed")
detect_all_indicator_specs(alternant_multiword_spec)
-- Propagate 'actual_number' after calling detect_all_indicator_specs(), which sets 'actual_number' for adjectives.
propagate_properties(alternant_multiword_spec, "actual_number", "allthree", "allthree")
determine_noun_status(alternant_multiword_spec)
set_pos(alternant_multiword_spec)
alternant_multiword_spec.output_noun_slots = get_output_noun_slots(alternant_multiword_spec)
local inflect_props = {
skip_slot = function(slot)
return skip_slot(alternant_multiword_spec.actual_number, slot)
end,
slot_table = alternant_multiword_spec.output_noun_slots,
get_variants = get_variants,
inflect_word_spec = decline_noun,
}
iut.inflect_multiword_or_alternant_multiword_spec(alternant_multiword_spec, inflect_props)
compute_categories_and_annotation(alternant_multiword_spec)
alternant_multiword_spec.genders = compute_headword_genders(alternant_multiword_spec)
if args.json then
alternant_multiword_spec.args = nil
return require("Module:JSON").toJSON(alternant_multiword_spec)
end
return alternant_multiword_spec
end
-- Entry point for {{hsb-ndecl}}. Template-callable function to parse and decline a noun given
-- user-specified arguments and generate a displayable table of the declined forms.
function export.show(frame)
local parent_args = frame:getParent().args
local alternant_multiword_spec = export.do_generate_forms(parent_args)
if type(alternant_multiword_spec) == "string" then
-- JSON return value
return alternant_multiword_spec
end
show_forms(alternant_multiword_spec)
return make_table(alternant_multiword_spec) ..
require("Module:utilities").format_categories(alternant_multiword_spec.categories, lang, nil, nil, force_cat)
end
return export
p81lszpmh9rdq0519itqk76s673kplj
396540
396539
2026-06-07T16:52:17Z
咽頭べさ
33
396540
Scribunto
text/plain
local export = {}
--[=[
Authorship: Zhnka, heavily based on [[Module:cs-noun]] by Benwing
]=]
--[=[
TERMINOLOGY:
-- "slot" = A particular combination of case/number.
Example slot names for nouns are "gen_s" (genitive singular) and
"voc_p" (vocative plural). Each slot is filled with zero or more forms.
-- "form" = The declined form representing the value of a given slot.
-- "lemma" = The dictionary form. Generally the nominative
masculine singular, but may occasionally be another form if the nominative
masculine singular is missing.
]=]
local lang = require("Module:languages").getByCode("hsb")
local m_table = require("Module:table")
local m_links = require("Module:links")
local m_string_utilities = require("Module:string utilities")
local iut = require("Module:inflection utilities")
local put = require("Module:parse utilities")
local m_para = require("Module:parameters")
local com = require("Module:hsb-common")
local en_utilities_module = "Module:en-utilities"
local current_title = mw.title.getCurrentTitle()
local NAMESPACE = current_title.nsText
local PAGENAME = current_title.text
local u = mw.ustring.char
local rsplit = mw.text.split
local rfind = mw.ustring.find
local rmatch = mw.ustring.match
local rgmatch = mw.ustring.gmatch
local rsubn = mw.ustring.gsub
local ulen = mw.ustring.len
local usub = mw.ustring.sub
local uupper = mw.ustring.upper
local ulower = mw.ustring.lower
local force_cat = false -- set to true to make categories appear in non-mainspace pages, for testing
-- 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
-- version of rsubn() that returns a 2nd argument boolean indicating whether
-- a substitution was made.
local function rsubb(term, foo, bar)
local retval, nsubs = rsubn(term, foo, bar)
return retval, nsubs > 0
end
local function track(track_id)
require("Module:debug/track")("hsb-noun/" .. track_id)
return true
end
local output_noun_slots = {
nom_s = "nom|s",
gen_s = "gen|s",
dat_s = "dat|s",
acc_s = "acc|s",
voc_s = "voc|s",
loc_s = "loc|s",
ins_s = "ins|s",
nom_d = "nom|d",
gen_d = "gen|d",
dat_d = "dat|d",
acc_d = "acc|d",
voc_d = "voc|d",
loc_d = "loc|d",
ins_d = "ins|d",
nom_p = "nom|p",
gen_p = "gen|p",
dat_p = "dat|p",
acc_p = "acc|p",
voc_p = "voc|p",
loc_p = "loc|p",
ins_p = "ins|p",
}
local function get_output_noun_slots(alternant_multiword_spec)
-- FIXME: To save memory we modify the table in-place. This won't work if we ever end up with multiple calls to
-- this module in the same Lua invocation, and we would need to clone the table.
if alternant_multiword_spec.actual_number ~= "allthree" then
for slot, accel_form in pairs(output_noun_slots) do
output_noun_slots[slot] = accel_form:gsub("|[sp]$", "")
end
end
return output_noun_slots
end
local potential_lemma_slots = {"nom_s", "nom_p", "gen_s"}
local cases = {
nom = true,
gen = true,
dat = true,
acc = true,
voc = true,
loc = true,
ins = true,
}
local clitic_cases = {
gen = true,
dat = true,
acc = true,
}
local function dereduce(base, stem)
local dereduced_stem = com.dereduce(base, stem)
if not dereduced_stem then
error("Unable to dereduce stem '" .. stem .. "'")
end
return dereduced_stem
end
local function skip_slot(number, slot)
return number == "sg" and rfind(slot, "_p$") or
number == "pl" and rfind(slot, "_s$")
end
-- Basic function to combine stem(s) and ending(s) and insert the result into the appropriate slot. `stems` is either
-- the `stems` object passed into the declension functions (containing the various stems; see below) or a string to
-- override the stem. (NOTE: If you pass a string in as `stems`, you should pass the value of `stems.footnotes` as the
-- value of `footnotes` as it will be lost otherwise. If you need to supply your own footnote in addition, use
-- iut.combine_footnotes() to combine any user-specified footnote(s) with your footnote(s).) `endings` is either a
-- string specifying a single ending or a list of endings. If `endings` is nil, no forms are inserted. If an ending is
-- "-", the value of `stems` is ignored and the lemma is used instead as the stem; this is important in case the user
-- used `decllemma:` to specify a declension lemma different from the actual lemma, or specified '.foreign' (which has
-- a similar effect).
local function add(base, slot, stems, endings, footnotes)
if not endings then
return
end
-- Call skip_slot() based on the declined number; if the actual number is different, we correct this in
-- decline_noun() at the end.
if skip_slot(base.number, slot) then
return
end
local stems_footnotes = type(stems) == "table" and stems.footnotes or nil
footnotes = iut.combine_footnotes(iut.combine_footnotes(base.footnotes, stems_footnotes), footnotes)
if type(endings) == "string" then
endings = {endings}
end
for _, ending in ipairs(endings) do
-- Compute the stem. If ending is "-", use the lemma regardless. Otherwise if `stems` is a string, use it.
-- Otherwise `stems` is an object containing four stems (vowel-vs-non-vowel cross regular-vs-oblique);
-- compute the appropriate stem based on the slot and whether the ending begins with a vowel.
local stem
if ending == "-" then
stem = base.actual_lemma
ending = ""
elseif type(stems) == "string" then
stem = stems
else
local is_vowel_ending = rfind(ending, "^" .. com.vowel_c)
if stems.oblique_slots == "all" then
if is_vowel_ending then
stem = stems.oblique_vowel_stem
else
stem = stems.oblique_nonvowel_stem
end
elseif is_vowel_ending then
stem = stems.vowel_stem
else
stem = stems.nonvowel_stem
end
end
ending = iut.combine_form_and_footnotes(ending, footnotes)
local function combine_stem_ending(stem, ending)
return com.combine_stem_ending(base, slot, stem, ending)
end
iut.add_forms(base.forms, slot, stem, ending, combine_stem_ending)
end
end
local function process_slot_overrides(base, do_slot)
for slot, overrides in pairs(base.overrides) do
-- Call skip_slot() based on the declined number; if the actual number is different, we correct this in
-- decline_noun() at the end.
if skip_slot(base.number, slot) then
error("Override specified for invalid slot '" .. slot .. "' due to '" .. base.number .. "' number restriction")
end
if do_slot(slot) then
base.slot_overridden[slot] = true
base.forms[slot] = nil
for _, override in ipairs(overrides) do
for _, value in ipairs(override.values) do
local form = value.form
local combined_notes = iut.combine_footnotes(base.footnotes, value.footnotes)
if override.full then
if form ~= "" then
iut.insert_form(base.forms, slot, {form = form, footnotes = combined_notes})
end
else
-- Convert a null ending to "-" in the acc/voc sg slots so that e.g. [[Kerberos]] declared as
-- <m.sg.foreign.gena:u.acc-:a> works correctly and generates accusative 'Kerberos/Kerbera' not
-- #'Kerber/Kerbera'.
if (slot == "acc_s" or slot == "voc_s") and form == "" then
form = "-"
end
for _, stems in ipairs(base.stem_sets) do
add(base, slot, stems, form, combined_notes)
end
end
end
end
end
end
end
local function add_decl(base, stems,
gen_s, dat_s, acc_s, voc_s, loc_s, ins_s,
nom_d, gen_d, dat_d,
nom_p, gen_p, dat_p, acc_p, loc_p, ins_p, nom_s, footnotes
)
add(base, "nom_s", stems, "-", footnotes)
add(base, "gen_s", stems, gen_s, footnotes)
add(base, "dat_s", stems, dat_s, footnotes)
add(base, "acc_s", stems, acc_s, footnotes)
add(base, "voc_s", stems, voc_s, footnotes)
add(base, "loc_s", stems, loc_s, footnotes)
add(base, "ins_s", stems, ins_s, footnotes)
add(base, "nom_d", stems, nom_d, footnotes)
add(base, "gen_d", stems, gen_d, footnotes)
add(base, "dat_d", stems, dat_d, footnotes)
if base.number == "pl" then
-- If this is a plurale tantum noun and we're processing the nominative plural, use the user-specified lemma
-- rather than generating the plural from the synthesized singular, which may not match the specified lemma
-- (e.g. [[tvargle]] "Olomouc cheese" using <m.pl.mixed> would try to generate 'tvargle/tvargly', and [[peníze]]
-- "money" using <m.pl.#ě.genpl-> would try to generate 'peněze').
local acc_p_like_nom = m_table.deepEquals(nom_p, acc_p)
nom_p = "-"
if acc_p_like_nom then
acc_p = "-"
end
end
add(base, "nom_p", stems, nom_p, footnotes)
add(base, "gen_p", stems, gen_p, footnotes)
add(base, "dat_p", stems, dat_p, footnotes)
add(base, "acc_p", stems, acc_p, footnotes)
add(base, "loc_p", stems, loc_p, footnotes)
add(base, "ins_p", stems, ins_p, footnotes)
add(base, "nom_s", stems, nom_s, footnotes)
end
local function add_sg_decl(base, stems,
gen_s, dat_s, acc_s, voc_s, loc_s, ins_s, footnotes
)
add_decl(base, stems, gen_s, dat_s, acc_s, voc_s, loc_s, ins_s,
nil, nil, nil,
nil, nil, nil, nil, nil, nil, footnotes)
end
local function add_du_only_decl(base, stems,
gen_d, dat_d, footnotes
)
add_decl(base, stems, nil, nil, nil, nil, nil, nil,
"-", gen_d, dat_d,
nil, nil, nil, nil, nil, nil, footnotes)
end
local function add_pl_only_decl(base, stems,
gen_p, dat_p, acc_p, loc_p, ins_p, footnotes
)
add_decl(base, stems, nil, nil, nil, nil, nil, nil,
nil, nil, nil,
"-", gen_p, dat_p, acc_p, loc_p, ins_p, footnotes)
end
local function handle_derived_slots_and_overrides(base)
local function is_non_derived_slot(slot)
return slot ~= "voc_p" and slot ~= "acc_s" and slot ~= "clitic_acc_s"
end
local function is_derived_slot(slot)
return not is_non_derived_slot(slot)
end
base.slot_overridden = {}
-- Handle overrides for the non-derived slots. Do this before generating the derived
-- slots so overrides of the source slots (e.g. nom_p) propagate to the derived slots.
process_slot_overrides(base, is_non_derived_slot)
-- Generate the remaining slots that are derived from other slots.
if not base.pron and not base.det then
-- Pronouns don't have a vocative (singular or plural).
iut.insert_forms(base.forms, "voc_p", base.forms.nom_p)
end
if not base.forms.acc_s and not base.slot_overridden.acc_s then
iut.insert_forms(base.forms, "acc_s", base.forms[base.animacy == "inan" and "nom_s" or base.animacy == "pr" and "gen_s" or base.animacy == "anml" and "gen_s"])
end
if not base.forms.acc_d and not base.slot_overridden.acc_d then
iut.insert_forms(base.forms, "acc_d", base.forms[base.animacy == "inan" and "nom_d" or base.animacy == "pr" and "gen_d" or base.animacy == "anml" and "nom_d"])
end
if not base.forms.acc_p and not base.slot_overridden.acc_p then
iut.insert_forms(base.forms, "acc_p", base.forms[base.animacy == "inan" and "nom_p" or base.animacy == "pr" and "gen_p" or base.animacy == "anml" and "nom_p"])
end
if not base.forms.clitic_acc_s and not base.slot_overridden.clitic_acc_s then
iut.insert_forms(base.forms, "clitic_acc_s", base.forms[base.animacy == "inan" and "nom_s" or "clitic_gen_s"])
end
-- Handle overrides for derived slots, to allow them to be overridden.
process_slot_overrides(base, is_derived_slot)
-- Compute linked versions of potential lemma slots, for use in {{hsb-noun}}.
-- We substitute the original lemma (before removing links) for forms that
-- are the same as the lemma, if the original lemma has links.
for _, slot in ipairs(potential_lemma_slots) do
iut.insert_forms(base.forms, slot .. "_linked", iut.map_forms(base.forms[slot], function(form)
if form == base.orig_lemma_no_links and rfind(base.orig_lemma, "%[%[") then
return base.orig_lemma
else
return form
end
end))
end
end
-- Table mapping declension types to functions to decline the noun. The function takes two arguments, `base` and
-- `stems`; the latter specifies the computed stems (vowel vs. non-vowel, singular vs. plural) and whether the noun
-- is reducible and/or has vowel alternations in the stem. Most of the specifics of determining which stem to use
-- and how to modify it for the given ending are handled in add_decl(); the declension functions just need to generate
-- the appropriate endings.
local decls = {}
-- Table specifying additional properties for declension types. Every declension type must have such a table, which
-- specifies which category or categories to add and what annotation to show in the title bar of the declension table.
--
-- * Only the `cat` property of this table is mandatory; there is also a `desc` property to specify the annotation, but
-- this can be omitted and the annotation will then be computed from the `cat` property. The `cat` property is either
-- a string, a list of strings or a function (of two arguments, `base` and `stems` as above) returning a string or
-- list of strings. The string can contain the keywords GENDER to substitute the gender (and animacy for masculine
-- nouns) and POS (to substitute the pluralized part of speech). The keyword GENPOS is equivalent to 'GENDER POS'. If
-- no keyword is present, ' GENPOS' is added onto the end. If only GENDER is present, ' POS' is added onto the end.
-- In all cases, the language name is added onto the beginning to form the full category name.
-- * The `desc` property is of the same form as the `cat` property and specifies the annotation to display in the title
-- bar (which may have the same format as the category minus the part of speech, or may be abbreviated). The value
-- may not be a list of strings, as only one annotation is displayed. If omitted, it is derived from the category
-- spec(s) by taking the last category (if more than one is given) and removing ' POS' before keyword substitution.
local declprops = {}
decls["hard-m"] = function(base, stems)
local gen_s = base.animacy == "in" and {"a", "u"} or "a"
local nom_p = base.animacy == "pr" and "ojo" or "y"
local voc_s = not rmatch(base.lemma, ".*tr$") and "o"
add_decl(base, stems, gen_s, "ej", acc_s, voc_s, "u", "om",
"aj", "ow", "omaj",
nom_p, "ow", "am", nil, "ach", "ami")
add_decl(base, com.convert_paired_plain_to_palatal(com.apply_palatalization(stems.oblique_vowel_stem)), nil, nil, nil, "e", "e")
end
declprops["hard-m"] = {
desc = function(base, stems)
return "masculine hard stem"
end,
cat = function(base, stems)
return "masculine hard stem"
end
}
decls["soft-m"] = function(base, stems)
local gen_s = base.animacy == "in" and {"a", "u"} or "a"
local nom_p = base.animacy == "pr" and "ojo" or "e"
add_decl(base, com.addj(stems.oblique_vowel_stem), gen_s, "ej", acc_s, "o", "u", "om",
"ej", "ow", "omaj",
nom_p, "ow", "am", nil, "ach", "emi")
end
declprops["soft-m"] = {
desc = function(base, stems)
return "masculine soft stem"
end,
cat = function(base, stems)
return "masculine soft stem"
end
}
decls["czs-m"] = function(base, stems)
local gen_s = base.animacy == "in" and {"a", "u"} or "a"
local nom_p = base.animacy == "pr" and "ojo" or "y"
add_decl(base, stems, gen_s, "ej", acc_s, "o", "u", "om",
"aj", "ow", "omaj",
nom_p, "ow", "am", nil, "ach", "ami")
end
declprops["czs-m"] = {
desc = function(base, stems)
return "masculine hard hissing stem"
end,
cat = function(base, stems)
return "masculine hard stem"
end
}
decls["velar-m"] = function(base, stems)
local gen_s = base.animacy == "in" and {"a", "u"} or "a"
local nom_p = base.animacy == "pr" and "ojo" or "i"
add_decl(base, stems, gen_s, "ej", acc_s, "o", "u", "om",
"aj", "ow", "omaj",
nom_p, "ow", "am", nil, "ach", "ami")
add_decl(base, com.convert_paired_plain_to_palatal(com.apply_palatalization(stems.oblique_vowel_stem)), nil, nil, nil, nil, "e")
end
declprops["velar-m"] = {
desc = function(base, stems)
return "masculine velar stem"
end,
cat = function(base, stems)
return "masculine velar stem"
end
}
decls["adje-m"] = function(base, stems)
if rmatch(base.lemma, "^.*" .. com.velar_c .. "i$") then
add_decl(base, stems, "eho", "emu", nil, "-", "im", "im",
nom_d, "eju", "imaj",
nom_p, "ich", "im", nil, "ich", "imi")
if base.animacy == "pr" then
add_decl(base, com.apply_palatalization(stems.oblique_vowel_stem), nil, nil, nil, nil, nil, nil, nil, nil, nil, "y")
add_decl(base, stems, nil, nil, nil, nil, nil, nil, "aj")
else
add_decl(base, stems, nil, nil, nil, nil, nil, nil, "ej", nil, nil, "e")
end
elseif rmatch(base.lemma, "^.*" .. com.inherently_soft_c .. "i$") then
local nom_p = base.animacy == "pr" and "i" or "e"
add_decl(base, stems, "eho", "emu", nil, "-", "im", "im",
"ej", "eju", "imaj",
nom_p, "ich", "im", nil, "ich", "imi")
elseif rmatch(base.lemma, "^.*[czs]e$") then
local nom_p = base.animacy == "pr" and "y" or "e"
local nom_d = base.animacy == "pr" and "aj" or "ej"
add_decl(base, stems, "eho", "emu", nil, "-", "ym", "ym",
nom_d, "eju", "ymaj",
nom_p, "ych", "ym", nil, "ych", "ymi")
else
add_decl(base, stems, "eho", "emu", nil, "-", "ym", "ym",
nom_d, "eju", "ymaj",
nom_p, "ych", "ym", nil, "ych", "ymi")
if base.animacy == "pr" then
add_decl(base, com.apply_palatalization(stems.oblique_vowel_stem), nil, nil, nil, nil, nil, nil, nil, nil, nil, "i")
add_decl(base, stems, nil, nil, nil, nil, nil, nil, "aj")
else
add_decl(base, stems, nil, nil, nil, nil, nil, nil, "ej", nil, nil, "e")
end
end
end
declprops["adje-m"] = {
desc = function(base, stems)
return ""
end,
cat = function(base, stems)
return "masculine adjectival"
end
}
decls["hard-f"] = function(base, stems)
add_decl(base, stems, "y", nil, "u", "-", nil, "u",
nil, "ow", "omaj",
"y", "ow", "am", "y", "ach", "ami")
add_decl(base, com.convert_paired_plain_to_palatal(com.apply_palatalization(stems.oblique_vowel_stem)), nil, "e", nil, nil, "e", nil, "e")
end
declprops["hard-f"] = {
desc = function(base, stems)
return "feminine hard stem"
end,
cat = function(base, stems)
return "feminine hard stem"
end
}
decls["soft-f"] = function(base, stems)
if rmatch(base.lemma, "^(.*" .. com.cons_c .. ")$") then
add_decl(base, com.addj(stems.oblique_vowel_stem), "e", nil, "-", "-", nil, "u",
nil, "ow", "omaj",
"e", "ow", "am", "e", "ach", "emi")
else
add_decl(base, stems, "e", nil, "u", "-", nil, "u",
nil, "ow", "omaj",
"e", "ow", "am", "e", "ach", "emi")
end
add_decl(base, com.removej(com.addj(stems.oblique_vowel_stem)), nil, "i", nil, nil, "i", nil, "i", nil, nil, nil, "i")
end
declprops["soft-f"] = {
desc = function(base, stems)
return "feminine soft stem"
end,
cat = function(base, stems)
return "feminine soft stem"
end
}
decls["czs-f"] = function(base, stems)
if rmatch(base.lemma, "^(.*" .. com.cons_c .. ")$") then
add_decl(base, stems, "y", "y", "-", "-", "y", "u",
"y", "ow", "omaj",
"y", "ow", "am", "y", "ach", "ami")
else
add_decl(base, stems, "y", "y", "u", "-", "y", "u",
"y", "ow", "omaj",
"y", "ow", "am", "y", "ach", "ami")
end
end
declprops["czs-f"] = {
desc = function(base, stems)
return "feminine hard hissing stem"
end,
cat = function(base, stems)
return "feminine hard stem"
end
}
decls["velar-f"] = function(base, stems)
add_decl(base, stems, "i", nil, "u", "-", nil, "u",
nil, "ow", "omaj",
"i", "ow", "am", "i", "ach", "ami")
add_decl(base, com.convert_paired_plain_to_palatal(com.apply_palatalization(stems.oblique_vowel_stem)), nil, "e", nil, nil, "e", nil, "e")
end
declprops["velar-f"] = {
desc = function(base, stems)
return "feminine velar stem"
end,
cat = function(base, stems)
return "feminine velar stem"
end
}
decls["v-f"] = function(base, stems)
add_decl(base, stems, "wje", "wi", "-", "-", "wi", "wju",
"wi", "wjow", "wjomaj",
"wje", "wjow", "wjam", "wje", "wjach", "wjemi")
end
declprops["v-f"] = {
desc = function(base, stems)
return "feminine v-stem"
end,
cat = function(base, stems)
return "feminine v-stem"
end
}
decls["adje-f"] = function(base, stems)
if rmatch(base.lemma, "^.*" .. com.velar_c .. "a$") or rmatch(base.lemma, "^.*" .. com.inherently_soft_c .. "a$") then
add_decl(base, stems, "eje", "ej", "u", "-", "ej", "ej",
"ej", "eju", "imaj",
"e", "ich", "im", "e", "ich", "imi")
else
add_decl(base, stems, "eje", "ej", "u", "-", "ej", "ej",
"ej", "eju", "ymaj",
"e", "ych", "ym", "e", "ych", "ymi")
end
end
declprops["adje-f"] = {
desc = function(base, stems)
return ""
end,
cat = function(base, stems)
return "feminine adjectival"
end
}
decls["hard-n"] = function(base, stems)
add_decl(base, stems, "a", "u", "-", "-", nil, "om",
nil, "ow", "omaj",
"a", "ow", "am", "a", "ach", "ami")
add_decl(base, com.convert_paired_plain_to_palatal(com.apply_palatalization(stems.oblique_vowel_stem)), nil, nil, nil, nil, "e", nil, "e")
end
declprops["hard-n"] = {
desc = function(base, stems)
return "neuter hard stem"
end,
cat = function(base, stems)
return "neuter hard stem"
end
}
decls["soft-n"] = function(base, stems)
add_decl(base, stems, "a", "u", "-", "-", "u", "om",
nil, "ow", "omaj",
"a", "ow", "am", "a", "ach", "emi")
add_decl(base, com.removej(stems.oblique_vowel_stem), nil, nil, nil, nil, nil, nil, "i")
end
declprops["soft-n"] = {
desc = function(base, stems)
return "neuter soft stem"
end,
cat = function(base, stems)
return "neuter soft stem"
end
}
decls["czs-n"] = function(base, stems)
add_decl(base, stems, "a", "u", "-", "-", "u", "om",
"y", "ow", "omaj",
"a", "ow", "am", "a", "ach", "ami")
end
declprops["czs-n"] = {
desc = function(base, stems)
return "neuter hard hissing stem"
end,
cat = function(base, stems)
return "neuter hard stem"
end
}
decls["velar-n"] = function(base, stems)
add_decl(base, stems, "a", "u", "-", "-", "u", "om",
nil, "ow", "omaj",
"a", "ow", "am", "a", "ach", "ami")
add_decl(base, com.convert_paired_plain_to_palatal(com.apply_palatalization(stems.oblique_vowel_stem)), nil, nil, nil, nil, "e", nil, "e")
end
declprops["velar-n"] = {
desc = function(base, stems)
return "neuter velar stem"
end,
cat = function(base, stems)
return "neuter velar stem"
end
}
decls["adje-n"] = function(base, stems)
if rmatch(base.lemma, "^.*" .. com.velar_c .. "e$") or rmatch(base.lemma, "^.*" .. com.inherently_soft_c .. "e$") then
add_decl(base, stems, "eho", "emu", "-", "-", "im", "im",
"ej", "eju", "imaj",
"e", "ich", "im", "e", "ich", "imi")
else
add_decl(base, stems, "eje", "ej", "u", "-", "ym", "ym",
"ej", "eju", "ymaj",
"e", "ych", "ym", "e", "ych", "ymi")
end
end
declprops["adje-n"] = {
desc = function(base, stems)
return ""
end,
cat = function(base, stems)
return "neuter adjectival"
end
}
decls["tstem-n"] = function(base, stems)
add_decl(base, stems, "eća", "eću", "-", "-", "eću", "ećom",
"eći", "ećow", "ećomaj",
"ata", "atow", "atam", "ata", "atach", "atami")
end
declprops["tstem-n"] = {
desc = function(base, stems)
return "neuter t-stem"
end,
cat = function(base, stems)
return "neuter t-stem"
end
}
decls["nstem-n"] = function(base, stems)
add_decl(base, stems, "enja", "enju", "-", "-", "enju", "enjom",
"eni", "enjow", "enjomaj",
"enja", "enjow", "enjam", "enja", "enjach", "enjami")
end
declprops["nstem-n"] = {
desc = function(base, stems)
return "neuter n-stem"
end,
cat = function(base, stems)
return "neuter n-stem"
end
}
decls["adj"] = function(base, stems)
local props = {}
local propspec = table.concat(props, ".")
if propspec ~= "" then
propspec = "<" .. propspec .. ">"
end
local adj_alternant_multiword_spec = require("Module:zlw-ocs-adjective").do_generate_forms({base.lemma .. propspec})
local function copy(from_slot, to_slot)
base.forms[to_slot] = adj_alternant_multiword_spec.forms[from_slot]
end
if base.number ~= "pl" then
if base.gender == "m" then
copy("nom_m", "nom_s")
copy("gen_mn", "gen_s")
copy("dat_mn", "dat_s")
copy("loc_mn", "loc_s")
copy("ins_mn", "ins_s")
elseif base.gender == "f" then
copy("nom_f", "nom_s")
copy("gen_f", "gen_s")
copy("dat_f", "dat_s")
copy("acc_f", "acc_s")
copy("loc_f", "loc_s")
copy("ins_f", "ins_s")
else
copy("nom_n", "nom_s")
copy("gen_mn", "gen_s")
copy("dat_mn", "dat_s")
copy("acc_n", "acc_s")
copy("loc_mn", "loc_s")
copy("ins_mn", "ins_s")
end
if not base.forms.voc_s then
iut.insert_forms(base.forms, "voc_s", base.forms.nom_s)
end
end
if base.number ~= "sg" then
if base.gender == "m" then
copy("nom_mp", "nom_p")
copy("acc_mfp", "acc_p")
copy("nom_md", "nom_d")
elseif base.gender == "f" then
copy("nom_fp", "nom_p")
copy("acc_mfp", "acc_p")
copy("nom_fnd", "nom_d")
else
copy("nom_np", "nom_p")
copy("acc_np", "acc_p")
copy("nom_fnd", "nom_d")
end
copy("gen_p", "gen_p")
copy("dat_p", "dat_p")
copy("ins_p", "ins_p")
copy("loc_p", "loc_p")
copy("gen_d", "gen_d")
copy("dat_d", "dat_d")
end
end
local function get_stemtype(base)
if rfind(base.lemma, "ý$") then
return "hard"
elseif rfind(base.lemma, "í$") then
return "soft"
else
return "possessive"
end
end
declprops["adj"] = {
cat = function(base, stems)
return {"adjectival POS", get_stemtype(base) .. " GENDER adjectival POS"}
end,
}
decls["indecl"] = function(base, stems)
-- Indeclinable. Note that fully indeclinable nouns should not have a table at all rather than one all of whose forms
-- are the same; but having an indeclinable declension is useful for nouns that may or may not be indeclinable, e.g.
-- [[desatero]] "group of ten" or the plural of [[peso]], which may be indeclinable 'pesos'.
add_decl(base, stems, "-", "-", "-", "-", "-", "-",
"-", "-", "-", "-", "-", "-", "-", "-", "-")
end
declprops["indecl"] = {
cat = function(base, stems)
if base.adj then
return {"adjectival POS", "indeclinable adjectival POS", "indeclinable GENDER adjectival POS"}
else
return {"indeclinable POS", "indeclinable GENPOS"}
end
end
}
decls["manual"] = function(base, stems)
-- Anything declined manually using overrides. We don't set any declensions except the nom_s (or nom_p if plurale
-- tantum).
add(base, base.number == "pl" and "nom_p" or "nom_s", stems, "-")
end
declprops["manual"] = {
desc = "GENDER",
cat = {},
}
local function set_pron_defaults(base)
if base.gender or base.lemma ~= "ona" and base.number or base.animacy then
error("Can't specify gender, number or animacy for pronouns")
end
local function pron_props()
-- Return values are GENDER, NUMBER, ANIMACY, HAS_CLITIC.
if base.lemma == "štó" then
return "none", "sg", "pr", false
elseif base.lemma == "što" then
return "none", "sg", "inan", false
else
error(("Unrecognized pronoun '%s'"):format(base.lemma))
end
end
local gender, number, animacy, has_clitic = pron_props()
base.gender = gender
base.actual_gender = gender
base.number = number
base.actual_number = number
base.animacy = animacy
base.actual_animacy = animacy
base.has_clitic = has_clitic
end
local function determine_pronoun_stems(base)
if base.stem_sets then
error("Reducible and vowel alternation specs cannot be given with pronouns")
end
base.stem_sets = {{reducible = false, vowel_stem = "", nonvowel_stem = ""}}
base.decl = "pron"
end
decls["pron"] = function(base, stems)
if base.lemma == "štó" then
add_decl(base, stems, "koho", "komu", nil, nil, "kim", "kim")
elseif base.lemma == "što" then
add_decl(base, stems, "čeho", "čemu", nil, nil, "čim", "čim")
else
error(("Internal error: Unrecognized pronoun lemma '%s'"):format(base.lemma))
end
end
declprops["pron"] = {
desc = "GENDER pronoun",
cat = {},
}
local function set_num_defaults(base)
if base.gender or base.animacy then
error("Can't specify gender, number or animacy for numeral")
end
local function num_props()
-- Return values are GENDER, NUMBER, ANIMACY, HAS_CLITIC.
return "none", "pl", "none", false
end
local gender, number, animacy, has_clitic = num_props()
base.gender = gender
base.actual_gender = gender
base.number = number
base.actual_number = number
base.animacy = animacy
base.actual_animacy = animacy
base.has_clitic = has_clitic
end
local function set_det_defaults(base)
if base.gender or base.number or base.animacy then
error("Can't specify gender, number or animacy for determiner")
end
local function det_props()
-- Return values are GENDER, NUMBER, ANIMACY, HAS_CLITIC.
return "none", "none", "none", false
end
local gender, number, animacy, has_clitic = det_props()
base.gender = gender
base.actual_gender = gender
base.number = number
base.actual_number = number
base.animacy = animacy
base.actual_animacy = animacy
base.has_clitic = has_clitic
end
local function determine_determiner_stems(base)
if base.stem_sets then
error("Reducible and vowel alternation specs cannot be given with determiners")
end
local stem = rmatch(base.lemma, "^(.*)" .. com.vowel_c .. "$") or base.lemma
base.stem_sets = {{reducible = false, vowel_stem = stem, nonvowel_stem = stem}}
base.decl = "det"
end
decls["det"] = function(base, stems)
add_sg_decl(base, stems, "a", "a", "-", nil, "a", "a")
end
declprops["det"] = {
desc = "GENDER determiner",
cat = {},
}
local function fetch_footnotes(separated_group)
local footnotes
for j = 2, #separated_group - 1, 2 do
if separated_group[j + 1] ~= "" then
error("Extraneous text after bracketed footnotes: '" .. table.concat(separated_group) .. "'")
end
if not footnotes then
footnotes = {}
end
table.insert(footnotes, separated_group[j])
end
return footnotes
end
local function parse_override(segments)
local retval = {values = {}}
local part = segments[1]
local slots = {}
while true do
local case = usub(part, 1, 3)
if cases[case] then
-- ok
else
error(("Unrecognized case '%s' in override: '%s'"):format(case, table.concat(segments)))
end
part = usub(part, 4)
local slot
if rfind(part, "^pl") then
part = usub(part, 3)
slot = case .. "_p"
elseif rfind(part, "^du") then
part = usub(part, 3)
slot = case .. "_d"
else
slot = case .. "_s"
end
table.insert(slots, slot)
if rfind(part, "^%+") then
part = usub(part, 2)
else
break
end
end
if rfind(part, "^:") then
retval.full = true
part = usub(part, 2)
end
segments[1] = part
local colon_separated_groups = put.split_alternating_runs_and_strip_spaces(segments, ":")
for i, colon_separated_group in ipairs(colon_separated_groups) do
local value = {}
local form = colon_separated_group[1]
if form == "" then
error(("Use - to indicate an empty ending for slot%s '%s': '%s'"):format(#slots > 1 and "s" or "", table.concat(slots), table.concat(segments)))
elseif form == "-" then
value.form = ""
else
value.form = form
end
value.footnotes = fetch_footnotes(colon_separated_group)
table.insert(retval.values, value)
end
return slots, retval
end
--[=[
Parse an indicator spec (text consisting of angle brackets and zero or more
dot-separated indicators within them). Return value is an object of the form
{
overrides = {
SLOT = {OVERRIDE, OVERRIDE, ...}, -- as returned by parse_override()
...
},
forms = {}, -- forms for a single spec alternant; see `forms` below
footnotes = {"FOOTNOTE", "FOOTNOTE", ...}, -- may be missing
stems = { -- may be missing
{
reducible = TRUE_OR_FALSE,
footnotes = {"FOOTNOTE", "FOOTNOTE", ...}, -- may be missing
-- The following fields are filled in by determine_stems()
vowel_stem = "STEM",
nonvowel_stem = "STEM",
oblique_slots = "all",
oblique_vowel_stem = "STEM" or nil (only needs to be set if oblique_slots is non-nil),
oblique_nonvowel_stem = "STEM" or nil (only needs to be set if oblique_slots is non-nil),
},
...
},
gender = "GENDER", -- "m", "f", "n"
number = "NUMBER", -- "sg", "pl"; may be missing
animacy = "ANIMACY", -- "inan", "an"; may be missing
hard = true, -- may be missing
soft = true, -- may be missing
mixed = true, -- may be missing
surname = true, -- may be missing
istem = true, -- may be missing
["-istem"] = true, -- may be missing
tstem = true, -- may be missing
nstem = true, -- may be missing
tech = true, -- may be missing
foreign = true, -- may be missing
mostlyindecl = true, -- may be missing
indecl = true, -- may be missing
manual = true, -- may be missing
adj = true, -- may be missing
decllemma = "DECLENSION-LEMMA", -- may be missing
declgender = "DECLENSION-GENDER", -- may be missing
declnumber = "DECLENSION-NUMBER", -- may be missing
-- The following additional fields are added by other functions:
orig_lemma = "ORIGINAL-LEMMA", -- as given by the user
orig_lemma_no_links = "ORIGINAL-LEMMA-NO-LINKS", -- links removed
lemma = "LEMMA", -- `orig_lemma_no_links`, converted to singular form if plural and lowercase if all-uppercase
forms = {
SLOT = {
{
form = "FORM",
footnotes = {"FOOTNOTE", "FOOTNOTE", ...} -- may be missing
},
...
},
...
},
decl = "DECL", -- declension, e.g. "hard-m"
vowel_stem = "VOWEL-STEM", -- derived from vowel-ending lemmas
nonvowel_stem = "NONVOWEL-STEM", -- derived from non-vowel-ending lemmas
}
]=]
local function parse_indicator_spec(angle_bracket_spec)
local inside = rmatch(angle_bracket_spec, "^<(.*)>$")
assert(inside)
local base = {overrides = {}, forms = {}}
if inside ~= "" then
local segments = put.parse_balanced_segment_run(inside, "[", "]")
local dot_separated_groups = put.split_alternating_runs_and_strip_spaces(segments, "%.")
for i, dot_separated_group in ipairs(dot_separated_groups) do
local part = dot_separated_group[1]
local case_prefix = usub(part, 1, 3)
if cases[case_prefix] then
local slots, override = parse_override(dot_separated_group)
for _, slot in ipairs(slots) do
if base.overrides[slot] then
error(("Two overrides specified for slot '%s'"):format(slot))
else
base.overrides[slot] = {override}
end
end
elseif part == "" then
if #dot_separated_group == 1 then
error("Blank indicator: '" .. inside .. "'")
end
base.footnotes = fetch_footnotes(dot_separated_group)
elseif rfind(part, "^[-*#ě]*$") or rfind(part, "^[-*#ě]*,") then
if base.stem_sets then
error("Can't specify reducible/vowel-alternant indicator twice: '" .. inside .. "'")
end
local comma_separated_groups = put.split_alternating_runs_and_strip_spaces(dot_separated_group, ",")
local stem_sets = {}
for i, comma_separated_group in ipairs(comma_separated_groups) do
local pattern = comma_separated_group[1]
local orig_pattern = pattern
local reducible, vowelalt, oblique_slots
if pattern == "-" then
-- default reducible, no vowel alt
else
local before, after
before, reducible, after = rmatch(pattern, "^(.-)(%-?%*)(.-)$")
if before then
pattern = before .. after
reducible = reducible == "*"
end
if pattern ~= "" then
if not rfind(pattern, "^##?ě?$") then
error("Unrecognized vowel-alternation pattern '" .. pattern .. "', should be one of #, ##, #ě or ##ě: '" .. inside .. "'")
end
if pattern == "#ě" or pattern == "##ě" then
vowelalt = "quant-ě"
else
vowelalt = "quant"
end
-- `oblique_slots` will be later changed to "all" if the lemma ends in a consonant.
oblique_slots = "all"
end
end
table.insert(stem_sets, {
reducible = reducible,
vowelalt = vowelalt,
oblique_slots = oblique_slots,
footnotes = fetch_footnotes(comma_separated_group)
})
end
base.stem_sets = stem_sets
elseif #dot_separated_group > 1 then
error("Footnotes only allowed with slot overrides, reducible or vowel alternation specs or by themselves: '" .. table.concat(dot_separated_group) .. "'")
elseif part == "m" or part == "f" or part == "n" then
if base.gender then
error("Can't specify gender twice: '" .. inside .. "'")
end
base.gender = part
elseif part == "sg" or part == "du" or part == "pl" then
if base.number then
error("Can't specify number twice: '" .. inside .. "'")
end
base.number = part
elseif part == "pr" or part == "anml" or part == "inan" then
if base.animacy then
error("Can't specify animacy twice: '" .. inside .. "'")
end
base.animacy = part
elseif part == "hard" or part == "soft" or part == "istem" or part == "tstem" or part == "nstem" or
part == "indecl" or part == "pron" or part == "det" or part == "velar" or part == "vstem" or part == "adje" then
if base[part] then
error("Can't specify '" .. part .. "' twice: '" .. inside .. "'")
end
base[part] = true
elseif part == "+" then
if base.adj then
error("Can't specify '+' twice: '" .. inside .. "'")
end
base.adj = true
elseif part == "!" then
if base.manual then
error("Can't specify '!' twice: '" .. inside .. "'")
end
base.manual = true
elseif rfind(part, "^mixedistem:") then
if base.mixedistem then
error("Can't specify 'mixedistem:' twice: '" .. inside .. "'")
end
base.mixedistem = rsub(part, "^mixedistem:", "")
elseif rfind(part, "^decllemma:") then
if base.decllemma then
error("Can't specify 'decllemma:' twice: '" .. inside .. "'")
end
base.decllemma = rsub(part, "^decllemma:", "")
elseif rfind(part, "^declgender:") then
if base.declgender then
error("Can't specify 'declgender:' twice: '" .. inside .. "'")
end
base.declgender = rsub(part, "^declgender:", "")
elseif rfind(part, "^declnumber:") then
if base.declnumber then
error("Can't specify 'declnumber:' twice: '" .. inside .. "'")
end
base.declnumber = rsub(part, "^declnumber:", "")
else
error("Unrecognized indicator '" .. part .. "': '" .. inside .. "'")
end
end
end
return base
end
local function is_regular_noun(base)
return not base.adj and not base.pron and not base.det and not base.num
end
local function process_declnumber(base)
base.actual_number = base.number
if base.declnumber then
if base.declnumber == "sg" or base.declnumber == "du" or base.declnumber == "pl" then
base.number = base.declnumber
else
error(("Unrecognized value '%s' for 'declnumber', should be 'sg' or 'pl'"):format(base.declnumber))
end
end
end
local function set_defaults_and_check_bad_indicators(base)
-- Set default values.
local regular_noun = is_regular_noun(base)
if base.pron then
set_pron_defaults(base)
elseif base.det then
set_det_defaults(base)
elseif base.num then
set_num_defaults(base)
elseif not base.adj then
if not base.gender then
if base.manual then
base.gender = "none"
else
error("For nouns, gender must be specified")
end
end
base.number = base.number or "allthree"
process_declnumber(base)
base.animacy = base.animacy or "inan"
base.actual_gender = base.gender
base.actual_animacy = base.animacy
if base.declgender then
if base.declgender == "m-an" then
base.gender = "m"
base.animacy = "pr"
elseif base.declgender == "m-in" then
base.gender = "m"
base.animacy = "inan"
elseif base.declgender == "f" or base.declgender == "n" then
base.gender = base.declgender
else
error(("Unrecognized value '%s' for 'declgender', should be 'm-an', 'm-in', 'f' or 'n'"):format(base.declgender))
end
end
end
-- Check for bad indicator combinations.
if (base.hard and 1 or 0) + (base.soft and 1 or 0) > 1 then
error("At most one of 'hard' or 'soft' can be specified")
end
if base.istem and base["-istem"] then
error("'istem' and '-istem' cannot be specified together")
end
if (base.istem or base["-istem"]) then
if not regular_noun then
error("'istem' and '-istem' can only be specified with regular nouns")
end
end
if base.declgender and not regular_noun then
error("'declgender' can only be specified with regular nouns")
end
end
local function set_all_defaults_and_check_bad_indicators(alternant_multiword_spec)
local is_multiword = #alternant_multiword_spec.alternant_or_word_specs > 1
iut.map_word_specs(alternant_multiword_spec, function(base)
set_defaults_and_check_bad_indicators(base)
base.multiword = is_multiword -- FIXME: not currently used; consider deleting
alternant_multiword_spec.has_clitic = alternant_multiword_spec.has_clitic or base.has_clitic
if base.pron then
alternant_multiword_spec.saw_pron = true
else
alternant_multiword_spec.saw_non_pron = true
end
if base.det then
alternant_multiword_spec.saw_det = true
else
alternant_multiword_spec.saw_non_det = true
end
if base.num then
alternant_multiword_spec.saw_num = true
else
alternant_multiword_spec.saw_non_num = true
end
end)
end
local function undo_second_palatalization(base, word, is_adjective)
local function try(from, to)
local stem = rmatch(word, "^(.*)" .. from .. "$")
if stem then
return stem .. to
end
return nil
end
return is_adjective and try("št", "sk") or
is_adjective and try("čt", "ck") or
try("c", "k") or -- FIXME, this could be wrong and c correct
try("ř", "r") or
try("z", "h") or -- FIXME, this could be wrong and z or g correct
try("š", "ch") or
word
end
-- For a plural-only lemma, synthesize a likely singular lemma. It doesn't have to be
-- theoretically correct as long as it generates all the correct plural forms.
local function synthesize_singular_lemma(base)
if not base.stem_sets then
base.stem_sets = {{}}
end
local lemma_determined
-- Loop over all stem sets in case the user specified multiple ones (e.g. '*,-*'). If we try to reconstruct
-- different lemmas for different stem sets, we'll throw an error below.
for _, stems in ipairs(base.stem_sets) do
local stem, lemma
while true do
if base.indecl then
-- If specified as indeclinable, leave it alone; e.g. 'pesos' indeclinable plural of [[peso]].
lemma = base.lemma
break
elseif base.gender == "m" then
stem = rmatch(base.lemma, "^(.*)ojo$")
if stem then
lemma = undo_second_palatalization(base, stem)
else
stem = rmatch(base.lemma, "^(.*)[oyie]$")
if stem then
lemma = stem
else
error(("Masculine plural-only lemma '%s' should end in -ojo, -o, -y, -i or -e"):format(base.lemma))
end
end
if stems.reducible == nil then
if rfind(lemma, com.cons_c .. "[ck]$") and not com.is_monosyllabic(base.lemma) then
stems.reducible = true
end
if stems.reducible then
lemma = dereduce(base, lemma)
end
end
break
elseif base.gender == "f" then
stem = rmatch(base.lemma, "^(.*)y$")
if stem then
lemma = stem .. "a"
break
end
stem = rmatch(base.lemma, "^(.*)[eě]$")
if stem then
-- Singular like the plural. Cons-stem feminines like [[dlaň]] "palm (of the hand)" have identical
-- plurals to soft-stem feminines like [[růže]] (modulo e/ě differences), so we don't need to
-- reconstruct the former type.
lemma = base.lemma
break
end
stem = rmatch(base.lemma, "^(.*)i$")
if stem then
-- i-stems.
lemma = stem
base.istem = true
break
end
error(("Feminine plural-only lemma '%s' should end in -y, -ě, -e or -i"):format(base.lemma))
elseif base.gender == "n" then
-- -ata nouns like [[slůně]] "baby elephant" nom pl 'slůňata' are declined in the plural same as if
-- the singular were 'slůňato' so we don't have to worry about them.
stem = rmatch(base.lemma, "^(.*)a$")
if stem then
lemma = stem .. "o"
break
end
stem = rmatch(base.lemma, "^(.*)[eěí]$")
if stem then
-- singular lemma also in -e, -ě or -í; e.g. [[věčná loviště]] "[[happy hunting ground]]"
lemma = base.lemma
break
end
error(("Neuter plural-only lemma '%s' should end in -a, -í, -ě or -e"):format(base.lemma))
else
error(("Internal error: Unrecognized gender '%s'"):format(base.gender))
end
end
if lemma_determined and lemma_determined ~= lemma then
error(("Attempt to set two different singular lemmas '%s' and '%s'"):format(lemma_determined, lemma))
end
lemma_determined = lemma
end
base.lemma = lemma_determined
end
-- For an adjectival lemma, synthesize the masc singular form.
local function synthesize_adj_lemma(base)
local stem
if base.indecl then
base.decl = "indecl"
stem = base.lemma
else
local gender, number
local function sub_ov(stem)
stem = stem:gsub("ov$", "ův")
return stem
end
while true do
if base.number == "pl" then
if base.gender == "m" then
stem = rmatch(base.lemma, "^(.*)í$")
if stem then
if base.soft then
-- nothing to do
else
if base.animacy ~= "pr" then
error(("Masculine plural-only adjectival lemma '%s' ending in -í can only be animate unless '.soft' is specified"):
format(base.lemma))
end
base.lemma = undo_second_palatalization(base, stem, "is adjective") .. "ý"
end
break
end
stem = rmatch(base.lemma, "^(.*)é$")
if stem then
if base.animacy == "pr" then
error(("Masculine plural-only adjectival lemma '%s' ending in -é must be inanimate"):
format(base.lemma))
end
base.lemma = stem .. "ý"
break
end
stem = rmatch(base.lemma, "^(.*ov)i$") or rmatch(base.lemma, "^(.*in)i$")
if stem then
if base.animacy ~= "pr" then
error(("Masculine plural-only possessive adjectival lemma '%s' ending in -i must be animate"):
format(base.lemma))
end
base.lemma = sub_ov(stem)
break
end
stem = rmatch(base.lemma, "^(.*ov)y$") or rmatch(base.lemma, "^(.*in)y$")
if stem then
if base.animacy == "pr" then
error(("Masculine plural-only possessive adjectival lemma '%s' ending in -y must be inanimate"):
format(base.lemma))
end
base.lemma = sub_ov(stem)
break
end
if base.animacy == "pr" then
error(("Animate masculine plural-only adjectival lemma '%s' should end in -í, -ovi or -ini"):
format(base.lemma))
elseif base.soft then
error(("Soft masculine plural-only adjectival lemma '%s' should end in -í"):format(base.lemma))
else
error(("Inanimate masculine plural-only adjectival lemma '%s' should end in -é, -ovy or -iny"):
format(base.lemma))
end
elseif base.gender == "f" then
stem = rmatch(base.lemma, "^(.*)é$") -- hard adjective
if stem then
base.lemma = stem .. "ý"
break
end
stem = rmatch(base.lemma, "^(.*)í$") -- soft adjective
if stem then
break
end
stem = rmatch(base.lemma, "^(.*ov)y$") or rmatch(base.lemma, "^(.*in)y$") -- possessive adjective
if stem then
base.lemma = sub_ov(stem)
break
end
error(("Feminine plural-only adjectival lemma '%s' should end in -é, -í, -ovy or -iny"):format(base.lemma))
else
stem = rmatch(base.lemma, "^(.*)á$") -- hard adjective
if stem then
base.lemma = stem .. "ý"
break
end
stem = rmatch(base.lemma, "^(.*)í$") -- soft adjective
if stem then
break
end
stem = rmatch(base.lemma, "^(.*ov)a$") or rmatch(base.lemma, "^(.*in)a$") -- possessive adjective
if stem then
base.lemma = sub_ov(stem)
break
end
error(("Neuter plural-only adjectival lemma '%s' should end in -á, -í, -ova or -ina"):format(base.lemma))
end
else
if base.gender == "m" then
stem = rmatch(base.lemma, "^(.*)[ýí]$") or rmatch(base.lemma, "^(.*)ův$") or rmatch(base.lemma, "^(.*)in$")
if stem then
break
end
error(("Masculine adjectival lemma '%s' should end in -ý, -í, -ův or -in"):format(base.lemma))
elseif base.gender == "f" then
stem = rmatch(base.lemma, "^(.*)á$")
if stem then
base.lemma = stem .. "ý"
break
end
stem = rmatch(base.lemma, "^(.*)í$")
if stem then
break
end
stem = rmatch(base.lemma, "^(.*ov)a$") or rmatch(base.lemma, "^(.*in)a$")
if stem then
base.lemma = sub_ov(stem)
break
end
error(("Feminine adjectival lemma '%s' should end in -á, -í, -ova or -ina"):format(base.lemma))
else
stem = rmatch(base.lemma, "^(.*)í$")
if stem then
break
end
stem = rmatch(base.lemma, "^(.*ov)o$") or rmatch(base.lemma, "^(.*in)o$")
if stem then
base.lemma = sub_ov(stem)
break
end
error(("Neuter adjectival lemma '%s' should end in -é, -í, -ovo or -ino"):format(base.lemma))
end
end
end
base.decl = "adj"
end
-- Now set the stem sets if not given.
-- Now set the stem sets if not given.
if not base.stem_sets then
base.stem_sets = {{reducible = false}}
end
for _, stems in ipairs(base.stem_sets) do
-- Set the stems.
stems.vowel_stem = stem
stems.nonvowel_stem = stem
end
end
-- Determine the declension based on the lemma, gender and number. The declension is set in base.decl. In the process,
-- we set either base.vowel_stem (if the lemma ends in a vowel) or base.nonvowel_stem (if the lemma does not end in a
-- vowel), which is used by determine_stems(). In some cases (specifically with certain foreign nouns), we set
-- base.lemma to a new value; this is as if the user specified 'decllemma:'.
local function determine_declension(base)
if base.indecl then
base.decl = "indecl"
base.nonvowel_stem = base.lemma
return
end
-- Determine declension
stem = rmatch(base.lemma, "^(.*)a$")
if stem then
if base.gender == "m" then
if base.animacy ~= "pr" then
error("Masculine lemma in -a must be animate")
end
base.decl = "a-m"
elseif base.gender == "f" then
if base.hard then
base.decl = "hard-f"
elseif base.soft then
base.decl = "soft-f"
elseif base.adje then
base.decl = "adje-f"
elseif rfind(base.lemma, com.velar_c .. "a$") then
base.decl = "velar-f"
elseif rfind(base.lemma, "[czs]" .. "a$") then
base.decl = "czs-f"
elseif rfind(base.lemma, com.inherently_soft_c .. "a$") then
base.decl = "soft-f"
else
base.decl = "hard-f"
end
elseif base.gender == "n" then
if rfind(stem, "m$") then
base.decl = "ma-n"
else
error("Lemma ending in -a and neuter must end in -ma")
end
end
base.vowel_stem = stem
return
end
local ending
stem, ending = rmatch(base.lemma, "^(.*)e$")
if stem then
if base.tstem then
base.decl = "tstem-n"
elseif base.adje then
base.decl = "adje-n"
else
base.decl = "soft-n"
end
base.vowel_stem = stem
return
end
stem = rmatch(base.lemma, "^(.*)o$")
if stem then
if base.gender == "m" then
-- Cf. [[maestro]] m.
base.decl = "o-m"
elseif base.gender == "f" then
-- [[zoo]]; [[Žemaitsko]]?
error("Feminine nouns in -o are indeclinable; use '.indecl' if needed")
elseif base.hard then
base.decl = "hard-n"
elseif base.tstem then
base.decl = "tstem-n"
elseif base.nstem then
base.decl = "nstem-n"
elseif rfind(base.lemma, "[czs]" .. "o$") then
base.decl = "czs-n"
elseif rfind(base.lemma, com.inherently_soft_c .. "o$") then
base.decl = "soft-n"
elseif rfind(base.lemma, com.velar_c .. "o$") then
base.decl = "velar-n"
else
base.decl = "hard-n"
end
base.vowel_stem = stem
return
end
stem = rmatch(base.lemma, "^(.*)[iy]$")
if stem then
if base.gender == "m" then
if base.adje then
base.decl = "adje-m"
end
end
base.vowel_stem = stem
return
end
stem = rmatch(base.lemma, "^(.*" .. com.cons_c .. ")$")
if stem then
if base.gender == "m" then
if base.hard then
base.decl = "hard-m"
elseif base.soft then
base.decl = "soft-m"
elseif rfind(base.lemma, com.velar_c .. "$") then
base.decl = "velar-m"
elseif rfind(base.lemma, "[czs]" .. "$") then
base.decl = "czs-m"
elseif rfind(base.lemma, com.inherently_soft_c .. "$") then
base.decl = "soft-m"
else
base.decl = "hard-m"
end
elseif base.gender == "f" then
if base.vstem then
base.decl = "v-f"
stem = rmatch(base.lemma, "^(.*)ej$")
elseif base.soft then
base.decl = "soft-f"
elseif rfind(base.lemma, "[czs]" .. "$") then
base.decl = "czs-f"
else
base.decl = "soft-f"
end
elseif base.gender == "n" then
if base.foreign then
stem = rmatch(base.lemma, "^(.*)um$") or rmatch(base.lemma, "^(.*)on$")
if not stem then
error("Unrecognized neuter foreign ending, should be -um or -on")
end
if base.hard then
base.decl = "hard-n"
elseif rfind(stem, "[eiuy]$") then
base.decl = "semisoft-n"
else
base.decl = "hard-n"
end
-- set the lemma here as if decllemma: were given
base.lemma = stem .. "o"
base.vowel_stem = stem
return
else
error("Neuter nouns ending in a consonant should use '.foreign' or '.decllemma:...'")
end
end
base.nonvowel_stem = stem
return
end
error("Unrecognized ending for lemma: '" .. base.lemma .. "'")
end
-- Determine the default value for the 'reducible' flag.
local function determine_default_reducible(base)
-- Nouns in vowels other than -a/o as well as masculine nouns ending in all vowels don't have null endings so not
-- reducible. Note, we are never called on adjectival nouns.
if rfind(base.lemma, "[iyuíeě]$") or base.gender == "m" and rfind(base.lemma, "[ao]$") or base.tstem then
base.default_reducible = false
return
end
local stem
stem = rmatch(base.lemma, "^(.*" .. com.cons_c .. ")$")
if stem then
if base.gender == "m" and rfind(stem, "e[ck]$") and not com.is_monosyllabic(stem) then
base.default_reducible = true
elseif base.gender == "f" and rfind(stem, "eń$") then
-- pěseń
base.default_reducible = true
else
base.default_reducible = false
end
return
end
base.default_reducible = false
end
-- Determine the stems to use for each stem set: vowel and nonvowel stems, for singular
-- and plural. We assume that one of base.vowel_stem or base.nonvowel_stem has been
-- set in determine_declension(), depending on whether the lemma ends in
-- a vowel. We construct all the rest given the reducibility, vowel alternation spec and
-- any explicit stems given. We store the determined stems inside of the stem-set objects
-- in `base.stem_sets`, meaning that if the user gave multiple reducible or vowel-alternation
-- patterns, we will compute multiple sets of stems. The reason is that the stems may vary
-- depending on the reducibility and vowel alternation.
local function determine_stems(base)
if not base.stem_sets then
base.stem_sets = {{}}
end
-- Set default reducible and check for default mixed reducible, which needs to be expanded into two entries.
local default_mixed_reducible = false
for _, stems in ipairs(base.stem_sets) do
if stems.reducible == nil then
stems.reducible = base.default_reducible
end
end
if default_mixed_reducible then
local new_stem_sets = {}
for _, stems in ipairs(base.stem_sets) do
table.insert(new_stem_sets, stems)
end
base.stem_sets = new_stem_sets
end
-- Now determine all the stems for each stem set.
for _, stems in ipairs(base.stem_sets) do
local lemma_is_vowel_stem = not not base.vowel_stem
if base.vowel_stem then
stems.vowel_stem = base.vowel_stem
stems.nonvowel_stem = stems.vowel_stem
-- Apply vowel alternation first in cases like jádro -> jader; apply_vowel_alternation() will throw an error
-- if the vowel being modified isn't the last vowel in the stem.
stems.oblique_nonvowel_stem = com.apply_vowel_alternation(stems.vowelalt, stems.nonvowel_stem)
if stems.reducible then
stems.nonvowel_stem = dereduce(base, stems.nonvowel_stem)
stems.oblique_nonvowel_stem = dereduce(base, stems.oblique_nonvowel_stem)
end
else
stems.nonvowel_stem = base.nonvowel_stem
-- The user specified #. E.g. nóc nocy
if stems.oblique_slots then
stems.oblique_slots = "all"
end
stems.oblique_nonvowel_stem = com.apply_vowel_alternation(stems.vowelalt, stems.nonvowel_stem)
if stems.reducible then
stems.vowel_stem = com.reduce(base.nonvowel_stem)
if not stems.vowel_stem then
error("Unable to reduce stem '" .. base.nonvowel_stem .. "'")
end
else
stems.vowel_stem = base.nonvowel_stem
end
end
stems.oblique_vowel_stem = com.apply_vowel_alternation(stems.vowelalt, stems.vowel_stem)
end
end
local function detect_indicator_spec(base)
if base.pron then
determine_pronoun_stems(base)
elseif base.det then
determine_determiner_stems(base)
elseif base.num then
determine_numeral_stems(base)
elseif base.adj then
process_declnumber(base)
synthesize_adj_lemma(base)
elseif base.manual then
if base.stem_sets then
-- FIXME, maybe this should be allowed?
error("Reducible and vowel alternation specs cannot be given with manual declensions")
end
base.stem_sets = {{reducible = false, vowel_stem = "", nonvowel_stem = ""}}
base.decl = "manual"
else
if base.number == "pl" then
synthesize_singular_lemma(base)
end
determine_declension(base)
determine_default_reducible(base)
determine_stems(base)
end
end
local function detect_all_indicator_specs(alternant_multiword_spec)
alternant_multiword_spec.sg_genders = {}
alternant_multiword_spec.pl_genders = {}
iut.map_word_specs(alternant_multiword_spec, function(base)
detect_indicator_spec(base)
if base.number ~= "pl" then
alternant_multiword_spec.sg_genders[base.actual_gender] = true
end
if base.number ~= "sg" then
-- All t-stem masculines are neuter in the plural.
local plgender
plgender = base.actual_gender
alternant_multiword_spec.pl_genders[plgender] = true
end
end)
if (alternant_multiword_spec.saw_pron and 1 or 0) + (alternant_multiword_spec.saw_det and 1 or 0) + (alternant_multiword_spec.saw_num and 1 or 0) > 1 then
error("Can't combine pronouns, determiners and/or numerals")
end
end
local propagate_multiword_properties
local function propagate_alternant_properties(alternant_spec, property, mixed_value, nouns_only)
local seen_property
for _, multiword_spec in ipairs(alternant_spec.alternants) do
propagate_multiword_properties(multiword_spec, property, mixed_value, nouns_only)
if seen_property == nil then
seen_property = multiword_spec[property]
elseif multiword_spec[property] and seen_property ~= multiword_spec[property] then
seen_property = mixed_value
end
end
alternant_spec[property] = seen_property
end
propagate_multiword_properties = function(multiword_spec, property, mixed_value, nouns_only)
local seen_property = nil
local last_seen_nounal_pos = 0
local word_specs = multiword_spec.alternant_or_word_specs or multiword_spec.word_specs
for i = 1, #word_specs do
local is_nounal
if word_specs[i].alternants then
propagate_alternant_properties(word_specs[i], property, mixed_value)
is_nounal = not not word_specs[i][property]
elseif nouns_only then
is_nounal = is_regular_noun(word_specs[i])
else
is_nounal = not not word_specs[i][property]
end
if is_nounal then
if not word_specs[i][property] then
error("Internal error: noun-type word spec without " .. property .. " set")
end
for j = last_seen_nounal_pos + 1, i - 1 do
word_specs[j][property] = word_specs[j][property] or word_specs[i][property]
end
last_seen_nounal_pos = i
if seen_property == nil then
seen_property = word_specs[i][property]
elseif seen_property ~= word_specs[i][property] then
seen_property = mixed_value
end
end
end
if last_seen_nounal_pos > 0 then
for i = last_seen_nounal_pos + 1, #word_specs do
word_specs[i][property] = word_specs[i][property] or word_specs[last_seen_nounal_pos][property]
end
end
multiword_spec[property] = seen_property
end
local function propagate_properties_downward(alternant_multiword_spec, property, default_propval)
local function set_and_fetch(obj, default)
local retval
if obj[property] then
retval = obj[property]
else
obj[property] = default
retval = default
end
if not obj["actual_" .. property] then
obj["actual_" .. property] = retval
end
return retval
end
local propval1 = set_and_fetch(alternant_multiword_spec, default_propval)
for _, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do
local propval2 = set_and_fetch(alternant_or_word_spec, propval1)
if alternant_or_word_spec.alternants then
for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do
local propval3 = set_and_fetch(multiword_spec, propval2)
for _, word_spec in ipairs(multiword_spec.word_specs) do
local propval4 = set_and_fetch(word_spec, propval3)
if propval4 == "mixed" then
-- FIXME, use clearer error message.
error("Attempt to assign mixed " .. property .. " to word")
end
set_and_fetch(word_spec, propval4)
end
end
else
if propval2 == "mixed" then
-- FIXME, use clearer error message.
error("Attempt to assign mixed " .. property .. " to word")
end
set_and_fetch(alternant_or_word_spec, propval2)
end
end
end
--[=[
Propagate `property` (one of "animacy", "gender" or "number") from nouns to adjacent
adjectives. We proceed as follows:
1. We assume the properties in question are already set on all nouns. This should happen in
set_defaults_and_check_bad_indicators().
2. We first propagate properties upwards and sideways. We recurse downwards from the top. When we encounter a multiword
spec, we proceed left to right looking for a noun. When we find a noun, we fetch its property (recursing if the noun
is an alternant), and propagate it to any adjectives to its left, up to the next noun to the left. When we have
processed the last noun, we also propagate its property value to any adjectives to the right (to handle e.g.
[[anděl strážný]] "guardian angel", where the adjective [[strážný]] should inherit the 'masculine' and 'animate'
properties of [[anděl]]). Finally, we set the property value for the multiword spec itself by combining all the
non-nil properties of the individual elements. If all non-nil properties have the same value, the result is that
value, otherwise it is `mixed_value` (which is "mixed" for animacy and gender, but "allthree" for number).
3. When we encounter an alternant spec in this process, we recursively process each alternant (which is a multiword
spec) using the previous step, and combine any non-nil properties we encounter the same way as for multiword specs.
4. The effect of steps 2 and 3 is to set the property of each alternant and multiword spec based on its children or its
neighbors.
]=]
local function propagate_properties(alternant_multiword_spec, property, default_propval, mixed_value)
propagate_multiword_properties(alternant_multiword_spec, property, mixed_value, "nouns only")
propagate_multiword_properties(alternant_multiword_spec, property, mixed_value, false)
propagate_properties_downward(alternant_multiword_spec, property, default_propval)
end
local function determine_noun_status(alternant_multiword_spec)
for i, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do
if alternant_or_word_spec.alternants then
local is_noun = false
for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do
for j, word_spec in ipairs(multiword_spec.word_specs) do
if is_regular_noun(word_spec) then
multiword_spec.first_noun = j
is_noun = true
break
end
end
end
if is_noun then
alternant_multiword_spec.first_noun = i
end
elseif is_regular_noun(alternant_or_word_spec) then
alternant_multiword_spec.first_noun = i
return
end
end
end
-- Set the part of speech based on properties of the individual words.
local function set_pos(alternant_multiword_spec)
if alternant_multiword_spec.args.pos then
alternant_multiword_spec.pos = alternant_multiword_spec.args.pos
elseif alternant_multiword_spec.saw_pron and not alternant_multiword_spec.saw_non_pron then
alternant_multiword_spec.pos = "သဗ္ဗနာမ်"
elseif alternant_multiword_spec.saw_det and not alternant_multiword_spec.saw_non_det then
alternant_multiword_spec.pos = "ဖျေံလဝ်သန္နိဋ္ဌာန်"
elseif alternant_multiword_spec.saw_num and not alternant_multiword_spec.saw_non_num then
alternant_multiword_spec.pos = "ဂၞန်သၚ်္ချာ"
else
alternant_multiword_spec.pos = "နာမ်"
end
alternant_multiword_spec.plpos = require(en_utilities_module).pluralize(alternant_multiword_spec.pos)
end
local function normalize_all_lemmas(alternant_multiword_spec, pagename)
iut.map_word_specs(alternant_multiword_spec, function(base)
if base.lemma == "" then
base.lemma = pagename
end
base.orig_lemma = base.lemma
base.orig_lemma_no_links = m_links.remove_links(base.lemma)
local lemma = base.orig_lemma_no_links
-- If the lemma is all-uppercase, lowercase it but note this, so that later in combine_stem_ending() we convert it
-- back to uppercase. This allows us to handle all-uppercase acronyms without a lot of extra complexity.
-- FIXME: This may not make sense at all.
if uupper(lemma) == lemma then
base.all_uppercase = true
lemma = ulower(lemma)
end
base.actual_lemma = lemma
base.lemma = base.decllemma or lemma
end)
end
local function decline_noun(base)
for _, stems in ipairs(base.stem_sets) do
if not decls[base.decl] then
error("Internal error: Unrecognized declension type '" .. base.decl .. "'")
end
decls[base.decl](base, stems)
end
handle_derived_slots_and_overrides(base)
local function copy(from_slot, to_slot)
base.forms[to_slot] = base.forms[from_slot]
end
if base.gender ~= "m" then
copy("nom_d", "acc_d")
end
copy("nom_d", "voc_d")
copy("dat_d", "loc_d")
copy("dat_d", "ins_d")
if base.actual_number ~= base.number then
local source_num = base.number == "sg" and "_s" or base.number == "du" and "_d" or "_p"
local dest_num = base.number == "sg" and {"_p", "_d"} or base.number == "du" and {"_s", "_p"} or {"_s", "_d"}
for case, _ in pairs(cases) do
copy(case .. source_num, case .. dest_num)
copy("nom" .. source_num .. "_linked", "nom" .. dest_num .. "_linked")
end
if base.actual_number ~= "allthree" then
local erase_num = base.actual_number == "sg" and {"_d", "_p"} or base.actual_number == "du" and {"_s", "_p"} or {"_s", "_d"}
for case, _ in pairs(cases) do
base.forms[case .. erase_num] = nil
end
base.forms["nom" .. erase_num .. "_linked"] = nil
end
end
end
local function get_variants(form)
return nil
--[=[
FIXME
return
form:find(com.VAR1) and "var1" or
form:find(com.VAR2) and "var2" or
form:find(com.VAR3) and "var3" or
nil
]=]
end
-- Compute the categories to add the noun to, as well as the annotation to display in the
-- declension title bar. We combine the code to do these functions as both categories and
-- title bar contain similar information.
local function compute_categories_and_annotation(alternant_multiword_spec)
local all_cats = {}
local function insert(cattype)
m_table.insertIfNot(all_cats, "Upper Sorbian " .. cattype)
end
if alternant_multiword_spec.pos == "နာမ်" then
if alternant_multiword_spec.actual_number == "sg" then
-- insert("uncountable nouns")
elseif alternant_multiword_spec.actual_number == "du" then
-- insert("dualia tantum")
elseif alternant_multiword_spec.actual_number == "pl" then
-- insert("pluralia tantum")
end
end
local annotation
local annparts = {}
local decldescs = {}
local vowelalts = {}
local foreign = {}
local irregs = {}
local stemspecs = {}
local reducible = nil
local function get_genanim(gender, animacy)
local gender_code_to_desc = {
m = "ပုလ္လိၚ်",
f = "ဣတ္တိလိၚ်",
n = "နပုလ္လိၚ်",
none = nil,
}
local animacy_code_to_desc = {
pr = "ပူဂဵု",
anml = "ဆေၚ်စပ်ကဵုသတ်",
inan = "မသက္ကုဟၟဲကဵုလမျီု",
none = nil,
}
local descs = {}
table.insert(descs, gender_code_to_desc[gender])
if gender ~= "f" and gender ~= "n" then
-- masculine or "none" (e.g. certain pronouns and numerals)
table.insert(descs, animacy_code_to_desc[animacy])
end
return table.concat(descs, " ")
end
local function trim(text)
text = text:gsub(" +", " ")
return mw.text.trim(text)
end
local function do_word_spec(base)
local actual_genanim = get_genanim(base.actual_gender, base.actual_animacy)
local declined_genanim = get_genanim(base.gender, base.animacy)
local genanim
genanim = actual_genanim
if base.actual_gender == "m" then
insert(actual_genanim .. " " .. alternant_multiword_spec.plpos)
end
for _, stems in ipairs(base.stem_sets) do
local props = declprops[base.decl]
local cats = props.cat
if type(cats) == "function" then
cats = cats(base, stems)
end
if type(cats) == "string" then
cats = {cats}
end
local default_desc
for i, cat in ipairs(cats) do
if not cat:find("GENDER") and not cat:find("GENPOS") and not cat:find("POS") then
cat = cat
end
cat = cat:gsub("GENPOS", "GENDER POS")
if not cat:find("POS") then
cat = cat .. " POS"
end
if i == #cats then
default_desc = cat:gsub(" POS", "")
end
cat = cat:gsub("GENDER", actual_genanim)
cat = cat:gsub("POS", alternant_multiword_spec.plpos)
-- Need to trim `cat` because actual_genanim may be an empty string.
insert(trim(cat))
end
local desc = props.desc
if type(desc) == "function" then
desc = desc(base, stems)
end
desc = desc or default_desc
desc = desc:gsub("GENDER", genanim)
-- Need to trim `desc` because genanim may be an empty string.
m_table.insertIfNot(decldescs, trim(desc))
local vowelalt
if stems.vowelalt == "quant" then
vowelalt = "quant-alt"
-- insert("nouns with quantitative vowel alternation")
elseif stems.vowelalt == "quant-ě" then
vowelalt = "í-ě-alt"
-- insert("nouns with í-ě alternation")
end
if vowelalt then
m_table.insertIfNot(vowelalts, vowelalt)
end
if reducible == nil then
reducible = stems.reducible
end
if stems.reducible then
-- insert("nouns with reducible stem")
end
if base.foreign then
m_table.insertIfNot(foreign, "foreign")
if not base.decllemma then
-- NOTE: there are nouns that use both 'foreign' and 'decllemma', e.g. [[Zeus]].
-- insert("nouns with regular foreign declension")
end
end
-- User-specified 'decllemma:' indicates irregular stem. Don't consider foreign nouns in -us/-os/-es, -um/-on or
-- silent -e (e.g. [[software]]) where this ending is simply dropped in oblique and plural forms as irregular;
-- there are too many of these and they are already categorized above as 'nouns with regular foreign declension'.
if base.decllemma then
m_table.insertIfNot(irregs, "irreg-stem")
-- insert("nouns with irregular stem")
end
m_table.insertIfNot(stemspecs, stems.vowel_stem)
end
end
local key_entry = alternant_multiword_spec.first_noun or 1
if #alternant_multiword_spec.alternant_or_word_specs >= key_entry then
local alternant_or_word_spec = alternant_multiword_spec.alternant_or_word_specs[key_entry]
if alternant_or_word_spec.alternants then
for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do
key_entry = multiword_spec.first_noun or 1
if #multiword_spec.word_specs >= key_entry then
do_word_spec(multiword_spec.word_specs[key_entry])
end
end
else
do_word_spec(alternant_or_word_spec)
end
end
if alternant_multiword_spec.actual_number == "sg" or alternant_multiword_spec.actual_number == "pl" or alternant_multiword_spec.actual_number == "du" then
-- not "allthree" or "none" (for [[sebe]])
table.insert(annparts, alternant_multiword_spec.actual_number == "sg" and "sg-only" or alternant_multiword_spec.actual_number == "du" and "du-only" or "pl-only")
end
if #decldescs == 0 then
table.insert(annparts, "indecl")
else
table.insert(annparts, table.concat(decldescs, " // "))
end
if #vowelalts > 0 then
table.insert(annparts, table.concat(vowelalts, "/"))
end
if reducible == "mixed" then
table.insert(annparts, "mixed-reducible")
elseif reducible then
table.insert(annparts, "reducible")
end
if #foreign > 0 then
table.insert(annparts, table.concat(foreign, " // "))
end
if #irregs > 0 then
table.insert(annparts, table.concat(irregs, " // "))
end
alternant_multiword_spec.annotation = table.concat(annparts, " ")
if #stemspecs > 1 then
insert("nouns with multiple stems")
end
if alternant_multiword_spec.actual_number == "allthree" and not m_table.deepEquals(alternant_multiword_spec.sg_genders, alternant_multiword_spec.pl_genders) then
insert("nouns that change gender in the plural")
end
alternant_multiword_spec.categories = all_cats
end
local function show_forms(alternant_multiword_spec)
local lemmas = {}
for _, slot in ipairs(potential_lemma_slots) do
if alternant_multiword_spec.forms[slot] then
for _, formobj in ipairs(alternant_multiword_spec.forms[slot]) do
-- FIXME, now can support footnotes as qualifiers in headwords?
table.insert(lemmas, formobj.form)
end
break
end
end
local props = {
lemmas = lemmas,
slot_table = alternant_multiword_spec.output_noun_slots,
lang = lang,
canonicalize = function(form)
-- return com.remove_variant_codes(form)
return form
end,
}
iut.show_forms(alternant_multiword_spec.forms, props)
end
local function make_table(alternant_multiword_spec)
local forms = alternant_multiword_spec.forms
local function template_prelude(min_width)
return rsub([=[
<div>
<div class="NavFrame" style="max-width: MINWIDTHem">
<div class="NavHead" style="background:var(--wikt-palette-lighterblue, #ebf4ff)">{title}{annotation}</div>
<div class="NavContent">
{\op}| class="inflection-table inflection" style="width: 100%; margin: 0; text-align:center;"
|-
]=], "MINWIDTH", min_width)
end
local function template_postlude()
return [=[
|{\cl}{notes_clause}</div></div></div>]=]
end
local table_spec_allthree = template_prelude("45") .. [=[
|- class="rowgroup"
! style="background:var(--wikt-palette-lightblue, #d9ebff)" |
! scope="col" style="background:var(--wikt-palette-lightblue, #d9ebff)" | ကိုန်ဨကဝုစ်
! scope="col" style="background:var(--wikt-palette-lightblue, #d9ebff)" | ၜါလ္ပာ်
! scope="col" style="background:var(--wikt-palette-lightblue, #d9ebff)" | ကိုန်ဗဟုဝစ်
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | မဒုၚ်ယၟု
| {nom_s}
| {nom_d}
| {nom_p}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ဗဳဇဂကူ
| {gen_s}
| {gen_d}
| {gen_p}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ပြကမ္မကာရက
| {dat_s}
| {dat_d}
| {dat_p}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ကမ္မကာရက
| {acc_s}
| {acc_d}
| {acc_p}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | တိၚ်တိုက်ကပေါတ်ကွိၚ်ကွိုက်
| {ins_s}
| {ins_d}
| {ins_p}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ခၞံဗဒှ်ဌာန်မတန်တဴ
| {loc_s}
| {loc_d}
| {loc_p}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ပရေၚ်ဂယိုၚ်လမျီု
| {voc_s}
| {voc_d}
| {voc_p}
]=] .. template_postlude()
local function get_table_spec_one_number(number, numcode)
local table_spec_one_number = [=[
! style="width:33%;background:var(--wikt-palette-lightblue)" |
! style="background:var(--wikt-palette-lightblue)" | NUMBER
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | မဒုၚ်ယၟု
| {nom_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ဗဳဇဂကူ
| {gen_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ပြကမ္မကာရက
| {dat_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ကမ္မကာရက
| {acc_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ပရေၚ်ဂယိုၚ်လမျီု
| {voc_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ခၞံဗဒှ်ဌာန်မတန်တဴ
| {loc_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | တိၚ်တိုက်ကပေါတ်ကွိၚ်ကွိုက်
| {ins_CODE}
]=]
return template_prelude("30") .. table_spec_one_number:gsub("NUMBER", number):gsub("CODE", numcode) ..
template_postlude()
end
local function get_table_spec_one_number_clitic(number, numcode)
local table_spec_one_number_clitic = [=[
! rowspan=2 style="background:var(--wikt-palette-lightblue, #d9ebff)" |
! style="background:var(--wikt-palette-lightblue, #d9ebff)" |NUMBER
|-
! style="background:var(--wikt-palette-lightblue, #d9ebff)" |stressed
! style="background:var(--wikt-palette-lightblue, #d9ebff)" |clitic
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | မဒုၚ်ယၟု
| colspan=2 | {nom_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ဗဳဇဂကူ
| {gen_CODE}
| {clitic_gen_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ပြကမ္မကာရက
| {dat_CODE}
| {clitic_dat_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ကမ္မကာရက
| {acc_CODE}
| {clitic_acc_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ပရေၚ်ဂယိုၚ်လမျီု
| colspan=2 | {voc_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ခၞံဗဒှ်ဌာန်မတန်တဴ
| colspan=2 | {loc_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | တိၚ်တိုက်ကပေါတ်ကွိၚ်ကွိုက်
| colspan=2 | {ins_CODE}
]=]
return template_prelude("40") .. table_spec_one_number_clitic:gsub("NUMBER", number):gsub("CODE", numcode) ..
template_postlude()
end
local notes_template = [=[
<div style="width:100%;text-align:left;background:var(--wikt-palette-lightblue, #d9ebff)">
<div style="display:inline-block;text-align:left;padding-left:1em;padding-right:1em">
{footnote}
</div></div>
]=]
if alternant_multiword_spec.title then
forms.title = alternant_multiword_spec.title
else
forms.title = 'မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု <i lang="hsb">' .. forms.lemma .. '</i>'
end
local annotation = alternant_multiword_spec.annotation
if annotation == "" then
forms.annotation = ""
else
forms.annotation = " (<span style=\"font-size: smaller;\">" .. annotation .. "</span>)"
end
local number, numcode
if alternant_multiword_spec.actual_number == "sg" then
number, numcode = "singular", "s"
elseif alternant_multiword_spec.actual_number == "du" then
number, numcode = "dual", "d"
elseif alternant_multiword_spec.actual_number == "pl" then
number, numcode = "plural", "p"
elseif alternant_multiword_spec.actual_number == "none" then -- used for [[sebe]]
number, numcode = "", "s"
end
local table_spec =
alternant_multiword_spec.actual_number == "allthree" and table_spec_allthree or
alternant_multiword_spec.has_clitic and get_table_spec_one_number_clitic(number, numcode) or
get_table_spec_one_number(number, numcode)
forms.notes_clause = forms.footnote ~= "" and
m_string_utilities.format(notes_template, forms) or ""
return m_string_utilities.format(table_spec, forms)
end
local function compute_headword_genders(alternant_multiword_spec)
local genders = {}
local number
if alternant_multiword_spec.actual_number == "pl" then
number = "-p"
elseif alternant_multiword_spec.actual_number == "du" then
number = "-d"
else
number = ""
end
iut.map_word_specs(alternant_multiword_spec, function(base)
local animacy = base.animacy
if animacy == "inan" then
animacy = "in"
end
m_table.insertIfNot(genders, base.gender .. "-" .. animacy .. number)
end)
return genders
end
-- Externally callable function to parse and decline a noun given user-specified arguments.
-- Return value is ALTERNANT_MULTIWORD_SPEC, an object where the declined forms are in
-- `ALTERNANT_MULTIWORD_SPEC.forms` for each slot. If there are no values for a slot, the
-- slot key will be missing. The value for a given slot is a list of objects
-- {form=FORM, footnotes=FOOTNOTES}.
function export.do_generate_forms(parent_args, from_headword)
local params = {
[1] = {required = true, default = "žona<f>"},
title = {},
pagename = {},
json = {type = "boolean"},
pos = {},
}
if from_headword then
params["head"] = {list = true}
params["lemma"] = {list = true}
params["g"] = {list = true}
params["f"] = {list = true}
params["m"] = {list = true}
params["adj"] = {list = true}
params["dim"] = {list = true}
params["id"] = {}
end
local args = m_para.process(parent_args, params)
local parse_props = {
parse_indicator_spec = parse_indicator_spec,
angle_brackets_omittable = true,
allow_blank_lemma = true,
}
local alternant_multiword_spec = iut.parse_inflected_text(args[1], parse_props)
alternant_multiword_spec.title = args.title
alternant_multiword_spec.args = args
local pagename = args.pagename or from_headword and args.head[1] or mw.loadData("Module:headword/data").pagename
normalize_all_lemmas(alternant_multiword_spec, pagename)
set_all_defaults_and_check_bad_indicators(alternant_multiword_spec)
-- These need to happen before detect_all_indicator_specs() so that adjectives get their genders and numbers set
-- appropriately, which are needed to correctly synthesize the adjective lemma.
propagate_properties(alternant_multiword_spec, "animacy", "inan", "mixed")
propagate_properties(alternant_multiword_spec, "number", "allthree", "allthree")
-- FIXME, the default value (third param) used to be 'm' with a comment indicating that this applied only to
-- plural adjectives, where it didn't matter; but here, plural adjectives are distinguished for gender and
-- animacy. Make sure 'mixed' works.
propagate_properties(alternant_multiword_spec, "gender", "mixed", "mixed")
detect_all_indicator_specs(alternant_multiword_spec)
-- Propagate 'actual_number' after calling detect_all_indicator_specs(), which sets 'actual_number' for adjectives.
propagate_properties(alternant_multiword_spec, "actual_number", "allthree", "allthree")
determine_noun_status(alternant_multiword_spec)
set_pos(alternant_multiword_spec)
alternant_multiword_spec.output_noun_slots = get_output_noun_slots(alternant_multiword_spec)
local inflect_props = {
skip_slot = function(slot)
return skip_slot(alternant_multiword_spec.actual_number, slot)
end,
slot_table = alternant_multiword_spec.output_noun_slots,
get_variants = get_variants,
inflect_word_spec = decline_noun,
}
iut.inflect_multiword_or_alternant_multiword_spec(alternant_multiword_spec, inflect_props)
compute_categories_and_annotation(alternant_multiword_spec)
alternant_multiword_spec.genders = compute_headword_genders(alternant_multiword_spec)
if args.json then
alternant_multiword_spec.args = nil
return require("Module:JSON").toJSON(alternant_multiword_spec)
end
return alternant_multiword_spec
end
-- Entry point for {{hsb-ndecl}}. Template-callable function to parse and decline a noun given
-- user-specified arguments and generate a displayable table of the declined forms.
function export.show(frame)
local parent_args = frame:getParent().args
local alternant_multiword_spec = export.do_generate_forms(parent_args)
if type(alternant_multiword_spec) == "string" then
-- JSON return value
return alternant_multiword_spec
end
show_forms(alternant_multiword_spec)
return make_table(alternant_multiword_spec) ..
require("Module:utilities").format_categories(alternant_multiword_spec.categories, lang, nil, nil, force_cat)
end
return export
0a6ftnnu8r420h3blraju3nzc83v66l
396550
396540
2026-06-07T17:45:27Z
咽頭べさ
33
396550
Scribunto
text/plain
local export = {}
--[=[
Authorship: Zhnka, heavily based on [[Module:cs-noun]] by Benwing
]=]
--[=[
TERMINOLOGY:
-- "slot" = A particular combination of case/number.
Example slot names for nouns are "gen_s" (genitive singular) and
"voc_p" (vocative plural). Each slot is filled with zero or more forms.
-- "form" = The declined form representing the value of a given slot.
-- "lemma" = The dictionary form. Generally the nominative
masculine singular, but may occasionally be another form if the nominative
masculine singular is missing.
]=]
local lang = require("Module:languages").getByCode("hsb")
local m_table = require("Module:table")
local m_links = require("Module:links")
local m_string_utilities = require("Module:string utilities")
local iut = require("Module:inflection utilities")
local put = require("Module:parse utilities")
local m_para = require("Module:parameters")
local com = require("Module:hsb-common")
local en_utilities_module = "Module:en-utilities"
local current_title = mw.title.getCurrentTitle()
local NAMESPACE = current_title.nsText
local PAGENAME = current_title.text
local u = mw.ustring.char
local rsplit = mw.text.split
local rfind = mw.ustring.find
local rmatch = mw.ustring.match
local rgmatch = mw.ustring.gmatch
local rsubn = mw.ustring.gsub
local ulen = mw.ustring.len
local usub = mw.ustring.sub
local uupper = mw.ustring.upper
local ulower = mw.ustring.lower
local force_cat = false -- set to true to make categories appear in non-mainspace pages, for testing
-- 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
-- version of rsubn() that returns a 2nd argument boolean indicating whether
-- a substitution was made.
local function rsubb(term, foo, bar)
local retval, nsubs = rsubn(term, foo, bar)
return retval, nsubs > 0
end
local function track(track_id)
require("Module:debug/track")("hsb-noun/" .. track_id)
return true
end
local output_noun_slots = {
nom_s = "nom|s",
gen_s = "gen|s",
dat_s = "dat|s",
acc_s = "acc|s",
voc_s = "voc|s",
loc_s = "loc|s",
ins_s = "ins|s",
nom_d = "nom|d",
gen_d = "gen|d",
dat_d = "dat|d",
acc_d = "acc|d",
voc_d = "voc|d",
loc_d = "loc|d",
ins_d = "ins|d",
nom_p = "nom|p",
gen_p = "gen|p",
dat_p = "dat|p",
acc_p = "acc|p",
voc_p = "voc|p",
loc_p = "loc|p",
ins_p = "ins|p",
}
local function get_output_noun_slots(alternant_multiword_spec)
-- FIXME: To save memory we modify the table in-place. This won't work if we ever end up with multiple calls to
-- this module in the same Lua invocation, and we would need to clone the table.
if alternant_multiword_spec.actual_number ~= "allthree" then
for slot, accel_form in pairs(output_noun_slots) do
output_noun_slots[slot] = accel_form:gsub("|[sp]$", "")
end
end
return output_noun_slots
end
local potential_lemma_slots = {"nom_s", "nom_p", "gen_s"}
local cases = {
nom = true,
gen = true,
dat = true,
acc = true,
voc = true,
loc = true,
ins = true,
}
local clitic_cases = {
gen = true,
dat = true,
acc = true,
}
local function dereduce(base, stem)
local dereduced_stem = com.dereduce(base, stem)
if not dereduced_stem then
error("Unable to dereduce stem '" .. stem .. "'")
end
return dereduced_stem
end
local function skip_slot(number, slot)
return number == "sg" and rfind(slot, "_p$") or
number == "pl" and rfind(slot, "_s$")
end
-- Basic function to combine stem(s) and ending(s) and insert the result into the appropriate slot. `stems` is either
-- the `stems` object passed into the declension functions (containing the various stems; see below) or a string to
-- override the stem. (NOTE: If you pass a string in as `stems`, you should pass the value of `stems.footnotes` as the
-- value of `footnotes` as it will be lost otherwise. If you need to supply your own footnote in addition, use
-- iut.combine_footnotes() to combine any user-specified footnote(s) with your footnote(s).) `endings` is either a
-- string specifying a single ending or a list of endings. If `endings` is nil, no forms are inserted. If an ending is
-- "-", the value of `stems` is ignored and the lemma is used instead as the stem; this is important in case the user
-- used `decllemma:` to specify a declension lemma different from the actual lemma, or specified '.foreign' (which has
-- a similar effect).
local function add(base, slot, stems, endings, footnotes)
if not endings then
return
end
-- Call skip_slot() based on the declined number; if the actual number is different, we correct this in
-- decline_noun() at the end.
if skip_slot(base.number, slot) then
return
end
local stems_footnotes = type(stems) == "table" and stems.footnotes or nil
footnotes = iut.combine_footnotes(iut.combine_footnotes(base.footnotes, stems_footnotes), footnotes)
if type(endings) == "string" then
endings = {endings}
end
for _, ending in ipairs(endings) do
-- Compute the stem. If ending is "-", use the lemma regardless. Otherwise if `stems` is a string, use it.
-- Otherwise `stems` is an object containing four stems (vowel-vs-non-vowel cross regular-vs-oblique);
-- compute the appropriate stem based on the slot and whether the ending begins with a vowel.
local stem
if ending == "-" then
stem = base.actual_lemma
ending = ""
elseif type(stems) == "string" then
stem = stems
else
local is_vowel_ending = rfind(ending, "^" .. com.vowel_c)
if stems.oblique_slots == "all" then
if is_vowel_ending then
stem = stems.oblique_vowel_stem
else
stem = stems.oblique_nonvowel_stem
end
elseif is_vowel_ending then
stem = stems.vowel_stem
else
stem = stems.nonvowel_stem
end
end
ending = iut.combine_form_and_footnotes(ending, footnotes)
local function combine_stem_ending(stem, ending)
return com.combine_stem_ending(base, slot, stem, ending)
end
iut.add_forms(base.forms, slot, stem, ending, combine_stem_ending)
end
end
local function process_slot_overrides(base, do_slot)
for slot, overrides in pairs(base.overrides) do
-- Call skip_slot() based on the declined number; if the actual number is different, we correct this in
-- decline_noun() at the end.
if skip_slot(base.number, slot) then
error("Override specified for invalid slot '" .. slot .. "' due to '" .. base.number .. "' number restriction")
end
if do_slot(slot) then
base.slot_overridden[slot] = true
base.forms[slot] = nil
for _, override in ipairs(overrides) do
for _, value in ipairs(override.values) do
local form = value.form
local combined_notes = iut.combine_footnotes(base.footnotes, value.footnotes)
if override.full then
if form ~= "" then
iut.insert_form(base.forms, slot, {form = form, footnotes = combined_notes})
end
else
-- Convert a null ending to "-" in the acc/voc sg slots so that e.g. [[Kerberos]] declared as
-- <m.sg.foreign.gena:u.acc-:a> works correctly and generates accusative 'Kerberos/Kerbera' not
-- #'Kerber/Kerbera'.
if (slot == "acc_s" or slot == "voc_s") and form == "" then
form = "-"
end
for _, stems in ipairs(base.stem_sets) do
add(base, slot, stems, form, combined_notes)
end
end
end
end
end
end
end
local function add_decl(base, stems,
gen_s, dat_s, acc_s, voc_s, loc_s, ins_s,
nom_d, gen_d, dat_d,
nom_p, gen_p, dat_p, acc_p, loc_p, ins_p, nom_s, footnotes
)
add(base, "nom_s", stems, "-", footnotes)
add(base, "gen_s", stems, gen_s, footnotes)
add(base, "dat_s", stems, dat_s, footnotes)
add(base, "acc_s", stems, acc_s, footnotes)
add(base, "voc_s", stems, voc_s, footnotes)
add(base, "loc_s", stems, loc_s, footnotes)
add(base, "ins_s", stems, ins_s, footnotes)
add(base, "nom_d", stems, nom_d, footnotes)
add(base, "gen_d", stems, gen_d, footnotes)
add(base, "dat_d", stems, dat_d, footnotes)
if base.number == "pl" then
-- If this is a plurale tantum noun and we're processing the nominative plural, use the user-specified lemma
-- rather than generating the plural from the synthesized singular, which may not match the specified lemma
-- (e.g. [[tvargle]] "Olomouc cheese" using <m.pl.mixed> would try to generate 'tvargle/tvargly', and [[peníze]]
-- "money" using <m.pl.#ě.genpl-> would try to generate 'peněze').
local acc_p_like_nom = m_table.deepEquals(nom_p, acc_p)
nom_p = "-"
if acc_p_like_nom then
acc_p = "-"
end
end
add(base, "nom_p", stems, nom_p, footnotes)
add(base, "gen_p", stems, gen_p, footnotes)
add(base, "dat_p", stems, dat_p, footnotes)
add(base, "acc_p", stems, acc_p, footnotes)
add(base, "loc_p", stems, loc_p, footnotes)
add(base, "ins_p", stems, ins_p, footnotes)
add(base, "nom_s", stems, nom_s, footnotes)
end
local function add_sg_decl(base, stems,
gen_s, dat_s, acc_s, voc_s, loc_s, ins_s, footnotes
)
add_decl(base, stems, gen_s, dat_s, acc_s, voc_s, loc_s, ins_s,
nil, nil, nil,
nil, nil, nil, nil, nil, nil, footnotes)
end
local function add_du_only_decl(base, stems,
gen_d, dat_d, footnotes
)
add_decl(base, stems, nil, nil, nil, nil, nil, nil,
"-", gen_d, dat_d,
nil, nil, nil, nil, nil, nil, footnotes)
end
local function add_pl_only_decl(base, stems,
gen_p, dat_p, acc_p, loc_p, ins_p, footnotes
)
add_decl(base, stems, nil, nil, nil, nil, nil, nil,
nil, nil, nil,
"-", gen_p, dat_p, acc_p, loc_p, ins_p, footnotes)
end
local function handle_derived_slots_and_overrides(base)
local function is_non_derived_slot(slot)
return slot ~= "voc_p" and slot ~= "acc_s" and slot ~= "clitic_acc_s"
end
local function is_derived_slot(slot)
return not is_non_derived_slot(slot)
end
base.slot_overridden = {}
-- Handle overrides for the non-derived slots. Do this before generating the derived
-- slots so overrides of the source slots (e.g. nom_p) propagate to the derived slots.
process_slot_overrides(base, is_non_derived_slot)
-- Generate the remaining slots that are derived from other slots.
if not base.pron and not base.det then
-- Pronouns don't have a vocative (singular or plural).
iut.insert_forms(base.forms, "voc_p", base.forms.nom_p)
end
if not base.forms.acc_s and not base.slot_overridden.acc_s then
iut.insert_forms(base.forms, "acc_s", base.forms[base.animacy == "inan" and "nom_s" or base.animacy == "pr" and "gen_s" or base.animacy == "anml" and "gen_s"])
end
if not base.forms.acc_d and not base.slot_overridden.acc_d then
iut.insert_forms(base.forms, "acc_d", base.forms[base.animacy == "inan" and "nom_d" or base.animacy == "pr" and "gen_d" or base.animacy == "anml" and "nom_d"])
end
if not base.forms.acc_p and not base.slot_overridden.acc_p then
iut.insert_forms(base.forms, "acc_p", base.forms[base.animacy == "inan" and "nom_p" or base.animacy == "pr" and "gen_p" or base.animacy == "anml" and "nom_p"])
end
if not base.forms.clitic_acc_s and not base.slot_overridden.clitic_acc_s then
iut.insert_forms(base.forms, "clitic_acc_s", base.forms[base.animacy == "inan" and "nom_s" or "clitic_gen_s"])
end
-- Handle overrides for derived slots, to allow them to be overridden.
process_slot_overrides(base, is_derived_slot)
-- Compute linked versions of potential lemma slots, for use in {{hsb-noun}}.
-- We substitute the original lemma (before removing links) for forms that
-- are the same as the lemma, if the original lemma has links.
for _, slot in ipairs(potential_lemma_slots) do
iut.insert_forms(base.forms, slot .. "_linked", iut.map_forms(base.forms[slot], function(form)
if form == base.orig_lemma_no_links and rfind(base.orig_lemma, "%[%[") then
return base.orig_lemma
else
return form
end
end))
end
end
-- Table mapping declension types to functions to decline the noun. The function takes two arguments, `base` and
-- `stems`; the latter specifies the computed stems (vowel vs. non-vowel, singular vs. plural) and whether the noun
-- is reducible and/or has vowel alternations in the stem. Most of the specifics of determining which stem to use
-- and how to modify it for the given ending are handled in add_decl(); the declension functions just need to generate
-- the appropriate endings.
local decls = {}
-- Table specifying additional properties for declension types. Every declension type must have such a table, which
-- specifies which category or categories to add and what annotation to show in the title bar of the declension table.
--
-- * Only the `cat` property of this table is mandatory; there is also a `desc` property to specify the annotation, but
-- this can be omitted and the annotation will then be computed from the `cat` property. The `cat` property is either
-- a string, a list of strings or a function (of two arguments, `base` and `stems` as above) returning a string or
-- list of strings. The string can contain the keywords GENDER to substitute the gender (and animacy for masculine
-- nouns) and POS (to substitute the pluralized part of speech). The keyword GENPOS is equivalent to 'GENDER POS'. If
-- no keyword is present, ' GENPOS' is added onto the end. If only GENDER is present, ' POS' is added onto the end.
-- In all cases, the language name is added onto the beginning to form the full category name.
-- * The `desc` property is of the same form as the `cat` property and specifies the annotation to display in the title
-- bar (which may have the same format as the category minus the part of speech, or may be abbreviated). The value
-- may not be a list of strings, as only one annotation is displayed. If omitted, it is derived from the category
-- spec(s) by taking the last category (if more than one is given) and removing ' POS' before keyword substitution.
local declprops = {}
decls["hard-m"] = function(base, stems)
local gen_s = base.animacy == "in" and {"a", "u"} or "a"
local nom_p = base.animacy == "pr" and "ojo" or "y"
local voc_s = not rmatch(base.lemma, ".*tr$") and "o"
add_decl(base, stems, gen_s, "ej", acc_s, voc_s, "u", "om",
"aj", "ow", "omaj",
nom_p, "ow", "am", nil, "ach", "ami")
add_decl(base, com.convert_paired_plain_to_palatal(com.apply_palatalization(stems.oblique_vowel_stem)), nil, nil, nil, "e", "e")
end
declprops["hard-m"] = {
desc = function(base, stems)
return "masculine hard stem"
end,
cat = function(base, stems)
return "masculine hard stem"
end
}
decls["soft-m"] = function(base, stems)
local gen_s = base.animacy == "in" and {"a", "u"} or "a"
local nom_p = base.animacy == "pr" and "ojo" or "e"
add_decl(base, com.addj(stems.oblique_vowel_stem), gen_s, "ej", acc_s, "o", "u", "om",
"ej", "ow", "omaj",
nom_p, "ow", "am", nil, "ach", "emi")
end
declprops["soft-m"] = {
desc = function(base, stems)
return "masculine soft stem"
end,
cat = function(base, stems)
return "masculine soft stem"
end
}
decls["czs-m"] = function(base, stems)
local gen_s = base.animacy == "in" and {"a", "u"} or "a"
local nom_p = base.animacy == "pr" and "ojo" or "y"
add_decl(base, stems, gen_s, "ej", acc_s, "o", "u", "om",
"aj", "ow", "omaj",
nom_p, "ow", "am", nil, "ach", "ami")
end
declprops["czs-m"] = {
desc = function(base, stems)
return "masculine hard hissing stem"
end,
cat = function(base, stems)
return "masculine hard stem"
end
}
decls["velar-m"] = function(base, stems)
local gen_s = base.animacy == "in" and {"a", "u"} or "a"
local nom_p = base.animacy == "pr" and "ojo" or "i"
add_decl(base, stems, gen_s, "ej", acc_s, "o", "u", "om",
"aj", "ow", "omaj",
nom_p, "ow", "am", nil, "ach", "ami")
add_decl(base, com.convert_paired_plain_to_palatal(com.apply_palatalization(stems.oblique_vowel_stem)), nil, nil, nil, nil, "e")
end
declprops["velar-m"] = {
desc = function(base, stems)
return "masculine velar stem"
end,
cat = function(base, stems)
return "masculine velar stem"
end
}
decls["adje-m"] = function(base, stems)
if rmatch(base.lemma, "^.*" .. com.velar_c .. "i$") then
add_decl(base, stems, "eho", "emu", nil, "-", "im", "im",
nom_d, "eju", "imaj",
nom_p, "ich", "im", nil, "ich", "imi")
if base.animacy == "pr" then
add_decl(base, com.apply_palatalization(stems.oblique_vowel_stem), nil, nil, nil, nil, nil, nil, nil, nil, nil, "y")
add_decl(base, stems, nil, nil, nil, nil, nil, nil, "aj")
else
add_decl(base, stems, nil, nil, nil, nil, nil, nil, "ej", nil, nil, "e")
end
elseif rmatch(base.lemma, "^.*" .. com.inherently_soft_c .. "i$") then
local nom_p = base.animacy == "pr" and "i" or "e"
add_decl(base, stems, "eho", "emu", nil, "-", "im", "im",
"ej", "eju", "imaj",
nom_p, "ich", "im", nil, "ich", "imi")
elseif rmatch(base.lemma, "^.*[czs]e$") then
local nom_p = base.animacy == "pr" and "y" or "e"
local nom_d = base.animacy == "pr" and "aj" or "ej"
add_decl(base, stems, "eho", "emu", nil, "-", "ym", "ym",
nom_d, "eju", "ymaj",
nom_p, "ych", "ym", nil, "ych", "ymi")
else
add_decl(base, stems, "eho", "emu", nil, "-", "ym", "ym",
nom_d, "eju", "ymaj",
nom_p, "ych", "ym", nil, "ych", "ymi")
if base.animacy == "pr" then
add_decl(base, com.apply_palatalization(stems.oblique_vowel_stem), nil, nil, nil, nil, nil, nil, nil, nil, nil, "i")
add_decl(base, stems, nil, nil, nil, nil, nil, nil, "aj")
else
add_decl(base, stems, nil, nil, nil, nil, nil, nil, "ej", nil, nil, "e")
end
end
end
declprops["adje-m"] = {
desc = function(base, stems)
return ""
end,
cat = function(base, stems)
return "masculine adjectival"
end
}
decls["hard-f"] = function(base, stems)
add_decl(base, stems, "y", nil, "u", "-", nil, "u",
nil, "ow", "omaj",
"y", "ow", "am", "y", "ach", "ami")
add_decl(base, com.convert_paired_plain_to_palatal(com.apply_palatalization(stems.oblique_vowel_stem)), nil, "e", nil, nil, "e", nil, "e")
end
declprops["hard-f"] = {
desc = function(base, stems)
return "feminine hard stem"
end,
cat = function(base, stems)
return "feminine hard stem"
end
}
decls["soft-f"] = function(base, stems)
if rmatch(base.lemma, "^(.*" .. com.cons_c .. ")$") then
add_decl(base, com.addj(stems.oblique_vowel_stem), "e", nil, "-", "-", nil, "u",
nil, "ow", "omaj",
"e", "ow", "am", "e", "ach", "emi")
else
add_decl(base, stems, "e", nil, "u", "-", nil, "u",
nil, "ow", "omaj",
"e", "ow", "am", "e", "ach", "emi")
end
add_decl(base, com.removej(com.addj(stems.oblique_vowel_stem)), nil, "i", nil, nil, "i", nil, "i", nil, nil, nil, "i")
end
declprops["soft-f"] = {
desc = function(base, stems)
return "feminine soft stem"
end,
cat = function(base, stems)
return "feminine soft stem"
end
}
decls["czs-f"] = function(base, stems)
if rmatch(base.lemma, "^(.*" .. com.cons_c .. ")$") then
add_decl(base, stems, "y", "y", "-", "-", "y", "u",
"y", "ow", "omaj",
"y", "ow", "am", "y", "ach", "ami")
else
add_decl(base, stems, "y", "y", "u", "-", "y", "u",
"y", "ow", "omaj",
"y", "ow", "am", "y", "ach", "ami")
end
end
declprops["czs-f"] = {
desc = function(base, stems)
return "feminine hard hissing stem"
end,
cat = function(base, stems)
return "feminine hard stem"
end
}
decls["velar-f"] = function(base, stems)
add_decl(base, stems, "i", nil, "u", "-", nil, "u",
nil, "ow", "omaj",
"i", "ow", "am", "i", "ach", "ami")
add_decl(base, com.convert_paired_plain_to_palatal(com.apply_palatalization(stems.oblique_vowel_stem)), nil, "e", nil, nil, "e", nil, "e")
end
declprops["velar-f"] = {
desc = function(base, stems)
return "feminine velar stem"
end,
cat = function(base, stems)
return "feminine velar stem"
end
}
decls["v-f"] = function(base, stems)
add_decl(base, stems, "wje", "wi", "-", "-", "wi", "wju",
"wi", "wjow", "wjomaj",
"wje", "wjow", "wjam", "wje", "wjach", "wjemi")
end
declprops["v-f"] = {
desc = function(base, stems)
return "feminine v-stem"
end,
cat = function(base, stems)
return "feminine v-stem"
end
}
decls["adje-f"] = function(base, stems)
if rmatch(base.lemma, "^.*" .. com.velar_c .. "a$") or rmatch(base.lemma, "^.*" .. com.inherently_soft_c .. "a$") then
add_decl(base, stems, "eje", "ej", "u", "-", "ej", "ej",
"ej", "eju", "imaj",
"e", "ich", "im", "e", "ich", "imi")
else
add_decl(base, stems, "eje", "ej", "u", "-", "ej", "ej",
"ej", "eju", "ymaj",
"e", "ych", "ym", "e", "ych", "ymi")
end
end
declprops["adje-f"] = {
desc = function(base, stems)
return ""
end,
cat = function(base, stems)
return "feminine adjectival"
end
}
decls["hard-n"] = function(base, stems)
add_decl(base, stems, "a", "u", "-", "-", nil, "om",
nil, "ow", "omaj",
"a", "ow", "am", "a", "ach", "ami")
add_decl(base, com.convert_paired_plain_to_palatal(com.apply_palatalization(stems.oblique_vowel_stem)), nil, nil, nil, nil, "e", nil, "e")
end
declprops["hard-n"] = {
desc = function(base, stems)
return "neuter hard stem"
end,
cat = function(base, stems)
return "neuter hard stem"
end
}
decls["soft-n"] = function(base, stems)
add_decl(base, stems, "a", "u", "-", "-", "u", "om",
nil, "ow", "omaj",
"a", "ow", "am", "a", "ach", "emi")
add_decl(base, com.removej(stems.oblique_vowel_stem), nil, nil, nil, nil, nil, nil, "i")
end
declprops["soft-n"] = {
desc = function(base, stems)
return "neuter soft stem"
end,
cat = function(base, stems)
return "neuter soft stem"
end
}
decls["czs-n"] = function(base, stems)
add_decl(base, stems, "a", "u", "-", "-", "u", "om",
"y", "ow", "omaj",
"a", "ow", "am", "a", "ach", "ami")
end
declprops["czs-n"] = {
desc = function(base, stems)
return "neuter hard hissing stem"
end,
cat = function(base, stems)
return "neuter hard stem"
end
}
decls["velar-n"] = function(base, stems)
add_decl(base, stems, "a", "u", "-", "-", "u", "om",
nil, "ow", "omaj",
"a", "ow", "am", "a", "ach", "ami")
add_decl(base, com.convert_paired_plain_to_palatal(com.apply_palatalization(stems.oblique_vowel_stem)), nil, nil, nil, nil, "e", nil, "e")
end
declprops["velar-n"] = {
desc = function(base, stems)
return "neuter velar stem"
end,
cat = function(base, stems)
return "neuter velar stem"
end
}
decls["adje-n"] = function(base, stems)
if rmatch(base.lemma, "^.*" .. com.velar_c .. "e$") or rmatch(base.lemma, "^.*" .. com.inherently_soft_c .. "e$") then
add_decl(base, stems, "eho", "emu", "-", "-", "im", "im",
"ej", "eju", "imaj",
"e", "ich", "im", "e", "ich", "imi")
else
add_decl(base, stems, "eje", "ej", "u", "-", "ym", "ym",
"ej", "eju", "ymaj",
"e", "ych", "ym", "e", "ych", "ymi")
end
end
declprops["adje-n"] = {
desc = function(base, stems)
return ""
end,
cat = function(base, stems)
return "neuter adjectival"
end
}
decls["tstem-n"] = function(base, stems)
add_decl(base, stems, "eća", "eću", "-", "-", "eću", "ećom",
"eći", "ećow", "ećomaj",
"ata", "atow", "atam", "ata", "atach", "atami")
end
declprops["tstem-n"] = {
desc = function(base, stems)
return "neuter t-stem"
end,
cat = function(base, stems)
return "neuter t-stem"
end
}
decls["nstem-n"] = function(base, stems)
add_decl(base, stems, "enja", "enju", "-", "-", "enju", "enjom",
"eni", "enjow", "enjomaj",
"enja", "enjow", "enjam", "enja", "enjach", "enjami")
end
declprops["nstem-n"] = {
desc = function(base, stems)
return "neuter n-stem"
end,
cat = function(base, stems)
return "neuter n-stem"
end
}
decls["adj"] = function(base, stems)
local props = {}
local propspec = table.concat(props, ".")
if propspec ~= "" then
propspec = "<" .. propspec .. ">"
end
local adj_alternant_multiword_spec = require("Module:zlw-ocs-adjective").do_generate_forms({base.lemma .. propspec})
local function copy(from_slot, to_slot)
base.forms[to_slot] = adj_alternant_multiword_spec.forms[from_slot]
end
if base.number ~= "pl" then
if base.gender == "m" then
copy("nom_m", "nom_s")
copy("gen_mn", "gen_s")
copy("dat_mn", "dat_s")
copy("loc_mn", "loc_s")
copy("ins_mn", "ins_s")
elseif base.gender == "f" then
copy("nom_f", "nom_s")
copy("gen_f", "gen_s")
copy("dat_f", "dat_s")
copy("acc_f", "acc_s")
copy("loc_f", "loc_s")
copy("ins_f", "ins_s")
else
copy("nom_n", "nom_s")
copy("gen_mn", "gen_s")
copy("dat_mn", "dat_s")
copy("acc_n", "acc_s")
copy("loc_mn", "loc_s")
copy("ins_mn", "ins_s")
end
if not base.forms.voc_s then
iut.insert_forms(base.forms, "voc_s", base.forms.nom_s)
end
end
if base.number ~= "sg" then
if base.gender == "m" then
copy("nom_mp", "nom_p")
copy("acc_mfp", "acc_p")
copy("nom_md", "nom_d")
elseif base.gender == "f" then
copy("nom_fp", "nom_p")
copy("acc_mfp", "acc_p")
copy("nom_fnd", "nom_d")
else
copy("nom_np", "nom_p")
copy("acc_np", "acc_p")
copy("nom_fnd", "nom_d")
end
copy("gen_p", "gen_p")
copy("dat_p", "dat_p")
copy("ins_p", "ins_p")
copy("loc_p", "loc_p")
copy("gen_d", "gen_d")
copy("dat_d", "dat_d")
end
end
local function get_stemtype(base)
if rfind(base.lemma, "ý$") then
return "hard"
elseif rfind(base.lemma, "í$") then
return "soft"
else
return "possessive"
end
end
declprops["adj"] = {
cat = function(base, stems)
return {"adjectival POS", get_stemtype(base) .. " GENDER adjectival POS"}
end,
}
decls["indecl"] = function(base, stems)
-- Indeclinable. Note that fully indeclinable nouns should not have a table at all rather than one all of whose forms
-- are the same; but having an indeclinable declension is useful for nouns that may or may not be indeclinable, e.g.
-- [[desatero]] "group of ten" or the plural of [[peso]], which may be indeclinable 'pesos'.
add_decl(base, stems, "-", "-", "-", "-", "-", "-",
"-", "-", "-", "-", "-", "-", "-", "-", "-")
end
declprops["indecl"] = {
cat = function(base, stems)
if base.adj then
return {"adjectival POS", "indeclinable adjectival POS", "indeclinable GENDER adjectival POS"}
else
return {"indeclinable POS", "indeclinable GENPOS"}
end
end
}
decls["manual"] = function(base, stems)
-- Anything declined manually using overrides. We don't set any declensions except the nom_s (or nom_p if plurale
-- tantum).
add(base, base.number == "pl" and "nom_p" or "nom_s", stems, "-")
end
declprops["manual"] = {
desc = "GENDER",
cat = {},
}
local function set_pron_defaults(base)
if base.gender or base.lemma ~= "ona" and base.number or base.animacy then
error("Can't specify gender, number or animacy for pronouns")
end
local function pron_props()
-- Return values are GENDER, NUMBER, ANIMACY, HAS_CLITIC.
if base.lemma == "štó" then
return "none", "sg", "pr", false
elseif base.lemma == "što" then
return "none", "sg", "inan", false
else
error(("Unrecognized pronoun '%s'"):format(base.lemma))
end
end
local gender, number, animacy, has_clitic = pron_props()
base.gender = gender
base.actual_gender = gender
base.number = number
base.actual_number = number
base.animacy = animacy
base.actual_animacy = animacy
base.has_clitic = has_clitic
end
local function determine_pronoun_stems(base)
if base.stem_sets then
error("Reducible and vowel alternation specs cannot be given with pronouns")
end
base.stem_sets = {{reducible = false, vowel_stem = "", nonvowel_stem = ""}}
base.decl = "pron"
end
decls["pron"] = function(base, stems)
if base.lemma == "štó" then
add_decl(base, stems, "koho", "komu", nil, nil, "kim", "kim")
elseif base.lemma == "što" then
add_decl(base, stems, "čeho", "čemu", nil, nil, "čim", "čim")
else
error(("Internal error: Unrecognized pronoun lemma '%s'"):format(base.lemma))
end
end
declprops["pron"] = {
desc = "GENDER pronoun",
cat = {},
}
local function set_num_defaults(base)
if base.gender or base.animacy then
error("Can't specify gender, number or animacy for numeral")
end
local function num_props()
-- Return values are GENDER, NUMBER, ANIMACY, HAS_CLITIC.
return "none", "pl", "none", false
end
local gender, number, animacy, has_clitic = num_props()
base.gender = gender
base.actual_gender = gender
base.number = number
base.actual_number = number
base.animacy = animacy
base.actual_animacy = animacy
base.has_clitic = has_clitic
end
local function set_det_defaults(base)
if base.gender or base.number or base.animacy then
error("Can't specify gender, number or animacy for determiner")
end
local function det_props()
-- Return values are GENDER, NUMBER, ANIMACY, HAS_CLITIC.
return "none", "none", "none", false
end
local gender, number, animacy, has_clitic = det_props()
base.gender = gender
base.actual_gender = gender
base.number = number
base.actual_number = number
base.animacy = animacy
base.actual_animacy = animacy
base.has_clitic = has_clitic
end
local function determine_determiner_stems(base)
if base.stem_sets then
error("Reducible and vowel alternation specs cannot be given with determiners")
end
local stem = rmatch(base.lemma, "^(.*)" .. com.vowel_c .. "$") or base.lemma
base.stem_sets = {{reducible = false, vowel_stem = stem, nonvowel_stem = stem}}
base.decl = "det"
end
decls["det"] = function(base, stems)
add_sg_decl(base, stems, "a", "a", "-", nil, "a", "a")
end
declprops["det"] = {
desc = "GENDER determiner",
cat = {},
}
local function fetch_footnotes(separated_group)
local footnotes
for j = 2, #separated_group - 1, 2 do
if separated_group[j + 1] ~= "" then
error("Extraneous text after bracketed footnotes: '" .. table.concat(separated_group) .. "'")
end
if not footnotes then
footnotes = {}
end
table.insert(footnotes, separated_group[j])
end
return footnotes
end
local function parse_override(segments)
local retval = {values = {}}
local part = segments[1]
local slots = {}
while true do
local case = usub(part, 1, 3)
if cases[case] then
-- ok
else
error(("Unrecognized case '%s' in override: '%s'"):format(case, table.concat(segments)))
end
part = usub(part, 4)
local slot
if rfind(part, "^pl") then
part = usub(part, 3)
slot = case .. "_p"
elseif rfind(part, "^du") then
part = usub(part, 3)
slot = case .. "_d"
else
slot = case .. "_s"
end
table.insert(slots, slot)
if rfind(part, "^%+") then
part = usub(part, 2)
else
break
end
end
if rfind(part, "^:") then
retval.full = true
part = usub(part, 2)
end
segments[1] = part
local colon_separated_groups = put.split_alternating_runs_and_strip_spaces(segments, ":")
for i, colon_separated_group in ipairs(colon_separated_groups) do
local value = {}
local form = colon_separated_group[1]
if form == "" then
error(("Use - to indicate an empty ending for slot%s '%s': '%s'"):format(#slots > 1 and "s" or "", table.concat(slots), table.concat(segments)))
elseif form == "-" then
value.form = ""
else
value.form = form
end
value.footnotes = fetch_footnotes(colon_separated_group)
table.insert(retval.values, value)
end
return slots, retval
end
--[=[
Parse an indicator spec (text consisting of angle brackets and zero or more
dot-separated indicators within them). Return value is an object of the form
{
overrides = {
SLOT = {OVERRIDE, OVERRIDE, ...}, -- as returned by parse_override()
...
},
forms = {}, -- forms for a single spec alternant; see `forms` below
footnotes = {"FOOTNOTE", "FOOTNOTE", ...}, -- may be missing
stems = { -- may be missing
{
reducible = TRUE_OR_FALSE,
footnotes = {"FOOTNOTE", "FOOTNOTE", ...}, -- may be missing
-- The following fields are filled in by determine_stems()
vowel_stem = "STEM",
nonvowel_stem = "STEM",
oblique_slots = "all",
oblique_vowel_stem = "STEM" or nil (only needs to be set if oblique_slots is non-nil),
oblique_nonvowel_stem = "STEM" or nil (only needs to be set if oblique_slots is non-nil),
},
...
},
gender = "GENDER", -- "m", "f", "n"
number = "NUMBER", -- "sg", "pl"; may be missing
animacy = "ANIMACY", -- "inan", "an"; may be missing
hard = true, -- may be missing
soft = true, -- may be missing
mixed = true, -- may be missing
surname = true, -- may be missing
istem = true, -- may be missing
["-istem"] = true, -- may be missing
tstem = true, -- may be missing
nstem = true, -- may be missing
tech = true, -- may be missing
foreign = true, -- may be missing
mostlyindecl = true, -- may be missing
indecl = true, -- may be missing
manual = true, -- may be missing
adj = true, -- may be missing
decllemma = "DECLENSION-LEMMA", -- may be missing
declgender = "DECLENSION-GENDER", -- may be missing
declnumber = "DECLENSION-NUMBER", -- may be missing
-- The following additional fields are added by other functions:
orig_lemma = "ORIGINAL-LEMMA", -- as given by the user
orig_lemma_no_links = "ORIGINAL-LEMMA-NO-LINKS", -- links removed
lemma = "LEMMA", -- `orig_lemma_no_links`, converted to singular form if plural and lowercase if all-uppercase
forms = {
SLOT = {
{
form = "FORM",
footnotes = {"FOOTNOTE", "FOOTNOTE", ...} -- may be missing
},
...
},
...
},
decl = "DECL", -- declension, e.g. "hard-m"
vowel_stem = "VOWEL-STEM", -- derived from vowel-ending lemmas
nonvowel_stem = "NONVOWEL-STEM", -- derived from non-vowel-ending lemmas
}
]=]
local function parse_indicator_spec(angle_bracket_spec)
local inside = rmatch(angle_bracket_spec, "^<(.*)>$")
assert(inside)
local base = {overrides = {}, forms = {}}
if inside ~= "" then
local segments = put.parse_balanced_segment_run(inside, "[", "]")
local dot_separated_groups = put.split_alternating_runs_and_strip_spaces(segments, "%.")
for i, dot_separated_group in ipairs(dot_separated_groups) do
local part = dot_separated_group[1]
local case_prefix = usub(part, 1, 3)
if cases[case_prefix] then
local slots, override = parse_override(dot_separated_group)
for _, slot in ipairs(slots) do
if base.overrides[slot] then
error(("Two overrides specified for slot '%s'"):format(slot))
else
base.overrides[slot] = {override}
end
end
elseif part == "" then
if #dot_separated_group == 1 then
error("Blank indicator: '" .. inside .. "'")
end
base.footnotes = fetch_footnotes(dot_separated_group)
elseif rfind(part, "^[-*#ě]*$") or rfind(part, "^[-*#ě]*,") then
if base.stem_sets then
error("Can't specify reducible/vowel-alternant indicator twice: '" .. inside .. "'")
end
local comma_separated_groups = put.split_alternating_runs_and_strip_spaces(dot_separated_group, ",")
local stem_sets = {}
for i, comma_separated_group in ipairs(comma_separated_groups) do
local pattern = comma_separated_group[1]
local orig_pattern = pattern
local reducible, vowelalt, oblique_slots
if pattern == "-" then
-- default reducible, no vowel alt
else
local before, after
before, reducible, after = rmatch(pattern, "^(.-)(%-?%*)(.-)$")
if before then
pattern = before .. after
reducible = reducible == "*"
end
if pattern ~= "" then
if not rfind(pattern, "^##?ě?$") then
error("Unrecognized vowel-alternation pattern '" .. pattern .. "', should be one of #, ##, #ě or ##ě: '" .. inside .. "'")
end
if pattern == "#ě" or pattern == "##ě" then
vowelalt = "quant-ě"
else
vowelalt = "quant"
end
-- `oblique_slots` will be later changed to "all" if the lemma ends in a consonant.
oblique_slots = "all"
end
end
table.insert(stem_sets, {
reducible = reducible,
vowelalt = vowelalt,
oblique_slots = oblique_slots,
footnotes = fetch_footnotes(comma_separated_group)
})
end
base.stem_sets = stem_sets
elseif #dot_separated_group > 1 then
error("Footnotes only allowed with slot overrides, reducible or vowel alternation specs or by themselves: '" .. table.concat(dot_separated_group) .. "'")
elseif part == "m" or part == "f" or part == "n" then
if base.gender then
error("Can't specify gender twice: '" .. inside .. "'")
end
base.gender = part
elseif part == "sg" or part == "du" or part == "pl" then
if base.number then
error("Can't specify number twice: '" .. inside .. "'")
end
base.number = part
elseif part == "pr" or part == "anml" or part == "inan" then
if base.animacy then
error("Can't specify animacy twice: '" .. inside .. "'")
end
base.animacy = part
elseif part == "hard" or part == "soft" or part == "istem" or part == "tstem" or part == "nstem" or
part == "indecl" or part == "pron" or part == "det" or part == "velar" or part == "vstem" or part == "adje" then
if base[part] then
error("Can't specify '" .. part .. "' twice: '" .. inside .. "'")
end
base[part] = true
elseif part == "+" then
if base.adj then
error("Can't specify '+' twice: '" .. inside .. "'")
end
base.adj = true
elseif part == "!" then
if base.manual then
error("Can't specify '!' twice: '" .. inside .. "'")
end
base.manual = true
elseif rfind(part, "^mixedistem:") then
if base.mixedistem then
error("Can't specify 'mixedistem:' twice: '" .. inside .. "'")
end
base.mixedistem = rsub(part, "^mixedistem:", "")
elseif rfind(part, "^decllemma:") then
if base.decllemma then
error("Can't specify 'decllemma:' twice: '" .. inside .. "'")
end
base.decllemma = rsub(part, "^decllemma:", "")
elseif rfind(part, "^declgender:") then
if base.declgender then
error("Can't specify 'declgender:' twice: '" .. inside .. "'")
end
base.declgender = rsub(part, "^declgender:", "")
elseif rfind(part, "^declnumber:") then
if base.declnumber then
error("Can't specify 'declnumber:' twice: '" .. inside .. "'")
end
base.declnumber = rsub(part, "^declnumber:", "")
else
error("Unrecognized indicator '" .. part .. "': '" .. inside .. "'")
end
end
end
return base
end
local function is_regular_noun(base)
return not base.adj and not base.pron and not base.det and not base.num
end
local function process_declnumber(base)
base.actual_number = base.number
if base.declnumber then
if base.declnumber == "sg" or base.declnumber == "du" or base.declnumber == "pl" then
base.number = base.declnumber
else
error(("Unrecognized value '%s' for 'declnumber', should be 'sg' or 'pl'"):format(base.declnumber))
end
end
end
local function set_defaults_and_check_bad_indicators(base)
-- Set default values.
local regular_noun = is_regular_noun(base)
if base.pron then
set_pron_defaults(base)
elseif base.det then
set_det_defaults(base)
elseif base.num then
set_num_defaults(base)
elseif not base.adj then
if not base.gender then
if base.manual then
base.gender = "none"
else
error("For nouns, gender must be specified")
end
end
base.number = base.number or "allthree"
process_declnumber(base)
base.animacy = base.animacy or "inan"
base.actual_gender = base.gender
base.actual_animacy = base.animacy
if base.declgender then
if base.declgender == "m-an" then
base.gender = "m"
base.animacy = "pr"
elseif base.declgender == "m-in" then
base.gender = "m"
base.animacy = "inan"
elseif base.declgender == "f" or base.declgender == "n" then
base.gender = base.declgender
else
error(("Unrecognized value '%s' for 'declgender', should be 'm-an', 'm-in', 'f' or 'n'"):format(base.declgender))
end
end
end
-- Check for bad indicator combinations.
if (base.hard and 1 or 0) + (base.soft and 1 or 0) > 1 then
error("At most one of 'hard' or 'soft' can be specified")
end
if base.istem and base["-istem"] then
error("'istem' and '-istem' cannot be specified together")
end
if (base.istem or base["-istem"]) then
if not regular_noun then
error("'istem' and '-istem' can only be specified with regular nouns")
end
end
if base.declgender and not regular_noun then
error("'declgender' can only be specified with regular nouns")
end
end
local function set_all_defaults_and_check_bad_indicators(alternant_multiword_spec)
local is_multiword = #alternant_multiword_spec.alternant_or_word_specs > 1
iut.map_word_specs(alternant_multiword_spec, function(base)
set_defaults_and_check_bad_indicators(base)
base.multiword = is_multiword -- FIXME: not currently used; consider deleting
alternant_multiword_spec.has_clitic = alternant_multiword_spec.has_clitic or base.has_clitic
if base.pron then
alternant_multiword_spec.saw_pron = true
else
alternant_multiword_spec.saw_non_pron = true
end
if base.det then
alternant_multiword_spec.saw_det = true
else
alternant_multiword_spec.saw_non_det = true
end
if base.num then
alternant_multiword_spec.saw_num = true
else
alternant_multiword_spec.saw_non_num = true
end
end)
end
local function undo_second_palatalization(base, word, is_adjective)
local function try(from, to)
local stem = rmatch(word, "^(.*)" .. from .. "$")
if stem then
return stem .. to
end
return nil
end
return is_adjective and try("št", "sk") or
is_adjective and try("čt", "ck") or
try("c", "k") or -- FIXME, this could be wrong and c correct
try("ř", "r") or
try("z", "h") or -- FIXME, this could be wrong and z or g correct
try("š", "ch") or
word
end
-- For a plural-only lemma, synthesize a likely singular lemma. It doesn't have to be
-- theoretically correct as long as it generates all the correct plural forms.
local function synthesize_singular_lemma(base)
if not base.stem_sets then
base.stem_sets = {{}}
end
local lemma_determined
-- Loop over all stem sets in case the user specified multiple ones (e.g. '*,-*'). If we try to reconstruct
-- different lemmas for different stem sets, we'll throw an error below.
for _, stems in ipairs(base.stem_sets) do
local stem, lemma
while true do
if base.indecl then
-- If specified as indeclinable, leave it alone; e.g. 'pesos' indeclinable plural of [[peso]].
lemma = base.lemma
break
elseif base.gender == "m" then
stem = rmatch(base.lemma, "^(.*)ojo$")
if stem then
lemma = undo_second_palatalization(base, stem)
else
stem = rmatch(base.lemma, "^(.*)[oyie]$")
if stem then
lemma = stem
else
error(("Masculine plural-only lemma '%s' should end in -ojo, -o, -y, -i or -e"):format(base.lemma))
end
end
if stems.reducible == nil then
if rfind(lemma, com.cons_c .. "[ck]$") and not com.is_monosyllabic(base.lemma) then
stems.reducible = true
end
if stems.reducible then
lemma = dereduce(base, lemma)
end
end
break
elseif base.gender == "f" then
stem = rmatch(base.lemma, "^(.*)y$")
if stem then
lemma = stem .. "a"
break
end
stem = rmatch(base.lemma, "^(.*)[eě]$")
if stem then
-- Singular like the plural. Cons-stem feminines like [[dlaň]] "palm (of the hand)" have identical
-- plurals to soft-stem feminines like [[růže]] (modulo e/ě differences), so we don't need to
-- reconstruct the former type.
lemma = base.lemma
break
end
stem = rmatch(base.lemma, "^(.*)i$")
if stem then
-- i-stems.
lemma = stem
base.istem = true
break
end
error(("Feminine plural-only lemma '%s' should end in -y, -ě, -e or -i"):format(base.lemma))
elseif base.gender == "n" then
-- -ata nouns like [[slůně]] "baby elephant" nom pl 'slůňata' are declined in the plural same as if
-- the singular were 'slůňato' so we don't have to worry about them.
stem = rmatch(base.lemma, "^(.*)a$")
if stem then
lemma = stem .. "o"
break
end
stem = rmatch(base.lemma, "^(.*)[eěí]$")
if stem then
-- singular lemma also in -e, -ě or -í; e.g. [[věčná loviště]] "[[happy hunting ground]]"
lemma = base.lemma
break
end
error(("Neuter plural-only lemma '%s' should end in -a, -í, -ě or -e"):format(base.lemma))
else
error(("Internal error: Unrecognized gender '%s'"):format(base.gender))
end
end
if lemma_determined and lemma_determined ~= lemma then
error(("Attempt to set two different singular lemmas '%s' and '%s'"):format(lemma_determined, lemma))
end
lemma_determined = lemma
end
base.lemma = lemma_determined
end
-- For an adjectival lemma, synthesize the masc singular form.
local function synthesize_adj_lemma(base)
local stem
if base.indecl then
base.decl = "indecl"
stem = base.lemma
else
local gender, number
local function sub_ov(stem)
stem = stem:gsub("ov$", "ův")
return stem
end
while true do
if base.number == "pl" then
if base.gender == "m" then
stem = rmatch(base.lemma, "^(.*)í$")
if stem then
if base.soft then
-- nothing to do
else
if base.animacy ~= "pr" then
error(("Masculine plural-only adjectival lemma '%s' ending in -í can only be animate unless '.soft' is specified"):
format(base.lemma))
end
base.lemma = undo_second_palatalization(base, stem, "is adjective") .. "ý"
end
break
end
stem = rmatch(base.lemma, "^(.*)é$")
if stem then
if base.animacy == "pr" then
error(("Masculine plural-only adjectival lemma '%s' ending in -é must be inanimate"):
format(base.lemma))
end
base.lemma = stem .. "ý"
break
end
stem = rmatch(base.lemma, "^(.*ov)i$") or rmatch(base.lemma, "^(.*in)i$")
if stem then
if base.animacy ~= "pr" then
error(("Masculine plural-only possessive adjectival lemma '%s' ending in -i must be animate"):
format(base.lemma))
end
base.lemma = sub_ov(stem)
break
end
stem = rmatch(base.lemma, "^(.*ov)y$") or rmatch(base.lemma, "^(.*in)y$")
if stem then
if base.animacy == "pr" then
error(("Masculine plural-only possessive adjectival lemma '%s' ending in -y must be inanimate"):
format(base.lemma))
end
base.lemma = sub_ov(stem)
break
end
if base.animacy == "pr" then
error(("Animate masculine plural-only adjectival lemma '%s' should end in -í, -ovi or -ini"):
format(base.lemma))
elseif base.soft then
error(("Soft masculine plural-only adjectival lemma '%s' should end in -í"):format(base.lemma))
else
error(("Inanimate masculine plural-only adjectival lemma '%s' should end in -é, -ovy or -iny"):
format(base.lemma))
end
elseif base.gender == "f" then
stem = rmatch(base.lemma, "^(.*)é$") -- hard adjective
if stem then
base.lemma = stem .. "ý"
break
end
stem = rmatch(base.lemma, "^(.*)í$") -- soft adjective
if stem then
break
end
stem = rmatch(base.lemma, "^(.*ov)y$") or rmatch(base.lemma, "^(.*in)y$") -- possessive adjective
if stem then
base.lemma = sub_ov(stem)
break
end
error(("Feminine plural-only adjectival lemma '%s' should end in -é, -í, -ovy or -iny"):format(base.lemma))
else
stem = rmatch(base.lemma, "^(.*)á$") -- hard adjective
if stem then
base.lemma = stem .. "ý"
break
end
stem = rmatch(base.lemma, "^(.*)í$") -- soft adjective
if stem then
break
end
stem = rmatch(base.lemma, "^(.*ov)a$") or rmatch(base.lemma, "^(.*in)a$") -- possessive adjective
if stem then
base.lemma = sub_ov(stem)
break
end
error(("Neuter plural-only adjectival lemma '%s' should end in -á, -í, -ova or -ina"):format(base.lemma))
end
else
if base.gender == "m" then
stem = rmatch(base.lemma, "^(.*)[ýí]$") or rmatch(base.lemma, "^(.*)ův$") or rmatch(base.lemma, "^(.*)in$")
if stem then
break
end
error(("Masculine adjectival lemma '%s' should end in -ý, -í, -ův or -in"):format(base.lemma))
elseif base.gender == "f" then
stem = rmatch(base.lemma, "^(.*)á$")
if stem then
base.lemma = stem .. "ý"
break
end
stem = rmatch(base.lemma, "^(.*)í$")
if stem then
break
end
stem = rmatch(base.lemma, "^(.*ov)a$") or rmatch(base.lemma, "^(.*in)a$")
if stem then
base.lemma = sub_ov(stem)
break
end
error(("Feminine adjectival lemma '%s' should end in -á, -í, -ova or -ina"):format(base.lemma))
else
stem = rmatch(base.lemma, "^(.*)í$")
if stem then
break
end
stem = rmatch(base.lemma, "^(.*ov)o$") or rmatch(base.lemma, "^(.*in)o$")
if stem then
base.lemma = sub_ov(stem)
break
end
error(("Neuter adjectival lemma '%s' should end in -é, -í, -ovo or -ino"):format(base.lemma))
end
end
end
base.decl = "adj"
end
-- Now set the stem sets if not given.
-- Now set the stem sets if not given.
if not base.stem_sets then
base.stem_sets = {{reducible = false}}
end
for _, stems in ipairs(base.stem_sets) do
-- Set the stems.
stems.vowel_stem = stem
stems.nonvowel_stem = stem
end
end
-- Determine the declension based on the lemma, gender and number. The declension is set in base.decl. In the process,
-- we set either base.vowel_stem (if the lemma ends in a vowel) or base.nonvowel_stem (if the lemma does not end in a
-- vowel), which is used by determine_stems(). In some cases (specifically with certain foreign nouns), we set
-- base.lemma to a new value; this is as if the user specified 'decllemma:'.
local function determine_declension(base)
if base.indecl then
base.decl = "indecl"
base.nonvowel_stem = base.lemma
return
end
-- Determine declension
stem = rmatch(base.lemma, "^(.*)a$")
if stem then
if base.gender == "m" then
if base.animacy ~= "pr" then
error("Masculine lemma in -a must be animate")
end
base.decl = "a-m"
elseif base.gender == "f" then
if base.hard then
base.decl = "hard-f"
elseif base.soft then
base.decl = "soft-f"
elseif base.adje then
base.decl = "adje-f"
elseif rfind(base.lemma, com.velar_c .. "a$") then
base.decl = "velar-f"
elseif rfind(base.lemma, "[czs]" .. "a$") then
base.decl = "czs-f"
elseif rfind(base.lemma, com.inherently_soft_c .. "a$") then
base.decl = "soft-f"
else
base.decl = "hard-f"
end
elseif base.gender == "n" then
if rfind(stem, "m$") then
base.decl = "ma-n"
else
error("Lemma ending in -a and neuter must end in -ma")
end
end
base.vowel_stem = stem
return
end
local ending
stem, ending = rmatch(base.lemma, "^(.*)e$")
if stem then
if base.tstem then
base.decl = "tstem-n"
elseif base.adje then
base.decl = "adje-n"
else
base.decl = "soft-n"
end
base.vowel_stem = stem
return
end
stem = rmatch(base.lemma, "^(.*)o$")
if stem then
if base.gender == "m" then
-- Cf. [[maestro]] m.
base.decl = "o-m"
elseif base.gender == "f" then
-- [[zoo]]; [[Žemaitsko]]?
error("Feminine nouns in -o are indeclinable; use '.indecl' if needed")
elseif base.hard then
base.decl = "hard-n"
elseif base.tstem then
base.decl = "tstem-n"
elseif base.nstem then
base.decl = "nstem-n"
elseif rfind(base.lemma, "[czs]" .. "o$") then
base.decl = "czs-n"
elseif rfind(base.lemma, com.inherently_soft_c .. "o$") then
base.decl = "soft-n"
elseif rfind(base.lemma, com.velar_c .. "o$") then
base.decl = "velar-n"
else
base.decl = "hard-n"
end
base.vowel_stem = stem
return
end
stem = rmatch(base.lemma, "^(.*)[iy]$")
if stem then
if base.gender == "m" then
if base.adje then
base.decl = "adje-m"
end
end
base.vowel_stem = stem
return
end
stem = rmatch(base.lemma, "^(.*" .. com.cons_c .. ")$")
if stem then
if base.gender == "m" then
if base.hard then
base.decl = "hard-m"
elseif base.soft then
base.decl = "soft-m"
elseif rfind(base.lemma, com.velar_c .. "$") then
base.decl = "velar-m"
elseif rfind(base.lemma, "[czs]" .. "$") then
base.decl = "czs-m"
elseif rfind(base.lemma, com.inherently_soft_c .. "$") then
base.decl = "soft-m"
else
base.decl = "hard-m"
end
elseif base.gender == "f" then
if base.vstem then
base.decl = "v-f"
stem = rmatch(base.lemma, "^(.*)ej$")
elseif base.soft then
base.decl = "soft-f"
elseif rfind(base.lemma, "[czs]" .. "$") then
base.decl = "czs-f"
else
base.decl = "soft-f"
end
elseif base.gender == "n" then
if base.foreign then
stem = rmatch(base.lemma, "^(.*)um$") or rmatch(base.lemma, "^(.*)on$")
if not stem then
error("Unrecognized neuter foreign ending, should be -um or -on")
end
if base.hard then
base.decl = "hard-n"
elseif rfind(stem, "[eiuy]$") then
base.decl = "semisoft-n"
else
base.decl = "hard-n"
end
-- set the lemma here as if decllemma: were given
base.lemma = stem .. "o"
base.vowel_stem = stem
return
else
error("Neuter nouns ending in a consonant should use '.foreign' or '.decllemma:...'")
end
end
base.nonvowel_stem = stem
return
end
error("Unrecognized ending for lemma: '" .. base.lemma .. "'")
end
-- Determine the default value for the 'reducible' flag.
local function determine_default_reducible(base)
-- Nouns in vowels other than -a/o as well as masculine nouns ending in all vowels don't have null endings so not
-- reducible. Note, we are never called on adjectival nouns.
if rfind(base.lemma, "[iyuíeě]$") or base.gender == "m" and rfind(base.lemma, "[ao]$") or base.tstem then
base.default_reducible = false
return
end
local stem
stem = rmatch(base.lemma, "^(.*" .. com.cons_c .. ")$")
if stem then
if base.gender == "m" and rfind(stem, "e[ck]$") and not com.is_monosyllabic(stem) then
base.default_reducible = true
elseif base.gender == "f" and rfind(stem, "eń$") then
-- pěseń
base.default_reducible = true
else
base.default_reducible = false
end
return
end
base.default_reducible = false
end
-- Determine the stems to use for each stem set: vowel and nonvowel stems, for singular
-- and plural. We assume that one of base.vowel_stem or base.nonvowel_stem has been
-- set in determine_declension(), depending on whether the lemma ends in
-- a vowel. We construct all the rest given the reducibility, vowel alternation spec and
-- any explicit stems given. We store the determined stems inside of the stem-set objects
-- in `base.stem_sets`, meaning that if the user gave multiple reducible or vowel-alternation
-- patterns, we will compute multiple sets of stems. The reason is that the stems may vary
-- depending on the reducibility and vowel alternation.
local function determine_stems(base)
if not base.stem_sets then
base.stem_sets = {{}}
end
-- Set default reducible and check for default mixed reducible, which needs to be expanded into two entries.
local default_mixed_reducible = false
for _, stems in ipairs(base.stem_sets) do
if stems.reducible == nil then
stems.reducible = base.default_reducible
end
end
if default_mixed_reducible then
local new_stem_sets = {}
for _, stems in ipairs(base.stem_sets) do
table.insert(new_stem_sets, stems)
end
base.stem_sets = new_stem_sets
end
-- Now determine all the stems for each stem set.
for _, stems in ipairs(base.stem_sets) do
local lemma_is_vowel_stem = not not base.vowel_stem
if base.vowel_stem then
stems.vowel_stem = base.vowel_stem
stems.nonvowel_stem = stems.vowel_stem
-- Apply vowel alternation first in cases like jádro -> jader; apply_vowel_alternation() will throw an error
-- if the vowel being modified isn't the last vowel in the stem.
stems.oblique_nonvowel_stem = com.apply_vowel_alternation(stems.vowelalt, stems.nonvowel_stem)
if stems.reducible then
stems.nonvowel_stem = dereduce(base, stems.nonvowel_stem)
stems.oblique_nonvowel_stem = dereduce(base, stems.oblique_nonvowel_stem)
end
else
stems.nonvowel_stem = base.nonvowel_stem
-- The user specified #. E.g. nóc nocy
if stems.oblique_slots then
stems.oblique_slots = "all"
end
stems.oblique_nonvowel_stem = com.apply_vowel_alternation(stems.vowelalt, stems.nonvowel_stem)
if stems.reducible then
stems.vowel_stem = com.reduce(base.nonvowel_stem)
if not stems.vowel_stem then
error("Unable to reduce stem '" .. base.nonvowel_stem .. "'")
end
else
stems.vowel_stem = base.nonvowel_stem
end
end
stems.oblique_vowel_stem = com.apply_vowel_alternation(stems.vowelalt, stems.vowel_stem)
end
end
local function detect_indicator_spec(base)
if base.pron then
determine_pronoun_stems(base)
elseif base.det then
determine_determiner_stems(base)
elseif base.num then
determine_numeral_stems(base)
elseif base.adj then
process_declnumber(base)
synthesize_adj_lemma(base)
elseif base.manual then
if base.stem_sets then
-- FIXME, maybe this should be allowed?
error("Reducible and vowel alternation specs cannot be given with manual declensions")
end
base.stem_sets = {{reducible = false, vowel_stem = "", nonvowel_stem = ""}}
base.decl = "manual"
else
if base.number == "pl" then
synthesize_singular_lemma(base)
end
determine_declension(base)
determine_default_reducible(base)
determine_stems(base)
end
end
local function detect_all_indicator_specs(alternant_multiword_spec)
alternant_multiword_spec.sg_genders = {}
alternant_multiword_spec.pl_genders = {}
iut.map_word_specs(alternant_multiword_spec, function(base)
detect_indicator_spec(base)
if base.number ~= "pl" then
alternant_multiword_spec.sg_genders[base.actual_gender] = true
end
if base.number ~= "sg" then
-- All t-stem masculines are neuter in the plural.
local plgender
plgender = base.actual_gender
alternant_multiword_spec.pl_genders[plgender] = true
end
end)
if (alternant_multiword_spec.saw_pron and 1 or 0) + (alternant_multiword_spec.saw_det and 1 or 0) + (alternant_multiword_spec.saw_num and 1 or 0) > 1 then
error("Can't combine pronouns, determiners and/or numerals")
end
end
local propagate_multiword_properties
local function propagate_alternant_properties(alternant_spec, property, mixed_value, nouns_only)
local seen_property
for _, multiword_spec in ipairs(alternant_spec.alternants) do
propagate_multiword_properties(multiword_spec, property, mixed_value, nouns_only)
if seen_property == nil then
seen_property = multiword_spec[property]
elseif multiword_spec[property] and seen_property ~= multiword_spec[property] then
seen_property = mixed_value
end
end
alternant_spec[property] = seen_property
end
propagate_multiword_properties = function(multiword_spec, property, mixed_value, nouns_only)
local seen_property = nil
local last_seen_nounal_pos = 0
local word_specs = multiword_spec.alternant_or_word_specs or multiword_spec.word_specs
for i = 1, #word_specs do
local is_nounal
if word_specs[i].alternants then
propagate_alternant_properties(word_specs[i], property, mixed_value)
is_nounal = not not word_specs[i][property]
elseif nouns_only then
is_nounal = is_regular_noun(word_specs[i])
else
is_nounal = not not word_specs[i][property]
end
if is_nounal then
if not word_specs[i][property] then
error("Internal error: noun-type word spec without " .. property .. " set")
end
for j = last_seen_nounal_pos + 1, i - 1 do
word_specs[j][property] = word_specs[j][property] or word_specs[i][property]
end
last_seen_nounal_pos = i
if seen_property == nil then
seen_property = word_specs[i][property]
elseif seen_property ~= word_specs[i][property] then
seen_property = mixed_value
end
end
end
if last_seen_nounal_pos > 0 then
for i = last_seen_nounal_pos + 1, #word_specs do
word_specs[i][property] = word_specs[i][property] or word_specs[last_seen_nounal_pos][property]
end
end
multiword_spec[property] = seen_property
end
local function propagate_properties_downward(alternant_multiword_spec, property, default_propval)
local function set_and_fetch(obj, default)
local retval
if obj[property] then
retval = obj[property]
else
obj[property] = default
retval = default
end
if not obj["actual_" .. property] then
obj["actual_" .. property] = retval
end
return retval
end
local propval1 = set_and_fetch(alternant_multiword_spec, default_propval)
for _, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do
local propval2 = set_and_fetch(alternant_or_word_spec, propval1)
if alternant_or_word_spec.alternants then
for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do
local propval3 = set_and_fetch(multiword_spec, propval2)
for _, word_spec in ipairs(multiword_spec.word_specs) do
local propval4 = set_and_fetch(word_spec, propval3)
if propval4 == "mixed" then
-- FIXME, use clearer error message.
error("Attempt to assign mixed " .. property .. " to word")
end
set_and_fetch(word_spec, propval4)
end
end
else
if propval2 == "mixed" then
-- FIXME, use clearer error message.
error("Attempt to assign mixed " .. property .. " to word")
end
set_and_fetch(alternant_or_word_spec, propval2)
end
end
end
--[=[
Propagate `property` (one of "animacy", "gender" or "number") from nouns to adjacent
adjectives. We proceed as follows:
1. We assume the properties in question are already set on all nouns. This should happen in
set_defaults_and_check_bad_indicators().
2. We first propagate properties upwards and sideways. We recurse downwards from the top. When we encounter a multiword
spec, we proceed left to right looking for a noun. When we find a noun, we fetch its property (recursing if the noun
is an alternant), and propagate it to any adjectives to its left, up to the next noun to the left. When we have
processed the last noun, we also propagate its property value to any adjectives to the right (to handle e.g.
[[anděl strážný]] "guardian angel", where the adjective [[strážný]] should inherit the 'masculine' and 'animate'
properties of [[anděl]]). Finally, we set the property value for the multiword spec itself by combining all the
non-nil properties of the individual elements. If all non-nil properties have the same value, the result is that
value, otherwise it is `mixed_value` (which is "mixed" for animacy and gender, but "allthree" for number).
3. When we encounter an alternant spec in this process, we recursively process each alternant (which is a multiword
spec) using the previous step, and combine any non-nil properties we encounter the same way as for multiword specs.
4. The effect of steps 2 and 3 is to set the property of each alternant and multiword spec based on its children or its
neighbors.
]=]
local function propagate_properties(alternant_multiword_spec, property, default_propval, mixed_value)
propagate_multiword_properties(alternant_multiword_spec, property, mixed_value, "nouns only")
propagate_multiword_properties(alternant_multiword_spec, property, mixed_value, false)
propagate_properties_downward(alternant_multiword_spec, property, default_propval)
end
local function determine_noun_status(alternant_multiword_spec)
for i, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do
if alternant_or_word_spec.alternants then
local is_noun = false
for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do
for j, word_spec in ipairs(multiword_spec.word_specs) do
if is_regular_noun(word_spec) then
multiword_spec.first_noun = j
is_noun = true
break
end
end
end
if is_noun then
alternant_multiword_spec.first_noun = i
end
elseif is_regular_noun(alternant_or_word_spec) then
alternant_multiword_spec.first_noun = i
return
end
end
end
-- Set the part of speech based on properties of the individual words.
local function set_pos(alternant_multiword_spec)
if alternant_multiword_spec.args.pos then
alternant_multiword_spec.pos = alternant_multiword_spec.args.pos
elseif alternant_multiword_spec.saw_pron and not alternant_multiword_spec.saw_non_pron then
alternant_multiword_spec.pos = "သဗ္ဗနာမ်"
elseif alternant_multiword_spec.saw_det and not alternant_multiword_spec.saw_non_det then
alternant_multiword_spec.pos = "ဖျေံလဝ်သန္နိဋ္ဌာန်"
elseif alternant_multiword_spec.saw_num and not alternant_multiword_spec.saw_non_num then
alternant_multiword_spec.pos = "ဂၞန်သၚ်္ချာ"
else
alternant_multiword_spec.pos = "နာမ်"
end
alternant_multiword_spec.plpos = require(en_utilities_module).pluralize(alternant_multiword_spec.pos)
end
local function normalize_all_lemmas(alternant_multiword_spec, pagename)
iut.map_word_specs(alternant_multiword_spec, function(base)
if base.lemma == "" then
base.lemma = pagename
end
base.orig_lemma = base.lemma
base.orig_lemma_no_links = m_links.remove_links(base.lemma)
local lemma = base.orig_lemma_no_links
-- If the lemma is all-uppercase, lowercase it but note this, so that later in combine_stem_ending() we convert it
-- back to uppercase. This allows us to handle all-uppercase acronyms without a lot of extra complexity.
-- FIXME: This may not make sense at all.
if uupper(lemma) == lemma then
base.all_uppercase = true
lemma = ulower(lemma)
end
base.actual_lemma = lemma
base.lemma = base.decllemma or lemma
end)
end
local function decline_noun(base)
for _, stems in ipairs(base.stem_sets) do
if not decls[base.decl] then
error("Internal error: Unrecognized declension type '" .. base.decl .. "'")
end
decls[base.decl](base, stems)
end
handle_derived_slots_and_overrides(base)
local function copy(from_slot, to_slot)
base.forms[to_slot] = base.forms[from_slot]
end
if base.gender ~= "m" then
copy("nom_d", "acc_d")
end
copy("nom_d", "voc_d")
copy("dat_d", "loc_d")
copy("dat_d", "ins_d")
if base.actual_number ~= base.number then
local source_num = base.number == "sg" and "_s" or base.number == "du" and "_d" or "_p"
local dest_num = base.number == "sg" and {"_p", "_d"} or base.number == "du" and {"_s", "_p"} or {"_s", "_d"}
for case, _ in pairs(cases) do
copy(case .. source_num, case .. dest_num)
copy("nom" .. source_num .. "_linked", "nom" .. dest_num .. "_linked")
end
if base.actual_number ~= "allthree" then
local erase_num = base.actual_number == "sg" and {"_d", "_p"} or base.actual_number == "du" and {"_s", "_p"} or {"_s", "_d"}
for case, _ in pairs(cases) do
base.forms[case .. erase_num] = nil
end
base.forms["nom" .. erase_num .. "_linked"] = nil
end
end
end
local function get_variants(form)
return nil
--[=[
FIXME
return
form:find(com.VAR1) and "var1" or
form:find(com.VAR2) and "var2" or
form:find(com.VAR3) and "var3" or
nil
]=]
end
-- Compute the categories to add the noun to, as well as the annotation to display in the
-- declension title bar. We combine the code to do these functions as both categories and
-- title bar contain similar information.
local function compute_categories_and_annotation(alternant_multiword_spec)
local all_cats = {}
local function insert(cattype)
-- m_table.insertIfNot(all_cats, "Upper Sorbian " .. cattype)
end
if alternant_multiword_spec.pos == "နာမ်" then
if alternant_multiword_spec.actual_number == "sg" then
-- insert("uncountable nouns")
elseif alternant_multiword_spec.actual_number == "du" then
-- insert("dualia tantum")
elseif alternant_multiword_spec.actual_number == "pl" then
-- insert("pluralia tantum")
end
end
local annotation
local annparts = {}
local decldescs = {}
local vowelalts = {}
local foreign = {}
local irregs = {}
local stemspecs = {}
local reducible = nil
local function get_genanim(gender, animacy)
local gender_code_to_desc = {
m = "ပုလ္လိၚ်",
f = "ဣတ္တိလိၚ်",
n = "နပုလ္လိၚ်",
none = nil,
}
local animacy_code_to_desc = {
pr = "ပူဂဵု",
anml = "ဆေၚ်စပ်ကဵုသတ်",
inan = "မသက္ကုဟၟဲကဵုလမျီု",
none = nil,
}
local descs = {}
table.insert(descs, gender_code_to_desc[gender])
if gender ~= "f" and gender ~= "n" then
-- masculine or "none" (e.g. certain pronouns and numerals)
table.insert(descs, animacy_code_to_desc[animacy])
end
return table.concat(descs, " ")
end
local function trim(text)
text = text:gsub(" +", " ")
return mw.text.trim(text)
end
local function do_word_spec(base)
local actual_genanim = get_genanim(base.actual_gender, base.actual_animacy)
local declined_genanim = get_genanim(base.gender, base.animacy)
local genanim
genanim = actual_genanim
if base.actual_gender == "m" then
insert(actual_genanim .. " " .. alternant_multiword_spec.plpos)
end
for _, stems in ipairs(base.stem_sets) do
local props = declprops[base.decl]
local cats = props.cat
if type(cats) == "function" then
cats = cats(base, stems)
end
if type(cats) == "string" then
cats = {cats}
end
local default_desc
for i, cat in ipairs(cats) do
if not cat:find("GENDER") and not cat:find("GENPOS") and not cat:find("POS") then
cat = cat
end
cat = cat:gsub("GENPOS", "GENDER POS")
if not cat:find("POS") then
cat = cat .. " POS"
end
if i == #cats then
default_desc = cat:gsub(" POS", "")
end
cat = cat:gsub("GENDER", actual_genanim)
cat = cat:gsub("POS", alternant_multiword_spec.plpos)
-- Need to trim `cat` because actual_genanim may be an empty string.
insert(trim(cat))
end
local desc = props.desc
if type(desc) == "function" then
desc = desc(base, stems)
end
desc = desc or default_desc
desc = desc:gsub("GENDER", genanim)
-- Need to trim `desc` because genanim may be an empty string.
m_table.insertIfNot(decldescs, trim(desc))
local vowelalt
if stems.vowelalt == "quant" then
vowelalt = "quant-alt"
-- insert("nouns with quantitative vowel alternation")
elseif stems.vowelalt == "quant-ě" then
vowelalt = "í-ě-alt"
-- insert("nouns with í-ě alternation")
end
if vowelalt then
m_table.insertIfNot(vowelalts, vowelalt)
end
if reducible == nil then
reducible = stems.reducible
end
if stems.reducible then
-- insert("nouns with reducible stem")
end
if base.foreign then
m_table.insertIfNot(foreign, "foreign")
if not base.decllemma then
-- NOTE: there are nouns that use both 'foreign' and 'decllemma', e.g. [[Zeus]].
-- insert("nouns with regular foreign declension")
end
end
-- User-specified 'decllemma:' indicates irregular stem. Don't consider foreign nouns in -us/-os/-es, -um/-on or
-- silent -e (e.g. [[software]]) where this ending is simply dropped in oblique and plural forms as irregular;
-- there are too many of these and they are already categorized above as 'nouns with regular foreign declension'.
if base.decllemma then
m_table.insertIfNot(irregs, "irreg-stem")
-- insert("nouns with irregular stem")
end
m_table.insertIfNot(stemspecs, stems.vowel_stem)
end
end
local key_entry = alternant_multiword_spec.first_noun or 1
if #alternant_multiword_spec.alternant_or_word_specs >= key_entry then
local alternant_or_word_spec = alternant_multiword_spec.alternant_or_word_specs[key_entry]
if alternant_or_word_spec.alternants then
for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do
key_entry = multiword_spec.first_noun or 1
if #multiword_spec.word_specs >= key_entry then
do_word_spec(multiword_spec.word_specs[key_entry])
end
end
else
do_word_spec(alternant_or_word_spec)
end
end
if alternant_multiword_spec.actual_number == "sg" or alternant_multiword_spec.actual_number == "pl" or alternant_multiword_spec.actual_number == "du" then
-- not "allthree" or "none" (for [[sebe]])
table.insert(annparts, alternant_multiword_spec.actual_number == "sg" and "sg-only" or alternant_multiword_spec.actual_number == "du" and "du-only" or "pl-only")
end
if #decldescs == 0 then
table.insert(annparts, "indecl")
else
table.insert(annparts, table.concat(decldescs, " // "))
end
if #vowelalts > 0 then
table.insert(annparts, table.concat(vowelalts, "/"))
end
if reducible == "mixed" then
table.insert(annparts, "mixed-reducible")
elseif reducible then
table.insert(annparts, "reducible")
end
if #foreign > 0 then
table.insert(annparts, table.concat(foreign, " // "))
end
if #irregs > 0 then
table.insert(annparts, table.concat(irregs, " // "))
end
alternant_multiword_spec.annotation = table.concat(annparts, " ")
if #stemspecs > 1 then
insert("nouns with multiple stems")
end
if alternant_multiword_spec.actual_number == "allthree" and not m_table.deepEquals(alternant_multiword_spec.sg_genders, alternant_multiword_spec.pl_genders) then
insert("nouns that change gender in the plural")
end
alternant_multiword_spec.categories = all_cats
end
local function show_forms(alternant_multiword_spec)
local lemmas = {}
for _, slot in ipairs(potential_lemma_slots) do
if alternant_multiword_spec.forms[slot] then
for _, formobj in ipairs(alternant_multiword_spec.forms[slot]) do
-- FIXME, now can support footnotes as qualifiers in headwords?
table.insert(lemmas, formobj.form)
end
break
end
end
local props = {
lemmas = lemmas,
slot_table = alternant_multiword_spec.output_noun_slots,
lang = lang,
canonicalize = function(form)
-- return com.remove_variant_codes(form)
return form
end,
}
iut.show_forms(alternant_multiword_spec.forms, props)
end
local function make_table(alternant_multiword_spec)
local forms = alternant_multiword_spec.forms
local function template_prelude(min_width)
return rsub([=[
<div>
<div class="NavFrame" style="max-width: MINWIDTHem">
<div class="NavHead" style="background:var(--wikt-palette-lighterblue, #ebf4ff)">{title}{annotation}</div>
<div class="NavContent">
{\op}| class="inflection-table inflection" style="width: 100%; margin: 0; text-align:center;"
|-
]=], "MINWIDTH", min_width)
end
local function template_postlude()
return [=[
|{\cl}{notes_clause}</div></div></div>]=]
end
local table_spec_allthree = template_prelude("45") .. [=[
|- class="rowgroup"
! style="background:var(--wikt-palette-lightblue, #d9ebff)" |
! scope="col" style="background:var(--wikt-palette-lightblue, #d9ebff)" | ကိုန်ဨကဝုစ်
! scope="col" style="background:var(--wikt-palette-lightblue, #d9ebff)" | ၜါလ္ပာ်
! scope="col" style="background:var(--wikt-palette-lightblue, #d9ebff)" | ကိုန်ဗဟုဝစ်
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | မဒုၚ်ယၟု
| {nom_s}
| {nom_d}
| {nom_p}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ဗဳဇဂကူ
| {gen_s}
| {gen_d}
| {gen_p}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ပြကမ္မကာရက
| {dat_s}
| {dat_d}
| {dat_p}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ကမ္မကာရက
| {acc_s}
| {acc_d}
| {acc_p}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | တိၚ်တိုက်ကပေါတ်ကွိၚ်ကွိုက်
| {ins_s}
| {ins_d}
| {ins_p}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ခၞံဗဒှ်ဌာန်မတန်တဴ
| {loc_s}
| {loc_d}
| {loc_p}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ပရေၚ်ဂယိုၚ်လမျီု
| {voc_s}
| {voc_d}
| {voc_p}
]=] .. template_postlude()
local function get_table_spec_one_number(number, numcode)
local table_spec_one_number = [=[
! style="width:33%;background:var(--wikt-palette-lightblue)" |
! style="background:var(--wikt-palette-lightblue)" | NUMBER
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | မဒုၚ်ယၟု
| {nom_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ဗဳဇဂကူ
| {gen_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ပြကမ္မကာရက
| {dat_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ကမ္မကာရက
| {acc_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ပရေၚ်ဂယိုၚ်လမျီု
| {voc_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ခၞံဗဒှ်ဌာန်မတန်တဴ
| {loc_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | တိၚ်တိုက်ကပေါတ်ကွိၚ်ကွိုက်
| {ins_CODE}
]=]
return template_prelude("30") .. table_spec_one_number:gsub("NUMBER", number):gsub("CODE", numcode) ..
template_postlude()
end
local function get_table_spec_one_number_clitic(number, numcode)
local table_spec_one_number_clitic = [=[
! rowspan=2 style="background:var(--wikt-palette-lightblue, #d9ebff)" |
! style="background:var(--wikt-palette-lightblue, #d9ebff)" |NUMBER
|-
! style="background:var(--wikt-palette-lightblue, #d9ebff)" |stressed
! style="background:var(--wikt-palette-lightblue, #d9ebff)" |clitic
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | မဒုၚ်ယၟု
| colspan=2 | {nom_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ဗဳဇဂကူ
| {gen_CODE}
| {clitic_gen_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ပြကမ္မကာရက
| {dat_CODE}
| {clitic_dat_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ကမ္မကာရက
| {acc_CODE}
| {clitic_acc_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ပရေၚ်ဂယိုၚ်လမျီု
| colspan=2 | {voc_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | ခၞံဗဒှ်ဌာန်မတန်တဴ
| colspan=2 | {loc_CODE}
|-
! scope="row" style="background:var(--wikt-palette-lighterblue, #ebf4ff)" | တိၚ်တိုက်ကပေါတ်ကွိၚ်ကွိုက်
| colspan=2 | {ins_CODE}
]=]
return template_prelude("40") .. table_spec_one_number_clitic:gsub("NUMBER", number):gsub("CODE", numcode) ..
template_postlude()
end
local notes_template = [=[
<div style="width:100%;text-align:left;background:var(--wikt-palette-lightblue, #d9ebff)">
<div style="display:inline-block;text-align:left;padding-left:1em;padding-right:1em">
{footnote}
</div></div>
]=]
if alternant_multiword_spec.title then
forms.title = alternant_multiword_spec.title
else
forms.title = 'မလဟုတ်စှ်ေဆေၚ်စပ်ကဵု <i lang="hsb">' .. forms.lemma .. '</i>'
end
local annotation = alternant_multiword_spec.annotation
if annotation == "" then
forms.annotation = ""
else
forms.annotation = " (<span style=\"font-size: smaller;\">" .. annotation .. "</span>)"
end
local number, numcode
if alternant_multiword_spec.actual_number == "sg" then
number, numcode = "singular", "s"
elseif alternant_multiword_spec.actual_number == "du" then
number, numcode = "dual", "d"
elseif alternant_multiword_spec.actual_number == "pl" then
number, numcode = "plural", "p"
elseif alternant_multiword_spec.actual_number == "none" then -- used for [[sebe]]
number, numcode = "", "s"
end
local table_spec =
alternant_multiword_spec.actual_number == "allthree" and table_spec_allthree or
alternant_multiword_spec.has_clitic and get_table_spec_one_number_clitic(number, numcode) or
get_table_spec_one_number(number, numcode)
forms.notes_clause = forms.footnote ~= "" and
m_string_utilities.format(notes_template, forms) or ""
return m_string_utilities.format(table_spec, forms)
end
local function compute_headword_genders(alternant_multiword_spec)
local genders = {}
local number
if alternant_multiword_spec.actual_number == "pl" then
number = "-p"
elseif alternant_multiword_spec.actual_number == "du" then
number = "-d"
else
number = ""
end
iut.map_word_specs(alternant_multiword_spec, function(base)
local animacy = base.animacy
if animacy == "inan" then
animacy = "in"
end
m_table.insertIfNot(genders, base.gender .. "-" .. animacy .. number)
end)
return genders
end
-- Externally callable function to parse and decline a noun given user-specified arguments.
-- Return value is ALTERNANT_MULTIWORD_SPEC, an object where the declined forms are in
-- `ALTERNANT_MULTIWORD_SPEC.forms` for each slot. If there are no values for a slot, the
-- slot key will be missing. The value for a given slot is a list of objects
-- {form=FORM, footnotes=FOOTNOTES}.
function export.do_generate_forms(parent_args, from_headword)
local params = {
[1] = {required = true, default = "žona<f>"},
title = {},
pagename = {},
json = {type = "boolean"},
pos = {},
}
if from_headword then
params["head"] = {list = true}
params["lemma"] = {list = true}
params["g"] = {list = true}
params["f"] = {list = true}
params["m"] = {list = true}
params["adj"] = {list = true}
params["dim"] = {list = true}
params["id"] = {}
end
local args = m_para.process(parent_args, params)
local parse_props = {
parse_indicator_spec = parse_indicator_spec,
angle_brackets_omittable = true,
allow_blank_lemma = true,
}
local alternant_multiword_spec = iut.parse_inflected_text(args[1], parse_props)
alternant_multiword_spec.title = args.title
alternant_multiword_spec.args = args
local pagename = args.pagename or from_headword and args.head[1] or mw.loadData("Module:headword/data").pagename
normalize_all_lemmas(alternant_multiword_spec, pagename)
set_all_defaults_and_check_bad_indicators(alternant_multiword_spec)
-- These need to happen before detect_all_indicator_specs() so that adjectives get their genders and numbers set
-- appropriately, which are needed to correctly synthesize the adjective lemma.
propagate_properties(alternant_multiword_spec, "animacy", "inan", "mixed")
propagate_properties(alternant_multiword_spec, "number", "allthree", "allthree")
-- FIXME, the default value (third param) used to be 'm' with a comment indicating that this applied only to
-- plural adjectives, where it didn't matter; but here, plural adjectives are distinguished for gender and
-- animacy. Make sure 'mixed' works.
propagate_properties(alternant_multiword_spec, "gender", "mixed", "mixed")
detect_all_indicator_specs(alternant_multiword_spec)
-- Propagate 'actual_number' after calling detect_all_indicator_specs(), which sets 'actual_number' for adjectives.
propagate_properties(alternant_multiword_spec, "actual_number", "allthree", "allthree")
determine_noun_status(alternant_multiword_spec)
set_pos(alternant_multiword_spec)
alternant_multiword_spec.output_noun_slots = get_output_noun_slots(alternant_multiword_spec)
local inflect_props = {
skip_slot = function(slot)
return skip_slot(alternant_multiword_spec.actual_number, slot)
end,
slot_table = alternant_multiword_spec.output_noun_slots,
get_variants = get_variants,
inflect_word_spec = decline_noun,
}
iut.inflect_multiword_or_alternant_multiword_spec(alternant_multiword_spec, inflect_props)
compute_categories_and_annotation(alternant_multiword_spec)
alternant_multiword_spec.genders = compute_headword_genders(alternant_multiword_spec)
if args.json then
alternant_multiword_spec.args = nil
return require("Module:JSON").toJSON(alternant_multiword_spec)
end
return alternant_multiword_spec
end
-- Entry point for {{hsb-ndecl}}. Template-callable function to parse and decline a noun given
-- user-specified arguments and generate a displayable table of the declined forms.
function export.show(frame)
local parent_args = frame:getParent().args
local alternant_multiword_spec = export.do_generate_forms(parent_args)
if type(alternant_multiword_spec) == "string" then
-- JSON return value
return alternant_multiword_spec
end
show_forms(alternant_multiword_spec)
return make_table(alternant_multiword_spec) ..
require("Module:utilities").format_categories(alternant_multiword_spec.categories, lang, nil, nil, force_cat)
end
return export
5inibgt9g0rtrzr6sfco9kq0fnn5toc
ထာမ်ပလိက်:subpages/documentation
10
295720
396542
2026-06-07T16:56:02Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "{{documentation subpage}} This template shows all the subpages of a given name. This template should never be used in mainspace. ==TemplateData== {{TemplateData header}} <templatedata> { "params": { "1": { "label": "Page name", "description": "Page name to be searched", "type": "wiki-page-name", "default": "{{PAGENAME}}", "suggested": true }, "stripprefix": { "label": "Strip prefixes", "d..."
396542
wikitext
text/x-wiki
{{documentation subpage}}
This template shows all the subpages of a given name. This template should never be used in mainspace.
==TemplateData==
{{TemplateData header}}
<templatedata>
{
"params": {
"1": {
"label": "Page name",
"description": "Page name to be searched",
"type": "wiki-page-name",
"default": "{{PAGENAME}}",
"suggested": true
},
"stripprefix": {
"label": "Strip prefixes",
"description": "If set, strips the prefixes from the listed pages",
"type": "boolean"
},
"hideredirects": {
"label": "Hide redirects",
"description": "If set, hides all redirects from the list",
"type": "boolean"
}
},
"description": "List all pages in a given namespace and prefix.",
"paramOrder": [
"1",
"hideredirects",
"stripprefix"
],
"format": "inline"
}
</templatedata>
<includeonly>
[[ကဏ္ဍ:ထာမ်ပလိက်မနွံဒၟံၚ်အပ္ဍဲလေန်ဂမၠိုၚ်]]
</includeonly>
5fzfxvjvdahyj7evq79wb12bnlq0n19
ထာမ်ပလိက်:hsb-ndecl/documentation
10
295721
396544
2026-06-07T17:09:15Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "{{documentation subpage}} {{documentation needed}}<!-- Replace this with a short description of the purpose of the template, and how to use it. --> <includeonly> [[ကဏ္ဍ:ထာမ်ပလိက်အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏနာမ်သဝ်ဗဳယျာ လ္ပာ်သၠုၚ်ကျာဂမၠိုၚ်|*]] </includeonly>"
396544
wikitext
text/x-wiki
{{documentation subpage}}
{{documentation needed}}<!-- Replace this with a short description of the purpose of the template, and how to use it. -->
<includeonly>
[[ကဏ္ဍ:ထာမ်ပလိက်အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏနာမ်သဝ်ဗဳယျာ လ္ပာ်သၠုၚ်ကျာဂမၠိုၚ်|*]]
</includeonly>
6x0r9ywoh6d3dbf72ks8dkj3gsn183y
ကဏ္ဍ:ထာမ်ပလိက်အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏနာမ်သဝ်ဗဳယျာ လ္ပာ်သၠုၚ်ကျာဂမၠိုၚ်
14
295722
396545
2026-06-07T17:11:20Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "{{auto cat}}"
396545
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
396546
396545
2026-06-07T17:14:39Z
咽頭べさ
33
396546
wikitext
text/x-wiki
[[ကဏ္ဍ:ထာမ်ပလိက်အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏသဝ်ဗဳယျာ လ္ပာ်သၠုၚ်ကျာဂမၠိုၚ်]][[ကဏ္ဍ:ထာမ်ပလိက်အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏနာမ်ဗက်အလိုက်အရေဝ်ဘာသာဂမၠိုၚ်|သ]]
ry1wo79u0idelxq70arwm4xf3ig41x8
ကဏ္ဍ:ထာမ်ပလိက်အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏသဝ်ဗဳယျာ လ္ပာ်သၠုၚ်ကျာဂမၠိုၚ်
14
295723
396547
2026-06-07T17:16:05Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "[[ကဏ္ဍ:ထာမ်ပလိက်သဝ်ဗဳယျာ လ္ပာ်သၠုၚ်ကျာဂမၠိုၚ်]][[ကဏ္ဍ:ထာမ်ပလိက်အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏဗက်အလိုက်အရေဝ်ဘာသာဂမၠိုၚ်|သ]]"
396547
wikitext
text/x-wiki
[[ကဏ္ဍ:ထာမ်ပလိက်သဝ်ဗဳယျာ လ္ပာ်သၠုၚ်ကျာဂမၠိုၚ်]][[ကဏ္ဍ:ထာမ်ပလိက်အပြံၚ်အလှာဲပ္တဝ်ထ္ၜးပမာဏဗက်အလိုက်အရေဝ်ဘာသာဂမၠိုၚ်|သ]]
390x96ied8bm5oiv9uaeqcmsjhyvnls
မဳဒဳယာဝဳကဳ:Gadget-defaultVisibilityToggles.js
8
295724
396551
2026-06-07T18:50:44Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "/* jshint undef: true */ /* globals $, jQuery, mw, window, getComputedStyle */ (function defaultVisibilityTogglesIIFE() { "use strict"; if (window.noDefaultVisibilityToggles) return; /* == NavBars == */ var NavigationBarHide = "ပၞုက် ▲"; var NavigationBarShow = "ထ္ၜး ▼"; var nbsp = "\u00a0"; // Check if an element has been activated with a toggle. // For convenience, this has the..."
396551
javascript
text/javascript
/* jshint undef: true */
/* globals $, jQuery, mw, window, getComputedStyle */
(function defaultVisibilityTogglesIIFE() {
"use strict";
if (window.noDefaultVisibilityToggles) return;
/* == NavBars == */
var NavigationBarHide = "ပၞုက် ▲";
var NavigationBarShow = "ထ္ၜး ▼";
var nbsp = "\u00a0";
// Check if an element has been activated with a toggle.
// For convenience, this has the side effect of marking the element as having
// a toggle, if it is not already marked.
// Allows the functions to avoid toggleifying elements more than once, which
// can lead to multiple "show" buttons, for instance.
// The argument must be an Element, not a jQuery object.
function checkAndSetToggleified(element) {
if (element.isToggleified) {
return true;
}
element.isToggleified = true;
}
function getToggleCategory(element, defaultCategory) {
if ($(element).find("table").first().is(".translations"))
return "translations";
var heading = element;
while ((heading = heading.previousElementSibling)) {
// tagName is always uppercase:
// https://developer.mozilla.org/en-US/docs/Web/API/Element/tagName
var num = heading.tagName.match(/H(\d)/);
if (num)
num = Number(num[1]);
else
continue;
if (4 <= num && num <= 6) {
if (heading.getElementsByTagName("span")[1])
heading = heading.getElementsByTagName("span")[0];
var text = jQuery(heading).text()
.toLowerCase()
// jQuery's .text() is inconsistent about whitespace:
.replace(/^\s+|\s+$/g, "").replace(/\s+/g, " ")
// remove numbers added by the "Auto-number headings" pref:
.replace(/^[1-9][0-9.]+ ?/, "");
// Toggle category must be convertible to a valid CSS identifier so
// that it can be used in an id selector in jQuery in
// ToggleCategory.prototype.newSidebarToggle
// in [[MediaWiki:Gadget-VisibilityToggles.js]].
// Spaces must later be converted to hyphens or underscores.
// Reference: https://drafts.csswg.org/selectors-4/#id-selectors
if (/^[a-zA-Z0-9\s_-]+$/.test(text))
return text;
else
break;
} else if (num)
break;
}
return defaultCategory;
}
function createNavToggle(navFrame) {
if (checkAndSetToggleified(navFrame) || navFrame.classList.contains('no-collapse')) {
return;
}
var navHead, navContent;
for (var i = 0, children = navFrame.childNodes; i < children.length; ++i) {
var child = children[i];
if (child.nodeName === "DIV") {
var classList = child.classList;
if (classList.contains("NavHead"))
navHead = child;
if (classList.contains("NavContent"))
navContent = child;
}
}
if (!(navHead && navContent))
return;
// Step 1, don't react when a subitem is clicked.
$(navHead).find("a").on("click", function (e) {
e.stopPropagation();
e.stopImmediatePropagation();
});
// Step 2, toggle visibility when bar is clicked.
// NOTE This function was chosen due to some funny behaviour in Safari.
var $navToggle = $("<a>").attr("role", "button").attr("tabindex", "0");
$("<span>").addClass("NavToggle").attr("data-nosnippet", "")
.append($navToggle)
.prependTo(navHead);
navHead.style.cursor = "pointer";
var toggleCategory = $(navFrame).data("toggle-category")
|| getToggleCategory(navFrame, "other boxes");
navHead.onclick = window.VisibilityToggles.register(toggleCategory,
function show() {
$navToggle.text(NavigationBarHide);
if (navContent)
navContent.style.display = "block";
},
function hide() {
$navToggle.text(NavigationBarShow);
if (navContent)
navContent.style.display = "none";
},
$(navFrame).is("[data-toggle-show-default]"));
}
function createNavToggleForInflectionTable(it) {
if (checkAndSetToggleified(it)) {
return;
}
// The table caption is the clickable element
var itCaption = $(it).find("caption").get(0);
// Step 1, don't react when a subitem is clicked.
$(itCaption).find("a").on("click", function (e) {
e.stopPropagation();
e.stopImmediatePropagation();
});
// Step 2, toggle visibility when bar is clicked.
// NOTE This function was chosen due to some funny behaviour in Safari.
var $navToggle = $("<a>").attr("role", "button").attr("tabindex", "0");
$("<span>").addClass("NavToggle").attr("data-nosnippet", "")
.append($navToggle)
.prependTo(itCaption);
itCaption.style.cursor = "pointer";
var toggleCategory = $(it).data("toggle-category")
|| getToggleCategory(it, "other boxes");
itCaption.onclick = window.VisibilityToggles.register(toggleCategory,
function show() {
$navToggle.text(NavigationBarHide);
if (it) {
it.classList.remove("inflection-table-collapsed");
}
},
function hide() {
$navToggle.text(NavigationBarShow);
if (it) {
it.classList.add("inflection-table-collapsed");
}
},
$(it).is("[data-toggle-show-default]"));
// Check to see if we are on a browser that is known to support
// visibility: collapse, which permits inflection table headings to wrap.
// WebKit needs to be special-cased, as technically it does support
// visibility: collapse, but it just implements it as a synonym for
// visibility: hidden, which is useless. (as of November 2024)
// Yes, I know User-Agent sniffing is so 2004... but WebKit is the new IE
if (
CSS && CSS.supports && CSS.supports("visibility:collapse") &&
// exclude WebKit/Safari, excepting Blink engines which have a frozen WebKit version number
(navigator.userAgent.indexOf("AppleWebKit/") === -1 || navigator.userAgent.indexOf("AppleWebKit/537.36") > -1)
) {
it.classList.remove("no-vc");
} else {
// Strange behaviour occurs when you set the table caption to nowrap
// The [show/hide] toggle crashes into the caption text
// This spacer element prevents that
$("<span>").addClass("no-vc-spacer").appendTo(itCaption);
}
}
/* ==Hidden Quotes== */
function setupHiddenQuotes(li) {
if (checkAndSetToggleified(li))
return;
let HQToggleButton, liComp, dl;
function show() {
HQToggleButton.text("ခတှ်ေပ္တိုန်ၚုဟ် ▲");
$(li).children("ul").show();
}
function hide() {
HQToggleButton.text("ခတှ်ေပ္တိုန်ၚုဟ် ▼");
$(li).children("ul").hide();
}
for (const liComp of li.childNodes) {
// Look at each component of the definition.
if (liComp.tagName === "DL" && !dl)
dl = liComp;
// If we find a ul or dl, we have quotes or example sentences, and thus need a button.
if (liComp.tagName === "UL") {
$(li).children("ul").addClass("wikt-quote-container");
HQToggleButton = $("<a>").attr("role", "button").attr("tabindex", "0");
$(dl || liComp).before($("<span>").addClass("HQToggle").attr("data-nosnippet", "").append(HQToggleButton).css("margin-left", "5px"));
HQToggleButton.on("click", window.VisibilityToggles.register("quotations", show, hide));
break;
}
}
}
/* == View Switching == */
function viewSwitching(rootElement) {
if (checkAndSetToggleified(rootElement)) {
return;
}
var $rootElement = $(rootElement);
var showButtonText = $rootElement.data("vs-showtext") || "ပဵု ▼";
var hideButtonText = $rootElement.data("vs-hidetext") || "အောန် ▲";
var toSkip = $rootElement.find(".vsSwitcher").find("*");
var elemsToHide = $rootElement.find(".vsHide").not(toSkip);
var elemsToShow = $rootElement.find(".vsShow").not(toSkip);
// Find the element to place the toggle button in.
var toggleElement = $rootElement.find(".vsToggleElement").not(toSkip).first();
// The toggleElement becomes clickable in its entirety, but
// we need to prevent this if a contained link is clicked instead.
toggleElement.find("a").on("click", function (e) {
e.stopPropagation();
e.stopImmediatePropagation();
});
// Add the toggle button.
var toggleButton = $("<a>").attr("role", "button").attr("tabindex", "0");
$("<span>").addClass("NavToggle").attr("data-nosnippet", "").append(toggleButton).prependTo(toggleElement);
// Determine the visibility toggle category (for the links in the bar on the left).
var toggleCategory = $rootElement.data("toggle-category");
if (!toggleCategory) {
var classNames = $rootElement.attr("class").split(/\s+/);
for (var i = 0; i < classNames.length; ++i) {
var className = classNames[i].split("-");
if (className[0] == "vsToggleCategory") {
toggleCategory = className[1];
}
}
}
if (!toggleCategory)
toggleCategory = "others";
// Register the visibility toggle.
toggleElement.css("cursor", "pointer");
toggleElement.on("click", window.VisibilityToggles.register(toggleCategory,
function show() {
toggleButton.text(hideButtonText);
elemsToShow.hide();
elemsToHide.show();
},
function hide() {
toggleButton.text(showButtonText);
elemsToShow.show();
elemsToHide.hide();
},
$rootElement.is("[data-toggle-show-default]")));
}
/* ==List switching== */
function enableListSwitchGeneric(rootElement) {
if (checkAndSetToggleified(rootElement)) {
return;
}
var $rootElement = $(rootElement);
// Create a toggle button.
var $toggleElement = $("<div>").addClass("list-switcher-element");
var $navToggle = $("<span>").addClass("NavToggle").attr("data-nosnippet", "");
var $toggleButton = $("<a>").attr("role", "button").attr("tabindex", "0");
// Add the toggle button to the DOM tree.
$navToggle.append($toggleButton).prependTo($toggleElement);
$toggleElement.insertAfter($rootElement);
$toggleElement.show();
// Determine the visibility toggle category (for the links in the bar on the
// left). It will either be the value of the "data-toggle-category"
// attribute or will be based on the text of the closest preceding
// fourth-to-sixth-level header.
var toggleCategory = $rootElement.data("toggle-category")
|| getToggleCategory($rootElement[0], "other lists");
// Determine the text for the $toggleButton.
var showButtonText = "ပဵုထ္ၜး ▼";
var hideButtonText = "ထ္ၜးအောန်စှ်ေ ▲";
var numItems;
// special handling for [[Module:collapsible category tree]]
var $categoryTreeTag = $rootElement.children(".CategoryTreeTag");
if ($categoryTreeTag) {
if ($categoryTreeTag.attr("data-pages-left-over") !== "0") {
// some category members are omitted from the list (MediaWiki limitation)
// just use basic "show more/less" in this case for now, this is a big change
//showButtonText = "ထ္ၜး ၂၀၀ ကၠာနကဵု " + $categoryTreeTag.attr("data-pages-in-cat") + " ▼";
//hideButtonText = "ထ္ၜးညိည ▲";
} else {
// all category members are included in the list
numItems = $categoryTreeTag.attr("data-pages-in-cat");
}
} else {
// standard list-switcher using <li> elements
numItems = $rootElement.find("li").length;
}
if (numItems) {
showButtonText = "ထ္ၜးသီုဖအိုတ် " + numItems + " ▼";
hideButtonText = "ထ္ၜးညိည ▲";
}
// Register the visibility toggle.
$toggleElement.on("click", window.VisibilityToggles.register(toggleCategory,
function show() {
$toggleButton.text(hideButtonText);
if (rootElement) {
rootElement.classList.remove("list-switcher-collapsed");
}
},
function hide() {
$toggleButton.text(showButtonText);
if (rootElement) {
rootElement.classList.add("list-switcher-collapsed");
}
},
$rootElement.is("[data-toggle-show-default]")));
// Register a resize observer to see if we need to keep the
// show/hide toggle visible
var termList = rootElement.querySelector(':scope > .term-list');
if (termList && window.ResizeObserver) {
var resizeObserver = new ResizeObserver(function(entries) {
if (entries[0] && entries[0].contentBoxSize[0]) {
// Work out what the max-height would be, in pixels
// As a hack, this value is stored in the CSS `bottom`
// property, as `max-height` is only in place when
// the list is collapsed, but we need to do this check
// even when the list is not collapsed
var maxHeightPx = parseFloat(getComputedStyle(rootElement).bottom);
// If box height is less than its max height + 20 px, suppress
// collapsibility. The 20 px buffer prevents the situation where
// clicking "show more" expands the box by just a few pixels
if (entries[0].contentBoxSize[0].blockSize <= maxHeightPx + 20) {
$toggleElement.hide();
if (rootElement.classList.contains("list-switcher-collapsed")) {
rootElement.classList.remove("list-switcher-collapsed");
rootElement.classList.add("list-switcher-collapsibility-suppressed");
}
} else {
$toggleElement.show();
if (rootElement.classList.contains("list-switcher-collapsibility-suppressed")) {
rootElement.classList.remove("list-switcher-collapsibility-suppressed");
rootElement.classList.add("list-switcher-collapsed");
}
}
}
});
resizeObserver.observe(termList);
}
}
// based on [[User:Erutuon/scripts/semhide.js]], [[User:Jberkel/semhide.js]],
// [[User:Ungoliant_MMDCCLXIV/synshide.js]]
function setupNyms(index, dlTag) {
// [[Wiktionary:Semantic relations]]
var relationClasses = [ "ဝေါဟာလွာ", "လဟီု", "ဝေါဟာဒစး", "ဝေါဟာတၟုပ်", "ပွံၚ်နဲတၞဟ်",
"ကၠာဲ", "ဆက်လ္ၚတ်", "comeronym", "coordinate-term",
"near-synonym", "imperfective", "perfective", "ပွံၚ်အက္ခရ်နဲတၞဟ်" ];
var relations = $(dlTag).find("dd > .nyms").get().filter(
function(element) {
return Array.prototype.some.call(element.classList, function (className) {
if (relationClasses.indexOf(className) !== -1) {
element.dataset.relationClass = className;
return true;
}
});
});
function setupToggle(elements, category, visibleByDefault) {
if (elements.length === 0) return null;
var toggler = $("<a>").attr("role", "button").attr("tabindex", "0");
var text = elements.map(function (e) {
var linkCount = e.querySelectorAll("span[lang]").length;
return e.dataset.relationClass.replace("-", " ") +
(linkCount > 1 ? "s" : "");
}).join(", ");
function show() {
toggler.text(text + nbsp + "▲");
$(dlTag).show();
$(elements).show();
}
function hide() {
toggler.text(text + nbsp + "▼");
if ($(dlTag).children().length === elements.length) {
$(dlTag).hide();
} else {
$(elements).hide();
}
}
$(dlTag).before($("<span>")
.addClass("nyms-toggle")
.attr("data-nosnippet", "")
.append(toggler)
.css("margin-left", "5px"));
toggler.click(window.VisibilityToggles.register(category, show, hide, visibleByDefault));
}
var synonyms = relations.filter(function (e) {
return ["ဝေါဟာလွာ", "လဟီု", "ဝေါဟာဒစး", "ေါဟာတၟုပ်", "ပွံၚ်နဲတၞဟ်"].indexOf(e.dataset.relationClass) !== -1;
});
var other = relations.filter(function (e) { return synonyms.indexOf(e) === -1; });
setupToggle(synonyms, "synonyms", true /* show by default */);
setupToggle(other, "semantic relations");
}
function setupUsageExampleCollapses(index, dlTag) {
var usexTags = $(dlTag).find("dd > .h-usage-example").get();
function setupToggle(elements, category, visibleByDefault) {
if (elements.length === 0) return null;
var toggler = $("<a>").attr("role", "button").attr("tabindex", "0");
function show() {
toggler.text(category + nbsp + "▲");
$(dlTag).show();
$(elements).show();
}
function hide() {
toggler.text(category + nbsp + "▼");
if ($(dlTag).children().length === elements.length) {
$(dlTag).hide();
} else {
$(elements).hide();
}
}
$(dlTag).before($("<span>")
.addClass("nyms-toggle")
.append(toggler)
.css("margin-left", "5px"));
toggler.click(window.VisibilityToggles.register(category, show, hide, visibleByDefault));
}
var collocations = usexTags.filter(function (e) {
return $(e).hasClass("collocation");
});
var usexes = usexTags.filter(function (e) { return collocations.indexOf(e) === -1; });
setupToggle(usexes, "usage examples", true /* show by default */);
setupToggle(collocations, "collocations", true /* show by default */);
}
window.createNavToggle = createNavToggle;
window.setupHiddenQuotes = setupHiddenQuotes;
window.viewSwitching = viewSwitching;
window.getToggleCategory = getToggleCategory;
/* == Apply four functions defined above == */
mw.hook("wikipage.content").add(function($content) {
// NavToggles
$(".NavFrame", $content).each(function(){
createNavToggle(this);
});
$(".inflection-table-collapsible", $content).each(function(){
createNavToggleForInflectionTable(this);
});
// order nyms -> usexes -> quotes, to match the conventional order in entries
// synonyms and such under definitions
// if (mw.config.get("wgNamespaceNumber") === 0) {
$("dl:has(dd > .nyms)", $content).each(setupNyms);
// }
// usage examples and collocations
var namespaceNumber = mw.config.get("wgNamespaceNumber");
if (window.defaultVisibilityTogglesForUsageExamples) {
if (namespaceNumber === 0) {
$("ol > li dl:has(dd > .h-usage-example)", $content).each(setupUsageExampleCollapses);
}
}
// quotes
if (namespaceNumber === 0 || namespaceNumber === 100 || namespaceNumber === 118) {
// First, find all the ordered lists, i.e. all the series of definitions.
$("ol > li", $content).each(function(){
setupHiddenQuotes(this);
});
}
//view switching
$(".vsSwitcher", $content).each(function(){
viewSwitching(this);
});
// list switching
$(".list-switcher", $content).each(function () {
enableListSwitchGeneric(this);
});
});
jQuery(mw).on("LivePreviewDone", function (ev, sels) {
var ols = jQuery(sels.join(",")).find("ol");
for (var i = 0; i < ols.length; i++) {
for (var j = 0; j < ols[i].childNodes.length; j++) {
var li = ols[i].childNodes[j];
if (li.nodeName.toUpperCase() == "LI") {
setupHiddenQuotes(li);
}
}
}
});
})();
iunougrxey0xzqp11qi2e3l2g4kc1ep
396552
396551
2026-06-07T18:52:01Z
咽頭べさ
33
396552
javascript
text/javascript
/* jshint undef: true */
/* globals $, jQuery, mw, window, getComputedStyle */
(function defaultVisibilityTogglesIIFE() {
"use strict";
if (window.noDefaultVisibilityToggles) return;
/* == NavBars == */
var NavigationBarHide = "ပၞုက် ▲";
var NavigationBarShow = "ထ္ၜး ▼";
var nbsp = "\u00a0";
// Check if an element has been activated with a toggle.
// For convenience, this has the side effect of marking the element as having
// a toggle, if it is not already marked.
// Allows the functions to avoid toggleifying elements more than once, which
// can lead to multiple "show" buttons, for instance.
// The argument must be an Element, not a jQuery object.
function checkAndSetToggleified(element) {
if (element.isToggleified) {
return true;
}
element.isToggleified = true;
}
function getToggleCategory(element, defaultCategory) {
if ($(element).find("table").first().is(".translations"))
return "translations";
var heading = element;
while ((heading = heading.previousElementSibling)) {
// tagName is always uppercase:
// https://developer.mozilla.org/en-US/docs/Web/API/Element/tagName
var num = heading.tagName.match(/H(\d)/);
if (num)
num = Number(num[1]);
else
continue;
if (4 <= num && num <= 6) {
if (heading.getElementsByTagName("span")[1])
heading = heading.getElementsByTagName("span")[0];
var text = jQuery(heading).text()
.toLowerCase()
// jQuery's .text() is inconsistent about whitespace:
.replace(/^\s+|\s+$/g, "").replace(/\s+/g, " ")
// remove numbers added by the "Auto-number headings" pref:
.replace(/^[1-9][0-9.]+ ?/, "");
// Toggle category must be convertible to a valid CSS identifier so
// that it can be used in an id selector in jQuery in
// ToggleCategory.prototype.newSidebarToggle
// in [[MediaWiki:Gadget-VisibilityToggles.js]].
// Spaces must later be converted to hyphens or underscores.
// Reference: https://drafts.csswg.org/selectors-4/#id-selectors
if (/^[a-zA-Z0-9\s_-]+$/.test(text))
return text;
else
break;
} else if (num)
break;
}
return defaultCategory;
}
function createNavToggle(navFrame) {
if (checkAndSetToggleified(navFrame) || navFrame.classList.contains('no-collapse')) {
return;
}
var navHead, navContent;
for (var i = 0, children = navFrame.childNodes; i < children.length; ++i) {
var child = children[i];
if (child.nodeName === "DIV") {
var classList = child.classList;
if (classList.contains("NavHead"))
navHead = child;
if (classList.contains("NavContent"))
navContent = child;
}
}
if (!(navHead && navContent))
return;
// Step 1, don't react when a subitem is clicked.
$(navHead).find("a").on("click", function (e) {
e.stopPropagation();
e.stopImmediatePropagation();
});
// Step 2, toggle visibility when bar is clicked.
// NOTE This function was chosen due to some funny behaviour in Safari.
var $navToggle = $("<a>").attr("role", "button").attr("tabindex", "0");
$("<span>").addClass("NavToggle").attr("data-nosnippet", "")
.append($navToggle)
.prependTo(navHead);
navHead.style.cursor = "pointer";
var toggleCategory = $(navFrame).data("toggle-category")
|| getToggleCategory(navFrame, "other boxes");
navHead.onclick = window.VisibilityToggles.register(toggleCategory,
function show() {
$navToggle.text(NavigationBarHide);
if (navContent)
navContent.style.display = "block";
},
function hide() {
$navToggle.text(NavigationBarShow);
if (navContent)
navContent.style.display = "none";
},
$(navFrame).is("[data-toggle-show-default]"));
}
function createNavToggleForInflectionTable(it) {
if (checkAndSetToggleified(it)) {
return;
}
// The table caption is the clickable element
var itCaption = $(it).find("caption").get(0);
// Step 1, don't react when a subitem is clicked.
$(itCaption).find("a").on("click", function (e) {
e.stopPropagation();
e.stopImmediatePropagation();
});
// Step 2, toggle visibility when bar is clicked.
// NOTE This function was chosen due to some funny behaviour in Safari.
var $navToggle = $("<a>").attr("role", "button").attr("tabindex", "0");
$("<span>").addClass("NavToggle").attr("data-nosnippet", "")
.append($navToggle)
.prependTo(itCaption);
itCaption.style.cursor = "pointer";
var toggleCategory = $(it).data("toggle-category")
|| getToggleCategory(it, "other boxes");
itCaption.onclick = window.VisibilityToggles.register(toggleCategory,
function show() {
$navToggle.text(NavigationBarHide);
if (it) {
it.classList.remove("inflection-table-collapsed");
}
},
function hide() {
$navToggle.text(NavigationBarShow);
if (it) {
it.classList.add("inflection-table-collapsed");
}
},
$(it).is("[data-toggle-show-default]"));
// Check to see if we are on a browser that is known to support
// visibility: collapse, which permits inflection table headings to wrap.
// WebKit needs to be special-cased, as technically it does support
// visibility: collapse, but it just implements it as a synonym for
// visibility: hidden, which is useless. (as of November 2024)
// Yes, I know User-Agent sniffing is so 2004... but WebKit is the new IE
if (
CSS && CSS.supports && CSS.supports("visibility:collapse") &&
// exclude WebKit/Safari, excepting Blink engines which have a frozen WebKit version number
(navigator.userAgent.indexOf("AppleWebKit/") === -1 || navigator.userAgent.indexOf("AppleWebKit/537.36") > -1)
) {
it.classList.remove("no-vc");
} else {
// Strange behaviour occurs when you set the table caption to nowrap
// The [show/hide] toggle crashes into the caption text
// This spacer element prevents that
$("<span>").addClass("no-vc-spacer").appendTo(itCaption);
}
}
/* ==Hidden Quotes== */
function setupHiddenQuotes(li) {
if (checkAndSetToggleified(li))
return;
let HQToggleButton, liComp, dl;
function show() {
HQToggleButton.text("ခတှ်ေပ္တိုန်ၚုဟ် ▲");
$(li).children("ul").show();
}
function hide() {
HQToggleButton.text("ခတှ်ေပ္တိုန်ၚုဟ် ▼");
$(li).children("ul").hide();
}
for (const liComp of li.childNodes) {
// Look at each component of the definition.
if (liComp.tagName === "DL" && !dl)
dl = liComp;
// If we find a ul or dl, we have quotes or example sentences, and thus need a button.
if (liComp.tagName === "UL") {
$(li).children("ul").addClass("wikt-quote-container");
HQToggleButton = $("<a>").attr("role", "button").attr("tabindex", "0");
$(dl || liComp).before($("<span>").addClass("HQToggle").attr("data-nosnippet", "").append(HQToggleButton).css("margin-left", "5px"));
HQToggleButton.on("click", window.VisibilityToggles.register("quotations", show, hide));
break;
}
}
}
/* == View Switching == */
function viewSwitching(rootElement) {
if (checkAndSetToggleified(rootElement)) {
return;
}
var $rootElement = $(rootElement);
var showButtonText = $rootElement.data("vs-showtext") || "ပဵု ▼";
var hideButtonText = $rootElement.data("vs-hidetext") || "အောန် ▲";
var toSkip = $rootElement.find(".vsSwitcher").find("*");
var elemsToHide = $rootElement.find(".vsHide").not(toSkip);
var elemsToShow = $rootElement.find(".vsShow").not(toSkip);
// Find the element to place the toggle button in.
var toggleElement = $rootElement.find(".vsToggleElement").not(toSkip).first();
// The toggleElement becomes clickable in its entirety, but
// we need to prevent this if a contained link is clicked instead.
toggleElement.find("a").on("click", function (e) {
e.stopPropagation();
e.stopImmediatePropagation();
});
// Add the toggle button.
var toggleButton = $("<a>").attr("role", "button").attr("tabindex", "0");
$("<span>").addClass("NavToggle").attr("data-nosnippet", "").append(toggleButton).prependTo(toggleElement);
// Determine the visibility toggle category (for the links in the bar on the left).
var toggleCategory = $rootElement.data("toggle-category");
if (!toggleCategory) {
var classNames = $rootElement.attr("class").split(/\s+/);
for (var i = 0; i < classNames.length; ++i) {
var className = classNames[i].split("-");
if (className[0] == "vsToggleCategory") {
toggleCategory = className[1];
}
}
}
if (!toggleCategory)
toggleCategory = "others";
// Register the visibility toggle.
toggleElement.css("cursor", "pointer");
toggleElement.on("click", window.VisibilityToggles.register(toggleCategory,
function show() {
toggleButton.text(hideButtonText);
elemsToShow.hide();
elemsToHide.show();
},
function hide() {
toggleButton.text(showButtonText);
elemsToShow.show();
elemsToHide.hide();
},
$rootElement.is("[data-toggle-show-default]")));
}
/* ==List switching== */
function enableListSwitchGeneric(rootElement) {
if (checkAndSetToggleified(rootElement)) {
return;
}
var $rootElement = $(rootElement);
// Create a toggle button.
var $toggleElement = $("<div>").addClass("list-switcher-element");
var $navToggle = $("<span>").addClass("NavToggle").attr("data-nosnippet", "");
var $toggleButton = $("<a>").attr("role", "button").attr("tabindex", "0");
// Add the toggle button to the DOM tree.
$navToggle.append($toggleButton).prependTo($toggleElement);
$toggleElement.insertAfter($rootElement);
$toggleElement.show();
// Determine the visibility toggle category (for the links in the bar on the
// left). It will either be the value of the "data-toggle-category"
// attribute or will be based on the text of the closest preceding
// fourth-to-sixth-level header.
var toggleCategory = $rootElement.data("toggle-category")
|| getToggleCategory($rootElement[0], "other lists");
// Determine the text for the $toggleButton.
var showButtonText = "ပဵုထ္ၜး ▼";
var hideButtonText = "ထ္ၜးအောန်စှ်ေ ▲";
var numItems;
// special handling for [[Module:collapsible category tree]]
var $categoryTreeTag = $rootElement.children(".CategoryTreeTag");
if ($categoryTreeTag) {
if ($categoryTreeTag.attr("data-pages-left-over") !== "0") {
// some category members are omitted from the list (MediaWiki limitation)
// just use basic "show more/less" in this case for now, this is a big change
//showButtonText = "ထ္ၜး ၂၀၀ ကၠာနကဵု " + $categoryTreeTag.attr("data-pages-in-cat") + " ▼";
//hideButtonText = "ထ္ၜးညိည ▲";
} else {
// all category members are included in the list
numItems = $categoryTreeTag.attr("data-pages-in-cat");
}
} else {
// standard list-switcher using <li> elements
numItems = $rootElement.find("li").length;
}
if (numItems) {
showButtonText = "ထ္ၜးသီုဖအိုတ် " + numItems + " ▼";
hideButtonText = "ထ္ၜးညိည ▲";
}
// Register the visibility toggle.
$toggleElement.on("click", window.VisibilityToggles.register(toggleCategory,
function show() {
$toggleButton.text(hideButtonText);
if (rootElement) {
rootElement.classList.remove("list-switcher-collapsed");
}
},
function hide() {
$toggleButton.text(showButtonText);
if (rootElement) {
rootElement.classList.add("list-switcher-collapsed");
}
},
$rootElement.is("[data-toggle-show-default]")));
// Register a resize observer to see if we need to keep the
// show/hide toggle visible
var termList = rootElement.querySelector(':scope > .term-list');
if (termList && window.ResizeObserver) {
var resizeObserver = new ResizeObserver(function(entries) {
if (entries[0] && entries[0].contentBoxSize[0]) {
// Work out what the max-height would be, in pixels
// As a hack, this value is stored in the CSS `bottom`
// property, as `max-height` is only in place when
// the list is collapsed, but we need to do this check
// even when the list is not collapsed
var maxHeightPx = parseFloat(getComputedStyle(rootElement).bottom);
// If box height is less than its max height + 20 px, suppress
// collapsibility. The 20 px buffer prevents the situation where
// clicking "show more" expands the box by just a few pixels
if (entries[0].contentBoxSize[0].blockSize <= maxHeightPx + 20) {
$toggleElement.hide();
if (rootElement.classList.contains("list-switcher-collapsed")) {
rootElement.classList.remove("list-switcher-collapsed");
rootElement.classList.add("list-switcher-collapsibility-suppressed");
}
} else {
$toggleElement.show();
if (rootElement.classList.contains("list-switcher-collapsibility-suppressed")) {
rootElement.classList.remove("list-switcher-collapsibility-suppressed");
rootElement.classList.add("list-switcher-collapsed");
}
}
}
});
resizeObserver.observe(termList);
}
}
// based on [[User:Erutuon/scripts/semhide.js]], [[User:Jberkel/semhide.js]],
// [[User:Ungoliant_MMDCCLXIV/synshide.js]]
function setupNyms(index, dlTag) {
// [[Wiktionary:Semantic relations]]
var relationClasses = [ "ဝေါဟာလွာ", "လဟီု", "ဝေါဟာဒစး", "ဝေါဟာတၟုပ်", "ပွံၚ်နဲတၞဟ်",
"ကၠာဲ", "ဆက်လ္ၚတ်", "comeronym", "coordinate-term",
"near-synonym", "imperfective", "perfective", "ပွံၚ်အက္ခရ်နဲတၞဟ်" ];
var relations = $(dlTag).find("dd > .nyms").get().filter(
function(element) {
return Array.prototype.some.call(element.classList, function (className) {
if (relationClasses.indexOf(className) !== -1) {
element.dataset.relationClass = className;
return true;
}
});
});
function setupToggle(elements, category, visibleByDefault) {
if (elements.length === 0) return null;
var toggler = $("<a>").attr("role", "button").attr("tabindex", "0");
var text = elements.map(function (e) {
var linkCount = e.querySelectorAll("span[lang]").length;
return e.dataset.relationClass.replace("-", " ") +
(linkCount > 1 ? "s" : "");
}).join(", ");
function show() {
toggler.text(text + nbsp + "▲");
$(dlTag).show();
$(elements).show();
}
function hide() {
toggler.text(text + nbsp + "▼");
if ($(dlTag).children().length === elements.length) {
$(dlTag).hide();
} else {
$(elements).hide();
}
}
$(dlTag).before($("<span>")
.addClass("nyms-toggle")
.attr("data-nosnippet", "")
.append(toggler)
.css("margin-left", "5px"));
toggler.click(window.VisibilityToggles.register(category, show, hide, visibleByDefault));
}
var synonyms = relations.filter(function (e) {
return ["ဝေါဟာလွာ", "လဟီု", "ဝေါဟာဒစး", "ဝေါဟာတၟုပ်", "ပွံၚ်နဲတၞဟ်"].indexOf(e.dataset.relationClass) !== -1;
});
var other = relations.filter(function (e) { return synonyms.indexOf(e) === -1; });
setupToggle(synonyms, "synonyms", true /* show by default */);
setupToggle(other, "semantic relations");
}
function setupUsageExampleCollapses(index, dlTag) {
var usexTags = $(dlTag).find("dd > .h-usage-example").get();
function setupToggle(elements, category, visibleByDefault) {
if (elements.length === 0) return null;
var toggler = $("<a>").attr("role", "button").attr("tabindex", "0");
function show() {
toggler.text(category + nbsp + "▲");
$(dlTag).show();
$(elements).show();
}
function hide() {
toggler.text(category + nbsp + "▼");
if ($(dlTag).children().length === elements.length) {
$(dlTag).hide();
} else {
$(elements).hide();
}
}
$(dlTag).before($("<span>")
.addClass("nyms-toggle")
.append(toggler)
.css("margin-left", "5px"));
toggler.click(window.VisibilityToggles.register(category, show, hide, visibleByDefault));
}
var collocations = usexTags.filter(function (e) {
return $(e).hasClass("collocation");
});
var usexes = usexTags.filter(function (e) { return collocations.indexOf(e) === -1; });
setupToggle(usexes, "usage examples", true /* show by default */);
setupToggle(collocations, "collocations", true /* show by default */);
}
window.createNavToggle = createNavToggle;
window.setupHiddenQuotes = setupHiddenQuotes;
window.viewSwitching = viewSwitching;
window.getToggleCategory = getToggleCategory;
/* == Apply four functions defined above == */
mw.hook("wikipage.content").add(function($content) {
// NavToggles
$(".NavFrame", $content).each(function(){
createNavToggle(this);
});
$(".inflection-table-collapsible", $content).each(function(){
createNavToggleForInflectionTable(this);
});
// order nyms -> usexes -> quotes, to match the conventional order in entries
// synonyms and such under definitions
// if (mw.config.get("wgNamespaceNumber") === 0) {
$("dl:has(dd > .nyms)", $content).each(setupNyms);
// }
// usage examples and collocations
var namespaceNumber = mw.config.get("wgNamespaceNumber");
if (window.defaultVisibilityTogglesForUsageExamples) {
if (namespaceNumber === 0) {
$("ol > li dl:has(dd > .h-usage-example)", $content).each(setupUsageExampleCollapses);
}
}
// quotes
if (namespaceNumber === 0 || namespaceNumber === 100 || namespaceNumber === 118) {
// First, find all the ordered lists, i.e. all the series of definitions.
$("ol > li", $content).each(function(){
setupHiddenQuotes(this);
});
}
//view switching
$(".vsSwitcher", $content).each(function(){
viewSwitching(this);
});
// list switching
$(".list-switcher", $content).each(function () {
enableListSwitchGeneric(this);
});
});
jQuery(mw).on("LivePreviewDone", function (ev, sels) {
var ols = jQuery(sels.join(",")).find("ol");
for (var i = 0; i < ols.length; i++) {
for (var j = 0; j < ols[i].childNodes.length; j++) {
var li = ols[i].childNodes[j];
if (li.nodeName.toUpperCase() == "LI") {
setupHiddenQuotes(li);
}
}
}
});
})();
e90k8fh3lviuxdqu3ffe0v7yyl4wqjv
မဳဒဳယာဝဳကဳ:Gadget-zhDialMap.js
8
295725
396553
2026-06-07T18:54:59Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "// Chinese dialectal maps, part of gadget zhDialMap // designed by [[User:Suzukaze-c]], originally posted at User:Suzukaze-c/zhDialMap.js // discussion: [[Module talk:User:Suzukaze-c/zh-dial-map]] // ideas: // * group locations in the popup by lect 'use strict'; mw.loader.load('oojs-ui-core'); var classes = { dotHover: 'zh-dial-map__dot--hover', dotSuperHover: 'zh-dial-map__dot--superHover', legendRowHover: 'zh..."
396553
javascript
text/javascript
// Chinese dialectal maps, part of gadget zhDialMap
// designed by [[User:Suzukaze-c]], originally posted at User:Suzukaze-c/zhDialMap.js
// discussion: [[Module talk:User:Suzukaze-c/zh-dial-map]]
// ideas:
// * group locations in the popup by lect
'use strict';
mw.loader.load('oojs-ui-core');
var classes = {
dotHover: 'zh-dial-map__dot--hover',
dotSuperHover: 'zh-dial-map__dot--superHover',
legendRowHover: 'zh-dial-map__legend-row--hover',
};
var groupZH = {
'Mandarin': '官話',
'Northeastern Mandarin': '東北官話',
'Jilu Mandarin': '冀魯官話',
'Jiaoliao Mandarin': '膠遼官話',
'Central Plains Mandarin': '中原官話',
'Lanyin Mandarin': '蘭銀官話',
'Southwestern Mandarin': '西南官話',
'Jianghuai Mandarin': '江淮官話',
'Cantonese': '粵語',
'Hakka': '客家話',
'Huizhou': '徽語',
'Gan': '贛語',
'Jin': '晉語',
'Jiuxing Yumin': '九姓漁民方言',
'Northern Min': '閩北語',
'Eastern Min': '閩東語',
'Southern Min': '閩南語',
'Puxian Min': '莆仙閩語',
'Zhongshan Min': '中山閩語',
'Central Min': '閩中語',
'Shaojiang Min': '邵將閩語',
'Southern Pinghua': '桂南平話',
'Northern Pinghua': '桂北平話',
'Shehua': '畬話',
'Waxiang': '瓦鄉話',
'Wu': '吳語',
'Xiang': '湘語',
'Xiangnan Tuhua': '湘南土話',
'Yuebei Tuhua': '粵北土話',
};
function zh(data) {
var element;
if (data.type == 'lang') {
element = $('<span>');
} else if (data.type == 'link') {
element = $('<a>').attr('href', '/wiki/' + data.word + '#Chinese');
}
element.addClass('Hani').attr('lang', 'zh').text(data.alt ? data.alt : data.word);
if (data.red) element.addClass('new');
return element;
}
function updateGroupSelection() {
var groupSelectionStyle = $("style.zh-dial-map__groupSelectionStyle");
var groupInputChecked = $("input.zh-dial-map__groupItem:checked");
var checkedGroups = [];
for (var i = 0; i < groupInputChecked.length; i++) {
checkedGroups.push($(groupInputChecked[i]).attr('data-group'));
}
if (checkedGroups.length === 0) {
groupSelectionStyle.text('');
} else {
var checkedGroupsNotSelector = '';
for (var i = 0; i < checkedGroups.length; i++) {
checkedGroupsNotSelector += ':not([data-group="' + checkedGroups[i] + '"])'
}
groupSelectionStyle.text('.zh-dial-map__dot' + checkedGroupsNotSelector + ' { display: none; }');
}
}
function main() {
var dots;
var redLinks = {}; // Record which pages don't exist (for the "other terms" popup links)
var wordLocations = {}; // Initialize object that will contain locations for each dialectal word. Cache instead of regenerating all the time
var otherTerms = [];
var activePopup;
var popups = [];
var mapImg = $('.zh-dial-map__map img');
// take original pixel dimensions from the image
// and give them to the image and the container holding the image
$('.zh-dial-map__map, .zh-dial-map__map img')
.css('width', mapImg.attr('width') + 'em')
.css('height', mapImg.attr('height') + 'em')
;
// <s>change image source to svg</s>
// and remove dimension attributes
mapImg
.removeAttr('width')
.removeAttr('height')
.attr('src', function(index, attr) {
return attr.replace('1200px', '2400px');
})
.removeAttr('srcset')
/*
.attr('src', function(index, attr) {
return attr.replace('thumb/', '').replace(/(svg).+/, '$1')
})
.removeAttr('srcset')
*/
;
var inputContainer = $('<span>');
inputContainer
.addClass('zh-dial-map__inputContainer')
.prependTo('.zh-dial-map__container')
;
var zoomContainer = $('<span>');
var zoomController = $('<input>');
zoomContainer
.addClass('zh-dial-map__zoomContainer')
.appendTo('.zh-dial-map__inputContainer')
;
zoomController
.addClass('zh-dial-map__zoomController')
.attr('type', 'range')
.attr('min', 0.1)
.attr('max', 4)
.attr('step', 0.1)
.val(1) // default zoom at 100%
.on("change", function() {
//console.log($(this).val());
$('.zh-dial-map__map').css("font-size", $(this).val() + "px");
})
.appendTo('.zh-dial-map__zoomContainer')
;
var groupSelectionContainer = $('<span>');
var groupSelectionStyle = $("<style>");
groupSelectionContainer
.addClass('zh-dial-map__groupSelectionContainer')
.appendTo('.zh-dial-map__inputContainer')
;
groupSelectionStyle
.addClass('zh-dial-map__groupSelectionStyle')
.appendTo('head')
;
for (var group in groupZH) {
var groupItem = $('<input>');
var groupLabel = $('<label>');
var id = 'zh-dial-map__groupItem--' + group.replace(' ', '-');
groupItem
.attr('type', 'checkbox')
.addClass('zh-dial-map__groupItem')
.attr('id', id)
.attr('name', 'zh-dial-map__groupItem')
.attr('data-group', group)
.on("change", updateGroupSelection)
.appendTo('.zh-dial-map__groupSelectionContainer')
;
groupLabel
.attr('for', id)
.text(group)
.appendTo('.zh-dial-map__groupSelectionContainer')
;
}
$('.zh-dial-map__legend-row').on("touchstart mouseenter",
function() {
var word = $(this).data('word'); // Get the dialectal word for the active legend row
var isOther = (word == 'other'); // Is our row the "other terms" row?
if (isOther) {
dots = $('.zh-dial-map__dot-other'); // Get all grey dots
} else {
dots = $('.zh-dial-map__dot[data-word=' + word + ']'); // Get every dot corresponding to our word
}
$(this).addClass(classes.legendRowHover); // Add hovered class to the legend row
dots.addClass(classes.dotHover); // Add hovered class to every corresponding dot
if (! wordLocations[word]) {
wordLocations[word] = [];
dots.each(
function(index) {
var location = [$(this).data('location-en'), $(this).data('location-zh'), $(this).data('group')]; // Get location of dot
wordLocations[word].push(location);
if (isOther) {
var otherWord = $(this).data('word'); // For "other terms", collect the terms too
otherTerms.push(otherWord);
if (! redLinks[otherWord]) redLinks[otherWord] = $(this).parent().hasClass('new');
//console.log(otherWord, redLinks[otherWord]);
}
}
);
//console.log(word, wordLocations[word]);
//if (isOther) { console.log(otherTerms) };
}
if (! popups[word]) {
var locationsList = $('<ul>').addClass('zh-dial-map__legend-row-locations'); // Popup contents
$.each(wordLocations[word], function(index, value) { // Do stuff related to each location recorded for this word
if (isOther) word = otherTerms[index];
var locationEn = value[0], locationZh = value[1], group = value[2];
var listItem = $('<li>');
listItem.append(zh({type: 'lang', word: locationZh + groupZH[group]})).append(' (' + locationEn + ' ' + group + ')'); // FIXME: this feels really hacky
if (isOther) listItem.append(': ').append(zh({type: 'link', word: otherTerms[index], red: redLinks[word]})); // FIXME: this too
var locationDot = $('[data-location-zh=' + locationZh + '][data-word=' + word + ']'); // Find the dot for this word at this location
//console.log('[data-location-zh=' + locationZh + '][data-word=' + word + ']', locationDot);
listItem.hover(
function() {
locationDot.addClass(classes.dotSuperHover);
},
function() {
locationDot.removeClass(classes.dotSuperHover);
}
);
listItem.appendTo(locationsList);
});
popups[word] = new OO.ui.PopupWidget({
$content: locationsList,
$container: $('.zh-dial-map__container'),
});
$(this).append(popups[word].$element); // Add cool popup to DOM
}
popups[word].toggle(true); // Make the popup visible
activePopup = popups[word];
}
);
$('.zh-dial-map__legend-row').on("touchend mouseleave",
function() {
$(this).removeClass(classes.legendRowHover); // Remove hovered class from legend row
dots.removeClass(classes.dotHover); // Remove hovered class from every dot that corresponds to that word
activePopup.toggle(false); // Hide popup
}
);
}
if ($('.zh-dial-map__map').length) {
main();
}
qb252rtmpllf3cwpa1ja22c4zd74234
မဳဒဳယာဝဳကဳ:Gadget-zhDialMap.css
8
295726
396554
2026-06-07T18:56:09Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု ".zh-dial-map__container { margin: auto; max-width: 1100px; position: relative; } .zh-dial-map__inputContainer { position: absolute; top: 0; left: 0; background: rgba(255, 255, 255, 0.5); z-index: 1; margin: 1em; padding: 0.5em; font-size: 1em; opacity: 0.5; transition: opacity 1s; /* take 1 second to fade */ color-scheme: light; color: black; } .zh-dial-map__inputContainer:hover { opacity: 1; transiti..."
396554
css
text/css
.zh-dial-map__container {
margin: auto;
max-width: 1100px;
position: relative;
}
.zh-dial-map__inputContainer {
position: absolute;
top: 0;
left: 0;
background: rgba(255, 255, 255, 0.5);
z-index: 1;
margin: 1em;
padding: 0.5em;
font-size: 1em;
opacity: 0.5;
transition: opacity 1s; /* take 1 second to fade */
color-scheme: light;
color: black;
}
.zh-dial-map__inputContainer:hover {
opacity: 1;
transition: opacity 0.2s; /* take 0.2 seconds to be non-opaque */
}
.zh-dial-map__inputContainer input {
vertical-align: middle;
}
.zh-dial-map__zoomContainer:before {
content: "10% ";
}
.zh-dial-map__zoomContainer:after {
content: " 400%";
}
.zh-dial-map__zoomController {
width: 120px;
}
.zh-dial-map__frame {
max-height: 80vh;
width: 100%;
box-sizing: border-box; /* ... */
}
.zh-dial-map__map {
position: relative; /* dots relative to the map */
margin: auto; /* center in frame */
}
.zh-dial-map__map img {
pointer-events: none;
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
}
.zh-dial-map__dot, .zh-dial-map__legend-row-dot {
height: 10px;
width: 10px;
border-radius: 100%;
/* https://stackoverflow.com/q/471510 */
text-indent: 100%;
overflow: hidden;
}
.zh-dial-map__legend-row-dot {
display: inline-block;
margin-right: 0.5em;
}
.zh-dial-map__dot {
position: absolute;
transform: translate(-50%,-50%); /* https://stackoverflow.com/q/33683602 */
cursor: help;
}
.zh-dial-map__dot:hover, .zh-dial-map__dot--hover {
opacity: 0.75;
height: 20px;
width: 20px;
z-index: 1;
border: 5px solid white;
}
.zh-dial-map__dot--superHover {
opacity: 1;
height: 30px;
width: 30px;
z-index: 2;
animation: .4s infinite alternate dotPulse;
/* for when your mouse is inside the popup && over the dot */
pointer-events: none;
}
@keyframes dotPulse {
from {
opacity: 0.75;
height: 20px;
width: 20px;
}
to {
opacity: 1;
height: 30px;
width: 30px;
}
}
.zh-dial-map__legend {
-ms-column-count: 5;
-moz-column-count: 5;
-webkit-column-count: 5;
column-count: 5;
line-height: 1.4;
padding: 0.5em;
}
.zh-dial-map__legend-row--hover,
.zh-dial-map__legend-row .oo-ui-popupWidget-popup li:hover {
background: rgba(128, 255, 0, 0.25);
}
.zh-dial-map__legend-row .oo-ui-popupWidget-popup {
background-color: rgba(255, 255, 255, 0.75);
text-shadow: 1px 1px 0 rgba(255, 255, 255, 0.5);
}
/* dark mode colors; styles need to be duplicated exactly between these two media blocks */
@media screen {
html.skin-theme-clientpref-night .zh-dial-map__legend-row .oo-ui-popupWidget-popup {
background-color: rgba(32, 32, 32, 0.75);
text-shadow: 1px 1px 0 rgba(32, 32, 32, 0.5);
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os .zh-dial-map__legend-row .oo-ui-popupWidget-popup {
background-color: rgba(32, 32, 32, 0.75);
text-shadow: 1px 1px 0 rgba(32, 32, 32, 0.5);
}
}
/* vertically increase the "hitbox" of the popup */
/* so that it is easier to get the cursor inside */
.zh-dial-map__legend-row .oo-ui-popupWidget:before,
.zh-dial-map__legend-row .oo-ui-popupWidget:after {
content: "";
position: absolute;
top: 0;
bottom: 0;
/*left: 30%;*/ /* if it fully extends horizontally, getting from ... */
/*right: 30%;*/ /* ... a row to the one above/below gets annoying */
left: 0;
right: 0;
z-index: 2;
}
.zh-dial-map__legend-row .oo-ui-popupWidget:before {
top: -1.2em;
background: rgba(255, 0, 0, 0.05);
background: transparent;
}
.zh-dial-map__legend-row .oo-ui-popupWidget:after {
bottom: -1.2em;
background: rgba(0, 0, 255, 0.05);
background: transparent;
}
.zh-dial-map__legend-row .oo-ui-popupWidget-popup {
z-index: 3; /* the "hitbox" can block pointer events to <li>s */
}
q95w756sq1mpcu8xs3gisj4seiymmcu
မဳဒဳယာဝဳကဳ:Gadget-zhDialMap
8
295727
396555
2026-06-07T18:59:59Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "မဗပေၚ်စုတ်တၚ်သွဟ်နကဵု [[Module:zh-dial-map|ဗီုတိဍာ်အရေဝ်ဒေသကြုက်]] {{gadget tag|လ္တူမစပံၚ်ရပ်စပ်}}"
396555
wikitext
text/x-wiki
မဗပေၚ်စုတ်တၚ်သွဟ်နကဵု [[Module:zh-dial-map|ဗီုတိဍာ်အရေဝ်ဒေသကြုက်]] {{gadget tag|လ္တူမစပံၚ်ရပ်စပ်}}
6wrz4o0sm2422ikh3f1qte6e82dza2r
မဝ်ဂျူ:zh-dial-map
828
295728
396556
2026-06-07T19:00:59Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "local export = {} local m_links = require("Module:links") local lang = require("Module:languages").getByCode("zh") local variety_data = require("Module:zh/data/dial") local maxn = table.maxn or require("Module:table").maxIndex -- maxn is deprecated; maxIndex is not strictly equivalent, but equivalent enough here local dots = { "e72c27", "2589e7", "96e725", "8328e7", "ece65a", "28dde7", "e727dc", "e78322", "20e680"..."
396556
Scribunto
text/plain
local export = {}
local m_links = require("Module:links")
local lang = require("Module:languages").getByCode("zh")
local variety_data = require("Module:zh/data/dial")
local maxn = table.maxn or require("Module:table").maxIndex -- maxn is deprecated; maxIndex is not strictly equivalent, but equivalent enough here
local dots = {
"e72c27", "2589e7", "96e725", "8328e7", "ece65a",
"28dde7", "e727dc", "e78322", "20e680", "3b49d1",
"9b5b5a", "59869c", "809c59", "79599c", "9d9a5a",
"52a1a3", "9c5995", "9c7a59", "5a9b7a", "59619c",
"da716c", "6ca5da", "8fda6c", "a16dda", "e1d08a",
"6dd3da", "da6dd4", "dca875", "84d9ad", "726dda",
"783017", "164f73", "4d7313", "411777", "766312",
"167364", "7e187d", "744116", "16742b", "161f7a",
"e1a4a3", "a2c3e1", "bedc93", "bb9ade", "dcd993",
"93d8dc", "dc93d8", "dcb793", "93dcb6", "9395dc",
"b74945", "538bbd", "87b644", "844fbd", "cdc741",
"4db6bc", "b644af", "b67c44", "44b67a", "4448b6",
"a06d6d", "6f88a0", "8ba16d", "876ea0", "b0ae84",
"6d9ea1", "a16d9e", "a1866d", "6da186", "6d6da1",
"f18383", "83bbf1", "c3f183", "bb83f1", "f1eb82",
"83ebf1", "f282e9", "f1b883", "83f1b8", "8286f2",
}
local grey = "ccccbf"
local elements = {}
elements.map_header = function(text)
return tostring(
mw.html.create( "h2" )
:wikitext( text )
:done()
)
end
elements.map = function(points, legend)
return tostring(
mw.html.create( "div" )
:addClass( "thumb" )
:addClass( "zh-dial-map__container" )
:tag( "div" )
:addClass( "thumbinner" )
:addClass( "zh-dial-map__frame" )
-- these styles can't be moved to the .css file because .thumbinner has its own definitions
:css( "overflow", "auto" )
:css( "font-size", "1px" ) -- related to the positioning of the dots
:tag( "div" )
:addClass( "zh-dial-map__map" )
:wikitext( '[[File:Chinese dialectal variation location map.svg|1200px|link=]]' )
:wikitext( points )
:done()
:done()
:tag( "div" )
:addClass( "zh-dial-map__legend" )
:wikitext( legend )
:done()
:done()
)
end
elements.dot = function(d, loc_name, loc_info, top, left, colour)
local term = mw.ustring.gsub(d.term, "[%[%]]", "")
return tostring(
mw.html.create( "span" )
:attr( "data-word", term )
:attr( "data-location-en", loc_name )
:attr( "data-location-zh", loc_info.chinese )
:attr( "data-group", loc_info.group )
:addClass( "zh-dial-map__dot" )
:addClass( (colour == grey and "zh-dial-map__dot-other" or nil) )
:css( "top", top .. "em" ) -- The size of 1 em is tied to the font-size of .zh-dial-map__frame
:css( "left", left .. "em" )
:css( "background-color", "#" .. colour )
:attr( "title", loc_name .. " (" .. loc_info.chinese .. ") " .. loc_info.group .. ": " .. term )
-- without text in the <span> it seems like the wikitext render discards the whole <span>???
-- and makes a link with no text at all??????
:wikitext( " " )
:done()
)
end
elements.legend = function(d, colour, appendedText)
return tostring(
mw.html.create( "div" )
:attr( "data-word", (d and d.term or "other") )
:addClass( "zh-dial-map__legend-row" )
:addClass( (colour == grey and "zh-dial-map__legend-row-other" or nil) )
:tag( "span" )
:addClass( "zh-dial-map__legend-row-dot" )
:css( "background-color", "#" .. colour )
:wikitext( " " ) -- please let me make empty spans
:done()
:wikitext( appendedText )
:done()
)
end
function export.make_map(frame)
local syn_data = require("Module:zh/data/dial-syn/" .. frame.args[1]).list
local prelim_data, data, points, legend = {}, {}, {}, {}
local loc, cur = {}, {}
for i = 1,#variety_data,1 do
loc = variety_data[i]
cur = syn_data[loc.key] or {""}
if cur[1] ~= "" and loc.lat then
for _, term in ipairs(cur) do
term = mw.text.split(term, ":")
if term[2] ~= "mT" and term[2] ~= "GT" then
term = term[1]
if prelim_data[term] then
prelim_data[term].count = prelim_data[term].count + 1
table.insert(prelim_data[term].locations, loc)
else
prelim_data[term] = { count = 1, locations = { loc } }
end
end
end
end
end
for term, term_data in pairs(prelim_data) do
table.insert(data, { term = term, count = term_data.count, locations = term_data.locations })
end
table.sort(data, function(first, second) return first.count > second.count end)
local prev_count, num = data[1].count, 1
local greyed, greyed_count = false, 0
local num_syn = maxn(data)
--when need to have greyed points, grey out all synonyms with only 1 dialect point
local have_greyed = num_syn > 80 and data[num_syn].count == 1
for _, d in ipairs(data) do
greyed = greyed or (have_greyed and d.count == 1) or (num > 70 and d.count ~= prev_count) or num > 80
local colour = greyed and grey or dots[num]
for _, loc in ipairs(d.locations) do
local top_offset, left_offset = 0, 0
if #syn_data[loc.key] > 1 then
top_offset = math.random(-300, 300) / 100
left_offset = math.random(-300, 300) / 100
end
local top = ((55 - loc.lat) * 89520 / 5593) + top_offset --((55 - loc.lat) * 1200 * 746 / 799 / 70) + top_offset
local left = ((loc.long - 70) * 16) + left_offset --((loc.long - 70) * 1200 / 75) + left_offset
local loc_name = mw.ustring.gsub(loc.english or loc.key, "%((.*)%)$", "- %1")
table.insert(points, '[[' .. d.term .. '|' .. elements.dot(d, loc_name, loc, top, left, colour) .. ']]')
end
if greyed then
greyed_count = greyed_count + d.count
else
local link = m_links.full_link({
lang = lang,
term = mw.ustring.gsub(d.term, "(.+)_[1-9]", "%1"),
alt = mw.ustring.gsub(d.term, "(.+)_([1-9])", "%1<sub>%2</sub>"),
tr = "-",
})
table.insert(legend, elements.legend(d, colour, link .. " (" .. d.count .. ")"))
end
prev_count = d.count
num = num + 1
end
if greyed_count > 0 then
table.insert(legend, elements.legend(false, grey, "other terms (" .. greyed_count .. ")"))
end
local map_header = elements.map_header(
"Map of Chinese dialectal equivalents for " .. m_links.full_link(
{
lang = lang,
term = mw.ustring.gsub(frame.args[1], "%-.*", ""),
gloss = syn_data["meaning"],
tr = '-'
}
)
)
local note = "\n''Note: This map may not be well-supported on mobile devices. Please view this page on a computer.''"
local map = elements.map(table.concat(points), table.concat(legend))
return map_header .. note .. map
end
return export
d1axz4qsihyovxstzzpvictnfrf2fl0
မဝ်ဂျူ:zh-dial-map/doc
828
295729
396557
2026-06-07T19:02:15Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "Module for generating maps showing the distribution of Chinese dialectal equivalents; the visual companion to the table {{temp|zh-dial}}. See {{temp|zh-dial-map}}. <includeonly> [[ကဏ္ဍ:မဝ်ဂျူကြုက်ဂမၠိုၚ်]] </includeonly>"
396557
wikitext
text/x-wiki
Module for generating maps showing the distribution of Chinese dialectal equivalents; the visual companion to the table {{temp|zh-dial}}. See {{temp|zh-dial-map}}.
<includeonly>
[[ကဏ္ဍ:မဝ်ဂျူကြုက်ဂမၠိုၚ်]]
</includeonly>
prjj79onmfkka8j7l5l656itmkvw726
မဳဒဳယာဝဳကဳ:Gadget-catfix.js
8
295730
396558
2026-06-07T19:08:30Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "// <nowiki> // Makes tweaks to links in language-specific categories, where we would normally // be wrapping them in {{l}}. for (let catfix of document.getElementsByClassName("catfix")) { // Catfix looks something like this: // <ul class="catfix" data-anchor="Russian"><span class="Cyrl" lang="ru"></span></ul> // The outer element records the anchor that we need to use, while the child element is used as the wrappe..."
396558
javascript
text/javascript
// <nowiki>
// Makes tweaks to links in language-specific categories, where we would normally
// be wrapping them in {{l}}.
for (let catfix of document.getElementsByClassName("catfix")) {
// Catfix looks something like this:
// <ul class="catfix" data-anchor="Russian"><span class="Cyrl" lang="ru"></span></ul>
// The outer element records the anchor that we need to use, while the child element is used as the wrapper.
// Applies a language-specific wrapper to the link based on the information from `catfix`.
// If the link is something like `Appendix:Toki Pona/nasin`,
// we need to wrap it like this: `Appendix:Toki Pona/<span prop="blah blah">nasin</span>`.
// Otherwise, put the span around the link and add an anchor.
function processLink(link) {
if (link.catfixed) return;
let wrapper = catfix.firstElementChild.cloneNode();
let pageName = link.textContent;
let ns = (new mw.Title(pageName)).getNamespaceId();
if (ns === 0) { // Mainspace
link.parentElement.append(wrapper);
wrapper.append(link);
link.hash = catfix.dataset.anchor;
} else if (ns === 100 && pageName.includes("/")) { // Appendix
let [prefix, ...title] = pageName.split("/");
// only do Appendix:ABC/def if ABC matches the catfix language
if (prefix.replace('အဆက်လက္ကရဴ:', '') === catfix.dataset.anchor.replaceAll('_', ' ')) {
title = title.join('/');
wrapper.textContent = title;
link.innerHTML = "";
link.append(prefix + "/", wrapper);
}
} else if (ns === 118) { // Reconstruction
// experiment: try replacing Reconstruction:XYZ/ with * for a cleaner layout
// (but only if XYZ matches the catfix's language!)
let [prefix, ...title] = pageName.split("/");
if (prefix.replace('ဗီုပြၚ်သိုၚ်တၟိ:', '') === catfix.dataset.anchor.replaceAll('_', ' ')) {
title = title.join('/');
link.textContent = "*" + title;
link.parentElement.append(wrapper);
wrapper.append(link);
}
}
link.catfixed = true;
}
// There are currently two types of catfixes: those which apply to an entire category page,
// And those applying to a single category tree.
// This is a bit of a hack to tell the two kinds apart.
let categoryTree = catfix.nextElementSibling;
if (
categoryTree &&
(categoryTree.matches(".CategoryTreeTag") || categoryTree.querySelector(".CategoryTreeTag"))
) {
// Skip known empty CategoryTrees
var children = categoryTree.querySelector(".CategoryTreeChildren");
if (children) {
// Process category tree elements already present on the page.
categoryTree.querySelectorAll(".CategoryTreeChildren a").forEach(processLink);
// Also deal with any category tree data loaded dynamically using a MutationObserver.
(new MutationObserver(events => events
.flatMap(event => [...event.addedNodes])
.flatMap(node => [...node.querySelectorAll("a")])
.forEach(processLink)
)).observe(children, {
childList: true,
subtree: true
});
}
} else {
document.querySelectorAll(".mw-category li > a").forEach(processLink);
}
}
// </nowiki>
npbgfxijx74nh5x49rm7qoxkw3cwd3i
မဳဒဳယာဝဳကဳ:Gadget-WiktSidebarTranslation.js
8
295731
396559
2026-06-07T19:10:02Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "$(function(){ $("#p-lang .interlanguage-link a").each(function(){ var langnameMatch = $(this).attr("title").match(/(.*) – (.*)/); var langname = $(this).attr("title"); //some wiktionaries like afrikaans have '' as their main page if (langnameMatch && langnameMatch.length >= 3) langname = langnameMatch[2]; $(this).text(langname) .attr("lang", "mnw");//this line breaks compact languages }); $("#p-lang..."
396559
javascript
text/javascript
$(function(){
$("#p-lang .interlanguage-link a").each(function(){
var langnameMatch = $(this).attr("title").match(/(.*) – (.*)/);
var langname = $(this).attr("title"); //some wiktionaries like afrikaans have '' as their main page
if (langnameMatch && langnameMatch.length >= 3) langname = langnameMatch[2];
$(this).text(langname)
.attr("lang", "mnw");//this line breaks compact languages
});
$("#p-lang .interlanguage-link").sort(function(lia, lib){
return $(lia).children().first().text() < $(lib).children().first().text() ? -1 : 1;
}).appendTo("#p-lang>div>ul");
});
lbn64k93t37urze0z45otblg3b8nb54
မဳဒဳယာဝဳကဳ:Gadget-WiktGadgetPrefs.js
8
295732
396560
2026-06-07T19:12:22Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "/* global mw */ // <nowiki> /*jshint shadow:true, undef:true, latedef:true, unused:true, esversion:3 */ /** * WiktGadgetPrefs -- developed by [[wikt:en:User:Surjection]] * * *********************************************************************** * Documentation for gadget authors: * Example usage (remember to add ext.gadget.WiktGadgetPrefs as a dependency): const preferences = mw.wiktGadgetPrefs.get("gadgetfoo..."
396560
javascript
text/javascript
/* global mw */
// <nowiki>
/*jshint shadow:true, undef:true, latedef:true, unused:true, esversion:3 */
/**
* WiktGadgetPrefs -- developed by [[wikt:en:User:Surjection]]
*
* ***********************************************************************
* Documentation for gadget authors:
* Example usage (remember to add ext.gadget.WiktGadgetPrefs as a dependency):
const preferences = mw.wiktGadgetPrefs.get("gadgetfoobar",
{
"label": {
"en": "Foobar Gadget"
}
},
{
"frobulate": {
"type": "boolean",
"default": false,
"label": {
"en": "Whether this gadget should frobulate"
}
}
}
);
const shouldFrobulate = preferences.frobulate;
// strenum example
const preferences = mw.wiktGadgetPrefs.get("gadgetfoobar",
{
"label": {
"en": "Foobar Gadget"
}
},
{
"frobulate": {
"type": "strenum",
"default": "yes",
"label": {
"en": "Whether this gadget should frobulate"
},
"choices": [
"yes",
"no",
"maybe"
],
"choiceLabels": {
"en": {
"yes": "Yes!",
"no": "No!!",
"maybe": "Maybe?"
}
}
}
}
);
const shouldFrobulate = preferences.frobulate;
***********************************************************************
*
* Dependencies needed: mw.user, user.options
*/
(function() {
"use strict";
/**
* @typedef WiktGadgetOptions
* - Represents gadget options.
* @property {{[languageCode: string]: string}} [label]
* - The label to display on the preferences page for this
* gadget/namespace. The language code should be a MediaWiki
* language code, e.g. "en" for English.
*/
/**
* @typedef WiktGadgetPreference
* - Represents a single preference.
* @property {{"boolean"|"integer"|"string"|"strenum"|"object"}} type
* - Must be one of the following:
* - "boolean" for boolean values, either true or false.
* - "integer" for an integer value.
* - "string" for a string value.
* - "strenum" for a string value that must be one of the options.
* - "object" for an arbitrary JSON value, including null.
* No type checks are done.
* @property {any} default
* - The default value for this preference if missing, or if
* the value is invalid.
* - The developer is responsible for ensuring that this value is
* given, and that it meets the preconditions defined below.
* @property {string[]} [choices]
* - If the type is "strenum", then this must be an array of
* allowed options. If the value is not in this list, the default
* value will be returned.
* @property {number} [minimum]
* - If the type is "integer", this is the minimum allowed value.
* @property {number} [maximum]
* - If the type is "integer", this is the maximum allowed value.
* @property {number} [maximumLength]
* - If the type is "string", this is the maximum allowed length.
* @property {{[languageCode: string]: string}} [label]
* - The label to display on the preferences page for this
* preference. The language code should be a MediaWiki
* language code, e.g. "en" for English.
* @property {{[languageCode: string]: {[choice: string]: string}}} [choiceLabels]
* - The label to display on the preferences page for the choices
* of this preference (if it is an enum type). The language code
* should be a MediaWiki language code, e.g. "en" for English.
*/
var wiktGadgetPrefs__registeredPrefs = {};
var wiktGadgetPrefs__error = (Error ?
function (message) {
return new Error(message);
} :
function (message) {
return message;
});
var wiktGadgetPrefs__asciify = function (text) {
return text.replace(/[^A-Za-z0-9-]/g, function (match) {
if (match == "_") {
return "__";
} else {
return "_" + match.charCodeAt(0).toString(16) + "_";
}
});
};
var wiktGadgetPrefs__namespace_to_key = function (namespace) {
return "wiktgadgetprefs-" + wiktGadgetPrefs__asciify(namespace);
};
var wiktGadgetPrefs__is_integer = function (num) {
return num == (num | 0);
};
var wiktGadgetPrefs__parse_one = function (value, preference, key) {
var defaultValue = preference["default"];
switch (preference.type) {
case "boolean":
// data type
if (typeof value != "boolean") return defaultValue;
return value;
case "integer":
// data type
if (typeof value != "number") return defaultValue;
// integer check
if (!wiktGadgetPrefs__is_integer(value)) return defaultValue;
// constraints
if ((preference.minimum != null && value < preference.minimum) ||
(preference.maximum != null && value > preference.maximum))
return defaultValue;
return value;
case "string":
// data type
if (typeof value != "string") return defaultValue;
// constraints
if (preference.maximumLength != null && value.length > preference.maximumLength)
return defaultValue;
return value;
case "strenum":
// data type
if (typeof value != "string") return defaultValue;
// constraints
if (preference.choices.indexOf(value) < 0)
return defaultValue;
return value;
case "object":
if (typeof value == "undefined") return defaultValue;
return value;
}
throw wiktGadgetPrefs__error("Invalid preference specification '" + key + "'");
};
var wiktGadgetPrefs__parse_all = function (values, preferences) {
var parsed = {};
if (!values) values = {};
for (var key in preferences) {
if (preferences.hasOwnProperty(key)) {
parsed[key] = wiktGadgetPrefs__parse_one(values[key], preferences[key], key);
}
}
return parsed;
};
var wiktGadgetPrefs__resolve = function (namespaceKey, preferences) {
var resolved;
var mwOptions;
try {
mwOptions = mw.user.options;
} catch (e) {
}
if (!resolved && window.localStorage && window.localStorage.getItem("anon" + namespaceKey) != null) {
try {
resolved = JSON.parse(window.localStorage.getItem("anon" + namespaceKey));
} catch (e) { }
}
if (!resolved && mwOptions && mwOptions.exists("userjs-" + namespaceKey)) {
try {
resolved = JSON.parse(mwOptions.get("userjs-" + namespaceKey));
} catch (e) { }
}
return wiktGadgetPrefs__parse_all(resolved || {}, preferences);
};
/**
* Resolve preferences.
*
* Note that there is a maximum length imposed by MediaWiki
* on the total size of the preferences for each namespace.
*
* @param {string} namespace
* - An arbitrary string. This should be unique to each gadget.
* @param {WiktGadgetOptions} options
* - Options.
* @param {{[key: string]: WiktGadgetPreference}} preferences
* - Requested preferences, indexed by name.
* @returns {{[key: string]: any}}
*/
var wiktGadgetPrefs_get = function (namespace, options, preferences) {
if (wiktGadgetPrefs__registeredPrefs[namespace]) {
throw wiktGadgetPrefs__error("wiktGadgetPrefs_get has already been called with the namespace '" + namespace + "'.");
}
wiktGadgetPrefs__registeredPrefs[namespace] = {
options: options,
preferences: preferences
};
var namespaceKey = wiktGadgetPrefs__namespace_to_key(namespace);
if (namespaceKey.length >= 240) {
throw wiktGadgetPrefs__error("Namespace key '" + namespace + "' is too long. " +
"Please prefer ASCII characters A-Z, a-z, 0-9 and the hyphen, " +
"as other characters need to be converted, which increases their size.");
}
return wiktGadgetPrefs__resolve(namespaceKey, preferences);
};
mw.wiktGadgetPrefs = {};
mw.wiktGadgetPrefs.get = wiktGadgetPrefs_get;
// Only to be used by the preferences page.
mw.wiktGadgetPrefs._namespaceToKey = wiktGadgetPrefs__namespace_to_key;
mw.wiktGadgetPrefs._resolve = wiktGadgetPrefs__resolve;
mw.wiktGadgetPrefs._registeredPrefs = wiktGadgetPrefs__registeredPrefs;
})();
// </nowiki>
o4n21hnlfqc4ly11ntzob901aoo3w3p
မဳဒဳယာဝဳကဳ:Gadget-WiktGadgetPrefs.js/documentation
8
295733
396561
2026-06-07T19:13:16Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "{{documentation subpage}} Provides a framework for gadgets to store additional preferences, both for registered and anonymous users. [[MediaWiki:Gadget-WiktGadgetPrefsPage.js]] implements [[Wiktionary:Gadget preferences]], which is where these preferences can be modified. See also: [[Special:Gadgets]]. <includeonly> [[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]..."
396561
wikitext
text/x-wiki
{{documentation subpage}}
Provides a framework for gadgets to store additional preferences, both for registered and anonymous users.
[[MediaWiki:Gadget-WiktGadgetPrefsPage.js]] implements [[Wiktionary:Gadget preferences]], which is where these preferences can be modified.
See also: [[Special:Gadgets]].
<includeonly>
[[ကဏ္ဍ:ဂေက်ဂျေက်ဝိတ်ရှေန်နရဳဂမၠိုၚ်]]
</includeonly>
4fj59phs5ioldh1w94s33ety6tqedn0
မဳဒဳယာဝဳကဳ:Gadget-WiktGadgetPrefsPage.js
8
295734
396562
2026-06-07T19:15:08Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "// <nowiki> /*jshint shadow:true, undef:true, latedef:true, unused:true, esversion:6 */ /* global mw, $, OO */ /* *********************************************************************** * For gadget authors: * Please read the documentation at [[MediaWiki:Gadget-WiktGadgetPrefs.js]]. *********************************************************************** */ /* TODO: - support migrating settings from localStorage..."
396562
javascript
text/javascript
// <nowiki>
/*jshint shadow:true, undef:true, latedef:true, unused:true, esversion:6 */
/* global mw, $, OO */
/* ***********************************************************************
* For gadget authors:
* Please read the documentation at [[MediaWiki:Gadget-WiktGadgetPrefs.js]].
*********************************************************************** */
/* TODO:
- support migrating settings from localStorage
*/
(function () {
"use strict";
if (!document.getElementById("wikt-gadget-prefs-go-here")) return;
const isLoggedIn = mw.config.get("wgUserId") != null;
const userLanguage = mw.config.get("wgUserLanguage");
const projectLanguage = "mnw";
const seenNamespaces = new Set();
const strings = {
"mnw": {
wiktLangCode: "mnw",
noGadgets: "None of your currently enabled gadgets have any preferences.",
savedOk: "Saved successfully! Reloading...",
saveError: "Failed to save preferences.",
saveAndReload: "Save and reload",
resetOne: "Reset defaults for this gadget",
resetAll: "Reset defaults for all gadgets",
resetAllConfirm: "Are you sure you want to reset defaults for all gadgets?",
}
};
const resolveString = (strings, stringKey, defaultValue) => {
try {
if (defaultValue == null && stringKey)
defaultValue = stringKey;
if (strings == null)
return defaultValue;
// try to use user language first
if (strings[userLanguage] != null) {
if (stringKey == null)
return strings[userLanguage];
else if (strings[userLanguage][stringKey] != null)
return strings[userLanguage][stringKey];
}
// fall back to project language
if (strings[projectLanguage] != null) {
if (stringKey == null)
return strings[projectLanguage];
else if (strings[projectLanguage][stringKey] != null)
return strings[projectLanguage][stringKey];
}
return defaultValue;
} catch (_) {
// invalid strings object, etc.
return defaultValue;
}
};
let unsavedChanges = null;
const modifications = {};
const applyModification = (namespace, preferenceKey, preference, newValue) => {
if (unsavedChanges) unsavedChanges();
if (!modifications[namespace])
modifications[namespace] = {};
modifications[namespace][preferenceKey] = newValue;
};
const controlRefs = {};
const buildControlFor = (namespace, preferenceKey, preference, resolvedValue) => {
const label = resolveString(preference.label, null, preferenceKey);
if (!controlRefs[namespace])
controlRefs[namespace] = {};
switch (preference.type) {
case "boolean":
{
const checkBox = new OO.ui.CheckboxInputWidget({
selected: !!resolvedValue
});
controlRefs[namespace][preferenceKey] = {
setValue: (value) => checkBox.setSelected(value)
};
checkBox.on("change", function () {
const checked = checkBox.isSelected();
applyModification(namespace, preferenceKey, preference, checked);
});
const field = new OO.ui.FieldLayout(checkBox, {
label: $('<span></span>').text(label),
align: "inline"
});
return field.$element;
}
case "integer":
{
const prefs = {
input: { value: resolvedValue }
};
if (preference.minimum != null)
prefs.min = preference.minimum;
if (preference.maximum != null)
prefs.max = preference.maximum;
const numericInput = new OO.ui.NumberInputWidget(prefs);
controlRefs[namespace][preferenceKey] = {
setValue: (value) => numericInput.setValue(value)
};
numericInput.on("change", function () {
const newValue = Number(numericInput.getValue());
if (!Number.isNaN(newValue) &&
(preference.minimum == null || newValue >= preference.minimum) &&
(preference.maximum == null || newValue <= preference.maximum)) {
applyModification(namespace, preferenceKey, preference, newValue);
}
});
const field = new OO.ui.FieldLayout(numericInput, {
label: $('<span></span>').text(label),
align: "inline"
});
return field.$element;
}
case "string":
{
const prefs = {
value: resolvedValue
};
if (preference.maximumLength != null)
prefs.maxLength = preference.maximumLength;
const textInput = new OO.ui.TextInputWidget(prefs);
controlRefs[namespace][preferenceKey] = {
setValue: (value) => textInput.setValue(value)
};
textInput.on("change", function () {
const newValue = textInput.getValue();
if (preference.maximumLength == null || newValue.length <= preference.maximumLength) {
applyModification(namespace, preferenceKey, preference, newValue);
}
});
const field = new OO.ui.FieldLayout(textInput, {
label: $('<span></span>').text(label),
align: "inline"
});
return field.$element;
}
case "strenum":
{
const prefs = {
value: resolvedValue,
options: (preference.choices || []).map((choice) => ({
data: choice,
label: resolveString(preference.choiceLabels, choice, choice)
}))
};
const dropdown = new OO.ui.DropdownInputWidget(prefs);
controlRefs[namespace][preferenceKey] = {
setValue: (value) => dropdown.setValue(value)
};
dropdown.on("change", function () {
const newValue = dropdown.getValue();
if (preference.choices.includes(newValue)) {
applyModification(namespace, preferenceKey, preference, newValue);
}
});
dropdown.setValue(resolvedValue);
const field = new OO.ui.FieldLayout(dropdown, {
label: $('<span></span>').text(label),
align: "inline"
});
return field.$element;
}
case "object":
{
const prefs = {
value: JSON.stringify(resolvedValue, null, 4)
};
const textInput = new OO.ui.MultilineTextInputWidget(prefs);
controlRefs[namespace][preferenceKey] = {
setValue: (value) => textInput.setValue(JSON.stringify(value, null, 4))
};
textInput.on("change", function () {
const newValue = textInput.getValue();
try {
const jsonObject = JSON.parse(newValue);
applyModification(namespace, preferenceKey, preference, jsonObject);
textInput.setValidityFlag(true);
} catch (e) {
textInput.setValidityFlag(false);
}
});
const field = new OO.ui.FieldLayout(textInput, {
label: $('<span></span>').text(label),
align: "inline"
});
return field.$element;
}
default:
console.error(`Failed to build preference control for ${namespace}.${preferenceKey}`);
return null;
}
};
const addControlsFor = (controls, seenNamespaces, namespace, options, preferences, resolvedValues) => {
if (seenNamespaces.has(namespace)) return;
seenNamespaces.add(namespace);
// no section if there are no preferences
if (!Object.keys(preferences).length) return;
// create wrapper and heading
const gadgetControls = $('<div>');
controls.append(gadgetControls);
gadgetControls.append($("<h2>").text(resolveString(options.label, null, namespace)));
// add reset button
const resetButton = new OO.ui.ButtonWidget({
label: resolveString(strings, "resetOne"),
flags: ["destructive"],
framed: false
});
resetButton.on("click", () => {
for (const [preferenceKey, preference] of Object.entries(preferences)) {
applyModification(namespace, preferenceKey, preference, preference.default);
const control = controlRefs[namespace][preferenceKey];
if (control != null)
control.setValue(preference.default);
}
});
gadgetControls.append(resetButton.$element);
// add control for each preference
for (const [preferenceKey, preference] of Object.entries(preferences)) {
const control = buildControlFor(namespace, preferenceKey, preference, resolvedValues[preferenceKey]);
if (control != null)
gadgetControls.append(control);
}
};
const makeSaveButton = (saveCallback) => {
const button = new OO.ui.ButtonWidget({
label: resolveString(strings, "saveAndReload"),
flags: ["primary", "progressive"]
});
button.on("click", saveCallback);
return button;
};
const makeResetAllButton = (resetCallback) => {
const button = new OO.ui.ButtonWidget({
label: resolveString(strings, "resetAll"),
flags: ["primary", "destructive"]
});
button.on("click", resetCallback);
return button;
};
$.when(mw.loader.using(["mediawiki.util", "mediawiki.user", "ext.gadget.WiktGadgetPrefs"], $.ready).done(function() {
const originalControls = $("#wikt-gadget-prefs-go-here");
if (!originalControls.length) return;
const controls = $('<div id="wikt-gadget-prefs-controls">');
originalControls.replaceWith(controls);
const namespaceToKey = mw.wiktGadgetPrefs._namespaceToKey;
const registeredPrefs = mw.wiktGadgetPrefs._registeredPrefs;
const resolver = mw.wiktGadgetPrefs._resolve;
const doSave = (callback) => {
const optionChanges = {};
for (const [namespace, namespaceChanges] of Object.entries(modifications)) {
// clone and apply
const resolvedValues = {};
const namespaceKey = namespaceToKey(namespace);
const preferences = resolver(namespaceKey, registeredPrefs[namespace].preferences);
for (const [key, value] of Object.entries(preferences))
resolvedValues[key] = value;
for (const [key, value] of Object.entries(namespaceChanges))
resolvedValues[key] = value;
optionChanges[namespaceKey] = JSON.stringify(resolvedValues, null, null);
}
if (isLoggedIn) {
const apiOptionChanges = {};
for (const [key, value] of Object.entries(optionChanges))
apiOptionChanges[`userjs-${key}`] = value;
// use saveOptions for logged-in users
new mw.Api().saveOptions(apiOptionChanges).then(function() {
callback(true);
}, function() {
callback(false);
});
} else {
// use localStorage for anonymous users
for (const [key, value] of Object.entries(modifications)) {
try {
window.localStorage.setItem(`anon${namespaceToKey(key)}`, JSON.stringify(value, null, null));
} catch (e) {
callback(false);
return;
}
}
callback(true);
}
};
const trySave = () => {
doSave((success) => {
if (success) {
mw.notification.notify(resolveString(strings, "savedOk"));
setTimeout(() => location.reload(), 1500);
} else {
mw.notification.notify(resolveString(strings, "saveError"));
}
});
};
const confirmResetAll = () => {
OO.ui.confirm(resolveString(strings, "resetAllConfirm")).done((confirmed) => {
if (!confirmed) return;
if (isLoggedIn) {
const prefix = `userjs-${namespaceToKey("")}`;
const apiOptionChanges = {};
for (const optionKey of Object.keys(mw.user.options.values)) {
if (optionKey.startsWith(prefix))
apiOptionChanges[optionKey] = null;
}
new mw.Api().saveOptions(apiOptionChanges).then(function() {
location.reload();
}, function() {
mw.notification.notify(resolveString(strings, "saveError"));
});
} else {
const prefix = `anon${namespaceToKey("")}`;
// use localStorage for anonymous users
for (const key of Object.keys(window.localStorage)) {
if (key.startsWith(prefix)) {
try {
window.localStorage.removeItem(key);
} catch (_) {
}
}
}
location.reload();
}
});
};
const buttons = $('<div>');
buttons.css("margin-top", "1em");
buttons.insertAfter(controls);
// add save button
const saveButton = makeSaveButton(trySave);
saveButton.setDisabled(true);
buttons.append(saveButton.$element);
unsavedChanges = () => saveButton.setDisabled(false);
// add reset button
const resetAllButton = makeResetAllButton(confirmResetAll);
buttons.append(resetAllButton.$element);
// placeholder text
mw.util.addCSS(`
#wikt-gadget-prefs-controls:empty::after {
content: "${resolveString(strings, 'noGadgets').replace('"', '\"')}";
}
`);
// bit of a hook, but we hook onto the WiktGadgetPrefs get function
// to capture all registrations
const original_wiktGadgetPrefs_get = mw.wiktGadgetPrefs.get;
mw.wiktGadgetPrefs.get = function (namespace, options, preferences) {
const returnValue = original_wiktGadgetPrefs_get(namespace, options, preferences);
const resolvedValues = resolver(namespaceToKey(namespace), preferences);
addControlsFor(controls, seenNamespaces, namespace, options, preferences, resolvedValues);
return returnValue;
};
// add gadgets that have already registered
for (const namespace of Object.keys(registeredPrefs)) {
const { options, preferences } = registeredPrefs[namespace];
try {
const resolvedValues = resolver(namespaceToKey(namespace), preferences);
addControlsFor(controls, seenNamespaces, namespace, options, preferences, resolvedValues);
} catch (e) {
console.error(e);
}
}
}));
})();
// </nowiki>
rmp2tyogny5b5yvboijklzhhioh6s2e
မဳဒဳယာဝဳကဳ:Gadget-UTCLiveClock.css
8
295735
396563
2026-06-07T19:16:12Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "/** * Explicitly set width of the UTC-clock list element, so that we can use a * hidden peer gadget to add space where the clock would go before it loads. */ .skin-vector #utcdate { width: 6em; /* * The default margin-left is 0.75em, but set it again here explicitly, so * we can be sure of the exact width. */ margin-left: 0.75em; } .skin-monobook #utcdate { width: 6.279em; display: inline-block; /* We..."
396563
css
text/css
/**
* Explicitly set width of the UTC-clock list element, so that we can use a
* hidden peer gadget to add space where the clock would go before it loads.
*/
.skin-vector #utcdate {
width: 6em;
/*
* The default margin-left is 0.75em, but set it again here explicitly, so
* we can be sure of the exact width.
*/
margin-left: 0.75em;
}
.skin-monobook #utcdate {
width: 6.279em;
display: inline-block; /* We need this for the width property to work */
/*
* The default margin-left is 1em, but set it again here explicitly, so
* we can be sure of the exact width.
*/
margin-left: 1em;
}
a9b5uwepu2o3etikfxjo5stx2yqmdz4
မဳဒဳယာဝဳကဳ:Gadget-UTCLiveClock-pagestyles.css
8
295736
396564
2026-06-07T19:17:09Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "/** * This is loaded as a hidden peer gadget of UTCLiveClock. * Before UTCLiveClock has loaded, it adds space where the clock would go, * so that the personal toolbar does not "jump". */ /* * In Vector, by default the li elements in the p-personal bar have a font-size * of 0.75em. Duplicate that here so that we can be sure of the proper factor to * multiply the ul element's margin-right by. And just in case,..."
396564
css
text/css
/**
* This is loaded as a hidden peer gadget of UTCLiveClock.
* Before UTCLiveClock has loaded, it adds space where the clock would go,
* so that the personal toolbar does not "jump".
*/
/*
* In Vector, by default the li elements in the p-personal bar have a font-size
* of 0.75em. Duplicate that here so that we can be sure of the proper factor to
* multiply the ul element's margin-right by. And just in case, override any
* possible changes to the font size in Monobook too, although by default there
* is no change in font size between the ul and li elements.
*/
.client-js > body.skin-vector #p-personal li {
font-size: 0.75em;
}
.client-js > body.skin-monobook #p-personal li {
font-size: 1em;
}
/*
* Reserve space for the clock gadget after the end of the p-personal ul
* element.
*/
.client-js > body.skin-vector #p-personal ul {
/*
* The clock width plus its left margin, multiplied by the relative font
* size: (6em + 0.75em) * 0.75
*/
margin-right: 5.0625em;
}
.client-js > body.skin-monobook #p-personal ul {
/*
* Clock width: 6.279em
* Left margin: 1em
* Space separator after "log out" link: 0.321em (found through trial and
* error)
* Total space: 6.279em + 1em + 0.321em
*/
margin-right: 7.6em;
}
5z8u3sry5p8f5l96yrqrxf2j4uun111
မဳဒဳယာဝဳကဳ:Gadget-TreeDescs-pagestyles.css
8
295737
396565
2026-06-07T19:18:55Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "/* code forked from [[w:Template:Tree list/styles.css]] */ .treeview ul { padding: 0; margin: 0; } .treeview li li { margin: 0; list-style-type: none; list-style-image: none; background: url("https://upload.wikimedia.org/wikipedia/commons/f/f2/Treeview-grey-line.png") no-repeat 0 -2979.5px; /* @noflip */ padding: 0 0 0 21px; text-indent: 0.3em; } .treeview li li:last-child { background-position: 0 -5969.5..."
396565
css
text/css
/* code forked from [[w:Template:Tree list/styles.css]] */
.treeview ul {
padding: 0;
margin: 0;
}
.treeview li li {
margin: 0;
list-style-type: none;
list-style-image: none;
background: url("https://upload.wikimedia.org/wikipedia/commons/f/f2/Treeview-grey-line.png") no-repeat 0 -2979.5px;
/* @noflip */
padding: 0 0 0 21px;
text-indent: 0.3em;
}
.treeview li li:last-child {
background-position: 0 -5969.5px
}
/* first line here deals with new situation after RemexHTML switch,
* where emptyline cause the first child to become the 2nd child. Such a mess
*/
.treeview li.emptyline > ul > .mw-empty-elt:first-child + .emptyline,
.treeview li.emptyline > ul > li:first-child {
background-position: 0 9px
}
/* additional code for Wiktionary */
.treeview li dd {
margin: 0;
}
.treeview li dl {
margin: 0;
padding-left: 21px;
}
.treeview li dl:not(:last-child) {
background: url("https://upload.wikimedia.org/wikipedia/commons/f/f2/Treeview-grey-line.png") no-repeat 0 -2px;
/* @noflip */
padding-left: 21px;
text-indent: 0.3em;
}
.treeview .desc-arr {
color: var(--wikt-palette-grey-9, grey);
}
.treeview li li .desc-arr {
vertical-align: top;
}
lz282qet5qzkmuxcslusjro6a7akd85
မဳဒဳယာဝဳကဳ:Gadget-TranslationAdder.js
8
295738
396566
2026-06-07T19:48:40Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "// <syntaxhighlight lang="javascript"> // <nowiki> // implicit dependencies : ext.gadget.Editor,ext.gadget.LegacyScriptsNewNode,mediawiki.cookie,ext.gadget.LanguageUtils,mediawiki.util /* jshint maxerr:1048576, strict:true, undef:true, latedef:true, sub:true */ /* global mw, importScript, importScriptURI, $, AdderWrapper, LangMetadata */ // ******** imported from [[MediaWiki:Gadget-LegacyScriptsNewNode.js]] ********..."
396566
javascript
text/javascript
// <syntaxhighlight lang="javascript">
// <nowiki>
// implicit dependencies : ext.gadget.Editor,ext.gadget.LegacyScriptsNewNode,mediawiki.cookie,ext.gadget.LanguageUtils,mediawiki.util
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, sub:true */
/* global mw, importScript, importScriptURI, $, AdderWrapper, LangMetadata */
// ******** imported from [[MediaWiki:Gadget-LegacyScriptsNewNode.js]] ********
var newNode = window.newNode = function newNode(tagname) {
var node = document.createElement(tagname);
for (var i = 1; i < arguments.length; i++) {
var argument = arguments[i];
if (typeof argument == 'string') { //Text
node.appendChild(document.createTextNode(argument));
} else if (typeof argument == 'object') {
if (argument instanceof Node) { // If it is a DOM Node
node.appendChild(argument);
} else { // Attributes (hopefully)
for (var j in argument) {
if (j === 'class') { // Classname different because...
node.className = argument[j];
} else if (j == 'style') { // Style is special
node.style.cssText = argument[j];
} else if (typeof argument[j] == 'function') { // Basic event handlers
node.addEventListener(j, argument[j], false);
} else {
node.setAttribute(j, argument[j]); //Normal attributes
}
}
}
}
}
return node;
};
// ****************************************************************************
/**
* Storage of "string" preferences.
*/
function CookiePreferences(context) {
//Repeated calls with the same context should get the same preferences object.
if (arguments.callee[context])
return arguments.callee[context];
else
arguments.callee[context] = this;
var storage = {};
var defaults = {};
/**
* Change the value of a preference and store into a cookie.
*/
this.set = function (name, value) {
if (value === null || storage[name] === value)
return;
storage[name] = value;
updateCookie();
};
/**
* Get the value of a preference from the cookie or default
*
* If the preference isn't set, return the second argument or undefined.
*/
this.get = function (name, def) {
return storage[name] || defaults[name] || def;
};
/**
* let the default for get(name) be value for this session
*/
this.setDefault = function (name, value) {
defaults[name] = value;
};
// Save storage into the cookie, and if available, the local storage.
function updateCookie() {
if ('localStorage' in window && window.localStorage) {
window.localStorage.setItem('TranslationAdderSettings', JSON.stringify(storage));
}
var value = "";
for (var name in storage) {
value += '&' + encodeURIComponent(name) + "=" + encodeURIComponent(storage[name]);
}
mw.cookie.set('preferences' + context, value, {
expires: 30
});
}
// Load storage from the cookie.
// NOTE: If you wish to update the cookie format, both loading and storing
// must continue to work for 30 days.
function updateStorage() {
var value;
if ('localStorage' in window && window.localStorage) {
value = window.localStorage.getItem('TranslationAdderSettings');
if (value) {
try {
var inLocal = JSON.parse(value);
for (var key in inLocal) {
if (inLocal.hasOwnProperty(key)) {
storage[key] = inLocal[key];
}
}
return;
} catch (e) { }
}
}
value = mw.cookie.set('preferences' + context, value, {
expires: 30
}) || '';
var pairs = value.split('&');
for (var i = 1; i < pairs.length; i++) {
var val = pairs[i].split('=');
if (storage[val[0]] === val[1])
continue;
storage[val[0]] = val[1];
}
}
//__init__
updateStorage();
updateCookie(); // update this too
}
var util = {
getVanillaIndexOf: function (str, text, pos) {
function getIndex(string, regex, pos) {
var newPos = string.substring(pos).search(regex);
return newPos === -1 ? -1 : newPos + pos;
}
if (!pos)
pos = 0;
var cpos = 0,
mpos = 0;
tpos = 0,
wpos = 0,
spos = 0;
do {
cpos = text.indexOf('<!--', pos);
mpos = getIndex(text, /\{\{\s*multitrans\s*\|/, pos);
tpos = text.indexOf('{{', pos);
wpos = text.indexOf('<nowiki>', pos);
spos = text.indexOf(str, pos);
pos = Math.min(
cpos == -1 ? Infinity : cpos,
tpos == -1 ? Infinity : tpos,
wpos == -1 ? Infinity : wpos,
spos == -1 ? Infinity : spos
);
if (pos == spos)
return pos == Infinity ? -1 : pos;
else if (pos == cpos)
pos = text.indexOf('-->', pos) + 3;
else if (pos == wpos)
pos = text.indexOf('<\/nowiki>', pos) + 9;
else if (pos == mpos)
pos = getIndex(text, /\|\s*data\s*=/, pos);
else if (pos == tpos)
pos = text.indexOf('}}', pos) + 2;
} while (pos < Infinity);
return -1;
},
validateNoWikisyntax: function (field, nonempty) {
return function (txt, error) {
if (/[\{\}#]/.test(txt))
return error("Please don't use wiki markup ({}#) in the " + field + ".");
if (nonempty && !txt)
return error("Please specify a " + field + ".");
return txt;
};
},
// pos is a position in the line containing the gloss
getWikitextGloss: function (txt, pos) {
var start = txt.lastIndexOf('\n{{trans-top', pos) + 1;
var end = txt.indexOf('\n', pos);
var line = txt.substring(start, end);
var parametersMatch = /\{\{trans-top[^\|\}]*(\|[^\{\}]+)/.exec(line);
var gloss = "";
if (parametersMatch) {
var parameterText = parametersMatch[1];
var parameterRegex = /\|(?:([^\|=]*)=([^\|]+)|([^\|]*))/g;
var parameterMatch;
var index = 0;
while ((parameterMatch = parameterRegex.exec(parameterText))) {
var key = parameterMatch[1],
value = parameterMatch[2],
unnamedValue = parameterMatch[3];
if (key) {
if (key === "1") {
gloss = value;
}
} else {
index++;
if (index === 1) {
gloss = unnamedValue;
}
}
}
}
return gloss;
},
// get [start_pos, end_pos] of position of wikitext for trans_table containing node in text
getTransTable: function (text, node, recursive) {
var gloss = util.getTransGloss(node);
var pos = 0;
var transect = [];
while (pos > -1) {
pos = util.getVanillaIndexOf('{{trans-top', text, pos + 1);
if (pos > -1 && util.matchGloss(util.getWikitextGloss(text, pos), gloss)) {
transect.push(pos);
}
}
if (transect.length > 1) {
var poss = transect;
transect = [];
for (var i = 0; i < poss.length; i++) {
pos = poss[i];
if (util.matchGloss(gloss, util.getWikitextGloss(text, pos))) {
transect.push(pos);
}
}
if (transect.length > 1 && !recursive)
transect = util.tieBreakTransTable(text, transect, node);
}
if (transect.length == 1) {
pos = transect[0];
pos = util.getVanillaIndexOf("\n", text, pos) + 1;
var endpos = text.indexOf('{{trans-bottom}}', pos);
if (endpos > -1 && pos > 0)
return [pos, endpos];
}
console.trace("getTransTable failure; text = %o, node = %o, recursive = %o, gloss = %o", text, node, recursive, gloss);
return false;
},
// try to narrow down the correct poss if multiple matching trans tables
tieBreakTransTable: function (text, poss, node) {
if (node.nodeName == 'DIV') {
while (node && !(node.classList && node.classList.contains('NavFrame')))
node = node.parentNode;
var nodes = node.getElementsByTagName('table');
if (!nodes.length)
return poss;
node = nodes[0];
} else {
while (node && node.nodeName != 'TABLE')
node = node.parentNode;
}
var before_count = 0;
var after_count = 0;
var is_found = false;
$("table.translations").each(function () {
var gloss = util.getTransGloss(this);
if (gloss == "Translations to be checked")
return;
if (this == node) {
is_found = true;
return;
}
var pos = util.getTransTable(text, this, true);
if (pos) {
for (var j = 0; j < poss.length; j++) {
if (poss[j] == pos)
return util.tieBreakTransTable(poss.splice(j, 1), node);
}
} else {
var matched = 0;
for (var j = 0; j < poss.length; j++) {
if (util.matchGloss(util.getWikitextGloss(text, poss[j]), gloss) &&
util.matchGloss(gloss, util.getWikitextGloss(text, poss[j]))) {
matched++;
}
}
if (matched == poss.length) {
if (is_found)
after_count++;
else
before_count++;
}
}
});
if (before_count + 1 + after_count == poss.length)
return [poss[before_count]];
else
return poss;
},
matchGloss: function (line, gloss) {
if (gloss.match(/^ *$/))
return !!(line.match(/\{\{trans-top\|? *\}\}/) || line.match(/^ *$/));
var words = gloss.split(/\W+/);
var pos = 0;
for (var i = 0; i < words.length; i++) {
pos = line.indexOf(words[i], pos);
if (pos == -1)
return false;
}
return pos > -1;
},
// If the translationNode is a NavHead, this goes up to the NavFrame and then
// down to the translation table. If the translationNode is the translation
// table or is inside the translation table, it goes up till it finds the
// translation table. Then it retrieves the data-gloss="..." attribute
// that is provided by {{trans-top}} and the other translation header
// templates.
// This function returns a gloss after it has been transformed by the parser
// into HTML. So the gloss will not match the gloss in the wikitext if the
// gloss in the wikitext contains templates, for instance.
getTransGloss: function(translationNode) {
var node = translationNode;
if (translationNode.nodeType === Node.ELEMENT_NODE
&& translationNode.nodeName === 'DIV'
&& translationNode.classList.contains('NavHead')) {
var navFrame = translationNode.parentNode;
if (!navFrame.classList.contains('NavFrame')) {
console.trace('No NavFrame parent of %o', translationNode);
return '';
}
node = navFrame.getElementsByClassName('translations')[0];
if (!node) {
console.trace('No child of parent of %o with "translations" class', translationNode);
return '';
}
}
while (true) {
if (node.nodeType === Node.ELEMENT_NODE
&& node.nodeName === 'TABLE'
&& node.classList.contains('translations')) {
return node.dataset.gloss;
}
node = node.parentNode;
if (!node || node.id === 'mw-parser-output') break;
}
console.trace('Found no gloss in <table class="translations" data-gloss="..."> at or above %o', translationNode);
return '';
},
isTrreq: function (li) {
var span = li.getElementsByTagName('span')[0];
return (span && span.classList.contains("trreq"));
},
genderList: {
"s": "ကိုန်ဨကဝုစ်", "d": "ၜါလ္ပာ်", "p": "ကိုန်ဗဟုဝစ်",
"m": "masc.", "m-d": "masc. dual", "m-p": "masc. pl.",
"f": "fem.", "f-d": "fem. dual", "f-p": "fem. pl.",
"c": "common", "c-d": "common dual", "c-p": "common pl.",
"n": "နပုလ္လိၚ်", "n-d": "နပုလ္လိၚ်ၜါလ္ပာ်", "n-p": "neuter pl.",
"impf": "imperfective", "pf": "perfective"
}
};
// An adder for translations on en.wikt
function TranslationAdders(editor) {
function TranslationLabeller(insertDiv) {
var original_span;
var adder_form;
var initial_value;
var edit_button;
var editing = false;
var adderInterface = {
'fields': {
'gloss': function (txt, error) {
return util.validateNoWikisyntax('gloss', true)(txt, error);
}
},
'createForm': function () {
var thisId = "a" + String(Math.random()).replace(".", "");
adder_form = newNode('form', {
style: 'display: inline',
width: 'auto',
click: kill_event
},
newNode('label', {
'for': thisId
}, "Gloss: "),
newNode('input', {
type: 'text',
name: 'gloss',
value: initial_value,
style: 'width: 50%',
title: 'Insert a summary of the relevant definition',
id: thisId
}),
newNode('input', {
type: 'submit',
name: 'preview',
value: 'Preview'
}),
newNode('a', {
href: '/wiki/Help:Glosses'
}, 'Help?!')
);
return adder_form;
},
'onsubmit': function (values, render) {
render(values.gloss, function (new_html) {
if (editing)
toggle_editing(false);
var old_html = original_span.innerHTML;
editor.addEdit({
'undo': function () {
original_span.innerHTML = old_html;
if (!editing) toggle_editing();
},
'redo': function () {
original_span.innerHTML = new_html;
if (editing) toggle_editing();
},
'edit': function (text) {
return perform_edit(text, values.gloss);
},
'summary': 'tgloss:"' + (values.gloss.length > 50 ? values.gloss.substr(0, 50) + '...' : values.gloss + '"')
}, original_span);
});
}
};
// The actual modification to the wikitext
function perform_edit(wikitext, gloss) {
var pos = util.getTransTable(wikitext, insertDiv)[0] - 4;
var g_start = wikitext.lastIndexOf('\n{{trans-top', pos) + 1;
var g_end = wikitext.indexOf('}}\n', pos) + 2;
if (g_start === 0 || wikitext.substr(g_start, g_end - g_start).indexOf("\n") > -1) {
editor.error("Could not find translation table.");
return wikitext;
} else {
return wikitext.substr(0, g_start) + '{{trans-top|' + gloss + '}}' + wikitext.substr(g_end);
}
}
// Don't open and close box when interacting with form.
function kill_event(e) {
e.stopPropagation();
}
// What to do when the +/- button is clicked.
function toggle_editing() {
if (editing) {
adder_form.style.display = "none";
original_span.style.display = "inline";
editing = false;
return;
}
editing = true;
edit_button.text("Loading...");
editor.withCurrentText(function (currentText) {
var pos = util.getTransTable(currentText, insertDiv);
edit_button.text('±');
if (!pos)
return editor.error("Could not find translation table");
var gloss_line = currentText.substr(currentText.lastIndexOf('\n', pos[0] - 2) + 1);
gloss_line = gloss_line.substr(0, gloss_line.indexOf('\n'));
initial_value = gloss_line.replace(/^\{\{trans-top(\|(.*)|)\}\}\s*$/, "$2");
if (initial_value.indexOf("\n") > 0)
return editor.error("Internal error: guess spanning multiple lines");
if (!original_span) {
original_span = newNode('span', {
'class': 'wt-edit-recurse'
});
for (var i = 0; i < insertDiv.childNodes.length; i++) {
var child = insertDiv.childNodes[i];
if (child != edit_button[0] && child.className !== 'NavToggle') {
original_span.appendChild(insertDiv.removeChild(child));
i--;
}
}
insertDiv.appendChild(original_span);
new AdderWrapper(editor, adderInterface, insertDiv, original_span);
}
original_span.style.display = "none";
adder_form.style.display = "inline";
adder_form.getElementsByTagName('input')[0].focus();
});
}
edit_button = $('<a href="#">±</a>')
//.addClass("translation-gloss-edit-button")
.attr("title", "Edit table heading").css("padding", "2px").css("margin-left", "-5px")
.on("click", function (e) {
if (e && e.preventDefault)
e.preventDefault();
kill_event(e);
toggle_editing();
return false;
});
$(insertDiv).prepend(edit_button); //XXX: this places anchor before NavToggle which may not be a good idea
}
function TranslationAdder(insertUl) {
// Hippietrail
var langmetadata = new LangMetadata();
this.fields = {
lang: function (txt, error) {
if (txt == 'en')
return error("Please choose a foreign language. (fr, es, aaa)");
if (!/^[a-z]{2,3}(?:-[a-z]{2,3}){0,2}$/.test(txt))
return error("Please use a language code. (fr, es, aaa)");
return txt;
},
word: function (txt, error) {
if (txt == '{{trreq}}')
return '{{t-needed}}';
if (txt == '{{t-needed}}')
return txt;
if (txt.indexOf(',') == -1 || forceComma) {
forceComma = false;
if (langmetadata.expectedCase(thiz.elements.lang.value, mw.config.get('wgTitle'), txt) || forceCase) {
forceCase = false;
return util.validateNoWikisyntax('translation', true)(txt, error);
}
if (prefs.get('case-knowledge', 'none') == 'none') {
return error(newNode('span',
"Translations normally don't have capital letters. If you're certain it does, you can ",
newNode('span', {
style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;",
click: function () {
forceCase = true;
inputForm.onsubmit();
prefs.set('case-knowledge', 'guru');
}
}, "continue by clicking here.")));
} else {
var msg = newNode('span',
newNode('span', {
style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;",
click: function () {
prefs.set('case-knowledge', 'none');
try {
msg.parentNode.removeChild(msg);
} catch (e) {
}
editor.undo();
}
}, "Please click undo"), " unless you are certain that this translation has a capital letter.");
error(msg);
return txt;
}
}
if (prefs.get('comma-knowledge', 'none') == 'none') {
return error(newNode('span',
"You can only add one translation at a time. If this is one translation that contains a comma, you can ",
newNode('span', {
style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;",
click: function () {
forceComma = true;
inputForm.onsubmit();
prefs.set('comma-knowledge', 'guru');
}
}, "add it by clicking here.")));
} else {
var msg = newNode('span',
newNode('span', {
style: "color: var(--wikt-palette-blue, #0000FF); text-decoration: underline; cursor: pointer;",
click: function () {
prefs.set('comma-knowledge', 'none');
try {
msg.parentNode.removeChild(msg);
} catch (e) {
}
editor.undo();
}
}, "Please click undo"), " if you were trying to create a list of translations in one go, this is currently not supported.");
error(msg);
return txt;
}
},
qual: util.validateNoWikisyntax('qualifier'),
tr: util.validateNoWikisyntax('transcription'),
lit: util.validateNoWikisyntax('literal'),
rawPageName: util.validateNoWikisyntax('raw page name'),
nested: util.validateNoWikisyntax('nested'),
"s": 'checkbox',
"d": 'checkbox',
"p": 'checkbox',
"m": 'checkbox',
"m-d": 'checkbox',
"m-p": 'checkbox',
"f": 'checkbox',
"f-d": 'checkbox',
"f-p": 'checkbox',
"c": 'checkbox',
"c-d": 'checkbox',
"c-p": 'checkbox',
"n": 'checkbox',
"n-d": 'checkbox',
"n-p": 'checkbox',
"impf": 'checkbox',
"pf": 'checkbox',
nclass1: util.validateNoWikisyntax('noun class'),
nclass2: util.validateNoWikisyntax('plural class')
};
this.createForm = function () {
function createGenderNode(name, text) {
var inp = document.createElement("input");
inp.type = "checkbox";
inp.name = name;
var lbl = document.createElement("label");
lbl.appendChild(inp);
lbl.appendChild(document.createTextNode(text));
return lbl;
}
var genderControls = {};
for (var g in util.genderList) genderControls[g] = createGenderNode(g, util.genderList[g]);
var controls = {
lang: newNode('input', {
size: 4,
type: 'text',
name: 'lang',
value: prefs.get('curlang', ''),
title: 'မဆေၚ်စပ်ကဵုၜါ ဝါ or အက္ခရ်ပိမနကဵုကုဒ်အရေဝ်ဘာ ISO 639'
}),
transliteration: newNode('p', newNode('a', {
href: '/wiki/Wiktionary:Transliteration'
}, "Transliteration"), ": ",
newNode('input', {
name: "tr",
title: "မဆေၚ်စပ်ကဵုဝေါဟာပရေၚ်မကၠာဲနကဵုအပ္ဍဲလပ်တေန်။"
}), " (e.g. zìmǔ for 字母)"),
literal: newNode('p', "ပရေၚ်မကၠာဲဗက်အလိုက်အက္ခရ်: ",
newNode('input', {
name: "lit",
title: "မဆေၚ်စပ်ကဵုပရေၚ်မကၠာဲဗက်အလိုက်အက္ခရ်သွက်မအရေဝ်ဂှ်။"
})),
qualifier: newNode('p', "ပရေၚ်ဒးရး: ", newNode('input', {
name: 'qual',
title: "ပရေၚ်ဒးရးမွဲသာ်သွက်နကဵုမအရေဝ်ဂှ်"
}), " (e.g. literally, formally, slang)"),
display: newNode('p', "ယၟုမုက်လိက်ဟွံဆဵုဏီ: ", newNode('input', {
name: 'rawPageName',
title: "လေန်မွဲသာ်နကဵုဝေါဟာအဓိက၊ ယဝ်ရပရေၚ်ကၠာဲဝေါဟာအဓိကဟွံသေၚ်တှ်ေ။"
}), " (e.g. 疲れる for 疲れた)"),
nested: newNode('p', "ပွမတိတ်ဗၠး: ", newNode('input', {
name: 'nested',
title: "မဆေၚ်စပ်ကဵုပွမတိတ်ဗၠးနကဵုအရေဝ်ဘာသာတဏအ်"
}), " (ဥပမာ သာဗ်ခြဝ်ဨရှဳယာန်/သဳရိလိ)"),
// Genders
genders: genderControls,
// Noun class
nclass: newNode('p',
"အဇာနာမ်: ", newNode('input', {
type: 'text',
size: 4,
name: 'nclass1',
title: "The noun class of the word in the singular or citation form."
}),
" အဇာကိုန်ဗဟုဝစ်: ", newNode('input', {
type: 'text',
size: 4,
name: 'nclass2',
title: "The noun class of the word in the plural form, if any."
}))
};
// suppress unused script field
function createGenderDiv(genderz) {
var $div = $("<div>");
for (var i = 1; i < arguments.length; i++) $div.append(genderz[arguments[i]]);
return $div;
}
var $mdiv = createGenderDiv(controls.genders, "m", "m-d", "m-p");
var $fdiv = createGenderDiv(controls.genders, "f", "f-d", "f-p");
var $cdiv = createGenderDiv(controls.genders, "c", "c-d", "c-p");
var $ndiv = createGenderDiv(controls.genders, "n", "n-d", "n-p");
var $sdpdiv = createGenderDiv(controls.genders, "s", "d", "p");
var $pfdiv = createGenderDiv(controls.genders, "impf", "pf");
controls.gender = $("<p>").append($mdiv).append($fdiv).append($cdiv).append($ndiv).append($sdpdiv).append($pfdiv)[0];
langInput = controls.lang;
var showButton = newNode('span', {
'click': function () {
if (!advancedMode) {
advancedMode = true;
showButton.innerHTML = " Less";
} else {
advancedMode = false;
showButton.innerHTML = " More";
}
updateFieldVisibility.call(langInput, true);
},
'style': "color: var(--wikt-palette-blue,#0000FF); cursor: pointer;"
}, advancedMode ? " Less" : " More");
function autoTransliterate() {
var rawPageName = langmetadata.computeRawPageName(thiz.elements.lang.value, thiz.elements.word.value);
if (rawPageName && rawPageName !== thiz.elements.word.value)
thiz.elements.rawPageName.value = rawPageName;
}
function updateFieldVisibility(preserve) {
preserve = (preserve === true);
//show all arguments
function show() {
for (var i = 0; i < arguments.length; i++) {
if (arguments[i].nodeName == 'P')
arguments[i].style.display = "block";
else
arguments[i].style.display = "inline";
}
}
//hide all arguments
function hide() {
for (var i = 0; i < arguments.length; i++)
arguments[i].style.display = "none";
}
//if the first argument is false hide the remaining arguments, otherwise show them.
function toggle(condition) {
if (condition) //eww...
show.apply(this, [].splice.call(arguments, 1, arguments.length - 1));
else
hide.apply(this, [].splice.call(arguments, 1, arguments.length - 1));
}
if (!preserve)
langInput.value = langmetadata.cleanLangCode(langInput.value);
var scriptGuess = langmetadata.guessScript(langInput.value || '');
if (!preserve) {
thiz.elements.nested.value = langmetadata.getNested(langInput.value || '');
thiz.elements.nested.dataset.initialValue = thiz.elements.nested.value;
autoTransliterate();
}
var lang = langInput.value;
if (!advancedMode) {
var g = langmetadata.getGenders(lang);
if (!lang) {
hide(controls.gender);
}
else if (g === undefined) {
hide(controls.gender);
}
else {
for (var genderName in util.genderList) {
toggle(g.indexOf(genderName) != -1, controls.genders[genderName]);
}
}
toggle(g, controls.gender);
toggle(langmetadata.hasNounClasses(lang), controls.nclass);
var hasAutomaticTransliteration = new window.LanguageUtils().HasAutomaticTransliteration(lang);
toggle(scriptGuess && scriptGuess != 'Latn' && !hasAutomaticTransliteration, controls.transliteration);
toggle(langmetadata.needsRawPageName(lang), controls.display);
hide(controls.literal, controls.qualifier, controls.nested); //only in more
} else {
show(
controls.gender,
controls.genders['s'], controls.genders['d'], controls.genders['p'],
controls.genders['m'], controls.genders['m-d'], controls.genders['m-p'],
controls.genders['f'], controls.genders['f-d'], controls.genders['f-p'],
controls.genders['c'], controls.genders['c-d'], controls.genders['c-p'],
controls.genders['n'], controls.genders['n-d'], controls.genders['n-p'],
controls.genders['impf'], controls.genders['pf'],
controls.nclass, controls.transliteration, controls.literal, controls.qualifier, controls.display,
controls.nested);
}
}
//autocomplete language names
function langAutoFill(e) {
e = (e || event).keyCode;
if ((e >= 33 && e <= 40) || e == 8 || e == 46 || e == 27 || e == 16) {
return;
}
var t = this,
langPrefix = t.value;
if (langPrefix.substr(0, 1) != langPrefix.substr(0, 1).toUpperCase()) {
return;
}
new mw.Api().get({
"action": "expandtemplates",
"format": "json",
"text": "{{#invoke:languages/javascript-interface|GetSingleLanguageByLanguagePrefix|" + langPrefix + "}}",
"prop": "wikitext"
}).done(function (r) {
if (r.expandtemplates && r.expandtemplates.wikitext !== "") {
var langcode = r.expandtemplates.wikitext.split(":")[0];
var langname = r.expandtemplates.wikitext.split(":")[1];
var oldLength = t.value.length;
t.value = langname;
$(t).data("langcode", langcode);
if (t.setSelectionRange) {
t.setSelectionRange(oldLength, langname.length);
} else if (t.createTextRange) {
var z = t.createTextRange();
z.moveEnd('character', 0 - z.move('character', [t.value.length, t.value = langname][0]) + t.value.length);
z.select();
}
} else {
$(t).removeData("langcode");
}
});
}
langInput.onkeyup = langAutoFill;
langInput.onblur = function () {
if ($(this).data("langcode"))
$(this).val($(this).data("langcode"));
updateFieldVisibility.call(langInput);
};
window.setTimeout(function () {
updateFieldVisibility.call(langInput);
}, 0);
inputForm = newNode('form',
newNode('p', newNode('a', {
href: "/wiki/MediaWiki:Gadget-TranslationAdder.js/help#Usage"
}, "Add translation"), ' ',
langInput, newNode('b', ': '), newNode('input', {
'name': 'word',
size: 20,
keyup: autoTransliterate,
change: autoTransliterate
}),
newNode('input', {
'type': 'submit',
'value': 'Preview translation'
}), showButton
),
controls.gender,
controls.nclass,
controls.transliteration,
controls.literal,
controls.display,
controls.qualifier,
controls.nested
);
return inputForm;
};
this.onsubmit = function (values, render) {
var wikitext;
function callRender() {
render(wikitext, function (html) {
registerEdits(values, wikitext, html);
});
}
// Keep track of whether the "nested" field has changed
// so that we can tag the edit
values.nestedChanged = inputForm.nested.dataset.initialValue !== values.nested;
var languagePrefix = '{{subst:#invoke:languages/templates|getByCodeAllowEtym|' + values.lang + '|getCanonicalName}}: ';
if (values.word.indexOf('{{t-needed') === 0) {
wikitext = languagePrefix + ' {{t-needed|' + values.lang + '}}';
callRender();
} else {
langmetadata.retrieveRawPageName(values.lang, values.word, values.rawPageName, function (rawPageName, needsRawPageName) {
if (needsRawPageName) {
values.rawPageName = rawPageName;
values.wordMinus = '';
} else {
values.rawPageName = '';
values.wordMinus = rawPageName;
}
langmetadata.hasWiktionaryWithEntry(values.lang, values.rawPageName || values.wordMinus, function (hasInterwiki) {
var templateBody = '|' + values.lang + '|' +
langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word) +
(values.nclass1 ? '|c' + values.nclass1 : '') +
(values.nclass2 ? '|c' + values.nclass2 : '');
// Add gender codes
templateBody += [
's', 'd', 'p', 'm', 'm-d', 'm-p', 'f', 'f-d', 'f-p',
'c', 'c-d', 'c-p', 'n', 'n-d', 'n-p', 'impf', 'pf'
].map(function(k) {
return values[k] ? '|' + k : '';
}).join("");
templateBody +=
(values.tr ? '|tr=' + values.tr : '') +
(values.lit ? '|lit=' + values.lit : '') +
(values.rawPageName ? '|alt=' + values.word : '');
var qualifier = values.qual ? ' {{q|' + values.qual + '}}' : '';
wikitext = languagePrefix + "{{" + (hasInterwiki ? "t+" : "t") + templateBody + "}}" + qualifier;
callRender();
});
});
}
};
var thiz = this;
var prefs = new CookiePreferences('EditorJs');
var langInput;
var inputForm;
var advancedMode = prefs.get('more-display', 'none') != 'none';
var forceComma = false;
var forceCase = false;
//Reset elements to default values.
function resetElements() {
if (prefs.get('more-display', 'none') != advancedMode ? 'block' : 'none')
prefs.set('more-display', advancedMode ? 'block' : 'none'); //named for compatibility
thiz.elements.word.value = thiz.elements.nclass1.value = thiz.elements.nclass2.value = thiz.elements.lit.value = thiz.elements.tr.value = thiz.elements.rawPageName.value = thiz.elements.qual.value = '';
thiz.elements['s'].checked = false;
thiz.elements['d'].checked = false;
thiz.elements['p'].checked = false;
thiz.elements['m'].checked = false;
thiz.elements['m-d'].checked = false;
thiz.elements['m-p'].checked = false;
thiz.elements['f'].checked = false;
thiz.elements['f-d'].checked = false;
thiz.elements['f-p'].checked = false;
thiz.elements['c'].checked = false;
thiz.elements['c-d'].checked = false;
thiz.elements['c-p'].checked = false;
thiz.elements['n'].checked = false;
thiz.elements['n-d'].checked = false;
thiz.elements['n-p'].checked = false;
thiz.elements['impf'].checked = false;
thiz.elements['pf'].checked = false;
prefs.set('curlang', thiz.elements.lang.value);
}
// This is onsubmit after the wikitext has been rendered to give content
function registerEdits(values, wikitext, content) {
var li = newNode('li', {
'class': 'trans-' + values.lang
});
li.innerHTML = content;
var lang = getLangName(li);
var x = langmetadata.applyOrthographicalCorrections(values.lang, values.rawPageName || values.word);
var resp = x;
var langCodeBad = false;
if (x.indexOf("{{") < 0) {
new mw.Api().get({
"action": "expandtemplates",
"format": "json",
"text": "{{ll|" + values.lang + "|" + x + "}}",
"prop": "wikitext"
}, {"async": false}).done(function (r) {
if (r.expandtemplates && r.expandtemplates.wikitext) {
// [[page#Language|display]]
// resp = r.expandtemplates.wikitext;
// [[page|display]]
// resp = r.expandtemplates.wikitext.replace(/#[^|]*/, "");
// [[page]]
resp = r.expandtemplates.wikitext.replace(/#[^\]]*/, "");
try {
if (DOMParser && new DOMParser().parseFromString(r.expandtemplates.wikitext, "text/html").querySelector(".scribunto-error"))
langCodeBad = true;
} catch (e) { }
}
}).fail(function(r) {
console.error(r);
resp = resp.includes("[[") ? resp : "[[" + resp + "]]";
});
}
if (langCodeBad)
return editor.error("Unrecognized language code '" + values.lang + "'");
var summary = 't+' + values.lang + ':' + resp;
var insertBefore = null;
var nextLanguage = null;
var nestedHeading, nestedLang;
var nested = values.nested.split('/');
if (nested.length > 1) {
nestedHeading = nested[0];
nestedLang = (nested[1] === 'Roman' ? 'Latin' : nested[1]); // [[Wiktionary:Beer parlour/2026/January#sorting and reformating translation tables]]
if (nestedHeading === '')
nestedHeading = lang;
content = content.replace(/.*: /, nestedLang + ": ");
wikitext = wikitext.replace("subst:", "").replace(/.*: /, nestedLang + ": ");
} else {
nestedHeading = values.nested;
nestedLang = lang;
}
var nestedWikitext = "\n* " + nestedHeading + ":\n*: " + wikitext;
function addEdit(edit, span) {
var fields = ["lang", "word", "nclass1", "nclass2", "rawPageName", "qual", "tr", "lit"];
editor.addEdit({
'undo': function () {
edit.undo();
if (thiz.elements.word.value === "" &&
thiz.elements.nclass1.value === "" &&
thiz.elements.nclass2.value === "" &&
thiz.elements.tr.value === "" &&
thiz.elements.lit.value === "" &&
thiz.elements.rawPageName.value === "" &&
thiz.elements.qual.value === "") {
for (var i = 0; i < fields.length; i++) {
thiz.elements[fields[i]].value = values[fields[i]];
}
for (var genderName in util.genderList) {
thiz.elements[genderName].checked = values[genderName];
}
}
},
'redo': function () {
edit.redo();
for (var i = 0; i < fields.length; i++) {
if (thiz.elements[fields[i]].value != values[fields[i]])
return;
}
resetElements();
},
'edit': edit.edit,
'tags': [values.nestedChanged ? 'translation-adder-custom-nesting' : null],
'summary': summary
}, span);
}
// Compare language names without accents and punctuation marks
var normalizeAndStrip = s => s.normalize("NFD").replace(/[\u0300-\u036f'ʼ", -]/g, '').toUpperCase();
var compare = function(a, b) {
if (normalizeAndStrip(a) < normalizeAndStrip(b)) {
return -1;
}
if (normalizeAndStrip(a) > normalizeAndStrip(b)) {
return 1;
}
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
}
if (lang) {
//Get all li's in this table row.
var lss = $(insertUl).parent().parent().find('li').get();
var dds = $(insertUl).parent().parent().find('dd').get();
var lis = lss.concat(dds);
for (var j = 0; j < lis.length; j++) {
if (lis[j].getElementsByTagName('form').length > 0)
continue;
var ln = getLangName(lis[j]);
if (ln == lang) {
if (values.word == '{{t-needed}}') {
addEdit({
'redo': function () {
},
'undo': function () {
},
'edit': function () {
return editor.error("Can not add a translation request for a language with translations");
}
});
return resetElements();
}
var span = newNode('span');
var parent = lis[j];
if (util.isTrreq(parent)) {
span.innerHTML = content.substr(content.indexOf(':') + 1);
var trspan = parent.getElementsByTagName('span')[0];
addEdit({
'redo': function () {
parent.removeChild(trspan);
parent.appendChild(span);
},
'undo': function () {
parent.removeChild(span);
parent.appendChild(trspan);
},
'edit': getEditFunction(values, wikitext, ln, values.lang, true, function (text, ipos) {
//Converting a Translation request into a translation
var lineend = text.indexOf('\n', ipos);
return text.substr(0, ipos) + wikitext + text.substr(lineend);
})
}, span);
return resetElements();
} else if (parent.tagName !== 'DD' || lang !== 'Latin') {
if (parent.getElementsByTagName('ul').length + parent.getElementsByTagName('dl').length == 0) {
span.innerHTML = ", " + content.substr(content.indexOf(':') + 1);
addEdit({
'redo': function () {
parent.appendChild(span)
},
'undo': function () {
parent.removeChild(span)
},
'edit': getEditFunction(values, wikitext, ln, values.lang, false, function (text, ipos) {
//We are adding the wikitext to a list of translations that already exists.
var lineend = text.indexOf('\n', ipos);
var wt = wikitext.replace('subst:#invoke:', '');
wt = wt.substr(wt.indexOf(':') + 1);
return text.substr(0, lineend) + "," + wt + text.substr(lineend);
})
}, span);
return resetElements();
} else {
var node = parent.firstChild;
var hastrans = false;
while (node) {
if (node.nodeType == 1) {
var nn = node.nodeName;
if (nn == 'UL' || nn == 'DL') {
// If we want to use the dialectical nesting for orthographical nesting
// then we need to skip this (otherwise perfect) match.
if (!hastrans && nestedHeading == ln) {
node = node.nextSibling;
continue;
}
span.innerHTML = (hastrans ? ", " : " ") + content.substr(content.indexOf(':') + 1);
addEdit({
'redo': function () {
parent.insertBefore(span, node)
},
'undo': function () {
parent.removeChild(span)
},
'edit': getEditFunction(values, wikitext, ln, values.lang, false, function (text, ipos) {
//Adding the translation to a language that has nested translations under it
var lineend = text.indexOf('\n', ipos);
var wt = wikitext.replace('subst:#invoke:', '');
wt = wt.substr(wt.indexOf(':') + 1);
return text.substr(0, lineend) + (hastrans ? "," : "") + wt + text.substr(lineend);
})
}, span);
return resetElements();
} else {
hastrans = true;
}
}
node = node.nextSibling;
}
}
}
} else if (ln && compare(ln, lang) > 0 && (!nextLanguage || compare(ln, nextLanguage) < 0) && lis[j].parentNode.parentNode.nodeName != 'LI') {
nextLanguage = ln;
var parent = lis[j];
insertBefore = [{
'redo': function () {
parent.parentNode.insertBefore(li, parent);
},
'undo': function () {
parent.parentNode.removeChild(li)
},
'edit': getEditFunction(values, wikitext, ln, getLangCode(parent), util.isTrreq(parent), function (text, ipos) {
//Adding a new language's translation before another language's translation
var lineend = text.lastIndexOf('\n', ipos);
return text.substr(0, lineend) + "\n* " + wikitext + text.substr(lineend);
})
}, li];
}
}
}
if (values.nested) {
nextLanguage = null;
insertBefore = null;
li.innerHTML = nestedHeading + ":" + "<dl><dd class=\"trans-" + values.lang + "\">" + content + "</dd></dl>";
var lis = insertUl.parentNode.parentNode.getElementsByTagName('li');
for (var j = 0; j < lis.length; j++) {
//Ignore the editor form
if (lis[j].getElementsByTagName('form').length > 0)
continue;
//Don't look at nested translations
if (lis[j].parentNode.parentNode.nodeName != 'TD')
continue;
var ln = getLangName(lis[j]);
if (ln == nestedHeading) {
var sublis = lis[j].getElementsByTagName('li');
if (!sublis.length)
sublis = lis[j].getElementsByTagName('dd');
if (sublis.length == 0) {
var parent = lis[j];
var dd = newNode('dd', {
'class': 'trans-' + values.lang
});
var dl = newNode('dl', dd);
dd.innerHTML = content;
addEdit({
'redo': function () {
parent.appendChild(dl);
},
'undo': function () {
parent.removeChild(dl);
},
'edit': getEditFunction(values, wikitext, nestedHeading, getLangCode(parent), util.isTrreq(parent), function (text, ipos) {
//Adding a new dl to an existing translation line
var lineend = text.indexOf('\n', ipos);
return text.substr(0, lineend) + "\n*: " + wikitext + text.substr(lineend);
})
}, dd);
return resetElements();
} else {
var dd = newNode(sublis[0].nodeName, {
'class': 'trans-' + values.lang
});
var linestart = dd.nodeName == 'DD' ? '\n*: ' : '\n** ';
dd.innerHTML = content;
for (var k = 0; k < sublis.length; k++) {
var subln = getLangName(sublis[k]);
var parent = sublis[k];
if (subln == nestedLang) {
var span = newNode('span');
span.innerHTML = ", " + content.substr(content.indexOf(':') + 1);
addEdit({
'redo': function () {
parent.appendChild(span)
},
'undo': function () {
parent.removeChild(span)
},
'edit': getEditFunction(values, wikitext, nestedLang, values.lang, false, function (text, ipos) {
// Adding the wikitext to a list of translations that already exists.
var lineend = text.indexOf('\n', ipos);
var wt = wikitext.replace('subst:#invoke:', '');
wt = wt.substr(wt.indexOf(':') + 1);
return text.substr(0, lineend) + "," + wt + text.substr(lineend);
})
}, span);
return resetElements();
} else if (langmetadata.nestsBefore(nestedLang, subln)) {
addEdit({
'redo': function () {
parent.parentNode.insertBefore(dd, parent);
},
'undo': function () {
parent.parentNode.removeChild(dd);
},
'edit': getEditFunction(values, wikitext, subln, getLangCode(parent), util.isTrreq(parent),
function (text, ipos) {
// Adding a nested translation in-order
var lineend = text.lastIndexOf('\n', ipos);
return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend);
})
}, dd);
return resetElements();
}
}
addEdit({
'redo': function () {
parent.parentNode.appendChild(dd);
},
'undo': function () {
parent.parentNode.removeChild(dd);
},
'edit': getEditFunction(values, wikitext, subln, getLangCode(parent), util.isTrreq(parent),
function (text, ipos) {
// Adding a nested translation at the end of its group
var lineend = text.indexOf('\n', ipos);
return text.substr(0, lineend) + linestart + wikitext + text.substr(lineend);
})
}, dd);
return resetElements();
}
} else if (ln && compare(ln, nestedHeading) > 0 && (!nextLanguage || compare(ln, nextLanguage) < 0)) {
nextLanguage = ln;
var parent = lis[j];
insertBefore = [{
'redo': function () {
parent.parentNode.insertBefore(li, parent);
},
'undo': function () {
parent.parentNode.removeChild(li)
},
'edit': getEditFunction(values, wikitext, ln, getLangCode(parent), util.isTrreq(parent), function (text, ipos) {
//Adding a new nested translation section.
var lineend = text.lastIndexOf('\n', ipos);
return text.substr(0, lineend) + nestedWikitext + text.substr(lineend);
})
}, li];
}
}
wikitext = nestedHeading + ":\n*: " + wikitext;
}
li.className = "trans-" + values.lang;
if (insertBefore) {
addEdit(insertBefore[0], insertBefore[1]);
} else {
//Append the translations to the end (no better way found)
addEdit({
'redo': function () {
insertUl.appendChild(li);
},
'undo': function () {
insertUl.removeChild(li)
},
'edit': getEditFunction(values, wikitext)
}, li);
}
return resetElements();
}
//Get the wikitext modification for the current form submission.
function getEditFunction(values, wikitext, findLanguage, findLangCode, trreq, callback) {
return function (text) {
var p = util.getTransTable(text, insertUl);
if (!p)
return editor.error("Could not find translation table for '"
+ values.lang + ":" + values.word
+ "'. Glosses should be unique");
var stapos = p[0];
var endpos = p[1];
if (findLanguage) {
var ipos = 0;
if (trreq) {
ipos = text.substr(stapos).search(new RegExp(mw.util.escapeRegExp(findLanguage) + ":\\s*\\{\\{t-needed(?:\\|" + findLangCode + "}})?")) + stapos;
if (ipos < stapos || ipos > endpos)
ipos = text.indexOf('{{trreq|' + findLanguage + '}}', stapos);
if (ipos < stapos || ipos > endpos)
ipos = text.indexOf('{{trreq|' + findLangCode + '}}', stapos);
}
// If we have a nested trreq, then we still need to look for the non-trreq form of the heading language
if (!trreq || ipos < stapos || ipos > endpos) {
var escapedLang = mw.util.escapeRegExp(findLanguage);
//if the translation should not be nested it should not go before any nested language
var regexByShouldFindSubLanguage = values.nested != "" ? "[:*]?" : "";
var possibleRegexes = [
RegExp("\\*" + regexByShouldFindSubLanguage + " ?\\[\\[" + escapedLang + "\\]\\]:"),
RegExp("\\*" + regexByShouldFindSubLanguage + " ?" + escapedLang + ":"),
RegExp("\\*" + regexByShouldFindSubLanguage + " ?\\{\\{ttbc\\|" + escapedLang + "}}:")
];
ipos = text.substr(stapos).search(possibleRegexes[0]) + stapos;
if (ipos < stapos || ipos > endpos)
ipos = text.substr(stapos).search(possibleRegexes[1]) + stapos;
if (ipos < stapos || ipos > endpos)
ipos = text.substr(stapos).search(possibleRegexes[2]) + stapos;
if (ipos < stapos || ipos > endpos)
ipos = text.indexOf('{{subst:#invoke:languages/templates|getByCodeAllowEtym|' + findLangCode + '|getCanonicalName}}:', stapos);
}
if (ipos >= stapos && ipos < endpos) {
return callback(text, ipos, trreq);
} else {
return editor.error("Could not find translation entry for '" + values.lang + ":" + values.word + "'. Please reformat");
}
}
return text.substr(0, endpos) + "* " + wikitext + "\n" + text.substr(endpos);
};
}
// For an <li> in well-formed translation sections, return the language name.
function getLangName(li) {
var guess = li.textContent || li.innerText;
if (guess)
guess = guess.substr(0, guess.indexOf(':'));
if (guess == 'ထာမ်ပလိက်') {
return false;
}
return guess.replace(/^[\s\n]*/, '');
}
// Try to get the language code from an <li> containing {{t}}, {{t+}} or {{t-}}
function getLangCode(li) {
if (li.className.indexOf('trans-') === 0)
return li.className.substr(6);
var spans = li.getElementsByTagName('span');
for (var i = 0; i < spans.length; i++) {
if (spans[i].lang) {
return spans[i].lang;
}
if (spans[i].dataset && spans[i].dataset.lang) {
return spans[i].dataset.lang;
}
}
return false;
}
}
function createTranslationAdderRow(li) {
var $ul = $("<ul>").append(li);
return $("<tr>")
.append($("<td>"))
.append($("<td>"))
.append($("<td>").css("text-align", "left").append($ul));
}
$('table.translations').each(function () {
if (util.getTransGloss(this) != 'ပရေၚ်မကၠာဲလဝ်နွံပၟိက်ဒးစၟဳစၟတ်') {
var tdSelector = "td:not(.mw-hiero-table td):not(.targeted-translations-select)";
var uls = $(this).find(tdSelector + " > ul").get();
if (uls.length == 0) {
$(this).find(tdSelector)[0].appendChild(newNode('ul'));
uls = this.getElementsByTagName('ul');
}
if (uls.length == 1) {
var td = $(this).find(tdSelector)[2];
if (td) {
td.appendChild(newNode('ul'));
uls = this.getElementsByTagName('ul');
}
}
if (uls) {
var li = document.createElement('li');
var ul = uls[uls.length - 1];
$(this).append(createTranslationAdderRow(li));
new AdderWrapper(editor, new TranslationAdder(ul), li);
if (true) {// XXX: why not? (new CookiePreferences('EditorJs')).get('labeller') == 'true') {
var div = $(this).parent().parent().find('div.NavHead')[0];
if (div) {
new TranslationLabeller(div)
}
}
}
}
});
}
$(function () {
// Check if we are on a sensible page
var isOnPrintable = mw.util.getParamValue("printable") !== null;
var isDiff = mw.util.getParamValue("diff") !== null || mw.util.getParamValue("oldid") !== null;
if (mw.config.get('wgAction') != 'view' || isOnPrintable || isDiff)
return;
var editor = new Editor();
TranslationAdders(editor);
/*
// Check that we have not been disabled
var prefs = new CookiePreferences('EditorJs');
if (prefs.get('enabled', 'true') == 'true') {
if (!window.loadedEditor) {
prefs.setDefault('labeller', mw.config.get('wgUserName') ? 'true' : 'false');
window.loadedEditor = true;
}
}
// The enable-disable button on WT:EDIT
var node = document.getElementById('editor-js-disable-button');
if (node) {
node.innerHTML = "";
var toggle = newNode('span', {
click: function() {
if (prefs.get('enabled', 'true') == 'true') {
toggle.innerHTML = "Enable";
prefs.set('enabled', 'false');
} else {
toggle.innerHTML = "Disable";
prefs.set('enabled', 'true');
}
}
}, (prefs.get('enabled', 'true') == 'true' ? 'Disable' : 'Enable'))
node.appendChild(toggle);
}
var node = document.getElementById("editor-js-labeller-button");
if (node) {
node.innerHTML = "";
var toggle2 = newNode('span', {
click: function() {
if (prefs.get('labeller') == 'true') {
toggle2.innerHTML = "Enable";
prefs.set('labeller', 'false');
} else {
toggle2.innerHTML = "Disable";
prefs.set('labeller', 'true');
}
}
}, (prefs.get('labeller') == 'true' ? 'Disable' : 'Enable'))
node.appendChild(toggle2);
}*/
});
//</nowiki>
// </syntaxhighlight>
okb9clzkt5d3iu1c9am10h5qrii7de4
မဳဒဳယာဝဳကဳ:Gadget-TranslationAdder-Data.js
8
295739
396567
2026-06-07T19:49:59Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "// {{documentation}} // <nowiki> // implicit dependencies : ext.gadget.LanguageUtils /* jshint maxerr:1048576, strict:true, undef:true, latedef:true, es5:true */ /* global $, ScriptUtils, mw */ window.LangMetadata = function() { //Singleton if (arguments.callee.instance) return arguments.callee.instance; else arguments.callee.instance = this; // {{{ Metadata dictionaries // FIXME: Is it possible to query th..."
396567
javascript
text/javascript
// {{documentation}}
// <nowiki>
// implicit dependencies : ext.gadget.LanguageUtils
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, es5:true */
/* global $, ScriptUtils, mw */
window.LangMetadata = function() {
//Singleton
if (arguments.callee.instance)
return arguments.callee.instance;
else
arguments.callee.instance = this;
// {{{ Metadata dictionaries
// FIXME: Is it possible to query this information directly from [[Module:languages]]?
var metadata = {
aa: {
haswikt: 1,
sc: ["Latn", "Ethi"]
},
ab: {
haswikt: 1,
sc: ["Cyrl", "Latn", "Geor"]
},
aer: {
sc: "Latn"
},
af: {
g: ["s", "p"],
haswikt: 1
},
ak: {
haswikt: 1
},
akk: {
g: ["m", "f", "m-p", "f-p"],
sc: "Xsux"
},
als: {
haswikt: 1
},
am: {
g: ["m", "f", "m-p", "f-p"],
haswikt: 1,
sc: "Ethi"
},
an: {
g: ["m", "f", "m-p", "f-p"],
haswikt: 1
},
ang: {
alt: 1,
g: ["m", "f", "n", "m-p", "f-p", "n-p"],
haswikt: 1
},
ar: {
g: ["m", "f", "m-p", "f-p", "p"],
alt: 1,
haswikt: 1,
sc: "Arab"
},
arc: {
g: ["m", "f", "m-p", "f-p"],
sc: ["Hebr", "Syrc"]
},
are: {},
arz: {
alt: 1,
g: ["m", "f", "m-p", "f-p"],
sc: "Arab"
},
as: {
haswikt: 1,
sc: "Beng"
},
ast: {
g: ["m", "f", "m-p", "f-p"],
haswikt: 1
},
av: {
haswikt: 1
},
ay: {
haswikt: 1
},
az: {
g: ["s", "p"],
haswikt: 1,
sc: ["Latn", "Cyrl", "Arab"]
},
ba: {
g: ["s", "p"],
sc: "Cyrl"
},
bar: {},
"bat-smg": {
g: ["m", "f", "m-p", "f-p"]
},
bbl: {
sc: "Geor"
},
be: {
g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"],
haswikt: 1,
sc: "Cyrl"
},
"be-x-old": {
sc: "Cyrl"
},
bg: {
g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"],
haswikt: 1,
sc: "Cyrl"
},
bh: {
haswikt: 1,
sc: "Deva"
},
bhb: {
sc: "Deva"
},
bi: {
haswikt: 1
},
blt: {
sc: "Tavt"
},
bm: {
haswikt: 1,
sc: ["Latn", "Nkoo", "Arab"]
},
bn: {
haswikt: 1,
sc: "Beng"
},
bo: {
haswikt: 1,
sc: "Tibt"
},
br: {
g: ["m", "f"],
haswikt: 1
},
ca: {
g: ["m", "f", "m-p", "f-p"],
haswikt: 1
},
cdo: {
sc: "Hani"
},
ce: {
sc: "Cyrl"
},
ch: {
haswikt: 1
},
chr: {
haswikt: 1,
sc: "Cher"
},
cjy: {
sc: "Hani"
},
ckb: {
wiktprefix: "ku",
sc: "Arab",
wsc: "ku-Arab"
},
cmn: {
sc: "Hani",
wiktprefix: "zh"
},
co: {
haswikt: 1
},
cpx: {
sc: "Hani"
},
cr: {
haswikt: 1,
sc: "Cans"
},
crh: {},
cs: {
g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"],
haswikt: 1
},
csb: {
g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"],
haswikt: 1
},
cu: {
g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"],
sc: ["Cyrs", "Glag"]
},
cv: {
sc: "Cyrl"
},
cy: {
g: ["m", "f", "m-p", "f-p"],
haswikt: 1
},
czh: {
sc: "Hani"
},
czo: {
sc: "Hani"
},
da: {
g: ["c", "n", "c-p", "n-p", "p"],
haswikt: 1
},
dax: {},
de: {
g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"],
haswikt: 1,
allowCaps: 1
},
dhg: {},
djb: {},
dji: {},
djr: {},
dng: {
sc: "Cyrl"
},
dsb: {
g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"]
},
dsx: {},
duj: {},
dv: {
g: ["s", "p"],
haswikt: 1,
sc: "Thaa"
},
dz: {
haswikt: 1,
sc: "Tibt"
},
el: {
g: ["m", "f", "n", "m-p", "f-p", "n-p"],
haswikt: 1
},
en: {
g: ["s", "p"],
haswikt: 1
},
eo: {
g: ["s", "p"],
haswikt: 1
},
es: {
g: ["m", "f", "m-p", "f-p", "p"],
haswikt: 1
},
et: {
g: ["s", "p"],
haswikt: 1
},
ett: {
g: ["s", "p"],
sc: "Ital"
},
eu: {
g: ["s", "p"],
haswikt: 1
},
fa: {
haswikt: 1,
g: ["s", "p"],
sc: "Arab",
wsc: "fa-Arab"
},
// modelling on cmn, nb, prs
"fa-cls": {
wiktprefix: "fa",
sc: "Arab",
wsc: "fa-Arab"
},
// modelling on cmn, nb, prs
"fa-ira": {
wiktprefix: "fa",
sc: "Arab",
wsc: "fa-Arab"
},
fi: {
g: ["s", "p"],
haswikt: 1
},
fil: {},
fj: {
haswikt: 1
},
fo: {
g: ["m", "f", "n"],
haswikt: 1
},
fr: {
g: ["m", "f", "m-p", "f-p", "p"],
haswikt: 1
},
frm: {
g: ["m", "f", "m-p", "f-p"]
},
fro: {
g: ["m", "f", "m-p", "f-p"]
},
fur: {
g: ["m", "f", "m-p", "f-p"],
},
fy: {
g: ["c", "n", "p"],
haswikt: 1
},
ga: {
g: ["m", "f", "m-p", "f-p", "p"],
haswikt: 1
},
gan: {
sc: "Hani"
},
gd: {
g: ["m", "f", "m-p", "f-p"],
haswikt: 1
},
gez: {
sc: "Ethi"
},
gl: {
g: ["m", "f", "m-p", "f-p"],
haswikt: 1
},
"gmw-ecg": {
g: ["m", "f", "n", "p"],
allowCaps: 1
},
gmy: {
sc: "Linb"
},
gnn: {
sc: "Latn"
},
got: {
g: ["m", "f", "n", "m-p", "f-p", "n-p"],
sc: "Goth"
},
grc: {
g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"]
},
gsw: {
g: ["m", "f", "n", "p"],
allowCaps: 1
},
gu: {
g: ["m", "f", "n", "m-p", "f-p", "n-p"],
haswikt: 1,
sc: "Gujr"
},
guf: {
sc: "Latn"
},
gug: {
wiktprefix: "gn"
},
gv: {
haswikt: 1
},
ha: {
haswikt: 1
},
hak: {
sc: "Hani"
},
har: {
sc: "Ethi"
},
he: {
alt: 1,
g: ["m", "f", "m-p", "f-p", "p"],
haswikt: 1,
sc: "Hebr"
},
hi: {
g: ["m", "f", "m-p", "f-p", "p"],
haswikt: 1,
sc: "Deva"
},
hif: {
sc: ["Latn", "Deva"]
},
hit: {
sc: "Xsux"
},
hrx: {
g: ["m", "f", "n", "m-p", "f-p", "n-p"],
haswikt: 1
},
hsb: {
g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"],
haswikt: 1
},
hsn: {
sc: "Hani"
},
hu: {
g: ["s", "p"],
haswikt: 1
},
hy: {
g: ["s", "p"],
haswikt: 1,
sc: "Armn"
},
ia: {
haswikt: 1
},
id: {
haswikt: 1
},
ie: {
haswikt: 1
},
ik: {
haswikt: 1
},
ike: {
sc: "Cans"
},
ikt: {
sc: "Cans"
},
io: {
haswikt: 1
},
is: {
g: ["m", "f", "n", "m-p", "f-p", "n-p"],
haswikt: 1
},
it: {
g: ["m", "f", "m-p", "f-p", "p"],
haswikt: 1
},
iu: {
haswikt: 1,
sc: "Cans"
},
ja: {
haswikt: 1,
sc: "Jpan"
},
jay: {
sc: "Latn"
},
jbo: {
haswikt: 1
},
jv: {
haswikt: 1
},
ka: {
g: ["s", "p"],
haswikt: 1
}, // script should include "Geok" for Asomtavruli & Nuskhuri but prevents automatic transliteration?
khb: {
sc: ["Talu", "Lana", "Thai"]
},
kjh: {
sc: "Cyrl"
},
kk: {
haswikt: 1,
g: ["s", "p"],
sc: "Cyrl"
},
kkh: {
sc: ["Lana", "Thai"]
},
kl: {
haswikt: 1
},
km: {
haswikt: 1,
sc: "Khmr"
},
kn: {
haswikt: 1,
sc: "Knda"
},
kmr: {
g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"],
wiktprefix: "ku"
},
ko: {
haswikt: 1,
sc: "Kore"
},
krc: {
g: ["s", "p"],
sc: "Cyrl"
},
ks: {
haswikt: 1,
sc: ["Arab", "Deva"],
wsc: "ks-Arab"
},
kw: {
haswikt: 1
},
ky: {
haswikt: 1,
g: ["s", "p"],
sc: "Cyrl"
},
la: {
alt: 1,
g: ["m", "f", "n", "m-p", "f-p", "n-p"],
haswikt: 1
},
lb: {
g: ["m", "f", "n", "p"],
haswikt: 1,
allowCaps: 1
},
lez: {
sc: "Cyrl"
},
li: {
haswikt: 1
},
lki: {
wiktprefix: "ku"
},
lld: {
g: ["m", "f", "m-p", "f-p"],
haswikt: 1
},
lmo: {
g: ["m", "f", "m-p", "f-p"],
haswikt: 1
},
ln: {
haswikt: 1
},
lo: {
haswikt: 1,
sc: "Laoo"
},
lt: {
alt: 1,
g: ["m", "f", "m-p", "f-p"],
haswikt: 1
},
lv: {
g: ["m", "f", "m-p", "f-p"],
haswikt: 1
},
lzz: {
sc: "Geor"
},
mg: {
haswikt: 1
},
mh: {
haswikt: 1
},
mi: {
g: 0,
haswikt: 1
},
mk: {
g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"],
haswikt: 1,
sc: "Cyrl"
},
ml: {
haswikt: 1,
sc: "Mlym"
},
mn: {
g: ["s", "p"],
haswikt: 1,
sc: ["Cyrl", "Mong"]
},
mnp: {
sc: "Hani"
},
mo: {
haswikt: 1,
sc: "Cyrl"
},
mol: {
sc: "Cyrl"
},
mr: {
g: ["m", "f", "n"],
haswikt: 1,
sc: "Deva"
},
ms: {
haswikt: 1,
sc: ["Latn", "Arab"]
},
mt: {
g: ["m", "f", "m-p", "f-p", "p"],
haswikt: 1
},
mwp: {
sc: "Latn"
},
my: {
haswikt: 1,
sc: "Mymr"
},
na: {
haswikt: 1
},
nah: {
haswikt: 1
},
nan: {
wiktprefix: "zh-min-nan",
sc: "Hani"
},
nb: {
g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"],
wiktprefix: "no"
},
nds: {
g: ["m", "f", "n", "m-p", "f-p", "n-p"]
},
"nds-de": {
g: ["m", "f", "n", "m-p", "f-p", "n-p"],
wiktprefix: "nds"
},
"nds-nl": {
g: ["m", "f", "n", "m-p", "f-p", "n-p"],
wiktprefix: "nds"
},
ne: {
haswikt: 1,
sc: "Deva"
},
nl: {
g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"],
haswikt: 1
},
nn: {
g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"],
haswikt: 1
},
no: {
g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"],
haswikt: 1
},
nod: {
sc: "Lana"
},
non: {
g: ["m", "f", "n", "m-p", "f-p", "n-p"]
},
oc: {
g: ["m", "f", "m-p", "f-p"],
haswikt: 1
},
om: {
haswikt: 1
},
or: {
haswikt: 1,
sc: "Orya"
},
orv: {
g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"],
sc: "Cyrs"
},
osc: {
sc: "Ital"
},
ota: {
g: ["s", "p"],
sc: "Arab",
wsc: "ota-Arab"
},
pa: {
g: ["m", "f", "m-p", "f-p", "p"],
haswikt: 1,
sc: ["Guru", "Arab"]
},
pdt: {
g: ["m", "f", "n", "m-p", "f-p", "n-p"],
wiktprefix: "nds"
},
peo: {
sc: "Xpeo"
},
phn: {
sc: "Phnx"
},
pi: {
g: ["m", "f", "n", "m-p", "f-p", "n-p"],
haswikt: 1
},
pjt: {
sc: "Latn"
},
pl: {
g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"],
haswikt: 1
},
pms: {
g: ["m", "f", "m-p", "f-p"],
haswikt: 1
},
pox: {
g: ["m", "f", "n", "m-p", "f-p", "n-p", "impf", "pf"]
},
prs: {
wiktprefix: "fa",
sc: "Arab",
wsc: "fa-Arab"
},
ps: {
g: ["m", "f", "m-p", "f-p", "p"],
haswikt: 1,
sc: "Arab",
wsc: "ps-Arab"
},
pt: {
g: ["m", "f", "m-p", "f-p", "p"],
haswikt: 1
},
qu: {
haswikt: 1
},
rgn: {
g: ["m", "f", "m-p", "f-p"],
},
rit: {
sc: "Latn"
},
rm: {
g: ["m", "f"],
haswikt: 1
},
rn: {
haswikt: 1
},
ro: {
g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"],
haswikt: 1,
sc: ["Latn", "Cyrl"]
},
"roa-rup": {
haswikt: 1
},
ru: {
alt: 1,
g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"],
haswikt: 1,
sc: "Cyrl"
},
rue: {
g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"],
sc: "Cyrl"
},
ruo: {
g: ["m", "f", "n", "m-p", "f-p", "n-p"]
},
rup: {
g: ["m", "f", "n", "m-p", "f-p", "n-p"],
wiktprefix: "roa-rup"
},
ruq: {
g: ["m", "f", "n", "m-p", "f-p", "n-p"]
},
rw: {
haswikt: 1
},
sa: {
g: ["m", "f", "n", "m-p", "f-p", "n-p"],
haswikt: 1,
sc: "Deva"
},
sah: {
sc: "Cyrl"
},
sc: {
haswikt: 1
},
scn: {
g: ["m", "f", "m-p", "f-p"],
haswikt: 1
},
sco: {
sc: "Latn"
},
sd: {
haswikt: 1,
sc: "Arab",
wsc: "sd-Arab"
},
sdh: {
wiktprefix: "ku"
},
sg: {
haswikt: 1
},
sh: {
alt: 1,
g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"],
haswikt: 1,
sc: ["Latn", "Cyrl"]
},
si: {
haswikt: 1,
sc: "Sinh"
},
simple: {
haswikt: 1
},
sk: {
g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"],
haswikt: 1
},
sl: {
alt: 1,
g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"],
haswikt: 1
},
sm: {
haswikt: 1
},
sn: {
haswikt: 1
},
so: {
haswikt: 1
},
spx: {
sc: "Ital"
},
sq: {
g: ["m", "f"],
haswikt: 1
},
ss: {
haswikt: 1
},
st: {
haswikt: 1
},
su: {
haswikt: 1
},
sux: {
sc: "Xsux"
},
sv: {
g: ["c", "n", "c-p", "n-p"],
haswikt: 1
},
sva: {
sc: "Geor"
},
sw: {
nclass: 1,
haswikt: 1
},
syc: {
sc: "Syrc"
},
syr: {
sc: "Syrc"
},
szl: {
g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"]
},
ta: {
haswikt: 1,
sc: "Taml"
},
tdd: {
sc: "Tale"
},
te: {
haswikt: 1,
sc: "Telu"
},
tg: {
g: ["s", "p"],
haswikt: 1,
sc: "Cyrl"
},
th: {
haswikt: 1,
sc: "Thai"
},
ti: {
haswikt: 1,
sc: "Ethi"
},
tig: {
sc: "Ethi"
},
tiw: {
sc: "Latn"
},
tk: {
g: ["s", "p"],
haswikt: 1
},
tl: {
haswikt: 1,
sc: ["Latn", "Tglg"]
},
tmr: {
sc: "Hebr"
},
tn: {
haswikt: 1
},
to: {
haswikt: 1
},
tpi: {
haswikt: 1
},
tr: {
g: ["s", "p"],
alt: 1,
haswikt: 1
},
ts: {
haswikt: 1
},
tt: {
g: ["s", "p"],
haswikt: 1
},
tts: {
sc: "Thai"
},
tw: {
haswikt: 1
},
udi: {
sc: ["Cyrl", "Latn", "Armn", "Geor"]
},
ug: {
g: ["s", "p"],
haswikt: 1,
sc: "Arab",
wsc: "ug-Arab"
},
uga: {
sc: "Ugar"
},
uk: {
g: ["m", "f", "n", "m-p", "f-p", "n-p", "p", "impf", "pf"],
haswikt: 1,
sc: "Cyrl"
},
ulk: {
sc: "Latn"
},
ur: {
g: ["m", "f", "m-p", "f-p", "p"],
haswikt: 1,
sc: "Arab",
wsc: "ur-Arab"
},
uz: {
g: ["s", "p"],
haswikt: 1
},
vec: {
g: ["m", "f", "m-p", "f-p"],
haswikt: 1
},
vi: {
haswikt: 1
},
vls: {
g: ["m", "f", "n", "m-p", "f-p", "n-p"]
},
vo: {
haswikt: 1
},
wa: {
haswikt: 1
},
wbp: {
sc: "Latn"
},
wo: {
haswikt: 1
},
wuu: {
sc: "Hani"
},
xae: {
sc: "Ital"
},
xcr: {
sc: "Cari"
},
xfa: {
sc: "Ital"
},
xh: {
alt: 1,
nclass: 1,
haswikt: 1
},
xlc: {
sc: "Lyci"
},
xld: {
sc: "Lydi"
},
xlu: {
sc: "Xsux"
},
xmf: {
sc: "Geor"
},
xno: {
g: ["m", "f", "m-p", "f-p"]
},
xrr: {
sc: "Ital"
},
xst: {
sc: "Ethi"
},
xum: {
sc: "Ital"
},
xve: {
sc: "Ital"
},
xvo: {
sc: "Ital"
},
yi: {
g: ["m", "f", "n", "m-p", "f-p", "n-p", "p"],
haswikt: 1,
sc: "Hebr"
},
yo: {
haswikt: 1
},
yua: {
g: ["s", "p"],
alt: 1
},
yue: {
sc: "Hani"
},
za: {
haswikt: 1,
sc: "Latn"
},
"zh-classical": {
sc: "Hani"
},
"zh-min-nan": {
haswikt: 1
},
"zh-yue": {
sc: "Hani"
},
zu: {
alt: 1,
nclass: 1,
haswikt: 1
}
};
var clean = {
aar: "aa",
afar: "aa",
abk: "ab",
abkhazian: "ab",
afr: "af",
afrikaans: "af",
aka: "ak",
akan: "ak",
amh: "am",
amharic: "am",
ara: "ar",
arabic: "ar",
arg: "an",
aragonese: "an",
asm: "as",
assamese: "as",
ava: "av",
avaric: "av",
ave: "ae",
avestan: "ae",
aym: "ay",
aymara: "ay",
aze: "az",
azerbaijani: "az",
bak: "ba",
bashkir: "ba",
bam: "bm",
bambara: "bm",
bel: "be",
belarusian: "be",
ben: "bn",
bengali: "bn",
bis: "bi",
bislama: "bi",
bod: "bo",
tibetan: "bo",
bs: "sh",
bos: "sh",
bosnian: "sh",
bre: "br",
breton: "br",
bul: "bg",
bulgarian: "bg",
cat: "ca",
catalan: "ca",
ces: "cs",
czech: "cs",
cha: "ch",
chamorro: "ch",
che: "ce",
chechen: "ce",
chu: "cu",
churchslavic: "cu",
chv: "cv",
chuvash: "cv",
cor: "kw",
cornish: "kw",
cos: "co",
corsican: "co",
cre: "cr",
cree: "cr",
cym: "cy",
welsh: "cy",
dan: "da",
danish: "da",
deu: "de",
german: "de",
div: "dv",
dhivehi: "dv",
dzo: "dz",
dzongkha: "dz",
ekk: "et",
ell: "el",
greek: "el",
eng: "en",
english: "en",
epo: "eo",
esperanto: "eo",
est: "et",
estonian: "et",
eus: "eu",
basque: "eu",
ewe: "ee",
fao: "fo",
faroese: "fo",
fas: "fa",
//"fa-ira": "fa",
//"fa-cls": "fa",
//prs: "fa",
persian: "fa",
fij: "fj",
fijian: "fj",
fil: "tl",
fin: "fi",
finnish: "fi",
fra: "fr",
french: "fr",
fry: "fy",
westernfrisian: "fy",
ful: "ff",
fulah: "ff",
gla: "gd",
scottishgaelic: "gd",
gle: "ga",
irish: "ga",
glg: "gl",
galician: "gl",
guj: "gu",
gujarati: "gu",
gn: "gug",
grn: "gug",
guarani: "gug",
glv: "gv",
manx: "gv",
hat: "ht",
haitian: "ht",
hau: "ha",
hausa: "ha",
heb: "he",
hebrew: "he",
her: "hz",
herero: "hz",
hin: "hi",
hindi: "hi",
hmo: "ho",
hirimotu: "ho",
hr: "sh",
hrv: "sh",
croatian: "sh",
hun: "hu",
hungarian: "hu",
hye: "hy",
armenian: "hy",
ibo: "ig",
igbo: "ig",
ido: "io",
iii: "ii",
sichuanyi: "ii",
iku: "iu",
inuktitut: "iu",
ile: "ie",
interlingue: "ie",
ina: "ia",
interlingua: "ia",
ind: "id",
indonesian: "id",
ipk: "ik",
inupiaq: "ik",
isl: "is",
icelandic: "is",
ita: "it",
italian: "it",
jav: "jv",
javanese: "jv",
jpn: "ja",
japanese: "ja",
kal: "kl",
kalaallisut: "kl",
kan: "kn",
kannada: "kn",
kas: "ks",
kashmiri: "ks",
kat: "ka",
georgian: "ka",
kau: "kr",
kanuri: "kr",
kaz: "kk",
kazakh: "kk",
khm: "km",
centralkhmer: "km",
kik: "ki",
kikuyu: "ki",
kin: "rw",
kinyarwanda: "rw",
kir: "ky",
kirghiz: "ky",
kom: "kv",
komi: "kv",
kon: "kg",
kongo: "kg",
kor: "ko",
korean: "ko",
ksh: "gmw-cfr",
kua: "kj",
kuanyama: "kj",
kv: "kpv",
lao: "lo",
lat: "la",
latin: "la",
lav: "lv",
latvian: "lv",
lim: "li",
limburgan: "li",
lin: "ln",
lingala: "ln",
lit: "lt",
lithuanian: "lt",
ltz: "lb",
luxembourgish: "lb",
lub: "lu",
lubakatanga: "lu",
lug: "lg",
ganda: "lg",
mah: "mh",
marshallese: "mh",
mal: "ml",
malayalam: "ml",
mar: "mr",
marathi: "mr",
chm: "mhr",
mari: "mhr",
mkd: "mk",
macedonian: "mk",
mlg: "mg",
malagasy: "mg",
mlt: "mt",
maltese: "mt",
mon: "mn",
mongolian: "mn",
mri: "mi",
maori: "mi",
msa: "ms",
malay: "ms",
mya: "my",
burmese: "my",
nau: "na",
nauru: "na",
hokkien: "nan-hbl",
hokkienese: "nan-hbl",
fujian: "nan-hbl",
fujianese: "nan-hbl",
hainam: "hnm",
hainan: "hnm",
hainanese: "hnm",
leizhou: "luh",
"zhx-lui": "luh",
teochew: "nan-tws",
teochewese: "nan-tws",
"zhx-teo": "nan-tws",
wep: "nds",
westphalian: "nds",
germanlowgerman: "nds",
dutchlowsaxon: "nds",
act: "nds",
achterhoeks: "nds",
drt: "nds",
drents: "nds",
gos: "nds",
gronings: "nds",
sdz: "nds",
sallands: "nds",
stl: "nds",
stellingwerfs: "nds",
twd: "nds",
twents: "nds",
vel: "nds",
veluws: "nds",
nav: "nv",
navajo: "nv",
nbl: "nr",
southndebele: "nr",
nde: "nd",
northndebele: "nd",
ndo: "ng",
ndonga: "ng",
nep: "ne",
nepali: "ne",
nld: "nl",
dutch: "nl",
flemish: "nl",
nno: "nn",
norwegiannynorsk: "nn",
nob: "nb",
norwegianbokmal: "nb",
nor: "no",
norwegian: "no",
nya: "ny",
nyanja: "ny",
oci: "oc",
occitan: "oc",
oji: "oj",
ojibwa: "oj",
ori: "or",
oriya: "or",
orm: "om",
oromo: "om",
oss: "os",
ossetian: "os",
pan: "pa",
panjabi: "pa",
pfl: "gmw-rfr",
pli: "pi",
pali: "pi",
pol: "pl",
polish: "pl",
por: "pt",
portuguese: "pt",
pus: "ps",
pushto: "ps",
que: "qu",
quechua: "qu",
roh: "rm",
romansh: "rm",
ron: "ro",
romanian: "ro",
rn: "rw",
run: "rw",
rundi: "rw",
rus: "ru",
russian: "ru",
sag: "sg",
sango: "sg",
san: "sa",
sanskrit: "sa",
sin: "si",
sinhala: "si",
slk: "sk",
slovak: "sk",
slv: "sl",
slovenian: "sl",
sme: "se",
northernsami: "se",
smo: "sm",
samoan: "sm",
sna: "sn",
shona: "sn",
snd: "sd",
sindhi: "sd",
som: "so",
somali: "so",
sot: "st",
southernsotho: "st",
spa: "es",
spanish: "es",
sqi: "sq",
albanian: "sq",
srd: "sc",
sardinian: "sc",
sr: "sh",
srp: "sh",
serbian: "sh",
ssw: "ss",
swati: "ss",
sun: "su",
sundanese: "su",
swa: "sw",
swahili: "sw",
swe: "sv",
swedish: "sv",
tah: "ty",
tahitian: "ty",
tam: "ta",
tamil: "ta",
tat: "tt",
tatar: "tt",
tel: "te",
telugu: "te",
tgk: "tg",
taishanese: "zhx-tai",
tajik: "tg",
tgl: "tl",
tagalog: "tl",
tha: "th",
thai: "th",
tir: "ti",
tigrinya: "ti",
ton: "to",
tonga: "to",
tsn: "tn",
tswana: "tn",
tso: "ts",
tsonga: "ts",
tuk: "tk",
turkmen: "tk",
tur: "tr",
turkish: "tr",
twi: "tw",
uig: "ug",
uighur: "ug",
ukr: "uk",
ukrainian: "uk",
urd: "ur",
urdu: "ur",
uzb: "uz",
uzbek: "uz",
ven: "ve",
venda: "ve",
vie: "vi",
vietnamese: "vi",
westflemish: "vls",
vol: "vo",
volapuk: "vo",
wln: "wa",
walloon: "wa",
wol: "wo",
wolof: "wo",
xho: "xh",
xhosa: "xh",
yid: "yi",
yiddish: "yi",
yor: "yo",
yoruba: "yo",
zbc: "lod",
zbe: "lod",
zbw: "lod",
zha: "za",
zhuang: "za",
zh: "cmn",
zho: "cmn",
chinese: "cmn",
zul: "zu",
zulu: "zu",
};
// }}}
// FIXME: merge into above
// These nesting rules have been derived from Benwing's script sort_and_reformat_translations.py
// https://github.com/benwing2/WingerBot/blob/master/sort_and_reformat_translations.py
// Generated by the script at https://gist.github.com/atlight/5c0f6fdc846cea5609064003a4ced569
var nesting = {
"abc": "Ayta",
"abd": "Agta",
"abh": "Arabic",
"abp": "Ayta",
"abv": "Arabic",
"acm": "Arabic",
"acw": "Arabic",
"acx": "Arabic",
"acy": "Arabic",
"adf": "Arabic",
"aeb": "Arabic",
"aee": "Pashayi",
"aer": "Arrernte",
"afb": "Arabic",
"agk": "Agta",
"agt": "Agta",
"agv": "Agta",
"agy": "Alta",
"agz": "Agta",
"ahi": "Aizi",
"ahm": "Aizi",
"ahp": "Aizi",
"aii": "Aramaic",
"aij": "Aramaic",
"ajp": "Arabic",
"akb": "Batak",
"alt": "Altai",
"ams": "Amami \u014cshima",
"amu": "Amuzgo",
"amw": "Aramaic",
"aou": "Gelao",
"apc": "Arabic",
"apd": "Arabic",
"apf": "Agta",
"apj": "Apache",
"apk": "Apache",
"apl": "Apache",
"apm": "Apache",
"apw": "Apache",
"aqd": "Dogon",
"aqn": "Alta",
"are": "Arrernte",
"arq": "Arabic",
"ars": "Arabic",
"ary": "Arabic",
"arz": "Arabic",
"asc": "Asmat",
"asy": "Asmat",
"atd": "Manobo",
"atj": "Cree",
"atl": "Agta",
"atp": "Atta",
"att": "Atta",
"atv": "Altai",
"aun": "One",
"auz": "Arabic",
"aws": "Awyu",
"awu": "Awyu",
"awv": "Awyu",
"awy": "Awyu",
"axm": "Armenian",
"ayl": "Arabic",
"ayn": "Arabic",
"ayp": "Arabic",
"ays": "Ayta",
"ayt": "Ayta",
"ayy": "Ayta",
"azd": "Nahuatl",
"azg": "Amuzgo",
"azm": "Amuzgo",
"azn": "Nahuatl",
"azt": "Atta",
"azz": "Nahuatl",
"bar": "German",
"bbc": "Batak",
"bca": "Bai",
"bcl": "Bikol",
"bdl": "Bajau",
"bdr": "Bajau",
"bet": "B\u00e9t\u00e9",
"bev": "B\u00e9t\u00e9",
"bfb": "Bareli",
"bfc": "Bai",
"bfo": "Birifor",
"bfs": "Bai",
"bgd": "Bareli",
"bgo": "Baga",
"bgr": "Chin",
"bgx": "Turkish",
"bhn": "Aramaic",
"bhq": "Tukang Besi",
"bhv": "Kayan",
"biv": "Birifor",
"bjf": "Aramaic",
"bjx": "Itneg",
"bks": "Sorsogon",
"blk": "Karen",
"bln": "Bikol",
"blx": "Ayta",
"bmd": "Baga",
"bnj": "Tawbuid",
"bnt-sbo": "Boma",
"boh": "Boma",
"boo": "Bozo",
"boz": "Bozo",
"bpn": "Chinese",
"bpq": "Malay",
"bpr": "Blaan",
"bps": "Blaan",
"bpx": "Bareli",
"bpy": "Manipuri",
"bqf": "Baga",
"bru": "Bru",
"brv": "Bru",
"bsp": "Baga",
"bsv": "Baga",
"btd": "Batak",
"btg": "B\u00e9t\u00e9",
"bth": "Bidayuh",
"btj": "Malay",
"btm": "Batak",
"bto": "Bikol",
"bts": "Batak",
"btx": "Batak",
"btz": "Batak",
"buh": "Bunu",
"bve": "Malay",
"bvu": "Malay",
"bwe": "Karen",
"bwn": "Bunu",
"bwx": "Bunu",
"bya": "Batak",
"bze": "Bozo",
"bzh": "Buang",
"bzx": "Bozo",
"caq": "Nicobarese",
"cbj": "Ede",
"cbl": "Chin",
"cco": "Chinantec",
"cdo": "Chinese",
"cfm": "Chin",
"chd": "Chontal",
"chj": "Chinantec",
"chq": "Chinantec",
"chz": "Chinantec",
"cib": "Gbe",
"cja": "Cham",
"cjm": "Cham",
"cjo": "Ash\u00e9ninka",
"cjy": "Chinese",
"ckb": "Kurdish",
"ckn": "Chin",
"cld": "Aramaic",
"cle": "Chinantec",
"clj": "Chin",
"clo": "Chontal",
"cly": "Chatino",
"cmg": "Mongolian",
"cmn": "Chinese",
"cmo": "Mnong",
"cmr": "Chin",
"cnb": "Chin",
"cng": "Qiang",
"cnk": "Chin",
"cnl": "Chinantec",
"cnp": "Chinese",
"cns": "Asmat",
"cnt": "Chinantec",
"cnw": "Chin",
"cnx": "Cornish",
"cpa": "Chinantec",
"cpb": "Ash\u00e9ninka",
"cpc": "Ash\u00e9ninka",
"cpg": "Greek",
"cpu": "Ash\u00e9ninka",
"cpx": "Chinese",
"cpy": "Ash\u00e9ninka",
"cqd": "Hmong",
"crj": "Cree",
"crk": "Cree",
"crl": "Cree",
"crm": "Cree",
"crp-mpp": "Portuguese",
"crq": "Chorote",
"crt": "Chorote",
"csa": "Chinantec",
"csh": "Chin",
"csi": "Miwok",
"csj": "Chin",
"csm": "Miwok",
"cso": "Chinantec",
"csp": "Chinese",
"css": "Ohlone",
"cst": "Ohlone",
"csv": "Chin",
"csw": "Cree",
"csy": "Chin",
"cta": "Chatino",
"ctd": "Chin",
"cte": "Chinantec",
"cth": "Chin",
"ctl": "Chinantec",
"ctp": "Chatino",
"cts": "Bikol",
"ctz": "Chatino",
"cuc": "Chinantec",
"cut": "Cuicatec",
"cux": "Cuicatec",
"cvn": "Chinantec",
"cwd": "Cree",
"cya": "Chatino",
"czh": "Chinese",
"czn": "Chatino",
"czo": "Chinese",
"czt": "Chin",
"dao": "Chin",
"daq": "Maria",
"dbg": "Dogon",
"dbt": "Dogon",
"dbu": "Dogon",
"dbw": "Dogon",
"dds": "Dogon",
"dgb": "Dogon",
"dgc": "Agta",
"dib": "Dinka",
"dic": "Dida",
"dik": "Dinka",
"dip": "Dinka",
"diw": "Dinka",
"djm": "Dogon",
"dks": "Dinka",
"dmb": "Dogon",
"dna": "Dani",
"dng": "Chinese",
"dni": "Dani",
"dnt": "Dani",
"dnw": "Dani",
"doc": "Kam",
"dra-mkn": "Kannada",
"dra-okn": "Kannada",
"dra-ote": "Telugu",
"dro": "Melanau",
"dsb": "Sorbian",
"dtb": "Kadazan",
"dti": "Dogon",
"dtk": "Dogon",
"dtm": "Dogon",
"dto": "Dogon",
"dtp": "Dusun",
"dts": "Dogon",
"dtt": "Dogon",
"dtu": "Dogon",
"due": "Agta",
"dul": "Agta",
"dum": "Dutch",
"duo": "Agta",
"duy": "Agta",
"dyg": "Agta",
"dyi": "Senoufo",
"dym": "Dogon",
"ebk": "Bontoc",
"egx-dem": "Egyptian",
"eky": "Kayah",
"emg": "Meohang",
"emk": "Maninkakan",
"emu": "Muria",
"enc": "Buyang",
"enf": "Enets",
"enh": "Enets",
"erk": "Efate",
"fal": "Fali",
"fbl": "Bikol",
"fll": "Fali",
"fmu": "Muria",
"frm": "French",
"fro": "French",
"frr": "Frisian",
"fy": "Frisian",
"gan": "Chinese",
"gas": "Garasia",
"gbh": "Gbe",
"gbo": "Grebo",
"gbs": "Gbe",
"gbx": "Gbe",
"gec": "Grebo",
"ggn": "Gurung",
"ghe": "Ghale",
"ghh": "Ghale",
"ghk": "Karen",
"ght": "Ghale",
"gie": "Dida",
"giq": "Gelao",
"gir": "Gelao",
"gis": "Giziga",
"giu": "Gelao",
"giw": "Gelao",
"giz": "Giziga",
"gjk": "Koli",
"gkp": "Kpelle",
"glh": "Pashayi",
"gmh": "German",
"gml": "Low German",
"gmq-mno": "Norwegian",
"gmq-oda": "Danish",
"gmq-ogt": "Gutnish",
"gmq-osw": "Swedish",
"gmw-cfr": "German",
"gmw-ecg": "German",
"gmw-jdt": "Dutch",
"gmw-msc": "Scots",
"gmw-rfr": "German",
"gmy": "Greek",
"gn-cls": "Guarani",
"gnw": "Guarani",
"goh": "German",
"gqu": "Gelao",
"gra": "Garasia",
"grc": "Greek",
"grj": "Grebo",
"grk-cal": "Greek",
"grk-ita": "Greek",
"grk-mar": "Greek",
"grv": "Grebo",
"gry": "Grebo",
"gsw": "German",
"gud": "Dida",
"gug": "Guarani",
"gui": "Guarani",
"gun": "Guarani",
"gvr": "Gurung",
"gxx": "Wee",
"hak": "Chinese",
"hax": "Haida",
"hdn": "Haida",
"hea": "Hmong",
"hma": "Hmong",
"hmb": "Senni",
"hmc": "Hmong",
"hmd": "Hmong",
"hme": "Hmong",
"hmf": "Hmong",
"hmg": "Hmong",
"hmh": "Hmong",
"hmi": "Hmong",
"hmj": "Hmong",
"hml": "Hmong",
"hmm": "Hmong",
"hmp": "Hmong",
"hmq": "Hmong",
"hms": "Hmong",
"hmv": "Hmong",
"hmw": "Hmong",
"hmy": "Hmong",
"hmz": "Hmong",
"hnd": "Hindko",
"hnj": "Hmong",
"hnm": "Chinese",
"hno": "Hindko",
"hrm": "Hmong",
"hrt": "Aramaic",
"hsb": "Sorbian",
"hsn": "Chinese",
"hto": "Huitoto",
"huj": "Hmong",
"huu": "Huitoto",
"hux": "Huitoto",
"huy": "Aramaic",
"ica": "Ede",
"idd": "Ede",
"ifa": "Ifugao",
"ifb": "Ifugao",
"ife": "Ede",
"ifk": "Ifugao",
"ifu": "Ifugao",
"ify": "Kallahan",
"ijj": "Ede",
"ike": "Inuktitut",
"inc-ash": "Prakrit",
"inc-kam": "Prakrit",
"inc-mas": "Assamese",
"inc-mbn": "Bengali",
"inc-mgu": "Gujarati",
"inc-mor": "Odia",
"inc-oas": "Assamese",
"inc-oaw": "Awadhi",
"inc-obn": "Bengali",
"inc-ogu": "Gujarati",
"inc-ohi": "Hindi",
"inc-oor": "Odia",
"inc-opa": "Punjabi",
"itb": "Itneg",
"itd": "Tidung",
"iti": "Itneg",
"itt": "Itneg",
"ity": "Itneg",
"jas": "Javanese",
"jax": "Malay",
"jiu": "Jino",
"jiy": "Jino",
"jkp": "Karen",
"jmn": "Naga",
"jmx": "Mixtec",
"jvn": "Javanese",
"kak": "Kallahan",
"kaw": "Javanese",
"kbk": "Koiari",
"kca-eas": "Khanty",
"kca-nor": "Khanty",
"kca-sou": "Khanty",
"kee": "Keres",
"kfd": "Koraga",
"kfw": "Naga",
"kgj": "Kham",
"khc": "Tukang Besi",
"kif": "Kham",
"kip": "Kham",
"kiw": "Kiwai",
"kix": "Naga",
"kjd": "Kiwai",
"kjl": "Kham",
"kjp": "Pwo",
"kjq": "Keres",
"kjt": "Pwo",
"kju": "Pomo",
"kkg": "Kalinga",
"klg": "Kalagan",
"kll": "Kalagan",
"kmc": "Kam",
"kmd": "Kalinga",
"kmj": "Paharia",
"kmk": "Kalinga",
"kml": "Kalinga",
"kmr": "Kurdish",
"kmz": "Turkish",
"knb": "Kalinga",
"ko-ear": "Korean",
"koi": "Komi",
"kpv": "Komi",
"kpx": "Koiari",
"kqd": "Aramaic",
"kqk": "Gbe",
"kqo": "Krahn",
"kqs": "Kissi",
"kqt": "Kadazan",
"krw": "Krahn",
"ksc": "Kalinga",
"kss": "Kissi",
"ksw": "Karen",
"kti": "Muyu",
"ktj": "Krumen",
"kts": "Muyu",
"ktv": "Katu",
"kuf": "Katu",
"kvl": "Karen",
"kvq": "Karen",
"kvt": "Karen",
"kvu": "Karen",
"kvx": "Koli",
"kvy": "Karen",
"kxd": "Malay",
"kxf": "Karen",
"kxi": "Murut",
"kxk": "Karen",
"kxm": "Khmer",
"kxp": "Koli",
"kyb": "Kalinga",
"kyu": "Kayah",
"kza": "Karaboro",
"kzc": "Kulango",
"kzf": "Kaili",
"kzj": "Kadazan",
"kzs": "Dusun",
"kzt": "Dusun",
"laa": "Subanen",
"lay": "Bai",
"lbk": "Bontoc",
"lbl": "Bikol",
"lbr": "Lorung",
"lcp": "Lawa",
"len": "Lenca",
"lew": "Kaili",
"lhs": "Aramaic",
"lia": "Limba",
"lki": "Kurdish",
"llp": "Efate",
"lma": "Limba",
"lmw": "Miwok",
"low": "Lobu",
"lpn": "Naga",
"lrc": "Luri",
"lrr": "Lorung",
"lrt": "Malay",
"lsd": "Aramaic",
"ltc": "Chinese",
"luh": "Chinese",
"luz": "Luri",
"lwl": "Lawa",
"lzh": "Chinese",
"lzn": "Naga",
"maa": "Mazatec",
"mab": "Mixtec",
"maj": "Mazatec",
"man": "Mandingo",
"maq": "Mazatec",
"mau": "Mazatec",
"maz": "Mazahua",
"mbb": "Manobo",
"mbd": "Manobo",
"mbi": "Manobo",
"mbs": "Manobo",
"mbt": "Manobo",
"mbz": "Mixtec",
"mce": "Mixtec",
"mco": "Mixe",
"mcu": "Mambila",
"mcy": "Watut",
"mdv": "Mixtec",
"meh": "Mixtec",
"mel": "Melanau",
"meo": "Malay",
"mey": "Arabic",
"mfa": "Malay",
"mga": "Irish",
"mgp": "Magar",
"mhp": "Malay",
"mhr": "Mari",
"mib": "Mixtec",
"mid": "Aramaic",
"mie": "Mixtec",
"mig": "Mixtec",
"mih": "Mixtec",
"mii": "Mixtec",
"mil": "Mixtec",
"mim": "Mixtec",
"mio": "Mixtec",
"mip": "Mixtec",
"mir": "Mixe",
"mis-hkl": "Chinese",
"mis-tnw": "Chinese",
"mit": "Mixtec",
"miu": "Mixtec",
"mix": "Mixtec",
"miy": "Mixtec",
"miz": "Mixtec",
"mjc": "Mixtec",
"mjd": "Maidu",
"mjt": "Paharia",
"mkh-mmn": "Mon",
"mkh-mvi": "Vietnamese",
"mkn": "Malay",
"mkq": "Miwok",
"mks": "Mixtec",
"mku": "Mandingo",
"mkx": "Manobo",
"mlq": "Maninkakan",
"mmc": "Mazahua",
"mmo": "Buang",
"mmr": "Hmong",
"mng": "Mnong",
"mnk": "Mandingo",
"mnn": "Mnong",
"mnp": "Chinese",
"mns-cen": "Mansi",
"mns-nor": "Mansi",
"mns-sou": "Mansi",
"mnw-tha": "Mon",
"moe": "Cree",
"mpl": "Watut",
"mpm": "Mixtec",
"mqg": "Malay",
"mqh": "Mixtec",
"mqk": "Manobo",
"mqm": "Marquesan",
"mrd": "Magar",
"mrh": "Chin",
"mrj": "Mari",
"mrq": "Marquesan",
"mrr": "Maria",
"msc": "Mandingo",
"msi": "Malay",
"msm": "Manobo",
"mss": "Masela",
"mta": "Manobo",
"mto": "Mixe",
"mtu": "Mixtec",
"mtx": "Mixtec",
"muq": "Hmong",
"mut": "Muria",
"mvg": "Mixtec",
"mvv": "Murut",
"mwk": "Maninkakan",
"mwq": "Chin",
"mww": "Hmong",
"mxa": "Mixtec",
"mxb": "Mixtec",
"mxl": "Gbe",
"mxp": "Mixe",
"mxq": "Mixe",
"mxs": "Mixtec",
"mxt": "Mixtec",
"mxv": "Mixtec",
"mxy": "Mixtec",
"mxz": "Masela",
"myk": "Senoufo",
"myz": "Aramaic",
"mza": "Mixtec",
"mzi": "Mazatec",
"mzk": "Mambila",
"mzl": "Mixe",
"nai-sln": "Lenca",
"nan": "Chinese",
"nan-dat": "Chinese",
"nan-hbl": "Chinese",
"nan-hlh": "Chinese",
"nan-lnx": "Chinese",
"nan-tws": "Chinese",
"nan-zhe": "Chinese",
"nan-zsh": "Chinese",
"naz": "Nahuatl",
"nb": "Norwegian/Bokm\u00e5l",
"nbe": "Naga",
"nbi": "Naga",
"nbu": "Naga",
"nbw": "Ngbandi",
"ncb": "Nicobarese",
"nch": "Nahuatl",
"nci": "Nahuatl",
"ncj": "Nahuatl",
"ncl": "Nahuatl",
"nct": "Naga",
"ncx": "Nahuatl",
"nd": "Ndebele",
"nds-de": "Low German",
"nds-nl": "Low German",
"neq": "Mixe",
"nes": "Bhoti",
"nfr": "Senoufo",
"ngb": "Ngbandi",
"ngu": "Nahuatl",
"nhc": "Nahuatl",
"nhe": "Nahuatl",
"nhg": "Nahuatl",
"nhi": "Nahuatl",
"nhk": "Nahuatl",
"nhm": "Nahuatl",
"nhn": "Nahuatl",
"nhp": "Nahuatl",
"nhq": "Nahuatl",
"nht": "Nahuatl",
"nhv": "Nahuatl",
"nhw": "Nahuatl",
"nhx": "Nahuatl",
"nhy": "Nahuatl",
"nhz": "Nahuatl",
"nik": "Nicobarese",
"njh": "Naga",
"njn": "Naga",
"nkb": "Naga",
"nkf": "Naga",
"nkh": "Naga",
"nki": "Naga",
"nks": "Asmat",
"nku": "Kulango",
"nlk": "Yali",
"nlq": "Naga",
"nlv": "Nahuatl",
"nma": "Naga",
"nme": "Naga",
"nmf": "Naga",
"nmh": "Naga",
"nmo": "Naga",
"nmu": "Maidu",
"nn": "Norwegian/Nynorsk",
"nng": "Naga",
"nni": "Nuaulu",
"nnl": "Naga",
"nnw": "Nuni",
"nod": "Thai",
"nos": "Nisu",
"npl": "Nahuatl",
"npo": "Naga",
"npu": "Naga",
"nqg": "Ede",
"nqk": "Ede",
"nqq": "Naga",
"nr": "Ndebele",
"nre": "Naga",
"nri": "Naga",
"nrt": "Kalapuya",
"nsa": "Naga",
"nsd": "Nisu",
"nsk": "Cree",
"nsl": "Norwegian",
"nsq": "Miwok",
"nsu": "Nahuatl",
"nsv": "Nisu",
"nsz": "Maidu",
"ntd": "Tidung",
"ntp": "Tepehuan",
"nuv": "Nuni",
"nuz": "Nahuatl",
"nwc": "Newar",
"nwx": "Newar",
"nxl": "Nuaulu",
"nzm": "Naga",
"nzz": "Dogon",
"oav": "Avar",
"obk": "Bontoc",
"obo": "Manobo",
"obr": "Burmese",
"obt": "Breton",
"och": "Chinese",
"oco": "Cornish",
"odt": "Dutch",
"ofs": "Frisian",
"oge": "Georgian",
"ohu": "Hungarian",
"oin": "One",
"ojb": "Ojibwa",
"ojc": "Ojibwa",
"ojg": "Ojibwa",
"ojp": "Japanese",
"ojs": "Ojibwa",
"ojw": "Ojibwa",
"okk": "One",
"okm": "Korean",
"oko": "Korean",
"okz": "Khmer",
"olt": "Lithuanian",
"omp": "Manipuri",
"omq-sjq": "Chatino",
"omq-tel": "Mixtec",
"omq-teo": "Chatino",
"omr": "Marathi",
"omw": "Tairora",
"omx": "Mon",
"onk": "One",
"onr": "One",
"ort": "Odia",
"osn": "Sundanese",
"osp": "Spanish",
"osu": "One",
"ota": "Turkish",
"otb": "Tibetan",
"ote": "Otomi",
"otl": "Otomi",
"otm": "Otomi",
"otn": "Otomi",
"otq": "Otomi",
"ots": "Otomi",
"ott": "Otomi",
"otx": "Otomi",
"oty": "Tamil",
"otz": "Otomi",
"owl": "Welsh",
"pal": "Persian",
"pbe": "Popoloca",
"pbf": "Popoloca",
"pbm": "Mazatec",
"pbs": "Pame",
"pca": "Popoloca",
"pce": "Palaung",
"peb": "Pomo",
"pef": "Pomo",
"pej": "Pomo",
"peo": "Persian",
"peq": "Pomo",
"pez": "Penan",
"pga": "Arabic",
"pgl": "Irish",
"phi-din": "Agta",
"phi-nag": "Agta",
"pht": "Thai",
"plc": "Palawano",
"pll": "Palaung",
"plo": "Popoluca",
"plr": "Senoufo",
"pls": "Popoloca",
"plv": "Palawano",
"plw": "Palawano",
"pmi": "Pumi",
"pmj": "Pumi",
"pmq": "Pame",
"pmu": "Punjabi",
"pmw": "Miwok",
"pmx": "Naga",
"pmy": "Malay",
"pmz": "Pame",
"pne": "Penan",
"pnt": "Greek",
"pnu": "Bunu",
"poe": "Popoloca",
"poi": "Popoluca",
"pom": "Pomo",
"poo": "Pomo",
"poq": "Popoluca",
"pos": "Popoluca",
"pow": "Popoloca",
"poz-sml": "Malay",
"poz-ter": "Malay",
"pps": "Popoloca",
"pra-niy": "Prakrit",
"pro": "Occitan",
"prq": "Ash\u00e9ninka",
"psa": "Awyu",
"pse": "Malay",
"psh": "Pashayi",
"psi": "Pashayi",
"pwo": "Pwo",
"pww": "Pwo",
"pxm": "Mixe",
"pye": "Krumen",
"pzn": "Naga",
"qwc": "Quechua",
"qxs": "Qiang",
"raf": "Meohang",
"rbb": "Palaung",
"rbk": "Bontoc",
"rbl": "Bikol",
"rgs": "Roglai",
"rmc": "Romani",
"rmf": "Romani",
"rml": "Romani",
"rmn": "Romani",
"rmo": "Romani",
"rmu": "Romani",
"rmw": "Romani",
"rmy": "Romani",
"roa-oca": "Catalan",
"roa-ole": "Leonese",
"roc": "Roglai",
"rog": "Roglai",
"ruu": "Lobu",
"ryn": "Amami \u014cshima",
"sam": "Aramaic",
"sbd": "Samo",
"sbr": "Murut",
"sbu": "Bhoti",
"scs": "Slavey",
"sdh": "Kurdish",
"sdo": "Bidayuh",
"sdx": "Melanau",
"se": "Sami",
"sef": "Senoufo",
"sel-nor": "Selkup",
"sel-sou": "Selkup",
"sen": "Senoufo",
"sep": "Senoufo",
"seq": "Senoufo",
"ses": "Senni",
"sev": "Senoufo",
"sez": "Chin",
"sfe": "Subanen",
"sfm": "Hmong",
"sga": "Irish",
"sgb": "Ayta",
"shu": "Arabic",
"shz": "Senoufo",
"sia": "Sami",
"sjc": "Chinese",
"sjd": "Sami",
"sje": "Sami",
"sjk": "Sami",
"sjt": "Sami",
"sju": "Sami",
"skd": "Miwok",
"skn": "Subanen",
"slg": "Murut",
"slm": "Sama",
"sma": "Sami",
"smj": "Sami",
"sml": "Sama",
"smn": "Sami",
"smp": "Hebrew",
"sms": "Sami",
"sne": "Bidayuh",
"sou": "Thai",
"spp": "Senoufo",
"spt": "Bhoti",
"srk": "Murut",
"srv": "Sorsogon",
"ssb": "Sama",
"ssh": "Arabic",
"stb": "Subanen",
"sti": "Stieng",
"stj": "Samo",
"stp": "Tepehuan",
"stq": "Frisian",
"stt": "Stieng",
"suc": "Subanen",
"swb": "Comorian",
"swg": "German",
"sxk": "Kalapuya",
"sxw": "Gbe",
"syb": "Subanen",
"syc": "Aramaic",
"syd-fne": "Nenets",
"sym": "Samo",
"syn": "Aramaic",
"taa": "Tanana",
"tac": "Tarahumara",
"taj": "Tamang",
"tar": "Tarahumara",
"tau": "Tanana",
"tbg": "Tairora",
"tbn": "Tunebo",
"tce": "Tutchone",
"tcp": "Chin",
"tcu": "Tarahumara",
"tcw": "Totonac",
"tcz": "Chin",
"tde": "Dogon",
"tdg": "Tamang",
"tdu": "Dusun",
"ted": "Krumen",
"tee": "Tepehua",
"tfi": "Gbe",
"tge": "Tamang",
"tgw": "Senoufo",
"the": "Tharu",
"thh": "Tarahumara",
"thl": "Tharu",
"thq": "Tharu",
"thr": "Tharu",
"tih": "Murut",
"tis": "Itneg",
"tji": "Tujia",
"tjs": "Tujia",
"tkt": "Tharu",
"tku": "Totonac",
"tla": "Tepehuan",
"tlc": "Totonac",
"tlp": "Totonac",
"tmk": "Tamang",
"tnb": "Tunebo",
"tnd": "Tunebo",
"tne": "Kallahan",
"toc": "Totonac",
"too": "Totonac",
"top": "Totonac",
"tos": "Totonac",
"tpc": "Me'phaa",
"tpl": "Me'phaa",
"tpp": "Tepehua",
"tpt": "Tepehua",
"tpx": "Me'phaa",
"tqt": "Totonac",
"trc": "Triqui",
"tre": "Tarangan",
"trg": "Aramaic",
"trk-oat": "Turkish",
"trq": "Triqui",
"trs": "Triqui",
"tru": "Aramaic",
"tsd": "Greek",
"tsf": "Tamang",
"tsp": "Toussian",
"ttm": "Tutchone",
"tuf": "Tunebo",
"tvt": "Naga",
"twb": "Tawbuid",
"twr": "Tarahumara",
"txn": "Tarangan",
"ubl": "Bikol",
"umn": "Naga",
"una": "Watut",
"unz": "Kaili",
"urj-koo": "Komi",
"urj-kya": "Komi",
"vbk": "Bontoc",
"vkp": "Portuguese",
"vkt": "Malay",
"vmc": "Mixtec",
"vmd": "Koraga",
"vme": "Masela",
"vmf": "German",
"vmj": "Mixtec",
"vmm": "Mixtec",
"vmp": "Mazatec",
"vmq": "Mixtec",
"vmv": "Maidu",
"vmx": "Mixtec",
"vmy": "Mazatec",
"vmz": "Mazatec",
"wci": "Gbe",
"wec": "Wee",
"wem": "Gbe",
"wib": "Toussian",
"wlc": "Comorian",
"wlm": "Welsh",
"wni": "Comorian",
"wuu": "Chinese",
"wxa": "Chinese",
"xaa": "Arabic",
"xbm": "Breton",
"xcl": "Armenian",
"xct": "Tibetan",
"xhm": "Khmer",
"xkb": "Ede",
"xme-mid": "Median",
"xme-old": "Median",
"xmm": "Malay",
"xpe": "Kpelle",
"xrb": "Karaboro",
"xsl": "Slavey",
"xta": "Mixtec",
"xtb": "Mixtec",
"xtd": "Mixtec",
"xti": "Mixtec",
"xtj": "Mixtec",
"xtl": "Mixtec",
"xtm": "Mixtec",
"xtn": "Mixtec",
"xtp": "Mixtec",
"xts": "Mixtec",
"xtt": "Mixtec",
"xtu": "Mixtec",
"xty": "Mixtec",
"xwe": "Gbe",
"xwl": "Gbe",
"xzp": "Zapotec",
"yac": "Yali",
"yej": "Greek",
"yhl": "Phowa",
"yik": "Lalo",
"yim": "Naga",
"yir": "Awyu",
"yit": "Lalo",
"yiv": "Nisu",
"ykg": "Yukaghir",
"yli": "Yali",
"yln": "Buyang",
"ymc": "Muji",
"ymq": "Muji",
"ymx": "Muji",
"yok-bvy": "Yokuts",
"yok-dly": "Yokuts",
"yok-gsy": "Yokuts",
"yok-kry": "Yokuts",
"yok-nvy": "Yokuts",
"yok-ply": "Yokuts",
"yok-svy": "Yokuts",
"yok-tky": "Yokuts",
"ypb": "Phowa",
"ypn": "Phowa",
"yrk": "Nenets",
"yrn": "Buyang",
"yue": "Chinese",
"yux": "Yukaghir",
"ywl": "Lalo",
"ywt": "Lalo",
"yzg": "Buyang",
"zaa": "Zapotec",
"zab": "Zapotec",
"zac": "Zapotec",
"zad": "Zapotec",
"zae": "Zapotec",
"zaf": "Zapotec",
"zai": "Zapotec",
"zam": "Zapotec",
"zao": "Zapotec",
"zaq": "Zapotec",
"zar": "Zapotec",
"zas": "Zapotec",
"zat": "Zapotec",
"zav": "Zapotec",
"zaw": "Zapotec",
"zax": "Zapotec",
"zca": "Zapotec",
"zdj": "Comorian",
"zhx-sht": "Chinese",
"zhx-sic": "Chinese",
"zhx-tai": "Chinese",
"zls-chs": "Old Church Slavonic",
"zlw-ocs": "Czech",
"zlw-opl": "Polish",
"zlw-osk": "Slovak",
"zmi": "Malay",
"zoc": "Zoque",
"zoh": "Zoque",
"zoo": "Zapotec",
"zoq": "Zoque",
"zor": "Zoque",
"zos": "Zoque",
"zpa": "Zapotec",
"zpb": "Zapotec",
"zpc": "Zapotec",
"zpd": "Zapotec",
"zpe": "Zapotec",
"zpf": "Zapotec",
"zpg": "Zapotec",
"zph": "Zapotec",
"zpi": "Zapotec",
"zpj": "Zapotec",
"zpk": "Zapotec",
"zpl": "Zapotec",
"zpm": "Zapotec",
"zpn": "Zapotec",
"zpo": "Zapotec",
"zpp": "Zapotec",
"zpq": "Zapotec",
"zpr": "Zapotec",
"zps": "Zapotec",
"zpt": "Zapotec",
"zpu": "Zapotec",
"zpv": "Zapotec",
"zpw": "Zapotec",
"zpx": "Zapotec",
"zpy": "Zapotec",
"zpz": "Zapotec",
"zsr": "Zapotec",
"zte": "Zapotec",
"ztg": "Zapotec",
"ztl": "Zapotec",
"ztm": "Zapotec",
"ztn": "Zapotec",
"ztp": "Zapotec",
"ztq": "Zapotec",
"zts": "Zapotec",
"ztt": "Zapotec",
"ztu": "Zapotec",
"ztx": "Zapotec",
"zty": "Zapotec",
"aae": "Albanian/Arb\u00ebresh",
"aat": "Albanian/Arvanitika",
"als": "Albanian/Tosk",
"aln": "Albanian/Gheg",
"syr": "Aramaic",
"ksh": "German",
"pfl": "German",
"prs": "Persian/Dari",
"fa-cls": "Persian/Classical Persian",
"fa-ira": "Persian/Iranian Persian"
};
var stripArabicDiacritics = { strip: "\u064B\u064C\u064D\u064E\u064F\u0650\u0651\u0652" };
var stripGraveAndAcute = { strip: "\u0300\u0301" };
var fullToHalfWidthNumbers = {
from: "0123456789",
to: "0123456789"
};
// These should reflect the replacements made in [[Module:languages]], but should not necessarily be equal.
var diacriticStrippers = {
ang: {
from: "ĀāǢǣĊċĒēĠġĪīŌōŪūȲȳ",
to: "AaÆæCcEeGgIiOoUuYy",
strip: "\u0304\u0307",
}, //macron and above dot
ar: stripArabicDiacritics,
aao: stripArabicDiacritics,
acm: stripArabicDiacritics,
acx: stripArabicDiacritics,
adf: stripArabicDiacritics,
aeb: stripArabicDiacritics,
afb: stripArabicDiacritics,
ajp: stripArabicDiacritics,
apc: stripArabicDiacritics,
apd: stripArabicDiacritics,
arq: stripArabicDiacritics,
ary: stripArabicDiacritics,
arz: stripArabicDiacritics,
fa: stripArabicDiacritics,
ps: stripArabicDiacritics,
sd: stripArabicDiacritics,
ur: stripArabicDiacritics,
chl: {
from: "ÁáÉéÍíÓóÚú",
to: "AaEeIiOoUu",
strip: "\u0304",
}, //acute accent
he: {
strip: "\u05B0\u05B1\u05B2\u05B3\u05B4\u05B5\u05B6\u05B7\u05B8\u05B9\u05BA\u05BB\u05BC\u05BD\u05BF\u05C1\u05C2",
from: "-'\"",
to: "־׳״",
},
la: {
from: "ĀāĒēĪīŌōŪūȲȳ",
to: "AaEeIiOoUuYy",
strip: "\u0304",
}, //macron
lt: {
from: "áãàéẽèìýỹñóõòúù",
to: "aaaeeeiyynooouu",
strip: "\u0340\u0301\u0303",
},
nci: {
from: "ĀāĒēĪīŌōŪūȲȳ",
to: "AaEeIiOoUu",
strip: "\u0304",
}, //macron
//strip ́ and ̀ on Cyrillic Slavic languages, Serbo-Croatian has a longer list
ru: stripGraveAndAcute,
uk: stripGraveAndAcute,
be: stripGraveAndAcute,
bg: stripGraveAndAcute,
orv: stripGraveAndAcute,
cu: stripGraveAndAcute,
rue: stripGraveAndAcute,
mk: stripGraveAndAcute,
sh: {
from: "ȀȁÀàȂȃÁáĀāȄȅÈèȆȇÉéĒēȈȉÌìȊȋÍíĪīȌȍÒòȎȏÓóŌōȐȑȒȓŔŕȔȕÙùȖȗÚúŪūѝӣ",
to: "AaAaAaAaAaEeEeEeEeEeIiIiIiIiIiOoOoOoOoOoRrRrRrUuUuUuUuUuии",
strip: "\u030F\u0300\u0311\u0301\u0304",
},
sl: {
from: "áÁàÀâÂȃȂȁȀéÉèÈêÊȇȆȅȄíÍìÌîÎȋȊȉȈóÓòÒôÔȏȎȍȌŕŔȓȒȑȐúÚùÙûÛȗȖȕȔệỆộỘẹẸọỌəł",
to: "aAaAaAaAaAeEeEeEeEeEiIiIiIiIiIoOoOoOoOoOrRrRrRuUuUuUuUuUeEoOeEoOel",
strip: "\u0301\u0300\u0302\u0311\u030f\u0323",
},
kk: stripGraveAndAcute,
ky: stripGraveAndAcute,
tg: stripGraveAndAcute,
sa: {
strip: "ः",
},
/** visarga **/
bo: {
strip: "།",
},
/** shad **/
tr: {
from: "ÂâÛû",
to: "AaUu",
strip: "\u0302",
},
ja: fullToHalfWidthNumbers,
cmn: fullToHalfWidthNumbers,
yue: fullToHalfWidthNumbers,
nan: fullToHalfWidthNumbers,
ko: fullToHalfWidthNumbers,
zu: {
strip_init_hyphen: true,
}
};
function stringOrNull(val) {
return typeof val == "string" ? val : null;
}
function makeMap(obj) {
var from = stringOrNull(obj.from);
var to = stringOrNull(obj.to);
var strip = stringOrNull(obj.strip);
var map = {};
if (from && to) {
for (var i = 0; i < from.length; i++) {
map[from.charAt(i)] = to.charAt(i);
}
}
if (strip) {
for (var ii = 0; ii < strip.length; ii++) {
map[strip.charAt(ii)] = "";
}
}
return map;
}
/** letters and number 1 are used instead of palochka **/
var palochkaCorrections = {
from: "Il1",
to: "ӏӏӏ"
};
//
var arabicYa = "ي"; // Arabic letter yāʾ (U+064A ARABIC LETTER YEH)
var arabicKaf = "ك"; // Arabic letter kāf (U+0643 ARABIC LETTER KAF)
var farsiYe = "ی"; // Persian letter ye (U+06CC ARABIC LETTER FARSI YEH)
var farsiKaf = "ک"; // Persian letter kâf (U+06A9 ARABIC LETTER KEHEH)
var alifMaqsura = "ى"; // Arabic letter ʾalif maqṣūra (U+0649 ARABIC LETTER ALEF MAKSURA)
/** commonly misspelled Persian letters (Arabic instead of Persian) (to be expanded) **/
var persianCorrections = {
from: arabicKaf + arabicYa + alifMaqsura,
to: farsiKaf + farsiYe + farsiYe,
};
// These replacements will be applied when a word is submitted.
var orthographicalCorrections = {
// Replace grave accents with acute accents for translations
// into Bulgarian per Wiktionary policy
bg: {
map: { "\u0300": "\u0301", "Ѐ": "Е́", "Ѝ": "И́", "ѐ": "е́", "ѝ": "и́" },
},
abq: palochkaCorrections,
ady: palochkaCorrections,
av: palochkaCorrections,
ce: palochkaCorrections,
dar: palochkaCorrections,
inh: palochkaCorrections,
kbd: palochkaCorrections,
lbe: palochkaCorrections,
lez: palochkaCorrections,
tab: palochkaCorrections,
/** Roman to Cyrillic**/
cv: {
from: "ĂăĔĕÇçŸÿ",
to: "ӐӑӖӗҪҫӲӳ",
},
kv: {
from: "ÖöIi",
to: "ӦӧІі",
},
koi: {
from: "ÖöIi",
to: "ӦӧІі",
},
kpv: {
from: "ÖöIi",
to: "ӦӧІі",
},
os: {
from: "Ææ",
to: "Ӕӕ",
},
/* Obsolete or incorrectly used letters */
mn: {
from: "ЄєѲѳЇїVv",
to: "ӨөӨөҮүҮү",
strip: "\u0300\u0301",
},
/* cedilla to comma below */
ro: {
from: "ŞŢşţ",
to: "ȘȚșț",
},
/** ʻ is a standard symbol in Uzbek but is often replaced with ' or ` **/
uz: {
from: "'`",
to: "ʻʻ",
},
fa: persianCorrections,
"fa-ira": persianCorrections,
"fa-cls": persianCorrections,
prs: persianCorrections,
ur: persianCorrections,
/** commonly misspelled Arabic letters (Persian instead of Arabic) (to be expanded) **/
ar: {
from: farsiKaf + farsiYe,
to: arabicKaf + arabicYa,
},
ota: {
from: farsiKaf + arabicYa + alifMaqsura,
to: arabicKaf + farsiYe + farsiYe,
},
ps: {
from: arabicKaf,
to: farsiKaf,
},
/** some letters are considered more standard (to be expanded) **/
cu: {
from: "ыѹ",
to: "ꙑу",
},
/** some letters are considered more standard (to be expanded) **/
orv: {
from: "ыѹ",
to: "ꙑу",
},
/** obsolete letters **/
ab: {
from: "ҔҕҦҧ",
to: "ӶӷԤԥ",
},
};
//Returns true if a Wiktionary exists for the specified language
this.hasWiktionary = function(lang) {
if (metadata[lang] && (metadata[lang].haswikt || metadata[lang].wiktprefix))
return true;
};
//Returns the domain-name prefix of the Wiktionary for the specified language
this.getWiktionaryPrefix = function(lang) {
if (metadata[lang])
return metadata[lang].wiktprefix || (metadata[lang].haswikt && lang);
};
// Keep this in sync with ignore_caps in [[Module:translations]].
this.ignoreCaps = { "ko": true };
// Calls the callback with a boolean indicating whether the specified language
// has a Wiktionary with the specified entry. The callback might be called
// synchronously, or it might be called asynchronously.
this.hasWiktionaryWithEntry = function(lang, title, callback) {
if (this.hasWiktionary(lang)) {
if (this.ignoreCaps[lang])
title = title.replace(/^\^/, "");
// Use this when we want to give up support for Internet Explorer 11.
// URL constructor and URLSearchParams are not supported in IE11.
// var url = new URL("https://" + this.getWiktionaryPrefix(lang) + ".wiktionary.org/w/api.php");
// url.search = new URLSearchParams({
// "format": "json",
// "formatversion": "2",
// "action": "query",
// "titles": title,
// "converttitles": 1,
// "origin": "*",
// });
var url = new URL("https://" + this.getWiktionaryPrefix(lang) + ".wiktionary.org/w/api.php");
url.searchParams.set("format", "json");
url.searchParams.set("formatversion", "2");
url.searchParams.set("action", "query");
url.searchParams.set("titles", title);
url.searchParams.set("converttitles", "1");
url.searchParams.set("origin", "*");
$.ajax({
type: 'GET',
url: url.toString(),
dataType: 'json',
headers: { 'Api-User-Agent': 'EnWiktTranslationAdderGadget/0.0 (https://en.wiktionary.org/wiki/MediaWiki:Gadget-TranslationAdder.js)' },
}).done(function(data) {
var page = data && data.query && data.query.pages && data.query.pages[0];
callback(page instanceof Object ? !page.missing : false);
});
} else
callback(false);
};
//Given a language code return a default script code.
this.guessScript = function(lang) {
var scripts = (new ScriptUtils()).GetScriptsByLangCode(lang);
if (scripts && scripts.length > 0) return scripts[0];
else return false;
if (metadata[lang]) {
// enwikt language template? (ur-Arab, polytonic)
if (metadata[lang].wsc) {
return metadata[lang].wsc;
}
// ISO script code? (Arab, Grek)
if (metadata[lang].sc) {
if (typeof metadata[lang].sc == 'object')
return metadata[lang].sc[0];
else
return metadata[lang].sc;
}
}
return false;
};
// In a given language, would we expect a translation of the title to have the capitalisation
// of word?
this.expectedCase = function(lang, title, word) {
if (metadata[lang] && metadata[lang].allowCaps)
return true;
if (title.substr(0, 1).toLowerCase() != title.substr(0, 1))
return true;
return word.substr(0, 1).toLowerCase() == word.substr(0, 1);
};
//Returns a string of standard gender letters (mfnc) or an empty string
this.getGenders = function(lang) {
if (metadata[lang])
return metadata[lang].g;
};
//Returns a string of standard noun class numbers or an empty string
this.hasNounClasses = function(lang) {
if (metadata[lang])
return metadata[lang].nclass;
};
//Returns true if the specified lang uses optional vowels or diacritics
this.needsRawPageName = function(lang) {
if (metadata[lang])
return metadata[lang].alt && !diacriticStrippers[lang];
};
function applyMap(map, str) {
var input = str.split("");
var output = "";
for (var i = 0; i < input.length; i++) {
var char = input[i];
var repl = map[char];
output += repl ? repl : char;
}
return output;
}
this.applyOrthographicalCorrections = function(lang, word) {
word = word.replace('\xAD', ''); // remove soft hyphens
var corrections = orthographicalCorrections[lang];
if (corrections) {
var map = corrections.map ? corrections.map : makeMap(corrections);
return applyMap(map, word);
}
return word;
};
// Computes the raw page name by removing diacritics (for Latin, etc.)
this.computeRawPageName = function(lang, word) {
var stripper = diacriticStrippers[lang];
if (stripper) {
var map = makeMap(stripper);
var output = applyMap(map, word);
if (stripper.strip_init_hyphen && output.length > 0 && output.charAt(0) == '-')
output = output.substr(1);
return output;
}
};
// Calls [[Module:links]]'s 'remove_diacritics' method on word and rawPageName,
// and invokes callback with two arguments: (1) the *real* raw page name
// (superseding even rawPageName), and (2) whether this real raw page name
// needs to be explicitly specified in the wikitext (i.e., whether the raw page
// name computed from rawPageName is different from the one that would have been
// computed from just word).
this.retrieveRawPageName = function(lang, word, rawPageName, callback) {
var ents = {
'<': 'lt',
'&': 'amp',
'>': 'gt'
};
var temps = {
'|': '!',
'=': '=',
'{': '(',
'}': ')'
};
function encode(s) {
s = s || '';
s = s.replace(/[|={}]/g, function(c) {
return '{{' + temps[c] + '}}';
});
s = s.replace(/[<&>]/g, function(c) {
return '&' + ents[c] + ';';
});
s = s.replace(/\t/g, ' ');
//s = encodeURIComponent(s);
return s;
}
function removeDiacritics(s) {
return '{{#invoke:languages/templates|getByCodeAllowEtym|' + lang + '|makeEntryName|' + encode(s) + '}}';
}
var text = removeDiacritics(word) + '\t' + removeDiacritics(rawPageName || word);
$.ajax({
dataType: "json",
url: '/w/api.php',
data: {
format: 'json',
action: 'expandtemplates',
prop: 'wikitext',
text: text
},
success: function(data) {
data = data && data.expandtemplates && data.expandtemplates.wikitext;
if (/<!--/.test(data)) {
data = false;
}
if (data) {
data = data && data.split('\t');
var wordMinus = data[0];
var rawPageNameMinus = data[1];
callback(rawPageNameMinus || wordMinus, rawPageNameMinus != wordMinus);
} else {
callback(rawPageName || word, rawPageName && rawPageName != word);
}
}
});
};
//Given user input, return a language code. Normalises ISO 639-1 codes and names to 639-3.
this.cleanLangCode = function(lang) {
var key = lang.toLowerCase().replace(' ', '');
if (clean[key])
return clean[key];
else
return lang;
};
// Get the nesting for a given sub-language
this.getNested = function(lang) {
if (nesting[lang])
return nesting[lang];
else
return "";
};
function temporalSortKey(langname) {
return langname.replace(/^(Ancient|Classical|Old|Middle|Early Modern|Modern) (.*)/, function(m, modifier, name) {
return ({
Ancient: 0,
Old: 1,
Middle: 2,
"Early Modern": 3,
Modern: 4
})[modifier] + name;
});
}
// For enforcing an ordering on nested languages.
this.nestsBefore = function(a, b) {
return temporalSortKey(a) < temporalSortKey(b);
};
this.getScripts = function(lang) {
var scripts = (new window.ScriptUtils()).GetScriptsByLangCode(lang) || [];
return scripts;
};
};
//</nowiki>
31t448b4bvl8adiou5sr1g2ollrbdtc
မဳဒဳယာဝဳကဳ:Gadget-TabbedLanguages.js
8
295740
396568
2026-06-07T19:53:05Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "// ******** imported from [[MediaWiki:Gadget-LegacyScriptsNewNode.js]] ******** var newNode = window.newNode = function newNode(tagname) { var node = document.createElement(tagname); for (var i = 1; i < arguments.length; i++) { var argument = arguments[i]; if (typeof argument == 'string') { //Text node.appendChild(document.createTextNode(argument)); } else if (typeof argument == 'object') { if (argument..."
396568
javascript
text/javascript
// ******** imported from [[MediaWiki:Gadget-LegacyScriptsNewNode.js]] ********
var newNode = window.newNode = function newNode(tagname) {
var node = document.createElement(tagname);
for (var i = 1; i < arguments.length; i++) {
var argument = arguments[i];
if (typeof argument == 'string') { //Text
node.appendChild(document.createTextNode(argument));
} else if (typeof argument == 'object') {
if (argument instanceof Node) { // If it is a DOM Node
node.appendChild(argument);
} else { // Attributes (hopefully)
for (var j in argument) {
if (j === 'class') { // Classname different because...
node.className = argument[j];
} else if (j == 'style') { // Style is special
node.style.cssText = argument[j];
} else if (typeof argument[j] == 'function') { // Basic event handlers
node.addEventListener(j, argument[j], false);
} else {
node.setAttribute(j, argument[j]); //Normal attributes
}
}
}
}
}
return node;
};
// ****************************************************************************
(function ( $ ) {
// {{documentation}}
// This is a (very) modified version of User:Atelaes/TabbedLanguages.js.
// Tabbed languages with tabs on the side.
// Tabs design by [[User:Jorm (WMF)]]
/*jshint maxerr:1048576, strict:true, undef:true, latedef:true, es5:true */
/*global mw, jQuery, importScript, importScriptURI, $, ObjectStorage */
if (($.cookie('disable-tabbed-languages') !== null) || (location.search.indexOf("tabbedlanguages=off") !== -1))
return;
if (!((mw.config.get('wgNamespaceNumber') === 0) || (mw.config.get('wgPageName') === "Wiktionary:Sandbox")))
return;
var bodyContent = $(".mw-content-ltr .mw-parser-output")[0], // NOT #bodyContent
languageLinks, ttr,
languageButtons,
caption,
bodyContentFragment,
complete = false,
MO = window.MutationObserver || window.WebKitMutationObserver,
observerStyleSheet;
// Setting up the tabs has not yet been started.
function makeTabsfromScratch() {
// Set up the variables...
window.tabbedLanguages = [];
window.languageContainers = [];
window.currentLanguageTab = 0;
window.languageHeaderEditButtons = [];
languageButtons = [];
window.tabstable = newNode('table', {id: 'tabstable'},
newNode('tbody',
ttr = newNode('tr',
newNode('td', {'style': 'padding-top:0px;vertical-align:top;'},
newNode('table', {'style': 'margin-top: -2px;'},
languageLinks = newNode('tbody', {id: 'languageLinks'}))))));
window.loadremovecatbuttons = false;
bodyContentFragment = document.createDocumentFragment();
// If bodyContent is complete, do it all at once.
// Otherwise, only start working, but don't attempt the whole thing.
// Use catlinks+*, as gEBCN isn't always available.
var catlinks = document.getElementById( "catlinks" );
if( bodyContent && catlinks && catlinks.nextSibling ) {
allAtOnce();
} else {
createObserver();
}
}
function allAtOnce() {
var languageContainer, toc = document.getElementById( "toc" );
currentLanguageTab = 0;
while ( bodyContent.firstChild ) {
if ( bodyContent.firstChild.nodeName === "SECTION" ) {
// Unwrap and remove top-level <section> tags in Parsoid content
// https://www.mediawiki.org/wiki/Specs/HTML/2.8.0#Headings_and_Sections
while ( bodyContent.firstChild.firstChild ) {
bodyContentFragment.appendChild( bodyContent.firstChild.firstChild );
}
bodyContent.removeChild( bodyContent.firstChild );
} else {
bodyContentFragment.appendChild( bodyContent.firstChild );
}
}
try {
for( var child = bodyContentFragment.firstChild; child && !isHeader( child ); ){
child = child.nextSibling;
}
for (
child = child && bodyContentFragment.insertBefore(tabstable, child).nextSibling;
child && child.className !== 'printfooter' && child.className !== 'catlinks';
child = child.nextSibling
) {
if ( isHeader( child ) ) {
var langspan = getHeaderContent( child ),
language = langspan && ( langspan.innerText || langspan.textContent );
if ( language ) {
newTab( tabbedLanguages.push(language) - 1, language );
processEditButton( child.getElementsByClassName('mw-editsection')[ 0 ] );
// should probably be set from a return value of above.
languageContainer = languageContainers[ languageContainers.length - 1 ];
bodyContentFragment.removeChild( child );
child = tabstable;
}
} else {
if ( child.nodeName !== "HR" ) {
languageContainer.insertBefore(child, languageContainer.lastChild);
} else {
bodyContentFragment.removeChild(child);
}
child = tabstable;
}
}
if( tabbedLanguages.length ) {
if( toc ) {
toc.parentNode.removeChild( toc );
}
sortCats();
bodyContent.appendChild( bodyContentFragment );
setUpHashChange()();
if( location.hash === '' ) {
location.replace( "#" + tabbedLanguages[ currentLanguageTab ] );
}
complete = true;
} else {
bodyContent.appendChild( bodyContentFragment );
}
} catch( e ) {
window.console && console.error( e );
bodyContent.appendChild( bodyContentFragment );
complete || setUpHashChange()();
}
}
// Set up a MutationObserver to detect when new elements are loaded.
function createObserver() {
// If .ready happens early (or MO and animstart aren't supported), go ahead.
// TODO the following code was commented out 2023-11-09 as $.isReady was suddenly
// always evaluating to true, meaning that TabbedLanguages would not load.
//if( $.isReady ) {
// return; // ??? How did we get $.isReady if bC or catlinks aren't loaded?
//} else {
$( document ).ready( function () {
if( tabbedLanguages.length === 0 ) {
removeObserver();
if( !bodyContent ) {
bodyContent = $(".mw-content-ltr")[0];
}
if( bodyContent ) {
allAtOnce();
}
}
});
//}
if( !document.getElementsByClassName ) {
return;
}
if( !MO ) {
var supportsAnimations = false;
// Check if we can use animations as a fallback. If not, abort.
// Basically yoinked from Modernizr.
$.each(
"animationName WebkitAnimationName MozAnimationName OAnimationName msAnimationName".split(" "),
function(a, b){
// Do documentElements even always have .style? If not, this'll need fixing.
if( document.documentElement.style[ b ] !== undefined ) {
supportsAnimations = true;
return false;
}
}
);
if( supportsAnimations === false ) {
return;
}
}
var foundHeader = false,
toc, recentChild,
timer = false, // It's the setTimeout value, or false otherwise.
recentHeader = -1,
checkTab, tabFound = false,
// If anyone on this project even considers making a random element
// on a page have the class "visualClear"...
visualClear = document.getElementsByClassName( "visualClear" ),
languageContainer,
observer;
// TODO: Deal with the little jumping elements below the tabstable.
// ...How?
function elemFound() {
if( timer === false ) {
timer = setTimeout( function() {
reactToObserver();
timer = false;
}, 1);
}
}
if( MO ) {
observer = new MO( elemFound );
observer.observe( document, { childList: true, subtree: true } );
} else {
// Fallback for browsers that don't support MO, but do support animations:
// IE10, FF5-13, Chrome 17<, Safari 4-5.1, Opera 12-12.1
// Set up a stylesheet that uses animations/keyframes to allow
// animationStart to see each time a new node loads onto the bodyContent.
observerStyleSheet =
document.getElementsByTagName( 'head' )[0].appendChild(
document.createElement( "style" )
);
var oSSText = "@/@-moz-/@-webkit-/@-ms-/@-o-/".split("/")
.join("keyframes nodeInserted{" +
"from{outline-color:#fff;}" +
"to{outline-color:#000;}" +
"}\n") +
".mw-content-ltr>*,.mw-content-ltr+*,.visualClear{" +
// TODO: Fix duplication here.
"/-moz-/-webkit-/-ms-/-o-/".split("/").join("animation-duration:0.01s;") +
"/-moz-/-webkit-/-ms-/-o-/".split("/").join("animation-name:nodeInserted;") +
"}";
// Pretty much copied from mw.util.
// Don't have time to wait until it would ordinarily load.
if( observerStyleSheet.styleSheet ) {
observerStyleSheet.styleSheet.cssText = oSSText;
} else {
observerStyleSheet.appendChild( document.createTextNode( oSSText ) );
}
observerStyleSheet = observerStyleSheet.sheet ||
observerStyleSheet.styleSheet ||
observerStyleSheet;
document.addEventListener('animationstart', elemFound, false);
document.addEventListener('MSAnimationStart', elemFound, false);
document.addEventListener('webkitAnimationStart', elemFound, false);
}
function reactToObserver() {
if( complete ) {
return;
}
// TODO: Surround main areas in try{} so that if it breaks, at least
// the content gets dumped back into visibility.
// First: What part are we up to?
if( !foundHeader ) {
// search for header or toc
if( !recentChild ) { // Just starting, apparently.
if( !bodyContent ) {
bodyContent = $(".mw-content-ltr")[0];
if( !bodyContent ) {
// bC hasn't loaded yet. Nothing to do here.
return;
}
}
// Check if we can do the whole thing in one go.
if( visualClear.length ) {
removeObserver();
allAtOnce();
return;
}
recentChild = bodyContent.firstChild;
if( !recentChild ) {
return; // Somehow got activated between bodyContent load
// and bodyContent's content's load.
}
}
for( ; !isHeader( recentChild ) && recentChild.nextSibling; ) {
recentChild = recentChild.nextSibling;
}
if( isHeader( recentChild ) ) { // should simplify check. Maybe !recentChild.nextSibling?
// We have our first header.
foundHeader = true;
checkTab = setUpHashChange();
// Default to the first tab.
currentLanguageTab = 0;
toc = document.getElementById('toc');
if ( toc ) {
if ( toc.nextSibling ) {
// And we have a usable ToC. Makes things much easier.
// Analyze the ToC. We'll be using it to determine what
// the tabs are going to be.
$( ".toclevel-1 > a > .toctext" ).each( function() {
var language = $( this ).text();
tabbedLanguages.push( language );
});
// Build all the tabs.
$( tabbedLanguages ).each( newTab );
toc.parentNode.removeChild( toc );
// Afterwards, we'll check if the right section
// is already available.
} else {
// Potential panic situation: Evil formatting places
// ToC *after* the first header, so it's actually
// *partly* loaded at this point, with no nextSibling.
// (Alternatively, someone put it alone in a box.) CSS
// builds it up as a huge tabbing block, but we can't
// remove it here. Oy.
// "Solution":
// Temporarily hide the ToC until ready() fires, then
// remove it. I'm not removing it right away, as I have
// no idea what doom might occur if something like this
// is removed while stuff is being loaded into it.
toc.style.display = "none";
( function ( toc ) {
$( function() {
if( toc.parentNode ) {
toc.parentNode.removeChild( toc );
}
});
})( toc );
toc = undefined;
window.console && console.error( "TL notice: " +
"Malformed entry. ToC either appears after " +
"headers, or is placed in a box. Mind fixing " +
"it or alerting me? Thanks. -- YR" );
}
}
languageContainer = languageContainers[ 0 ];
// Edit buttons.
processEditButton(
recentChild.getElementsByClassName( "mw-editsection" )[ 0 ]
);
// Either way, start displaying the tabs right.
bodyContent.insertBefore( tabstable, recentChild );
if( toc ) {
tabFound = checkTab();
}
}
}
if( foundHeader ) {
// Note that there may or may not be a ToC available...
// Should this be reworked so that the toc check is inside the loop?
if( recentChild.nextSibling ) {
// I hope I'm not going to regret leaving out a recentChild && condition here...
for( var nextChild; recentChild.nextSibling; ) {
nextChild = recentChild.nextSibling;
if( isHeader( recentChild ) ) {
var langspan = getHeaderContent( recentChild );
var editspan = recentChild.getElementsByClassName('mw-editsection')[ 0 ];
if( recentHeader >= 0 ) {
languageContainer.insertBefore( bodyContentFragment, languageContainer.lastChild );
}
recentHeader++;
var language = ( language = langspan ).innerText || language.textContent;
// Make sure it actually matches, when necessary.
if( toc && language !== tabbedLanguages[ recentHeader ]) {
// PANIC!!!
// Okay, maybe don't panic. TL has encountered
// a header that doesn't match the ToC's
// description of the page. Possibilities
// include an h1 on the page, a fake header
// around somewhere, or some other malformed
// kind of header. This really isn't supposed
// to happen ever.
// Response: Clear everything after this point,
// and from there act as though the ToC never
// existed.
if( recentHeader > 0 ) {
toggleLanguageTabs( tabbedLanguages[ 0 ] );
} else {
currentLanguageTab = 0;
}
while( tabbedLanguages.length > recentHeader ) {
tabbedLanguages.pop();
ttr.removeChild(
languageContainers.pop()
);
languageLinks.removeChild(
languageButtons.pop().parentNode
);
}
toc = undefined; // Never. Existed.
tabFound = false;
window.console && console.error( "TL notice: " +
"Malformed entry. ToC does not match " +
"headers. Possibly a misplaced H1, fake " +
"header, or header with incorrect " +
"contents. Mind fixing it or " +
"alerting me? Thanks. -- YR "
);
}
if( !toc ) {
tabbedLanguages.push( language );
newTab( recentHeader, language );
}
// Check to see if the target tab is found.
// TODO: Need a better system of knowing when to check:
// If it's found, stop checking.
// Whether or not we have a ToC, checking can be necessary.
// If we do have a ToC, but no hash, we don't check, right?
// Put another way:
// No hash? W/ ToC, don't check. W/o ToC, only check when new headers come in.
// Hash? Depends.
// W/ ToC: Technically, target could come in at any non-header element... ???
// W/o ToC: Could be whenever. Target header coming in is rather likely, in fact. Oy.
// Either way: If the target, whether header or random element, is found, stop looking.
// Does delaying the tab switch to a element target until full load matter? Not sure.
//
// Plan B: Regardless of ToC presence, only
// check for potential target elems
// at each new header, unless target is found.
if( recentHeader > 0 && tabFound === false ) {
tabFound = checkTab();
}
languageContainer = languageContainers[ recentHeader ];
if( recentHeader > 0 ) { // I have too many of these checks...
// Don't duplicate earlier pEB for first header.
processEditButton( editspan );
}
// All done. Clear.
bodyContent.removeChild( recentChild );
} else {
if( recentChild.nodeName === "HR" ) {
// Kill unnecessary bars.
bodyContent.removeChild( recentChild );
} else {
// Regular content. Prepare for dumping into the
// latest tab.
bodyContentFragment.appendChild( recentChild );
}
}
recentChild = nextChild;
}
}
/*
// If we're all done, finish up.
// How to tell?
// Option one: Repeatedly check for nextSibling.
// - Won't work. bodyContent doesn't always have a nS at the end.
// - Currently in use only for dealing with the last elem. (Uses O2 as fallback.)
// Option two: Set up getElemsByClassName, and poll for length.
// - presumably visualClear. This would probably be pretty heavy.
// - Currently in use.
// Option three: Use $.ready.
// - Potentially introduces substantial delay...
// Option four: In the listener, check for classnames each time.
// - Every time there's a new node? Lots of processing.
// Option five: ...
*/
if( bodyContent.nextSibling && bodyContent.lastChild === recentChild ) { // are we done? ...
// bodyContent.nextSibling doesn't always exist, but if it
// does, get a head start on the stuff that can be done already.
bodyContentFragment.appendChild( bodyContent.lastChild );
/*
// Problem: recentChild is now inside the languageContainer,
// and it's possible that reactToObserver will be called again
// before the post-visualClear stuff runs.
// Option one: recentChild = tabstable;
// - Nope. That would mean bodyContent.lastChild = recentChild again
// Option two: recentChild = false;
// - Nope. The check at the top would cause us to start over again.
// Blargh. This is an awful solution that probably violates every...
*/
recentChild = 1;
// "1" is hereby the indicator that we're not doing anything with
// this particular variable, okay?
// I'll try to make this nicer later.
}
// Dump collected elems into most recent languageContainer.
if( bodyContentFragment.firstChild ) { // Exact duplicate of above code. TODO: Fix.
languageContainer.insertBefore( bodyContentFragment, languageContainer.lastChild );
}
if( visualClear.length ) {
complete = true;
if( bodyContent.lastChild === recentChild ) {
// bC.nS didn't exist. Move the last one left now.
languageContainer.insertBefore( bodyContent.lastChild, languageContainer.lastChild );
}
removeObserver();
sortCats();
if( tabFound !== true ) {
checkTab();
if( location.hash === '' ) {
location.replace( "#" + tabbedLanguages[currentLanguageTab] );
}
}
}
}
}
function removeObserver() {
if( observer ) {
observer.disconnect();
} else if( observerStyleSheet ) {
document.removeEventListener('animationstart', elemFound, false);
document.removeEventListener('MSAnimationStart', elemFound, false);
document.removeEventListener('webkitAnimationStart', elemFound, false);
observerStyleSheet.disabled = true;
}
if( timer !== false ) {
clearTimeout( timer );
}
}
}
function isHeader( elem ) {
// Handle two versions of the markup: https://www.mediawiki.org/wiki/Heading_HTML_changes
return elem && elem.nodeType === Node.ELEMENT_NODE && (
elem.nodeName === "H2" && elem.getElementsByClassName( "mw-headline" ).length !== 0 ||
elem.classList.contains( "mw-heading2" ) && elem.getElementsByTagName( "H2" ).length !== 0
);
}
function getHeaderContent( elem ) {
// Handle two versions of the markup: https://www.mediawiki.org/wiki/Heading_HTML_changes
return elem.nodeName === "H2" ?
elem.getElementsByClassName( "mw-headline" )[0] :
elem.getElementsByTagName( "H2" )[0];
}
function newTab( index, language ) {
var active = index === currentLanguageTab;
var languageContainer = ttr.appendChild( newNode('td', {
'class': 'languageContainer',
'id': language + 'container'
}, active ? undefined : { 'style' : 'display:none;' } ));
languageContainers.push( languageContainer );
newCategoryBox( languageContainer, language );
// lB contains .(un)?selectedTab nodes
languageButtons.push(
languageLinks.appendChild( newNode('tr', newNode('td', {
'class': ( active ? '' : 'un' ) + 'selectedTab'
}, newNode('a', language, {
// Note: ' + language' makes this inconsistent with the ordinary links
// themselves, which have the language name encoded. Issue?
// Keep in mind that the parser itself actually corrects for this,
// changing #!Xóõ links to the proper #.C7.83X.C3.B3.C3.B5 links.
// Probably nothing to worry about.
'href': location.pathname + location.search + '#' + language.replace(/ /g, '_')
}), ' '))).firstChild
);
}
function newCategoryBox( container, name ) {
// Put a container in each for categories.
return container.appendChild( newNode('div', name + ' categories: ', newNode('ul'), {
'class': 'catlinks',
'id': 'catlinks'
}));
}
function processEditButton( button ) {
if( button ) {
// Yes, theoretically if you have some fake H2s the edit buttons will
// be misplaced. Yet another TODO...
var len = languageHeaderEditButtons.push( button );
if( len === 1 ) {
caption = document.createElement('caption');
tabstable.insertBefore( caption, tabstable.firstChild );
}
button.className += " editlangsection";
// use a argument or variable ( from .push? ) instead of .length?
if( len - 1 === currentLanguageTab ) {
caption.firstChild && caption.removeChild( caption.firstChild );
caption.appendChild( button );
}
}
}
// Sets up the hash toggle system.
// Returns the checkTab() function, which returns true if
// we found whatever it was.
function setUpHashChange() {
// Important Note: The decodeURI mess is a real mess.
// #.C7.83X.C3.B3.C3.B5 should go to tab "!Xóõ", with
// the id "!Xóõcontainer" (no encoding). Urgh.
// Also, "#Old_English" needs to go to decoded "Old English".
// Remember to .substr( 1 ) before passing here.
function decodeHash( hash ) {
return decodeURI(
hash
.replace(/\.(?=[0-9A-F]{2})/g, '%')
.replace(/_/g, ' ')
);
}
// Called by onhashchange.
function hashToggleLT() {
var destination = decodeHash( location.hash.substr(1) );
toggleLanguageTabs( destination );
tabbedLanguages[currentLanguageTab] !== destination && resetHash();
}
// For updating page positioning.
// Doesn't activate hashchange, at least in Chrome. Not sure about others.
function resetHash() {
location.replace( location.hash );
}
// Need to decide if language arg is encoded or decoded.
// Will work with either for the moment. Decoded works earlier.
// Currently passed as decoded by every function but itself.
// The hashes are encoded, but hashToggleLT decodes them.
// Toggles to a different language tab.
window.toggleLanguageTabs = function (language) {
// Find the destination language.
var destinationLanguageTab = $.inArray( language, tabbedLanguages );
if( destinationLanguageTab === -1 ) {
var decoded = decodeHash( language );
if( decoded !== language ) {
destinationLanguageTab = $.inArray( decoded, tabbedLanguages );
}
}
// Style the right toggle button, hide the old language section and show the new one.
// var languageButtons = $("#languageLinks .selectedTab, #languageLinks .unselectedTab");
if (destinationLanguageTab !== -1 ) {
if( destinationLanguageTab !== currentLanguageTab ) {
languageButtons[currentLanguageTab].className = 'unselectedTab';
languageContainers[currentLanguageTab].style.display = 'none';
currentLanguageTab = destinationLanguageTab;
languageButtons[destinationLanguageTab].className = 'selectedTab';
languageContainers[destinationLanguageTab].style.display = '';
if (caption) {
// extra checks shouldn't be necessary...
caption.firstChild && caption.removeChild(caption.firstChild);
languageHeaderEditButtons[currentLanguageTab] && caption.appendChild(languageHeaderEditButtons[currentLanguageTab]);
}
}
} else {
// Does the hash match the id of a node in a tab?
language = encodeURI( language.replace(/\ /g, '_') );
// Yes, I just possibly undid the decoding from hashToggleLT.
// Worse, this might double-encode, breaking things.
// Or maybe not? I think everything might be decoded before being
// sent here?
// I'll deal with it later.
// Find the node, and go up the node tree until
// you hit .languageContainer, or nothing.
language = document.getElementById( language );
for( ; language && ( language = language.parentNode ) &&
language.className !== 'languageContainer'; )
;
// language = language && language.parentNode.parentNode;
if ( language ) {
// If someone maliciously makes a languageContainer with a
// non-compliant ID, boom.
toggleLanguageTabs( language.id.split('container')[0] );
}
// Possible doom bug: Endless loop?
}
};
// This function gets returned as checkTab(), btw.
// For during or immediately after load: Check if we have a
// good "starting" tab. Return true if we have a definitive find.
function checkTab() {
// If there's a location hash, the window may have scrolled down before
// we got a chance to reorganize everything.
// If the destination was a subsection or sense id, switch to the right
// tab, and rescroll.
// If it was simply a language, switch to the appropriate tab, and
// scroll back up.
// If there's no hash at all, work off the TargetedTranslations prefs.
try {
var hash = '';
var destination = 'English';
if ( location.hash !== '' ) {
hash = ( location.hash ).substr( 1 ); // does hash.substr always exist?
destination = decodeHash( hash );
}
// 'k, this is still awful.
if ( $.inArray( destination, tabbedLanguages ) !== -1 ) {
toggleLanguageTabs( destination );
window.scrollY && window.scroll(0, 0);
return true;
} else if ( hash !== '' && document.getElementById( hash ) ) {
// This was going
// to receive an area to search from an argument, but it turns
// out that elem.getElementById doesn't actually exist. Meh.
toggleLanguageTabs( destination );
resetHash(); // Scroll to the element. (Necessary since we
// this isn't running through hashToggleLT, which normally does
// it in these situations.)
return true;
}
// No hash. Work from TargetedTranslations.
if ('localStorage' in window) {
if (tabbedLanguages[0] !== 'Translingual' && tabbedLanguages[0] !== 'English' && localStorage.TargetedTranslations) {
for (
var tt_ = localStorage.TargetedTranslations.split("|"), tt = tt_[0].split(";").concat(
$.grep(
tt_[1].replace(/[^;\/]+\//g, '').split(";"),
function (z) {
return z && z !== "Latin" && z !== "Hebrew" && z !== "Arabic";
}
)
).concat( tt_[1].replace(/\/[^;]+/g, '').split(";") ), i = 0;
i < tt.length;
i++
) {
if ($.inArray(tt[i], tabbedLanguages) !== -1) {
toggleLanguageTabs(tabbedLanguages[$.inArray(tt[i], tabbedLanguages)]);
break;
}
}
}
}
return false;
} catch ( e ) {
// This probably isn't all that unlikely to happen. Too complicated. :(
window.console && console.error( "TL error: checkTab broke.", e );
}
}
if ("onhashchange" in window && (document.documentMode === undefined || document.documentMode > 7)) {
window.onhashchange = hashToggleLT;
} else {
$( bodyContent ).on(
'click',
'a[href^="' + location.pathname + location.search + '#"], a[href^="#"]',
function () {
setTimeout( hashToggleLT, 10 );
}
);
}
return checkTab;
}
function sortCats() {
var catDiv = document.getElementById('mw-normal-catlinks'),
currentCatDiv;
if (catDiv) {
var cats = catDiv.getElementsByTagName('li'),
catname,
langcurrent = 0,
catskip = 1;
do {
while (cats.length > 0) {
catname = cats[ 0 ].getElementsByTagName('a')[0].innerHTML;
if (catname.indexOf(tabbedLanguages[langcurrent + catskip]) === 0 &&
!/letter\snames$|script\scharacters$|mythology$/.test(catname)
) {
langcurrent += catskip;
catskip = 1;
}
currentCatDiv = languageContainers[langcurrent].lastChild;
currentCatDiv.lastChild.appendChild(cats[0]);
}
if (langcurrent + 1 < languageContainers.length - catskip) {
// Didn't make it to the end, which means there's a section
// without any categories. Dump what's left back and try again.
while (currentCatDiv.lastChild.firstChild) {
catDiv.lastChild.appendChild(currentCatDiv.lastChild.firstChild);
}
catskip++;
} else {
break;
}
} while (true);
}
if( currentCatDiv ) {
// place patrol link at the bottom of the page
var pl = currentCatDiv.previousSibling; // languageContainer.lastChild.previousSibling;
// Got it, the patrollink is sometimes the lagging last elem.
if( pl && pl.className === "patrollink" ) {
tabstable.parentNode.appendChild(pl);
}
}
/*
// category editing buttons
if (mw.config.get('wgAction') === "view" && !/&printable=yes|&diff=|&oldid=/.test(location.search)) {
for (z = 0; z < languageContainers.length; z++) {
// TODO: import
// addTabbedLanguageNewCatButton(z);
}
if ( window.loadremovecatbuttons === true ) {
$.get(
mw.config.get( 'wgScript' ),
{ 'title' : mw.config.get('wgPageName'), 'action' : 'raw' },
// Note that this isn't actually defined. I'm considering not
// actually importing the function, and just dumping this section.
addRemoveCatButtons
);
}
}
*/
// Remove old cat box, allow display of hidden cats box.
if (catDiv && !(catDiv.nextSibling && catDiv.nextSibling.className === "mw-hidden-catlinks mw-hidden-cats-user-shown")) {
catDiv.parentNode.style.display = 'none';
} else {
if (catDiv) {
catDiv.style.display = 'none';
}
}
}
function testStuff() {
var delay = 5000, f = document.createDocumentFragment();
var x = [
function () {
// $("#toc").remove();
var x = document.getElementById( "mw-content-text" );
while( x.nextSibling ) {
f.appendChild( x.nextSibling );
}
}, function () {
var y = $( ".mw-content-ltr" )[ 0 ];
var q = document.createDocumentFragment();
while( y.firstChild ) {
q.appendChild( y.firstChild );
}
function u( i, e ) {
setTimeout( function () {
y.appendChild( q.firstChild );
}, i / e * delay );
}
for( var i = 0, e = q.childNodes.length; i < e; i++ ) {
u( i, e ); // encapsulate
}
makeTabsfromScratch();
}, function () {
function u( i, e ) {
setTimeout( function () {
$(".mw-content-ltr").after( f.firstChild );
}, i / e * delay / 5 );
}
for( var i = 0, e = f.childNodes.length; i < e; i++ ) {
u( i, e ); // encapsulate
}
console.log(9);
}
];
x[0]();
for( var i = 1; i < x.length; i++ ) {
setTimeout( x[ i ], (i-1) * delay + 50 );
}
}
//testStuff();
makeTabsfromScratch();
// This is officially deprecated as of MW1.22. Should be replaced by mw.hook,
// but the docs don't say what event, so...
$(mw).on('LivePreviewDone', function () {
bodyContent = $(".mw-content-ltr")[0]; // reset
makeTabsfromScratch();
});
})( window.jQuery );
bo3vk8t8xgwm7kymt0gkaic1zrtyvzq
မဳဒဳယာဝဳကဳ:Gadget-TabbedLanguages.css
8
295741
396569
2026-06-07T19:54:29Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "/* Styles for the Tabbed Languages gadget */ #languageLinks .selectedTab a { font-size: 19px; color: #000; padding: 6px; display: block; border-left: 1px solid #CCC; border-bottom: 1px solid #CCC; margin-bottom: -3px; background-color: #fff; margin-right: -6px; position: relative; border-top: 1px solid #CCC; white-space: nowrap; } #languageLinks .unselectedTab a { font-size: 16px; color: #929292; padd..."
396569
css
text/css
/* Styles for the Tabbed Languages gadget */
#languageLinks .selectedTab a {
font-size: 19px;
color: #000;
padding: 6px;
display: block;
border-left: 1px solid #CCC;
border-bottom: 1px solid #CCC;
margin-bottom: -3px;
background-color: #fff;
margin-right: -6px;
position: relative;
border-top: 1px solid #CCC;
white-space: nowrap;
}
#languageLinks .unselectedTab a {
font-size: 16px;
color: #929292;
padding: 6px;
display: block;
border-left: 1px solid #CCC;
border-top: 1px solid #CCC;
border-bottom: 1px solid #CCC;
margin-bottom: -3px;
margin-left: 15px;
margin-right: -5px;
white-space: nowrap;
background-color: #F6F6F6;
}
#languageLinks tr td {
padding: 0px;
}
tr td.languageContainer {
padding-left: 8px;
padding-bottom: 10px;
padding-right: 8px;
border: 1px solid #CCC;
vertical-align: top;
width: 100%;
}
.unselectedtab {
margin-left: 5px;
}
.editlangsection {
margin-top: -13px;
margin-right: 5px;
border: 1px solid #CCC;
margin-bottom: -2px;
border-bottom-width: 0px;
-webkit-border-top-left-radius: 5px;
-webkit-border-top-right-radius: 5px;
-moz-border-radius-topleft: 5px;
-moz-border-radius-topright: 5px;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
color: white;
line-height: 14px;
font-size: 12px;
padding: 0px 7px;
text-align: center;
clear: both;
background-color: white;
float: right;
}
#languageLinks a.addLanguageButton {
font-size: 12px;
padding: 3px 6px;
}
@media screen and (max-width:36em) {
#tabstable > tbody > tr > td {
display: block;
float: left;
width: 100%;
}
#languageLinks tr {
display: block;
float: left;
margin-bottom: -3px;
margin-left: -5px;
margin-top: 5px;
}
#languageLinks .unselectedTab a {
margin-left: 0;
margin-right: 5px;
border-right: 1px solid #CCC;
}
#languageLinks .selectedTab a {
margin-left: 0;
margin-right: 5px;
border-right: 1px solid #CCC;
border-bottom: 1px solid #FFF;
}
#tabstable {
width: 100%;
}
#languageLinks a.addLanguageButton {
margin-top: 2px;
margin-bottom: 1px;
}
.editlangsection {
border-bottom-width: 1px;
}
}
.minitoc:not(.mammoth-minitoc) {
display: none;
}
0vfsobgurlx0r6mcpypw92v32flnn6t
မဳဒဳယာဝဳကဳ:Gadget-StorageUtils.js
8
295742
396570
2026-06-07T19:55:56Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "// {{documentation}} // implicit dependencies : mediawiki.cookie,mw.storage /* jshint maxerr:1048576, strict:true, undef:true, latedef:true, es5:true, sub:true */ /* global mw, $ */ //lowest level wrapper. wraps localstorage and cookie var StorageWrapper = window.StorageWrapper = function(){ this.localStorageAvailable = mw.storage.get("localStorageTest") !== false; this.storageAvailable = this.localStorageAvailabl..."
396570
javascript
text/javascript
// {{documentation}}
// implicit dependencies : mediawiki.cookie,mw.storage
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, es5:true, sub:true */
/* global mw, $ */
//lowest level wrapper. wraps localstorage and cookie
var StorageWrapper = window.StorageWrapper = function(){
this.localStorageAvailable = mw.storage.get("localStorageTest") !== false;
this.storageAvailable = this.localStorageAvailable || navigator.cookieEnabled;
this.cookiePreferences = {
expires: 30, path: '/'
};
this.set = function(name, value){
if (this.localStorageAvailable) {
localStorage[name] = value;
} else {
mw.cookie.set(name, value, {});
}
};
this.get = function(name) {
if (this.localStorageAvailable){
return localStorage[name];
} else {
return mw.cookie.get(name);
}
};
};
// expirable, lazy object storage. ideally should inherit(?) from ObjectStorage
window.CacheableStorage = function(productName, getProductCallback, expireInDays, version){
this.storageWrapper = new StorageWrapper();
this.StorageAvailable = this.storageWrapper.storageAvailable;
this.expireInDays = expireInDays;
this.version = version || 1;
this.productName = productName;
this.getProductCallback = getProductCallback;
this._cachedData = null;
this.itemAddressPrefix = "enwikt/CacheableStorage/" + this.productName + "?v=" + this.version;
this.itemDataAddress = this.itemAddressPrefix + "&Type=Data";
this.itemTypeAddress = this.itemAddressPrefix + "&Type=Type";
this.itemExpirationAddress = this.itemAddressPrefix + "&Type=Expiration";
this.refreshData = function() {
var this1 = this;
this.getProductCallback().then(function(d) {
this1.storageWrapper.set(this1.itemTypeAddress, typeof(d));
this1.storageWrapper.set(this1.itemExpirationAddress, new Date().toISOString());
if (typeof(d) != "string") d = JSON.stringify(d);
this1.storageWrapper.set(this1.itemDataAddress, d);
});
};
this.GetItem = function(){
if (this._cachedData)
return this._cachedData;
var data = this.storageWrapper.get(this.itemDataAddress);
var type = this.storageWrapper.get(this.itemTypeAddress);
var expiration = this.storageWrapper.get(this.itemExpirationAddress);
if (data)
{
if (type != "string") data = JSON.parse(data);
if ((new Date() - new Date(expiration))/1000 > this.expireInDays*24*3600){
this.refreshData();
return data;
}
else{
this._cachedData=data;
return this._cachedData;
}
}
else
{
this.refreshData();
}
return null;
};
};
//not expirable
window.ObjectStorage = function(contextName, version){
this.storageWrapper = new StorageWrapper();
this.StorageAvailable = this.storageWrapper.storageAvailable;
this.version = version || 1;
this.contextName = contextName;
this._cachedData = null;
this.itemAddressPrefix = "enwikt/ObjectStorage/" + this.contextName + "?v=" + this.version;
this.itemDataAddress = this.itemAddressPrefix + "&Type=Data";
this.itemTypeAddress = this.itemAddressPrefix + "&Type=Type";
this.Set = function(obj) {
var s = typeof(obj) == "string" ? obj : JSON.stringify(obj);
this.storageWrapper.set(this.itemDataAddress, s);
this.storageWrapper.set(this.itemTypeAddress, typeof(obj));
this._cachedData = obj;
};
this.Get = function(){
if (this._cachedData)
return this._cachedData;
var data = this.storageWrapper.get(this.itemDataAddress);
var type = this.storageWrapper.get(this.itemTypeAddress);
if (data)
{
if (type != "string") data = JSON.parse(data);
this._cachedData=data;
return this._cachedData;
}
return null;
};
};
oui2tt7ksbk1olgzxij0ic2klkory1t
မဳဒဳယာဝဳကဳ:Gadget-Palette/table
8
295743
396571
2026-06-07T19:57:52Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "The Palette variables shown below are defined in [[MediaWiki:Gadget-Palette.css]]. To request a new variable to be added, do so [[MediaWiki talk:Gadget-Palette|here]] using this format: <code><name> <light mode hex code> <dark mode hex code></code>. See [[Wiktionary:Palette]] for more information. {{#invoke:palette|show}}"
396571
wikitext
text/x-wiki
The Palette variables shown below are defined in [[MediaWiki:Gadget-Palette.css]]. To request a new variable to be added, do so [[MediaWiki talk:Gadget-Palette|here]] using this format: <code><name> <light mode hex code> <dark mode hex code></code>. See [[Wiktionary:Palette]] for more information.
{{#invoke:palette|show}}
ibrl7ml1mx496004xkqd3btqmrqgn3k
မဝ်ဂျူ:palette/styles.css
828
295744
396573
2026-06-07T19:59:47Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု ".palette-table td:not(:first-child) { text-align: center; } .palette-table td:nth-child(4), .palette-table td:nth-child(5) { min-width: 200px; } .palette-highlight { position: relative; z-index: 0; padding: 2px 5px; background: var(--wikt-palette-lavender); opacity: 0.8; border-radius: 3px; }"
396573
sanitized-css
text/css
.palette-table td:not(:first-child) {
text-align: center;
}
.palette-table td:nth-child(4), .palette-table td:nth-child(5) {
min-width: 200px;
}
.palette-highlight {
position: relative;
z-index: 0;
padding: 2px 5px;
background: var(--wikt-palette-lavender);
opacity: 0.8;
border-radius: 3px;
}
r22pfpm1zg2w8e1xod26e3llyprz737
မဝ်ဂျူ:palette/doc
828
295745
396574
2026-06-07T20:01:04Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "Generates a table of [[MediaWiki:Gadget-Palette.css|Palette]] variables along with contrast information. See [[MediaWiki:Gadget-Palette/table]]. <includeonly> [[ကဏ္ဍ:မဝ်ဂျူဟွံမွဲကဵုကဏ္ဍဂမၠိုၚ်]] </includeonly>"
396574
wikitext
text/x-wiki
Generates a table of [[MediaWiki:Gadget-Palette.css|Palette]] variables along with contrast information. See [[MediaWiki:Gadget-Palette/table]].
<includeonly>
[[ကဏ္ဍ:မဝ်ဂျူဟွံမွဲကဵုကဏ္ဍဂမၠိုၚ်]]
</includeonly>
in08z11wdu7hp3mlb7vu8rvm5apgmj9
မဳဒဳယာဝဳကဳ:Gadget-Palette.css
8
295746
396575
2026-06-07T20:01:47Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "/* Wiktionary's palette of colors that work equally well in light and dark mode. */ /* Inspired by [[:ru:w:MediaWiki:Gadget-common-site.css]] */ :root, .skin-invert, .notheme { --wikt-palette-white: #ffffff; /* Same as <code>--background-color-base</code> */ --wikt-palette-black: #202122; /* Body text; same as <code>--color-base</code> */ --wikt-palette-beige: #fffbf2; --wikt-palette-darkgrey: #54595d; --wikt-pa..."
396575
css
text/css
/* Wiktionary's palette of colors that work equally well in light and dark mode. */
/* Inspired by [[:ru:w:MediaWiki:Gadget-common-site.css]] */
:root, .skin-invert, .notheme {
--wikt-palette-white: #ffffff; /* Same as <code>--background-color-base</code> */
--wikt-palette-black: #202122; /* Body text; same as <code>--color-base</code> */
--wikt-palette-beige: #fffbf2;
--wikt-palette-darkgrey: #54595d;
--wikt-palette-grey: #9e9e9e;
--wikt-palette-lightgrey: #cccccc;
--wikt-palette-lightergrey: #eeeeee;
--wikt-palette-cream: #f8f8f0;
--wikt-palette-pink: #ffe0f0;
--wikt-palette-peach: #e4d4c0;
--wikt-palette-gold: #b88d00;
--wikt-palette-dullgold: #826f34;
--wikt-palette-yellow: #ebe8b9;
--wikt-palette-dullyellow: #dedbc1;
--wikt-palette-brightyellow: #ffee77;
--wikt-palette-palegreen: #e2f6e2;
--wikt-palette-palergreen: #f8fffa;
--wikt-palette-mint: #c0e4c0;
--wikt-palette-green: #33d733;
--wikt-palette-forestgreen: #235923;
--wikt-palette-luminousgreen: #dafdda;
--wikt-palette-paleblue: #f8f9fa;
--wikt-palette-cyan: #eefbf9;
--wikt-palette-palecyan: #f6f7f7;
--wikt-palette-lightblue: #d9ebff;
--wikt-palette-dulllightblue: #aabbdd;
--wikt-palette-blue: #3333ff;
--wikt-palette-dullblue: #49555f;
--wikt-palette-dullcyan: #eaecf0;
--wikt-palette-deepblue: #2f445c;
--wikt-palette-lavender: #f8f8ff;
--wikt-palette-lightyellow: #ffffe0;
--wikt-palette-red: #d73333; /* Same as <code>--background-color-destructive</code> */
--wikt-palette-darkred: #660000;
--wikt-palette-deepred: #bf0218;
--wikt-palette-orange: #f2caa4;
--wikt-palette-lighterblue: #ebf4ff;
--wikt-palette-dulllighterblue: #e2eaf9;
--wikt-palette-lightred: #fee7e6;
--wikt-palette-indigo: #aaaaee;
--wikt-palette-lightindigo: #e9e9ff;
--wikt-palette-palerpink: #fff3f5;
--wikt-palette-honey: #81540e;
/* Autogenerated with [[Wiktionary:Palette/numbered]] */
--wikt-palette-red-0: #fef9f9;
--wikt-palette-red-1: #fdeef0;
--wikt-palette-red-2: #fce4e6;
--wikt-palette-red-3: #fad9dc;
--wikt-palette-red-4: #f7c2c6;
--wikt-palette-red-5: #f5afb5;
--wikt-palette-red-6: #f29ba3;
--wikt-palette-red-7: #f0858e;
--wikt-palette-red-8: #ed6d78;
--wikt-palette-red-9: #ce4350;
--wikt-palette-scarlet-0: #fef9f7;
--wikt-palette-scarlet-1: #fcefeb;
--wikt-palette-scarlet-2: #fae5de;
--wikt-palette-scarlet-3: #f8dad1;
--wikt-palette-scarlet-4: #f4c4b5;
--wikt-palette-scarlet-5: #f1b3a0;
--wikt-palette-scarlet-6: #eea088;
--wikt-palette-scarlet-7: #ea8c70;
--wikt-palette-scarlet-8: #e67553;
--wikt-palette-scarlet-9: #c25433;
--wikt-palette-orange-0: #fdfaf5;
--wikt-palette-orange-1: #fbf0e4;
--wikt-palette-orange-2: #f8e7d4;
--wikt-palette-orange-3: #f5ddc3;
--wikt-palette-orange-4: #f0c89e;
--wikt-palette-orange-5: #ebb882;
--wikt-palette-orange-6: #e6a662;
--wikt-palette-orange-7: #e19341;
--wikt-palette-orange-8: #db7f1e;
--wikt-palette-orange-9: #b16210;
--wikt-palette-amber-0: #fdfaf3;
--wikt-palette-amber-1: #f9f1df;
--wikt-palette-amber-2: #f5e8cc;
--wikt-palette-amber-3: #f1dfb8;
--wikt-palette-amber-4: #e8cc8f;
--wikt-palette-amber-5: #e2bc6d;
--wikt-palette-amber-6: #dbac4a;
--wikt-palette-amber-7: #d39b24;
--wikt-palette-amber-8: #c38c17;
--wikt-palette-amber-9: #9b6f10;
--wikt-palette-yellow-0: #fbfbf4;
--wikt-palette-yellow-1: #f5f2e0;
--wikt-palette-yellow-2: #eeeacc;
--wikt-palette-yellow-3: #e8e2b9;
--wikt-palette-yellow-4: #dbd191;
--wikt-palette-yellow-5: #d0c370;
--wikt-palette-yellow-6: #c5b44f;
--wikt-palette-yellow-7: #b6a63f;
--wikt-palette-yellow-8: #a69737;
--wikt-palette-yellow-9: #847827;
--wikt-palette-lime-0: #f9fbf3;
--wikt-palette-lime-1: #eff4e1;
--wikt-palette-lime-2: #e5edce;
--wikt-palette-lime-3: #dbe5bc;
--wikt-palette-lime-4: #c5d693;
--wikt-palette-lime-5: #b5ca73;
--wikt-palette-lime-6: #a3be52;
--wikt-palette-lime-7: #97af4e;
--wikt-palette-lime-8: #889f43;
--wikt-palette-lime-9: #6b7f2f;
--wikt-palette-green-0: #f8fbf5;
--wikt-palette-green-1: #eaf5e4;
--wikt-palette-green-2: #ddefd3;
--wikt-palette-green-3: #cee8c1;
--wikt-palette-green-4: #b3da9d;
--wikt-palette-green-5: #9cd080;
--wikt-palette-green-6: #83c460;
--wikt-palette-green-7: #69b840;
--wikt-palette-green-8: #64a640;
--wikt-palette-green-9: #4b852d;
--wikt-palette-teal-0: #f6fcf9;
--wikt-palette-teal-1: #e5f6ee;
--wikt-palette-teal-2: #d5efe3;
--wikt-palette-teal-3: #c3e9d7;
--wikt-palette-teal-4: #9fdcc0;
--wikt-palette-teal-5: #82d1ac;
--wikt-palette-teal-6: #64c599;
--wikt-palette-teal-7: #41b982;
--wikt-palette-teal-8: #3fa878;
--wikt-palette-teal-9: #2c865d;
--wikt-palette-cyan-0: #f6fbfc;
--wikt-palette-cyan-1: #e7f4f7;
--wikt-palette-cyan-2: #d7edf1;
--wikt-palette-cyan-3: #c7e6ec;
--wikt-palette-cyan-4: #a6d7e0;
--wikt-palette-cyan-5: #8accd7;
--wikt-palette-cyan-6: #6ebfcd;
--wikt-palette-cyan-7: #50b2c3;
--wikt-palette-cyan-8: #2ea3b7;
--wikt-palette-cyan-9: #288191;
--wikt-palette-blue-0: #f8fafe;
--wikt-palette-blue-1: #ecf2fe;
--wikt-palette-blue-2: #e0eafd;
--wikt-palette-blue-3: #d3e1fc;
--wikt-palette-blue-4: #bbd0fa;
--wikt-palette-blue-5: #a6c2f9;
--wikt-palette-blue-6: #92b3f7;
--wikt-palette-blue-7: #7ca4f5;
--wikt-palette-blue-8: #6494f4;
--wikt-palette-blue-9: #3d72db;
--wikt-palette-indigo-0: #fafafe;
--wikt-palette-indigo-1: #f1f1fd;
--wikt-palette-indigo-2: #e9e7fc;
--wikt-palette-indigo-3: #e0defa;
--wikt-palette-indigo-4: #cecbf8;
--wikt-palette-indigo-5: #bfbcf5;
--wikt-palette-indigo-6: #b0acf3;
--wikt-palette-indigo-7: #a19cf1;
--wikt-palette-indigo-8: #918aef;
--wikt-palette-indigo-9: #6e66e0;
--wikt-palette-purple-0: #fcfafe;
--wikt-palette-purple-1: #f5f0fd;
--wikt-palette-purple-2: #efe6fc;
--wikt-palette-purple-3: #e8dbfb;
--wikt-palette-purple-4: #dbc6f8;
--wikt-palette-purple-5: #d0b6f7;
--wikt-palette-purple-6: #c5a5f5;
--wikt-palette-purple-7: #b993f2;
--wikt-palette-purple-8: #ad7ff0;
--wikt-palette-purple-9: #9053eb;
--wikt-palette-magenta-0: #fcf9fe;
--wikt-palette-magenta-1: #f7effc;
--wikt-palette-magenta-2: #f3e4fa;
--wikt-palette-magenta-3: #eedaf9;
--wikt-palette-magenta-4: #e3c4f5;
--wikt-palette-magenta-5: #dbb2f2;
--wikt-palette-magenta-6: #d3a0ef;
--wikt-palette-magenta-7: #ca8deb;
--wikt-palette-magenta-8: #c078e8;
--wikt-palette-magenta-9: #a844df;
--wikt-palette-rose-0: #fcf9fc;
--wikt-palette-rose-1: #f8f0f6;
--wikt-palette-rose-2: #f3e6ef;
--wikt-palette-rose-3: #eedbe9;
--wikt-palette-rose-4: #e4c7dc;
--wikt-palette-rose-5: #dcb6d2;
--wikt-palette-rose-6: #d4a5c8;
--wikt-palette-rose-7: #cc94bd;
--wikt-palette-rose-8: #c381b1;
--wikt-palette-rose-9: #af5797;
--wikt-palette-brown-0: #fbfaf9;
--wikt-palette-brown-1: #f4f1ee;
--wikt-palette-brown-2: #eee8e4;
--wikt-palette-brown-3: #e7dfd9;
--wikt-palette-brown-4: #d8cdc3;
--wikt-palette-brown-5: #cdbfb2;
--wikt-palette-brown-6: #c2b1a1;
--wikt-palette-brown-7: #b7a18f;
--wikt-palette-brown-8: #ab927c;
--wikt-palette-brown-9: #917155;
--wikt-palette-grey-red-0: #fef9f9;
--wikt-palette-grey-red-1: #fbeff0;
--wikt-palette-grey-red-2: #f9e5e6;
--wikt-palette-grey-red-3: #f7dadc;
--wikt-palette-grey-red-4: #f2c3c8;
--wikt-palette-grey-red-5: #eeb2b7;
--wikt-palette-grey-red-6: #dca5ab;
--wikt-palette-grey-red-7: #ce979c;
--wikt-palette-grey-red-8: #c0878c;
--wikt-palette-grey-red-9: #9f686d;
--wikt-palette-grey-scarlet-0: #fdfaf8;
--wikt-palette-grey-scarlet-1: #faf0ec;
--wikt-palette-grey-scarlet-2: #f7e6e0;
--wikt-palette-grey-scarlet-3: #f4dcd4;
--wikt-palette-grey-scarlet-4: #eec7bb;
--wikt-palette-grey-scarlet-5: #e8b6a7;
--wikt-palette-grey-scarlet-6: #d6a99c;
--wikt-palette-grey-scarlet-7: #c89b8d;
--wikt-palette-grey-scarlet-8: #b98c7e;
--wikt-palette-grey-scarlet-9: #986d5f;
--wikt-palette-grey-orange-0: #fdfaf6;
--wikt-palette-grey-orange-1: #f9f0e8;
--wikt-palette-grey-orange-2: #f4e7d9;
--wikt-palette-grey-orange-3: #f0deca;
--wikt-palette-grey-orange-4: #e8caaa;
--wikt-palette-grey-orange-5: #e1bb92;
--wikt-palette-grey-orange-6: #cfae8a;
--wikt-palette-grey-orange-7: #c19f7c;
--wikt-palette-grey-orange-8: #b2906d;
--wikt-palette-grey-orange-9: #917151;
--wikt-palette-grey-amber-0: #fcfaf5;
--wikt-palette-grey-amber-1: #f7f1e6;
--wikt-palette-grey-amber-2: #f1e9d6;
--wikt-palette-grey-amber-3: #ece0c6;
--wikt-palette-grey-amber-4: #e0cda3;
--wikt-palette-grey-amber-5: #d8bf8a;
--wikt-palette-grey-amber-6: #c6b185;
--wikt-palette-grey-amber-7: #b7a377;
--wikt-palette-grey-amber-8: #a89468;
--wikt-palette-grey-amber-9: #88754c;
--wikt-palette-grey-yellow-0: #fbfaf6;
--wikt-palette-grey-yellow-1: #f3f2e7;
--wikt-palette-grey-yellow-2: #ecead9;
--wikt-palette-grey-yellow-3: #e5e1ca;
--wikt-palette-grey-yellow-4: #d5d0ab;
--wikt-palette-grey-yellow-5: #cac394;
--wikt-palette-grey-yellow-6: #beb57c;
--wikt-palette-grey-yellow-7: #aca67f;
--wikt-palette-grey-yellow-8: #9e9770;
--wikt-palette-grey-yellow-9: #7e7851;
--wikt-palette-grey-lime-0: #fafbf7;
--wikt-palette-grey-lime-1: #f0f3e8;
--wikt-palette-grey-lime-2: #e7ebda;
--wikt-palette-grey-lime-3: #dee3cc;
--wikt-palette-grey-lime-4: #cad3ae;
--wikt-palette-grey-lime-5: #bbc798;
--wikt-palette-grey-lime-6: #abba81;
--wikt-palette-grey-lime-7: #a0a984;
--wikt-palette-grey-lime-8: #919b74;
--wikt-palette-grey-lime-9: #727c55;
--wikt-palette-grey-green-0: #f8fbf7;
--wikt-palette-grey-green-1: #edf4ea;
--wikt-palette-grey-green-2: #e2ecdd;
--wikt-palette-grey-green-3: #d6e5ce;
--wikt-palette-grey-green-4: #bfd6b3;
--wikt-palette-grey-green-5: #acca9d;
--wikt-palette-grey-green-6: #9abe87;
--wikt-palette-grey-green-7: #93ad86;
--wikt-palette-grey-green-8: #839e75;
--wikt-palette-grey-green-9: #647f56;
--wikt-palette-grey-teal-0: #f7fbf9;
--wikt-palette-grey-teal-1: #eaf4ef;
--wikt-palette-grey-teal-2: #ddede5;
--wikt-palette-grey-teal-3: #d0e6dc;
--wikt-palette-grey-teal-4: #b4d7c7;
--wikt-palette-grey-teal-5: #9ecbb6;
--wikt-palette-grey-teal-6: #88bfa5;
--wikt-palette-grey-teal-7: #86ae9b;
--wikt-palette-grey-teal-8: #769f8c;
--wikt-palette-grey-teal-9: #56806d;
--wikt-palette-grey-cyan-0: #f7fbfb;
--wikt-palette-grey-cyan-1: #ebf3f5;
--wikt-palette-grey-cyan-2: #deecee;
--wikt-palette-grey-cyan-3: #d1e4e7;
--wikt-palette-grey-cyan-4: #b6d4da;
--wikt-palette-grey-cyan-5: #a1c8cf;
--wikt-palette-grey-cyan-6: #8cbbc4;
--wikt-palette-grey-cyan-7: #86acb2;
--wikt-palette-grey-cyan-8: #769da3;
--wikt-palette-grey-cyan-9: #577e84;
--wikt-palette-grey-blue-0: #f8fafe;
--wikt-palette-grey-blue-1: #ecf2fc;
--wikt-palette-grey-blue-2: #e1eafb;
--wikt-palette-grey-blue-3: #d5e1f9;
--wikt-palette-grey-blue-4: #bdd0f6;
--wikt-palette-grey-blue-5: #aec2ea;
--wikt-palette-grey-blue-6: #9fb4dd;
--wikt-palette-grey-blue-7: #91a6cf;
--wikt-palette-grey-blue-8: #8297c0;
--wikt-palette-grey-blue-9: #6477a0;
--wikt-palette-grey-indigo-0: #fafafe;
--wikt-palette-grey-indigo-1: #f1f1fb;
--wikt-palette-grey-indigo-2: #e9e8f9;
--wikt-palette-grey-indigo-3: #e0def7;
--wikt-palette-grey-indigo-4: #cdcbf2;
--wikt-palette-grey-indigo-5: #c0bcee;
--wikt-palette-grey-indigo-6: #b2afdc;
--wikt-palette-grey-indigo-7: #a4a1d0;
--wikt-palette-grey-indigo-8: #9592c2;
--wikt-palette-grey-indigo-9: #7572a3;
--wikt-palette-grey-purple-0: #fbfafe;
--wikt-palette-grey-purple-1: #f5f0fc;
--wikt-palette-grey-purple-2: #eee6fa;
--wikt-palette-grey-purple-3: #e7dcf8;
--wikt-palette-grey-purple-4: #d9c8f3;
--wikt-palette-grey-purple-5: #ceb8f0;
--wikt-palette-grey-purple-6: #c0aae1;
--wikt-palette-grey-purple-7: #b19bd3;
--wikt-palette-grey-purple-8: #a38cc5;
--wikt-palette-grey-purple-9: #846da5;
--wikt-palette-grey-magenta-0: #fcf9fd;
--wikt-palette-grey-magenta-1: #f7effa;
--wikt-palette-grey-magenta-2: #f1e5f7;
--wikt-palette-grey-magenta-3: #ebdbf4;
--wikt-palette-grey-magenta-4: #e0c6ee;
--wikt-palette-grey-magenta-5: #d7b5e9;
--wikt-palette-grey-magenta-6: #cda4e4;
--wikt-palette-grey-magenta-7: #bc98d0;
--wikt-palette-grey-magenta-8: #ae89c2;
--wikt-palette-grey-magenta-9: #8e69a2;
--wikt-palette-grey-rose-0: #fcfafb;
--wikt-palette-grey-rose-1: #f6f0f4;
--wikt-palette-grey-rose-2: #efe7ed;
--wikt-palette-grey-rose-3: #e9dde6;
--wikt-palette-grey-rose-4: #ddcad8;
--wikt-palette-grey-rose-5: #d3bacc;
--wikt-palette-grey-rose-6: #c9abc1;
--wikt-palette-grey-rose-7: #be9bb5;
--wikt-palette-grey-rose-8: #b38aa8;
--wikt-palette-grey-rose-9: #8c6e84;
--wikt-palette-grey-brown-0: #fafaf9;
--wikt-palette-grey-brown-1: #f3f2f0;
--wikt-palette-grey-brown-2: #ebe9e6;
--wikt-palette-grey-brown-3: #e4e0dc;
--wikt-palette-grey-brown-4: #d4cec8;
--wikt-palette-grey-brown-5: #c8c0b9;
--wikt-palette-grey-brown-6: #bcb2a9;
--wikt-palette-grey-brown-7: #afa398;
--wikt-palette-grey-brown-8: #a19488;
--wikt-palette-grey-brown-9: #7f766d;
--wikt-palette-grey-0: #fafafa;
--wikt-palette-grey-1: #f2f2f2;
--wikt-palette-grey-2: #e9e9e9;
--wikt-palette-grey-3: #e1e1e1;
--wikt-palette-grey-4: #cfcfcf;
--wikt-palette-grey-5: #c2c2c2;
--wikt-palette-grey-6: #b4b4b4;
--wikt-palette-grey-7: #a5a5a5;
--wikt-palette-grey-8: #979797;
--wikt-palette-grey-9: #777777;
}
/* Styles need to be duplicated exactly between these two selectors. */
@media screen {
html.skin-theme-clientpref-night {
--wikt-palette-white: #101418;
--wikt-palette-black: #eaecf0;
--wikt-palette-beige: #2f1f10;
--wikt-palette-darkgrey: #b2bbc3;
--wikt-palette-grey: #4e4e4e;
--wikt-palette-lightgrey: #3d3d3d;
--wikt-palette-lightergrey: #242424;
--wikt-palette-cream: #2c2c21;
--wikt-palette-pink: #612042;
--wikt-palette-peach: #58230a;
--wikt-palette-gold: #c9b163;
--wikt-palette-dullgold: #ab995e;
--wikt-palette-yellow: #4a3912;
--wikt-palette-dullyellow: #3a3520;
--wikt-palette-brightyellow: #5e5300;
--wikt-palette-palegreen: #112511;
--wikt-palette-palergreen: #051f0c;
--wikt-palette-mint: #194819;
--wikt-palette-green: #aad7aa;
--wikt-palette-forestgreen: #499149;
--wikt-palette-luminousgreen: #103310;
--wikt-palette-paleblue: #1c2229;
--wikt-palette-cyan: #0a3239;
--wikt-palette-palecyan: #171c21;
--wikt-palette-lightblue: #092f58;
--wikt-palette-dulllightblue: #305070;
--wikt-palette-blue: #aaaaff;
--wikt-palette-dullblue: #b8c0c5;
--wikt-palette-dullcyan: #27292d;
--wikt-palette-deepblue: #a5c3e1;
--wikt-palette-lavender: #1a1a2a;
--wikt-palette-lightyellow: #24241a;
--wikt-palette-red: #d73333;
--wikt-palette-darkred: #ffcccc;
--wikt-palette-deepred: #e36a79;
--wikt-palette-orange: #593e02;
--wikt-palette-lighterblue: #0e1e33;
--wikt-palette-dulllighterblue: #102437;
--wikt-palette-lightred: #421211;
--wikt-palette-indigo: #30306f;
--wikt-palette-lightindigo: #242448;
--wikt-palette-palerpink: #251111;
--wikt-palette-honey: #feefb3;
/* Autogenerated with [[Wiktionary:Palette/numbered]] */
--wikt-palette-red-0: #28070a;
--wikt-palette-red-1: #3e0c11;
--wikt-palette-red-2: #5a131a;
--wikt-palette-red-3: #731b24;
--wikt-palette-red-4: #89242d;
--wikt-palette-red-5: #a12d38;
--wikt-palette-red-6: #bf3b48;
--wikt-palette-red-7: #e95462;
--wikt-palette-red-8: #ee7984;
--wikt-palette-red-9: #f3a2aa;
--wikt-palette-scarlet-0: #250c05;
--wikt-palette-scarlet-1: #3a1409;
--wikt-palette-scarlet-2: #521d0e;
--wikt-palette-scarlet-3: #692814;
--wikt-palette-scarlet-4: #7f311a;
--wikt-palette-scarlet-5: #953c22;
--wikt-palette-scarlet-6: #b24b2c;
--wikt-palette-scarlet-7: #e25e37;
--wikt-palette-scarlet-8: #e88062;
--wikt-palette-scarlet-9: #efa791;
--wikt-palette-orange-0: #211101;
--wikt-palette-orange-1: #331b03;
--wikt-palette-orange-2: #462604;
--wikt-palette-orange-3: #5c3206;
--wikt-palette-orange-4: #703d08;
--wikt-palette-orange-5: #85490a;
--wikt-palette-orange-6: #a0580e;
--wikt-palette-orange-7: #cb7114;
--wikt-palette-orange-8: #de8931;
--wikt-palette-orange-9: #e8ac6e;
--wikt-palette-amber-0: #1c1401;
--wikt-palette-amber-1: #2c1f03;
--wikt-palette-amber-2: #3d2a04;
--wikt-palette-amber-3: #503806;
--wikt-palette-amber-4: #614508;
--wikt-palette-amber-5: #74520a;
--wikt-palette-amber-6: #8c640d;
--wikt-palette-amber-7: #b17f13;
--wikt-palette-amber-8: #cc9218;
--wikt-palette-amber-9: #ddb258;
--wikt-palette-yellow-0: #181504;
--wikt-palette-yellow-1: #262107;
--wikt-palette-yellow-2: #342e0a;
--wikt-palette-yellow-3: #443c0f;
--wikt-palette-yellow-4: #534a13;
--wikt-palette-yellow-5: #625819;
--wikt-palette-yellow-6: #776b21;
--wikt-palette-yellow-7: #97882f;
--wikt-palette-yellow-8: #ae9e3b;
--wikt-palette-yellow-9: #c8b95b;
--wikt-palette-lime-0: #131705;
--wikt-palette-lime-1: #1e2409;
--wikt-palette-lime-2: #28310d;
--wikt-palette-lime-3: #354112;
--wikt-palette-lime-4: #424f18;
--wikt-palette-lime-5: #4f5e1f;
--wikt-palette-lime-6: #607228;
--wikt-palette-lime-7: #7b903a;
--wikt-palette-lime-8: #8fa648;
--wikt-palette-lime-9: #aac25f;
--wikt-palette-green-0: #0c1805;
--wikt-palette-green-1: #122608;
--wikt-palette-green-2: #1a340c;
--wikt-palette-green-3: #234411;
--wikt-palette-green-4: #2c5317;
--wikt-palette-green-5: #35631d;
--wikt-palette-green-6: #427726;
--wikt-palette-green-7: #599737;
--wikt-palette-green-8: #6aae45;
--wikt-palette-green-9: #8dc86d;
--wikt-palette-teal-0: #04180f;
--wikt-palette-teal-1: #082618;
--wikt-palette-teal-2: #0c3422;
--wikt-palette-teal-3: #11452d;
--wikt-palette-teal-4: #165438;
--wikt-palette-teal-5: #1c6443;
--wikt-palette-teal-6: #267953;
--wikt-palette-teal-7: #36986b;
--wikt-palette-teal-8: #43af7e;
--wikt-palette-teal-9: #6fc9a0;
--wikt-palette-cyan-0: #04171b;
--wikt-palette-cyan-1: #07242a;
--wikt-palette-cyan-2: #0a3239;
--wikt-palette-cyan-3: #0f424b;
--wikt-palette-cyan-4: #14515c;
--wikt-palette-cyan-5: #1a606d;
--wikt-palette-cyan-6: #227583;
--wikt-palette-cyan-7: #3194a6;
--wikt-palette-cyan-8: #40aabd;
--wikt-palette-cyan-9: #78c4d1;
--wikt-palette-blue-0: #07132d;
--wikt-palette-blue-1: #0b1f45;
--wikt-palette-blue-2: #122b60;
--wikt-palette-blue-3: #19397b;
--wikt-palette-blue-4: #214692;
--wikt-palette-blue-5: #2954ab;
--wikt-palette-blue-6: #3667c9;
--wikt-palette-blue-7: #4e84f2;
--wikt-palette-blue-8: #709cf5;
--wikt-palette-blue-9: #99b8f8;
--wikt-palette-indigo-0: #100e36;
--wikt-palette-indigo-1: #1b1751;
--wikt-palette-indigo-2: #27226c;
--wikt-palette-indigo-3: #352f88;
--wikt-palette-indigo-4: #423b9e;
--wikt-palette-indigo-5: #5049b4;
--wikt-palette-indigo-6: #5f55e7;
--wikt-palette-indigo-7: #8179ec;
--wikt-palette-indigo-8: #9992f0;
--wikt-palette-indigo-9: #b6b1f4;
--wikt-palette-purple-0: #1a0934;
--wikt-palette-purple-1: #29104e;
--wikt-palette-purple-2: #3a196d;
--wikt-palette-purple-3: #4c238a;
--wikt-palette-purple-4: #5c2da3;
--wikt-palette-purple-5: #6e38bc;
--wikt-palette-purple-6: #8541e9;
--wikt-palette-purple-7: #a16cee;
--wikt-palette-purple-8: #b389f1;
--wikt-palette-purple-9: #c9abf5;
--wikt-palette-magenta-0: #20062d;
--wikt-palette-magenta-1: #310b46;
--wikt-palette-magenta-2: #471363;
--wikt-palette-magenta-3: #5c1b7f;
--wikt-palette-magenta-4: #6e2398;
--wikt-palette-magenta-5: #822cb1;
--wikt-palette-magenta-6: #9f30dc;
--wikt-palette-magenta-7: #b663e4;
--wikt-palette-magenta-8: #c581ea;
--wikt-palette-magenta-9: #d6a6f0;
--wikt-palette-rose-0: #240b1d;
--wikt-palette-rose-1: #37122d;
--wikt-palette-rose-2: #4b1c3f;
--wikt-palette-rose-3: #5f2751;
--wikt-palette-rose-4: #723362;
--wikt-palette-rose-5: #844072;
--wikt-palette-rose-6: #a7478e;
--wikt-palette-rose-7: #ba6fa6;
--wikt-palette-rose-8: #c78ab7;
--wikt-palette-rose-9: #d7abcc;
--wikt-palette-brown-0: #1d130b;
--wikt-palette-brown-1: #2d1e12;
--wikt-palette-brown-2: #3c2a1b;
--wikt-palette-brown-3: #4d3826;
--wikt-palette-brown-4: #5d4532;
--wikt-palette-brown-5: #6c533e;
--wikt-palette-brown-6: #876446;
--wikt-palette-brown-7: #9f826a;
--wikt-palette-brown-8: #b19986;
--wikt-palette-brown-9: #c6b5a7;
--wikt-palette-grey-red-0: #230f11;
--wikt-palette-grey-red-1: #34191b;
--wikt-palette-grey-red-2: #462327;
--wikt-palette-grey-red-3: #5a3034;
--wikt-palette-grey-red-4: #6b3c41;
--wikt-palette-grey-red-5: #7c4a4f;
--wikt-palette-grey-red-6: #925c61;
--wikt-palette-grey-red-7: #b1797e;
--wikt-palette-grey-red-8: #c78e94;
--wikt-palette-grey-red-9: #eba6ad;
--wikt-palette-grey-scarlet-0: #20110d;
--wikt-palette-grey-scarlet-1: #311c16;
--wikt-palette-grey-scarlet-2: #422720;
--wikt-palette-grey-scarlet-3: #54342b;
--wikt-palette-grey-scarlet-4: #654137;
--wikt-palette-grey-scarlet-5: #764e43;
--wikt-palette-grey-scarlet-6: #8b6154;
--wikt-palette-grey-scarlet-7: #aa7d70;
--wikt-palette-grey-scarlet-8: #c19386;
--wikt-palette-grey-scarlet-9: #e5ab9a;
--wikt-palette-grey-orange-0: #1c140a;
--wikt-palette-grey-orange-1: #2c1f11;
--wikt-palette-grey-orange-2: #3b2a19;
--wikt-palette-grey-orange-3: #4d3823;
--wikt-palette-grey-orange-4: #5d452c;
--wikt-palette-grey-orange-5: #6e5337;
--wikt-palette-grey-orange-6: #836547;
--wikt-palette-grey-orange-7: #a28260;
--wikt-palette-grey-orange-8: #b99774;
--wikt-palette-grey-orange-9: #ddb082;
--wikt-palette-grey-amber-0: #1a150a;
--wikt-palette-grey-amber-1: #282010;
--wikt-palette-grey-amber-2: #372c17;
--wikt-palette-grey-amber-3: #473b20;
--wikt-palette-grey-amber-4: #57482a;
--wikt-palette-grey-amber-5: #675634;
--wikt-palette-grey-amber-6: #7b6943;
--wikt-palette-grey-amber-7: #99855b;
--wikt-palette-grey-amber-8: #b09b70;
--wikt-palette-grey-amber-9: #d2b579;
--wikt-palette-grey-yellow-0: #17160a;
--wikt-palette-grey-yellow-1: #242211;
--wikt-palette-grey-yellow-2: #322e19;
--wikt-palette-grey-yellow-3: #413d22;
--wikt-palette-grey-yellow-4: #4f4a2c;
--wikt-palette-grey-yellow-5: #5e5837;
--wikt-palette-grey-yellow-6: #716b47;
--wikt-palette-grey-yellow-7: #8f8861;
--wikt-palette-grey-yellow-8: #a59e78;
--wikt-palette-grey-yellow-9: #c2b984;
--wikt-palette-grey-lime-0: #13160b;
--wikt-palette-grey-lime-1: #1f2312;
--wikt-palette-grey-lime-2: #2b3019;
--wikt-palette-grey-lime-3: #383f23;
--wikt-palette-grey-lime-4: #454d2d;
--wikt-palette-grey-lime-5: #535c39;
--wikt-palette-grey-lime-6: #666f49;
--wikt-palette-grey-lime-7: #838c65;
--wikt-palette-grey-lime-8: #98a27c;
--wikt-palette-grey-lime-9: #b1be8a;
--wikt-palette-grey-green-0: #0f170b;
--wikt-palette-grey-green-1: #182512;
--wikt-palette-grey-green-2: #22321a;
--wikt-palette-grey-green-3: #2e4124;
--wikt-palette-grey-green-4: #3a502e;
--wikt-palette-grey-green-5: #465f3a;
--wikt-palette-grey-green-6: #58724a;
--wikt-palette-grey-green-7: #748f66;
--wikt-palette-grey-green-8: #8ba57e;
--wikt-palette-grey-green-9: #a1c290;
--wikt-palette-grey-teal-0: #0b1812;
--wikt-palette-grey-teal-1: #12251d;
--wikt-palette-grey-teal-2: #1a3227;
--wikt-palette-grey-teal-3: #244235;
--wikt-palette-grey-teal-4: #2e5041;
--wikt-palette-grey-teal-5: #3a5f4e;
--wikt-palette-grey-teal-6: #4b7361;
--wikt-palette-grey-teal-7: #66907d;
--wikt-palette-grey-teal-8: #7ea694;
--wikt-palette-grey-teal-9: #91c3ac;
--wikt-palette-grey-cyan-0: #0b171a;
--wikt-palette-grey-cyan-1: #122428;
--wikt-palette-grey-cyan-2: #1a3136;
--wikt-palette-grey-cyan-3: #244145;
--wikt-palette-grey-cyan-4: #2f4f54;
--wikt-palette-grey-cyan-5: #3a5d64;
--wikt-palette-grey-cyan-6: #4b7178;
--wikt-palette-grey-cyan-7: #678e95;
--wikt-palette-grey-cyan-8: #7ea4ab;
--wikt-palette-grey-cyan-9: #94c0c8;
--wikt-palette-grey-blue-0: #0f1523;
--wikt-palette-grey-blue-1: #182134;
--wikt-palette-grey-blue-2: #212d46;
--wikt-palette-grey-blue-3: #2e3c5a;
--wikt-palette-grey-blue-4: #394a6a;
--wikt-palette-grey-blue-5: #47587c;
--wikt-palette-grey-blue-6: #586b92;
--wikt-palette-grey-blue-7: #7488b2;
--wikt-palette-grey-blue-8: #899ec8;
--wikt-palette-grey-blue-9: #9cb8f2;
--wikt-palette-grey-indigo-0: #141327;
--wikt-palette-grey-indigo-1: #201e3a;
--wikt-palette-grey-indigo-2: #2c2a4c;
--wikt-palette-grey-indigo-3: #3a385f;
--wikt-palette-grey-indigo-4: #484571;
--wikt-palette-grey-indigo-5: #565382;
--wikt-palette-grey-indigo-6: #696696;
--wikt-palette-grey-indigo-7: #8682b4;
--wikt-palette-grey-indigo-8: #9c98c8;
--wikt-palette-grey-indigo-9: #b6b2ec;
--wikt-palette-grey-purple-0: #1a1126;
--wikt-palette-grey-purple-1: #271b39;
--wikt-palette-grey-purple-2: #35264b;
--wikt-palette-grey-purple-3: #453460;
--wikt-palette-grey-purple-4: #544071;
--wikt-palette-grey-purple-5: #634e83;
--wikt-palette-grey-purple-6: #776098;
--wikt-palette-grey-purple-7: #957db7;
--wikt-palette-grey-purple-8: #aa93cc;
--wikt-palette-grey-purple-9: #c7acee;
--wikt-palette-grey-magenta-0: #1d0f24;
--wikt-palette-grey-magenta-1: #2c1937;
--wikt-palette-grey-magenta-2: #3c244a;
--wikt-palette-grey-magenta-3: #4d315d;
--wikt-palette-grey-magenta-4: #5d3d6e;
--wikt-palette-grey-magenta-5: #6c4b7f;
--wikt-palette-grey-magenta-6: #815d95;
--wikt-palette-grey-magenta-7: #9f7ab4;
--wikt-palette-grey-magenta-8: #be88dc;
--wikt-palette-grey-magenta-9: #d1aae6;
--wikt-palette-grey-rose-0: #1f101b;
--wikt-palette-grey-rose-1: #2f1a29;
--wikt-palette-grey-rose-2: #3e2538;
--wikt-palette-grey-rose-3: #503348;
--wikt-palette-grey-rose-4: #5f4057;
--wikt-palette-grey-rose-5: #6e4e66;
--wikt-palette-grey-rose-6: #816179;
--wikt-palette-grey-rose-7: #a9799c;
--wikt-palette-grey-rose-8: #b992ae;
--wikt-palette-grey-rose-9: #ccb0c5;
--wikt-palette-grey-brown-0: #1a140f;
--wikt-palette-grey-brown-1: #282019;
--wikt-palette-grey-brown-2: #362c23;
--wikt-palette-grey-brown-3: #463a31;
--wikt-palette-grey-brown-4: #53473d;
--wikt-palette-grey-brown-5: #62564c;
--wikt-palette-grey-brown-6: #746960;
--wikt-palette-grey-brown-7: #958578;
--wikt-palette-grey-brown-8: #a89b90;
--wikt-palette-grey-brown-9: #c0b7af;
--wikt-palette-grey-0: #151515;
--wikt-palette-grey-1: #212121;
--wikt-palette-grey-2: #2d2d2d;
--wikt-palette-grey-3: #3c3c3c;
--wikt-palette-grey-4: #494949;
--wikt-palette-grey-5: #585858;
--wikt-palette-grey-6: #6b6b6b;
--wikt-palette-grey-7: #878787;
--wikt-palette-grey-8: #9d9d9d;
--wikt-palette-grey-9: #b8b8b8;
}
}
@media screen and (prefers-color-scheme: dark) {
html.skin-theme-clientpref-os {
--wikt-palette-white: #101418;
--wikt-palette-black: #eaecf0;
--wikt-palette-beige: #2f1f10;
--wikt-palette-darkgrey: #b2bbc3;
--wikt-palette-grey: #4e4e4e;
--wikt-palette-lightgrey: #3d3d3d;
--wikt-palette-lightergrey: #242424;
--wikt-palette-cream: #2c2c21;
--wikt-palette-pink: #612042;
--wikt-palette-peach: #58230a;
--wikt-palette-gold: #c9b163;
--wikt-palette-dullgold: #ab995e;
--wikt-palette-yellow: #4a3912;
--wikt-palette-dullyellow: #3a3520;
--wikt-palette-brightyellow: #5e5300;
--wikt-palette-palegreen: #112511;
--wikt-palette-palergreen: #051f0c;
--wikt-palette-mint: #194819;
--wikt-palette-green: #aad7aa;
--wikt-palette-forestgreen: #499149;
--wikt-palette-luminousgreen: #103310;
--wikt-palette-paleblue: #1c2229;
--wikt-palette-cyan: #0a3239;
--wikt-palette-palecyan: #171c21;
--wikt-palette-lightblue: #092f58;
--wikt-palette-dulllightblue: #305070;
--wikt-palette-blue: #aaaaff;
--wikt-palette-dullblue: #b8c0c5;
--wikt-palette-dullcyan: #27292d;
--wikt-palette-deepblue: #a5c3e1;
--wikt-palette-lavender: #1a1a2a;
--wikt-palette-lightyellow: #24241a;
--wikt-palette-red: #d73333;
--wikt-palette-darkred: #ffcccc;
--wikt-palette-deepred: #e36a79;
--wikt-palette-orange: #593e02;
--wikt-palette-lighterblue: #0e1e33;
--wikt-palette-dulllighterblue: #102437;
--wikt-palette-lightred: #421211;
--wikt-palette-indigo: #30306f;
--wikt-palette-lightindigo: #242448;
--wikt-palette-palerpink: #251111;
--wikt-palette-honey: #feefb3;
/* Autogenerated with [[Wiktionary:Palette/numbered]] */
--wikt-palette-red-0: #28070a;
--wikt-palette-red-1: #3e0c11;
--wikt-palette-red-2: #5a131a;
--wikt-palette-red-3: #731b24;
--wikt-palette-red-4: #89242d;
--wikt-palette-red-5: #a12d38;
--wikt-palette-red-6: #bf3b48;
--wikt-palette-red-7: #e95462;
--wikt-palette-red-8: #ee7984;
--wikt-palette-red-9: #f3a2aa;
--wikt-palette-scarlet-0: #250c05;
--wikt-palette-scarlet-1: #3a1409;
--wikt-palette-scarlet-2: #521d0e;
--wikt-palette-scarlet-3: #692814;
--wikt-palette-scarlet-4: #7f311a;
--wikt-palette-scarlet-5: #953c22;
--wikt-palette-scarlet-6: #b24b2c;
--wikt-palette-scarlet-7: #e25e37;
--wikt-palette-scarlet-8: #e88062;
--wikt-palette-scarlet-9: #efa791;
--wikt-palette-orange-0: #211101;
--wikt-palette-orange-1: #331b03;
--wikt-palette-orange-2: #462604;
--wikt-palette-orange-3: #5c3206;
--wikt-palette-orange-4: #703d08;
--wikt-palette-orange-5: #85490a;
--wikt-palette-orange-6: #a0580e;
--wikt-palette-orange-7: #cb7114;
--wikt-palette-orange-8: #de8931;
--wikt-palette-orange-9: #e8ac6e;
--wikt-palette-amber-0: #1c1401;
--wikt-palette-amber-1: #2c1f03;
--wikt-palette-amber-2: #3d2a04;
--wikt-palette-amber-3: #503806;
--wikt-palette-amber-4: #614508;
--wikt-palette-amber-5: #74520a;
--wikt-palette-amber-6: #8c640d;
--wikt-palette-amber-7: #b17f13;
--wikt-palette-amber-8: #cc9218;
--wikt-palette-amber-9: #ddb258;
--wikt-palette-yellow-0: #181504;
--wikt-palette-yellow-1: #262107;
--wikt-palette-yellow-2: #342e0a;
--wikt-palette-yellow-3: #443c0f;
--wikt-palette-yellow-4: #534a13;
--wikt-palette-yellow-5: #625819;
--wikt-palette-yellow-6: #776b21;
--wikt-palette-yellow-7: #97882f;
--wikt-palette-yellow-8: #ae9e3b;
--wikt-palette-yellow-9: #c8b95b;
--wikt-palette-lime-0: #131705;
--wikt-palette-lime-1: #1e2409;
--wikt-palette-lime-2: #28310d;
--wikt-palette-lime-3: #354112;
--wikt-palette-lime-4: #424f18;
--wikt-palette-lime-5: #4f5e1f;
--wikt-palette-lime-6: #607228;
--wikt-palette-lime-7: #7b903a;
--wikt-palette-lime-8: #8fa648;
--wikt-palette-lime-9: #aac25f;
--wikt-palette-green-0: #0c1805;
--wikt-palette-green-1: #122608;
--wikt-palette-green-2: #1a340c;
--wikt-palette-green-3: #234411;
--wikt-palette-green-4: #2c5317;
--wikt-palette-green-5: #35631d;
--wikt-palette-green-6: #427726;
--wikt-palette-green-7: #599737;
--wikt-palette-green-8: #6aae45;
--wikt-palette-green-9: #8dc86d;
--wikt-palette-teal-0: #04180f;
--wikt-palette-teal-1: #082618;
--wikt-palette-teal-2: #0c3422;
--wikt-palette-teal-3: #11452d;
--wikt-palette-teal-4: #165438;
--wikt-palette-teal-5: #1c6443;
--wikt-palette-teal-6: #267953;
--wikt-palette-teal-7: #36986b;
--wikt-palette-teal-8: #43af7e;
--wikt-palette-teal-9: #6fc9a0;
--wikt-palette-cyan-0: #04171b;
--wikt-palette-cyan-1: #07242a;
--wikt-palette-cyan-2: #0a3239;
--wikt-palette-cyan-3: #0f424b;
--wikt-palette-cyan-4: #14515c;
--wikt-palette-cyan-5: #1a606d;
--wikt-palette-cyan-6: #227583;
--wikt-palette-cyan-7: #3194a6;
--wikt-palette-cyan-8: #40aabd;
--wikt-palette-cyan-9: #78c4d1;
--wikt-palette-blue-0: #07132d;
--wikt-palette-blue-1: #0b1f45;
--wikt-palette-blue-2: #122b60;
--wikt-palette-blue-3: #19397b;
--wikt-palette-blue-4: #214692;
--wikt-palette-blue-5: #2954ab;
--wikt-palette-blue-6: #3667c9;
--wikt-palette-blue-7: #4e84f2;
--wikt-palette-blue-8: #709cf5;
--wikt-palette-blue-9: #99b8f8;
--wikt-palette-indigo-0: #100e36;
--wikt-palette-indigo-1: #1b1751;
--wikt-palette-indigo-2: #27226c;
--wikt-palette-indigo-3: #352f88;
--wikt-palette-indigo-4: #423b9e;
--wikt-palette-indigo-5: #5049b4;
--wikt-palette-indigo-6: #5f55e7;
--wikt-palette-indigo-7: #8179ec;
--wikt-palette-indigo-8: #9992f0;
--wikt-palette-indigo-9: #b6b1f4;
--wikt-palette-purple-0: #1a0934;
--wikt-palette-purple-1: #29104e;
--wikt-palette-purple-2: #3a196d;
--wikt-palette-purple-3: #4c238a;
--wikt-palette-purple-4: #5c2da3;
--wikt-palette-purple-5: #6e38bc;
--wikt-palette-purple-6: #8541e9;
--wikt-palette-purple-7: #a16cee;
--wikt-palette-purple-8: #b389f1;
--wikt-palette-purple-9: #c9abf5;
--wikt-palette-magenta-0: #20062d;
--wikt-palette-magenta-1: #310b46;
--wikt-palette-magenta-2: #471363;
--wikt-palette-magenta-3: #5c1b7f;
--wikt-palette-magenta-4: #6e2398;
--wikt-palette-magenta-5: #822cb1;
--wikt-palette-magenta-6: #9f30dc;
--wikt-palette-magenta-7: #b663e4;
--wikt-palette-magenta-8: #c581ea;
--wikt-palette-magenta-9: #d6a6f0;
--wikt-palette-rose-0: #240b1d;
--wikt-palette-rose-1: #37122d;
--wikt-palette-rose-2: #4b1c3f;
--wikt-palette-rose-3: #5f2751;
--wikt-palette-rose-4: #723362;
--wikt-palette-rose-5: #844072;
--wikt-palette-rose-6: #a7478e;
--wikt-palette-rose-7: #ba6fa6;
--wikt-palette-rose-8: #c78ab7;
--wikt-palette-rose-9: #d7abcc;
--wikt-palette-brown-0: #1d130b;
--wikt-palette-brown-1: #2d1e12;
--wikt-palette-brown-2: #3c2a1b;
--wikt-palette-brown-3: #4d3826;
--wikt-palette-brown-4: #5d4532;
--wikt-palette-brown-5: #6c533e;
--wikt-palette-brown-6: #876446;
--wikt-palette-brown-7: #9f826a;
--wikt-palette-brown-8: #b19986;
--wikt-palette-brown-9: #c6b5a7;
--wikt-palette-grey-red-0: #230f11;
--wikt-palette-grey-red-1: #34191b;
--wikt-palette-grey-red-2: #462327;
--wikt-palette-grey-red-3: #5a3034;
--wikt-palette-grey-red-4: #6b3c41;
--wikt-palette-grey-red-5: #7c4a4f;
--wikt-palette-grey-red-6: #925c61;
--wikt-palette-grey-red-7: #b1797e;
--wikt-palette-grey-red-8: #c78e94;
--wikt-palette-grey-red-9: #eba6ad;
--wikt-palette-grey-scarlet-0: #20110d;
--wikt-palette-grey-scarlet-1: #311c16;
--wikt-palette-grey-scarlet-2: #422720;
--wikt-palette-grey-scarlet-3: #54342b;
--wikt-palette-grey-scarlet-4: #654137;
--wikt-palette-grey-scarlet-5: #764e43;
--wikt-palette-grey-scarlet-6: #8b6154;
--wikt-palette-grey-scarlet-7: #aa7d70;
--wikt-palette-grey-scarlet-8: #c19386;
--wikt-palette-grey-scarlet-9: #e5ab9a;
--wikt-palette-grey-orange-0: #1c140a;
--wikt-palette-grey-orange-1: #2c1f11;
--wikt-palette-grey-orange-2: #3b2a19;
--wikt-palette-grey-orange-3: #4d3823;
--wikt-palette-grey-orange-4: #5d452c;
--wikt-palette-grey-orange-5: #6e5337;
--wikt-palette-grey-orange-6: #836547;
--wikt-palette-grey-orange-7: #a28260;
--wikt-palette-grey-orange-8: #b99774;
--wikt-palette-grey-orange-9: #ddb082;
--wikt-palette-grey-amber-0: #1a150a;
--wikt-palette-grey-amber-1: #282010;
--wikt-palette-grey-amber-2: #372c17;
--wikt-palette-grey-amber-3: #473b20;
--wikt-palette-grey-amber-4: #57482a;
--wikt-palette-grey-amber-5: #675634;
--wikt-palette-grey-amber-6: #7b6943;
--wikt-palette-grey-amber-7: #99855b;
--wikt-palette-grey-amber-8: #b09b70;
--wikt-palette-grey-amber-9: #d2b579;
--wikt-palette-grey-yellow-0: #17160a;
--wikt-palette-grey-yellow-1: #242211;
--wikt-palette-grey-yellow-2: #322e19;
--wikt-palette-grey-yellow-3: #413d22;
--wikt-palette-grey-yellow-4: #4f4a2c;
--wikt-palette-grey-yellow-5: #5e5837;
--wikt-palette-grey-yellow-6: #716b47;
--wikt-palette-grey-yellow-7: #8f8861;
--wikt-palette-grey-yellow-8: #a59e78;
--wikt-palette-grey-yellow-9: #c2b984;
--wikt-palette-grey-lime-0: #13160b;
--wikt-palette-grey-lime-1: #1f2312;
--wikt-palette-grey-lime-2: #2b3019;
--wikt-palette-grey-lime-3: #383f23;
--wikt-palette-grey-lime-4: #454d2d;
--wikt-palette-grey-lime-5: #535c39;
--wikt-palette-grey-lime-6: #666f49;
--wikt-palette-grey-lime-7: #838c65;
--wikt-palette-grey-lime-8: #98a27c;
--wikt-palette-grey-lime-9: #b1be8a;
--wikt-palette-grey-green-0: #0f170b;
--wikt-palette-grey-green-1: #182512;
--wikt-palette-grey-green-2: #22321a;
--wikt-palette-grey-green-3: #2e4124;
--wikt-palette-grey-green-4: #3a502e;
--wikt-palette-grey-green-5: #465f3a;
--wikt-palette-grey-green-6: #58724a;
--wikt-palette-grey-green-7: #748f66;
--wikt-palette-grey-green-8: #8ba57e;
--wikt-palette-grey-green-9: #a1c290;
--wikt-palette-grey-teal-0: #0b1812;
--wikt-palette-grey-teal-1: #12251d;
--wikt-palette-grey-teal-2: #1a3227;
--wikt-palette-grey-teal-3: #244235;
--wikt-palette-grey-teal-4: #2e5041;
--wikt-palette-grey-teal-5: #3a5f4e;
--wikt-palette-grey-teal-6: #4b7361;
--wikt-palette-grey-teal-7: #66907d;
--wikt-palette-grey-teal-8: #7ea694;
--wikt-palette-grey-teal-9: #91c3ac;
--wikt-palette-grey-cyan-0: #0b171a;
--wikt-palette-grey-cyan-1: #122428;
--wikt-palette-grey-cyan-2: #1a3136;
--wikt-palette-grey-cyan-3: #244145;
--wikt-palette-grey-cyan-4: #2f4f54;
--wikt-palette-grey-cyan-5: #3a5d64;
--wikt-palette-grey-cyan-6: #4b7178;
--wikt-palette-grey-cyan-7: #678e95;
--wikt-palette-grey-cyan-8: #7ea4ab;
--wikt-palette-grey-cyan-9: #94c0c8;
--wikt-palette-grey-blue-0: #0f1523;
--wikt-palette-grey-blue-1: #182134;
--wikt-palette-grey-blue-2: #212d46;
--wikt-palette-grey-blue-3: #2e3c5a;
--wikt-palette-grey-blue-4: #394a6a;
--wikt-palette-grey-blue-5: #47587c;
--wikt-palette-grey-blue-6: #586b92;
--wikt-palette-grey-blue-7: #7488b2;
--wikt-palette-grey-blue-8: #899ec8;
--wikt-palette-grey-blue-9: #9cb8f2;
--wikt-palette-grey-indigo-0: #141327;
--wikt-palette-grey-indigo-1: #201e3a;
--wikt-palette-grey-indigo-2: #2c2a4c;
--wikt-palette-grey-indigo-3: #3a385f;
--wikt-palette-grey-indigo-4: #484571;
--wikt-palette-grey-indigo-5: #565382;
--wikt-palette-grey-indigo-6: #696696;
--wikt-palette-grey-indigo-7: #8682b4;
--wikt-palette-grey-indigo-8: #9c98c8;
--wikt-palette-grey-indigo-9: #b6b2ec;
--wikt-palette-grey-purple-0: #1a1126;
--wikt-palette-grey-purple-1: #271b39;
--wikt-palette-grey-purple-2: #35264b;
--wikt-palette-grey-purple-3: #453460;
--wikt-palette-grey-purple-4: #544071;
--wikt-palette-grey-purple-5: #634e83;
--wikt-palette-grey-purple-6: #776098;
--wikt-palette-grey-purple-7: #957db7;
--wikt-palette-grey-purple-8: #aa93cc;
--wikt-palette-grey-purple-9: #c7acee;
--wikt-palette-grey-magenta-0: #1d0f24;
--wikt-palette-grey-magenta-1: #2c1937;
--wikt-palette-grey-magenta-2: #3c244a;
--wikt-palette-grey-magenta-3: #4d315d;
--wikt-palette-grey-magenta-4: #5d3d6e;
--wikt-palette-grey-magenta-5: #6c4b7f;
--wikt-palette-grey-magenta-6: #815d95;
--wikt-palette-grey-magenta-7: #9f7ab4;
--wikt-palette-grey-magenta-8: #be88dc;
--wikt-palette-grey-magenta-9: #d1aae6;
--wikt-palette-grey-rose-0: #1f101b;
--wikt-palette-grey-rose-1: #2f1a29;
--wikt-palette-grey-rose-2: #3e2538;
--wikt-palette-grey-rose-3: #503348;
--wikt-palette-grey-rose-4: #5f4057;
--wikt-palette-grey-rose-5: #6e4e66;
--wikt-palette-grey-rose-6: #816179;
--wikt-palette-grey-rose-7: #a9799c;
--wikt-palette-grey-rose-8: #b992ae;
--wikt-palette-grey-rose-9: #ccb0c5;
--wikt-palette-grey-brown-0: #1a140f;
--wikt-palette-grey-brown-1: #282019;
--wikt-palette-grey-brown-2: #362c23;
--wikt-palette-grey-brown-3: #463a31;
--wikt-palette-grey-brown-4: #53473d;
--wikt-palette-grey-brown-5: #62564c;
--wikt-palette-grey-brown-6: #746960;
--wikt-palette-grey-brown-7: #958578;
--wikt-palette-grey-brown-8: #a89b90;
--wikt-palette-grey-brown-9: #c0b7af;
--wikt-palette-grey-0: #151515;
--wikt-palette-grey-1: #212121;
--wikt-palette-grey-2: #2d2d2d;
--wikt-palette-grey-3: #3c3c3c;
--wikt-palette-grey-4: #494949;
--wikt-palette-grey-5: #585858;
--wikt-palette-grey-6: #6b6b6b;
--wikt-palette-grey-7: #878787;
--wikt-palette-grey-8: #9d9d9d;
--wikt-palette-grey-9: #b8b8b8;
}
}
9f3vrf9yu9ryz0rpl2nsvxf7g0vzztl
မဳဒဳယာဝဳကဳ:Gadget-NoAutoCaseRedirect.js
8
295747
396576
2026-06-07T20:03:54Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "window.disableAutoRedirect = true;"
396576
javascript
text/javascript
window.disableAutoRedirect = true;
24pdvveeqbc3n84c6t533ssk2ibayrv
မဳဒဳယာဝဳကဳ:Gadget-LegacyScriptsNewNode.js
8
295748
396577
2026-06-07T20:05:28Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "// this script is deprecated do not use newNode function! use jQuery instead /** * Create a new DOM node for the current document. * Basic usage: * var mySpan = newNode('span', "Hello World!") * Supports attributes and event handlers: * var mySpan = newNode('span', {style:"color: red", focus: function(){alert(this)}, id:"hello"}, "World, Hello!") * Also allows nesting to create trees: *..."
396577
javascript
text/javascript
// this script is deprecated do not use newNode function! use jQuery instead
/**
* Create a new DOM node for the current document.
* Basic usage:
* var mySpan = newNode('span', "Hello World!")
* Supports attributes and event handlers:
* var mySpan = newNode('span', {style:"color: red", focus: function(){alert(this)}, id:"hello"}, "World, Hello!")
* Also allows nesting to create trees:
* var myPar = newNode('p', newNode('b',{style:"color: blue"},"Hello"), mySpan)
**/
var newNode = window.newNode = function newNode(tagname) {
var node = document.createElement(tagname);
for (var i = 1; i < arguments.length; i++) {
var argument = arguments[i];
if (typeof argument == 'string') { //Text
node.appendChild(document.createTextNode(argument));
} else if (typeof argument == 'object') {
if (argument instanceof Node) { // If it is a DOM Node
node.appendChild(argument);
} else { // Attributes (hopefully)
for (var j in argument) {
if (j === 'class') { // Classname different because...
node.className = argument[j];
} else if (j == 'style') { // Style is special
node.style.cssText = argument[j];
} else if (typeof argument[j] == 'function') { // Basic event handlers
node.addEventListener(j, argument[j], false);
} else {
node.setAttribute(j, argument[j]); //Normal attributes
}
}
}
}
}
return node;
};
st3lsf3rrqe1qmi0fmtlkl9igirdz3z
မဳဒဳယာဝဳကဳ:Gadget-LanguageUtils.js
8
295749
396578
2026-06-07T20:07:26Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "// {{documentation}} // implicit dependencies : ext.gadget.StorageUtils /* jshint maxerr:1048576, strict:true, undef:true, latedef:true, es5:true, sub:true */ /* global mw, $ */ // <nowiki> // usage: // var lutils = new LanguageUtilsAsync(); // lutils.GetWiktionaryCodeByCanonicalName("Georgian").then(langcode => console.log(langcode)); // lutils.GetCanonicalNameByWiktionaryCode("ka").then(langname => console.log(la..."
396578
javascript
text/javascript
// {{documentation}}
// implicit dependencies : ext.gadget.StorageUtils
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, es5:true, sub:true */
/* global mw, $ */
// <nowiki>
// usage:
// var lutils = new LanguageUtilsAsync();
// lutils.GetWiktionaryCodeByCanonicalName("Georgian").then(langcode => console.log(langcode));
// lutils.GetCanonicalNameByWiktionaryCode("ka").then(langname => console.log(langname));
window.LanguageUtilsAsync = function(){
this.langNameToLangCode = new mw.Api().get({
"action": "expandtemplates",
"format": "json",
"text": "{{#invoke:languages/javascript-interface|AllCanonicalToCode}}",
"prop": "wikitext"
}).then(function (response) {
return JSON.parse(response.expandtemplates.wikitext);
});
this.langCodeToLangName = this.langNameToLangCode.then(function(name2code){
var code2name = {};
for (var name in name2code)
{
code2name[name2code[name]] = name;
}
return code2name;
});
this.GetWiktionaryCodeByCanonicalName = function(canonicalName){
return this.langNameToLangCode.then(function(r){ return r[canonicalName];});
};
this.GetCanonicalNameByWiktionaryCode = function(langcode) {
return this.langCodeToLangName.then(function(r){ return r[langcode];});
};
};
window.LanguageUtils = function(){
this.automaticTranslitLanguages = function(){
return new mw.Api().get({
"action": "expandtemplates",
"format": "json",
"text": "{{#invoke:languages/javascript-interface|GetLanguagesWithAutomaticTransliteration}}",
"prop": "wikitext"
}).then(function (response) {
return JSON.parse(response.expandtemplates.wikitext);
});
};
this.wiktionaryCodeToWikimediaCode = function(){
return new mw.Api().get({
"action": "expandtemplates",
"format": "json",
"text": "{{#invoke:languages/javascript-interface|AllWiktionaryCodeToWikimediaCode}}",
"prop": "wikitext"
}).then(function (response) {
return JSON.parse(response.expandtemplates.wikitext);
});
};
this.automaticTranslitCacheableStorage = new CacheableStorage("LanguageUtils-AutomaticTransliteration", this.automaticTranslitLanguages, 7, 1);
this.wikimediaCodesCacheableStorage = new CacheableStorage("LanguageUtils-WikimediaCodes", this.wiktionaryCodeToWikimediaCode, 7, 1);
this.HasAutomaticTransliteration = function(langcode) {
var langs = this.automaticTranslitCacheableStorage.GetItem();
if (langs){
return langs[langcode] || false;
}
return false;
};
this.GetWikimediaCodeByWiktionaryCode = function(langcode) {
var wikimediaCodes = this.wikimediaCodesCacheableStorage.GetItem();
if (wikimediaCodes && wikimediaCodes[langcode]){
return wikimediaCodes[langcode][0];
}
return null;
};
};
window.ScriptUtils = function(){
this.getAllDataPromise = function(){
return new mw.Api().get({
"action": "expandtemplates",
"format": "json",
"text": "{{#invoke:languages/javascript-interface|AllLangcodeToScripts}}",
"prop": "wikitext"
}).then(function(r){return JSON.parse(r.expandtemplates.wikitext);});
};
this.cacheableStorage = new CacheableStorage("ScriptUtils", this.getAllDataPromise, 7, 1);
this.GetScriptsByLangCode = function(langcode){
var allData = this.cacheableStorage.GetItem();
if (allData){
var scripts = allData[langcode];
return typeof scripts === "string" ? [scripts] : scripts;
}
return [];
};
};
// </nowiki>
70h4e96hm95yovay4vf5zwt3zosb6o8
မဳဒဳယာဝဳကဳ:Gadget-JavascriptHeadings.js
8
295750
396579
2026-06-07T20:09:17Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "//<source lang="javascript"> HotCat.capitalizePageNames = false; //</source>"
396579
javascript
text/javascript
//<source lang="javascript">
HotCat.capitalizePageNames = false;
//</source>
aaafpazmwyq74ivkwgv72tucldzhu8u
မဳဒဳယာဝဳကဳ:Gadget-HideLogo.css
8
295751
396580
2026-06-07T20:10:21Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "#p-logo { display: none;} #column-one { padding-top: 0;} #mw-panel { top:0 !important;}"
396580
css
text/css
#p-logo {
display: none;}
#column-one {
padding-top: 0;}
#mw-panel {
top:0 !important;}
t0f2pppo06r0eh9xpagcpod6hm7bext
မဳဒဳယာဝဳကဳ:Gadget-FastRevert.js
8
295752
396581
2026-06-07T20:12:58Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "/** ************* FastRevert ******************* * Allows one to restore an old version * * * * Author: Quentinv57, for Wikimedia * * -- 06 Feb. 2010 -- * ******************************************** */ $( document ).ready( function () { if (mw.util.getParamValue('action') === 'history') { var chemin = '//mnw.wiktionary.org/w/index.p..."
396581
javascript
text/javascript
/**
************* FastRevert *******************
* Allows one to restore an old version *
* *
* Author: Quentinv57, for Wikimedia *
* -- 06 Feb. 2010 -- *
********************************************
*/
$( document ).ready( function () {
if (mw.util.getParamValue('action') === 'history') {
var chemin = '//mnw.wiktionary.org/w/index.php?action=edit&retablir';
var pageHistory = document.getElementById('pagehistory');
pageHistory = pageHistory && pageHistory.getElementsByTagName('li');
var prevUser;
for (var i = 0; i < pageHistory.length; i++) {
var li = pageHistory[i];
var oldid = li.dataset.mwRevid; // data-mw-revid="..."
var userLink = li.querySelector(".history-user .mw-userlink");
// Get inside of bdi tag if it's present.
while (userLink.firstElementChild) {
userLink = userLink.firstElementChild;
}
var username = userLink.textContent;
if (i !== 0) {
var revertLink = document.createElement( "a" );
revertLink.href = chemin + '&oldid=' + oldid + '&user=' + username + '&user2=' + prevUser;
revertLink.appendChild( document.createTextNode( "FastRevert" ) );
li.appendChild( document.createTextNode( " \u2014 (" ) );
li.appendChild( revertLink );
li.appendChild( document.createTextNode( ")" ) );
}
prevUser = username;
}
} else if (location.href.match(/&retablir&/)) {
var user2 = mw.util.getParamValue('user2');
var message = prompt(
'What message do you want to leave?',
'Reverted edits by [[Special:Contributions/' + user2 + '|' + user2 + ']]'
);
var user = mw.util.getParamValue('user');
document.getElementById('wpSummary').value = (message ? message + '; ' : '')
+ 'Restore to version ' + mw.util.getParamValue('oldid')
+ ' နူ [[Special:Contributions/' + user + '|' + user + ']]';
if (message != null) document.getElementById('editform').submit();
}
});
rr0uf09z5pfcw96d3mi885jwk6h2dt0
မဳဒဳယာဝဳကဳ:Gadget-Editor.js
8
295753
396583
2026-06-07T20:16:47Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "// This page consists of Editor and AdderWrapper // Author: Conrad Irwin /*jshint maxerr:1048576, strict:true, undef:true, latedef:true, es5:true */ /*global mw, jQuery, importScript, importScriptURI, $ */ window.PageEditor = function(title) { this.CheckOutForEdit = function() { return new mw.Api().get({ action: 'query', prop: 'revisions', rvprop: ['ids', 'content', 'timestamp'], titles: String(titl..."
396583
javascript
text/javascript
// This page consists of Editor and AdderWrapper
// Author: Conrad Irwin
/*jshint maxerr:1048576, strict:true, undef:true, latedef:true, es5:true */
/*global mw, jQuery, importScript, importScriptURI, $ */
window.PageEditor = function(title) {
this.CheckOutForEdit = function() {
return new mw.Api().get({
action: 'query',
prop: 'revisions',
rvprop: ['ids', 'content', 'timestamp'],
titles: String(title),
formatversion: '2',
curtimestamp: true
})
.then(function(data) {
var page, revision;
if (!data.query || !data.query.pages) {
return $.Deferred().reject('unknown');
}
page = data.query.pages[0];
if (!page || page.missing) {
return $.Deferred().reject('nocreate-missing');
}
revision = page.revisions[0];
this.baserevid = revision.revid;
this.basetimestamp = revision.timestamp;
this.curtimestamp = data.curtimestamp;
return revision.content;
});
}
this.Save = function(newWikitext, params) {
var editParams = typeof params === 'object' ? params : {
text: String(params)
};
return new mw.Api().postWithEditToken($.extend({
action: 'edit',
title: title,
formatversion: '2',
text: newWikitext,
// Protect against errors and conflicts
assert: mw.user.isAnon() ? undefined : 'user',
baserevid: this.baserevid,
basetimestamp: this.basetimestamp,
starttimestamp: this.curtimestamp,
nocreate: true
}, editParams));
}
}
/**
* A generic page editor for the current page.
*
* This is a singleton and it displays a small interface in the top left after
* the first edit has been registered.
*
* @public
* this.page
* this.addEdit
* this.error
*
*/
window.Editor = function() {
//Singleton
if (arguments.callee.instance)
return arguments.callee.instance;
else
arguments.callee.instance = this;
this.page = new PageEditor(mw.config.get('wgPageName'));
// get the current text of the article and call the callback with it
// NOTE: This function also acts as a loose non-re-entrant lock to protect currentText.
this.withCurrentText = function(callback) {
if (callbacks.length == 0) {
callbacks = [callback];
for (var i = 0; i < callbacks.length; i++) {
callbacks[i](currentText);
}
return callbacks = [];
}
if (callbacks.length > 0) {
return callbacks.push(callback);
}
callbacks = [callback];
thiz.page.CheckOutForEdit().then(function(wikitext) {
if (wikitext === null)
return thiz.error("Could not connect to server");
currentText = originalText = wikitext;
for (var i = 0; i < callbacks.length; i++) {
callbacks[i](currentText);
}
callbacks = [];
});
}
// A decorator for withCurrentText
function performSequentially(f) {
return (function() {
var the_arguments = arguments;
thiz.withCurrentText(function() {
f.apply(thiz, the_arguments);
});
});
}
// add an edit to the editstack
function addEdit(edit, node, fromRedo) {
withPresenceShowing(false, function() {
if (node) {
nodestack.push(node);
node.style.cssText = "border: 2px var(--wikt-palette-green-4, #00FF00) dashed;"
}
if (!fromRedo)
redostack = [];
var ntext = false;
try {
ntext = edit.edit(currentText);
if (ntext && ntext != currentText) {
edit.redo();
currentText = ntext;
} else
return false;
} catch (e) {
// TODO Uncaught TypeError: Object [object Window] has no method 'error'
// I may have just fixed this by changing "this" below to "thiz" ...
thiz.error("ERROR:" + e);
}
editstack.push(edit);
});
}
this.addEdit = performSequentially(addEdit);
// display an error to the user
this.error = function(message) {
console.trace(message);
if (!errorlog) {
errorlog = $('<ul>').css("margin", "0px -10px -10px -10px")
.css("padding", "10px")[0];
// jQuery $.css destroys var(...)
errorlog.style.backgroundColor = "var(--wikt-palette-pink, #FFDDDD)";
withPresenceShowing(true, function(presence) {
presence.appendChild(errorlog);
});
}
errorlog.appendChild($('<li>').text(message)[0]);
}
var thiz = this; // this is set incorrectly when private functions are used as callbacks.
var editstack = []; // A list of the edits that have been applied to get currentText
var redostack = []; // A list of the edits that have been recently undone.
var nodestack = []; // A lst of nodes to which we have added highlighting
var callbacks = {}; // A list of onload callbacks (initially .length == undefined)
var originalText = ""; // What was the contents of the page before we fiddled?
var currentText = ""; // What is the contents now?
var errorlog; // The ul for sticking errors in.
var $savelog; // The ul for save messages.
//Move an edit from the editstack to the redostack
function undo() {
if (editstack.length == 0)
return false;
var edit = editstack.pop();
redostack.push(edit);
edit.undo();
var text = originalText;
for (var i = 0; i < editstack.length; i++) {
var ntext = false;
try {
ntext = editstack[i].edit(text);
} catch (e) {
thiz.error("ERROR:" + e);
}
if (ntext && ntext != text) {
text = ntext;
} else {
editstack[i].undo();
editstack = editstack.splice(0, i);
break;
}
}
currentText = text;
return true;
}
this.undo = performSequentially(undo);
//Move an edit from the redostack to the editstack
function redo() {
if (redostack.length == 0)
return;
var edit = redostack.pop();
addEdit(edit, null, true);
}
this.redo = performSequentially(redo);
function withPresenceShowing(broken, callback) {
if (arguments.callee.presence) {
if (broken || arguments.callee.presence.querySelector("button")) {
arguments.callee.presence.style.display = "block";
return callback(arguments.callee.presence);
} else {
try {
arguments.callee.presence.parentNode.removeChild(arguments.callee.presence);
} catch (e) {
}
}
}
var presence = $('<div>').css("position", "fixed")
.css("top", "0px")
.css("left", "0px")
.css("z-index", "10")
.css("padding", "30px")[0];
// jQuery $.css destroys var(...)
presence.style.backgroundColor = "var(--wikt-palette-green-4, #00FF00)";
window.setTimeout(function() {
presence.style.backgroundColor = "var(--wikt-palette-dulllightblue, #CCCCFF)";
presence.style.padding = "10px";
}, 400);
var closeButton = $('<div>').css("position", "relative")
.css("top", "0px")
.css("left", "0px")
.css("margin", "-10px")
.css("cursor", "pointer")
.on("click", performSequentially(close))
.text("X")[0];
// jQuery $.css destroys var(...)
closeButton.style.color = "var(--wikt-palette-blue, #0000FF)";
presence.appendChild(closeButton);
document.body.insertBefore(presence, document.body.firstChild);
var contents = $('<p>').css('text-align', 'center')
.append($('<b>Page Editing</b></br>'));
if (!broken) {
contents.append($('<button>').text("Save Changes")
.attr('title', 'Save your changes [s]')
.attr('accesskey', 's')
.on("click", save));
contents.append($('<br>'));
contents.append($('<button>').text("Undo")
.attr('title', 'Undo last change [z]')
.attr('accesskey', 'z')
.on("click", thiz.undo));
contents.append($('<button>').text("Redo").on('click', thiz.redo));
mw.loader.using('mediawiki.util').then(function() {
contents.children().updateTooltipAccessKeys();
});
}
presence.appendChild(contents[0]);
arguments.callee.presence = presence;
callback(presence);
}
// Remove the button
function close() {
while (undo())
;
withPresenceShowing(true, function(presence) {
presence.style.display = "none";
if (errorlog) {
errorlog.parentNode.removeChild(errorlog);
errorlog = false;
}
});
}
//Send the currentText back to the server to save.
function save() {
thiz.withCurrentText(function() {
if (editstack.length == 0)
return;
var cleanup_callbacks = callbacks;
callbacks = [];
var sum = {};
var tags = [];
for (var i = 0; i < editstack.length; i++) {
sum[editstack[i].summary] = true;
if (editstack[i].after_save)
cleanup_callbacks.push(editstack[i].after_save);
if (editstack[i].tags)
tags = tags.concat(editstack[i].tags.filter(tag => !!tag));
}
var summary = "";
for (var name in sum) {
summary += name + " ";
}
editstack = [];
redostack = [];
var saveLi = $('<li>Saving:' + summary + '...</li>');
withPresenceShowing(false, function(presence) {
if (!$savelog) {
$savelog = $('<ul>').css("margin", "0px -10px -10px -10px")
.css("padding", "10px");
// jQuery $.css destroys var(...)
$savelog[0].style.backgroundColor = "var(--wikt-palette-palegreen, #DDFFDD)";
$(presence).append($savelog);
}
$savelog.append(saveLi);
if (originalText == currentText)
return thiz.error("No changes were made to the page.");
else if (!currentText)
return thiz.error("ERROR: page has become blank.");
});
originalText = currentText;
var nst = []
var node;
while (node = nodestack.pop()) {
nst.push(node);
}
thiz.page.Save(currentText, {
summary: summary + "([[WT:EDIT|Assisted]])",
notminor: true,
tags: tags.join('|')
}).then(function(res) {
if (res == null)
return thiz.error("An error occurred while saving.");
try {
saveLi.append(
$('<span>')
.append($("<b>Saved</b>"))
.append($('<a>').attr("href", mw.config.get('wgScript') +
'?title=' + encodeURIComponent(mw.config.get('wgPageName')) +
'&diff=' + encodeURIComponent(res.edit.newrevid) +
'&oldid=' + encodeURIComponent(res.edit.oldrevid))
.text("(Show changes)")));
} catch (e) {
if (res.error) {
thiz.error("Not saved: " + String(res.error.info));
} else {
thiz.error($('<p>').text(String(e))[0]);
}
}
for (var i = 0; i < nst.length; i++)
nst[i].style.cssText = "background-color: var(--wikt-palette-green-4,#0F0);border: 2px var(--wikt-palette-green-6,#0F0) solid;";
window.setTimeout(function() {
var node;
while (node = nst.pop())
node.style.cssText = "";
}, 400);
// restore any callbacks that were waiting for currentText before we started
for (var i = 0; i < cleanup_callbacks.length; i++)
thiz.withCurrentText(cleanup_callbacks[i]);
});
});
}
}
/**
* A small amount of common code that can be usefully applied to adder forms.
*
* An adder is assumed to be an object that has:
*
* .fields A object mapping field names to either validation functions used
* for text fields, or the word 'checkbox'
*
* .createForm A function () that returns a newNode('form') to be added to the
* document (by appending to insertNode)
*
* .onsubmit A function (values, register (wikitext, callback)) that accepts
* the validated set of values and processes them, the register
* function accepts wikitext and a continuation function to be
* called with the result of rendering it.
*
* Before onsubmit or any validation functions are called, but after running
* createForm, a new property .elements will be added to the adder which is a
* dictionary mapping field names to HTML input elements.
*
* @param {editor} The current editor.
* @param {adder} The relevant adder.
* @param {insertNode} Where to insert this in the document.
* @param {insertSibling} Where to insert this within insertNode.
*/
window.AdderWrapper = function(editor, adder, insertNode, insertSibling) {
var form = adder.createForm()
var status = $('<span>')[0];
form.appendChild(status);
if (insertSibling)
insertNode.insertBefore(form, insertSibling);
else
insertNode.appendChild(form);
adder.elements = {};
//This is all because IE doesn't reliably allow form.elements['name']
for (var i = 0; i < form.elements.length; i++) {
adder.elements[form.elements[i].name] = form.elements[i];
}
form.onsubmit = function() {
try {
var submit = true;
var values = {}
status.innerHTML = "";
for (var name in adder.fields) {
if (adder.fields[name] == 'checkbox') {
values[name] = adder.elements[name].checked ? name : false;
} else {
adder.elements[name].style.border = ''; // clear error styles
values[name] = adder.fields[name](adder.elements[name].value || '', function(msg) {
var element = $('<span>').append($('<img>').attr('src', 'http://upload.wikimedia.org/wikipedia/commons/4/4e/MW-Icon-AlertMark.png'))
.append(msg)
.append($('<br>'))[0];
// jQuery $.css destroys var(...)
element.style.color = "var(--wikt-palette-red, #ff0000)";
status.appendChild(element);
adder.elements[name].style.border = "solid var(--wikt-palette-red,#CC0000) 2px";
return false
});
if (values[name] === false)
submit = false;
}
}
if (!submit)
return false;
var loading = $('<span>Loading...</span>')[0];
status.appendChild(loading);
adder.onsubmit(values, function(text, callback) {
//text = "<p style='display:inline;'>" + text + "</p>";
//text = "<div id='editorjs-temp'>" + text + "</div>";
new mw.Api().parse(text, {
title: mw.config.get('wgPageName'),
pst: true, //pst makes subst work as expected
disablelimitreport: true
})
.then(function(r) {
var cleanedHtml = $.parseHTML(r)[0].children[0].innerHTML; //first child of .mw-parser-output
callback(cleanedHtml);
status.removeChild(loading);
}).fail(function(r) {
if (r) console.log("ERROR IN Editor.js:" + r);
var errorElement = $('<p>Could not connect to the server</p>')[0];
// jQuery $.css destroys var(...)
errorElement.style.color = "var(--wikt-palette-red, #ff0000)";
loading.appendChild(errorElement);
});
});
} catch (e) {
status.innerHTML = "ERROR:" + e.description;
return false;
}
return false;
}
};
f8q3sq83yhyrx0b1hml7om8zmgsn28a
မဳဒဳယာဝဳကဳ:Gadget-DocTabs.js
8
295754
396584
2026-06-07T20:26:33Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "// Idea: add or remove portlet links as required, using an API query to check what needs to be redlinked beforehand. // The following page types are unsupported: `Template_talk:XYZ/documentation`, `Module_talk:XYZ/documentation`, `Citations_talk:XYZ` // as it is not clear whether these pages should exist at all, and if so, what portlets should be shown on them. // <nowiki> let portletBar = document.querySelector("#p-..."
396584
javascript
text/javascript
// Idea: add or remove portlet links as required, using an API query to check what needs to be redlinked beforehand.
// The following page types are unsupported: `Template_talk:XYZ/documentation`, `Module_talk:XYZ/documentation`, `Citations_talk:XYZ`
// as it is not clear whether these pages should exist at all, and if so, what portlets should be shown on them.
// <nowiki>
let portletBar = document.querySelector("#p-associated-pages, #p-namespaces, #p-cactions");
if (portletBar) {
let actionAPI = new mw.Api({ ajax: { headers: { "Api-User-Agent": "Gadget developed by [[User:Ioaxxere]]" } } });
let ns = mw.config.values.wgNamespaceNumber;
let title = mw.config.values.wgTitle;
let isDocumentationPage = (ns === 10 || ns === 11 || ns === 828 || ns === 829) && title.endsWith("/documentation");
// Remove /documentation suffix if necessary.
let mainTitle = isDocumentationPage ? title.slice(0, -14) : title;
// Page title, display text, ID, tooltip, access key.
let portletData = {
"mainspace": [mainTitle, "Entry", "ca-nstab-main", "View the content page", "c"],
"talk": [`ဓရီုကျာ:${mainTitle}`, "Discussion", "ca-talk", "Discussion about the content page", "t"],
"citations": [`Citations:${mainTitle}`, "Citations", "ca-nstab-citations", "View the citations page", "3"],
"template": [`ထာမ်ပလိက်:${mainTitle}`, "ထာမ်ပလိက်", "ca-nstab-template", "View the template", "c"],
"template_talk": [`ထာမ်ပလိက် ဓရီုကျာ:${mainTitle}`, "Discussion", "ca-talk", "Discussion about the content page", "t"],
"template_documentation": [`ထာမ်ပလိက်:${mainTitle}/documentation`, "Documentation", "ca-nstab-docs", "View the documentation", "3"],
"module": [`မဝ်ဂျူ:${mainTitle}`, "မဝ်ဂျူ", "ca-nstab-module", "View the module page", "c"],
"module_talk": [`မဝ်ဂျူ ဓရီုကျာ:${mainTitle}`, "Discussion", "ca-talk", "Discussion about the content page", "t"],
"module_documentation": [`မဝ်ဂျူ:${mainTitle}/documentation`, "Documentation", "ca-nstab-docs", "View the documentation", "3"]
};
// Given a list of portlet types (see above), return a filtered set of portlets which need to be highlighted as redlinks.
async function checkRedlinkPortlets(...portletList) {
let titleList = portletList.map(portlet => portletData[portlet][0]);
// Check whether the page titles exist.
let response = await actionAPI.get({
action: "query",
titles: titleList.join("|"),
format: "json"
});
// Get page results which have a negative ID (i.e., do not exist).
let redlinks = new Set();
for (let [pageKey, pageData] of Object.entries(response.query.pages)) {
if (pageKey < 0)
redlinks.add(pageData.title);
}
return new Set(portletList.filter(portlet => redlinks.has(portletData[portlet][0])));
}
// Adds a namespace link of the specified type (see above) on the portlet bar.
// It uses the `redlinkPortlets` list generated by checkRedlinkPortlets().
// `location` should be set to either "beginning" or "end".
// There appears to be no way to create a portlet redlink, so this implementation tries to emulate the default behaviour.
// Note: adding two identical portlets is currently unsupported, as it results in duplicate IDs in the DOM.
// More info: https://doc.wikimedia.org/mediawiki-core/REL1_29/js/#!/api/mw.util-method-addPortletLink
function addNamespaceLink(portletType, redlinkPortlets, location, isSelected) {
let data = portletData[portletType];
// Certain skins that have unrelated portlets in the same bar which we should always be before.
// If none of the selectors match an element, the portlet is placed at the very end as desired.
// If `location` is "beginning", the selector always matches the first element.
let before = location === "beginning" ? "li" : "#ca-edit, #ca-view, #ca-watch";
let isRedlink = redlinkPortlets.has(portletType);
let tooltip = data[3];
let href = `/wiki/${mw.util.wikiUrlencode(data[0])}`;
if (isRedlink) {
tooltip += " (page does not exist)";
href = `/w/index.php?title=${mw.util.wikiUrlencode(data[0])}&action=edit&redlink=1`;
}
mw.util.addPortletLink(portletBar.id, href, data[1], data[2], tooltip, data[4], portletBar.querySelector(before));
let portletElem = document.getElementById(data[2]);
if (isSelected) {
portletElem.classList.add("selected");
} else if (isRedlink) {
portletElem.classList.add("new");
portletElem.querySelector("a").classList.add("new");
}
}
(async () => {
if (ns === 0 || ns === 1) { // Mainspace and Talk
// Add a link to the citations page.
let redlinkPortlets = await checkRedlinkPortlets("citations");
addNamespaceLink("citations", redlinkPortlets, "end");
} else if (ns === 114) { // Citations
// Add links to mainspace and talk and remove link to citations talk.
let redlinkPortlets = await checkRedlinkPortlets("mainspace", "talk");
// Remove link to Citations talk.
portletBar.querySelectorAll("li")[1].remove();
// Set access key to "3".
let citationsLink = portletBar.querySelector("li > a");
citationsLink.setAttribute("accesskey", 3);
citationsLink.setAttribute("title", "View the citations page [alt-shift-3]");
addNamespaceLink("talk", redlinkPortlets, "beginning");
addNamespaceLink("mainspace", redlinkPortlets, "beginning");
} else if ((ns === 10 || ns === 11) && !isDocumentationPage) { // Template and Template_talk
// Add a link to the documentation.
let redlinkPortlets = await checkRedlinkPortlets("template_documentation");
addNamespaceLink("template_documentation", redlinkPortlets, "end");
} else if (ns === 10 && isDocumentationPage) { // Template documentation
let redlinkPortlets = await checkRedlinkPortlets("template", "template_talk", "template_documentation");
// Remove existing two portlet links.
portletBar.querySelector("li").remove();
portletBar.querySelector("li").remove();
// Add links to the template, discussion, and current documentation pages.
addNamespaceLink("template", redlinkPortlets, "end");
addNamespaceLink("template_talk", redlinkPortlets, "end");
addNamespaceLink("template_documentation", redlinkPortlets, "end", true);
} else if ((ns === 828 || ns === 829) && !isDocumentationPage) { // Module and Module_talk
// Add a link to the documentation.
let redlinkPortlets = await checkRedlinkPortlets("module_documentation");
addNamespaceLink("module_documentation", redlinkPortlets, "end");
} else if (ns === 828 && isDocumentationPage) { // Module documentation
let redlinkPortlets = await checkRedlinkPortlets("module", "module_talk", "module_documentation");
// Remove existing two portlet links.
portletBar.querySelector("li").remove();
portletBar.querySelector("li").remove();
// Add links to the module, discussion, and current documentation pages.
addNamespaceLink("module", redlinkPortlets, "end");
addNamespaceLink("module_talk", redlinkPortlets, "end");
addNamespaceLink("module_documentation", redlinkPortlets, "end", true);
}
})();
}
// </nowiki>
f3xwo64qf8bxjmw4r8fuaa52te8k7a6
မဳဒဳယာဝဳကဳ:Gadget-DialectMap.js
8
295755
396585
2026-06-07T20:28:12Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "// <nowiki> /* jshint esversion: 8 */ /** * Dialect Map Renderer * * This gadget transforms static GeoJSON data into a fully interactive dialect map * using the Leaflet library (via the Kartographer extension). * * @author [[User:Fenakhay]] */ (function (mw) { "use strict"; const P = { MAP: "dialect-map", FILTER: "dialect-filter", TREE: "dialect-tree", MARKER: "dialect-marker", POPUP: "dialect-pop..."
396585
javascript
text/javascript
// <nowiki>
/* jshint esversion: 8 */
/**
* Dialect Map Renderer
*
* This gadget transforms static GeoJSON data into a fully interactive dialect map
* using the Leaflet library (via the Kartographer extension).
*
* @author [[User:Fenakhay]]
*/
(function (mw) {
"use strict";
const P = {
MAP: "dialect-map",
FILTER: "dialect-filter",
TREE: "dialect-tree",
MARKER: "dialect-marker",
POPUP: "dialect-popup",
UTIL: "dialect-element",
};
function withPrefix(prefix, suffixes) {
const o = {};
for (const [k, v] of Object.entries(suffixes)) {
o[k] = v === "" ? prefix : prefix + "-" + v;
}
return o;
}
const CLASSES = {
MAP: Object.assign(withPrefix(P.MAP, {
PLACEHOLDER: "placeholder",
PLACEHOLDER_MISSING_CONFIG: "placeholder--missing-config",
DATA: "data",
APP: "app",
PANEL: "panel",
HEADER_CONTAINER: "header-container",
PANEL_HEADER: "panel-header",
MAP_LINK: "link",
SCROLL_CONTAINER: "scroll-container",
FOOTER_CONTAINER: "footer-container",
LEGEND_STATIC: "legend-static",
LEGEND_GRID: "legend-grid",
LEGEND_GROUP: "legend-group",
LEGEND_ROW: "legend-row",
LEGEND_ROW_HIGHLIGHTED: "legend-row--highlighted",
LEGEND_ROW_NESTED: "legend-row--nested",
GROUP_HEADER: "group-header",
TOGGLE_ICON: "toggle-icon",
LEGEND_ROW_DOT: "legend-row-dot",
LEGEND_LABEL: "legend-label",
LEGEND_TERM_ROW: "legend-term-row",
LEGEND_TERMS: "legend-terms",
LEGEND_TERMS_COLLAPSED: "legend-terms--collapsed",
LEGEND_LOCS_TOGGLE: "legend-locs-toggle",
LEGEND_LOCS: "legend-locs",
LEGEND_LOCS_COLLAPSED: "legend-locs--collapsed",
LEGEND_LOCS_TOPLEVEL: "legend-locs--toplevel",
LEGEND_LOCS_NESTED: "legend-locs--nested",
LEGEND_LOCS_LIST: "legend-locs-list",
LEGEND_LOCS_ITEM: "legend-locs-item",
CONTENT_WRAPPER: "content-wrapper",
CUSTOM_POPUP: "custom-popup",
SEARCH_INPUT: "search-input",
EXPORT_BTN: "export-btn",
PROCESSED: "processed",
}), {
CONTROL_BTN: "dialect-map-control-btn",
CONTROL_BTN_FULLSCREEN: "dialect-map-control-btn--fullscreen",
DEPTH_CONTROL: "dialect-depth-control",
DEPTH_CONTROL_HIDDEN: "dialect-depth-control--hidden",
}),
FILTER: withPrefix(P.FILTER, {
CHECKBOX: "checkbox",
CHECKBOX_VISUAL: "checkbox-visual",
EXPANDER: "expander",
CONTROL: "control",
BTN: "btn",
}),
TREE: withPrefix(P.TREE, { ROW: "row", LABEL: "label" }),
MARKER: withPrefix(P.MARKER, {
ROOT: "",
SHAPE: "shape",
POPUP: "popup",
FOCUSED: "focused",
HIGHLIGHTED: "highlighted",
DIMMED: "dimmed",
}),
POPUP: withPrefix(P.POPUP, { TITLE: "title", COLOR_DOT: "color-dot" }),
UTIL: withPrefix(P.UTIL, { ELEMENT_HIDDEN: "hidden" }),
EXT: {
VSSWITCHER: "vsSwitcher",
VSTOGGLE_ELEMENT: "vsToggleElement",
VSHIDE: "vsHide",
},
};
function selectorsFrom(classes) {
const sel = {};
for (const [comp, obj] of Object.entries(classes)) {
if (typeof obj === "object" && obj !== null) {
sel[comp] = {};
for (const [k, v] of Object.entries(obj)) {
if (typeof v === "string") sel[comp][k] = "." + v;
}
}
}
return sel;
}
const SELECTORS = selectorsFrom(CLASSES);
SELECTORS.FILTER.CHECKBOX_INPUT = SELECTORS.FILTER.CHECKBOX + " input";
SELECTORS.ICON_SHAPES = "path, circle, rect, polygon";
SELECTORS.ICON_SHAPES_PIE = "path, circle";
const UI_STRINGS = {
FILTER_HEADER: "Filter",
FILTER_GROUPS_TITLE: "Filter Groups",
ALL: "All",
NONE: "None",
LECTS_EXPAND: "[lects ▼]",
LECTS_COLLAPSE: "[lects ▲]",
LEVEL_PREFIX: "Level ",
SEARCH_PLACEHOLDER: "Search location or term...",
EXPORT_BTN: "Download Data (GeoJSON)",
TOGGLE_FULLSCREEN: "Toggle Fullscreen",
EXIT_FULLSCREEN: "Exit Fullscreen",
MARKER_MODE_PIE: "P",
MARKER_MODE_CLUSTER: "S",
SWITCH_TO_PIE: "Switch to pie chart",
SWITCH_TO_CLUSTER: "Switch to separate markers",
ICON_EXPAND_RIGHT: "►",
ICON_EXPAND_DOWN: "▼",
};
const CONFIG = {
CACHE_EXPIRY_MS: 30 * 24 * 60 * 60 * 1000,
WIKITEXT_BATCH_SIZE: 100,
MARKER_SIZE: 12,
SORT_BY_SIMILARITY_MAX: 100,
SORT_BY_SIMILARITY_MAX_CANDIDATES: 50,
HIGHLIGHT_Z_INDEX: 1000,
MAP_MIN_ZOOM: 2,
MAP_MAX_ZOOM: 19,
FIT_BOUNDS_PADDING: 50,
FIT_BOUNDS_MAX_ZOOM: 10,
FLY_TO_MIN_ZOOM: 8,
POPUP_MIN_WIDTH: 200,
DEFAULT_MARKER_COLOR: "#333",
DEFAULT_POLYGON_COLOR: "#3388ff",
DEFAULT_MARKER_COLOR_HEX: "000000",
MARKER_STROKE_WIDTH: 1.5,
CLUSTER_SPREAD_RADIUS_DEG: 0.01,
CLUSTER_SPREAD_REF_ZOOM: 10,
CLUSTER_SPREAD_ZOOM_CLAMP: [4, 16],
MULTI_MARKER_MODES: ["pie", "cluster"],
MULTI_MARKER_DEFAULT: "pie",
CLASSES,
SELECTORS,
};
const PROPS = {
MARKER_GROUP: "marker-group",
GROUP_ID: "group-id",
TERM_GROUP: "term-group",
MARKER_LANG: "marker-lang",
MARKER_COLOR: "marker-color",
STROKE_WIDTH: "stroke-width",
STROKE_OPACITY: "stroke-opacity",
FILL_OPACITY: "fill-opacity",
PARSED_WIKITEXT: "parsedWikitext",
PARSED_LEGEND_WIKITEXT: "parsedLegendWikitext",
PARSED_LOCATION: "parsedLocation",
PARSED_GROUP: "parsedGroup",
};
const DATA_ATTR = {
PATH: "data-path",
GROUP_ID: "data-group-id",
MARKER_GROUP: "data-marker-group",
LAT: "data-lat",
LON: "data-lon",
ZOOM: "data-zoom",
MODE: "data-mode",
MAPSTYLE: "data-mapstyle",
SHOW_GROUP: "data-show-group",
MULTI_MARKER: "data-multi-marker",
IDX: "data-idx",
MISSING_CONFIG: "data-missing-config",
};
function isRTL(container) {
return container && container.getAttribute("dir") === "rtl";
}
function leafletControlPosition(container, atInlineStart) {
return isRTL(container)
? (atInlineStart ? "topright" : "topleft")
: (atInlineStart ? "topleft" : "topright");
}
function applyPageDirection(container) {
const pageDir = document.documentElement.getAttribute("dir") || "ltr";
if (pageDir === "rtl") container.setAttribute("dir", "rtl");
}
function createMapControl(_container, position, options) {
const Control = L.Control.extend({
options: { position },
onAdd: function (map) {
const wrapClass = "leaflet-bar leaflet-control" + (options.extraWrapClass ? " " + options.extraWrapClass : "");
const wrap = L.DomUtil.create("div", wrapClass);
L.DomEvent.disableClickPropagation(wrap);
if (options.appendToWrap) {
L.DomEvent.disableScrollPropagation(wrap);
}
const isHtml = typeof options.content === "string" && options.content.trim().startsWith("<");
const extraClass = (options.extraBtnClass || "") + (!isHtml && options.content ? " dialect-map-control-btn--text" : "");
const btn = L.DomUtil.create("a", CONFIG.CLASSES.MAP.CONTROL_BTN + (extraClass ? " " + extraClass.trim() : ""), wrap);
btn.href = "#";
btn.title = options.title || "";
if (isHtml) {
btn.innerHTML = options.content;
} else {
btn.innerText = options.content || "";
}
if (!options.appendToWrap) {
btn.onclick = function (e) {
e.preventDefault();
if (options.onClick) options.onClick(e);
};
}
if (options.appendToWrap) options.appendToWrap(wrap, btn);
if (options.onAfterAdd) options.onAfterAdd(btn);
return wrap;
},
onRemove: function () {
if (options.onRemove) options.onRemove();
},
});
return new Control();
}
function getClusterSpreadRadiusDeg(zoom) {
const [minZ, maxZ] = CONFIG.CLUSTER_SPREAD_ZOOM_CLAMP;
const z = Math.max(minZ, Math.min(maxZ, zoom));
return CONFIG.CLUSTER_SPREAD_RADIUS_DEG * Math.pow(2, CONFIG.CLUSTER_SPREAD_REF_ZOOM - z);
}
const SVG_ICONS = {
CHECKBOX_CHECKED: '<svg width="10" height="10" viewBox="0 0 10 10"><path d="M1 5 L4 8 L9 2" fill="none" stroke="var(--wikt-palette-white)" stroke-width="1.5"/></svg>',
EXPANDER_DOWN: '<svg width="10" height="10" viewBox="0 0 10 10"><path d="M1 3 L5 7 L9 3" fill="none" stroke="var(--wikt-palette-black)" stroke-width="1.5"/></svg>',
EXPANDER_RIGHT: '<svg width="10" height="10" viewBox="0 0 10 10"><path d="M3 1 L7 5 L3 9" fill="none" stroke="var(--wikt-palette-black)" stroke-width="1.5"/></svg>',
FILTER: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"></polygon></svg>',
FULLSCREEN_EXPAND: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"></path></svg>',
FULLSCREEN_COLLAPSE: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3"></path></svg>',
};
function markerSvgOpen(size) {
return `<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}">`;
}
function markerPathEl(d, color, dataAttr) {
return '<path class="' + CONFIG.CLASSES.MARKER.SHAPE + '" data-is-shape="true" data-original-d="' +
escapeHtml(d) + '" d="' + escapeHtml(d) + '" fill="' + escapeHtml(color) + '" fill-opacity="1"' + dataAttr + " />";
}
function markerPieSliceEl(d, color, dataAttr) {
return '<path class="' + CONFIG.CLASSES.MARKER.SHAPE + '" data-original-d="' +
escapeHtml(d) + '" d="' + escapeHtml(d) + '" fill="' + escapeHtml(color) + '" fill-opacity="1"' + dataAttr + " />";
}
function markerCircleEl(center, r, color, dataAttr) {
return '<circle class="' + CONFIG.CLASSES.MARKER.SHAPE + '" data-is-shape="true" cx="' + center +
'" cy="' + center + '" r="' + r + '" fill="' + escapeHtml(color) + '" fill-opacity="1"' + dataAttr + " />";
}
function shapeSquare(c, r) {
const s = r * 1.6;
return ["M", c - s * 0.5, c - s * 0.5, "h", s, "v", s, "h", -s, "z"].join(" ");
}
function shapeTriangleUp(c, r) {
return ["M", c, c - r * 1.3, "L", c + r * 1.2, c + r * 0.7, "L", c - r * 1.2, c + r * 0.7, "z"].join(" ");
}
function shapeDiamond(c, r) {
return ["M", c, c - r * 1.3, "L", c + r * 1.3, c, "L", c, c + r * 1.3, "L", c - r * 1.3, c, "z"].join(" ");
}
function shapeTriangleDown(c, r) {
return ["M", c, c + r * 1.3, "L", c + r * 1.2, c - r * 0.7, "L", c - r * 1.2, c - r * 0.7, "z"].join(" ");
}
function shapePentagon(c, r) {
return ["M", c, c - r * 1.1, "L", c + r, c - r * 0.35, "L", c + r * 0.65, c + r * 0.9, "L", c - r * 0.65, c + r * 0.9, "L", c - r, c - r * 0.35, "z"].join(" ");
}
function shapeStar(c, r) {
const outer = r * 1.3;
const inner = r * 0.5;
return [
"M", c, c - outer,
"L", c + inner * 0.6, c - inner * 0.6,
"L", c + outer, c - outer * 0.3,
"L", c + inner, c + inner * 0.3,
"L", c + outer * 0.6, c + outer,
"L", c, c + inner,
"L", c - outer * 0.6, c + outer,
"L", c - inner, c + inner * 0.3,
"L", c - outer, c - outer * 0.3,
"L", c - inner * 0.6, c - inner * 0.6,
"z",
].join(" ");
}
function shapePlus(c, r) {
const t = r * 0.4;
const l = r * 1.2;
return [
"M", c - t, c - l,
"h", t * 2, "v", l - t, "h", l - t, "v", t * 2,
"h", -(l - t), "v", l - t, "h", -t * 2, "v", -(l - t), "h", -(l - t), "v", -t * 2,
"h", l - t,
"z",
].join(" ");
}
const MARKER_SHAPE_PATHS = [
shapeSquare,
shapeTriangleUp,
shapeDiamond,
shapeTriangleDown,
shapePentagon,
shapeStar,
shapePlus,
];
function markerShapePathD(shapeIndex, center, fr) {
return MARKER_SHAPE_PATHS[shapeIndex](center, fr);
}
function escapeHtml(text) {
if (!text) return "";
return String(text)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
function setElementHTML(el, html) {
el.innerHTML = html;
}
function sanitizeUrl(url) {
if (!url) return "";
if (/^(https?:|\/\/|\/)/i.test(url)) {
return url.replace(/"/g, "%22");
}
return "#";
}
async function hashString(str) {
const encoder = new TextEncoder();
const data = encoder.encode(str);
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, "0")).join("");
}
class WikitextCache {
constructor() {
this.dbName = "DialectMapCache";
this.storeName = "parsedContent";
this.db = null;
this.CACHE_EXPIRY_MS = CONFIG.CACHE_EXPIRY_MS;
}
async init() {
if (this.db) return;
if (typeof indexedDB === "undefined") return;
try {
return await new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, 1);
request.onerror = () => reject(request.error);
request.onsuccess = () => {
this.db = request.result;
resolve();
};
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains(this.storeName)) {
db.createObjectStore(this.storeName, { keyPath: "hash" });
}
};
});
} catch (e) {
this.db = null;
}
}
async get(hash) {
if (!this.db) return null;
return new Promise((resolve) => {
const tx = this.db.transaction(this.storeName, "readwrite");
const store = tx.objectStore(this.storeName);
const request = store.get(hash);
request.onsuccess = () => {
const result = request.result;
if (!result || !result.html) {
resolve(null);
return;
}
const now = Date.now();
const age = now - (result.timestamp || 0);
if (age > this.CACHE_EXPIRY_MS) {
try { store.delete(hash); } catch (e) { }
resolve(null);
} else {
resolve(result.html);
}
};
request.onerror = () => resolve(null);
});
}
async set(hash, html) {
if (!this.db) return;
return new Promise((resolve) => {
const tx = this.db.transaction(this.storeName, "readwrite");
const store = tx.objectStore(this.storeName);
store.put({ hash, html, timestamp: Date.now() });
tx.oncomplete = () => resolve();
tx.onerror = () => resolve();
});
}
}
const wikitextCache = new WikitextCache();
class ColorUtils {
static resolveColor(rawColor) {
rawColor = rawColor || "333";
if (rawColor.indexOf("var(") === 0) {
return rawColor.replace(/-\d+\)$/, ")");
}
return "#" + rawColor.replace("#", "");
}
static damerauLevenshtein(str1, str2) {
const len1 = str1.length;
const len2 = str2.length;
if (len1 === 0) return len2;
if (len2 === 0) return len1;
if (str1 === str2) return 0;
const lenDiff = Math.abs(len1 - len2);
const maxLen = Math.max(len1, len2);
if (lenDiff > maxLen * 0.7) return maxLen;
const matrix = new Array(len1 + 1);
for (let i = 0; i <= len1; i++) {
matrix[i] = new Array(len2 + 1);
matrix[i][0] = i;
}
for (let j = 0; j <= len2; j++) {
matrix[0][j] = j;
}
for (let i = 1; i <= len1; i++) {
const ci = str1[i - 1];
for (let j = 1; j <= len2; j++) {
const cj = str2[j - 1];
const cost = (ci === cj) ? 0 : 1;
matrix[i][j] = Math.min(
matrix[i - 1][j] + 1,
matrix[i][j - 1] + 1,
matrix[i - 1][j - 1] + cost
);
if (i > 1 && j > 1 && ci === str2[j - 2] && str1[i - 2] === cj) {
matrix[i][j] = Math.min(
matrix[i][j],
matrix[i - 2][j - 2] + 1
);
}
}
}
return matrix[len1][len2];
}
static sortBySimilarity(list) {
if (list.length <= 1) return list;
if (list.length > CONFIG.SORT_BY_SIMILARITY_MAX) return list.sort();
const getComparisonText = (key) => {
const trMatch = key.match(/::tr:(.+)$/);
if (trMatch) return trMatch[1];
return key.replace(/::.*$/, "");
};
const comparisonTexts = new Map();
list.forEach(key => {
if (!comparisonTexts.has(key)) {
comparisonTexts.set(key, getComparisonText(key));
}
});
const distanceCache = new Map();
const getDistance = (text1, text2) => {
if (text1 === text2) return 0;
const key1 = text1 < text2 ? text1 : text2;
const key2 = text1 < text2 ? text2 : text1;
const cacheKey = `${key1}|${key2}`;
if (!distanceCache.has(cacheKey)) {
const lenDiff = Math.abs(text1.length - text2.length);
if (lenDiff > Math.max(text1.length, text2.length) * 0.5) {
distanceCache.set(cacheKey, lenDiff);
} else {
distanceCache.set(cacheKey, ColorUtils.damerauLevenshtein(text1, text2));
}
}
return distanceCache.get(cacheKey);
};
const sorted = [...list].sort();
const result = [sorted.shift()];
const maxCandidates = Math.min(CONFIG.SORT_BY_SIMILARITY_MAX_CANDIDATES, sorted.length);
while (sorted.length > 0) {
const currentKey = result[result.length - 1];
const currentText = comparisonTexts.get(currentKey);
let bestIdx = 0;
let bestDist = getDistance(currentText, comparisonTexts.get(sorted[0]));
if (bestDist === 0) {
result.push(sorted.splice(0, 1)[0]);
continue;
}
const checkCount = Math.min(maxCandidates, sorted.length);
for (let i = 1; i < checkCount; i++) {
const candText = comparisonTexts.get(sorted[i]);
const dist = getDistance(currentText, candText);
if (dist === 0) {
bestIdx = i;
bestDist = 0;
break;
}
if (dist < bestDist) {
bestDist = dist;
bestIdx = i;
} else if (dist === bestDist) {
if (sorted[i] < sorted[bestIdx]) {
bestIdx = i;
}
}
}
result.push(sorted.splice(bestIdx, 1)[0]);
}
return result;
}
static hslToHex(h, s, l) {
let r, g, b;
if (s === 0) {
r = g = b = l;
} else {
const hue2rgb = (p, q, t) => {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1/6) return p + (q - p) * 6 * t;
if (t < 1/2) return q;
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
};
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return ((Math.floor(r * 255) << 16) | (Math.floor(g * 255) << 8) | Math.floor(b * 255)).toString(16).padStart(6, "0");
}
}
class FeatureManager {
constructor() {
this.groupVisibility = {};
this.geoJSON = null;
this.maxGroupDepth = 0;
}
isVisible(feature) {
const group = feature.properties && feature.properties[PROPS.MARKER_GROUP];
if (!group) return true;
return this.groupVisibility[group] !== false;
}
toggleGroup(path, visible) {
this.groupVisibility[path] = visible;
}
getFeatures() {
if (!this.geoJSON) return [];
return Array.isArray(this.geoJSON)
? this.geoJSON
: (this.geoJSON.features || []);
}
}
class FilterCheckbox {
constructor(parent, path, checked, onChange) {
this.path = path;
this.checked = checked;
this.onChange = onChange;
this.wrapper = L.DomUtil.create(
"label",
CONFIG.CLASSES.FILTER.CHECKBOX,
parent,
);
this.input = L.DomUtil.create("input", "", this.wrapper);
this.input.type = "checkbox";
this.input.checked = checked;
this.input.setAttribute(DATA_ATTR.PATH, path);
this.input._filterCheckbox = this;
this.visual = L.DomUtil.create(
"span",
CONFIG.CLASSES.FILTER.CHECKBOX_VISUAL,
this.wrapper,
);
this._updateVisual();
const self = this;
this.input.onchange = function () {
self.checked = this.checked;
self._updateVisual();
if (self.onChange) {
self.onChange(self.checked);
}
};
}
setChecked(checked) {
this.checked = checked;
this.input.checked = checked;
this._updateVisual();
}
_updateVisual() {
if (this.checked) {
L.DomUtil.addClass(this.visual, "checked");
} else {
L.DomUtil.removeClass(this.visual, "checked");
}
this.visual.innerHTML = this.checked
? SVG_ICONS.CHECKBOX_CHECKED
: "";
}
}
class FilterExpander {
constructor(parent, hasChildren, onToggle) {
this.expanded = false;
this.hasChildren = hasChildren;
this.onToggle = onToggle;
this.element = L.DomUtil.create(
"span",
CONFIG.CLASSES.FILTER.EXPANDER,
parent,
);
if (hasChildren) {
L.DomUtil.addClass(this.element, "has-children");
}
this._updateVisual();
if (hasChildren) {
const self = this;
this.element.onclick = (e) => {
e.stopPropagation();
e.preventDefault();
self.toggle();
};
}
}
toggle() {
if (!this.hasChildren) return;
this.expanded = !this.expanded;
this._updateVisual();
if (this.onToggle) {
this.onToggle(this.expanded);
}
}
_updateVisual() {
if (!this.hasChildren) return;
if (this.expanded) {
this.element.innerHTML = SVG_ICONS.EXPANDER_DOWN;
} else {
this.element.innerHTML = SVG_ICONS.EXPANDER_RIGHT;
}
}
}
class FilterController {
constructor(container, featureManager, onUpdate) {
this.container = container;
this.featureManager = featureManager;
this.onUpdate = onUpdate;
this.childPathsCache = {};
this.descendantPathsCache = {};
this.groupLayers = {};
}
initUI(groupLayers) {
this.groupLayers = groupLayers;
this._buildCache();
this._render();
}
_buildCache() {
this.childPathsCache = {};
this.descendantPathsCache = {};
let allPaths = new Set();
Object.keys(this.groupLayers).forEach((path) => {
allPaths.add(path);
const parts = path.split("/");
for (let i = 1; i < parts.length; i++) {
allPaths.add(parts.slice(0, i).join("/"));
}
});
allPaths = Array.from(allPaths);
allPaths.forEach((path) => {
const parentPath = this._getParentPath(path);
if (parentPath) {
if (!this.childPathsCache[parentPath]) {
this.childPathsCache[parentPath] = [];
}
this.childPathsCache[parentPath].push(path);
}
});
allPaths.sort();
const pathCount = allPaths.length;
const self = this;
allPaths.forEach((path) => {
const prefix = path + "/";
const prefixEnd = path + "0";
var lower = 0, upper = pathCount;
while (lower < upper) {
var middle = (lower + upper) >> 1;
if (allPaths[middle] < prefix) lower = middle + 1;
else upper = middle;
}
var rangeStart = lower;
lower = rangeStart;
upper = pathCount;
while (lower < upper) {
middle = (lower + upper) >> 1;
if (allPaths[middle] < prefixEnd) lower = middle + 1;
else upper = middle;
}
self.descendantPathsCache[path] = allPaths.slice(rangeStart, lower);
});
}
_getParentPath(path) {
const lastSlash = path.lastIndexOf("/");
if (lastSlash === -1) return null;
return path.substring(0, lastSlash);
}
_render() {
const self = this;
this.control = createMapControl(
this.container,
leafletControlPosition(this.container, true),
{
content: SVG_ICONS.FILTER,
title: UI_STRINGS.FILTER_GROUPS_TITLE,
extraWrapClass: CONFIG.CLASSES.FILTER.CONTROL,
extraBtnClass: CONFIG.CLASSES.FILTER.BTN,
appendToWrap: function (wrap, btn) {
const panel = L.DomUtil.create("div", CONFIG.CLASSES.MAP.PANEL, wrap);
L.DomEvent.disableClickPropagation(panel);
L.DomUtil.addClass(panel, CONFIG.CLASSES.UTIL.ELEMENT_HIDDEN);
const headerContainer = L.DomUtil.create(
"div",
CONFIG.CLASSES.MAP.HEADER_CONTAINER,
panel,
);
const header = L.DomUtil.create(
"div",
CONFIG.CLASSES.MAP.PANEL_HEADER,
headerContainer,
);
header.innerHTML = "<strong>" + UI_STRINGS.FILTER_HEADER + "</strong>";
const actions = L.DomUtil.create("div", "", header);
const allBtn = L.DomUtil.create("span", CONFIG.CLASSES.MAP.MAP_LINK, actions);
allBtn.innerText = UI_STRINGS.ALL;
allBtn.onclick = () => self._setAllVisibility(true, panel);
const noneBtn = L.DomUtil.create("span", CONFIG.CLASSES.MAP.MAP_LINK, actions);
noneBtn.innerText = UI_STRINGS.NONE;
noneBtn.onclick = () => self._setAllVisibility(false, panel);
const scrollContainer = L.DomUtil.create(
"div",
CONFIG.CLASSES.MAP.SCROLL_CONTAINER,
panel,
);
const treeContainer = L.DomUtil.create("div", "", scrollContainer);
const tree = self._buildGroupTree(Object.keys(self.groupLayers));
self._renderTree(tree, treeContainer);
const footerContainer = L.DomUtil.create(
"div",
CONFIG.CLASSES.MAP.FOOTER_CONTAINER,
panel,
);
self.footerContainer = footerContainer;
self.headerContainer = headerContainer;
btn.onclick = function (e) {
e.preventDefault();
const hiddenClass = CONFIG.CLASSES.UTIL.ELEMENT_HIDDEN;
if (L.DomUtil.hasClass(panel, hiddenClass)) {
L.DomUtil.removeClass(panel, hiddenClass);
} else {
L.DomUtil.addClass(panel, hiddenClass);
}
};
self.panel = panel;
self.mainBtn = btn;
self._documentClickHandler = function (e) {
if (!wrap.contains(e.target)) {
if (!L.DomUtil.hasClass(panel, CONFIG.CLASSES.UTIL.ELEMENT_HIDDEN)) {
L.DomUtil.addClass(panel, CONFIG.CLASSES.UTIL.ELEMENT_HIDDEN);
}
}
};
document.addEventListener("click", self._documentClickHandler);
},
onRemove: function () {
if (self._documentClickHandler) {
document.removeEventListener("click", self._documentClickHandler);
self._documentClickHandler = null;
}
},
}
);
}
addTo(map) {
this.control.addTo(map);
}
_buildGroupTree(paths) {
const tree = {};
paths.sort().forEach((path) => {
const parts = path.split("/");
let current = tree;
parts.forEach((part, i) => {
if (!current[part]) {
current[part] = {
_children: {},
_path: parts.slice(0, i + 1).join("/"),
};
}
current = current[part]._children;
});
});
return tree;
}
_renderTree(tree, container, depth, parentLabel) {
depth = depth || 0;
parentLabel = parentLabel || "";
const self = this;
const keys = Object.keys(tree).sort();
if (keys.length === 1) {
const key = keys[0];
const node = tree[key];
const childKeys = Object.keys(node._children);
if (childKeys.length > 0) {
self._renderTree(node._children, container, depth, "");
return;
}
}
keys.forEach((key) => {
const node = tree[key];
const displayLabel = parentLabel ? parentLabel + "/" + key : key;
const row = L.DomUtil.create("div", CONFIG.CLASSES.TREE.ROW, container);
row.style.setProperty("--tree-depth", String(depth));
const hasChildren = Object.keys(node._children).length > 0;
const childContainer = L.DomUtil.create(
"div",
CONFIG.CLASSES.UTIL.ELEMENT_HIDDEN,
container,
);
const hiddenClass = CONFIG.CLASSES.UTIL.ELEMENT_HIDDEN;
const expander = new FilterExpander(row, hasChildren, (expanded) => {
if (expanded) {
L.DomUtil.removeClass(childContainer, hiddenClass);
} else {
L.DomUtil.addClass(childContainer, hiddenClass);
}
});
row.insertBefore(expander.element, row.firstChild);
new FilterCheckbox(
row,
node._path,
self.featureManager.groupVisibility[node._path] !== false,
(checked) => {
self._toggleGroup(node._path, checked);
self._syncDescendantCheckboxUI(node._path, checked);
if (self.onUpdate) self.onUpdate();
},
);
const label = L.DomUtil.create("span", CONFIG.CLASSES.TREE.LABEL, row);
label.innerText = displayLabel;
if (hasChildren) L.DomUtil.addClass(label, "bold");
if (hasChildren) {
self._renderTree(node._children, childContainer, depth + 1, "");
}
});
}
_toggleGroup(groupPath, targetVisible) {
this.featureManager.toggleGroup(groupPath, targetVisible);
const descendants = this.descendantPathsCache[groupPath] || [];
descendants.forEach((path) => {
this.featureManager.toggleGroup(path, targetVisible);
});
this._updateAncestorStates(groupPath);
}
_updateAncestorStates(startPath) {
let parentPath = this._getParentPath(startPath);
while (parentPath) {
this._syncParentWithChildren(parentPath);
parentPath = this._getParentPath(parentPath);
}
}
_syncParentWithChildren(parentPath) {
const childPaths = this.childPathsCache[parentPath] || [];
if (childPaths.length === 0) return;
const anyChecked = childPaths.some(
(p) => this.featureManager.groupVisibility[p] !== false,
);
if (this.featureManager.groupVisibility[parentPath] !== anyChecked) {
this.featureManager.toggleGroup(parentPath, anyChecked);
this._syncParentCheckboxUI(parentPath, anyChecked);
}
}
_syncParentCheckboxUI(path, checked) {
const input = document.querySelector(
CONFIG.SELECTORS.FILTER.CHECKBOX + ' input[' + DATA_ATTR.PATH + '="' + CSS.escape(path) + '"]',
);
if (input && input._filterCheckbox) {
input._filterCheckbox.setChecked(checked);
}
}
_syncDescendantCheckboxUI(groupPath, checked) {
const descendants = this.descendantPathsCache[groupPath] || [];
descendants.forEach((path) => {
const input = document.querySelector(
'input[' + DATA_ATTR.PATH + '="' + CSS.escape(path) + '"]',
);
if (input && input._filterCheckbox) {
input._filterCheckbox.setChecked(checked);
}
});
}
_setAllVisibility(visible, panel) {
const inputs = panel.querySelectorAll(CONFIG.SELECTORS.FILTER.CHECKBOX_INPUT);
inputs.forEach((input) => {
const path = input.getAttribute(DATA_ATTR.PATH);
this.featureManager.toggleGroup(path, visible);
if (input._filterCheckbox) {
input._filterCheckbox.setChecked(visible);
}
});
if (this.onUpdate) this.onUpdate();
}
}
class LegendController {
constructor(container, onHover, mode, onRowClick) {
this.container = container;
this.onHover = onHover;
this.onRowClick = onRowClick || null;
this.mode = mode || "term";
this.legendElement = null;
}
render(groups) {
let legend = this.container.querySelector(CONFIG.SELECTORS.MAP.LEGEND_STATIC);
if (!legend) {
legend = document.createElement("div");
legend.className = CONFIG.CLASSES.MAP.LEGEND_STATIC + " " + CONFIG.CLASSES.MAP.LEGEND_GRID;
this.container.appendChild(legend);
} else {
legend.innerHTML = "";
}
this.legendElement = legend;
groups.forEach((g) => {
const groupContainer = document.createElement("div");
groupContainer.className = CONFIG.CLASSES.MAP.LEGEND_GROUP;
const termKeys = Object.keys(g.terms);
termKeys.sort((a, b) => {
const tA = g.terms[a];
const tB = g.terms[b];
if (tA.locs.length !== tB.locs.length)
return tB.locs.length - tA.locs.length;
return (tA.term || "").localeCompare(tB.term || "");
});
if (termKeys.length === 1 && this.mode !== "all") {
const termKey = termKeys[0];
const termData = g.terms[termKey];
this._renderLegendRow(groupContainer, g, termData, true);
} else {
const groupHeader = document.createElement("div");
groupHeader.className = CONFIG.CLASSES.MAP.LEGEND_ROW + " " + CONFIG.CLASSES.MAP.GROUP_HEADER;
groupHeader.setAttribute(DATA_ATTR.GROUP_ID, g.gid);
const switcher = document.createElement("div");
switcher.className = CONFIG.CLASSES.EXT.VSSWITCHER;
const toggleIcon = document.createElement("span");
toggleIcon.className = CONFIG.CLASSES.MAP.TOGGLE_ICON;
toggleIcon.innerText = UI_STRINGS.ICON_EXPAND_RIGHT;
const dot = document.createElement("span");
dot.className = CONFIG.CLASSES.MAP.LEGEND_ROW_DOT;
dot.style.backgroundColor = g.color;
const labelSpan = document.createElement("span");
labelSpan.className = CONFIG.CLASSES.MAP.LEGEND_LABEL;
if (g.parsedGroup) {
setElementHTML(labelSpan, g.parsedGroup);
} else {
labelSpan.innerText = g.label;
}
switcher.appendChild(toggleIcon);
switcher.appendChild(dot);
switcher.appendChild(labelSpan);
groupHeader.appendChild(switcher);
const termsContainer = document.createElement("div");
termsContainer.className = CONFIG.CLASSES.MAP.LEGEND_TERMS + " " + CONFIG.CLASSES.MAP.LEGEND_TERMS_COLLAPSED;
groupHeader.onclick = () => {
termsContainer.classList.toggle(CONFIG.CLASSES.MAP.LEGEND_TERMS_COLLAPSED);
toggleIcon.innerText = termsContainer.classList.contains(CONFIG.CLASSES.MAP.LEGEND_TERMS_COLLAPSED) ? UI_STRINGS.ICON_EXPAND_RIGHT : UI_STRINGS.ICON_EXPAND_DOWN;
if (this.onRowClick) this.onRowClick(g.gid);
};
if (this.onHover) {
groupHeader.onmouseenter = () => this.onHover(g.gid, true);
groupHeader.onmouseleave = () => this.onHover(g.gid, false);
}
groupContainer.appendChild(groupHeader);
groupContainer.appendChild(termsContainer);
termKeys.forEach((key) => {
const termData = g.terms[key];
this._renderLegendRow(termsContainer, g, termData, false);
});
}
legend.appendChild(groupContainer);
});
}
_renderLegendRow(container, groupData, termData, isTopLevel) {
const count = termData.locs.length;
let labelHtml = termData.legendWikitext || `<strong>${escapeHtml(termData.term || "")}</strong>`;
if (count > 0) {
labelHtml += ` (${count})`;
}
const row = document.createElement("div");
row.className = CONFIG.CLASSES.MAP.LEGEND_ROW;
if (!isTopLevel) {
row.classList.add(CONFIG.CLASSES.MAP.LEGEND_ROW_NESTED, CONFIG.CLASSES.MAP.LEGEND_TERM_ROW);
}
row.setAttribute(DATA_ATTR.GROUP_ID, groupData.gid);
if (this.onHover) {
row.onmouseenter = () => this.onHover(groupData.gid, true);
row.onmouseleave = () => this.onHover(groupData.gid, false);
}
if (this.onRowClick) {
row.onclick = () => this.onRowClick(groupData.gid);
}
const switcher = document.createElement("div");
switcher.className = CONFIG.CLASSES.EXT.VSSWITCHER;
if (isTopLevel) {
const dot = document.createElement("span");
dot.className = CONFIG.CLASSES.MAP.LEGEND_ROW_DOT;
dot.style.backgroundColor = groupData.color;
switcher.appendChild(dot);
}
const labelSpan = document.createElement("span");
labelSpan.className = CONFIG.CLASSES.MAP.LEGEND_LABEL;
setElementHTML(labelSpan, labelHtml);
switcher.appendChild(labelSpan);
if (count > 0) {
const toggleEl = document.createElement("span");
toggleEl.className = CONFIG.CLASSES.EXT.VSTOGGLE_ELEMENT + " " + CONFIG.CLASSES.MAP.LEGEND_LOCS_TOGGLE;
toggleEl.innerText = UI_STRINGS.LECTS_EXPAND;
const hideDiv = document.createElement("div");
hideDiv.className = CONFIG.CLASSES.EXT.VSHIDE + " " + CONFIG.CLASSES.MAP.LEGEND_LOCS + " " + CONFIG.CLASSES.MAP.LEGEND_LOCS_COLLAPSED + " " + (isTopLevel ? CONFIG.CLASSES.MAP.LEGEND_LOCS_TOPLEVEL : CONFIG.CLASSES.MAP.LEGEND_LOCS_NESTED);
const ul = document.createElement("ul");
ul.className = CONFIG.CLASSES.MAP.LEGEND_LOCS_LIST;
termData.locs.sort((a, b) => a.localeCompare(b));
termData.locs.forEach((loc) => {
const li = document.createElement("li");
li.className = CONFIG.CLASSES.MAP.LEGEND_LOCS_ITEM;
setElementHTML(li, loc);
ul.appendChild(li);
});
hideDiv.appendChild(ul);
switcher.appendChild(toggleEl);
row.appendChild(switcher);
row.appendChild(hideDiv);
toggleEl.onclick = (e) => {
e.stopPropagation();
hideDiv.classList.toggle(CONFIG.CLASSES.MAP.LEGEND_LOCS_COLLAPSED);
toggleEl.innerText = hideDiv.classList.contains(CONFIG.CLASSES.MAP.LEGEND_LOCS_COLLAPSED) ? UI_STRINGS.LECTS_EXPAND : UI_STRINGS.LECTS_COLLAPSE;
};
} else {
row.appendChild(switcher);
}
container.appendChild(row);
}
highlightGroup(gid, active) {
const safeValue = CSS.escape(gid || "");
const query = CONFIG.SELECTORS.MAP.LEGEND_ROW + '[' + DATA_ATTR.GROUP_ID + '="' + safeValue + '"]';
const row = this.container.querySelector(query);
if (row) {
row.classList.toggle(CONFIG.CLASSES.MAP.LEGEND_ROW_HIGHLIGHTED, active);
}
}
}
class DialectMarker {
constructor(dialectMap, features, latlng) {
this.dialectMap = dialectMap;
this.features = features;
this.latlng = latlng;
this.leafletLayer = null;
this.size = CONFIG.MARKER_SIZE;
this.features.forEach((f) => {
if (!f.properties._originalColor) {
f.properties._originalColor = f.properties[PROPS.MARKER_COLOR];
}
});
}
render() {
const visibleFeatures = this.getVisibleFeatures();
if (visibleFeatures.length === 0) return;
this.leafletLayer = L.marker(this.latlng, {
icon: this._buildIcon(visibleFeatures),
});
this._setupPopup();
this._setupHover();
this._registerGroups();
this.leafletLayer.addTo(this.dialectMap.map);
}
update() {
const visibleFeatures = this.getVisibleFeatures();
if (visibleFeatures.length === 0) {
if (this.dialectMap.map.hasLayer(this.leafletLayer)) {
this.dialectMap.map.removeLayer(this.leafletLayer);
}
return;
}
if (!this.dialectMap.map.hasLayer(this.leafletLayer)) {
this.leafletLayer.addTo(this.dialectMap.map);
}
this.leafletLayer.setIcon(this._buildIcon(visibleFeatures));
this._setupSliceHover();
}
getVisibleFeatures() {
return this.features.filter((f) =>
this.dialectMap.featureManager.isVisible(f),
);
}
_buildIcon(visibleFeatures) {
const size = this.size;
const strokeWidth = CONFIG.MARKER_STROKE_WIDTH;
const r = (size - strokeWidth) / 2;
const center = size / 2;
const total = visibleFeatures.length;
let svg;
if (total === 1) {
const f = visibleFeatures[0];
const color = ColorUtils.resolveColor(f.properties[PROPS.MARKER_COLOR]);
const gid = f.properties[PROPS.GROUP_ID] || "";
const symbol = f.properties._derivedSymbol || 0;
const options = {
strokeWidth: CONFIG.MARKER_STROKE_WIDTH,
groupId: gid,
markerGroup: f.properties[PROPS.MARKER_GROUP] || "",
};
svg = this._getShapeSvg(symbol, color, size, options);
} else {
svg = markerSvgOpen(size);
let startAngle = -Math.PI / 2;
const anglePerSlice = (2 * Math.PI) / total;
visibleFeatures.forEach((f) => {
const color = ColorUtils.resolveColor(f.properties[PROPS.MARKER_COLOR]);
const gid = f.properties[PROPS.GROUP_ID] || "";
const endAngle = startAngle + anglePerSlice;
const x1 = center + r * Math.cos(startAngle);
const y1 = center + r * Math.sin(startAngle);
const x2 = center + r * Math.cos(endAngle);
const y2 = center + r * Math.sin(endAngle);
const largeArc = anglePerSlice > Math.PI ? 1 : 0;
const d = [
"M",
center,
center,
"L",
x1,
y1,
"A",
r,
r,
0,
largeArc,
1,
x2,
y2,
"Z",
].join(" ");
const dataAttr = ' ' + DATA_ATTR.GROUP_ID + '="' + escapeHtml(gid) + '"' +
' ' + DATA_ATTR.MARKER_GROUP + '="' + escapeHtml(f.properties[PROPS.MARKER_GROUP] || "") + '"';
svg += markerPieSliceEl(d, color, dataAttr);
startAngle = endAngle;
});
}
svg += "</svg>";
return L.divIcon({
className: CONFIG.CLASSES.MARKER.ROOT,
html: svg,
iconSize: [size, size],
iconAnchor: [size / 2, size / 2],
});
}
_getShapeSvg(symbolIndex, color, size, options) {
size = size || 10;
options = options || {};
const r = size / 2;
const center = size / 2;
const strokeWidth = options.strokeWidth || CONFIG.MARKER_STROKE_WIDTH;
const dataAttr = (options.groupId
? ' ' + DATA_ATTR.GROUP_ID + '="' + escapeHtml(options.groupId) + '"'
: "") + (options.markerGroup
? ' ' + DATA_ATTR.MARKER_GROUP + '="' + escapeHtml(options.markerGroup) + '"'
: "");
let svg = markerSvgOpen(size);
let d = "";
let fr = r - strokeWidth / 2;
if (fr < 0) fr = 0;
const shapeIndex = symbolIndex % 8;
if (shapeIndex === 0) {
svg += markerCircleEl(center, fr, color, dataAttr);
} else {
d = markerShapePathD(shapeIndex - 1, center, fr);
if (d) {
svg += markerPathEl(d, color, dataAttr);
}
}
svg += "</svg>";
return svg;
}
_setupPopup() {
const self = this;
this.leafletLayer.bindPopup(() => self._buildPopup(), {
className: CONFIG.CLASSES.MAP.CUSTOM_POPUP,
minWidth: CONFIG.POPUP_MIN_WIDTH,
});
}
_buildPopup() {
const dialectMap = this.dialectMap;
const featuresForPopup = this._popupFeatures
? this._popupFeatures.filter((f) => dialectMap.featureManager.isVisible(f))
: this.getVisibleFeatures();
const contentByLoc = new Map();
featuresForPopup.forEach((feature) => {
const props = feature.properties || {};
const term = props.term || "";
const loc = props.loc || "";
const gid = props[PROPS.GROUP_ID] || "";
const symbol = props._derivedSymbol || 0;
const langCode = props[PROPS.MARKER_LANG] || "";
const tr = props.tr || "";
const isIPA = props.isIPA ? "1" : "";
let color = ColorUtils.resolveColor(props[PROPS.MARKER_COLOR]);
let termKey = `term::${term}::${langCode}::${tr}::${isIPA}`;
if (props.wikitext) {
termKey = props.wikitext;
}
let displayTerm = escapeHtml(term);
if (dialectMap.contentCache && dialectMap.contentCache.has(termKey)) {
displayTerm = dialectMap.contentCache.get(termKey);
} else if (props.url) {
displayTerm = `<a href="${sanitizeUrl(props.url)}">${escapeHtml(
term,
)}</a>`;
}
let displayLoc = escapeHtml(loc);
const locKey = `loc::${loc}`;
if (dialectMap.contentCache && dialectMap.contentCache.has(locKey)) {
displayLoc = dialectMap.contentCache.get(locKey);
}
const rawGroup = props[PROPS.TERM_GROUP];
let displayGroup = rawGroup;
if (displayGroup) {
const groupCacheKey = `group::${displayGroup}::${langCode}`;
if (
dialectMap.contentCache &&
dialectMap.contentCache.has(groupCacheKey)
) {
displayGroup = dialectMap.contentCache.get(groupCacheKey);
} else {
displayGroup = escapeHtml(displayGroup);
}
}
if (!contentByLoc.has(displayLoc)) {
contentByLoc.set(displayLoc, new Map());
}
const groupsInLoc = contentByLoc.get(displayLoc);
const uniqueGroupKey = `${loc}::${gid}::${color}::${rawGroup || ""}`;
if (!groupsInLoc.has(uniqueGroupKey)) {
groupsInLoc.set(uniqueGroupKey, {
groupName: displayGroup,
color: color,
symbol: symbol,
terms: [],
});
}
groupsInLoc.get(uniqueGroupKey).terms.push(displayTerm);
});
let html = '<div class="' + CONFIG.CLASSES.MARKER.POPUP + '">';
contentByLoc.forEach((groups, locationTitle) => {
html += `<div><span class="${CONFIG.CLASSES.POPUP.TITLE}">${locationTitle}</span>`;
groups.forEach((group) => {
html += `<br><span class="${CONFIG.CLASSES.POPUP.COLOR_DOT}" style="background:${escapeHtml(group.color)
};"></span> ${group.terms.join(", ")}`;
if (dialectMap.config.showGroup && group.groupName) {
html += ` (${group.groupName})`;
}
});
html += "</div>";
});
html += "</div>";
return html;
}
_setupHover() {
const self = this;
const dialectMap = this.dialectMap;
this.leafletLayer.on("add", () => {
self._setupSliceHover();
});
this.leafletLayer.on("mouseover", () => {
dialectMap._closePolygonTooltips();
});
}
_setupSliceHover() {
const self = this;
const dialectMap = this.dialectMap;
const icon = this.leafletLayer.getElement();
if (!icon) return;
if (icon._sliceHoverBound) return;
icon._sliceHoverBound = true;
const paths = icon.querySelectorAll(CONFIG.SELECTORS.ICON_SHAPES);
paths.forEach((path) => {
path.addEventListener("mouseenter", function () {
dialectMap._setPieFocus(icon, [this]);
const gid = path.getAttribute(DATA_ATTR.GROUP_ID);
self.features.forEach((f) => {
if (f.properties[PROPS.GROUP_ID] === gid) {
dialectMap._handleFeatureHover(f, true);
}
});
});
path.addEventListener("mouseleave", function () {
dialectMap._resetPieFocus(icon);
const gid = path.getAttribute(DATA_ATTR.GROUP_ID);
self.features.forEach((f) => {
if (f.properties[PROPS.GROUP_ID] === gid) {
dialectMap._handleFeatureHover(f, false);
}
});
});
});
}
_registerGroups() {
const dialectMap = this.dialectMap;
const layer = this.leafletLayer;
this.features.forEach((feature) => {
const props = feature.properties || {};
const groupId = props[PROPS.GROUP_ID];
const group = props[PROPS.MARKER_GROUP];
if (groupId) {
if (!dialectMap.groupIdLayers[groupId]) {
dialectMap.groupIdLayers[groupId] = [];
}
dialectMap.groupIdLayers[groupId].push(layer);
}
if (group) {
if (!dialectMap.groupLayers[group]) {
dialectMap.groupLayers[group] = L.layerGroup();
}
}
});
}
}
class DialectMap {
constructor(container) {
this.container = container;
applyPageDirection(container);
this.map = null;
this.config = {};
this.mapContainer = null;
this.contentCache = new Map();
this.currentDepthMode = 2;
this.featureManager = new FeatureManager();
this.filterController = new FilterController(
this.container,
this.featureManager,
() => {
this._updateAllMarkers();
this._refreshLegend();
},
);
const mode = this.container.getAttribute(DATA_ATTR.MODE) || "term";
this.legendController = new LegendController(
this.container,
(gid, active) => { this._handleLegendHover(gid, active); },
mode,
(gid) => { this._handleLegendRowClick(gid); },
);
this.groupLayers = {};
this.groupIdLayers = {};
this.pinnedLegendGroupId = null;
}
init() {
const missingConfigLang = this.container.getAttribute(DATA_ATTR.MISSING_CONFIG);
if (missingConfigLang) {
this._setPlaceholderMissingConfig(missingConfigLang);
return;
}
if (!this._parseData()) return;
if (this.config.mode === "all") {
this.currentDepthMode = this.featureManager.maxGroupDepth;
}
const self = this;
mw.loader.using(["ext.kartographer.box", "mediawiki.api"]).then(
function () {
if (!self._renderMap()) return;
self.filterController.initUI(self.groupLayers);
self.filterController.addTo(self.map);
const features = self.featureManager.getFeatures();
self.legendController.render(self._getLegendData(features));
self._legendClickHandler = function (e) {
if (e.target.closest(CONFIG.SELECTORS.MAP.LEGEND_ROW)) return;
self._clearPinnedLegend();
};
document.addEventListener("click", self._legendClickHandler);
self._setupSearch();
self._createFullscreenControl();
self._createMarkerModeControl();
if (self.config.mode === "all") {
self._createDepthControl();
}
self._fetchMissingContent().then(() => {
self.legendController.render(
self._getLegendData(self.featureManager.getFeatures()),
);
}).catch(function (e) {
console.warn("Dialect Map: Failed to fetch wikitext content", e);
});
},
function () {
self._setPlaceholderError("Error: Kartographer extension not available.");
},
);
}
_setPlaceholderError(message, options) {
const el = this.container.querySelector(CONFIG.SELECTORS.MAP.PLACEHOLDER);
if (!el) return;
if (options && options.asHtml) {
setElementHTML(el, message);
if (options.placeholderClass) el.classList.add(options.placeholderClass);
} else {
el.textContent = message;
}
}
_setPlaceholderMissingConfig(langCode) {
const modulePath = "Module:dialect map/data/" + langCode;
const example = "return {\n\tconfig = {\n\t\tlang_code = \"" + langCode + "\",\n\t\tlat = 0,\n\t\tlong = 0,\n\t\tzoom = 2,\n\t\twidth = \"100%\",\n\t\theight = 600,\n\t\tmapstyle = \"osm-intl\",\n\t},\n\tgroups = {},\n}";
const html = [
"<strong>Missing map config for language: " + escapeHtml(langCode) + "</strong>",
"<p>No config module was found. Create <strong><a href=\"" + escapeHtml(mw.util.getUrl(modulePath)) + "\">" + escapeHtml(modulePath) + "</a></strong> and export a table with a <em>config</em> (and optionally <em>groups</em>) key.</p>",
"<p><strong>Example:</strong></p>",
"<pre>" + escapeHtml(example) + "</pre>",
"<p>Adjust <em>lat</em>, <em>long</em>, and <em>zoom</em> for your language's map view.</p>",
].join("\n");
this._setPlaceholderError(html, { asHtml: true, placeholderClass: CONFIG.CLASSES.MAP.PLACEHOLDER_MISSING_CONFIG });
}
_parseData() {
const dataEl = this.container.querySelector(CONFIG.SELECTORS.MAP.DATA);
if (!dataEl) {
this._setPlaceholderError("No map data.");
return false;
}
let rawData;
try {
rawData = JSON.parse(dataEl.textContent);
} catch (e) {
this._setPlaceholderError("Error parsing map data.");
return false;
}
if (rawData.d && rawData.f) {
const dict = rawData.d;
const minifiedFeatures = rawData.f;
const self = this;
const decompress = (obj) => {
if (typeof obj === "string") {
if (obj.startsWith("@@") && obj.endsWith("@@")) {
const id = obj.substring(2, obj.length - 2);
return dict[id] || obj;
}
} else if (Array.isArray(obj)) {
for (let i = 0; i < obj.length; i++) {
obj[i] = decompress(obj[i]);
}
} else if (typeof obj === "object" && obj !== null) {
for (const key in obj) {
obj[key] = decompress(obj[key]);
}
}
return obj;
};
this.featureManager.geoJSON = minifiedFeatures.map((mf) => {
let feature;
if (Array.isArray(mf)) {
const lat = mf[1];
const lon = mf[2];
const props = {
wikitext: decompress(mf[3]),
legendWikitext: decompress(mf[4]),
term: decompress(mf[5]),
url: decompress(mf[6]),
[PROPS.TERM_GROUP]: decompress(mf[7]),
[PROPS.MARKER_GROUP]: decompress(mf[8]),
[PROPS.MARKER_COLOR]: mf[9],
[PROPS.MARKER_LANG]: mf[10],
[PROPS.GROUP_ID]: mf[11],
loc: decompress(mf[12]),
};
feature = {
type: "Feature",
properties: props,
geometry: {
type: "Point",
coordinates: [lon, lat],
},
};
} else {
feature = {
type: "Feature",
properties: decompress(mf.p || {}),
geometry: null,
};
if (mf.g) {
const geom = {
coordinates: mf.g.c,
};
if (mf.g.t === "pt") {
geom.type = "Point";
} else if (mf.g.t === "pl") {
geom.type = "Polygon";
} else {
geom.type = mf.g.t;
}
feature.geometry = geom;
}
}
if (feature.properties[PROPS.MARKER_GROUP]) {
const depth = feature.properties[PROPS.MARKER_GROUP].split("/").length;
if (depth > self.featureManager.maxGroupDepth) {
self.featureManager.maxGroupDepth = depth;
}
}
return feature;
});
} else {
this.featureManager.geoJSON = rawData;
}
this._deriveSymbols();
this._generateColors();
const multiMarkerRaw = (this.container.getAttribute(DATA_ATTR.MULTI_MARKER) || CONFIG.MULTI_MARKER_DEFAULT).toLowerCase();
const multiMarker = CONFIG.MULTI_MARKER_MODES.includes(multiMarkerRaw)
? multiMarkerRaw
: CONFIG.MULTI_MARKER_DEFAULT;
this.config = {
lat: parseFloat(this.container.getAttribute(DATA_ATTR.LAT)),
lon: parseFloat(this.container.getAttribute(DATA_ATTR.LON)),
zoom: parseInt(this.container.getAttribute(DATA_ATTR.ZOOM), 10),
style: (this.container.getAttribute(DATA_ATTR.MAPSTYLE) || "osm-intl").replace(/[^a-zA-Z0-9\-]/g, ""),
showGroup: this.container.getAttribute(DATA_ATTR.SHOW_GROUP) === "true",
mode: this.container.getAttribute(DATA_ATTR.MODE) || "term",
multiMarker,
};
const features = this.featureManager.getFeatures();
if (!features || features.length === 0) {
this._setPlaceholderError("No features to display.");
return false;
}
return true;
}
_deriveSymbols() {
const groups = {};
const features = this.featureManager.getFeatures();
features.forEach((f) => {
const p = f.properties || {};
const groupName = p[PROPS.TERM_GROUP];
if (groupName) {
const key = `${groupName}::${p[PROPS.MARKER_LANG] || ""}`;
if (!groups[key]) groups[key] = new Set();
const term = p.term || "";
groups[key].add(term);
}
});
const variantMap = {};
Object.keys(groups).forEach((key) => {
const terms = Array.from(groups[key]);
if (terms.length > 1) {
terms.sort();
const termToSymbol = {};
terms.forEach((t, i) => {
termToSymbol[t] = i;
});
variantMap[key] = termToSymbol;
}
});
features.forEach((f) => {
const p = f.properties || {};
const groupName = p[PROPS.TERM_GROUP];
if (groupName) {
const key = `${groupName}::${p[PROPS.MARKER_LANG] || ""}`;
if (variantMap[key]) {
const term = p.term || "";
const symbolIndex = variantMap[key][term];
if (symbolIndex !== undefined) {
p._derivedSymbol = symbolIndex;
}
}
}
});
}
_generateColors() {
const features = this.featureManager.getFeatures();
const groupIdToFormKey = new Map();
features.forEach((f) => {
if (f.geometry && f.geometry.type === "Point") {
const p = f.properties || {};
const groupId = p[PROPS.GROUP_ID];
if (!groupId) return;
if (groupIdToFormKey.has(groupId)) return;
let formKey = p[PROPS.TERM_GROUP] || p.term || "";
if (p.tr && !p[PROPS.TERM_GROUP]) {
formKey = formKey + "::tr:" + p.tr;
}
if (formKey) {
groupIdToFormKey.set(groupId, formKey);
}
}
});
const groupIds = Array.from(groupIdToFormKey.keys());
const formKeys = groupIds.map(gid => groupIdToFormKey.get(gid));
const mode = this.container.getAttribute(DATA_ATTR.MODE) || "term";
let sortedFormKeys;
if (mode === "all") {
sortedFormKeys = [...formKeys].sort();
} else {
sortedFormKeys = ColorUtils.sortBySimilarity(formKeys);
}
const formKeyToGroupId = new Map();
groupIds.forEach(gid => {
const fk = groupIdToFormKey.get(gid);
if (fk) formKeyToGroupId.set(fk, gid);
});
const sortedGroupIds = sortedFormKeys
.map(fk => formKeyToGroupId.get(fk))
.filter(gid => gid !== undefined);
const formColors = {};
sortedGroupIds.forEach((groupId, idx) => {
const hue = (idx / (sortedGroupIds.length > 0 ? sortedGroupIds.length : 1)) % 1;
const sat = ((idx + 1) % 2 === 0) ? 0.7 : 1.0;
const light = ((idx + 1) % 3 === 0) ? 0.4 : 0.5;
formColors[groupId] = ColorUtils.hslToHex(hue, sat, light);
});
features.forEach((f) => {
if (f.geometry && f.geometry.type === "Point") {
const p = f.properties || {};
const groupId = p[PROPS.GROUP_ID];
if (groupId && formColors[groupId]) {
p[PROPS.MARKER_COLOR] = formColors[groupId];
} else if (!p[PROPS.MARKER_COLOR] || p[PROPS.MARKER_COLOR] === "") {
p[PROPS.MARKER_COLOR] = CONFIG.DEFAULT_MARKER_COLOR_HEX;
}
}
});
}
_renderMap() {
const placeholder = this.container.querySelector(
CONFIG.SELECTORS.MAP.PLACEHOLDER,
);
if (!placeholder) {
this._setPlaceholderError("No map container.");
return false;
}
const lat = this.config.lat;
const lon = this.config.lon;
const zoom = this.config.zoom;
if (
typeof lat !== "number" || !Number.isFinite(lat) ||
typeof lon !== "number" || !Number.isFinite(lon) ||
typeof zoom !== "number" || !Number.isFinite(zoom)
) {
this._setPlaceholderError("Invalid map configuration.");
return false;
}
placeholder.innerHTML = "";
this.mapContainer = placeholder;
try {
this.map = L.map(this.mapContainer, {
center: [lat, lon],
zoom: zoom,
maxBounds: [
[-90, -180],
[90, 180],
],
maxBoundsViscosity: 1.0,
minZoom: CONFIG.MAP_MIN_ZOOM,
});
} catch (e) {
this._setPlaceholderError("Error initializing map.");
return false;
}
L.tileLayer(
"https://maps.wikimedia.org/" +
this.config.style +
"/{z}/{x}/{y}{r}.png?lang=en",
{
attribution:
'<a href="https://wikimediafoundation.org/wiki/Maps_Terms_of_Use">Wikimedia</a> | © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
maxZoom: CONFIG.MAP_MAX_ZOOM,
noWrap: true,
},
).addTo(this.map);
const features = this.featureManager.getFeatures();
this._createPointMarkers();
this.polygonLayers = [];
features.forEach((feature) => {
if (feature.geometry && feature.geometry.type === "Polygon") {
const ringCoords = feature.geometry.coordinates[0];
const latlngs = ringCoords.map((coord) => [coord[1], coord[0]]);
const props = feature.properties || {};
const polygon = L.polygon(latlngs, {
color: props.stroke || props.fill || CONFIG.DEFAULT_POLYGON_COLOR,
weight:
props[PROPS.STROKE_WIDTH] !== undefined ? props[PROPS.STROKE_WIDTH] : 1,
opacity:
props[PROPS.STROKE_OPACITY] !== undefined
? props[PROPS.STROKE_OPACITY]
: 0.5,
fillColor: props.fill || CONFIG.DEFAULT_POLYGON_COLOR,
fillOpacity:
props[PROPS.FILL_OPACITY] !== undefined ? props[PROPS.FILL_OPACITY] : 0.2,
bubblingMouseEvents: true,
});
if (props.title) {
polygon.bindTooltip(escapeHtml(props.title), { sticky: true });
}
polygon.on("mouseover", function () {
this.bringToBack();
});
polygon.addTo(this.map);
polygon.bringToBack();
this.polygonLayers.push(polygon);
}
});
return true;
}
async _fetchMissingContent() {
const features = this.featureManager.getFeatures();
const self = this;
await wikitextCache.init();
const pendingItems = [];
const featuresByKey = {};
features.forEach((f) => {
const p = f.properties || {};
if (p.wikitext) {
pendingItems.push({ key: p.wikitext, wikitext: p.wikitext, feature: f, prop: PROPS.PARSED_WIKITEXT });
}
if (p.legendWikitext) {
pendingItems.push({ key: `legend::${p.legendWikitext}`, wikitext: p.legendWikitext, feature: f, prop: PROPS.PARSED_LEGEND_WIKITEXT });
} else if (p[PROPS.TERM_GROUP]) {
const groupName = p[PROPS.TERM_GROUP];
const groupWikitext = `${groupName}`;
pendingItems.push({ key: `legend::${groupWikitext}`, wikitext: groupWikitext, feature: f, prop: PROPS.PARSED_LEGEND_WIKITEXT });
}
const loc = p.loc || "";
if (loc && loc.indexOf("[[") !== -1) {
pendingItems.push({ key: `loc::${loc}`, wikitext: loc, feature: f, prop: PROPS.PARSED_LOCATION });
}
if (p[PROPS.TERM_GROUP]) {
const groupName = p[PROPS.TERM_GROUP];
pendingItems.push({
key: `group::${groupName}::${p[PROPS.MARKER_LANG] || ""}`,
wikitext: groupName,
feature: f,
prop: PROPS.PARSED_GROUP
});
}
});
const seen = new Set();
const uniqueItems = [];
pendingItems.forEach((item) => {
if (!featuresByKey[item.key]) featuresByKey[item.key] = [];
featuresByKey[item.key].push({ feature: item.feature, prop: item.prop });
if (!seen.has(item.key)) {
seen.add(item.key);
uniqueItems.push(item);
}
});
const cacheResults = await Promise.all(
uniqueItems.map(async (item) => {
const hash = await hashString(item.wikitext);
const cached = await wikitextCache.get(hash);
return Object.assign({}, item, { hash: hash, cached: cached });
})
);
const itemsToFetch = [];
cacheResults.forEach((item) => {
if (item.cached) {
self.contentCache.set(item.key, item.cached);
featuresByKey[item.key].forEach((entry) => {
entry.feature.properties[entry.prop] = item.cached;
});
} else {
itemsToFetch.push(item);
}
});
if (itemsToFetch.length === 0) return;
const API_HEADERS = {
"Api-User-Agent": "Dialect Map (Gadget developed by [[User:Fenakhay]])",
};
const actionAPI = new mw.Api({ ajax: { headers: API_HEADERS } });
for (let i = 0; i < itemsToFetch.length; i += CONFIG.WIKITEXT_BATCH_SIZE) {
const batch = itemsToFetch.slice(i, i + CONFIG.WIKITEXT_BATCH_SIZE);
const wikitext = batch
.map(
(item, idx) =>
`<div class="${CONFIG.CLASSES.MAP.CONTENT_WRAPPER}" ${DATA_ATTR.IDX}="${idx}">${item.wikitext}</div>`,
)
.join("");
try {
const data = await actionAPI.post({
action: "parse",
text: wikitext,
prop: "text",
contentmodel: "wikitext",
disablelimitreport: true,
});
const parseText = data.parse && data.parse.text && data.parse.text["*"];
if (parseText === undefined) {
console.warn("Dialect Map: Parse API returned unexpected shape", data);
continue;
}
const tempDiv = document.createElement("div");
setElementHTML(tempDiv, parseText);
const wrappers = tempDiv.querySelectorAll(CONFIG.SELECTORS.MAP.CONTENT_WRAPPER);
const cachePromises = [];
wrappers.forEach((wrapper) => {
const idx = parseInt(wrapper.getAttribute(DATA_ATTR.IDX));
if (!isNaN(idx) && idx < batch.length) {
const item = batch[idx];
const content = wrapper.innerHTML;
self.contentCache.set(item.key, content);
cachePromises.push(wikitextCache.set(item.hash, content));
if (featuresByKey[item.key]) {
featuresByKey[item.key].forEach((entry) => {
entry.feature.properties[entry.prop] = content;
});
}
}
});
await Promise.all(cachePromises);
} catch (e) {
console.warn("Dialect Map: Batch formatting failed", e);
}
}
}
_handleLegendHover(groupId, active) {
if (this.pinnedLegendGroupId && active && groupId !== this.pinnedLegendGroupId) {
this._clearPinnedLegend();
}
if (this.pinnedLegendGroupId) {
this._highlightLegendRow(this.pinnedLegendGroupId, true);
if (this.config.mode === "all") {
this._highlightPath(this.pinnedLegendGroupId, true);
} else {
this._highlightGroup(this.pinnedLegendGroupId, true);
}
return;
}
this._highlightLegendRow(groupId, active);
if (this.config.mode === "all") {
this._highlightPath(groupId, active);
} else {
this._highlightGroup(groupId, active);
}
}
_handleLegendRowClick(gid) {
if (this.pinnedLegendGroupId === gid) {
this._clearPinnedLegend();
} else {
this._setPinnedLegend(gid);
}
}
_setPinnedLegend(gid) {
this.pinnedLegendGroupId = gid;
this._handleLegendHover(gid, true);
}
_clearPinnedLegend() {
const prev = this.pinnedLegendGroupId;
this.pinnedLegendGroupId = null;
if (prev) {
this._highlightLegendRow(prev, false);
if (this.config.mode === "all") {
this._highlightPath(prev, false);
} else {
this._highlightGroup(prev, false);
}
}
}
_setLayerHighlight(layer, active, groupId, markerGroupPath) {
const self = this;
if (active) {
if (layer.setIcon) {
const icon = layer.getElement();
if (icon) {
let highlightedSlices = null;
if (markerGroupPath) {
const safePath = CSS.escape(markerGroupPath);
const slices = icon.querySelectorAll(
`[${DATA_ATTR.MARKER_GROUP}="${safePath}"], [${DATA_ATTR.MARKER_GROUP}^="${safePath}/"]`,
);
const allPaths = icon.querySelectorAll(CONFIG.SELECTORS.ICON_SHAPES);
if (slices.length > 0) {
highlightedSlices = slices;
if (slices.length === allPaths.length) {
const svg = icon.querySelector("svg");
if (svg) L.DomUtil.addClass(svg, CONFIG.CLASSES.MARKER.FOCUSED);
} else {
self._setPieFocus(icon, slices);
}
}
} else if (groupId) {
const safeGroupId = CSS.escape(groupId);
const slices = icon.querySelectorAll(
`[${DATA_ATTR.GROUP_ID}="${safeGroupId}"]`,
);
const allPaths = icon.querySelectorAll(CONFIG.SELECTORS.ICON_SHAPES);
if (slices.length > 0) {
highlightedSlices = slices;
if (slices.length === allPaths.length) {
const svg = icon.querySelector("svg");
if (svg) L.DomUtil.addClass(svg, CONFIG.CLASSES.MARKER.FOCUSED);
} else {
self._setPieFocus(icon, slices);
}
} else {
const svg = icon.querySelector("svg");
if (svg) L.DomUtil.addClass(svg, CONFIG.CLASSES.MARKER.FOCUSED);
highlightedSlices = icon.querySelectorAll(CONFIG.SELECTORS.ICON_SHAPES_PIE);
}
} else {
const svg = icon.querySelector("svg");
if (svg) L.DomUtil.addClass(svg, CONFIG.CLASSES.MARKER.FOCUSED);
highlightedSlices = icon.querySelectorAll(CONFIG.SELECTORS.ICON_SHAPES);
}
if (highlightedSlices) {
highlightedSlices.forEach(p => {
L.DomUtil.addClass(p, CONFIG.CLASSES.MARKER.HIGHLIGHTED);
});
}
layer.setZIndexOffset(CONFIG.HIGHLIGHT_Z_INDEX);
}
}
} else {
if (layer.setIcon) {
const icon = layer.getElement();
if (icon) {
self._resetPieFocus(icon);
const paths = icon.querySelectorAll(CONFIG.SELECTORS.ICON_SHAPES);
paths.forEach(p => {
L.DomUtil.removeClass(p, CONFIG.CLASSES.MARKER.HIGHLIGHTED);
});
layer.setZIndexOffset(0);
}
}
}
}
_highlightLegendRow(groupId, active) {
this.legendController.highlightGroup(groupId, active);
}
_highlightGroup(groupId, active) {
const self = this;
const layers = this.groupIdLayers[groupId];
if (layers) {
layers.forEach((layer) => {
self._setLayerHighlight(layer, active, groupId, null);
});
}
}
_highlightPath(path, active) {
if (!this.markers) return;
const self = this;
const pathPrefix = path + "/";
this.markers.forEach((marker) => {
if (marker.leafletLayer) {
const hasMatch = marker.features.some((f) => {
const markerGroup = f.properties && f.properties[PROPS.MARKER_GROUP];
return markerGroup && (markerGroup === path || markerGroup.startsWith(pathPrefix));
});
if (hasMatch) {
self._setLayerHighlight(marker.leafletLayer, active, null, path);
}
}
});
}
_closePolygonTooltips() {
if (!this.polygonLayers) return;
this.polygonLayers.forEach(function (polygon) {
polygon.closeTooltip();
});
}
_updateAllMarkers() {
if (!this.markers) return;
this.markers.forEach(function (marker) {
marker.update();
});
}
_updateClusterSpread() {
if (this.config.multiMarker !== "cluster" || !this.markers || !this.map) return;
const zoom = this.map.getZoom();
const spreadRadiusDeg = getClusterSpreadRadiusDeg(zoom);
this.markers.forEach((marker) => {
const c = marker._clusterCenter;
if (!c || !marker.leafletLayer) return;
const centerLat = c.lat;
const centerLon = c.lon;
const featureCount = marker._clusterFeatureCount;
const featureIndex = marker._clusterFeatureIndex;
const angle = featureCount === 1 ? 0 : (2 * Math.PI * featureIndex) / featureCount;
const markerLat = centerLat + spreadRadiusDeg * Math.cos(angle);
const markerLon = centerLon + (spreadRadiusDeg * Math.sin(angle)) / Math.max(0.01, Math.cos(centerLat * Math.PI / 180));
marker.latlng = L.latLng(markerLat, markerLon);
marker.leafletLayer.setLatLng(marker.latlng);
});
}
_removePointMarkers() {
if (!this.markers) return;
this.markers.forEach((marker) => {
if (marker.leafletLayer && this.map && this.map.hasLayer(marker.leafletLayer)) {
this.map.removeLayer(marker.leafletLayer);
}
});
this.markers = [];
this.groupIdLayers = {};
this.groupLayers = {};
if (this._clusterZoomHandler && this.map) {
this.map.off("zoomend", this._clusterZoomHandler);
this._clusterZoomHandler = null;
}
}
_groupFeaturesByLocation(pointFeatures) {
const featuresByLocation = {};
pointFeatures.forEach((feature) => {
const coords = feature.geometry.coordinates;
const locationKey = coords[1].toFixed(6) + "," + coords[0].toFixed(6);
if (!featuresByLocation[locationKey]) featuresByLocation[locationKey] = [];
featuresByLocation[locationKey].push(feature);
});
return featuresByLocation;
}
_createPointMarkers() {
const self = this;
const features = this.featureManager.getFeatures();
const pointFeatures = features.filter((f) => f.geometry && f.geometry.type === "Point");
this.markers = [];
if (this.config.multiMarker === "cluster") {
const featuresByLocation = this._groupFeaturesByLocation(pointFeatures);
const initialZoom = self.map.getZoom();
const spreadRadiusDeg = getClusterSpreadRadiusDeg(initialZoom);
Object.keys(featuresByLocation).forEach((locationKey) => {
const featuresAtLocation = featuresByLocation[locationKey];
const centerLat = featuresAtLocation[0].geometry.coordinates[1];
const centerLon = featuresAtLocation[0].geometry.coordinates[0];
const featureCount = featuresAtLocation.length;
featuresAtLocation.forEach((feature, featureIndex) => {
const angle = featureCount === 1 ? 0 : (2 * Math.PI * featureIndex) / featureCount;
const markerLat = centerLat + spreadRadiusDeg * Math.cos(angle);
const markerLon = centerLon + (spreadRadiusDeg * Math.sin(angle)) / Math.max(0.01, Math.cos(centerLat * Math.PI / 180));
const markerLatLng = L.latLng(markerLat, markerLon);
const marker = new DialectMarker(self, [feature], markerLatLng);
marker._clusterCenter = { lat: centerLat, lon: centerLon };
marker._clusterFeatureIndex = featureIndex;
marker._clusterFeatureCount = featureCount;
marker._popupFeatures = featuresAtLocation;
marker.render();
self.markers.push(marker);
});
});
this._clusterZoomHandler = () => this._updateClusterSpread();
this.map.on("zoomend", this._clusterZoomHandler);
} else {
const featuresByLocation = this._groupFeaturesByLocation(pointFeatures);
Object.keys(featuresByLocation).forEach((locationKey) => {
const featuresAtLocation = featuresByLocation[locationKey];
const firstFeature = featuresAtLocation[0];
const markerLatLng = L.latLng(
firstFeature.geometry.coordinates[1],
firstFeature.geometry.coordinates[0],
);
const marker = new DialectMarker(self, featuresAtLocation, markerLatLng);
marker._popupFeatures = featuresAtLocation;
marker.render();
self.markers.push(marker);
});
}
}
_switchMarkerMode() {
this.config.multiMarker = this.config.multiMarker === "cluster" ? "pie" : "cluster";
this._removePointMarkers();
this._createPointMarkers();
if (this._markerModeBtn) {
this._markerModeBtn.innerText = this.config.multiMarker === "cluster" ? UI_STRINGS.MARKER_MODE_CLUSTER : UI_STRINGS.MARKER_MODE_PIE;
this._markerModeBtn.title = this.config.multiMarker === "cluster" ? UI_STRINGS.SWITCH_TO_PIE : UI_STRINGS.SWITCH_TO_CLUSTER;
}
}
_refreshLegend() {
const features = this.featureManager.getFeatures();
const visibleFeatures = features.filter((f) => this.featureManager.isVisible(f));
const legendData = this._getLegendData(visibleFeatures);
this.legendController.render(legendData);
}
_createFullscreenControl() {
const self = this;
var fullscreenUpdateIcon = null;
const control = createMapControl(this.container, leafletControlPosition(this.container, true), {
content: SVG_ICONS.FULLSCREEN_EXPAND,
title: UI_STRINGS.TOGGLE_FULLSCREEN,
extraBtnClass: CONFIG.CLASSES.MAP.CONTROL_BTN_FULLSCREEN,
onClick: function () {
const mapContainer = self.map.getContainer();
const isFullscreen = document.fullscreenElement || document.webkitFullscreenElement;
if (!isFullscreen) {
if (mapContainer.requestFullscreen) {
mapContainer.requestFullscreen().catch(function () { });
} else if (mapContainer.webkitRequestFullscreen) {
mapContainer.webkitRequestFullscreen();
}
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
}
}
},
onAfterAdd: function (btn) {
fullscreenUpdateIcon = function () {
const isFullscreen = document.fullscreenElement || document.webkitFullscreenElement;
if (isFullscreen) {
btn.title = UI_STRINGS.EXIT_FULLSCREEN;
btn.innerHTML = SVG_ICONS.FULLSCREEN_COLLAPSE;
} else {
btn.title = UI_STRINGS.TOGGLE_FULLSCREEN;
btn.innerHTML = SVG_ICONS.FULLSCREEN_EXPAND;
}
};
document.addEventListener("fullscreenchange", fullscreenUpdateIcon);
document.addEventListener("webkitfullscreenchange", fullscreenUpdateIcon);
},
onRemove: function () {
if (fullscreenUpdateIcon) {
document.removeEventListener("fullscreenchange", fullscreenUpdateIcon);
document.removeEventListener("webkitfullscreenchange", fullscreenUpdateIcon);
fullscreenUpdateIcon = null;
}
},
});
this.map.addControl(control);
}
_createMarkerModeControl() {
const self = this;
const control = createMapControl(this.container, leafletControlPosition(this.container, true), {
content: self.config.multiMarker === "cluster" ? UI_STRINGS.MARKER_MODE_CLUSTER : UI_STRINGS.MARKER_MODE_PIE,
title: self.config.multiMarker === "cluster" ? UI_STRINGS.SWITCH_TO_PIE : UI_STRINGS.SWITCH_TO_CLUSTER,
onClick: function () { self._switchMarkerMode(); },
onAfterAdd: function (btn) { self._markerModeBtn = btn; },
});
this.map.addControl(control);
}
_getLegendData(features) {
const groups = {};
const groupOrder = [];
const self = this;
features.forEach((f) => {
const p = f.properties || {};
let gid, label, color;
if (self.config.mode === "all") {
const fullGroup = p[PROPS.MARKER_GROUP];
if (!fullGroup) {
gid = "ungrouped";
label = "Other";
color = p[PROPS.MARKER_COLOR] || CONFIG.DEFAULT_MARKER_COLOR;
} else {
const parts = fullGroup.split("/");
const sliceLen = Math.min(parts.length, self.currentDepthMode);
gid = parts.slice(0, sliceLen).join("/");
label = parts[sliceLen - 1] || gid;
color = ColorUtils.resolveColor(p[PROPS.MARKER_COLOR]);
}
} else {
gid = p[PROPS.GROUP_ID];
if (!gid) return;
label = p.term;
color = ColorUtils.resolveColor(p[PROPS.MARKER_COLOR]);
}
if (!groups[gid]) {
groups[gid] = {
gid: gid,
label: label,
color: color,
parsedGroup: p.parsedGroup,
terms: {},
};
groupOrder.push(gid);
}
const loc = p.parsedLocation || p.loc;
if (loc) {
const termKey = `term::${p.term}::${p[PROPS.MARKER_LANG] || ""}::${p.tr || ""}`;
if (!groups[gid].terms[termKey]) {
groups[gid].terms[termKey] = {
term: p.term,
alt: p.alt || p.term,
tr: p.tr || "",
wikitext: p.wikitext,
parsedWikitext: p.parsedWikitext,
legendWikitext: p.parsedLegendWikitext,
url: p.url,
locs: [],
};
}
groups[gid].terms[termKey].locs.push(loc);
}
});
function getGroupLocCount(gid) {
return Object.values(groups[gid].terms).reduce(
(acc, t) => acc + t.locs.length,
0,
);
}
const sorter = (a, b) => {
const countA = getGroupLocCount(a);
const countB = getGroupLocCount(b);
if (countA !== countB) return countB - countA;
return a.localeCompare(b);
};
if (self.config.mode === "all") {
groupOrder.sort(sorter);
} else {
groupOrder.sort((a, b) => {
const countA = getGroupLocCount(a);
const countB = getGroupLocCount(b);
if (countA !== countB) return countB - countA;
const numA = parseInt(a.replace("term-group-", "")) || 0;
const numB = parseInt(b.replace("term-group-", "")) || 0;
return numA - numB;
});
}
return groupOrder.map((gid) => groups[gid]);
}
_setupSearch() {
if (!this.filterController || !this.filterController.headerContainer)
return;
const self = this;
const headerContainer = this.filterController.headerContainer;
const searchInput = L.DomUtil.create("input", CONFIG.CLASSES.MAP.SEARCH_INPUT);
searchInput.type = "text";
searchInput.placeholder = UI_STRINGS.SEARCH_PLACEHOLDER;
if (headerContainer.firstChild) {
headerContainer.insertBefore(searchInput, headerContainer.firstChild);
} else {
headerContainer.appendChild(searchInput);
}
this.searchInput = searchInput;
this.searchInput.addEventListener("keydown", function (e) {
if (e.key !== "Enter") return;
const query = this.value.toLowerCase();
if (!query) return;
const matchedLocs = [];
for (let i = 0; i < self.markers.length; i++) {
const marker = self.markers[i];
const features = marker.features;
let matched = false;
for (let j = 0; j < features.length; j++) {
const props = features[j].properties;
const loc = (props.loc || "").toLowerCase();
const term = (props.term || "").toLowerCase();
const group = (props[PROPS.TERM_GROUP] || "").toLowerCase();
if (
loc.indexOf(query) !== -1 ||
term.indexOf(query) !== -1 ||
group.indexOf(query) !== -1
) {
matched = true;
break;
}
}
if (matched) {
matchedLocs.push(marker.latlng);
}
}
if (matchedLocs.length === 1) {
self.map.flyTo(matchedLocs[0], Math.max(self.map.getZoom(), CONFIG.FLY_TO_MIN_ZOOM));
} else if (matchedLocs.length > 1) {
const bounds = L.latLngBounds(matchedLocs);
const pad = CONFIG.FIT_BOUNDS_PADDING;
self.map.fitBounds(bounds, { padding: [pad, pad], maxZoom: CONFIG.FIT_BOUNDS_MAX_ZOOM });
}
});
this._setupExport();
}
_setupExport() {
if (!this.filterController || !this.filterController.footerContainer)
return;
const self = this;
const footerContainer = this.filterController.footerContainer;
const exportBtn = L.DomUtil.create(
"button",
CONFIG.CLASSES.MAP.EXPORT_BTN,
footerContainer,
);
exportBtn.innerText = UI_STRINGS.EXPORT_BTN;
exportBtn.onclick = function () {
self._exportData();
};
}
_exportData() {
let dataToExport = JSON.parse(JSON.stringify(this.featureManager.geoJSON));
let features = [];
if (Array.isArray(dataToExport)) {
features = dataToExport;
dataToExport = {
type: "FeatureCollection",
features: features,
};
} else if (dataToExport && !dataToExport.type) {
if (dataToExport.features) {
features = dataToExport.features;
dataToExport.type = "FeatureCollection";
}
} else if (dataToExport && dataToExport.features) {
features = dataToExport.features;
}
features.forEach(function (f) {
if (f.properties) {
const props = f.properties;
Object.keys(props).forEach(function (key) {
const val = props[key];
if (val === null || val === undefined || val === "") {
delete props[key];
}
});
if (props[PROPS.MARKER_COLOR] && !props[PROPS.MARKER_COLOR].startsWith("#")) {
props[PROPS.MARKER_COLOR] = "#" + props[PROPS.MARKER_COLOR];
}
if (props[PROPS.MARKER_LANG] && !props.lang) {
props.lang = props[PROPS.MARKER_LANG];
}
}
});
const pairs = new Set();
features.forEach(function (f) {
if (f.properties && f.properties.lang && f.properties.term) {
const safeLang = f.properties.lang.replace(/[^a-zA-Z0-9\-_]/g, "");
const safeTerm = f.properties.term.replace(/[^a-zA-Z0-9\-_]/g, "");
if (safeLang && safeTerm) {
pairs.add(safeLang + "-" + safeTerm);
}
}
});
let filename = "dialect_map";
const suffix = Array.from(pairs).join("-and-");
if (suffix) {
filename += "_" + suffix;
}
filename += ".json";
const jsonStr = JSON.stringify(dataToExport, null, 2);
const blob = new Blob([jsonStr], { type: "application/json" });
const url = URL.createObjectURL(blob);
const downloadAnchorNode = document.createElement("a");
downloadAnchorNode.href = url;
downloadAnchorNode.download = filename;
document.body.appendChild(downloadAnchorNode);
downloadAnchorNode.click();
downloadAnchorNode.remove();
URL.revokeObjectURL(url);
}
_setPieFocus(container, featuresToFocus) {
const svg = container.querySelector("svg");
if (svg) L.DomUtil.addClass(svg, CONFIG.CLASSES.MARKER.FOCUSED);
const allPaths = container.querySelectorAll(CONFIG.SELECTORS.ICON_SHAPES);
const isFocused = function (el) {
for (let i = 0; i < featuresToFocus.length; i++) {
if (featuresToFocus[i] === el) return true;
}
return false;
};
allPaths.forEach(function (p) {
if (isFocused(p)) {
if (!p.getAttribute("data-is-shape")) {
p.setAttribute(
"d",
"M 6,6 m -5.25,0 a 5.25,5.25 0 1,0 10.5,0 a 5.25,5.25 0 1,0 -10.5,0",
);
p.parentNode.appendChild(p);
}
L.DomUtil.addClass(p, CONFIG.CLASSES.MARKER.FOCUSED);
} else {
L.DomUtil.addClass(p, CONFIG.CLASSES.MARKER.DIMMED);
}
});
}
_resetPieFocus(container) {
const svg = container.querySelector("svg");
if (svg) L.DomUtil.removeClass(svg, CONFIG.CLASSES.MARKER.FOCUSED);
const allPaths = container.querySelectorAll(CONFIG.SELECTORS.ICON_SHAPES);
allPaths.forEach(function (p) {
if (!p.getAttribute("data-is-shape")) {
const originalD = p.getAttribute("data-original-d");
if (originalD) {
p.setAttribute("d", originalD);
}
}
L.DomUtil.removeClass(p, CONFIG.CLASSES.MARKER.FOCUSED);
L.DomUtil.removeClass(p, CONFIG.CLASSES.MARKER.DIMMED);
});
}
_createDepthControl() {
const control = L.control({ position: leafletControlPosition(this.container, false) });
const self = this;
control.onAdd = function (map) {
const container = L.DomUtil.create(
"div",
"leaflet-bar leaflet-control " + CONFIG.CLASSES.MAP.DEPTH_CONTROL,
);
if (self.featureManager.maxGroupDepth < 2) {
container.classList.add(CONFIG.CLASSES.MAP.DEPTH_CONTROL_HIDDEN);
return container;
}
const select = L.DomUtil.create("select", "", container);
if (!self.currentDepthMode)
self.currentDepthMode = self.featureManager.maxGroupDepth;
for (let d = 2; d <= self.featureManager.maxGroupDepth; d++) {
const levelIndex = d - 1;
const text = UI_STRINGS.LEVEL_PREFIX + levelIndex;
const el = document.createElement("option");
el.value = d;
el.innerText = text;
if (d === self.currentDepthMode) el.selected = true;
select.appendChild(el);
}
L.DomEvent.disableClickPropagation(container);
select.onchange = function () {
self.currentDepthMode = parseInt(this.value, 10);
self._updateAllColors();
};
return container;
};
this.map.addControl(control);
}
_handleFeatureHover(feature, active) {
let key;
if (this.config.mode === "all") {
const group = feature.properties[PROPS.MARKER_GROUP];
if (group) {
const parts = group.split("/");
const sliceLen = Math.min(parts.length, this.currentDepthMode);
key = parts.slice(0, sliceLen).join("/");
}
} else {
key = feature.properties[PROPS.GROUP_ID];
}
if (key) {
this._handleLegendHover(key, active);
}
}
_updateAllColors() {
const self = this;
const seenKeys = {};
const groupKeys = [];
this.markers.forEach((marker) => {
marker.features.forEach((f) => {
const groupId = f.properties[PROPS.MARKER_GROUP];
if (groupId) {
const parts = groupId.split("/");
const sliceLen = Math.min(parts.length, self.currentDepthMode);
const key = parts.slice(0, sliceLen).join("/");
if (!seenKeys[key]) {
seenKeys[key] = true;
groupKeys.push(key);
}
}
});
});
const repColors = {};
const sortedKeys = ColorUtils.sortBySimilarity(groupKeys);
sortedKeys.forEach((key, idx) => {
const hue = (idx / (sortedKeys.length > 0 ? sortedKeys.length : 1)) % 1;
const sat = ((idx + 1) % 2 === 0) ? 0.7 : 1.0;
const light = ((idx + 1) % 3 === 0) ? 0.4 : 0.5;
repColors[key] = ColorUtils.hslToHex(hue, sat, light);
});
this.markers.forEach((marker) => {
marker.features.forEach((f) => {
let color;
const groupId = f.properties[PROPS.MARKER_GROUP];
const pointColor = f.properties._originalColor || CONFIG.DEFAULT_MARKER_COLOR;
if (!groupId) {
color = pointColor;
} else {
const parts = groupId.split("/");
const sliceLen = Math.min(parts.length, self.currentDepthMode);
const key = parts.slice(0, sliceLen).join("/");
color = repColors[key] || pointColor;
}
f.properties[PROPS.MARKER_COLOR] = color;
});
marker.update();
});
this._refreshLegend();
}
destroy() {
if (this._legendClickHandler) {
document.removeEventListener("click", this._legendClickHandler);
this._legendClickHandler = null;
}
this._removePointMarkers();
if (this.polygonLayers) {
this.polygonLayers.forEach((polygon) => {
if (this.map && this.map.hasLayer(polygon)) {
this.map.removeLayer(polygon);
}
});
this.polygonLayers = [];
}
if (this.map) {
this.map.remove();
this.map = null;
}
this.contentCache.clear();
this.groupIdLayers = {};
this.groupLayers = {};
}
}
mw.hook("wikipage.content").add((content) => {
const rawContent = content instanceof jQuery ? content[0] : content;
if (!rawContent) return;
const apps = rawContent.querySelectorAll(CONFIG.SELECTORS.MAP.APP);
apps.forEach((app) => {
if (app.classList.contains(CONFIG.CLASSES.MAP.PROCESSED)) return;
app.classList.add(CONFIG.CLASSES.MAP.PROCESSED);
new DialectMap(app).init();
});
});
})(mediaWiki);
// </nowiki>
dfn21e48omv65fbhff343ylcqanx8t8
မဳဒဳယာဝဳကဳ:Gadget-DialectMap.css
8
295756
396586
2026-06-07T20:29:11Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု ".dialect-map-panel, .dialect-map-custom-popup .leaflet-popup-content-wrapper { background: white; border: 1px solid #ccc; border-radius: 4px; box-shadow: none; color: #333; } .dialect-map-panel { padding: 0; min-width: 200px; max-height: 300px; position: absolute; top: 0; inset-inline-start: 36px; display: flex; flex-direction: column; } .dialect-map-header-container { padding: 10px 10px 0 10px; backg..."
396586
css
text/css
.dialect-map-panel,
.dialect-map-custom-popup .leaflet-popup-content-wrapper {
background: white;
border: 1px solid #ccc;
border-radius: 4px;
box-shadow: none;
color: #333;
}
.dialect-map-panel {
padding: 0;
min-width: 200px;
max-height: 300px;
position: absolute;
top: 0;
inset-inline-start: 36px;
display: flex;
flex-direction: column;
}
.dialect-map-header-container {
padding: 10px 10px 0 10px;
background: white;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
flex-shrink: 0;
}
.dialect-map-scroll-container {
padding: 0 10px;
overflow-y: auto;
scrollbar-width: thin;
flex-grow: 1;
}
.dialect-map-footer-container {
padding: 10px;
background: white;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
flex-shrink: 0;
border-top: 1px solid #eee;
}
.dialect-map-custom-popup .leaflet-popup-content {
margin: 10px;
font-size: 13px;
line-height: 1.4;
}
.dialect-map-custom-popup .leaflet-popup-tip {
background: white;
border: 1px solid #ccc;
box-shadow: none;
}
.dialect-map-custom-popup .leaflet-popup-close-button {
color: #666;
}
.dialect-map-panel a,
.dialect-map-custom-popup a,
.dialect-map-link {
color: #0645ad;
text-decoration: none;
cursor: pointer;
}
.dialect-map-custom-popup .dialect-marker-popup a.new {
color: #ba0000;
}
.dialect-map-panel a:hover,
.dialect-map-custom-popup a:hover,
.dialect-map-link:hover {
text-decoration: underline;
}
.dialect-map-link + .dialect-map-link {
margin-inline-start: 8px;
}
.dialect-map-panel-header {
margin-bottom: 8px;
border-bottom: 1px solid #eee;
padding-bottom: 4px;
display: flex;
justify-content: space-between;
}
.dialect-marker-popup > div {
padding: 3px 0;
border-bottom: 1px solid #eee;
}
.dialect-marker-popup > div:last-child {
border-bottom: none;
}
.dialect-filter-checkbox {
display: inline-flex;
align-items: center;
cursor: pointer;
margin-inline-end: 4px;
}
.dialect-filter-checkbox input {
display: none;
}
.dialect-filter-checkbox-visual {
width: 14px;
height: 14px;
border: 1px solid #666;
border-radius: 2px;
display: inline-flex;
align-items: center;
justify-content: center;
}
.dialect-filter-checkbox-visual.checked {
background-color: #0645ad;
}
.dialect-filter-expander {
display: inline-flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
margin-inline-end: 4px;
}
.dialect-filter-expander.has-children {
cursor: pointer;
}
.leaflet-bar .dialect-map-control-btn {
display: flex;
align-items: center;
justify-content: center;
width: 26px;
height: 26px;
padding: 0;
margin: 0;
text-decoration: none;
font-size: 0;
line-height: 0;
background: white;
color: #333;
cursor: pointer;
}
.leaflet-bar .dialect-map-control-btn svg {
display: block;
margin: 0;
}
.leaflet-bar .dialect-map-control-btn:hover {
background: #f4f4f4;
color: #000;
}
.leaflet-bar .dialect-map-control-btn--text {
font-size: inherit;
line-height: inherit;
}
@media (max-width: 768px) {
.leaflet-bar .dialect-map-control-btn--fullscreen {
display: none;
}
}
.dialect-popup-color-dot {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 50%;
margin-inline-end: 6px;
vertical-align: middle;
}
.dialect-marker {
overflow: visible;
}
.dialect-marker path,
.dialect-marker circle,
.dialect-marker rect,
.dialect-marker polygon {
cursor: pointer;
transition: all 0.2s ease;
transform-origin: center center;
stroke: rgba(255, 255, 255, 0.4);
stroke-width: 1.5px;
vector-effect: non-scaling-stroke;
}
.dialect-marker path.dialect-marker-highlighted,
.dialect-marker circle.dialect-marker-highlighted,
.dialect-marker rect.dialect-marker-highlighted,
.dialect-marker polygon.dialect-marker-highlighted {
stroke: rgba(0, 0, 0, 0.4);
stroke-width: 1px;
}
.dialect-map-app .dialect-element-hidden {
display: none;
}
.dialect-tree-row {
--tree-depth: 0;
margin-bottom: 2px;
display: flex;
align-items: center;
padding-inline-start: calc(var(--tree-depth) * 15px);
}
.dialect-tree-label {
font-size: 13px;
font-weight: normal;
}
.dialect-tree-label.bold {
font-weight: bold;
}
.dialect-marker svg {
overflow: visible;
}
.dialect-marker-focused {
transform: scale(1.2);
transform-box: fill-box;
stroke: #000;
stroke-width: 1.33px;
opacity: 1;
}
.dialect-marker-dimmed {
opacity: 0.5;
transform: scale(0.8);
}
.dialect-map-legend-row-dot {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 50%;
margin-inline-end: 5px;
}
.dialect-marker-popup {
text-align: center;
}
.dialect-marker-popup .dialect-popup-title,
.dialect-marker-popup .dialect-popup-title a {
font-weight: bold;
}
.dialect-map-search-input {
width: 100%;
padding: 6px;
margin-bottom: 8px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
font-family: sans-serif;
font-size: 13px;
background: #fff;
color: #202122;
}
.dialect-map-export-btn {
display: block;
width: 100%;
margin-top: 10px;
padding: 6px;
background-color: #f8f9fa;
border: 1px solid #a2a9b1;
border-radius: 2px;
text-align: center;
cursor: pointer;
font-size: 13px;
color: #202122;
}
.dialect-map-export-btn:hover {
background-color: #ffffff;
border-color: #72777d;
}
.dialect-depth-control {
background: #fff;
padding: 5px;
border-radius: 4px;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.4);
}
.dialect-depth-control--hidden {
display: none;
}
.dialect-depth-control select {
font-size: 12px;
padding: 2px 4px;
min-width: 120px;
border: 1px solid #ccc;
background: #fff;
color: #202122;
}
.dialect-map-legend-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 10px;
margin-top: 15px;
width: 100%;
}
.dialect-map-legend-group {
display: flex;
flex-direction: column;
}
.dialect-map-legend-row {
break-inside: avoid;
background: var(--wikt-palette-paleblue);
color: var(--wikt-palette-black);
padding: 5px 10px;
border-radius: 4px;
border: 1px solid var(--wikt-palette-dulllighterblue);
}
.dialect-map-legend-row .vsSwitcher {
display: flex;
align-items: center;
width: 100%;
}
.dialect-map-legend-row-dot {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 50%;
margin-inline-end: 5px;
}
.dialect-map-legend-label {
flex-grow: 1;
}
.dialect-map-legend-row .vsToggleElement {
cursor: pointer;
font-size: 0.8em;
margin-inline-start: auto;
color: var(--wikt-palette-blue);
white-space: nowrap;
}
.dialect-map-legend-row .vsHide {
width: 100%;
margin-top: 5px;
}
.dialect-map-legend-row .vsHide ul {
margin: 0;
padding-inline-start: 20px;
}
.dialect-map-legend-row--highlighted {
background: var(--wikt-palette-dulllighterblue);
}
.dialect-map-legend-row--nested {
margin-inline-start: 20px;
}
.dialect-map-group-header .dialect-map-legend-label {
font-weight: bold;
}
.dialect-map-legend-terms--collapsed {
display: none;
}
.dialect-map-legend-locs-toggle {
cursor: pointer;
margin-inline-start: 5px;
font-size: 0.9em;
color: var(--wikt-palette-blue);
}
.dialect-map-legend-locs--collapsed {
display: none;
}
.dialect-map-legend-locs--toplevel {
padding-inline-start: 20px;
}
.dialect-map-legend-locs--nested {
padding-inline-start: 15px;
}
.dialect-map-legend-locs-list {
list-style-type: none;
padding: 0;
margin: 5px 0;
}
.dialect-map-legend-locs-item {
font-size: 0.9em;
color: var(--wikt-palette-darkgrey);
}
.dialect-map-group-header {
cursor: pointer;
}
.dialect-map-toggle-icon {
display: inline-block;
margin-inline-end: 5px;
font-size: 0.8em;
transition: transform 0.2s;
}
[dir="rtl"] .dialect-map-custom-popup .leaflet-popup-content {
text-align: center;
direction: rtl;
}
.dialect-map-app {
position: relative;
width: 100%;
}
.dialect-map-placeholder {
width: 100%;
height: 600px;
display: flex;
align-items: center;
justify-content: center;
background-color: #f8f9fa;
border: 1px solid #c8ccd1;
color: #72777d;
}
.dialect-map-placeholder--missing-config {
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
padding: 1em;
overflow: auto;
text-align: left;
}
.dialect-map-placeholder pre {
background: #fff;
border: 1px solid #c8ccd1;
padding: 0.75em;
white-space: pre;
overflow-x: auto;
max-width: 100%;
box-sizing: border-box;
}
64lmurkiblqcdkn6wfdqclm4pccgrem
မဳဒဳယာဝဳကဳ:Gadget-AggregateInterprojectLinks.js
8
295757
396587
2026-06-07T20:30:10Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "/* ** Generates a sidebar list of links to other projects from {{pedia}}, etc. ** <nowiki> */ $(function () { // Broken on Minerva... if (mw.config.values.skin === "minerva") return; var spans = $('span.interProject a:first-child').clone(); if (spans.length === 0) return; // sort alphabetically function sortbylabel(a, b) { // get labels a = $(a).text(); b = $(b).text(); // return sort order if (a < b..."
396587
javascript
text/javascript
/*
** Generates a sidebar list of links to other projects from {{pedia}}, etc.
** <nowiki>
*/
$(function () {
// Broken on Minerva...
if (mw.config.values.skin === "minerva") return;
var spans = $('span.interProject a:first-child').clone();
if (spans.length === 0) return;
// sort alphabetically
function sortbylabel(a, b) {
// get labels
a = $(a).text();
b = $(b).text();
// return sort order
if (a < b) return -1;
if (a > b) return 1;
return 0;
}
spans.sort(sortbylabel);
var isVector2022 = mw.config.get('skin') === 'vector-2022';
var newLinksWrapper = $('<div>', {
'class': 'pBody body vector-menu-content',
'style': 'display:block'
});
if (isVector2022)
newLinksWrapper.addClass('mw-portlet mw-portlet-projects');
$(spans).wrap('<li class="mw-list-item">').parent()
.appendTo($('<ul class="vector-menu-content-list">')).parent()
.appendTo(newLinksWrapper);
var collapsed = mw.cookie.get("vector-nav-p-projects") == "false";
var projectBox = $('<div>', {
addClass: 'vector-menu vector-menu-portal portlet portal ' + (collapsed ? "collapsed" : "expanded"),
id: 'p-projects'
})
.append($(isVector2022
? '<div id="p-projects-label" class="vector-menu-heading">In other projects</div>'
: '<label id="p-projects-label" aria-label="" class="vector-menu-heading"><span class="vector-menu-heading-label">In other projects</span></label>'))
.append(newLinksWrapper);
$('#p-tb').after(projectBox);
});
//</nowiki>
4mxst2h6pxj1o22mlp5bicxzmqi368j
математика
0
295758
396588
2026-06-07T21:01:37Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "==ဗူလ်ဂရဳယာန်== {{wp|bg:}} ===နိရုတ်=== ဝေါဟာကၠုၚ်နူ {{bor|bg|la|mathēmatica}}၊ နူကဵုဝေါဟာ {{der|bg|grc|μᾰθημᾰτῐκός}} ===ဗွဟ်ရမ္သာၚ်=== {{bg-pr|матема́тика|a=[KK]}} ===နာမ်=== {{bg-noun|матема́тика|f|adj=математи́чен|adj2=математи́чески}} # သ..."
396588
wikitext
text/x-wiki
==ဗူလ်ဂရဳယာန်==
{{wp|bg:}}
===နိရုတ်===
ဝေါဟာကၠုၚ်နူ {{bor|bg|la|mathēmatica}}၊ နူကဵုဝေါဟာ {{der|bg|grc|μᾰθημᾰτῐκός}}
===ဗွဟ်ရမ္သာၚ်===
{{bg-pr|матема́тика|a=[KK]}}
===နာမ်===
{{bg-noun|матема́тика|f|adj=математи́чен|adj2=математи́чески}}
# သၚ်္ချာ။
# လိက်အုပ်သၚ်္ချာမွဲအုပ်။
=====လဟုတ်စှ်ေ=====
{{bg-ndecl|матема́тика</n:sg>}}
==ကဇက်==
{{kk-alt}}
{{wp|kk:}}
===နိရုတ်===
ဝေါဟာကၠုၚ်နူ {{bor|kk|ru|матема́тика}}၊ နူကဵုဝေါဟာ {{der|kk|pl|matematyka}}၊ နကဵုအဆက်နူ {{der|kk|la|mathēmatica}}၊ နူကဵုဝေါဟာ {{der|kk|grc|μαθηματικός}}
===ဗွဟ်ရမ္သာၚ်===
* {{IPA|kk|[mətʲɪˈmatʲɪkə]}}
===နာမ်===
{{kk-noun}}
# သၚ်္ချာ။
==မက်သဳဒဝ်နဳယျာ==
{{wp|mk:}}
===ဗွဟ်ရမ္သာၚ်===
* {{mk-IPA}}
* {{audio|mk|LL-Q9296 (mkd)-Bjankuloski06-математика.wav}}
===နာမ်===
{{mk-noun|f|adj=математички}}
# သၚ်္ချာ။
=====လဟုတ်စှ်ေ=====
{{mk-decl-noun-f|математик|n=sg}}
==ရုဿျှာ==
{{wp|ru:}}
===ဗွဟ်ရမ္သာၚ်===
* {{ru-IPA|матема́тика}}
* {{audio|ru|Ru-математика.ogg}}
===နိရုတ်===
{{bor+|ru|pl|matematyka}}
===နာမ်===
{{ru-noun+|матема́тика|adj=математи́ческий|hypocor=мате́ша}}
# သၚ်္ချာ။
=====လဟုတ်စှ်ေ=====
{{ru-noun-table|матема́тика}}
===မဒုၚ်လွဳစ===
* {{desc|kk|математика|bor=1}}
* {{desc|mn|математик|bor=1}}
* {{desc|tg|математика|bor=1}}
* {{desc|ug|ماتېماتىكا|bor=1}}
===နာမ် ၂ ===
{{head|ru|noun form|head=матема́тика|g=m-an}}
# {{inflection of|ru|матема́тик||gen//acc|s}}
==သာဗ်ခြဝ်ဨရှဳယာန်==
{{wp|sh:,sr:}}
===နိရုတ်===
{{bor+|sh|la|mathematica}}
===နာမ်===
{{sh-noun|матема̀тика|f}}
# သၚ်္ချာ။
=====လဟုတ်စှ်ေ=====
{{sh-decl-noun
|математика|математике
|математике|математика
|математици|математикама
|математику|математике
|математико|математике
|математици|математикама
|математиком|математикама
}}
==တာဇိက်==
{{fa-regional|ریاضیات|ریاضیات|математика|mathematics}}
===နိရုတ်===
{{bor+|tg|ru|матема́тика}}
===ဗွဟ်ရမ္သာၚ်===
{{tg-IPA}}
===နာမ်===
{{tg-noun}}
# သၚ်္ချာ။
#: {{syn|tg|риёзиёт|шуморнома|шумор}}
==ယူကရိန်==
{{wp|uk:}}
===ဗွဟ်ရမ္သာၚ်===
* {{uk-IPA|матема́тика}}
* {{audio|uk|Uk-математика.ogg}}
===နိရုတ်===
ဝေါဟာကၠုၚ်နူ {{der|uk|la|mathēmatica}}
===နာမ်===
{{uk-noun|матема́тика<sg>}}
# သၚ်္ချာ။
=====လဟုတ်စှ်ေ=====
{{uk-ndecl|матема́тика<sg>}}
===နာမ် ၂ ===
{{head|uk|nf|head=матема́тика|g=m-pr}}
# {{infl of|uk|матема́тик||gen//acc|s}}
qk7qsrkby9xi3pvmrnga59irq2ayyd3
ထာမ်ပလိက်:bg-ndecl
10
295759
396590
2026-06-07T21:05:30Z
咽頭べさ
33
ခၞံကၠောန်လဝ် မုက်လိက် နကု "{{#invoke:bg-nominal|show_noun}}<!-- --><noinclude>{{documentation}}</noinclude>"
396590
wikitext
text/x-wiki
{{#invoke:bg-nominal|show_noun}}<!--
--><noinclude>{{documentation}}</noinclude>
gtfeulk2jcc2a8fvesmxt6h66hgfdcj