Мазмунга өтүү

Модуль:parameters

Wiktionary долбоорунан

Экспорттолгон функциялар

[түзөтүү]

Бул модул аргументтерди иштеп чыгууну жана текшерүүнү стандартташтыруу үчүн колдонулат. Кадимки иш процесси төмөнкүдөй (негизделген Module:translations боюнча):

	...
	local parent_args = frame:getParent().args

	local params = {
		[1] = {required = true, type = "language", default = "und"},
		[2] = true,
		[3] = {list = true},
		["alt"] = true,
		["id"] = true,
		["sc"] = {type = "script"},
		["tr"] = true,
		["ts"] = true,
		["lit"] = true,
	}

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

	-- Do further processing of the parsed arguments in <code>args</code>.
	...

Params таблицасында ачкыч катары параметр аттары жана параметр тегдеринин (мүмкүн бош) таблицасы болушу керек. баалуулук. Бош таблица маани катары жөн гана параметр бар экенин айтат, бирок эч кандай өзгөчөлүктү кабыл албашы керек дарылоо; кааласа, бош таблицаларды аткарууну оптималдаштыруу катары "true" мааниси менен алмаштырса болот.

Мүмкүн болгон параметр тэгдери төмөндө келтирилген:

required = true
параметр талап кылынат; эгерде ал жок болсо, ката көрсөтүлөт. Калыптын баракчасынын өзү өзгөчө; жок ката ошол жерде көрсөтүлөт.
default =
Эгерде ал жок же бош болсо, параметр үчүн демейки киргизүү маанисин белгилейт. Бул болгон сыяктуу иштетилет анын ордуна киргизүү, ошондуктан (мисалы) default = "und" түрү менен "language" тил объектисин кайтарат Белгисиз тил эгер тил коду берилбесе. Тизмеде колдонулганда параметрлер, бул тизмедеги биринчи нерсе үчүн гана демейки маанини көрсөтөт. мүмкүн эмес экенин белгилей кетүү керек башка параметрлердин маанисине жараша демейки түзүү. required = true менен бирге колдонулса, демейки шаблон баракчаларына гана тиешелүү (кийинки жазууну караңыз), "талап кылынган" параметрлердин терс таасири катары шаблон беттеринде талап кылынбайт. Бул шаблондун үлгүсүн көрсөтүү үчүн колдонулушу мүмкүн шаблон барагына кирди; бирок түшүнүктүү болуу үчүн бул максат үчүн template_default колдонуу артык.
template_default =
Калып беттеринде гана жок же бош параметрлер үчүн демейки киргизүү маанисин белгилейт. Үлгү барактар - бул каалаган барак шаблон мейкиндиги ('Шаблон:' менен башталат) документация барактарынан тышкары ('.../documentation' менен аяктаган). Бул шаблон барагына киргенде талап кылынбаган параметр үчүн мисал маанисин берүү үчүн колдонулушу мүмкүн. шаблондун башка колдонулушуна тоскоолдук кылуу. template_default жана default экөө тең бирдей көрсөтүлүшү мүмкүн параметр. Бул жасалса, template_default шаблон беттеринде, ал эми демейки башка барактарда колдонулат. Мисал катары, {{cs-IPA}} демейки менен камсыз кылуу үчүн [1] = {демейки = "+", template_default = "příklad"} эквивалентин колдонот Негизги мейкиндик жана документация беттери үчүн "+" (модулга |pagename= маанисин колдонууну айтат параметр, анык беттин атына кайтып келет), бирок "příklad" (бул "мисалы" дегенди билдирет), Калып:cs-IPAда.
alias_of =
Параметрди башканын лакап аты катары караңыз. Бул параметр үчүн аргументтер көрсөтүлгөндө, алар автоматтык түрдө болот аталышы өзгөртүлүп, лакап ат менен сакталат. Бул дагы эле бир нече альтернативалуу аттары бар параметрлерге мүмкүндүк берет аларга бир гана аты бар сыяктуу мамиле кылуу. Каймана ат коюлган параметрдин конверсияга байланышкан касиеттери (мис. `түр', set, convert, sublist) лакап аттан алынган жана тиешелүү касиеттер лакап аттын өзүнө коюлган этибарга алынбайт; бирок лакап аттын башка касиеттери лакап аттын спецификациясынан эмес, лакап аттын спецификациясынан алынат. Бул мисалы, эгер сиз тизме параметринин лакап атын түзсөңүз, лакап ат "тизме" касиетин да көрсөтүшү керек дегенди билдирет. же бул тизме эмес. (Мындай учурда, лакап ат үчүн көрсөтүлгөн маани лакап аттын тизмесинин биринчи пунктуна кирет. Сиз тизмеден тышкары параметрдин лакап атын жасай албайсыз; бул катаны ыргытууга алып келет.) Ушундай эле, эгер сиз белгилесеңиз 'separate_no_index' лакап атта эмес, бирок лакап атта эмес, индекстелбеген псевдоним параметринин колдонулушу лакап атта сакталат. .default ачкычы, бирок индекстелбеген лакап аттын колдонулушу лакап аттын тизмесинин биринчи номерленген ачкычында сакталат. Лакап аттарды талап кылуу мүмкүн эмес, анткени бул параметрдин башка аталышын же атын колдонууга жол бербейт. Параметрлер лакап аттар жана ошол эле учурда талап кылынган каталар ыргытылат.
allow_empty = true
Эгерде аргумент бош сап мааниси болсо, ал nil дегенге айландырылбайт, бирок ошол бойдон сакталат. allow_empty колдонуу болуп саналат бир түрү көрсөтүлгөн болсо, тыюу салынат жана ката ыргытылат.
no_trim = true
Позициялык параметрдин башындагы жана аягындагы боштуктар жана жаңы сызыктар сыяктуу интервал символдору алынып салынбайт. (MediaWiki өзү аталган параметрлердин четиндеги боштуктарды жана жаңы сызыктарды автоматтык түрдө кыскартат.) no_trim колдонулушу бир түрү көрсөтүлгөн болсо, тыюу салынат жана ката ыргытылат.
type =
Аргументти кандай маани түрүнө айландырууну белгилейт. Демейки - аны текст сап катары калтыруу. Альтернативалар:
type = "boolean"
Маани логикалык маани катары каралат, чын же жалган. Маани жок, бош сап жана саптар "0", "no", "n", "false", "f" and "off" are treated as false, all other values are considered true.
type = "number"
The value is converted into a number, and throws an error if the value is not parsable as a number. Input values may be signed (+ or -), and may contain decimal points and leading zeroes. If allow_hex = true, then hexadecimal values in the form "0x100" may optionally be used instead, which otherwise have the same syntax restrictions (including signs, decimal digits, and leading zeroes after "0x"). Hexadecimal inputs are not case-sensitive. Lua's special number values (inf and nan) are not possible inputs.
type = "language"
The value is interpreted as a full or etymology-only language code language code (or name, if method = "name") and converted into the corresponding object (see Module:languages). If the code or name is invalid, then an error is thrown. The additional setting family = true can be given to allow language family codes to be considered valid and the corresponding object returned. Note that to distinguish an etymology-only language object from a full language object, use object:hasType("language", "etymology-only").
type = "full language"
The value is interpreted as a full language code (or name, if method = "name") and converted into the corresponding object (see Module:languages). If the code or name is invalid, then an error is thrown. Etymology-only languages are not allowed. The additional setting family = true can be given to allow language family codes to be considered valid and the corresponding object returned.
type = "Wikimedia language"
The value is interpreted as a code and converted into a Wikimedia language object. If the code is invalid, then an error is thrown. If fallback = true is specified, conventional language codes which are different from their Wikimedia equivalent will also be accepted as a fallback.
type = "family"
The value is interpreted as a language family code (or name, if method = "name") and converted into the corresponding object (see Module:families). If the code or name is invalid, then an error is thrown.
type = "script"
The value is interpreted as a script code (or name, if method = "name") and converted into the corresponding object (see Module:scripts). If the code or name is invalid, then an error is thrown.
type = "title"
The value is interpreted as a page title and converted into the corresponding object (see the Title library). If the page title is invalid, then an error is thrown; by default, external titles (i.e. those on other wikis) are not treated as valid. Options are:
namespace = n
The default namespace, where n is a namespace number; this is treated as 0 (the mainspace) if not specified.
allow_external = true
External titles are treated as valid.
prefix = "namespace override" (default)
The default namespace prefix will be prefixed to the value is already prefixed by a namespace prefix. For instance, the input "Foo" with namespace 10 returns "Template:Foo", "Wiktionary:Foo" returns "Wiktionary:Foo", and "Template:Foo" returns "Template:Foo". Interwiki prefixes cannot act as overrides, however: the input "fr:Foo" returns "Template:fr:Foo".
prefix = "force"
The default namespace prefix will be prefixed unconditionally, even if the value already appears to be prefixed. This is the way that {{[[Template:#invoke:|#invoke:]]}} works when calling modules from the module namespace (828): the input "Foo" returns "Module:Foo", "Wiktionary:Foo" returns "Module:Wiktionary:Foo", and "Module:Foo" returns "Module:Module:Foo".
prefix = "full override"
The same as prefix = "namespace override", except that interwiki prefixes can also act as overrides. For instance, "el:All topics" with namespace 14 returns "el:Category:All topics". Due to the limitations of MediaWiki, only the first prefix in the value may act as an override, so the namespace cannot be overridden if the first prefix is an interwiki prefix: e.g. "el:Template:All topics" with namespace 14 returns "el:Category:Template:All topics".
type = "parameter"
The value is interpreted as the name of a parameter, and will be normalized using the method that Scribunto uses when constructing a frame.args table of arguments. This means that integers will be converted to numbers, but all other arguments will remain as strings (e.g. "1" will be normalized to 1, but "foo" and "1.5" will remain unchanged). Note that Scribunto also trims parmeter names, following the same trimming method that this module applies by default to all parameter types.
This type is useful when one set of input arguments is used to construct a params table for use in a subsequent export.process() call with another set of input arguments; for instance, the set of valid parameters for a template might be defined as {{[[Template:#invoke:[some module]|#invoke:[some module]]]}} in the template, where args is a sublist of valid parameters for the template.
type = "qualifier"
The value is interpreted as a qualifier and converted into the correct format for passing into format_qualifiers() in Module:qualifier (which currently just means converting it to a one-item list).
type = "labels"
The value is interpreted as a comma-separated list of labels and converted into the correct format for passing into show_labels() in Module:labels (which is currently a list of strings). Splitting is done on commas not followed by whitespace, except that commas inside of double angle brackets do not count even if not followed by whitespace. This type should be used by for normal labels (typically specified using |l= or |ll=) and accent qualifiers (typically specified using |a= and |aa=).
type = "references"
The value is interpreted as one or more references, in the format prescribed by parse_references() in Module:references, and converted into a list of objects of the form accepted by format_references() in the same module. If a syntax error is found in the reference format, an error is thrown.
type = function(val) ... end
type may be set to a function (or callable table), which must take the argument value as its sole argument, and must output one of the other recognized types. This is particularly useful for lists (see below), where certain values need to be interpreted differently to others.
list =
Treat the parameter as a list of values, each having its own parameter name, rather than a single value. The parameters will have a number at the end, except optionally for the first (but see also require_index = true). For example, list = true on a parameter named "head" will include the parameters |head= (or |head1=), |head2=, |head3= and so on. If the parameter name is a number, another number doesn't get appended, but the counting simply continues, e.g. for parameter 3 the sequence is |3=, |4=, |5= etc. List parameters are returned as numbered lists, so for a template that is given the parameters |head=a|head2=b|head3=c, the processed value of the parameter "head" will be { "a", "b", "c" }}.
The value for list = can also be a string. This tells the module that parameters other than the first should have a different name, which is useful when the first parameter in a list is a number, but the remainder is named. An example would be for genders: list = "g" on a parameter named 1 would have parameters |1=, |g2=, |g3= etc.
If the number is not located at the end, it can be specified by putting "\1" at the number position. For example, parameters |f1accel=, |f2accel=, ... can be captured by using the parameter name "f\1accel", as is done in Module:headword/templates.
set =
Параметрдин мааниси көрсөтүлгөн маанилердин тизмесинин бири болушун талап кылуу (же талап = чындык болбосо, алынып салынды. берилген). Көрсөтүлгөн тизмедеги маанилер чийки параметрдин маанилерине дал келген саптар болушу керек, учурдан тышкары type = "number", бул учурда алар сандар болушу керек. Тизмедеги жеке маани лакап ат тизмеси да болушу мүмкүн, бул тизме, анда биринчи маани "канондук" маани, ал эми калгандары лакап аттар. Качан лакап аттардын бири колдонулса, кайтарылган аргументтердин структурасында алынган параметр талаасы канондук мааниге ээ болот. колдонуу Эгерде type = "boolean" жана ката ыргытылышына алып келсе, set уруксат берилбейт.
sublist =
Параметрдин мааниси өзүнчө чийки маанилердин бөлгүч менен бөлүнгөн тизмеси. args ичинде пайда болгон талаа болот айландырылган маанилердин Луа тизмеси (б.а. сандык индекстери бар таблица) болуңуз. Эгер sublist = true берилсе, маанилер үтүргө бөлүнөт (мүмкүн үтүрдүн бир же эки тарабында бош боштук болушу мүмкүн, ал этибарга алынбайт). Эгерде кошумча тизме = "боштуксуз үтүр" берилген, маанилер үтүрлөргө бөлүнөт, алардан кийин боштук жок, жана алардын алдында качуучу арткы сызык жок. Болбосо, "кошумча тизменин" мааниси же Луа үлгүсү болушу керек бөлүү үчүн бөлүүчүнү (ларды) же бөлүү үчүн функцияны (же чакыра турган таблицаны) көрсөтүү, ага эки маани берилген (бөлүнүүчү маани жана ката жөнүндө сигнал берүүчү функция) жана бөлүнгөн маанилердин тизмесин кайтарышы керек.
convert =
Эгер берилсе, бул чийки параметр маанисин колдонулган Lua объектисине айландыруу үчүн функцияны (же чакыра турган таблицаны) көрсөтөт. андан ары кайра иштетүү учурунда. Функцияга эки аргумент берилет, чийки параметр маанисинин өзү жана колдонулган функция талдоо же конвертациялоо учурунда катаны билдирет жана бир маанини, айландырылган параметрди кайтарышы керек. Ката сигнализациясы Функция генерациялаган билдирүүгө камтылган параметрдин атын жана чийки маанисин камтыйт, андыктан булардын кереги жок ага жиберилген билдирүүдө көрсөтүлгөн. Эгерде "type" "конверттөө" менен бирге көрсөтүлсө, иштетүү тип биринчи болот. Эгерде "sublist" "convert" менен бирге берилсе, чийки параметрдин мааниси бөлүнөт ылайыктуу жана ар бир натыйжада пунктка чакырылган "конверттөө".
allow_hex = true
type = "number" менен бирге колдонулганда, "0x100" форматында он алтылык сандарды киргизүүгө уруксат берет (бул тамга сезгич эмес).
family = true
type = "language" менен бирге колдонулганда, тил үй-бүлө коддору кайтып келди. Берилген объект тил үй-бүлөсүнө тиешелүү экендигин текшерүү үчүн object:hasType("family") колдонуңуз.
method = "name"
type = "language", type = "family" же type = "script" менен бирге колдонулганда, текшерет жана талдайт коддун ордуна тил, үй-бүлө же скрипт аты.
allow_holes = true
Бул тизме тибиндеги параметрлер менен бирге колдонулат. Демейки боюнча, баалуулуктар натыйжада тыгыз топтолот тизме. Бул, мисалы, |head2= эмес, head=a|head3=c деген жазуу көрсөтүлсө, кайтарылган тизме Калып:"a", "c"} болот, маанилер 1 жана 3 эмес, 1 жана 2 индекстеринде сакталат. Эгерде аны сактоо кааласа номерлөө бузулбаган, мисалы, бир нече тизме параметрлеринин сандары бири-бири менен корреляцияланган болсо (мисалы, {{affix}}), анда бул тег көрсөтүлүшү керек.
Эгер allow_holes = true берилсе, эки чыныгы маанинин ортосунда nil маанилер болушу мүмкүн, бул Луанын көбүн түзөт # же ipairs() сыяктуу таблицаны иштетүү функциялары иштебей калды. Муну оңдоо үчүн, пайда болгон таблицада ан камтылат кошумча аталган маани, maxindex, ал сизге таблицадагы эң жогорку сандык индексти билдирет. Ичинде Жогорудагы мисал, натыйжада таблица эми Калып:"a", nil, "c", maxindex = 3} болот. Ушундай жол менен, сиз кайталай аласыз 1ден maxindexге чейинки маанилер, алардын ортосунда nil маанилерди өткөрүп жиберүү.
disallow_holes = true
Бул тизме тибиндеги параметрлер менен бирге колдонулат. Жогоруда айтылгандай, адатта, булакта тешик бар болсо аргументтер, мис. head=a|head3=c бирок |head2= эмес, ал кайтарылган тизмеден алынып салынат. Эгерде disallow_holes = true көрсөтүлгөн, бирок мындай учурда ката кетирилет. Бул ошол жерде колдонулушу керек тизилиши керек болгон бир нече тизме тибиндеги параметрлер (мисалы, |head= жана |tr= экөө тең жеткиликтүү жана |head3= |tr3= менен сапталат), эгерде allow_holes = true берилбесе жана сиз чечүүгө даярсыз кайтарылган тизмелерде тешиктер.
disallow_missing = true
Бул disallow_holes = true дегенге окшош, бирок аргумент бош болсо, ката кетирилбейт. толугу менен жок. Бул кээде тизмеде пайда болгон аралык бош сандык параметрлерди көтөрүү үчүн колдонулушу мүмкүн шаблондор. Мисалы, head=a|head2=|head3=c ката кетирбейт, бирок head=a|head3=c ката кетирет.
require_index = true
Бул тизме тибиндеги параметрлер менен бирге колдонулат. Демейки боюнча, биринчи параметр анын индексин өткөрүп жибериши мүмкүн. Мисалы, head деп аталган тизме параметринин биринчи параметри |head= же |head1=. Эгер require_index = true көрсөтүлсө, бирок |head1= гана таанылат жана |head= белгисиз параметр катары каралат. {{affixusex}} (жана варианттары {{suffixusex}}, {{prefixusex}}) колдонуңуз бул, мисалы, бардык тизме параметрлери боюнча.
separate_no_index = true
Бул |head= менен |head1= башка параметрлерди айырмалоо үчүн колдонулат. Мисалы, в {{affixusex}}, |sc= (usex тилиндеги бардык элементтер үчүн скрипт коду) жана |sc1= (биринчи элементтин скрипт коду, биринчи элементке тил коду менен префикс коюлганда колдонулат башка тилде экенин көрсөтүп турат). Бул колдонулганда, пайда болгон таблица кошумча аталышты камтыйт индекссиз аргументтин маанисин камтыган "демейки" маани.
demo = true
Бул параметр шаблондун өз бетинде гана иштетилгенин камсыз кылуунун бир жолу катары колдонулат (жана анын документтери бет) жана Колдонуучуда: аттар мейкиндиги; антпесе, ал белгисиз параметр катары каралат. Бул учурда гана колдонулушу керек шаблонду анын документтеринде көрсөтүү үчүн атайын орнотуулар талап кылынат (мисалы, беттин атын тууралоо же өчүрүү категориялар). Көпчүлүк учурларда, муну демо параметрлерди колдонбостон жасоо мүмкүн болушу керек, бирок алар болушу мүмкүн шаблон/документация бетинде ошол эле шаблондун реалдуу колдонулушу да камтылган болсо (мис., {{shortcut}}), талап кылынат. аларды айырмалоонун бир жолу катары.

export.convert_val

[түзөтүү]

function export.convert_val(val, name, param)

Өткөрүлгөн params таблицасында тизмеленген тиешелүү спецификацияларга ылайык параметрдин маанисин айландырыңыз Модуль:parameters. val аты name (ката билдирүүлөрүндө гана колдонулат) болгон параметр үчүн конвертациялануучу маани. param - спецификация (параметр үчүн params таблицасынын маани бөлүгү). Параметрдин аталышындагы өтүүнүн ордуна, name ката кетирүүчү функция болушу мүмкүн, параметр аты жана мааниси менен бирге көрсөтүлгөн билдирүүнү көрсөтөт. Бул функция 'param' ичиндеги бардык конверсияга байланыштуу талааларды иштетет, анын ичинде 'түр', 'коюу', 'кошумча тизме', 'конверттөө', ж.б. Бул конверттелген маанини кайтарат.

export.process

[түзөтүү]

function export.process(args, params, return_unknown)

Process arguments with a given list of parameters. Return a table containing the processed arguments. The args parameter specifies the arguments to be processed; they are the arguments you might retrieve from frame:getParent().args (the template arguments) or in some cases frame.args (the invocation arguments). The params parameter specifies a list of valid parameters, and consists of a table. If an argument is encountered that is not in the parameter table, an error is thrown.

The structure of the params table is as described above in the intro comment.

WARNING: The params table is destructively modified to save memory. Nonetheless, different keys can share the same value objects in memory without causing problems.

The return_unknown parameter, if set to true, prevents the function from triggering an error when it comes across an argument with a name that it doesn't recognise. Instead, the return value is a pair of values: the first is the processed arguments as usual, while the second contains all the unrecognised arguments that were left unprocessed. This allows you to do multi-stage processing, where the entire set of arguments that a template should accept is not known at once. For example, an inflection-table might do some generic processing on some arguments, but then defer processing of the remainder to the function that handles a specific inflectional type.


--[==[TODO:
* Change certain flag names, as some are misnomers:
	* Change `allow_holes` to `keep_holes`, because it's not the inverse of `disallow_holes`.
	* Change `allow_empty` to `keep_empty`, as it causes them to be kept as "" instead of deleted.
* Sort out all the internal error calls. Manual error(format()) calls are used when certain parameters shouldn't be dumped, so find a way to avoid that.
]==]

local export = {}

local debug_track_module = "Module:debug/track"
local families_module = "Module:families"
local function_module = "Module:fun"
local labels_module = "Module:labels"
local languages_module = "Module:languages"
local math_module = "Module:math"
local pages_module = "Module:pages"
local parse_utilities_module = "Module:parse utilities"
local references_module = "Module:references"
local scripts_module = "Module:scripts"
local string_utilities_module = "Module:string utilities"
local table_module = "Module:table"
local wikimedia_languages_module = "Module:wikimedia languages"
local yesno_module = "Module:yesno"

local mw = mw
local mw_title = mw.title
local string = string
local table = table

local dump = mw.dumpObject
local find = string.find
local format = string.format
local gmatch = string.gmatch
local gsub = string.gsub
local insert = table.insert
local ipairs = ipairs
local list_to_text = mw.text.listToText
local make_title = mw_title.makeTitle
local match = string.match
local max = math.max
local maxn = table.maxn
local new_title = mw_title.new
local next = next
local pairs = pairs
local pcall = pcall
local rawset = rawset
local require = require
local sort = table.sort
local sub = string.sub
local tonumber = tonumber
local traceback = debug.traceback
local type = type

local current_title_text, current_namespace -- Керек болгондо аныкталат.
local namespaces = mw.site.namespaces

--[==[
Башка модулдардагы функциялар үчүн жүктөгүчтөр, алар чакырганда максаттуу функциянын үстүнөн жазылат. Бул модулдардын керек болгондо гана жүктөлүшүн камсыздайт, локалдык түрдө жарыяланган алдын ала жүктөлгөн функциялардын ылдамдыгын/ынгайлуулугун сактап калат жана биринчи чалуудан кийин эч кандай кошумча чыгым болбойт, анткени максаттуу функциялар ар кандай кийинки чалууларда түздөн-түз чакырылат.]==]
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 extend(...)
	extend = require(table_module).extend
	return extend(...)
end

local function get_family_by_code(...)
	get_family_by_code = require(families_module).getByCode
	return get_family_by_code(...)
end

local function get_family_by_name(...)
	get_family_by_name = require(families_module).getByCanonicalName
	return get_family_by_name(...)
end

local function get_language_by_code(...)
	get_language_by_code = require(languages_module).getByCode
	return get_language_by_code(...)
end

local function get_language_by_name(...)
	get_language_by_name = require(languages_module).getByCanonicalName
	return get_language_by_name(...)
end

local function get_script_by_code(...)
	get_script_by_code = require(scripts_module).getByCode
	return get_script_by_code(...)
end

local function get_script_by_name(...)
	get_script_by_name = require(scripts_module).getByCanonicalName
	return get_script_by_name(...)
end

local function get_wm_lang_by_code(...)
	get_wm_lang_by_code = require(wikimedia_languages_module).getByCode
	return get_wm_lang_by_code(...)
end

local function get_wm_lang_by_code_with_fallback(...)
	get_wm_lang_by_code_with_fallback = require(wikimedia_languages_module).getByCodeWithFallback
	return get_wm_lang_by_code_with_fallback(...)
end

local function gsplit(...)
	gsplit = require(string_utilities_module).gsplit
	return gsplit(...)
end

local function is_callable(...)
	is_callable = require(function_module).is_callable
	return is_callable(...)
end

local function is_finite_real_number(...)
	is_finite_real_number = require(math_module).is_finite_real_number
	return is_finite_real_number(...)
end

local function is_integer(...)
	is_integer = require(math_module).is_integer
	return is_integer(...)
end

local function is_internal_title(...)
	is_internal_title = require(pages_module).is_internal_title
	return is_internal_title(...)
end

local function is_positive_integer(...)
	is_positive_integer = require(math_module).is_positive_integer
	return is_positive_integer(...)
end

local function iterate_list(...)
	iterate_list = require(table_module).iterateList
	return iterate_list(...)
end

local function num_keys(...)
	num_keys = require(table_module).numKeys
	return num_keys(...)
end

local function parse_references(...)
	parse_references = require(references_module).parse_references
	return parse_references(...)
end

local function pattern_escape(...)
	pattern_escape = require(string_utilities_module).pattern_escape
	return pattern_escape(...)
end

local function php_trim(...)
	php_trim = require(string_utilities_module).php_trim
	return php_trim(...)
end

local function scribunto_param_key(...)
	scribunto_param_key = require(string_utilities_module).scribunto_param_key
	return scribunto_param_key(...)
end

local function sorted_pairs(...)
	sorted_pairs = require(table_module).sortedPairs
	return sorted_pairs(...)
end

local function split(...)
	split = require(string_utilities_module).split
	return split(...)
end

local function split_labels_on_comma(...)
	split_labels_on_comma = require(labels_module).split_labels_on_comma
	return split_labels_on_comma(...)
end

local function split_on_comma(...)
	split_on_comma = require(parse_utilities_module).split_on_comma
	return split_on_comma(...)
end

local function yesno(...)
	yesno = require(yesno_module)
	return yesno(...)
end

--[==[ intro:
Бул модул аргументтерди иштеп чыгууну жана текшерүүнү стандартташтыруу үчүн колдонулат. Кадимки иш процесси төмөнкүдөй (негизделген [[Module:translations]] боюнча):

{
	...
	local parent_args = frame:getParent().args

	local params = {
		[1] = {required = true, type = "language", default = "und"},
		[2] = true,
		[3] = {list = true},
		["alt"] = true,
		["id"] = true,
		["sc"] = {type = "script"},
		["tr"] = true,
		["ts"] = true,
		["lit"] = true,
	}

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

	-- Do further processing of the parsed arguments in `args`.
	...
}

`Params` таблицасында ачкыч катары параметр аттары жана параметр тегдеринин (мүмкүн бош) таблицасы болушу керек.
баалуулук. Бош таблица маани катары жөн гана параметр бар экенин айтат, бирок эч кандай өзгөчөлүктү кабыл албашы керек
дарылоо; кааласа, бош таблицаларды аткарууну оптималдаштыруу катары "true" мааниси менен алмаштырса болот.

Мүмкүн болгон параметр тэгдери төмөндө келтирилген:

; {required = true}
: параметр талап кылынат; эгерде ал жок болсо, ката көрсөтүлөт. Калыптын баракчасынын өзү өзгөчө; жок
  ката ошол жерде көрсөтүлөт.
; {default =}
: Эгерде ал жок же бош болсо, параметр үчүн демейки киргизүү маанисин белгилейт. Бул болгон сыяктуу иштетилет
  анын ордуна киргизүү, ошондуктан (мисалы) {default = "und"} түрү менен {"language"} тил объектисин кайтарат
  [[:Категория:Белгисиз тил|Белгисиз тил]] эгер тил коду берилбесе. Тизмеде колдонулганда
  параметрлер, бул тизмедеги биринчи нерсе үчүн гана демейки маанини көрсөтөт. мүмкүн эмес экенин белгилей кетүү керек
  башка параметрлердин маанисине жараша демейки түзүү. {required = true} менен бирге колдонулса, демейки
  шаблон баракчаларына гана тиешелүү (кийинки жазууну караңыз), "талап кылынган" параметрлердин терс таасири катары
  шаблон беттеринде талап кылынбайт. Бул шаблондун үлгүсүн көрсөтүү үчүн колдонулушу мүмкүн
  шаблон барагына кирди; бирок түшүнүктүү болуу үчүн бул максат үчүн `template_default` колдонуу артык.
; {template_default =}
: Калып беттеринде гана жок же бош параметрлер үчүн демейки киргизүү маанисин белгилейт. Үлгү барактар - бул каалаган барак
  шаблон мейкиндиги ('Шаблон:' менен башталат) документация барактарынан тышкары ('.../documentation' менен аяктаган).
  Бул шаблон барагына киргенде талап кылынбаган параметр үчүн мисал маанисин берүү үчүн колдонулушу мүмкүн.
  шаблондун башка колдонулушуна тоскоолдук кылуу. `template_default` жана `default` экөө тең бирдей көрсөтүлүшү мүмкүн
  параметр. Бул жасалса, `template_default` шаблон беттеринде, ал эми `демейки` башка барактарда колдонулат. Мисал катары,
  {{tl|cs-IPA}} демейки менен камсыз кылуу үчүн {[1] = {демейки = "+", template_default = "příklad"}} эквивалентин колдонот
  Негизги мейкиндик жана документация беттери үчүн {"+"} (модулга {{para|pagename}} маанисин колдонууну айтат
  параметр, анык беттин атына кайтып келет), бирок {"příklad"} (бул "мисалы" дегенди билдирет), [[Калып:cs-IPA]]да.
; {alias_of =}
: Параметрди башканын лакап аты катары караңыз. Бул параметр үчүн аргументтер көрсөтүлгөндө, алар автоматтык түрдө болот
  аталышы өзгөртүлүп, лакап ат менен сакталат. Бул дагы эле бир нече альтернативалуу аттары бар параметрлерге мүмкүндүк берет
  аларга бир гана аты бар сыяктуу мамиле кылуу. Каймана ат коюлган параметрдин конверсияга байланышкан касиеттери (мис. `түр',
  `set`, `convert`, `sublist`) лакап аттан алынган жана тиешелүү касиеттер лакап аттын өзүнө коюлган
  этибарга алынбайт; бирок лакап аттын башка касиеттери лакап аттын спецификациясынан эмес, лакап аттын спецификациясынан алынат. Бул
  мисалы, эгер сиз тизме параметринин лакап атын түзсөңүз, лакап ат "тизме" касиетин да көрсөтүшү керек дегенди билдирет.
  же бул тизме эмес. (Мындай учурда, лакап ат үчүн көрсөтүлгөн маани лакап аттын тизмесинин биринчи пунктуна кирет.
  Сиз тизмеден тышкары параметрдин лакап атын жасай албайсыз; бул катаны ыргытууга алып келет.) Ушундай эле, эгер сиз белгилесеңиз
  'separate_no_index' лакап атта эмес, бирок лакап атта эмес, индекстелбеген псевдоним параметринин колдонулушу лакап атта сакталат.
  `.default` ачкычы, бирок индекстелбеген лакап аттын колдонулушу лакап аттын тизмесинин биринчи номерленген ачкычында сакталат.
  Лакап аттарды талап кылуу мүмкүн эмес, анткени бул параметрдин башка аталышын же атын колдонууга жол бербейт. Параметрлер
  лакап аттар жана ошол эле учурда талап кылынган каталар ыргытылат.
; {allow_empty = true}
: Эгерде аргумент бош сап мааниси болсо, ал {nil} дегенге айландырылбайт, бирок ошол бойдон сакталат. `allow_empty` колдонуу болуп саналат
  бир түрү көрсөтүлгөн болсо, тыюу салынат жана ката ыргытылат.
; {no_trim = true}
: Позициялык параметрдин башындагы жана аягындагы боштуктар жана жаңы сызыктар сыяктуу интервал символдору алынып салынбайт.
  (MediaWiki өзү аталган параметрлердин четиндеги боштуктарды жана жаңы сызыктарды автоматтык түрдө кыскартат.) `no_trim` колдонулушу
  бир түрү көрсөтүлгөн болсо, тыюу салынат жана ката ыргытылат.
; {type =}
: Аргументти кандай маани түрүнө айландырууну белгилейт. Демейки - аны текст сап катары калтыруу. Альтернативалар:
:; {type = "boolean"}
:: Маани логикалык маани катары каралат, чын же жалган. Маани жок, бош сап жана саптар {"0"},
   {"no"}, {"n"}, {"false"}, {"f"} and {"off"} are treated as {false}, all other values are considered {true}.
:; {type = "number"}
:: The value is converted into a number, and throws an error if the value is not parsable as a number. Input values may
   be signed (`+` or `-`), and may contain decimal points and leading zeroes. If {allow_hex = true}, then hexadecimal
   values in the form {"0x100"} may optionally be used instead, which otherwise have the same syntax restrictions
   (including signs, decimal digits, and leading zeroes after {"0x"}). Hexadecimal inputs are not case-sensitive. Lua's
   special number values (`inf` and `nan`) are not possible inputs.
:; {type = "language"}
:: The value is interpreted as a full or [[Wiktionary:Languages#Etymology-only languages|etymology-only language]] code
   language code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:languages]]).
   If the code or name is invalid, then an error is thrown. The additional setting {family = true} can be given to allow
   [[Wiktionary:Language families|language family codes]] to be considered valid and the corresponding object returned.
   Note that to distinguish an etymology-only language object from a full language object, use
   {object:hasType("language", "etymology-only")}.
:; {type = "full language"}
:: The value is interpreted as a full language code (or name, if {method = "name"}) and converted into the corresponding
   object (see [[Module:languages]]). If the code or name is invalid, then an error is thrown. Etymology-only languages
   are not allowed. The additional setting {family = true} can be given to allow
   [[Wiktionary:Language families|language family codes]] to be considered valid and the corresponding object returned.
:; {type = "Wikimedia language"}
:: The value is interpreted as a code and converted into a Wikimedia language object. If the code is invalid, then an
   error is thrown. If {fallback = true} is specified, conventional language codes which are different from their
   Wikimedia equivalent will also be accepted as a fallback.
:; {type = "family"}
:: The value is interpreted as a language family code (or name, if {method = "name"}) and converted into the
   corresponding object (see [[Module:families]]). If the code or name is invalid, then an error is thrown.
:; {type = "script"}
:: The value is interpreted as a script code (or name, if {method = "name"}) and converted into the corresponding object
   (see [[Module:scripts]]). If the code or name is invalid, then an error is thrown.
:; {type = "title"}
:: The value is interpreted as a page title and converted into the corresponding object (see the
   [[mw:Extension:Scribunto/Lua_reference_manual#Title_library|Title library]]). If the page title is invalid, then an
   error is thrown; by default, external titles (i.e. those on other wikis) are not treated as valid. Options are:
::; {namespace = n}
::: The default namespace, where {n} is a namespace number; this is treated as {0} (the mainspace) if not specified.
::; {allow_external = true}
::: External titles are treated as valid.
::; {prefix = "namespace override"} (default)
::: The default namespace prefix will be prefixed to the value is already prefixed by a namespace prefix. For instance,
    the input {"Foo"} with namespace {10} returns {"Template:Foo"}, {"Wiktionary:Foo"} returns {"Wiktionary:Foo"}, and
    {"Template:Foo"} returns {"Template:Foo"}. Interwiki prefixes cannot act as overrides, however: the input {"fr:Foo"}
    returns {"Template:fr:Foo"}.
::; {prefix = "force"}
::: The default namespace prefix will be prefixed unconditionally, even if the value already appears to be prefixed.
    This is the way that {{tl|#invoke:}} works when calling modules from the module namespace ({828}): the input {"Foo"}
    returns {"Module:Foo"}, {"Wiktionary:Foo"} returns {"Module:Wiktionary:Foo"}, and {"Module:Foo"} returns
    {"Module:Module:Foo"}.
::; {prefix = "full override"}
::: The same as {prefix = "namespace override"}, except that interwiki prefixes can also act as overrides. For instance,
    {"el:All topics"} with namespace {14} returns {"el:Category:All topics"}. Due to the limitations of MediaWiki, only
    the first prefix in the value may act as an override, so the namespace cannot be overridden if the first prefix is
    an interwiki prefix: e.g. {"el:Template:All topics"} with namespace {14} returns {"el:Category:Template:All topics"}.
:; {type = "parameter"}
:: The value is interpreted as the name of a parameter, and will be normalized using the method that Scribunto uses when
   constructing a {frame.args} table of arguments. This means that integers will be converted to numbers, but all other
   arguments will remain as strings (e.g. {"1"} will be normalized to {1}, but {"foo"} and {"1.5"} will remain
   unchanged). Note that Scribunto also trims parmeter names, following the same trimming method that this module
   applies by default to all parameter types.
:: This type is useful when one set of input arguments is used to construct a {params} table for use in a subsequent
   {export.process()} call with another set of input arguments; for instance, the set of valid parameters for a template
   might be defined as {{tl|#invoke:[some module]|args=}} in the template, where {args} is a sublist of valid parameters
   for the template.
:; {type = "qualifier"}
:: The value is interpreted as a qualifier and converted into the correct format for passing into `format_qualifiers()`
   in [[Module:qualifier]] (which currently just means converting it to a one-item list).
:; {type = "labels"}
:: The value is interpreted as a comma-separated list of labels and converted into the correct format for passing into
   `show_labels()` in [[Module:labels]] (which is currently a list of strings). Splitting is done on commas not followed
   by whitespace, except that commas inside of double angle brackets do not count even if not followed by whitespace.
   This type should be used by for normal labels (typically specified using {{para|l}} or {{para|ll}}) and accent
   qualifiers (typically specified using {{para|a}} and {{para|aa}}).
:; {type = "references"}
:: The value is interpreted as one or more references, in the format prescribed by `parse_references()` in
   [[Module:references]], and converted into a list of objects of the form accepted by `format_references()` in the same
   module. If a syntax error is found in the reference format, an error is thrown.
:; {type = function(val) ... end}
:: `type` may be set to a function (or callable table), which must take the argument value as its sole argument, and must
   output one of the other recognized types. This is particularly useful for lists (see below), where certain values need
   to be interpreted differently to others.
; {list =}
: Treat the parameter as a list of values, each having its own parameter name, rather than a single value. The
  parameters will have a number at the end, except optionally for the first (but see also {require_index = true}). For
  example, {list = true} on a parameter named "head" will include the parameters {{para|head}} (or {{para|head1}}),
  {{para|head2}}, {{para|head3}} and so on. If the parameter name is a number, another number doesn't get appended, but
  the counting simply continues, e.g. for parameter {3} the sequence is {{para|3}}, {{para|4}}, {{para|5}} etc. List
  parameters are returned as numbered lists, so for a template that is given the parameters `|head=a|head2=b|head3=c`,
  the processed value of the parameter {"head"} will be { { "a", "b", "c" }}}.
: The value for {list =} can also be a string. This tells the module that parameters other than the first should have a
  different name, which is useful when the first parameter in a list is a number, but the remainder is named. An example
  would be for genders: {list = "g"} on a parameter named {1} would have parameters {{para|1}}, {{para|g2}}, {{para|g3}}
  etc.
: If the number is not located at the end, it can be specified by putting {"\1"} at the number position. For example,
  parameters {{para|f1accel}}, {{para|f2accel}}, ... can be captured by using the parameter name {"f\1accel"}, as is
  done in [[Module:headword/templates]].
; {set =}
: Параметрдин мааниси көрсөтүлгөн маанилердин тизмесинин бири болушун талап кылуу (же {талап = чындык} болбосо, алынып салынды.
  берилген). Көрсөтүлгөн тизмедеги маанилер чийки параметрдин маанилерине дал келген саптар болушу керек, учурдан тышкары
  {type = "number"}, бул учурда алар сандар болушу керек. Тизмедеги жеке маани ''лакап ат тизмеси'' да болушу мүмкүн,
  бул тизме, анда биринчи маани "канондук" маани, ал эми калгандары лакап аттар. Качан лакап аттардын бири
  колдонулса, кайтарылган аргументтердин структурасында алынган параметр талаасы канондук мааниге ээ болот. колдонуу
  Эгерде {type = "boolean"} жана ката ыргытылышына алып келсе, `set` уруксат берилбейт.
; {sublist =}
: Параметрдин мааниси өзүнчө чийки маанилердин бөлгүч менен бөлүнгөн тизмеси. `args` ичинде пайда болгон талаа болот
  айландырылган маанилердин Луа тизмеси (б.а. сандык индекстери бар таблица) болуңуз. Эгер {sublist = true} берилсе, маанилер
  үтүргө бөлүнөт (мүмкүн үтүрдүн бир же эки тарабында бош боштук болушу мүмкүн, ал этибарга алынбайт). Эгерде
  {кошумча тизме = "боштуксуз үтүр"} берилген, маанилер үтүрлөргө бөлүнөт, алардан кийин боштук жок,
  жана алардын алдында качуучу арткы сызык жок. Болбосо, "кошумча тизменин" мааниси же Луа үлгүсү болушу керек
  бөлүү үчүн бөлүүчүнү (ларды) же бөлүү үчүн функцияны (же чакыра турган таблицаны) көрсөтүү, ага эки маани берилген
  (бөлүнүүчү маани жана ката жөнүндө сигнал берүүчү функция) жана бөлүнгөн маанилердин тизмесин кайтарышы керек.
; {convert =}
: Эгер берилсе, бул чийки параметр маанисин колдонулган Lua объектисине айландыруу үчүн функцияны (же чакыра турган таблицаны) көрсөтөт.
  андан ары кайра иштетүү учурунда. Функцияга эки аргумент берилет, чийки параметр маанисинин өзү жана колдонулган функция
  талдоо же конвертациялоо учурунда катаны билдирет жана бир маанини, айландырылган параметрди кайтарышы керек. Ката сигнализациясы
  Функция генерациялаган билдирүүгө камтылган параметрдин атын жана чийки маанисин камтыйт, андыктан булардын кереги жок
  ага жиберилген билдирүүдө көрсөтүлгөн. Эгерде "type" "конверттөө" менен бирге көрсөтүлсө, иштетүү
  `тип` биринчи болот. Эгерде "sublist" "convert" менен бирге берилсе, чийки параметрдин мааниси бөлүнөт
  ылайыктуу жана ар бир натыйжада пунктка чакырылган "конверттөө".
; {allow_hex = true}
: {type = "number"} менен бирге колдонулганда, {"0x100"} форматында он алтылык сандарды киргизүүгө уруксат берет (бул
  тамга сезгич эмес).
; {family = true}
: {type = "language"} менен бирге колдонулганда, [[Wiktionary:Language family|тил үй-бүлө коддору]]
  кайтып келди. Берилген объект тил үй-бүлөсүнө тиешелүү экендигин текшерүү үчүн {object:hasType("family")} колдонуңуз.
; {method = "name"}
: {type = "language"}, {type = "family"} же {type = "script"} менен бирге колдонулганда, текшерет жана талдайт
  коддун ордуна тил, үй-бүлө же скрипт аты.
; {allow_holes = true}
: Бул тизме тибиндеги параметрлер менен бирге колдонулат. Демейки боюнча, баалуулуктар натыйжада тыгыз топтолот
  тизме. Бул, мисалы, {{para|head2}} эмес, `head=a|head3=c` деген жазуу көрсөтүлсө, кайтарылган тизме
  {{"a", "c"}}} болот, маанилер {1} жана {3} эмес, {1} жана {2} индекстеринде сакталат. Эгерде аны сактоо кааласа
  номерлөө бузулбаган, мисалы, бир нече тизме параметрлеринин сандары бири-бири менен корреляцияланган болсо (мисалы,
  {{tl|affix}}), анда бул тег көрсөтүлүшү керек.
: Эгер {allow_holes = true} берилсе, эки чыныгы маанинин ортосунда {nil} маанилер болушу мүмкүн, бул Луанын көбүн түзөт
  {#} же {ipairs()} сыяктуу таблицаны иштетүү функциялары иштебей калды. Муну оңдоо үчүн, пайда болгон таблицада ан камтылат
  кошумча аталган маани, `maxindex`, ал сизге таблицадагы эң жогорку сандык индексти билдирет. Ичинде
  Жогорудагы мисал, натыйжада таблица эми {{"a", nil, "c", maxindex = 3}}} болот. Ушундай жол менен, сиз кайталай аласыз
 {1}ден `maxindex`ге чейинки маанилер, алардын ортосунда {nil} маанилерди өткөрүп жиберүү.
; {disallow_holes = true}
: Бул тизме тибиндеги параметрлер менен бирге колдонулат. Жогоруда айтылгандай, адатта, булакта тешик бар болсо
  аргументтер, мис. `head=a|head3=c` бирок {{para|head2}} эмес, ал кайтарылган тизмеден алынып салынат. Эгерде
  {disallow_holes = true} көрсөтүлгөн, бирок мындай учурда ката кетирилет. Бул ошол жерде колдонулушу керек
  тизилиши керек болгон бир нече тизме тибиндеги параметрлер (мисалы, {{para|head}} жана {{para|tr}} экөө тең жеткиликтүү жана
  {{para|head3}} {{para|tr3}} менен сапталат), эгерде {allow_holes = true} берилбесе жана сиз чечүүгө даярсыз
  кайтарылган тизмелерде тешиктер.
; {disallow_missing = true}
: Бул {disallow_holes = true} дегенге окшош, бирок аргумент бош болсо, ката кетирилбейт.
  толугу менен жок. Бул кээде тизмеде пайда болгон аралык бош сандык параметрлерди көтөрүү үчүн колдонулушу мүмкүн
  шаблондор. Мисалы, `head=a|head2=|head3=c` ката кетирбейт, бирок `head=a|head3=c` ката кетирет.
; {require_index = true}
: Бул тизме тибиндеги параметрлер менен бирге колдонулат. Демейки боюнча, биринчи параметр анын индексин өткөрүп жибериши мүмкүн.
  Мисалы, `head` деп аталган тизме параметринин биринчи параметри {{para|head}} же
  {{para|head1}}. Эгер {require_index = true} көрсөтүлсө, бирок {{para|head1}} гана таанылат жана {{para|head}}
  белгисиз параметр катары каралат. {{tl|affixusex}} (жана варианттары {{tl|suffixusex}}, {{tl|prefixusex}}) колдонуңуз
  бул, мисалы, бардык тизме параметрлери боюнча.
; {separate_no_index = true}
: Бул {{para|head}} менен {{para|head1}} башка параметрлерди айырмалоо үчүн колдонулат. Мисалы, в
  {{tl|affixusex}}, {{para|sc}} (usex тилиндеги бардык элементтер үчүн скрипт коду) жана
  {{para|sc1}} (биринчи элементтин скрипт коду, биринчи элементке тил коду менен префикс коюлганда колдонулат
  башка тилде экенин көрсөтүп турат). Бул колдонулганда, пайда болгон таблица кошумча аталышты камтыйт
  индекссиз аргументтин маанисин камтыган "демейки" маани.
; {demo = true}
: Бул параметр шаблондун өз бетинде гана иштетилгенин камсыз кылуунун бир жолу катары колдонулат (жана анын документтери
  бет) жана Колдонуучуда: аттар мейкиндиги; антпесе, ал белгисиз параметр катары каралат. Бул учурда гана колдонулушу керек
  шаблонду анын документтеринде көрсөтүү үчүн атайын орнотуулар талап кылынат (мисалы, беттин атын тууралоо же өчүрүү
  категориялар). Көпчүлүк учурларда, муну демо параметрлерди колдонбостон жасоо мүмкүн болушу керек, бирок алар болушу мүмкүн
  шаблон/документация бетинде ошол эле шаблондун реалдуу колдонулушу да камтылган болсо (мис., {{tl|shortcut}}), талап кылынат.
  аларды айырмалоонун бир жолу катары.
]==]

-- Учурдагы барак учурдагы {{#invoke}} камтылган шаблон же модуль болсо, чындыкты кайтарат.
-- Documentation_documentation аргументи берилсе, учурдагы барак же барактын документация барагы болсо, чындыкты кайтарат.
local own_page, own_page_or_documentation
local function is_own_page(include_documentation)
	if own_page == nil then
		if current_namespace == nil then
			local current_title = mw_title.getCurrentTitle()
			current_title_text, current_namespace = current_title.prefixedText, current_title.namespace
		end
		local frame = current_namespace == 828 and mw.getCurrentFrame() or
			current_namespace == 10 and mw.getCurrentFrame():getParent()
		if frame then
			local frame_title_text = frame:getTitle()
			own_page = current_title_text == frame_title_text
			own_page_or_documentation = own_page or current_title_text == frame_title_text .. "/documentation"
		else
			own_page, own_page_or_documentation = false, false
		end
	end
	return include_documentation and own_page_or_documentation or own_page
end

local function track(page)
	local pages, current = {"parameters/" .. page}
	-- Check through the traceback to get the calling module and function.
	for mod, func in gmatch(traceback(), "%f[^%z\n]\tModule:(.-):%d+: in function '(.-)'%f[%z\n]") do
		if current == nil then
			current = mod -- Name of this module.
		elseif mod ~= current then
			insert(pages, "parameters/" .. page .. "/" .. mod)
			-- FIXME: эгерде чакыруучу функция #invoke: тарабынан чакырылган функция болсо, кайра артка кайтаруу аны чыныгы аталышынын ордуна "chunk" деп атайт.
			insert(pages, "parameters/" .. page .. "/" .. mod .. "/" .. func)
			break
		end
	end
	debug_track(pages)
end

-------------------------------------- Some helper functions -----------------------------

-- Convert a list in `list` to a string, separating the final element from the preceding one(s) by `conjunction`. If
-- `dump_vals` is given, pass all values in `list` through mw.dumpObject() (WARNING: this destructively modifies
-- `list`). This is similar to serialCommaJoin() in [[Module:table]] when used with the `dontTag = true` option, but
-- internally uses mw.text.listToText().
local function concat_list(list, conjunction, dump_vals)
	if dump_vals then
		for k, v in pairs(list) do
			list[k] = dump(v)
		end
	end
	return list_to_text(list, nil, conjunction)
end

-- Split an argument on comma, but not comma followed by whitespace.
local function split_on_comma_without_whitespace(val)
	if find(val, "\\", nil, true) or match(val, ",%s") then
		return split_on_comma(val)
	end
	return split(val, ",")
end

-- A helper function for use with generating error-signaling functions in the presence of raw value conversion. Format a
-- message `msg`, including the processed value `processed` if it is different from the raw value `rawval`; otherwise,
-- just return `msg`.
local function msg_with_processed(msg, rawval, processed)
	if rawval == processed then
		return msg
	end
	local processed_type = type(processed)
	return format("%s (processed value %s)",
		msg, (processed_type == "string" or processed_type == "number") and processed or dump(processed)
	)
end

-------------------------------------- Error handling -----------------------------

local function process_error(fmt, ...)
	local args = {...}
	for i, val in ipairs(args) do
		args[i] = dump(val)
	end
	if type(fmt) == "table" then
		-- hacky signal that we're called from internal_process_error(), and not to omit stack frames
		return error(format(fmt[1], unpack(args)))
	end
	return error(format(fmt, unpack(args)), 3)
end

local function internal_process_error(fmt, ...)
	process_error({"Internal error in `params` table: " .. fmt}, ...)
end

-- Check that a parameter or argument is in the form form Scribunto normalizes input argument keys into (e.g. 1 not "1", "foo" not " foo "). Otherwise, it won't be possible to normalize inputs in the expected way. Unless is_argument is set, also check that the name only contains one placeholder at most, and that strings don't resolve to numeric keys once the placeholder has been substituted.
local function validate_name(name, desc, extra_name, is_argument)
	local normalized = scribunto_param_key(name)
	if name and name == normalized then
		if is_argument or type(name) ~= "string" then
			return
		end
		local placeholder = find(name, "\1", nil, true)
		if not placeholder then
			return
		elseif find(name, "\1", placeholder + 1, true) then
			error(format(
				"Internal error: expected %s to only contain one placeholder, but saw %s",
				extra_name and (desc .. dump(extra_name)) or desc, dump(name)
			))
		end
		local first_name = gsub(name, "\1", "1")
		normalized = scribunto_param_key(first_name)
		if first_name == normalized then
			return
		end
		error(format(
			"Internal error: %s cannot resolve to numeric parameters once any placeholder has been substituted, but %s resolves to %s",
			extra_name and (desc .. dump(extra_name)) or desc, dump(name), dump(normalized)
		))
	elseif normalized == nil then
		error(format(
			"Internal error: expected %s to be of type string or number, but saw %s",
			extra_name and (desc .. dump(extra_name)) or desc, type(name)
		))
	end
	error(format(
		"Internal error: expected %s to be Scribunto-compatible: %s (a %s) should be %s (a %s)",
		extra_name and (desc .. dump(extra_name)) or desc, dump(name), type(name), dump(normalized), type(normalized)
	))
end

-- TODO: give ranges instead of long lists, if possible.
local function params_list_error(params, msg)
	local list, n = {}, 0
	for name in sorted_pairs(params) do
		n = n + 1
		list[n] = name
	end
	error(format(
		"Parameter%s %s.",
		format(n == 1 and " %s is" or "s %s are", concat_list(list, " and ", true)),
		msg
	), 3)
end

-- convert_val_error() менен колдонуу үчүн жардамчы функция. `concat_list` жана колдонуу менен мүмкүн болгон тандоолордун тизмесин форматтаңыз
-- бирден көп болсо, тандоолордун алдында "же" дегенди көрсөтүүчү "же" байланышы.
local function format_choice_list(valid)
	return (#valid > 1 and "either " or "") .. concat_list(valid, " or ")
end

-- Туура эмес "valid" маанисине ката сигнал берүү (ал түрүн көрсөткөн сап, же
-- "коюу" колдонулган учурда мүмкүн болгон маанилердин тизмеси). `name` параметрдин аты жана а болушу мүмкүн
-- катаны белгилөө функциясы (ал параметрдин атын жана маанисин автоматтык түрдө көрсөтөт деп болжолдонот). `seetext` болуп саналат
-- көрсөтүү үчүн кошумча кошумча түшүндүрүүчү шилтеме (мисалы, [[WT:LOL]], мүмкүн болгон тилдердин жана коддордун тизмеси).
local function convert_val_error(val, name, valid, seetext)
	if is_callable(name) then
		if type(valid) == "table" then
			valid = "choice, must be " .. format_choice_list(valid)
		end
		name(format("Invalid %s; the value %s is not valid%s", valid, val, seetext and "; see " .. seetext or ""))
	else
		if type(valid) == "table" then
			valid = format_choice_list(valid)
		else
			valid = "a valid " .. valid
		end
		error(format("Parameter %s must be %s; the value %s is not valid.%s", dump(name), valid, dump(val),
			seetext and " See " .. seetext .. "." or ""))
	end
end

-- Параметрдин мааниси `val` жана `name` аталышы берилген тиешелүү ката-сигнал функциясын жаратыңыз. Эгерде `аты` мурунтан эле
-- функция, ал жөн гана кайтарылган; антпесе, берилген билдирүүнү көрсөткөн функция түзүлөт жана кайтарылат
-- параметрдин аты жана мааниси менен бирге.
local function make_parse_err(val, name)
	if is_callable(name) then
		return name
	end
	return function(msg)
		error(format("%s: parameter %s=%s", msg, name, val))
	end
end

-------------------------------------- Наркы конверсия -----------------------------

-- Тизме параметринин "аты" жана "тизме" талаасынын тиешелүү "тизме_аты" мааниси үчүн (бир эле мааниге ээ болушу керек)
-- "ат" катары, эгерде "тизме = чындык" берилген болсо), тизменин параметрлерине дал келүү үчүн үлгү түзүңүз жана үлгү катары сактаңыз
-- "үлгү" баскычын басыңыз, тиешелүү маани "аты" деп коюлган. Мисалы, "тизме_аты" "tr" болсо, үлгү болот
-- "tr" менен "tr1", "tr2", ..., "tr10", "tr11" ж.б. дал келет. Эгерде `тизме_аты` ичинде \1 болсо, сан
-- бөлүгү \1 ордуна келет. Мисалы, "тизме_аты" "f\1accel" болсо, үлгү "faccel" менен дал келет.
-- "f1accel", "f2accel" ж.б. `name` ичиндеги бардык \1 `үлгүлөргө` сактоодон мурун алынып салынат.
local function save_pattern(name, list_name, patterns)
	name = type(name) == "string" and gsub(name, "\1", "") or name
	if find(list_name, "\1", nil, true) then
		patterns["^" .. gsub(pattern_escape(list_name), "\1", "([1-9]%%d*)") .. "$"] = name
	else
		patterns["^" .. pattern_escape(list_name) .. "([1-9]%d*)$"] = name
		list_name = list_name .. "\1"
	end
	validate_name(list_name, "the list field of parameter ", name)
	return patterns
end

-- `sublist` менен колдонуу үчүн жардамчы функция. Бул бөлүүнү кайтарган for циклинде колдонуу үчүн итератор функциясы
-- `sublist` колдонулган `val` элементтери (Lua бөлүү үлгүсү; бульдик `true` үтүрлөргө бөлүү үчүн ыктыярдуу түрдө тегереги менен курчалган
-- боштук; "Боштуксуз үтүр" үтүрлөргө гана бөлүнөт, андан кийин бошотулбаган боштуктар
-- тескери сызык менен; же бөлүү үчүн функция, ага эки маани берилет, бөлүнүүчү маани жана функция
-- ката сигналы жана бөлүнгөн элементтердин тизмесин кайтарышы керек). `name` - бул параметрдин аты же ката сигналы
-- функция convert_val()га өткөрүлдү.
local function split_sublist(val, name, sublist)
	if sublist == true then
		return gsplit(val, "%s*,%s*")
	elseif sublist == "comma without whitespace" then
		sublist = split_on_comma_without_whitespace
	elseif type(sublist) == "string" then
		return gsplit(val, sublist)
	elseif not is_callable(sublist) then
		error(format('Internal error: expected `sublist` to be of type "string" or "function" or boolean `true`, but saw %s', dump(sublist)))
	end
	return iterate_list(sublist(val, make_parse_err(val, name)))
end

-- `val` мааниси бар `name` деп аталган параметр жана параметр спецификациясы `param` үчүн, эгерде `set` талаасы көрсөтүлгөн болсо,
-- маани `set`те көрсөтүлгөндөрдүн бири болуп саналат жана башка учурда ката кетирет. `name` түз жерден алынат
-- ылайыктуу параметр convert_val()га өтүп, катаны белгилөөчү функция болушу мүмкүн. Кошумча `param_type` болуп саналат
-- `val` конверсия түрүн көрсөткөн сап жана атайын каптоо үчүн колдонулат: Эгерде `param_type` "логикалык" болсо, анда
-- ички ката ыргытылды (анткени `set` логикалык маанилер менен бирге колдонулбайт) жана `param_type` "сан" болсо,
-- эч кандай текшерүү болбойт, анткени бул учурда `set` сандарды камтыйт жана сандарды өзгөртүү функциясынын ичинде текшерилет.
-- өзү, `val` санга айландыргандан кийин. `val`дын канондук маанисин кайтарыңыз (ал `val`дан башкача болушу мүмкүн
-- эгер лакап ат картасы берилсе).
local function check_set(val, name, param, param_type)
	if param_type == "boolean" then
		error(format('Internal error: Cannot use `set` with `type = "%s"`', param_type))
	elseif param_type == "number" then
		-- Өзгөчө жагдайлар керек, анткени текшерүү сандарга өткөндөн кийин болот.
		return val
	end
	local newval = param.set[val]
	if newval == nil then
		local list = {}
		for k, v in pairs(param.set) do
			if v == true then
				insert(list, dump(k))
			else
				insert(list, ("%s (alias of %s)"):format(dump(k), dump(v)))
			end
		end
		sort(list)
		-- Эгер параметр талап кылынбаса, анда тизменин аягына "же бош" деп коюңуз, параметр чындыгында талап кылынат дегенди билдирбеш үчүн.
		if not param.required then
			insert(list, "бош")
		end
		convert_val_error(val, name, list)
	end
	if newval == true then
		return val
	end
	return newval
end

local function convert_language(val, name, param, allow_etym)
	local method, func = param.method
	if method == nil or method == "code" then
		func, method = get_language_by_code, "code"
	elseif method == "name" then
		func, method = get_language_by_name, "name"
	else
		error(format('Ички ката: "тил" түрү үчүн күтүлгөн "ыкма" "код", "аты" же аныкталбаган, бирок %s көрдү', dump(method)))
	end
	local lang = func(val, nil, allow_etym, param.family)
	if lang then
		return lang
	end
	local list, links = {"language"}, {"[[WT:LOL]]"}
	if allow_etym then
		insert(list, "etymology language")
		insert(links, "[[WT:LOL/E]]")
	end
	if param.family then
		insert(list, "family")
		insert(links, "[[WT:LOF]]")
	end
	convert_val_error(val, name, concat_list(list, " or ") .. " " .. (method == "name" and "name" or "code"), concat_list(links, " and "))
end

-- TODO: validate parameter specs separately, as it's making the handler code really messy at the moment.
local type_handlers = setmetatable({
	["boolean"] = function(val)
		return yesno(val, true)
	end,

	["family"] = function(val, name, param)
		local method, func = param.method
		if method == nil or method == "code" then
			func, method = get_family_by_code, "code"
		elseif method == "name" then
			func, method = get_family_by_name, "name"
		else
			error(format('Internal error: expected `method` for type `family` to be "code", "name" or undefined, but saw %s', dump(method)))
		end
		return func(val) or convert_val_error(val, name, "family " .. method, "[[WT:LOF]]")
	end,

	["labels"] = function(val, name, param)
		-- FIXME: Should be able to pass in a parse_err function.
		return split_labels_on_comma(val)
	end,

	["language"] = function(val, name, param)
		return convert_language(val, name, param, true)
	end,

	["full language"] = function(val, name, param)
		return convert_language(val, name, param)
	end,

	["number"] = function(val, name, param)
		local allow_hex = param.allow_hex
		if allow_hex and allow_hex ~= true then
			error(format('Internal error: expected `allow_hex` for type `number` to be of type "boolean" or undefined, but saw %s', dump(allow_hex)))
		end
		local num = tonumber(val)
		-- Avoid converting inputs like "nan" or "inf", and disallow 0x hex inputs unless explicitly enabled
		-- with `allow_hex`.
		if not (num and is_finite_real_number(num) and (allow_hex or not match(val, "^[+-]?0[Xx]%x*%.?%x*$"))) then
			convert_val_error(val, name, (allow_hex and "decimal or hexadecimal " or "") .. "number")
		-- Track various unusual number inputs to determine if it should be restricted to positive integers by default (possibly including 0).
		elseif not is_positive_integer(num) then
			track("number not a positive integer")
			if num == 0 then
				track("number is 0")
			elseif not is_integer(num) then
				track("number not an integer")
			end
		end
		if param.set then
			-- Don't pass in "number" here; otherwise no checking will happen.
			num = check_set(num, name, param)
		end
		return num
	end,

	["parameter"] = function(val, name, param)
		-- Use the `no_trim` option, as any trimming will have already been done.
		return scribunto_param_key(val, true)
	end,

	["qualifier"] = function(val, name, param)
		return {val}
	end,

	["references"] = function(val, name, param)
		return parse_references(val, make_parse_err(val, name))
	end,

	["script"] = function(val, name, param)
		local method, func = param.method
		if method == nil or method == "code" then
			func, method = get_script_by_code, "code"
		elseif method == "name" then
			func, method = get_script_by_name, "name"
		else
			error(format('Internal error: expected `method` for type `script` to be "code", "name" or undefined, but saw %s', dump(method)))
		end
		return func(val) or convert_val_error(val, name, "script " .. method, "[[WT:LOS]]")
	end,

	["string"] = function(val, name, param) -- To be removed as unnecessary.
		track("string")
		return val
	end,

	-- TODO: add support for resolving to unsupported titles.
	-- TODO: split this into "page name" (i.e. internal) and "link target" (i.e. external as well), which is more intuitive.
	["title"] = function(val, name, param)
		local namespace = param.namespace
		if namespace == nil then
			namespace = 0
		else
			local valid_type = type(namespace) ~= "number" and 'of type "number" or undefined' or
				not namespaces[namespace] and "a valid namespace number" or
				nil
			if valid_type then
				error(format('Internal error: expected `namespace` for type `title` to be %s, but saw %s', valid_type, dump(namespace)))
			end
		end
		-- Decode entities. WARNING: mw.title.makeTitle must be called with `decoded` (as it doesn't decode) and mw.title.new must be called with `val` (as it does decode, so double-decoding needs to be avoided).
		local decoded, prefix, title = decode_entities(val), param.prefix
		-- If the input is a fragment, treat the title as the current title with the input fragment.
		if sub(decoded, 1, 1) == "#" then
			-- If prefix is "force", only get the current title if it's in the specified namespace. current_title includes the namespace prefix.
			if current_namespace == nil then
				local current_title = mw_title.getCurrentTitle()
				current_title_text, current_namespace = current_title.prefixedText, current_title.namespace
			end
			if not (prefix == "force" and namespace ~= current_namespace) then
				title = new_title(current_title_text .. val)
			end
		elseif prefix == "force" then
			-- Unconditionally add the namespace prefix (mw.title.makeTitle).
			title = make_title(namespace, decoded)
		elseif prefix == "full override" then
			-- The first input prefix will be used as an override (mw.title.new). This can be a namespace or interwiki prefix.
			title = new_title(val, namespace)
		elseif prefix == nil or prefix == "namespace override" then
			-- Only allow namespace prefixes to override. Interwiki prefixes therefore need to be treated as plaintext (e.g. "el:All topics" with namespace 14 returns "el:Category:All topics", but we want "Category:el:All topics" instead; if the former is really needed, then the input ":el:Category:All topics" will work, as the initial colon overrides the namespace). mw.title.new can take namespace names as well as numbers in the second argument, and will throw an error if the input isn't a valid namespace, so this can be used to determine if a prefix is for a namespace, since mw.title.new will return successfully only if there's either no prefix or the prefix is for a valid namespace (in which case we want the override).
			local success
			success, title = pcall(new_title, val, match(decoded, "^.-%f[:]") or namespace)
			-- Otherwise, get the title with mw.title.makeTitle, which unconditionally adds the namespace prefix, but behaves like mw.title.new if the namespace is 0.
			if not success then
				title = make_title(namespace, decoded)
			end
		else
			error(format('Internal error: expected `prefix` for type `title` to be "force", "full override", "namespace override" or undefined, but saw %s', dump(prefix)))
		end
		local allow_external = param.allow_external
		if allow_external == true then
			return title or convert_val_error(val, name, "Wiktionary or external page title")
		elseif not allow_external then
			return title and is_internal_title(title) and title or convert_val_error(val, name, "Wiktionary page title")
		end
		error(format('Internal error: expected `allow_external` for type `title` to be of type "boolean" or undefined, but saw %s', dump(allow_external)))
	end,

	["Wikimedia language"] = function(val, name, param)
		local fallback = param.fallback
		if fallback == true then
			return get_wm_lang_by_code_with_fallback(val) or convert_val_error(val, name, "Wikimedia language or language code")
		elseif not fallback then
			return get_wm_lang_by_code(val) or convert_val_error(val, name, "Wikimedia language code")
		end
		error(format('Internal error: expected `fallback` for type `Wikimedia language` to be of type "boolean" or undefined, but saw %s', dump(fallback)))
	end,
}, {
	-- TODO: decode HTML entities in all input values. Non-trivial to implement, because we need to avoid any downstream functions decoding the output from this module, which would be double-decoding. Note that "title" has this implemented already, and it needs to have both the raw input and the decoded input to avoid double-decoding by me.title.new, so any implementation can't be as simple as decoding in __call then passing the result to the handler.
	__call = function(self, val, name, param, param_type)
		local val_type = type(val)
		-- TODO: check this for all possible parameter types.
		if val_type == param_type then
			return val
		-- TODO: throw an internal error.
		elseif val_type ~= "string" then
			track("input is not string")
			track("input is not string/type handlers")
		end
		local func = self[param_type]
		if func == nil then
			error(format("Internal error: %s is not a recognized parameter type.", dump(param_type)))
		end
		return func(val, name, param)
	end
})

--[==[ func: export.convert_val(val, name, param)
Өткөрүлгөн `params` таблицасында тизмеленген тиешелүү спецификацияларга ылайык параметрдин маанисин айландырыңыз
[[Модуль:parameters]]. `val` аты `name` (ката билдирүүлөрүндө гана колдонулат) болгон параметр үчүн конвертациялануучу маани.
`param` - спецификация (параметр үчүн `params` таблицасынын маани бөлүгү). Параметрдин аталышындагы өтүүнүн ордуна,
`name` ката кетирүүчү функция болушу мүмкүн, параметр аты жана мааниси менен бирге көрсөтүлгөн билдирүүнү көрсөтөт.
Бул функция 'param' ичиндеги бардык конверсияга байланыштуу талааларды иштетет, анын ичинде 'түр', 'коюу', 'кошумча тизме', 'конверттөө',
ж.б. Бул конверттелген маанини кайтарат.
]==]
local function convert_val(val, name, param)
	local param_type = param.type or "string"
	-- Эгер param.type функция болсо, аны таанылган түргө чечиңиз.
	if is_callable(param_type) then
		param_type = param_type(val)
	end
	local sublist = param.sublist
	if sublist then
		local retlist = {}
		if type(val) ~= "string" then
			error(format("Ички ката: %s сап эмес.", dump(val)))
		end
		if param.convert then
			local thisval, insval
			local thisindex = 0
			local parse_err
			if is_callable(name) then
				-- Биз `name` ичинде өтүп кеткен ката функциясы параметрдин аталышын жана чийки маанини көрсөтүп турат деп ойлойбуз.
				function parse_err(msg)
					name(format("%s: пункт #%s=%s",
						msg_with_processed(msg, thisval, insval), thisindex, thisval)
					)
				end
			else
				function parse_err(msg)
					error(format("%s: пункт #%s=%s параметринин %s=%s",
						msg_with_processed(msg, thisval, insval), thisindex, thisval, name, val)
					)
				end
			end
			for v in split_sublist(val, name, sublist) do
				thisval = v
				thisindex = thisindex + 1
				if param.set then
					v = check_set(v, name, param, param_type)
				end
				insert(retlist, param.convert(type_handlers(v, name, param, param_type), parse_err))
			end
		else
			for v in split_sublist(val, name, sublist) do
				if param.set then
					v = check_set(v, name, param, param_type)
				end
				insert(retlist, type_handlers(v, name, param, param_type))
			end
		end
		return retlist
	else
		if param.set then
			val = check_set(val, name, param, param_type)
		end
		local retval = type_handlers(val, name, param, param_type)
		if param.convert then
			local parse_err
			if is_callable(name) then
				-- Биз `name` ичинде өтүп кеткен ката функциясы параметрдин аталышын жана чийки маанини көрсөтүп турат деп ойлойбуз.
				if retval == val then
					-- Бул жабууну түзбөө үчүн оптималдаштыруу. Экинчи кол да туура иштейт
					-- качан retval == val.
					parse_err = name
				else
					function parse_err(msg)
						name(msg_with_processed(msg, val, retval))
					end
				end
			else
				function parse_err(msg)
					error(format("%s: параметрдин %s=%s", msg_with_processed(msg, val, retval), name, val))
				end
			end
			retval = param.convert(retval, parse_err)
		end
		return retval
	end
end
export.convert_val = convert_val -- used by [[Module:parameter utilities]]

local function unknown_param(name, val, args_unknown)
	track("unknown parameters")
	args_unknown[name] = val
	return args_unknown
end

local function check_string_param_modifier(param_type, name, tag)
	if param_type and not (param_type == "string" or param_type == "parameter" or type(param_type) == "function") then
		internal_process_error(
			"%s cannot be set unless %s is set to %s (the default), %s or a function: parameter %s has the type %s.",
			tag, "type", "string", "параметрдин", name, param_type
		)
	end
end

local function hole_error(params, name, listname, this, nxt, extra)
	-- `process_error` calls `dump` on values to be inserted into
	-- error messages, but with numeric lists this causes "numeric"
	-- to look like the name of the list rather than a description,
	-- as `dump` adds quote marks. Insert it early to avoid this,
	-- but add another %s specifier in all other cases, so that
	-- actual list names will be displayed properly.
	local offset, specifier, starting_from = 0, "%s", ""
	local msg = "%s параметрлеринин тизмесиндеги %%d пункту берилиши керек, эгерде %%d пункту берилсе, анткени %s параметрлери жетишпей калгандыктан, % анда эч кандай боштук болбошу керек."
	local specs = {}
	if type(listname) == "string" then
		specs[2] = listname
	elseif type(name) == "number" then
		offset = name - 1 -- To get the original parameter.
		specifier = "numeric"
		-- If the list doesn't start at parameter 1, avoid implying
		-- there can't be any gaps in the numeric parameters if
		-- some parameter with a lower key is optional.
		for j = name - 1, 1, -1 do
			local _param = params[j]
			if not (_param and _param.required) then
				starting_from = format("(starting from parameter %d) ", dump(j + 1))
				break
			end
		end
	else
		specs[2] = name
	end
	specs[1] = this + offset -- Absolute index for this item.
	insert(specs, nxt + offset) -- Absolute index for the next item.
	process_error(format(msg, specifier, starting_from, extra or ""), unpack(specs))
end

local function check_disallow_holes(params, val, name, listname, extra)
	for i = 1, val.maxindex do
		if val[i] == nil then
			hole_error(params, name, listname, i, num_keys(val)[i], extra)
		end
	end
end

local function handle_holes(params, val, name)
	local param = params[name]
	local disallow_holes = param.disallow_holes
	-- Iterate up the list, and throw an error if a hole is found.
	if disallow_holes then
		check_disallow_holes(params, val, name, param.list, " or empty")
	end
	-- Iterate up the list, and throw an error if a hole is found due to a
	-- missing parameter, treating empty parameters as part of the list. This
	-- applies beyond maxindex if blank arguments are supplied beyond it, so
	-- isn't mutually exclusive with `disallow_holes`.
	local empty = val.empty
	if param.disallow_missing then
		if empty then
			-- Remove `empty` from `val`, so it doesn't get returned.
			val.empty = nil
			for i = 1, max(val.maxindex, maxn(empty)) do
				if val[i] == nil and not empty[i] then
					local keys = extend(num_keys(val), num_keys(empty))
					sort(keys)
					hole_error(params, name, param.list, i, keys[i])
				end
			end
		-- If there's no table of empty parameters, the check is identical to
		-- `disallow_holes`, except that the error message only refers to
		-- missing parameters, not missing or empty ones. If `disallow_holes` is
		-- also set, there's no point checking again.
		elseif not disallow_holes then
			check_disallow_holes(params, val, name, param.list)
		end
	end
	-- If `allow_holes` is set, there's nothing left to do.
	if param.allow_holes then
		return
	-- Otherwise, remove any holes: `pairs` won't work, as it's unsorted, and
	-- iterating from 1 to `maxindex` times out with inputs like |100000000000=,
	-- so use num_keys to get a list of numerical keys sorted from lowest to
	-- highest, then iterate up the list, moving each value in `val` to the
	-- lowest unused positive integer key. This also avoids the need to create a
	-- new table. If `disallow_holes` is specified, then there can't be any
	-- holes in the list, so there's no reason to check again; this doesn't
	-- apply to `disallow_missing`, however.
	elseif not disallow_holes then
		local keys, i = num_keys(val), 0
		while true do
			i = i + 1
			local key = keys[i]
			if key == nil then
				break
			elseif i ~= key then
				val[i], val[key] = val[key], nil
			end
		end
	end
	-- Some code depends on only numeric params being present when no holes are
	-- allowed (e.g. by checking for the presence of arguments using next()), so
	-- remove `maxindex`.
	val.maxindex = nil
end

-- If both `template_default` and `default` are given, `template_default` takes precedence, but only on the template or
-- module page. This means a different default can be specified for the template or module page example. However,
-- `template_default` doesn't apply if any args are set, which helps (somewhat) with examples on documentation pages
-- transcluded into the template page. HACK: We still run into problems on documentation pages transcluded into the
-- template page when pagename= is set. Check this on the assumption that pagename= is fairly standard.
local function convert_default_val(name, param, pagename_set, any_args_set)
	if not pagename_set then
		local val = param.template_default
		if val ~= nil and not any_args_set and is_own_page() then
			return convert_val(val, name, param)
		end
	end
	local val = param.default
	if val ~= nil then
		return convert_val(val, name, param)
	end
end


local function is_string_or_number(item)
	return type(item) == "string" or type(item) == "number"
end

local function list_to_alias_map(list)
	local set, i = {}, 0
	while true do
		i = i + 1
		local item = list[i]
		if item == nil then
			return set
		end
		if type(item) == "table" then
			local canon = item[1]
			if not is_string_or_number(canon) then
				internal_process_error("First element of list item #%s must be a string or number: %s", i, canon)
			end
			set[canon] = true
			local j = 1
			while true do
				j = j + 1
				local alias = item[j]
				if alias == nil then
					break
				end
				if not is_string_or_number(alias) then
					internal_process_error("Alias at position %s of canonical item %s at position %s must be a string or number: %s",
						j, canon, i, alias)
				end
				set[alias] = canon
			end
		else
			if not is_string_or_number(item) then
				internal_process_error("Alias map element #%s must be a string or number: %s", i, item)
			end
			set[item] = true
		end
	end
	return set
end

--[==[
Process arguments with a given list of parameters. Return a table containing the processed arguments. The `args`
parameter specifies the arguments to be processed; they are the arguments you might retrieve from
{frame:getParent().args} (the template arguments) or in some cases {frame.args} (the invocation arguments). The `params`
parameter specifies a list of valid parameters, and consists of a table. If an argument is encountered that is not in
the parameter table, an error is thrown.

The structure of the `params` table is as described above in the intro comment.

'''WARNING:''' The `params` table is destructively modified to save memory. Nonetheless, different keys can share the
same value objects in memory without causing problems.

The `return_unknown` parameter, if set to {true}, prevents the function from triggering an error when it comes across an
argument with a name that it doesn't recognise. Instead, the return value is a pair of values: the first is the
processed arguments as usual, while the second contains all the unrecognised arguments that were left unprocessed. This
allows you to do multi-stage processing, where the entire set of arguments that a template should accept is not known at
once. For example, an inflection-table might do some generic processing on some arguments, but then defer processing of
the remainder to the function that handles a specific inflectional type.
]==]
function export.process(args, params, return_unknown)
	-- Process parameters for specific properties
	local args_new, args_unknown, any_args_set, spec_types, required, patterns, list_args, index_list, args_placeholders, placeholders_n = {}

	-- TODO: memoize the processing of each unique `param` value, since it's common for the same value to be used for many parameter names.
	for name, param in pairs(params) do
		validate_name(name, "parameter names")
		if spec_types == nil then
			spec_types = {}
		end
		local param_spec_type = type(param)
		spec_types[param] = param_spec_type
		if param_spec_type == "table" then
			-- Populate required table, and make sure aliases aren't set to required.
			if param.required then
				if param.alias_of then
					internal_process_error(
						"Parameter %s is an alias of %s, but is also set as a required parameter. Only %s should be set as required.",
						name, param.alias_of, name
					)
				elseif required == nil then
					required = {}
				end
				required[name] = true
			end

			-- FIXME: modifying one of the input tables is a bad idea.
			-- Convert param.set from a list into a set.
			-- `converted_set` prevents double-conversion if multiple parameter keys share the same param table.
			-- rawset avoids errors if param has been loaded via mw.loadData; however, it's probably more efficient to preconvert them, and set the `converted_set` key in advance.
			local set = param.set
			if set and not param.converted_set then
				rawset(param, "set", list_to_alias_map(set))
				rawset(param, "converted_set", true)
			end

			local listname, alias = param.list, param.alias_of
			if alias then
				validate_name(alias, "the alias_of field of parameter ", name)
				-- Check that the alias_of is set to a valid parameter.
				if not params[alias] then
					internal_process_error(
						"Parameter %s is an alias of an invalid parameter.",
						name
					)
				elseif alias == name then
					internal_process_error(
						"Parameter %s cannot be an alias of itself.",
						name
					)
				end
				local main_param = params[alias]
				local main_spec_type = spec_types[main_param] or type(main_param) -- Might not yet be memoized.
				-- Aliases can't be lists unless the canonical parameter is also a list.
				if listname and not (main_spec_type == "table" and main_param.list) then
					internal_process_error(
						"The list parameter %s is set as an alias of %s, which is not a list parameter.", name, alias
					)
				-- Can't be an alias of an alias.
				elseif main_spec_type == "table" then
					local main_alias_of = main_param.alias_of
					if main_alias_of ~= nil then
						internal_process_error(
							"alias_of cannot be set to another alias: parameter %s is set as an alias of %s, which is in turn an alias of %s. Set alias_of for %s to %s.",
							name, alias, main_alias_of, name, main_alias_of
						)
					end
				end
			end

			if listname then
				if not alias then
					local key = name
					if type(name) == "string" then
						key = gsub(name, "\1", "")
					end
					local list_arg = {maxindex = 0}
					args_new[key] = list_arg
					if list_args == nil then
						list_args = {}
					end
					list_args[key] = list_arg
				end
				local list_type = type(listname)
				if list_type == "string" then
					-- If the list property is a string, then it represents the name
					-- to be used as the prefix for list items. This is for use with lists
					-- where the first item is a numbered parameter and the
					-- subsequent ones are named, such as 1, pl2, pl3.
					patterns = save_pattern(name, listname, patterns or {})
				elseif listname ~= true then
					internal_process_error(
						"The list field for parameter %s must be a boolean, string or undefined, but saw a %s.",
						name, list_type
					)
				elseif type(name) == "number" then
					if index_list ~= nil then
						internal_process_error(
							"Only one numeric parameter can be a list, unless the list property is a string."
						)
					end
					-- If the name is a number, then all indexed parameters from
					-- this number onwards go in the list.
					index_list = name
				else
					patterns = save_pattern(name, name, patterns or {})
				end
				if find(name, "\1", nil, true) then
					if args_placeholders then
						placeholders_n = placeholders_n + 1
						args_placeholders[placeholders_n] = name
					else
						args_placeholders, placeholders_n = {name}, 1
					end
				end
			end
		elseif param ~= true then
			internal_process_error(
				"Spec for parameter %s must be a table of specs or the value true, but found %s.",
				name, param_spec_type ~= "boolean" and param_spec_type or param
			)
		end
	end

	--Process required changes to `params`.
	if args_placeholders then
		for i = 1, placeholders_n do
			local name = args_placeholders[i]
			params[gsub(name, "\1", "")], params[name] = params[name], nil
		end
	end

	-- Process the arguments
	for name, val in pairs(args) do
		any_args_set = true
		validate_name(name, "argument names", nil, true)
		-- Once all of these have been eliminated, throw an internal error.
		-- Guaranteeing that all values are strings avoids issues with type coercion being inconsistent between functions.
		if type(val) ~= "string" then
			track("input is not string")
			track("input is not string/raw")
		end
		
		local orig_name, raw_type, index, canonical = name, type(name)

		if raw_type == "number" then
			if index_list and name >= index_list then
				index = name - index_list + 1
				name = index_list
			end
		elseif patterns then
			-- Does this argument name match a pattern?
			for pattern, pname in next, patterns do
				index = match(name, pattern)
				-- It matches, so store the parameter name and the
				-- numeric index extracted from the argument name.
				if index then
					index = tonumber(index)
					name = pname
					break
				end
			end
		end

		local param = params[name]

		-- If the argument is not in the list of parameters, store it in a separate list.
		if not param then
			args_unknown = unknown_param(name, val, args_unknown or {})
		elseif param == true then
			canonical = orig_name
			val = php_trim(val)
			if val ~= "" then
				-- If the parameter is duplicated, throw an error.
				if args_new[name] ~= nil then
					process_error(
						"Parameter %s has been entered more than once. This is probably because a parameter alias has been used.",
						canonical
					)
				end
				args_new[name] = val
			end
		else
			if param.require_index then
				-- Disallow require_index for numeric parameter names, as this doesn't make sense.
				if raw_type == "number" then
					internal_process_error(
						"Cannot set require_index for numeric parameter %s.",
						name
					)
				-- If a parameter without the trailing index was found, and
				-- require_index is set on the param, treat it
				-- as if it isn't recognized.
				elseif not index then
					args_unknown = unknown_param(name, val, args_unknown or {})
				end
			end

			-- Check that separate_no_index is not being used with a numeric parameter.
			if param.separate_no_index then
				if raw_type == "number" then
					internal_process_error(
						"Cannot set separate_no_index for numeric parameter %s.",
						name
					)
				elseif type(param.alias_of) == "number" then
					internal_process_error(
						"Cannot set separate_no_index for parameter %s, as it is an alias of numeric parameter %s.",
						name, param.alias_of
					)
				end
			end

			-- If no index was found, use 1 as the default index.
			-- This makes list parameters like g, g2, g3 put g at index 1.
			-- If `separate_no_index` is set, then use 0 as the default instead.
			if not index and param.list then
				index = param.separate_no_index and 0 or 1
			end

			-- Normalize to the canonical parameter name. If it's a list, but the alias is not, then determine the index.
			local raw_name = param.alias_of
			if raw_name then
				raw_type = type(raw_name)
				if raw_type == "number" then
					name = raw_name
					local main_param = params[raw_name]
					if spec_types[main_param] == "table" and main_param.list then
						if not index then
							index = param.separate_no_index and 0 or 1
						end
						canonical = raw_name + index - 1
					else
						canonical = raw_name
					end
				else
					name = gsub(raw_name, "\1", "")
					local main_param = params[name]
					if not index and spec_types and spec_types[main_param] == "table" and main_param.list then
						index = param.separate_no_index and 0 or 1
					end
					if not index or index == 0 then
						canonical = name
					elseif name == raw_name then
						canonical = name .. index
					else
						canonical = gsub(raw_name, "\1", index)
					end
				end
			else
				canonical = orig_name
			end

			-- Only recognize demo parameters if this is the current template or module's
			-- page, or its documentation page.
			if param.demo and not is_own_page("include_documentation") then
				args_unknown = unknown_param(name, val, args_unknown or {})
			end

			-- Remove leading and trailing whitespace unless no_trim is true.
			if param.no_trim then
				check_string_param_modifier(param.type, name, "no_trim")
			else
				val = php_trim(val)
			end

			-- Empty string is equivalent to nil unless allow_empty is true.
			if param.allow_empty then
				check_string_param_modifier(param.type, name, "allow_empty")
			elseif val == "" then
				-- If `disallow_missing` is set, keep track of empty parameters
				-- via the `empty` field in `arg`, which will be used by the
				-- `disallow_missing` check. This will be deleted before
				-- returning.
				if index and param.disallow_missing then
					local arg = args_new[name]
					local empty = arg.empty
					if empty == nil then
						empty = {}
						arg.empty = empty
					end
					empty[index] = true
				end
				val = nil
			end

			-- Allow boolean false.
			if val ~= nil then
				-- Convert to proper type if necessary.
				local main_param = params[raw_name]
				if not main_param or (spec_types and spec_types[main_param] == "table") then
					val = convert_val(val, orig_name, main_param or param)
				end

				-- Mark it as no longer required, as it is present.
				if required then
					required[name] = nil
				end

				-- Store the argument value.
				if index then
					local arg = args_new[name]
					-- If the parameter is duplicated, throw an error.
					if arg[index] ~= nil then
						process_error(
							"Parameter %s has been entered more than once. This is probably because a list parameter has been entered without an index and with index 1 at the same time, or because a parameter alias has been used.",
							canonical
						)
					end
					arg[index] = val
					-- Store the highest index we find.
					local maxindex = max(index, arg.maxindex)
					if arg[0] ~= nil then
						arg.default, arg[0] = arg[0], nil
						if maxindex == 0 then
							maxindex = 1
						end
					end
					arg.maxindex = maxindex
					if not params[name].list then
						args_new[name] = val
					-- Don't store index 0, as it's a proxy for the default.
					elseif index > 0 then
						arg[index] = val
					end
				else
					-- If the parameter is duplicated, throw an error.
					if args_new[name] ~= nil then
						process_error(
							"Parameter %s has been entered more than once. This is probably because a parameter alias has been used.",
							canonical
						)
					end

					if not raw_name then
						args_new[name] = val
					else
						local main_param = params[raw_name]
						if spec_types[main_param] == "table" and main_param.list then
							local main_arg = args_new[raw_name]
							main_arg[1] = val
							-- Store the highest index we find.
							main_arg.maxindex = max(1, main_arg.maxindex)
						else
							args_new[raw_name] = val
						end
					end
				end
			end
		end
	end

	-- Remove holes in any list parameters if needed. This must be handled
	-- straight after the previous loop, as any instances of `empty` need to be
	-- converted to nil.
	if list_args then
		for name, val in next, list_args do
			handle_holes(params, val, name)
		end
	end

	-- If the current page is the template which invoked this Lua instance, then ignore the `require` flag, as it
	-- means we're viewing the template directly. Required parameters sometimes have a `template_default` key set,
	-- which gets used in such cases as a demo.
	-- Note: this won't work on other pages in the Template: namespace (including the /documentation subpage),
	-- or if the #invoke: is on a page in another namespace.
	local pagename_set = args_new.pagename

	-- Handle defaults.
	for name, param in pairs(params) do
		if spec_types[param] == "table" then
			local arg_new = args_new[name]
			if arg_new == nil then
				args_new[name] = convert_default_val(name, param, pagename_set, any_args_set)
			elseif param.list and arg_new[1] == nil then
				local default_val = convert_default_val(name, param, pagename_set, any_args_set)
				if default_val ~= nil then
					arg_new[1] = default_val
					if arg_new.maxindex == 0 then
						arg_new.maxindex = 1
					end
				end
			end
		end
	end
	
	-- The required table should now be empty.
	-- If any parameters remain, throw an error, unless we're on the current template or module's page.
	if required and next(required) ~= nil and not is_own_page() then
		params_list_error(required, "required")
	-- Return the arguments table.
	-- If there are any unknown parameters, throw an error, unless return_unknown is set, in which case return args_unknown as a second return value.
	elseif return_unknown then
		return args_new, args_unknown or {}
	elseif args_unknown and next(args_unknown) ~= nil then
		params_list_error(args_unknown, "not used by this template")
	end
	return args_new
end

return export