<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://hna.wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Cronos</id>
	<title>Honkai: Nexus Anima Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://hna.wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Cronos"/>
	<link rel="alternate" type="text/html" href="https://hna.wiki/Special:Contributions/Cronos"/>
	<updated>2026-07-14T19:06:28Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.46.0</generator>
	<entry>
		<id>https://hna.wiki/MediaWiki:Searchsuggest-search?diff=8813</id>
		<title>MediaWiki:Searchsuggest-search</title>
		<link rel="alternate" type="text/html" href="https://hna.wiki/MediaWiki:Searchsuggest-search?diff=8813"/>
		<updated>2026-07-13T11:02:05Z</updated>

		<summary type="html">&lt;p&gt;Cronos: Created page with &amp;quot;Search the {{SITENAME}}&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Search the {{SITENAME}}&lt;/div&gt;</summary>
		<author><name>Cronos</name></author>
	</entry>
	<entry>
		<id>https://hna.wiki/MediaWiki:Vector.css?diff=8812</id>
		<title>MediaWiki:Vector.css</title>
		<link rel="alternate" type="text/html" href="https://hna.wiki/MediaWiki:Vector.css?diff=8812"/>
		<updated>2026-07-13T11:00:29Z</updated>

		<summary type="html">&lt;p&gt;Cronos: Created page with &amp;quot;/* All CSS here will be loaded for users of the Vector skin */  /* Hide &amp;quot;Read&amp;quot; tab */ body.action-view #p-views li#ca-view {     display: none !important; }&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* All CSS here will be loaded for users of the Vector skin */&lt;br /&gt;
&lt;br /&gt;
/* Hide &amp;quot;Read&amp;quot; tab */&lt;br /&gt;
body.action-view #p-views li#ca-view {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Cronos</name></author>
	</entry>
	<entry>
		<id>https://hna.wiki/MediaWiki:Nstab-main?diff=8811</id>
		<title>MediaWiki:Nstab-main</title>
		<link rel="alternate" type="text/html" href="https://hna.wiki/MediaWiki:Nstab-main?diff=8811"/>
		<updated>2026-07-13T10:59:18Z</updated>

		<summary type="html">&lt;p&gt;Cronos: Created page with &amp;quot;Article&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Article&lt;/div&gt;</summary>
		<author><name>Cronos</name></author>
	</entry>
	<entry>
		<id>https://hna.wiki/MediaWiki:Weirdgloop-tou?diff=8810</id>
		<title>MediaWiki:Weirdgloop-tou</title>
		<link rel="alternate" type="text/html" href="https://hna.wiki/MediaWiki:Weirdgloop-tou?diff=8810"/>
		<updated>2026-07-13T10:58:29Z</updated>

		<summary type="html">&lt;p&gt;Cronos: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Terms of Use&lt;/div&gt;</summary>
		<author><name>Cronos</name></author>
	</entry>
	<entry>
		<id>https://hna.wiki/MediaWiki:Weirdgloop-tou?diff=8809</id>
		<title>MediaWiki:Weirdgloop-tou</title>
		<link rel="alternate" type="text/html" href="https://hna.wiki/MediaWiki:Weirdgloop-tou?diff=8809"/>
		<updated>2026-07-13T10:58:21Z</updated>

		<summary type="html">&lt;p&gt;Cronos: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-&lt;/div&gt;</summary>
		<author><name>Cronos</name></author>
	</entry>
	<entry>
		<id>https://hna.wiki/MediaWiki:Weirdgloop-tou?diff=8808</id>
		<title>MediaWiki:Weirdgloop-tou</title>
		<link rel="alternate" type="text/html" href="https://hna.wiki/MediaWiki:Weirdgloop-tou?diff=8808"/>
		<updated>2026-07-13T10:58:09Z</updated>

		<summary type="html">&lt;p&gt;Cronos: Created blank page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Cronos</name></author>
	</entry>
	<entry>
		<id>https://hna.wiki/MediaWiki:Aboutsite?diff=8807</id>
		<title>MediaWiki:Aboutsite</title>
		<link rel="alternate" type="text/html" href="https://hna.wiki/MediaWiki:Aboutsite?diff=8807"/>
		<updated>2026-07-13T10:57:33Z</updated>

		<summary type="html">&lt;p&gt;Cronos: Created page with &amp;quot;About the {{SITENAME}}&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;About the {{SITENAME}}&lt;/div&gt;</summary>
		<author><name>Cronos</name></author>
	</entry>
	<entry>
		<id>https://hna.wiki/MediaWiki:Lastmodifiedat?diff=8806</id>
		<title>MediaWiki:Lastmodifiedat</title>
		<link rel="alternate" type="text/html" href="https://hna.wiki/MediaWiki:Lastmodifiedat?diff=8806"/>
		<updated>2026-07-13T10:57:03Z</updated>

		<summary type="html">&lt;p&gt;Cronos: Created page with &amp;quot;This page was last modified on $1, at $2.&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page was last modified on $1, at $2.&lt;/div&gt;</summary>
		<author><name>Cronos</name></author>
	</entry>
	<entry>
		<id>https://hna.wiki/MediaWiki:Copyright-footer?diff=8805</id>
		<title>MediaWiki:Copyright-footer</title>
		<link rel="alternate" type="text/html" href="https://hna.wiki/MediaWiki:Copyright-footer?diff=8805"/>
		<updated>2026-07-13T10:55:45Z</updated>

		<summary type="html">&lt;p&gt;Cronos: Created page with &amp;quot;Content on this site is licensed under $1, except non-text media and any game content. &amp;lt;br /&amp;gt;&amp;lt;i&amp;gt;Honkai: Nexus Anima&amp;lt;/i&amp;gt; is a trademark of [https://www.mihoyo.com/en/ miHoYo Co., Ltd.].&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Content on this site is licensed under $1, except non-text media and any game content.&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;i&amp;gt;Honkai: Nexus Anima&amp;lt;/i&amp;gt; is a trademark of [https://www.mihoyo.com/en/ miHoYo Co., Ltd.].&lt;/div&gt;</summary>
		<author><name>Cronos</name></author>
	</entry>
	<entry>
		<id>https://hna.wiki/User:Shirra2006/Sandbox?diff=8804</id>
		<title>User:Shirra2006/Sandbox</title>
		<link rel="alternate" type="text/html" href="https://hna.wiki/User:Shirra2006/Sandbox?diff=8804"/>
		<updated>2026-07-13T10:49:24Z</updated>

		<summary type="html">&lt;p&gt;Cronos: Cronos moved page User:Shirra2006/Sandbox to User:Shirra/Sandbox&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[User:Shirra/Sandbox]]&lt;/div&gt;</summary>
		<author><name>Cronos</name></author>
	</entry>
	<entry>
		<id>https://hna.wiki/User:Shirra/Sandbox?diff=8803</id>
		<title>User:Shirra/Sandbox</title>
		<link rel="alternate" type="text/html" href="https://hna.wiki/User:Shirra/Sandbox?diff=8803"/>
		<updated>2026-07-13T10:49:24Z</updated>

		<summary type="html">&lt;p&gt;Cronos: Cronos moved page User:Shirra2006/Sandbox to User:Shirra/Sandbox&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Physical==&lt;br /&gt;
==Memory==&lt;br /&gt;
===Challenge 1===&lt;br /&gt;
*Level 22&lt;br /&gt;
*Plumelune&lt;br /&gt;
*Donnatallow&lt;br /&gt;
*Heartcease&lt;br /&gt;
*Lumeridon&lt;br /&gt;
*Bunmareen&lt;br /&gt;
*Felinoxie (with relic)&lt;br /&gt;
*Inkicorn&lt;br /&gt;
*Mushgloomini&lt;br /&gt;
===C2===&lt;br /&gt;
*Lv 24&lt;br /&gt;
*Starspine&lt;br /&gt;
*Vixemourne(w relic)&lt;br /&gt;
*Mushgloomini&lt;br /&gt;
*Bunmareen&lt;br /&gt;
*Felinoxie(w rel)&lt;br /&gt;
*Capybunner&lt;br /&gt;
*Apollinis&lt;br /&gt;
*Lumeridon&lt;br /&gt;
===C3===&lt;br /&gt;
*Lv 26&lt;br /&gt;
*Vulberry&lt;br /&gt;
*Vixemourne(w relic)&lt;br /&gt;
*Waxula (w relic)&lt;br /&gt;
*Dragamour&lt;br /&gt;
*Chocllamour&lt;br /&gt;
*Felinoxie(w rel)&lt;br /&gt;
*Apollinis&lt;br /&gt;
*Lumeridon&lt;br /&gt;
===C4===&lt;br /&gt;
*Lv 28&lt;br /&gt;
*Donnatallow&lt;br /&gt;
*Vixemourne&lt;br /&gt;
*Mushgloomini&lt;br /&gt;
*Marshalbird&lt;br /&gt;
*Eggnok&lt;br /&gt;
*Felinoxie(w rel)&lt;br /&gt;
*Noctumbra (w rel)&lt;br /&gt;
*Lumeridon&lt;br /&gt;
===C5===&lt;br /&gt;
*Lv 40&lt;br /&gt;
*Donnatallow&lt;br /&gt;
*Waxula(w 2 rel)&lt;br /&gt;
*Vixemourne&lt;br /&gt;
*Lumeridon&lt;br /&gt;
*Eggnok&lt;br /&gt;
*Felinoxie(w 2 rel)&lt;br /&gt;
*Mushgloomini&lt;br /&gt;
*Furrari&lt;br /&gt;
===C6===&lt;br /&gt;
*Lv 45&lt;br /&gt;
*Donnatallow&lt;br /&gt;
*Vixemourne (w 2 rel)&lt;br /&gt;
*Mesmerith&lt;br /&gt;
*Cumulon&lt;br /&gt;
*Mushgloomini&lt;br /&gt;
*Lumeridon&lt;br /&gt;
*Felinoxie (w rel)&lt;br /&gt;
*Dragamour&lt;br /&gt;
*Noctumbra (w 2 rel)&lt;br /&gt;
&lt;br /&gt;
==Dark==&lt;/div&gt;</summary>
		<author><name>Cronos</name></author>
	</entry>
	<entry>
		<id>https://hna.wiki/User:Shirra2006?diff=8802</id>
		<title>User:Shirra2006</title>
		<link rel="alternate" type="text/html" href="https://hna.wiki/User:Shirra2006?diff=8802"/>
		<updated>2026-07-13T10:49:24Z</updated>

		<summary type="html">&lt;p&gt;Cronos: Cronos moved page User:Shirra2006 to User:Shirra&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[User:Shirra]]&lt;/div&gt;</summary>
		<author><name>Cronos</name></author>
	</entry>
	<entry>
		<id>https://hna.wiki/User:Shirra?diff=8801</id>
		<title>User:Shirra</title>
		<link rel="alternate" type="text/html" href="https://hna.wiki/User:Shirra?diff=8801"/>
		<updated>2026-07-13T10:49:24Z</updated>

		<summary type="html">&lt;p&gt;Cronos: Cronos moved page User:Shirra2006 to User:Shirra&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Custom Tabs|tab2=Sandbox}}&lt;br /&gt;
{{Quote|Moneytree, Moneytree, one shake to live in luxury&amp;lt;br&amp;gt;Moneytree, Moneytree, fast cash and that&#039;s a guarantee&amp;lt;br&amp;gt;Moneytree, Moneytree, the Nexus gonna make it rain&amp;lt;br&amp;gt;Moneytree, Moneytree, there&#039;s no brakes on the fortune train}}&lt;br /&gt;
==Checklist==&lt;br /&gt;
* Lumeridon&lt;br /&gt;
* Donubi&lt;br /&gt;
* Mushgloomini&lt;br /&gt;
* Faeflux (yes...)&lt;br /&gt;
* Nomnom (yes)&lt;br /&gt;
* Pupanoo (nah)&lt;br /&gt;
* Damasque (yes, cbt2 portrait is also outdated)&lt;br /&gt;
* Vixemourne (no)&lt;br /&gt;
* Camcorgi&lt;br /&gt;
* Hoppelon&lt;br /&gt;
* Pomparrot&lt;br /&gt;
* Lumehart&lt;br /&gt;
* Derpydile&lt;br /&gt;
* Blade (splash art defo got updated)&lt;br /&gt;
&lt;br /&gt;
==Gone==&lt;br /&gt;
* Scorscythe&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Others==&lt;br /&gt;
*Aspect Fracture -&amp;gt; Aspect contamination (Kiana&#039;s bio)&lt;br /&gt;
&lt;br /&gt;
==Media Products w/ Authors==&lt;br /&gt;
===Camcorgi Moments Photography Studio===&lt;br /&gt;
* [https://www.youtube.com/watch?v=KfPCXYYRBDc Planar Transmission: An Anima Fashion Brand, Modeled by Lumehart | #HonkaiNexusAnima]&lt;br /&gt;
* Iia Visitor&#039;s Guide: [https://www.hoyolab.com/article/45594251 Vol. 1], [https://www.hoyolab.com/article/45647745 Vol. 2]&lt;br /&gt;
&lt;br /&gt;
===ICM Finity Pictures Co., LTD. (Finity Plane)===&lt;br /&gt;
* [https://www.youtube.com/watch?v=SQgkvqLYuTE Planar Transmission: Trailer for a Psychological Thriller Starring Noctumbra | #HonkaiNexusAnima]&lt;/div&gt;</summary>
		<author><name>Cronos</name></author>
	</entry>
	<entry>
		<id>https://hna.wiki/Module:Arguments?diff=8800</id>
		<title>Module:Arguments</title>
		<link rel="alternate" type="text/html" href="https://hna.wiki/Module:Arguments?diff=8800"/>
		<updated>2026-07-13T10:47:25Z</updated>

		<summary type="html">&lt;p&gt;Cronos: Reverted edit by Cronos (talk) to last revision by 919dd&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- based on https://dev.fandom.com/wiki/Module:Arguments?oldid=160378&lt;br /&gt;
&lt;br /&gt;
--- Arguments invocation argument extractor for Scribunto modules.&lt;br /&gt;
--  It is intended for use by other Lua modules, and should not be&lt;br /&gt;
--  called from an invocation (`#invoke`) directly.&lt;br /&gt;
--  &lt;br /&gt;
--  This module supports the following features:&lt;br /&gt;
--   * Trimming and blank argument removal.&lt;br /&gt;
--   * Argument inheritance between child and parent frames.&lt;br /&gt;
--   * Argument extraction for external modules and console input.&lt;br /&gt;
--   * Options to customise argument extraction behaviour.&lt;br /&gt;
--  &lt;br /&gt;
--  @script             arguments&lt;br /&gt;
--  @release            stable&lt;br /&gt;
--  @note               The `args` table from the @{arguments.getArgs}&lt;br /&gt;
--                      function is a metatable for performance reasons.&lt;br /&gt;
--                      Thus, the table will not permit Lua table methods&lt;br /&gt;
--                      such as `#args`, @{next|next(args)}, and @{table}&lt;br /&gt;
--                      library functions.&lt;br /&gt;
--  @note               This module will eventually be adapted as a&lt;br /&gt;
--                      library in [[mw:gerrit:q/158323|MediaWiki&lt;br /&gt;
--                      core]], called as `require(&#039;getArgs&#039;)`. The core&lt;br /&gt;
--                      library will remove `options.parentOnly`.&lt;br /&gt;
--  @author             [[wikipedia:User:Mr. Stradivarius|Mr. Stradivarius]] (Wikipedia)&lt;br /&gt;
--  @author             [[wikipedia:User:Anomie|Anomie]] (Wikipedia)&lt;br /&gt;
--  @author             [[wikipedia:User:Jackmcbarn|Jackmcbarn]] (Wikipedia)&lt;br /&gt;
--  @author             [[User:Dessamator|Dessamator]]&lt;br /&gt;
--  @author             [[User:DarthKitty|DarthKitty]]&lt;br /&gt;
--  @attribution        [[wikipedia:Module:Arguments|Module:Arguments]] (Wikipedia)&lt;br /&gt;
--  @see                [[wikipedia:Module:Arguments|Original module on Wikipedia]]&lt;br /&gt;
--  @see                [[Module:Arguments/testcases|Test cases for this module]]&lt;br /&gt;
local arguments = {}&lt;br /&gt;
&lt;br /&gt;
--  Module dependencies.&lt;br /&gt;
local i18n = require(&#039;Module:I18n&#039;).loadMessages(&#039;Arguments&#039;)&lt;br /&gt;
local util = require(&#039;libraryUtil&#039;)&lt;br /&gt;
local checkType = util.checkType&lt;br /&gt;
&lt;br /&gt;
local SANDBOX_PATTERN = &#039;/Draft$&#039;&lt;br /&gt;
&lt;br /&gt;
-- Four different value tidying functions.&lt;br /&gt;
-- This way, we don&#039;t have to check the options every time we call them.&lt;br /&gt;
&lt;br /&gt;
--- Default value tidying function.&lt;br /&gt;
--  Trims parameter values automatically if they are defined strings.&lt;br /&gt;
--  Treats blank strings as `nil`.&lt;br /&gt;
--  @function           tidyValDefault&lt;br /&gt;
--  @param              {string|number} key MediaWiki parameter key.&lt;br /&gt;
--  @param              {string|nil} val MediaWiki parameter value,&lt;br /&gt;
--                      or nil if `key` is an empty string or nil.&lt;br /&gt;
--  @local&lt;br /&gt;
local function tidyValDefault(key, val)&lt;br /&gt;
    if type(val) == &#039;string&#039; then&lt;br /&gt;
        val = val:match(&#039;^%s*(.-)%s*$&#039;)&lt;br /&gt;
        if val == &#039;&#039; then&lt;br /&gt;
            return nil&lt;br /&gt;
        else&lt;br /&gt;
            return val&lt;br /&gt;
        end&lt;br /&gt;
    else&lt;br /&gt;
        return val&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Value tidying function that trims values.&lt;br /&gt;
--  Trims parameter values automatically if they are defined strings.&lt;br /&gt;
--  @function           tidyValTrimOnly&lt;br /&gt;
--  @param              {string|number} key MediaWiki parameter key.&lt;br /&gt;
--  @param              {string|nil} val MediaWiki parameter value.&lt;br /&gt;
--  @local&lt;br /&gt;
local function tidyValTrimOnly(key, val)&lt;br /&gt;
    if type(val) == &#039;string&#039; then&lt;br /&gt;
        return val:match(&#039;^%s*(.-)%s*$&#039;)&lt;br /&gt;
    else&lt;br /&gt;
        return val&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Value tidying function that removes blanks.&lt;br /&gt;
--  Removes blank values from the arguments table.&lt;br /&gt;
--  @function           tidyValRemoveBlanksOnly&lt;br /&gt;
--  @param              {string|number} key MediaWiki parameter key.&lt;br /&gt;
--  @param              {string|nil} val MediaWiki parameter value,&lt;br /&gt;
--                      or nil if `key` is whitespace or nil.&lt;br /&gt;
--  @local&lt;br /&gt;
local function tidyValRemoveBlanksOnly(key, val)&lt;br /&gt;
    if type(val) == &#039;string&#039; then&lt;br /&gt;
        if val:find(&#039;%S&#039;) then&lt;br /&gt;
            return val&lt;br /&gt;
        else&lt;br /&gt;
            return nil&lt;br /&gt;
        end&lt;br /&gt;
    else&lt;br /&gt;
        return val&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Value tidying function that returns original value.&lt;br /&gt;
--  Effectively a NOOP function that does no value processing.&lt;br /&gt;
--  @function           tidyValNoChange&lt;br /&gt;
--  @param              {string|number} key MediaWiki parameter key.&lt;br /&gt;
--  @param              {string|nil} val MediaWiki parameter value.&lt;br /&gt;
--  @local&lt;br /&gt;
local function tidyValNoChange(key, val)&lt;br /&gt;
    return val&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Parent template title match checker.&lt;br /&gt;
--  @function           matchesTitle&lt;br /&gt;
--  @param              {string|number|nil} given Local prefixed page&lt;br /&gt;
--                      title, or MediaWiki article ID (`wgArticleId`).&lt;br /&gt;
--  @param              {string} title Title of parent template.&lt;br /&gt;
--  @return             {boolean} Whether the `given` ID/title matches&lt;br /&gt;
--                      the title of the parent template.&lt;br /&gt;
local function matchesTitle(given, title)&lt;br /&gt;
    local tp = type( given )&lt;br /&gt;
    return (tp == &#039;string&#039; or tp == &#039;number&#039;) and mw.title.new( given ).prefixedText == title&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Default argument translation metatable.&lt;br /&gt;
--  @table              translate_mt&lt;br /&gt;
--  @local&lt;br /&gt;
local translate_mt = { __index = function(t, k) return k end }&lt;br /&gt;
&lt;br /&gt;
--- Main argument extraction utility.&lt;br /&gt;
--  Arguments are memoized once fetched for optimal performance,&lt;br /&gt;
--  as with the `frame.args` metatable in Scribunto core.&lt;br /&gt;
--  &lt;br /&gt;
--  The default argument lookup behaviour uses the child frame arguments&lt;br /&gt;
--  first, then the parent frame arguments. There are numerous frame&lt;br /&gt;
--  options to change this behaviour.&lt;br /&gt;
--  &lt;br /&gt;
--  The default value tidying behaviour trims parameter values if they&lt;br /&gt;
--  are defined strings and treats blank strings as `nil`. This can be&lt;br /&gt;
--  customised in the `getArgs` options.&lt;br /&gt;
--  &lt;br /&gt;
--  @param              {frame|table} frame Scribunto frame object or&lt;br /&gt;
--                      Lua arguments table, passed from an invocation&lt;br /&gt;
--                      or Lua logic such as `frame:getParent()`.&lt;br /&gt;
--                      If this parameter does not have an `args` field&lt;br /&gt;
--                      and a `getParent` method, `frame` is assumed&lt;br /&gt;
--                      to be a Lua arguments table, such as the&lt;br /&gt;
--                      arguments from a named arguments call.&lt;br /&gt;
--  @param[opt]         {table} options Extraction/processing options.&lt;br /&gt;
--  @param[opt]         {boolean} options.trim&lt;br /&gt;
--                      Whether to trim the blank arguments present in&lt;br /&gt;
--                      the arguments table. Accepts `false` only.&lt;br /&gt;
--                      Default: `true`.&lt;br /&gt;
--  @param[opt]         {boolean} options.removeBlanks&lt;br /&gt;
--                      Whether to remove blank arguments from the&lt;br /&gt;
--                      arguments table. Does not shift sequential&lt;br /&gt;
--                      arguments removed by the processing stage.&lt;br /&gt;
--                      Accepts `false` only. Default: `true`.&lt;br /&gt;
--  @param[opt]         {function} options.valueFunc&lt;br /&gt;
--                      Custom value tidying function for use if the&lt;br /&gt;
--                      `trim` and `removeBlanks` options don&#039;t cover&lt;br /&gt;
--                      the developer&#039;s argument processing use case.&lt;br /&gt;
--  @param[opt]         {boolean} options.frameOnly&lt;br /&gt;
--                      Only read arguments from child frame (the&lt;br /&gt;
--                      `frame` parameter - usually invocation frame).&lt;br /&gt;
--  @param[opt]         {boolean} options.parentOnly&lt;br /&gt;
--                      Only read arguments from `frame` parent (the&lt;br /&gt;
--                      `frame` parameter - usually template frame).&lt;br /&gt;
--  @param[opt]         {boolean} options.parentFirst&lt;br /&gt;
--                      Argument lookup in the `frame` parent first,&lt;br /&gt;
--                      prioritised over the invocation frame arguments.&lt;br /&gt;
--  @param[opt]         {table} options.wrappers&lt;br /&gt;
--                      Individual value or array of values, listing&lt;br /&gt;
--                      wrapper title name(s) or article ID(s) to permit&lt;br /&gt;
--                      parent argument lookup from.&lt;br /&gt;
--  @param[opt]         {string|number} options.wrapper&lt;br /&gt;
--                      Alias of `options.wrappers` - contains title&lt;br /&gt;
--                      name or article ID to permit parent argument&lt;br /&gt;
--                      lookup from.&lt;br /&gt;
--  @param[opt]         {boolean} options.readOnly&lt;br /&gt;
--                      Whether to restrict write permissions to the&lt;br /&gt;
--                      arguments table. When set to a truthy value,&lt;br /&gt;
--                      an error will be thrown on any write attempt.&lt;br /&gt;
--  @param[opt]         {boolean} options.noOverwrite&lt;br /&gt;
--                      Whether to restrict overwrite attempts on&lt;br /&gt;
--                      existing argument keys in the arguments table.&lt;br /&gt;
--                      When set to a truthy value, an error will be&lt;br /&gt;
--                      thrown on any write attempt that would result&lt;br /&gt;
--                      in an existing argument being overwritten.&lt;br /&gt;
--  @param[opt]         {table} options.translate&lt;br /&gt;
--                      Map of parameter name aliases to their canonical&lt;br /&gt;
--                      argument parameter names.&lt;br /&gt;
--  @param[opt]         {table} options.backtranslate&lt;br /&gt;
--                      Map of canonical parameter names to their&lt;br /&gt;
--                      argument parameter aliases.&lt;br /&gt;
--                      Supersedes `options.translate` if both options&lt;br /&gt;
--                      are in use.&lt;br /&gt;
--  @error[opt,317]     &#039;bad value assigned to option &amp;quot;valueFunc&amp;quot;&lt;br /&gt;
--                      (function expected, got $type)&#039;&lt;br /&gt;
--  @error[opt,407]     &#039;could not write to argument table key &amp;quot;$key&amp;quot;;&lt;br /&gt;
--                      the table is read-only&#039;&lt;br /&gt;
--  @error[opt,409]     &#039;could not write to argument table key &amp;quot;$key&amp;quot;;&lt;br /&gt;
--                      overwriting existing arguments is not permitted&#039;&lt;br /&gt;
--  @return             {table} Arguments extracted from invocation.&lt;br /&gt;
--                      The argument data is embedded as a metatable in&lt;br /&gt;
--                      the exported table and cannot be accessed with&lt;br /&gt;
--                      the `#` operator or @{table} library methods.&lt;br /&gt;
--                      However, the exported table can be written to if&lt;br /&gt;
--                      the `options.readOnly` flag parameter is not&lt;br /&gt;
--                      truthy.&lt;br /&gt;
--  @usage&lt;br /&gt;
--      &lt;br /&gt;
--      local getArgs = require(&#039;Module:Arguments&#039;).getArgs&lt;br /&gt;
--      function p.main(frame)&lt;br /&gt;
--          local args = getArgs(frame, {&lt;br /&gt;
--              wrapper = &#039;Template:&amp;lt;TEMPLATE&amp;gt;&#039;&lt;br /&gt;
--          })&lt;br /&gt;
--          -- Use the args table here.&lt;br /&gt;
--          -- A common paradigm is `return p._main(args)`.&lt;br /&gt;
--          -- This allows other Lua modules to access the&lt;br /&gt;
--          -- main logic in a performant manner without a&lt;br /&gt;
--          -- frame object.&lt;br /&gt;
--      end&lt;br /&gt;
--      &lt;br /&gt;
--  @note               Reference tags in the form of `&amp;lt;ref&amp;gt;` will&lt;br /&gt;
--                      generate phantom references when calling the&lt;br /&gt;
--                      @{pairs} iterator on the arguments table,&lt;br /&gt;
--                      **IF** the `&amp;lt;ref&amp;gt;` tag does not appear in the&lt;br /&gt;
--                      dependent module&#039;s wikitext output.&lt;br /&gt;
function arguments.getArgs(frame, options)&lt;br /&gt;
    checkType(&#039;getArgs&#039;, 1, frame, &#039;table&#039;, true)&lt;br /&gt;
    checkType(&#039;getArgs&#039;, 2, options, &#039;table&#039;, true)&lt;br /&gt;
    frame = frame or {}&lt;br /&gt;
    options = options or {}&lt;br /&gt;
&lt;br /&gt;
    -- Set up argument translation.&lt;br /&gt;
    options.translate = options.translate or {}&lt;br /&gt;
    if getmetatable(options.translate) == nil then&lt;br /&gt;
        setmetatable(options.translate, translate_mt)&lt;br /&gt;
    end&lt;br /&gt;
    if options.backtranslate == nil then&lt;br /&gt;
        options.backtranslate = {}&lt;br /&gt;
        for k,v in pairs(options.translate) do&lt;br /&gt;
            options.backtranslate[v] = k&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    if options.backtranslate and getmetatable(options.backtranslate) == nil then&lt;br /&gt;
        setmetatable(options.backtranslate, {&lt;br /&gt;
            __index = function(t, k)&lt;br /&gt;
                if options.translate[k] ~= k then&lt;br /&gt;
                    return nil&lt;br /&gt;
                else&lt;br /&gt;
                    return k&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
        })&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- Get the argument tables. If we were passed a valid frame object,&lt;br /&gt;
    -- get the frame arguments (fargs) and the parent frame arguments&lt;br /&gt;
    -- (pargs), depending on the options set and on the parent frame&#039;s&lt;br /&gt;
    -- availability. If we weren&#039;t passed a valid frame object, we are&lt;br /&gt;
    -- being called from another Lua module or from the debug console,&lt;br /&gt;
    -- so assume that we were passed a table of args directly, and&lt;br /&gt;
    -- assign it to a new variable (luaArgs).&lt;br /&gt;
    local fargs, pargs, luaArgs&lt;br /&gt;
    options.wrappers = options.wrappers or options.wrapper&lt;br /&gt;
    if&lt;br /&gt;
        type(frame.args) == &#039;table&#039; and&lt;br /&gt;
        type(frame.getParent) == &#039;function&#039;&lt;br /&gt;
    then&lt;br /&gt;
        -- The wrappers option makes Module:Arguments look up&lt;br /&gt;
        -- arguments in either the frame argument table or the&lt;br /&gt;
        -- parent argument table, but not both. This means that&lt;br /&gt;
        -- users can use either the #invoke syntax or a wrapper&lt;br /&gt;
        -- template without the loss of performance associated&lt;br /&gt;
        -- with looking arguments up in both the frame and the&lt;br /&gt;
        -- parent frame.&lt;br /&gt;
        -- The arguments will be fetched from the parent frame if&lt;br /&gt;
        -- the parent frame&#039;s title is present in options.wrapper;&lt;br /&gt;
        -- otherwise it will look up arguments in the frame object&lt;br /&gt;
        -- passed to getArgs.&lt;br /&gt;
        if options.wrappers then&lt;br /&gt;
            local parent = frame:getParent()&lt;br /&gt;
            if not parent then&lt;br /&gt;
                fargs = frame.args&lt;br /&gt;
            else&lt;br /&gt;
                local title = parent:getTitle():gsub(SANDBOX_PATTERN, &#039;&#039;)&lt;br /&gt;
                local found = false&lt;br /&gt;
                if matchesTitle(options.wrappers, title) then&lt;br /&gt;
                    found = true&lt;br /&gt;
                elseif type(options.wrappers) == &#039;table&#039; then&lt;br /&gt;
                    for _,v in pairs(options.wrappers) do&lt;br /&gt;
                        if matchesTitle(v, title) then&lt;br /&gt;
                            found = true&lt;br /&gt;
                            break&lt;br /&gt;
                        end&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
&lt;br /&gt;
                -- We test for false specifically here so that nil (the&lt;br /&gt;
                -- default) acts like true.&lt;br /&gt;
                if found or options.frameOnly == false then&lt;br /&gt;
                    pargs = parent.args&lt;br /&gt;
                end&lt;br /&gt;
                if not found or options.parentOnly == false then&lt;br /&gt;
                    fargs = frame.args&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
        -- When options.wrapper isn&#039;t set, check the other options.&lt;br /&gt;
        else&lt;br /&gt;
            if not options.parentOnly then&lt;br /&gt;
                fargs = frame.args&lt;br /&gt;
            end&lt;br /&gt;
            if not options.frameOnly then&lt;br /&gt;
                local parent = frame:getParent()&lt;br /&gt;
                pargs = parent and parent.args or nil&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
        if options.parentFirst then&lt;br /&gt;
            fargs, pargs = pargs, fargs&lt;br /&gt;
        end&lt;br /&gt;
    else&lt;br /&gt;
        luaArgs = frame&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- Set the order of precedence of the argument tables. If the variables are&lt;br /&gt;
    -- nil, nothing will be added to the table, which is how we avoid clashes&lt;br /&gt;
    -- between the frame/parent args and the Lua args.&lt;br /&gt;
    local argTables = {fargs}&lt;br /&gt;
    argTables[#argTables + 1] = pargs&lt;br /&gt;
    argTables[#argTables + 1] = luaArgs&lt;br /&gt;
&lt;br /&gt;
    -- Generate the tidyVal function. If it has been specified by the user, we&lt;br /&gt;
    -- use that; if not, we choose one of four functions depending on the&lt;br /&gt;
    -- options chosen. This is so that we don&#039;t have to call the options table&lt;br /&gt;
    -- every time the function is called.&lt;br /&gt;
    local tidyVal = options.valueFunc&lt;br /&gt;
    if tidyVal then&lt;br /&gt;
        if type(tidyVal) ~= &#039;function&#039; then&lt;br /&gt;
            error(i18n:msg(&#039;error-value-func&#039;, type(tidyVal)), 2)&lt;br /&gt;
        end&lt;br /&gt;
    elseif options.trim ~= false then&lt;br /&gt;
        if options.removeBlanks ~= false then&lt;br /&gt;
            tidyVal = tidyValDefault&lt;br /&gt;
        else&lt;br /&gt;
            tidyVal = tidyValTrimOnly&lt;br /&gt;
        end&lt;br /&gt;
    else&lt;br /&gt;
        if options.removeBlanks ~= false then&lt;br /&gt;
            tidyVal = tidyValRemoveBlanksOnly&lt;br /&gt;
        else&lt;br /&gt;
            tidyVal = tidyValNoChange&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- Set up the args, metaArgs and nilArgs tables. args will be the one&lt;br /&gt;
    -- accessed from functions, and metaArgs will hold the actual arguments. Nil&lt;br /&gt;
    -- arguments are memoized in nilArgs, and the metatable connects all of them&lt;br /&gt;
    -- together.&lt;br /&gt;
    local args, metaArgs, nilArgs, metatable = {}, {}, {}, {}&lt;br /&gt;
    setmetatable(args, metatable)&lt;br /&gt;
&lt;br /&gt;
    -- Accepts multiple tables as input and merges their keys and values&lt;br /&gt;
    -- into one table. If a value is already present it is not overwritten;&lt;br /&gt;
    -- tables listed earlier have precedence. We are also memoizing nil&lt;br /&gt;
    -- values, which can be overwritten if they are &#039;s&#039; (soft).&lt;br /&gt;
    local function mergeArgs(tables)&lt;br /&gt;
        for _, t in ipairs(tables) do&lt;br /&gt;
            for key, val in pairs(t) do&lt;br /&gt;
                if metaArgs[key] == nil and nilArgs[key] ~= &#039;h&#039; then&lt;br /&gt;
                    local tidiedVal = tidyVal(key, val)&lt;br /&gt;
                    if tidiedVal == nil then&lt;br /&gt;
                        nilArgs[key] = &#039;s&#039;&lt;br /&gt;
                    else&lt;br /&gt;
                        metaArgs[key] = tidiedVal&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- Define metatable behaviour. Arguments are memoized in the metaArgs table,&lt;br /&gt;
    -- and are only fetched from the argument tables once. Fetching arguments&lt;br /&gt;
    -- from the argument tables is the most resource-intensive step in this&lt;br /&gt;
    -- module, so we try and avoid it where possible. For this reason, nil&lt;br /&gt;
    -- arguments are also memoized, in the nilArgs table. Also, we keep a record&lt;br /&gt;
    -- in the metatable of when pairs and ipairs have been called, so we do not&lt;br /&gt;
    -- run pairs and ipairs on the argument tables more than once. We also do&lt;br /&gt;
    -- not run ipairs on fargs and pargs if pairs has already been run, as all&lt;br /&gt;
    -- the arguments will already have been copied over.&lt;br /&gt;
&lt;br /&gt;
    -- Fetches an argument when the args table is indexed. First we check&lt;br /&gt;
    -- to see if the value is memoized, and if not we try and fetch it from&lt;br /&gt;
    -- the argument tables. When we check memoization, we need to check&lt;br /&gt;
    -- metaArgs before nilArgs, as both can be non-nil at the same time.&lt;br /&gt;
    -- If the argument is not present in metaArgs, we also check whether&lt;br /&gt;
    -- pairs has been run yet. If pairs has already been run, we return nil.&lt;br /&gt;
    -- This is because all the arguments will have already been copied into&lt;br /&gt;
    -- metaArgs by the mergeArgs function, meaning that any other arguments&lt;br /&gt;
    -- must be nil.&lt;br /&gt;
    metatable.__index = function (t, key)&lt;br /&gt;
        if type(key) == &#039;string&#039; then&lt;br /&gt;
            key = options.translate[key]&lt;br /&gt;
        end&lt;br /&gt;
        local val = metaArgs[key]&lt;br /&gt;
        if val ~= nil then&lt;br /&gt;
            return val&lt;br /&gt;
        elseif metatable.donePairs or nilArgs[key] then&lt;br /&gt;
            return nil&lt;br /&gt;
        end&lt;br /&gt;
        for _, argTable in ipairs(argTables) do&lt;br /&gt;
            local argTableVal = tidyVal(key, argTable[key])&lt;br /&gt;
            if argTableVal ~= nil then&lt;br /&gt;
                metaArgs[key] = argTableVal&lt;br /&gt;
                return argTableVal&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
        nilArgs[key] = &#039;h&#039;&lt;br /&gt;
        return nil&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    metatable.__newindex = function (t, key, val)&lt;br /&gt;
        -- This function is called when a module tries to add a new&lt;br /&gt;
        -- value to the args table, or tries to change an existing&lt;br /&gt;
        -- value.&lt;br /&gt;
        if type(key) == &#039;string&#039; then&lt;br /&gt;
            key = options.translate[key]&lt;br /&gt;
        end&lt;br /&gt;
        if options.readOnly then&lt;br /&gt;
            error(i18n:msg(&#039;error-write-permission&#039;, tostring(key)), 2)&lt;br /&gt;
        elseif options.noOverwrite and args[key] ~= nil then&lt;br /&gt;
            error(i18n:msg(&#039;error-overwrite-permission&#039;, tostring(key)), 2)&lt;br /&gt;
        elseif val == nil then&lt;br /&gt;
            -- If the argument is to be overwritten with nil, we need to erase&lt;br /&gt;
            -- the value in metaArgs, so that __index, __pairs and __ipairs do&lt;br /&gt;
            -- not use a previous existing value, if present; and we also need&lt;br /&gt;
            -- to memoize the nil in nilArgs, so that the value isn&#039;t looked&lt;br /&gt;
            -- up in the argument tables if it is accessed again.&lt;br /&gt;
            metaArgs[key] = nil&lt;br /&gt;
            nilArgs[key] = &#039;h&#039;&lt;br /&gt;
        else&lt;br /&gt;
            metaArgs[key] = val&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function translatenext(invariant)&lt;br /&gt;
        local k, v = next(invariant.t, invariant.k)&lt;br /&gt;
        invariant.k = k&lt;br /&gt;
        if k == nil then&lt;br /&gt;
            return nil&lt;br /&gt;
        elseif type(k) ~= &#039;string&#039; or not options.backtranslate then&lt;br /&gt;
            return k, v&lt;br /&gt;
        else&lt;br /&gt;
            local backtranslate = options.backtranslate[k]&lt;br /&gt;
            if backtranslate == nil then&lt;br /&gt;
                -- Skip this one. This is a tail call, so this&lt;br /&gt;
                -- won&#039;t cause stack overflow.&lt;br /&gt;
                return translatenext(invariant)&lt;br /&gt;
            else&lt;br /&gt;
                return backtranslate, v&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- This metamethod is called when pairs is run on the args table.&lt;br /&gt;
    metatable.__pairs = function ()&lt;br /&gt;
        if not metatable.donePairs then&lt;br /&gt;
            mergeArgs(argTables)&lt;br /&gt;
            metatable.donePairs = true&lt;br /&gt;
        end&lt;br /&gt;
        return translatenext, { t = metaArgs }&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- This custom `ipairs`-style iterator uses our __index metamethod.&lt;br /&gt;
    local function inext(t, i)&lt;br /&gt;
        local v = t[i + 1]&lt;br /&gt;
        if v ~= nil then&lt;br /&gt;
            return i + 1, v&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- This metamethod is called when ipairs is run on the args table.&lt;br /&gt;
    metatable.__ipairs = function (t)&lt;br /&gt;
        return inext, t, 0&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return args&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return arguments&lt;/div&gt;</summary>
		<author><name>Cronos</name></author>
	</entry>
	<entry>
		<id>https://hna.wiki/Module:I18n?diff=8799</id>
		<title>Module:I18n</title>
		<link rel="alternate" type="text/html" href="https://hna.wiki/Module:I18n?diff=8799"/>
		<updated>2026-07-13T10:47:21Z</updated>

		<summary type="html">&lt;p&gt;Cronos: Reverted edit by Cronos (talk) to last revision by 919dd&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--- I18n library for message storage in Lua datastores.&lt;br /&gt;
--  The module is designed to enable message separation from modules &amp;amp;&lt;br /&gt;
--  templates. It has support for handling language fallbacks. This&lt;br /&gt;
--  module is a Lua port of [[I18n-js]] and i18n modules that can be loaded&lt;br /&gt;
--  by it are editable through [[I18nEdit]].&lt;br /&gt;
--  &lt;br /&gt;
--  @module         i18n&lt;br /&gt;
--  @version        1.4.0&lt;br /&gt;
--  @require        Module:Entrypoint&lt;br /&gt;
--  @require        Module:Fallbacklist&lt;br /&gt;
--  @author         [[User:KockaAdmiralac|KockaAdmiralac]]&lt;br /&gt;
--  @author         [[User:Speedit|Speedit]]&lt;br /&gt;
--  @attribution    [[User:Cqm|Cqm]]&lt;br /&gt;
--  @release        stable&lt;br /&gt;
--  @see            [[I18n|I18n guide]]&lt;br /&gt;
--  @see            [[I18n-js]]&lt;br /&gt;
--  @see            [[I18nEdit]]&lt;br /&gt;
--  &amp;lt;nowiki&amp;gt;&lt;br /&gt;
local i18n, _i18n = {}, {}&lt;br /&gt;
&lt;br /&gt;
--  Module variables &amp;amp; dependencies.&lt;br /&gt;
local title = mw.title.getCurrentTitle()&lt;br /&gt;
local fallbacks = require(&#039;Module:Fallbacklist&#039;)&lt;br /&gt;
local entrypoint = require(&#039;Module:Entrypoint&#039;)&lt;br /&gt;
local uselang&lt;br /&gt;
&lt;br /&gt;
--- Argument substitution as $n where n &amp;gt; 0.&lt;br /&gt;
--  @function           _i18n.handleArgs&lt;br /&gt;
--  @param              {string} msg Message to substitute arguments into.&lt;br /&gt;
--  @param              {table} args Arguments table to substitute.&lt;br /&gt;
--  @return             {string} Resulting message.&lt;br /&gt;
--  @local&lt;br /&gt;
function _i18n.handleArgs(msg, args)&lt;br /&gt;
    for i, a in ipairs(args) do&lt;br /&gt;
        msg = (string.gsub(msg, &#039;%$&#039; .. tostring(i), tostring(a)))&lt;br /&gt;
    end&lt;br /&gt;
    return msg&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Checks whether a language code is valid.&lt;br /&gt;
--  @function           _i18n.isValidCode&lt;br /&gt;
--  @param              {string} code Language code to check.&lt;br /&gt;
--  @return             {boolean} Whether the language code is valid.&lt;br /&gt;
--  @local&lt;br /&gt;
function _i18n.isValidCode(code)&lt;br /&gt;
    return type(code) == &#039;string&#039; and #mw.language.fetchLanguageName(code) ~= 0&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Checks whether a message contains unprocessed wikitext.&lt;br /&gt;
--  Used to optimise message getter by not preprocessing pure text.&lt;br /&gt;
--  @function           _i18n.isWikitext&lt;br /&gt;
--  @param              {string} msg Message to check.&lt;br /&gt;
--  @return             {boolean} Whether the message contains wikitext.&lt;br /&gt;
function _i18n.isWikitext(msg)&lt;br /&gt;
    return&lt;br /&gt;
        type(msg) == &#039;string&#039; and&lt;br /&gt;
        (&lt;br /&gt;
            msg:find(&#039;%-%-%-%-&#039;) or&lt;br /&gt;
            msg:find(&#039;%f[^\n%z][;:*#] &#039;) or&lt;br /&gt;
            msg:find(&#039;%f[^\n%z]==* *[^\n|]+ =*=%f[\n]&#039;) or&lt;br /&gt;
            msg:find(&#039;%b&amp;lt;&amp;gt;&#039;) or msg:find(&#039;\&#039;\&#039;&#039;) or&lt;br /&gt;
            msg:find(&#039;%[%b[]%]&#039;) or msg:find(&#039;{%b{}}&#039;)&lt;br /&gt;
        )&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- I18n datastore class.&lt;br /&gt;
--  This is used to control language translation and access to individual&lt;br /&gt;
--  messages. The datastore instance provides language and message&lt;br /&gt;
--  getter-setter methods, which can be used to internationalize Lua modules.&lt;br /&gt;
--  The language methods (any ending in `Lang`) are all **chainable**.&lt;br /&gt;
--  @type            Data&lt;br /&gt;
local Data = {}&lt;br /&gt;
Data.__index = Data&lt;br /&gt;
&lt;br /&gt;
--- Datastore message getter utility.&lt;br /&gt;
--  This method returns localized messages from the datastore corresponding&lt;br /&gt;
--  to a `key`. These messages may have `$n` parameters, which can be&lt;br /&gt;
--  replaced by optional argument strings supplied by the `msg` call.&lt;br /&gt;
--  &lt;br /&gt;
--  This function supports [[Lua reference manual#named_arguments|named&lt;br /&gt;
--  arguments]]. The named argument syntax is more versatile despite its&lt;br /&gt;
--  verbosity; it can be used to select message language &amp;amp; source(s).&lt;br /&gt;
--  @function           Data:msg&lt;br /&gt;
--  @usage&lt;br /&gt;
--  &lt;br /&gt;
--      ds:msg{&lt;br /&gt;
--          key = &#039;message-name&#039;,&lt;br /&gt;
--          lang = &#039;&#039;,&lt;br /&gt;
--          args = {...},&lt;br /&gt;
--          sources = {}&lt;br /&gt;
--      }&lt;br /&gt;
--  &lt;br /&gt;
--  @usage&lt;br /&gt;
--  &lt;br /&gt;
--      ds:msg(&#039;message-name&#039;, ...)&lt;br /&gt;
--  &lt;br /&gt;
--  @param              {string|table} opts Message configuration or key.&lt;br /&gt;
--  @param[opt]         {string} opts.key Message key to return from the&lt;br /&gt;
--                      datastore.&lt;br /&gt;
--  @param[opt]         {table} opts.args Arguments to substitute into the&lt;br /&gt;
--                      message (`$n`).&lt;br /&gt;
--  @param[opt]         {table} opts.sources Source names to limit to (see&lt;br /&gt;
--                      `Data:fromSources`).&lt;br /&gt;
--  @param[opt]         {table} opts.lang Temporary language to use (see&lt;br /&gt;
--                      `Data:inLang`).&lt;br /&gt;
--  @param[opt]         {string} ... Arguments to substitute into the message&lt;br /&gt;
--                      (`$n`).&lt;br /&gt;
--  @error[115]         {string} &#039;missing arguments in Data:msg&#039;&lt;br /&gt;
--  @return             {string} Localised datastore message or `&#039;&amp;lt;key&amp;gt;&#039;`.&lt;br /&gt;
function Data:msg(opts, ...)&lt;br /&gt;
    local frame = mw.getCurrentFrame()&lt;br /&gt;
    -- Argument normalization.&lt;br /&gt;
    if not self or not opts then&lt;br /&gt;
        error(&#039;missing arguments in Data:msg&#039;)&lt;br /&gt;
    end&lt;br /&gt;
    local key = type(opts) == &#039;table&#039; and opts.key or opts&lt;br /&gt;
    local args = opts.args or {...}&lt;br /&gt;
    -- Configuration parameters.&lt;br /&gt;
    if opts.sources then&lt;br /&gt;
        self:fromSources(unpack(opts.sources))&lt;br /&gt;
    end&lt;br /&gt;
    if opts.lang then&lt;br /&gt;
        self:inLang(opts.lang)&lt;br /&gt;
    end&lt;br /&gt;
    -- Source handling.&lt;br /&gt;
    local source_n = self.tempSources or self._sources&lt;br /&gt;
    local source_i = {}&lt;br /&gt;
    for n, i in pairs(source_n) do&lt;br /&gt;
        source_i[i] = n&lt;br /&gt;
    end&lt;br /&gt;
    self.tempSources = nil&lt;br /&gt;
    -- Language handling.&lt;br /&gt;
    local lang = self.tempLang or self.defaultLang&lt;br /&gt;
    self.tempLang = nil&lt;br /&gt;
    -- Message fetching.&lt;br /&gt;
    local msg&lt;br /&gt;
    for i, messages in ipairs(self._messages) do&lt;br /&gt;
        -- Message data.&lt;br /&gt;
        local msg = (messages[lang] or {})[key]&lt;br /&gt;
        -- Fallback support (experimental).&lt;br /&gt;
        for _, l in ipairs((fallbacks[lang] or {})) do&lt;br /&gt;
            if msg == nil then&lt;br /&gt;
                msg = (messages[l] or {})[key]&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
        -- Internal fallback to &#039;en&#039;.&lt;br /&gt;
        msg = msg ~= nil and msg or messages.en[key]&lt;br /&gt;
        -- Handling argument substitution from Lua.&lt;br /&gt;
        if msg and source_i[i] and #args &amp;gt; 0 then&lt;br /&gt;
            msg = _i18n.handleArgs(msg, args)&lt;br /&gt;
        end&lt;br /&gt;
        if msg and source_i[i] and lang ~= &#039;qqx&#039; then&lt;br /&gt;
            return frame and _i18n.isWikitext(msg)&lt;br /&gt;
                and frame:preprocess(mw.text.trim(msg))&lt;br /&gt;
                or  mw.text.trim(msg)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    return mw.text.nowiki(&#039;&amp;lt;&#039; .. key .. &#039;&amp;gt;&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Datastore template parameter getter utility.&lt;br /&gt;
--  This method, given a table of arguments, tries to find a parameter&#039;s&lt;br /&gt;
--  localized name in the datastore and returns its value, or nil if&lt;br /&gt;
--  not present.&lt;br /&gt;
--&lt;br /&gt;
--  This method always uses the wiki&#039;s content language.&lt;br /&gt;
--  @function           Data:parameter&lt;br /&gt;
--  @param              {string} parameter Parameter&#039;s key in the datastore&lt;br /&gt;
--  @param              {table} args Arguments to find the parameter in&lt;br /&gt;
--  @error[176]         {string} &#039;missing arguments in Data:parameter&#039;&lt;br /&gt;
--  @return             {string|nil} Parameter&#039;s value or nil if not present&lt;br /&gt;
function Data:parameter(key, args)&lt;br /&gt;
    -- Argument normalization.&lt;br /&gt;
    if not self or not key or not args then&lt;br /&gt;
        error(&#039;missing arguments in Data:parameter&#039;)&lt;br /&gt;
    end&lt;br /&gt;
    local contentLang = mw.language.getContentLanguage():getCode()&lt;br /&gt;
    -- Message fetching.&lt;br /&gt;
    for i, messages in ipairs(self._messages) do&lt;br /&gt;
        local msg = (messages[contentLang] or {})[key]&lt;br /&gt;
        if msg ~= nil and args[msg] ~= nil then&lt;br /&gt;
            return args[msg]&lt;br /&gt;
        end&lt;br /&gt;
        for _, l in ipairs((fallbacks[contentLang] or {})) do&lt;br /&gt;
            if msg == nil or args[msg] == nil then&lt;br /&gt;
                -- Check next fallback.&lt;br /&gt;
                msg = (messages[l] or {})[key]&lt;br /&gt;
            else&lt;br /&gt;
                -- A localized message was found.&lt;br /&gt;
                return args[msg]&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
        -- Fallback to English.&lt;br /&gt;
        msg = messages.en[key]&lt;br /&gt;
        if msg ~= nil and args[msg] ~= nil then&lt;br /&gt;
            return args[msg]&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Datastore temporary source setter to a specificed subset of datastores.&lt;br /&gt;
--  By default, messages are fetched from the datastore in the same&lt;br /&gt;
--  order of priority as `i18n.loadMessages`.&lt;br /&gt;
--  @function           Data:fromSource&lt;br /&gt;
--  @param              {string} ... Source name(s) to use.&lt;br /&gt;
--  @return             {Data} Datastore instance.&lt;br /&gt;
function Data:fromSource(...)&lt;br /&gt;
    local c = select(&#039;#&#039;, ...)&lt;br /&gt;
    if c ~= 0 then&lt;br /&gt;
        self.tempSources = {}&lt;br /&gt;
        for i = 1, c do&lt;br /&gt;
            local n = select(i, ...)&lt;br /&gt;
            if type(n) == &#039;string&#039; and type(self._sources[n]) == &#039;number&#039; then&lt;br /&gt;
                self.tempSources[n] = self._sources[n]&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    return self&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Datastore default language getter.&lt;br /&gt;
--  @function           Data:getLang&lt;br /&gt;
--  @return             {string} Default language to serve datastore messages in.&lt;br /&gt;
function Data:getLang()&lt;br /&gt;
    return self.defaultLang&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Datastore language setter to `wgUserLanguage`.&lt;br /&gt;
--  @function           Data:useUserLang&lt;br /&gt;
--  @return             {Data} Datastore instance.&lt;br /&gt;
--  @note               Scribunto only registers `wgUserLanguage` when an&lt;br /&gt;
--                      invocation is at the top of the call stack.&lt;br /&gt;
function Data:useUserLang()&lt;br /&gt;
    self.defaultLang = i18n.getLang() or self.defaultLang&lt;br /&gt;
    return self&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Datastore language setter to `wgContentLanguage`.&lt;br /&gt;
--  @function           Data:useContentLang&lt;br /&gt;
--  @return             {Data} Datastore instance.&lt;br /&gt;
function Data:useContentLang()&lt;br /&gt;
    self.defaultLang = mw.language.getContentLanguage():getCode()&lt;br /&gt;
    return self&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Datastore language setter to specificed language.&lt;br /&gt;
--  @function           Data:useLang&lt;br /&gt;
--  @param              {string} code Language code to use.&lt;br /&gt;
--  @return             {Data} Datastore instance.&lt;br /&gt;
function Data:useLang(code)&lt;br /&gt;
    self.defaultLang = _i18n.isValidCode(code)&lt;br /&gt;
        and code&lt;br /&gt;
        or  self.defaultLang&lt;br /&gt;
    return self&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Temporary datastore language setter to `wgUserLanguage`.&lt;br /&gt;
--  The datastore language reverts to the default language in the next&lt;br /&gt;
--  @{Data:msg} call.&lt;br /&gt;
--  @function           Data:inUserLang&lt;br /&gt;
--  @return             {Data} Datastore instance.&lt;br /&gt;
function Data:inUserLang()&lt;br /&gt;
    self.tempLang = i18n.getLang() or self.tempLang&lt;br /&gt;
    return self&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Temporary datastore language setter to `wgContentLanguage`.&lt;br /&gt;
--  Only affects the next @{Data:msg} call.&lt;br /&gt;
--  @function           Data:inContentLang&lt;br /&gt;
--  @return             {Data} Datastore instance.&lt;br /&gt;
function Data:inContentLang()&lt;br /&gt;
    self.tempLang = mw.language.getContentLanguage():getCode()&lt;br /&gt;
    return self&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Temporary datastore language setter to a specificed language.&lt;br /&gt;
--  Only affects the next @{Data:msg} call.&lt;br /&gt;
--  @function           Data:inLang&lt;br /&gt;
--  @param              {string} code Language code to use.&lt;br /&gt;
--  @return             {Data} Datastore instance.&lt;br /&gt;
function Data:inLang(code)&lt;br /&gt;
    self.tempLang = _i18n.isValidCode(code)&lt;br /&gt;
        and code&lt;br /&gt;
        or  self.tempLang&lt;br /&gt;
    return self&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--  Package functions.&lt;br /&gt;
&lt;br /&gt;
--- Localized message getter by key.&lt;br /&gt;
--  Can be used to fetch messages in a specific language code through `uselang`&lt;br /&gt;
--  parameter. Extra numbered parameters can be supplied for substitution into&lt;br /&gt;
--  the datastore message.&lt;br /&gt;
--  @function           i18n.getMsg&lt;br /&gt;
--  @param              {table} frame Frame table from invocation.&lt;br /&gt;
--  @param              {table} frame.args Metatable containing arguments.&lt;br /&gt;
--  @param              {string} frame.args[1] ROOTPAGENAME of i18n submodule.&lt;br /&gt;
--  @param              {string} frame.args[2] Key of i18n message.&lt;br /&gt;
--  @param[opt]         {string} frame.args.lang Default language of message.&lt;br /&gt;
--  @error[271]         &#039;missing arguments in i18n.getMsg&#039;&lt;br /&gt;
--  @return             {string} I18n message in localised language.&lt;br /&gt;
--  @usage              {{i18n|getMsg|source|key|arg1|arg2|uselang {{=}} code}}&lt;br /&gt;
function i18n.getMsg(frame)&lt;br /&gt;
    if&lt;br /&gt;
        not frame or&lt;br /&gt;
        not frame.args or&lt;br /&gt;
        not frame.args[1] or&lt;br /&gt;
        not frame.args[2]&lt;br /&gt;
    then&lt;br /&gt;
        error(&#039;missing arguments in i18n.getMsg&#039;)&lt;br /&gt;
    end&lt;br /&gt;
    local source = frame.args[1]&lt;br /&gt;
    local key = frame.args[2]&lt;br /&gt;
    -- Pass through extra arguments.&lt;br /&gt;
    local repl = {}&lt;br /&gt;
    for i, a in ipairs(frame.args) do&lt;br /&gt;
        if i &amp;gt;= 3 then&lt;br /&gt;
            repl[i-2] = a&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    -- Load message data.&lt;br /&gt;
    local ds = i18n.loadMessages(source)&lt;br /&gt;
    -- Pass through language argument.&lt;br /&gt;
    ds:inLang(frame.args.uselang)&lt;br /&gt;
    -- Return message.&lt;br /&gt;
    return ds:msg { key = key, args = repl }&lt;br /&gt;
end&lt;br /&gt;
 &lt;br /&gt;
--- I18n message datastore loader.&lt;br /&gt;
--  @function           i18n.loadMessages&lt;br /&gt;
--  @param              {string} ... ROOTPAGENAME/path for target i18n&lt;br /&gt;
--                      submodules.&lt;br /&gt;
--  @error[322]         {string} &#039;no source supplied to i18n.loadMessages&#039;&lt;br /&gt;
--  @return             {table} I18n datastore instance.&lt;br /&gt;
--  @usage              require(&#039;Dev:I18n&#039;).loadMessages(&#039;1&#039;, &#039;2&#039;)&lt;br /&gt;
function i18n.loadMessages(...)&lt;br /&gt;
    local ds&lt;br /&gt;
    local i = 0&lt;br /&gt;
    local s = {}&lt;br /&gt;
    for j = 1, select(&#039;#&#039;, ...) do&lt;br /&gt;
        local source = select(j, ...)&lt;br /&gt;
        if type(source) == &#039;string&#039; and source ~= &#039;&#039; then&lt;br /&gt;
            i = i + 1&lt;br /&gt;
            s[source] = i&lt;br /&gt;
            if not ds then&lt;br /&gt;
                -- Instantiate datastore.&lt;br /&gt;
                ds = {}&lt;br /&gt;
                ds._messages = {}&lt;br /&gt;
                -- Set default language.&lt;br /&gt;
                setmetatable(ds, Data)&lt;br /&gt;
                ds:useUserLang()&lt;br /&gt;
            end&lt;br /&gt;
            source = string.gsub(source, &#039;^.&#039;, mw.ustring.upper)&lt;br /&gt;
            source = mw.ustring.find(source, &#039;:&#039;)&lt;br /&gt;
                and source&lt;br /&gt;
                or  &#039;Module:&#039; .. source .. &#039;/i18n&#039; -- Dev: -&amp;gt; Module: for on-wiki use&lt;br /&gt;
            ds._messages[i] = mw.loadData(source)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    if not ds then&lt;br /&gt;
        error(&#039;no source supplied to i18n.loadMessages&#039;)&lt;br /&gt;
    else&lt;br /&gt;
        -- Attach source index map.&lt;br /&gt;
        ds._sources = s&lt;br /&gt;
        -- Return datastore instance.&lt;br /&gt;
        return ds&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Language code getter.&lt;br /&gt;
--  Can validate a template&#039;s language code through `uselang` parameter.&lt;br /&gt;
--  @function           i18n.getLang&lt;br /&gt;
--  @usage              {{i18n|getLang|uselang {{=}} code}}&lt;br /&gt;
--  @return             {string} Language code.&lt;br /&gt;
function i18n.getLang()&lt;br /&gt;
    local frame = mw.getCurrentFrame() or {}&lt;br /&gt;
    local parentFrame = frame.getParent and frame:getParent() or {}&lt;br /&gt;
&lt;br /&gt;
    local code = mw.language.getContentLanguage():getCode()&lt;br /&gt;
    local subPage = title.subpageText&lt;br /&gt;
&lt;br /&gt;
    -- Language argument test.&lt;br /&gt;
    local langOverride =&lt;br /&gt;
        (frame.args or {}).uselang or&lt;br /&gt;
        (parentFrame.args or {}).uselang&lt;br /&gt;
    if _i18n.isValidCode(langOverride) then&lt;br /&gt;
        code = langOverride&lt;br /&gt;
&lt;br /&gt;
    -- Subpage language test.&lt;br /&gt;
    elseif title.isSubpage and _i18n.isValidCode(subPage) then&lt;br /&gt;
        code = _i18n.isValidCode(subPage) and subPage or code&lt;br /&gt;
&lt;br /&gt;
    -- User language test.&lt;br /&gt;
    elseif parentFrame.preprocess or frame.preprocess then&lt;br /&gt;
        uselang = uselang&lt;br /&gt;
            or  parentFrame.preprocess&lt;br /&gt;
                and parentFrame:preprocess(&#039;{{int:lang}}&#039;)&lt;br /&gt;
                or  frame:preprocess(&#039;{{int:lang}}&#039;)&lt;br /&gt;
        local decodedLang = mw.text.decode(uselang) &lt;br /&gt;
        if decodedLang ~= &#039;&amp;lt;lang&amp;gt;&#039; and decodedLang ~= &#039;⧼lang⧽&#039; then&lt;br /&gt;
            code = decodedLang == &#039;(lang)&#039;&lt;br /&gt;
                and &#039;qqx&#039;&lt;br /&gt;
                or  uselang&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return code&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Template wrapper for [[Template:I18n]].&lt;br /&gt;
--  @function           i18n.main&lt;br /&gt;
--  @param              {table} frame Frame invocation object.&lt;br /&gt;
--  @return             {string} Module output in template context.&lt;br /&gt;
--  @usage              {{#invoke:i18n|main}}&lt;br /&gt;
i18n.main = entrypoint(i18n)&lt;br /&gt;
&lt;br /&gt;
return i18n&lt;br /&gt;
-- &amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Cronos</name></author>
	</entry>
	<entry>
		<id>https://hna.wiki/Module:I18n?diff=8798</id>
		<title>Module:I18n</title>
		<link rel="alternate" type="text/html" href="https://hna.wiki/Module:I18n?diff=8798"/>
		<updated>2026-07-13T10:46:59Z</updated>

		<summary type="html">&lt;p&gt;Cronos: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--- I18n library for message storage in Lua datastore collections.&lt;br /&gt;
--  The module is designed to enable message separation from modules &amp;amp;&lt;br /&gt;
--  templates. It has support for handling language fallbacks. This&lt;br /&gt;
--  module is a Lua port of [[I18n-js]] and i18n modules that can be loaded&lt;br /&gt;
--  by it are editable through [[I18nEdit]].&lt;br /&gt;
--  &lt;br /&gt;
--  @module         i18n&lt;br /&gt;
--  @version        1.5.0&lt;br /&gt;
--  @require        Module:Entrypoint&lt;br /&gt;
--  @require        Module:Fallbacklist&lt;br /&gt;
--  @author         [[User:KockaAdmiralac|KockaAdmiralac]]&lt;br /&gt;
--  @author         [[User:Speedit|Speedit]]&lt;br /&gt;
--  @attribution    [[User:Cqm|Cqm]]&lt;br /&gt;
--  @release        stable&lt;br /&gt;
--  @see            [[I18n|I18n guide]]&lt;br /&gt;
--  @see            [[I18n-js]]&lt;br /&gt;
--  @see            [[I18nEdit]]&lt;br /&gt;
--  &amp;lt;nowiki&amp;gt;&lt;br /&gt;
local i18n, _i18n = {}, {}&lt;br /&gt;
&lt;br /&gt;
--  Module variables &amp;amp; dependencies.&lt;br /&gt;
local title = mw.title.getCurrentTitle()&lt;br /&gt;
local fallbacks = require(&#039;Dev:Fallbacklist&#039;)&lt;br /&gt;
local entrypoint = require(&#039;Dev:Entrypoint&#039;)&lt;br /&gt;
local uselang&lt;br /&gt;
&lt;br /&gt;
--- Argument substitution as $n where n &amp;gt; 0.&lt;br /&gt;
--  @function           _i18n.handleArgs&lt;br /&gt;
--  @param              {string} msg Message to substitute arguments into.&lt;br /&gt;
--  @param              {table} args Arguments table to substitute.&lt;br /&gt;
--  @return             {string} Resulting message.&lt;br /&gt;
--  @local&lt;br /&gt;
function _i18n.handleArgs(msg, args)&lt;br /&gt;
    for i, a in ipairs(args) do&lt;br /&gt;
        msg = (string.gsub(msg, &#039;%$&#039; .. tostring(i), tostring(a)))&lt;br /&gt;
    end&lt;br /&gt;
    return msg&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Checks whether a language code is valid.&lt;br /&gt;
--  @function           _i18n.isValidCode&lt;br /&gt;
--  @param              {string} code Language code to check.&lt;br /&gt;
--  @return             {boolean} Whether the language code is valid.&lt;br /&gt;
--  @local&lt;br /&gt;
function _i18n.isValidCode(code)&lt;br /&gt;
    return type(code) == &#039;string&#039; and #mw.language.fetchLanguageName(code) ~= 0&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Checks whether a message contains unprocessed wikitext.&lt;br /&gt;
--  Used to optimise message getter by not preprocessing pure text.&lt;br /&gt;
--  @function           _i18n.isWikitext&lt;br /&gt;
--  @param              {string} msg Message to check.&lt;br /&gt;
--  @return             {boolean} Whether the message contains wikitext.&lt;br /&gt;
function _i18n.isWikitext(msg)&lt;br /&gt;
    return&lt;br /&gt;
        type(msg) == &#039;string&#039; and&lt;br /&gt;
        (&lt;br /&gt;
            msg:find(&#039;%-%-%-%-&#039;) or&lt;br /&gt;
            msg:find(&#039;%f[^\n%z][;:*#] &#039;) or&lt;br /&gt;
            msg:find(&#039;%f[^\n%z]==* *[^\n|]+ =*=%f[\n]&#039;) or&lt;br /&gt;
            msg:find(&#039;%b&amp;lt;&amp;gt;&#039;) or msg:find(&#039;\&#039;\&#039;&#039;) or&lt;br /&gt;
            msg:find(&#039;%[%b[]%]&#039;) or msg:find(&#039;{%b{}}&#039;)&lt;br /&gt;
        )&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- I18n datastore class.&lt;br /&gt;
--  This is used to control language translation and access to individual&lt;br /&gt;
--  messages. The datastore instance provides language and message&lt;br /&gt;
--  getter-setter methods, which can be used to internationalize Lua modules.&lt;br /&gt;
--  The language methods (any ending in `Lang`) are all **chainable**.&lt;br /&gt;
--  @type            Data&lt;br /&gt;
local Data = {}&lt;br /&gt;
Data.__index = Data&lt;br /&gt;
&lt;br /&gt;
--- Gets data message utility.&lt;br /&gt;
--  This method returns localized messages from the datastore corresponding&lt;br /&gt;
--  to a `key`. These messages may have `$n` parameters, which can be&lt;br /&gt;
--  replaced by optional argument strings supplied by the `msg` call.&lt;br /&gt;
--  &lt;br /&gt;
--  This function supports [[Lua reference manual#named_arguments|named&lt;br /&gt;
--  arguments]]. The named argument syntax is more versatile despite its&lt;br /&gt;
--  verbosity; it can be used to select message language &amp;amp; source(s).&lt;br /&gt;
--  @function           Data:msg&lt;br /&gt;
&lt;br /&gt;
--  @usage&lt;br /&gt;
--  &lt;br /&gt;
--      ds:msg{&lt;br /&gt;
--          key = &#039;message-name&#039;,&lt;br /&gt;
--          lang = &#039;&#039;,&lt;br /&gt;
--          args = {...},&lt;br /&gt;
--          sources = {}&lt;br /&gt;
--      }&lt;br /&gt;
--  &lt;br /&gt;
--  @usage&lt;br /&gt;
--  &lt;br /&gt;
--      ds:msg(&#039;message-name&#039;, ...)&lt;br /&gt;
--  &lt;br /&gt;
--  @param              {string|table} opts Message configuration or key.&lt;br /&gt;
--  @param[opt]         {string} opts.key Message key to return from the&lt;br /&gt;
--                      datastore.&lt;br /&gt;
--  @param[opt]         {table} opts.args Arguments to substitute into the&lt;br /&gt;
--                      message (`$n`).&lt;br /&gt;
--  @param[opt]         {table} opts.sources Source names to limit to (see&lt;br /&gt;
--                      `Data:fromSources`).&lt;br /&gt;
--  @param[opt]         {table} opts.lang Temporary language to use (see&lt;br /&gt;
--                      `Data:inLang`).&lt;br /&gt;
--  @param[opt]         {string} ... Arguments to substitute into the message&lt;br /&gt;
--                      (`$n`).&lt;br /&gt;
--  @error[115]         {string} &#039;missing arguments in Data:msg&#039;&lt;br /&gt;
--  @return             {string} Localised datastore message or `&#039;&amp;lt;key&amp;gt;&#039;`.&lt;br /&gt;
function Data:msg(opts, ...)&lt;br /&gt;
    local frame = mw.getCurrentFrame()&lt;br /&gt;
    -- Argument normalization.&lt;br /&gt;
    if not self or not opts then&lt;br /&gt;
        error(&#039;missing arguments in Data:msg&#039;)&lt;br /&gt;
    end&lt;br /&gt;
    local key = type(opts) == &#039;table&#039; and opts.key or opts&lt;br /&gt;
    local args = opts.args or {...}&lt;br /&gt;
    -- Configuration parameters.&lt;br /&gt;
    if opts.sources then&lt;br /&gt;
        self:fromSources(unpack(opts.sources))&lt;br /&gt;
    end&lt;br /&gt;
    if opts.lang then&lt;br /&gt;
        self:inLang(opts.lang)&lt;br /&gt;
    end&lt;br /&gt;
    -- Source handling.&lt;br /&gt;
    local source_n = self.tempSources or self._sources&lt;br /&gt;
    local source_i = {}&lt;br /&gt;
    for n, i in pairs(source_n) do&lt;br /&gt;
        source_i[i] = n&lt;br /&gt;
    end&lt;br /&gt;
    self.tempSources = nil&lt;br /&gt;
    -- Language handling.&lt;br /&gt;
    local lang = self.tempLang or self.defaultLang&lt;br /&gt;
    self.tempLang = nil&lt;br /&gt;
    -- Language converter handling&lt;br /&gt;
    -- Types: &#039;languageConverterMarkup&#039;, &#039;pageSourceCodeLanguage&#039;,&lt;br /&gt;
    --   &#039;siteLanguage&#039;, &#039;userLanguage&#039;&lt;br /&gt;
    -- Note: Currently cannot handle page view language without using&lt;br /&gt;
    --   &#039;languageConverterMarkup&#039;, should use &#039;userLanguage&#039; in most cases if&lt;br /&gt;
    --   not able to use &#039;languageConverterMarkup&#039;.&lt;br /&gt;
    local languageConversionType = self.tempLanguageConversionType or&lt;br /&gt;
    	&#039;languageConverterMarkup&#039;&lt;br /&gt;
    self.tempLangConversionType = &#039;languageConverterMarkup&#039;&lt;br /&gt;
    -- Message fetching.&lt;br /&gt;
    local msg&lt;br /&gt;
    for i, messages in ipairs(self._messages) do&lt;br /&gt;
        -- Message data.&lt;br /&gt;
        local msg = nil&lt;br /&gt;
        -- Language converter support&lt;br /&gt;
        if lang == &#039;zh&#039; and&lt;br /&gt;
        	languageConversionType ~= &#039;pageSourceCodeLanguage&#039; and&lt;br /&gt;
        	languageConversionType ~= &#039;siteLanguage&#039; and&lt;br /&gt;
        	-- Lua error: too many expensive function calls.&lt;br /&gt;
        	-- (&lt;br /&gt;
        	--	mw.title.getCurrentTitle().pageLang:toBcp47Code() == &#039;zh&#039; or&lt;br /&gt;
        	--	(&lt;br /&gt;
        	--		-- Module namespace&lt;br /&gt;
        	-- 		mw.title.getCurrentTitle():inNamespace( 828 ) and&lt;br /&gt;
        			mw.language.getContentLanguage():toBcp47Code() == &#039;zh&#039;&lt;br /&gt;
    		--	)&lt;br /&gt;
        	-- )&lt;br /&gt;
        then&lt;br /&gt;
        	local userLang = ( i18n.getLang() or self.defaultLang )&lt;br /&gt;
        	local zh_hani = (messages[&#039;zh&#039;] or {})[key]&lt;br /&gt;
        	local zh_hans = (messages[&#039;zh-hans&#039;] or {})[key]&lt;br /&gt;
        	local zh_hant = (messages[&#039;zh-hant&#039;] or {})[key]&lt;br /&gt;
        	local zh_hant_hk = (messages[&#039;zh-hk&#039;] or {})[key]&lt;br /&gt;
&lt;br /&gt;
			if&lt;br /&gt;
				languageConversionType == &#039;userLanguage&#039;&lt;br /&gt;
			then&lt;br /&gt;
				if userLang == &#039;zh&#039; then&lt;br /&gt;
        			msg = zh_hani or zh_hans or zh_hant or zh_hant_hk&lt;br /&gt;
				elseif&lt;br /&gt;
					userLang == &#039;zh-hans&#039; or&lt;br /&gt;
					userLang == &#039;zh-cn&#039; or&lt;br /&gt;
					userLang == &#039;zh-my&#039; or&lt;br /&gt;
					userLang == &#039;zh-sg&#039;&lt;br /&gt;
				then&lt;br /&gt;
        			msg = zh_hans or zh_hani or zh_hant or zh_hant_hk&lt;br /&gt;
				elseif&lt;br /&gt;
					userLang == &#039;zh-hant&#039; or&lt;br /&gt;
					userLang == &#039;zh-tw&#039;&lt;br /&gt;
				then&lt;br /&gt;
        			msg = zh_hant or zh_hant_hk or zh_hani or zh_hans&lt;br /&gt;
				elseif&lt;br /&gt;
					userLang == &#039;zh-hk&#039; or&lt;br /&gt;
					userLang == &#039;zh-mo&#039;&lt;br /&gt;
				then&lt;br /&gt;
        			msg = zh_hant_hk or zh_hant or zh_hani or zh_hans&lt;br /&gt;
        		end&lt;br /&gt;
        	elseif zh_hans ~= nil then&lt;br /&gt;
        		if zh_hant ~= nil then&lt;br /&gt;
    				msg = &#039;-{&#039; ..&lt;br /&gt;
    					&#039;zh-hans:&#039; .. zh_hans .. &#039;;&#039; ..&lt;br /&gt;
    					&#039; zh-hant:&#039; .. zh_hant .. &#039;;&#039;&lt;br /&gt;
&lt;br /&gt;
    				if zh_hant_hk ~= nil then&lt;br /&gt;
    					msg = msg ..&lt;br /&gt;
    						&#039; zh-hk:&#039; .. zh_hant_hk .. &#039;;&#039;&lt;br /&gt;
    				end&lt;br /&gt;
&lt;br /&gt;
    				msg = msg .. &#039;}-&#039;&lt;br /&gt;
        		elseif zh_hant_hk ~= nil then&lt;br /&gt;
        			msg = &#039;-{&#039; ..&lt;br /&gt;
        				&#039;zh-hans:&#039; .. zh_hans .. &#039;;&#039; ..&lt;br /&gt;
        				&#039; zh-hk:&#039; .. zh_hant_hk .. &#039;;&#039; ..&lt;br /&gt;
        				&#039;}-&#039;&lt;br /&gt;
        		else&lt;br /&gt;
        			msg = zh_hans&lt;br /&gt;
        		end&lt;br /&gt;
        	elseif zh_hant ~= nil then&lt;br /&gt;
        		if zh_hant_hk ~= nil then&lt;br /&gt;
        			msg = &#039;-{&#039; ..&lt;br /&gt;
        				&#039;zh-hant:&#039; .. zh_hant .. &#039;;&#039; ..&lt;br /&gt;
        				&#039; zh-hk:&#039; .. zh_hant_hk .. &#039;;&#039; ..&lt;br /&gt;
        				&#039;}-&#039;&lt;br /&gt;
        		else&lt;br /&gt;
        			msg = zh_hant&lt;br /&gt;
        		end&lt;br /&gt;
        	elseif zh_hant_hk ~= nil then&lt;br /&gt;
        		msg = zh_hant_hk&lt;br /&gt;
        	end&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        if msg == nil then&lt;br /&gt;
        	msg = (messages[lang] or {})[key]&lt;br /&gt;
        end&lt;br /&gt;
        -- Fallback support (experimental).&lt;br /&gt;
        for _, l in ipairs((fallbacks[lang] or {})) do&lt;br /&gt;
            if msg == nil then&lt;br /&gt;
                msg = (messages[l] or {})[key]&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
        -- Internal fallback to &#039;en&#039;.&lt;br /&gt;
        msg = msg ~= nil and msg or messages.en[key]&lt;br /&gt;
        -- Handling argument substitution from Lua.&lt;br /&gt;
        if msg and source_i[i] and #args &amp;gt; 0 then&lt;br /&gt;
            msg = _i18n.handleArgs(msg, args)&lt;br /&gt;
        end&lt;br /&gt;
        if msg and source_i[i] and lang ~= &#039;qqx&#039; then&lt;br /&gt;
            return frame and _i18n.isWikitext(msg)&lt;br /&gt;
                and frame:preprocess(msg)&lt;br /&gt;
                or  mw.text.trim(msg, &#039;\r\n\f\v&#039;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    return mw.text.nowiki(&#039;&amp;lt;&#039; .. key .. &#039;&amp;gt;&#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Gets localised template parameter from a datastore.&lt;br /&gt;
--  This method, given a table of arguments, tries to find a parameter&#039;s&lt;br /&gt;
--  localized name in the datastore and returns its value, or nil if&lt;br /&gt;
--  not present.&lt;br /&gt;
--&lt;br /&gt;
--  This method always uses the wiki&#039;s content language.&lt;br /&gt;
--  @function           Data:parameter&lt;br /&gt;
--  @param              {string} parameter Parameter&#039;s key in the datastore&lt;br /&gt;
--  @param              {table} args Arguments to find the parameter in&lt;br /&gt;
--  @error[176]         {string} &#039;missing arguments in Data:parameter&#039;&lt;br /&gt;
--  @return             {string|nil} Parameter&#039;s value or nil if not present&lt;br /&gt;
function Data:parameter(key, args)&lt;br /&gt;
    -- Argument normalization.&lt;br /&gt;
    if not self or not key or not args then&lt;br /&gt;
        error(&#039;missing arguments in Data:parameter&#039;)&lt;br /&gt;
    end&lt;br /&gt;
    local contentLang = mw.language.getContentLanguage():getCode()&lt;br /&gt;
    -- Message fetching.&lt;br /&gt;
    for i, messages in ipairs(self._messages) do&lt;br /&gt;
        local msg = (messages[contentLang] or {})[key]&lt;br /&gt;
        if msg ~= nil and args[msg] ~= nil then&lt;br /&gt;
            return args[msg]&lt;br /&gt;
        end&lt;br /&gt;
        for _, l in ipairs((fallbacks[contentLang] or {})) do&lt;br /&gt;
            if msg == nil or args[msg] == nil then&lt;br /&gt;
                -- Check next fallback.&lt;br /&gt;
                msg = (messages[l] or {})[key]&lt;br /&gt;
            else&lt;br /&gt;
                -- A localized message was found.&lt;br /&gt;
                return args[msg]&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
        -- Fallback to English.&lt;br /&gt;
        msg = messages.en[key]&lt;br /&gt;
        if msg ~= nil and args[msg] ~= nil then&lt;br /&gt;
            return args[msg]&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Sets temporary source to a specificed subset of datastores.&lt;br /&gt;
--  By default, messages are fetched from the datastore in the same&lt;br /&gt;
--  order of priority as `i18n.loadMessages`.&lt;br /&gt;
--  @function           Data:fromSource&lt;br /&gt;
--  @param              {string} ... Source name(s) to use.&lt;br /&gt;
--  @return             {Data} Datastore instance.&lt;br /&gt;
function Data:fromSource(...)&lt;br /&gt;
    local c = select(&#039;#&#039;, ...)&lt;br /&gt;
    if c ~= 0 then&lt;br /&gt;
        self.tempSources = {}&lt;br /&gt;
        for i = 1, c do&lt;br /&gt;
            local n = select(i, ...)&lt;br /&gt;
            if type(n) == &#039;string&#039; and type(self._sources[n]) == &#039;number&#039; then&lt;br /&gt;
                self.tempSources[n] = self._sources[n]&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    return self&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Gets data default language.&lt;br /&gt;
--  @function           Data:getLang&lt;br /&gt;
--  @return             {string} Default language to serve datastore messages in.&lt;br /&gt;
function Data:getLang()&lt;br /&gt;
    return self.defaultLang&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Sets data language to `wgUserLanguage`.&lt;br /&gt;
--  @function           Data:useUserLang&lt;br /&gt;
--  @return             {Data} Datastore instance.&lt;br /&gt;
--  @note               Scribunto only registers `wgUserLanguage` when an&lt;br /&gt;
--                      invocation is at the top of the call stack.&lt;br /&gt;
function Data:useUserLang()&lt;br /&gt;
    self.defaultLang = i18n.getLang() or self.defaultLang&lt;br /&gt;
    return self&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Sets data language to `wgContentLanguage`.&lt;br /&gt;
--  @function           Data:useContentLang&lt;br /&gt;
--  @return             {Data} Datastore instance.&lt;br /&gt;
function Data:useContentLang()&lt;br /&gt;
    self.defaultLang = i18n.getContentLang()&lt;br /&gt;
    return self&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Sets data language to specificed language.&lt;br /&gt;
--  @function           Data:useLang&lt;br /&gt;
--  @param              {string} code Language code to use.&lt;br /&gt;
--  @return             {Data} Datastore instance.&lt;br /&gt;
function Data:useLang(code)&lt;br /&gt;
    self.defaultLang = _i18n.isValidCode(code)&lt;br /&gt;
        and code&lt;br /&gt;
        or  self.defaultLang&lt;br /&gt;
    return self&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Sets temporary data language to `wgUserLanguage`.&lt;br /&gt;
--  The data language reverts to the default language in the next&lt;br /&gt;
--  @{Data:msg} call.&lt;br /&gt;
--  @function           Data:inUserLang&lt;br /&gt;
--  @return             {Data} Datastore instance.&lt;br /&gt;
function Data:inUserLang()&lt;br /&gt;
    self.tempLang = i18n.getLang() or self.tempLang&lt;br /&gt;
    return self&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Sets temporary data language to `wgContentLanguage`.&lt;br /&gt;
--  Only affects the next @{Data:msg} call.&lt;br /&gt;
--  @function           Data:inContentLang&lt;br /&gt;
--  @return             {Data} Datastore instance.&lt;br /&gt;
function Data:inContentLang()&lt;br /&gt;
    self.tempLang = i18n.getContentLang()&lt;br /&gt;
    return self&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Sets temporary data language to a specificed language.&lt;br /&gt;
--  Only affects the next @{Data:msg} call.&lt;br /&gt;
--  @function           Data:inLang&lt;br /&gt;
--  @param              {string} code Language code to use.&lt;br /&gt;
--  @return             {Data} Datastore instance.&lt;br /&gt;
function Data:inLang(code)&lt;br /&gt;
    self.tempLang = _i18n.isValidCode(code)&lt;br /&gt;
        and code&lt;br /&gt;
        or  self.tempLang&lt;br /&gt;
    return self&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Sets temporary data language to a specificed language conversion resolution.&lt;br /&gt;
--  Only affects the next @{Data:msg} call.&lt;br /&gt;
--  @function           Data:useLanguageConversionType&lt;br /&gt;
--  @param              {string} convType Language conversion type to use.&lt;br /&gt;
--  @return             {Data} Datastore instance.&lt;br /&gt;
function Data:useLanguageConversionType(convType)&lt;br /&gt;
    self.tempLanguageConversionType = convType&lt;br /&gt;
    return self&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--  Package functions.&lt;br /&gt;
&lt;br /&gt;
--- Gets a localized message by data module key.&lt;br /&gt;
--  Can be used to fetch messages in a specific language code through `uselang`&lt;br /&gt;
--  parameter. Extra numbered parameters can be supplied for substitution into&lt;br /&gt;
--  the data collection&#039;s message.&lt;br /&gt;
--  @function           i18n.getMsg&lt;br /&gt;
--  @param              {table} frame Frame table from invocation.&lt;br /&gt;
--  @param              {table} frame.args Metatable containing arguments.&lt;br /&gt;
--  @param              {string} frame.args[1] ROOTPAGENAME of i18n submodule.&lt;br /&gt;
--  @param              {string} frame.args[2] Key of i18n message.&lt;br /&gt;
--  @param[opt]         {string} frame.args.lang Default language of message.&lt;br /&gt;
--  @error[271]         &#039;missing arguments in i18n.getMsg&#039;&lt;br /&gt;
--  @return             {string} I18n message in localised language.&lt;br /&gt;
--  @usage              {{i18n|getMsg|source|key|arg1|arg2|uselang {{=}} code}}&lt;br /&gt;
function i18n.getMsg(frame)&lt;br /&gt;
    if&lt;br /&gt;
        not frame or&lt;br /&gt;
        not frame.args or&lt;br /&gt;
        not frame.args[1] or&lt;br /&gt;
        not frame.args[2]&lt;br /&gt;
    then&lt;br /&gt;
        error(&#039;missing arguments in i18n.getMsg&#039;)&lt;br /&gt;
    end&lt;br /&gt;
    local source = frame.args[1]&lt;br /&gt;
    local key = frame.args[2]&lt;br /&gt;
    -- Pass through extra arguments.&lt;br /&gt;
    local repl = {}&lt;br /&gt;
    for i, a in ipairs(frame.args) do&lt;br /&gt;
        if i &amp;gt;= 3 then&lt;br /&gt;
            repl[i-2] = a&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    -- Load message data.&lt;br /&gt;
    local ds = i18n.loadMessages(source)&lt;br /&gt;
    -- Pass through language argument.&lt;br /&gt;
    ds:inLang(frame.args.uselang)&lt;br /&gt;
    -- Return message.&lt;br /&gt;
    return ds:msg { key = key, args = repl }&lt;br /&gt;
end&lt;br /&gt;
 &lt;br /&gt;
--- I18n data loader for message collections.&lt;br /&gt;
--  @function           i18n.loadMessages&lt;br /&gt;
--  @param              {string} ... ROOTPAGENAME/path for target i18n&lt;br /&gt;
--                      submodules.&lt;br /&gt;
--  @error[322]         {string} &#039;no source supplied to i18n.loadMessages&#039;&lt;br /&gt;
--  @return             {table} I18n datastore instance.&lt;br /&gt;
--  @usage              require(&#039;Dev:I18n&#039;).loadMessages(&#039;1&#039;, &#039;2&#039;)&lt;br /&gt;
function i18n.loadMessages(...)&lt;br /&gt;
    local ds&lt;br /&gt;
    local i = 0&lt;br /&gt;
    local s = {}&lt;br /&gt;
    for j = 1, select(&#039;#&#039;, ...) do&lt;br /&gt;
        local source = select(j, ...)&lt;br /&gt;
        if type(source) == &#039;string&#039; and source ~= &#039;&#039; then&lt;br /&gt;
            i = i + 1&lt;br /&gt;
            s[source] = i&lt;br /&gt;
            if not ds then&lt;br /&gt;
                -- Instantiate datastore.&lt;br /&gt;
                ds = {}&lt;br /&gt;
                ds._messages = {}&lt;br /&gt;
                -- Set default language.&lt;br /&gt;
                setmetatable(ds, Data)&lt;br /&gt;
                ds:useUserLang()&lt;br /&gt;
            end&lt;br /&gt;
            source = string.gsub(source, &#039;^.&#039;, mw.ustring.upper)&lt;br /&gt;
            source = mw.ustring.find(source, &#039;:&#039;)&lt;br /&gt;
                and source&lt;br /&gt;
                or  &#039;Dev:&#039; .. source .. &#039;/i18n&#039;&lt;br /&gt;
            ds._messages[i] = mw.loadData(source)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    if not ds then&lt;br /&gt;
        error(&#039;no source supplied to i18n.loadMessages&#039;)&lt;br /&gt;
    else&lt;br /&gt;
        -- Attach source index map.&lt;br /&gt;
        ds._sources = s&lt;br /&gt;
        -- Return datastore instance.&lt;br /&gt;
        return ds&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Gets the language code of the wiki or page contents.&lt;br /&gt;
--  @function           i18n.getContentLang&lt;br /&gt;
--  @usage              {{i18n|getContentLang}}&lt;br /&gt;
--  @return             {string} Content language code.&lt;br /&gt;
function i18n.getContentLang()&lt;br /&gt;
    local code = mw.language.getContentLanguage():getCode()&lt;br /&gt;
    local subPage = title.subpageText&lt;br /&gt;
&lt;br /&gt;
    -- Subpage language test.&lt;br /&gt;
    if title.isSubpage and _i18n.isValidCode(subPage) then&lt;br /&gt;
        code = _i18n.isValidCode(subPage) and subPage or code&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    return code&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Gets the language code of the content.&lt;br /&gt;
--  Can validate a template&#039;s language code through `uselang` parameter.&lt;br /&gt;
--  @function           i18n.getLang&lt;br /&gt;
--  @usage              {{i18n|getLang|uselang {{=}} code}}&lt;br /&gt;
--  @return             {string} Language code.&lt;br /&gt;
function i18n.getLang()&lt;br /&gt;
    local frame = mw.getCurrentFrame() or {}&lt;br /&gt;
    local parentFrame = frame.getParent and frame:getParent() or {}&lt;br /&gt;
&lt;br /&gt;
    local code = i18n.getContentLang()&lt;br /&gt;
&lt;br /&gt;
    -- Language argument test.&lt;br /&gt;
    local langOverride =&lt;br /&gt;
        (frame.args or {}).uselang or&lt;br /&gt;
        (parentFrame.args or {}).uselang&lt;br /&gt;
    if _i18n.isValidCode(langOverride) then&lt;br /&gt;
        code = langOverride&lt;br /&gt;
&lt;br /&gt;
    -- User language test.&lt;br /&gt;
    elseif parentFrame.preprocess or frame.preprocess then&lt;br /&gt;
        uselang = uselang&lt;br /&gt;
            or  parentFrame.preprocess&lt;br /&gt;
                and parentFrame:preprocess(&#039;{{int:lang}}&#039;)&lt;br /&gt;
                or  frame:preprocess(&#039;{{int:lang}}&#039;)&lt;br /&gt;
        local decodedLang = mw.text.decode(uselang) &lt;br /&gt;
        if decodedLang ~= &#039;&amp;lt;lang&amp;gt;&#039; and decodedLang ~= &#039;⧼lang⧽&#039; then&lt;br /&gt;
            code = decodedLang == &#039;(lang)&#039;&lt;br /&gt;
                and &#039;qqx&#039;&lt;br /&gt;
                or  uselang&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return code&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Template wrapper for [[Template:I18n]].&lt;br /&gt;
--  @function           i18n.main&lt;br /&gt;
--  @param              {table} frame Frame invocation object.&lt;br /&gt;
--  @return             {string} Module output in template context.&lt;br /&gt;
--  @usage              {{#invoke:i18n|main}}&lt;br /&gt;
i18n.main = entrypoint(i18n)&lt;br /&gt;
&lt;br /&gt;
return i18n&lt;br /&gt;
-- &amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Cronos</name></author>
	</entry>
	<entry>
		<id>https://hna.wiki/Module:Arguments?diff=8797</id>
		<title>Module:Arguments</title>
		<link rel="alternate" type="text/html" href="https://hna.wiki/Module:Arguments?diff=8797"/>
		<updated>2026-07-13T10:46:15Z</updated>

		<summary type="html">&lt;p&gt;Cronos: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--- Arguments invocation argument extractor for Scribunto modules.&lt;br /&gt;
--  It is intended for use by other Lua modules, and should not be&lt;br /&gt;
--  called from an invocation (`#invoke`) directly.&lt;br /&gt;
--  &lt;br /&gt;
--  This module supports the following features:&lt;br /&gt;
--   * Trimming and blank argument removal.&lt;br /&gt;
--   * Argument inheritance between child and parent frames.&lt;br /&gt;
--   * Argument extraction for external modules and console input.&lt;br /&gt;
--   * Options to customise argument extraction behaviour.&lt;br /&gt;
--  &lt;br /&gt;
--  @script             arguments&lt;br /&gt;
--  @release            stable&lt;br /&gt;
--  @note               The `args` table from the @{arguments.getArgs}&lt;br /&gt;
--                      function is a metatable for performance reasons.&lt;br /&gt;
--                      Thus, the table will not permit Lua table methods&lt;br /&gt;
--                      such as `#args`, @{next|next(args)}, and @{table}&lt;br /&gt;
--                      library functions.&lt;br /&gt;
--  @note               This module will eventually be adapted as a&lt;br /&gt;
--                      library in [[mw:gerrit:q/158323|MediaWiki&lt;br /&gt;
--                      core]], called as `require(&#039;getArgs&#039;)`. The core&lt;br /&gt;
--                      library will remove `options.parentOnly`.&lt;br /&gt;
--  @author             [[wikipedia:User:Mr. Stradivarius|Mr. Stradivarius]] (Wikipedia)&lt;br /&gt;
--  @author             [[wikipedia:User:Anomie|Anomie]] (Wikipedia)&lt;br /&gt;
--  @author             [[wikipedia:User:Jackmcbarn|Jackmcbarn]] (Wikipedia)&lt;br /&gt;
--  @author             [[User:Dessamator|Dessamator]]&lt;br /&gt;
--  @author             [[User:DarthKitty|DarthKitty]]&lt;br /&gt;
--  @attribution        [[wikipedia:Module:Arguments|Module:Arguments]] (Wikipedia)&lt;br /&gt;
--  @see                [[wikipedia:Module:Arguments|Original module on Wikipedia]]&lt;br /&gt;
--  @see                [[Module:Arguments/testcases|Test cases for this module]]&lt;br /&gt;
local arguments = {}&lt;br /&gt;
&lt;br /&gt;
--  Module dependencies.&lt;br /&gt;
local i18n = require(&#039;Dev:I18n&#039;).loadMessages(&#039;Arguments&#039;)&lt;br /&gt;
local util = require(&#039;libraryUtil&#039;)&lt;br /&gt;
local checkType = util.checkType&lt;br /&gt;
&lt;br /&gt;
-- Four different value tidying functions.&lt;br /&gt;
-- This way, we don&#039;t have to check the options every time we call them.&lt;br /&gt;
&lt;br /&gt;
--- Default value tidying function.&lt;br /&gt;
--  Trims parameter values automatically if they are defined strings.&lt;br /&gt;
--  Treats blank strings as `nil`.&lt;br /&gt;
--  @function           tidyValDefault&lt;br /&gt;
--  @param              {string|number} key MediaWiki parameter key.&lt;br /&gt;
--  @param              {string|nil} val MediaWiki parameter value,&lt;br /&gt;
--                      or nil if `key` is an empty string or nil.&lt;br /&gt;
--  @local&lt;br /&gt;
local function tidyValDefault(key, val)&lt;br /&gt;
    if type(val) == &#039;string&#039; then&lt;br /&gt;
        val = val:match(&#039;^%s*(.-)%s*$&#039;)&lt;br /&gt;
        if val == &#039;&#039; then&lt;br /&gt;
            return nil&lt;br /&gt;
        else&lt;br /&gt;
            return val&lt;br /&gt;
        end&lt;br /&gt;
    else&lt;br /&gt;
        return val&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Value tidying function that trims values.&lt;br /&gt;
--  Trims parameter values automatically if they are defined strings.&lt;br /&gt;
--  @function           tidyValTrimOnly&lt;br /&gt;
--  @param              {string|number} key MediaWiki parameter key.&lt;br /&gt;
--  @param              {string|nil} val MediaWiki parameter value.&lt;br /&gt;
--  @local&lt;br /&gt;
local function tidyValTrimOnly(key, val)&lt;br /&gt;
    if type(val) == &#039;string&#039; then&lt;br /&gt;
        return val:match(&#039;^%s*(.-)%s*$&#039;)&lt;br /&gt;
    else&lt;br /&gt;
        return val&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Value tidying function that removes blanks.&lt;br /&gt;
--  Removes blank values from the arguments table.&lt;br /&gt;
--  @function           tidyValRemoveBlanksOnly&lt;br /&gt;
--  @param              {string|number} key MediaWiki parameter key.&lt;br /&gt;
--  @param              {string|nil} val MediaWiki parameter value,&lt;br /&gt;
--                      or nil if `key` is whitespace or nil.&lt;br /&gt;
--  @local&lt;br /&gt;
local function tidyValRemoveBlanksOnly(key, val)&lt;br /&gt;
    if type(val) == &#039;string&#039; then&lt;br /&gt;
        if val:find(&#039;%S&#039;) then&lt;br /&gt;
            return val&lt;br /&gt;
        else&lt;br /&gt;
            return nil&lt;br /&gt;
        end&lt;br /&gt;
    else&lt;br /&gt;
        return val&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Value tidying function that returns original value.&lt;br /&gt;
--  Effectively a NOOP function that does no value processing.&lt;br /&gt;
--  @function           tidyValNoChange&lt;br /&gt;
--  @param              {string|number} key MediaWiki parameter key.&lt;br /&gt;
--  @param              {string|nil} val MediaWiki parameter value.&lt;br /&gt;
--  @local&lt;br /&gt;
local function tidyValNoChange(key, val)&lt;br /&gt;
    return val&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Parent template title match checker.&lt;br /&gt;
--  @function           matchesTitle&lt;br /&gt;
--  @param              {string|number|nil} given Local prefixed page&lt;br /&gt;
--                      title, or MediaWiki article ID (`wgArticleId`).&lt;br /&gt;
--  @param              {string} title Title of parent template.&lt;br /&gt;
--  @return             {boolean} Whether the `given` ID/title matches&lt;br /&gt;
--                      the title of the parent template.&lt;br /&gt;
local function matchesTitle(given, title)&lt;br /&gt;
    local tp = type( given )&lt;br /&gt;
    return (tp == &#039;string&#039; or tp == &#039;number&#039;) and mw.title.new( given ).prefixedText == title&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Default argument translation metatable.&lt;br /&gt;
--  @table              translate_mt&lt;br /&gt;
--  @local&lt;br /&gt;
local translate_mt = { __index = function(t, k) return k end }&lt;br /&gt;
&lt;br /&gt;
--- Main argument extraction utility.&lt;br /&gt;
--  Arguments are memoized once fetched for optimal performance,&lt;br /&gt;
--  as with the `frame.args` metatable in Scribunto core.&lt;br /&gt;
--  &lt;br /&gt;
--  The default argument lookup behaviour uses the child frame arguments&lt;br /&gt;
--  first, then the parent frame arguments. There are numerous frame&lt;br /&gt;
--  options to change this behaviour.&lt;br /&gt;
--  &lt;br /&gt;
--  The default value tidying behaviour trims parameter values if they&lt;br /&gt;
--  are defined strings and treats blank strings as `nil`. This can be&lt;br /&gt;
--  customised in the `getArgs` options.&lt;br /&gt;
--  &lt;br /&gt;
--  @param              {frame|table} frame Scribunto frame object or&lt;br /&gt;
--                      Lua arguments table, passed from an invocation&lt;br /&gt;
--                      or Lua logic such as `frame:getParent()`.&lt;br /&gt;
--                      If this parameter does not have an `args` field&lt;br /&gt;
--                      and a `getParent` method, `frame` is assumed&lt;br /&gt;
--                      to be a Lua arguments table, such as the&lt;br /&gt;
--                      arguments from a named arguments call.&lt;br /&gt;
--  @param[opt]         {table} options Extraction/processing options.&lt;br /&gt;
--  @param[opt]         {boolean} options.trim&lt;br /&gt;
--                      Whether to trim the blank arguments present in&lt;br /&gt;
--                      the arguments table. Accepts `false` only.&lt;br /&gt;
--                      Default: `true`.&lt;br /&gt;
--  @param[opt]         {boolean} options.removeBlanks&lt;br /&gt;
--                      Whether to remove blank arguments from the&lt;br /&gt;
--                      arguments table. Does not shift sequential&lt;br /&gt;
--                      arguments removed by the processing stage.&lt;br /&gt;
--                      Accepts `false` only. Default: `true`.&lt;br /&gt;
--  @param[opt]         {function} options.valueFunc&lt;br /&gt;
--                      Custom value tidying function for use if the&lt;br /&gt;
--                      `trim` and `removeBlanks` options don&#039;t cover&lt;br /&gt;
--                      the developer&#039;s argument processing use case.&lt;br /&gt;
--  @param[opt]         {boolean} options.frameOnly&lt;br /&gt;
--                      Only read arguments from child frame (the&lt;br /&gt;
--                      `frame` parameter - usually invocation frame).&lt;br /&gt;
--  @param[opt]         {boolean} options.parentOnly&lt;br /&gt;
--                      Only read arguments from `frame` parent (the&lt;br /&gt;
--                      `frame` parameter - usually template frame).&lt;br /&gt;
--  @param[opt]         {boolean} options.parentFirst&lt;br /&gt;
--                      Argument lookup in the `frame` parent first,&lt;br /&gt;
--                      prioritised over the invocation frame arguments.&lt;br /&gt;
--  @param[opt]         {table} options.wrappers&lt;br /&gt;
--                      Individual value or array of values, listing&lt;br /&gt;
--                      wrapper title name(s) or article ID(s) to permit&lt;br /&gt;
--                      parent argument lookup from.&lt;br /&gt;
--  @param[opt]         {string|number} options.wrapper&lt;br /&gt;
--                      Alias of `options.wrappers` - contains title&lt;br /&gt;
--                      name or article ID to permit parent argument&lt;br /&gt;
--                      lookup from.&lt;br /&gt;
--  @param[opt]         {boolean} options.readOnly&lt;br /&gt;
--                      Whether to restrict write permissions to the&lt;br /&gt;
--                      arguments table. When set to a truthy value,&lt;br /&gt;
--                      an error will be thrown on any write attempt.&lt;br /&gt;
--  @param[opt]         {boolean} options.noOverwrite&lt;br /&gt;
--                      Whether to restrict overwrite attempts on&lt;br /&gt;
--                      existing argument keys in the arguments table.&lt;br /&gt;
--                      When set to a truthy value, an error will be&lt;br /&gt;
--                      thrown on any write attempt that would result&lt;br /&gt;
--                      in an existing argument being overwritten.&lt;br /&gt;
--  @param[opt]         {table} options.translate&lt;br /&gt;
--                      Map of parameter name aliases to their canonical&lt;br /&gt;
--                      argument parameter names.&lt;br /&gt;
--  @param[opt]         {table} options.backtranslate&lt;br /&gt;
--                      Map of canonical parameter names to their&lt;br /&gt;
--                      argument parameter aliases.&lt;br /&gt;
--                      Supersedes `options.translate` if both options&lt;br /&gt;
--                      are in use.&lt;br /&gt;
--  @error[opt,317]     &#039;bad value assigned to option &amp;quot;valueFunc&amp;quot;&lt;br /&gt;
--                      (function expected, got $type)&#039;&lt;br /&gt;
--  @error[opt,407]     &#039;could not write to argument table key &amp;quot;$key&amp;quot;;&lt;br /&gt;
--                      the table is read-only&#039;&lt;br /&gt;
--  @error[opt,409]     &#039;could not write to argument table key &amp;quot;$key&amp;quot;;&lt;br /&gt;
--                      overwriting existing arguments is not permitted&#039;&lt;br /&gt;
--  @return             {table} Arguments extracted from invocation.&lt;br /&gt;
--                      The argument data is embedded as a metatable in&lt;br /&gt;
--                      the exported table and cannot be accessed with&lt;br /&gt;
--                      the `#` operator or @{table} library methods.&lt;br /&gt;
--                      However, the exported table can be written to if&lt;br /&gt;
--                      the `options.readOnly` flag parameter is not&lt;br /&gt;
--                      truthy.&lt;br /&gt;
--  @usage&lt;br /&gt;
--      &lt;br /&gt;
--      local getArgs = require(&#039;Module:Arguments&#039;).getArgs&lt;br /&gt;
--      function p.main(frame)&lt;br /&gt;
--          local args = getArgs(frame, {&lt;br /&gt;
--              wrapper = &#039;Template:&amp;lt;TEMPLATE&amp;gt;&#039;&lt;br /&gt;
--          })&lt;br /&gt;
--          -- Use the args table here.&lt;br /&gt;
--          -- A common paradigm is `return p._main(args)`.&lt;br /&gt;
--          -- This allows other Lua modules to access the&lt;br /&gt;
--          -- main logic in a performant manner without a&lt;br /&gt;
--          -- frame object.&lt;br /&gt;
--      end&lt;br /&gt;
--      &lt;br /&gt;
--  @note               Reference tags in the form of `&amp;lt;ref&amp;gt;` will&lt;br /&gt;
--                      generate phantom references when calling the&lt;br /&gt;
--                      @{pairs} iterator on the arguments table,&lt;br /&gt;
--                      **IF** the `&amp;lt;ref&amp;gt;` tag does not appear in the&lt;br /&gt;
--                      dependent module&#039;s wikitext output.&lt;br /&gt;
function arguments.getArgs(frame, options)&lt;br /&gt;
    checkType(&#039;getArgs&#039;, 1, frame, &#039;table&#039;, true)&lt;br /&gt;
    checkType(&#039;getArgs&#039;, 2, options, &#039;table&#039;, true)&lt;br /&gt;
    frame = frame or {}&lt;br /&gt;
    options = options or {}&lt;br /&gt;
&lt;br /&gt;
    -- Set up argument translation.&lt;br /&gt;
    options.translate = options.translate or {}&lt;br /&gt;
    if getmetatable(options.translate) == nil then&lt;br /&gt;
        setmetatable(options.translate, translate_mt)&lt;br /&gt;
    end&lt;br /&gt;
    if options.backtranslate == nil then&lt;br /&gt;
        options.backtranslate = {}&lt;br /&gt;
        for k,v in pairs(options.translate) do&lt;br /&gt;
            options.backtranslate[v] = k&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    if options.backtranslate and getmetatable(options.backtranslate) == nil then&lt;br /&gt;
        setmetatable(options.backtranslate, {&lt;br /&gt;
            __index = function(t, k)&lt;br /&gt;
                if options.translate[k] ~= k then&lt;br /&gt;
                    return nil&lt;br /&gt;
                else&lt;br /&gt;
                    return k&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
        })&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- Get the argument tables. If we were passed a valid frame object,&lt;br /&gt;
    -- get the frame arguments (fargs) and the parent frame arguments&lt;br /&gt;
    -- (pargs), depending on the options set and on the parent frame&#039;s&lt;br /&gt;
    -- availability. If we weren&#039;t passed a valid frame object, we are&lt;br /&gt;
    -- being called from another Lua module or from the debug console,&lt;br /&gt;
    -- so assume that we were passed a table of args directly, and&lt;br /&gt;
    -- assign it to a new variable (luaArgs).&lt;br /&gt;
    local fargs, pargs, luaArgs&lt;br /&gt;
    options.wrappers = options.wrappers or options.wrapper&lt;br /&gt;
    if&lt;br /&gt;
        type(frame.args) == &#039;table&#039; and&lt;br /&gt;
        type(frame.getParent) == &#039;function&#039;&lt;br /&gt;
    then&lt;br /&gt;
        -- The wrappers option makes Module:Arguments look up&lt;br /&gt;
        -- arguments in either the frame argument table or the&lt;br /&gt;
        -- parent argument table, but not both. This means that&lt;br /&gt;
        -- users can use either the #invoke syntax or a wrapper&lt;br /&gt;
        -- template without the loss of performance associated&lt;br /&gt;
        -- with looking arguments up in both the frame and the&lt;br /&gt;
        -- parent frame.&lt;br /&gt;
        -- The arguments will be fetched from the parent frame if&lt;br /&gt;
        -- the parent frame&#039;s title is present in options.wrapper;&lt;br /&gt;
        -- otherwise it will look up arguments in the frame object&lt;br /&gt;
        -- passed to getArgs.&lt;br /&gt;
        if options.wrappers then&lt;br /&gt;
            local parent = frame:getParent()&lt;br /&gt;
            if not parent then&lt;br /&gt;
                fargs = frame.args&lt;br /&gt;
            else&lt;br /&gt;
                local title = parent:getTitle():gsub(&#039;/sandbox$&#039;, &#039;&#039;)&lt;br /&gt;
                local found = false&lt;br /&gt;
                if matchesTitle(options.wrappers, title) then&lt;br /&gt;
                    found = true&lt;br /&gt;
                elseif type(options.wrappers) == &#039;table&#039; then&lt;br /&gt;
                    for _,v in pairs(options.wrappers) do&lt;br /&gt;
                        if matchesTitle(v, title) then&lt;br /&gt;
                            found = true&lt;br /&gt;
                            break&lt;br /&gt;
                        end&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
&lt;br /&gt;
                -- We test for false specifically here so that nil (the&lt;br /&gt;
                -- default) acts like true.&lt;br /&gt;
                if found or options.frameOnly == false then&lt;br /&gt;
                    pargs = parent.args&lt;br /&gt;
                end&lt;br /&gt;
                if not found or options.parentOnly == false then&lt;br /&gt;
                    fargs = frame.args&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
        -- When options.wrapper isn&#039;t set, check the other options.&lt;br /&gt;
        else&lt;br /&gt;
            if not options.parentOnly then&lt;br /&gt;
                fargs = frame.args&lt;br /&gt;
            end&lt;br /&gt;
            if not options.frameOnly then&lt;br /&gt;
                local parent = frame:getParent()&lt;br /&gt;
                pargs = parent and parent.args or nil&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
        if options.parentFirst then&lt;br /&gt;
            fargs, pargs = pargs, fargs&lt;br /&gt;
        end&lt;br /&gt;
    else&lt;br /&gt;
        luaArgs = frame&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- Set the order of precedence of the argument tables. If the variables are&lt;br /&gt;
    -- nil, nothing will be added to the table, which is how we avoid clashes&lt;br /&gt;
    -- between the frame/parent args and the Lua args.&lt;br /&gt;
    local argTables = {fargs}&lt;br /&gt;
    argTables[#argTables + 1] = pargs&lt;br /&gt;
    argTables[#argTables + 1] = luaArgs&lt;br /&gt;
&lt;br /&gt;
    -- Generate the tidyVal function. If it has been specified by the user, we&lt;br /&gt;
    -- use that; if not, we choose one of four functions depending on the&lt;br /&gt;
    -- options chosen. This is so that we don&#039;t have to call the options table&lt;br /&gt;
    -- every time the function is called.&lt;br /&gt;
    local tidyVal = options.valueFunc&lt;br /&gt;
    if tidyVal then&lt;br /&gt;
        if type(tidyVal) ~= &#039;function&#039; then&lt;br /&gt;
            error(i18n:msg(&#039;error-value-func&#039;, type(tidyVal)), 2)&lt;br /&gt;
        end&lt;br /&gt;
    elseif options.trim ~= false then&lt;br /&gt;
        if options.removeBlanks ~= false then&lt;br /&gt;
            tidyVal = tidyValDefault&lt;br /&gt;
        else&lt;br /&gt;
            tidyVal = tidyValTrimOnly&lt;br /&gt;
        end&lt;br /&gt;
    else&lt;br /&gt;
        if options.removeBlanks ~= false then&lt;br /&gt;
            tidyVal = tidyValRemoveBlanksOnly&lt;br /&gt;
        else&lt;br /&gt;
            tidyVal = tidyValNoChange&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- Set up the args, metaArgs and nilArgs tables. args will be the one&lt;br /&gt;
    -- accessed from functions, and metaArgs will hold the actual arguments. Nil&lt;br /&gt;
    -- arguments are memoized in nilArgs, and the metatable connects all of them&lt;br /&gt;
    -- together.&lt;br /&gt;
    local args, metaArgs, nilArgs, metatable = {}, {}, {}, {}&lt;br /&gt;
    setmetatable(args, metatable)&lt;br /&gt;
&lt;br /&gt;
    -- Accepts multiple tables as input and merges their keys and values&lt;br /&gt;
    -- into one table. If a value is already present it is not overwritten;&lt;br /&gt;
    -- tables listed earlier have precedence. We are also memoizing nil&lt;br /&gt;
    -- values, which can be overwritten if they are &#039;s&#039; (soft).&lt;br /&gt;
    local function mergeArgs(tables)&lt;br /&gt;
        for _, t in ipairs(tables) do&lt;br /&gt;
            for key, val in pairs(t) do&lt;br /&gt;
                if metaArgs[key] == nil and nilArgs[key] ~= &#039;h&#039; then&lt;br /&gt;
                    local tidiedVal = tidyVal(key, val)&lt;br /&gt;
                    if tidiedVal == nil then&lt;br /&gt;
                        nilArgs[key] = &#039;s&#039;&lt;br /&gt;
                    else&lt;br /&gt;
                        metaArgs[key] = tidiedVal&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- Define metatable behaviour. Arguments are memoized in the metaArgs table,&lt;br /&gt;
    -- and are only fetched from the argument tables once. Fetching arguments&lt;br /&gt;
    -- from the argument tables is the most resource-intensive step in this&lt;br /&gt;
    -- module, so we try and avoid it where possible. For this reason, nil&lt;br /&gt;
    -- arguments are also memoized, in the nilArgs table. Also, we keep a record&lt;br /&gt;
    -- in the metatable of when pairs and ipairs have been called, so we do not&lt;br /&gt;
    -- run pairs and ipairs on the argument tables more than once. We also do&lt;br /&gt;
    -- not run ipairs on fargs and pargs if pairs has already been run, as all&lt;br /&gt;
    -- the arguments will already have been copied over.&lt;br /&gt;
&lt;br /&gt;
    -- Fetches an argument when the args table is indexed. First we check&lt;br /&gt;
    -- to see if the value is memoized, and if not we try and fetch it from&lt;br /&gt;
    -- the argument tables. When we check memoization, we need to check&lt;br /&gt;
    -- metaArgs before nilArgs, as both can be non-nil at the same time.&lt;br /&gt;
    -- If the argument is not present in metaArgs, we also check whether&lt;br /&gt;
    -- pairs has been run yet. If pairs has already been run, we return nil.&lt;br /&gt;
    -- This is because all the arguments will have already been copied into&lt;br /&gt;
    -- metaArgs by the mergeArgs function, meaning that any other arguments&lt;br /&gt;
    -- must be nil.&lt;br /&gt;
    metatable.__index = function (t, key)&lt;br /&gt;
        if type(key) == &#039;string&#039; then&lt;br /&gt;
            key = options.translate[key]&lt;br /&gt;
        end&lt;br /&gt;
        local val = metaArgs[key]&lt;br /&gt;
        if val ~= nil then&lt;br /&gt;
            return val&lt;br /&gt;
        elseif metatable.donePairs or nilArgs[key] then&lt;br /&gt;
            return nil&lt;br /&gt;
        end&lt;br /&gt;
        for _, argTable in ipairs(argTables) do&lt;br /&gt;
            local argTableVal = tidyVal(key, argTable[key])&lt;br /&gt;
            if argTableVal ~= nil then&lt;br /&gt;
                metaArgs[key] = argTableVal&lt;br /&gt;
                return argTableVal&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
        nilArgs[key] = &#039;h&#039;&lt;br /&gt;
        return nil&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    metatable.__newindex = function (t, key, val)&lt;br /&gt;
        -- This function is called when a module tries to add a new&lt;br /&gt;
        -- value to the args table, or tries to change an existing&lt;br /&gt;
        -- value.&lt;br /&gt;
        if type(key) == &#039;string&#039; then&lt;br /&gt;
            key = options.translate[key]&lt;br /&gt;
        end&lt;br /&gt;
        if options.readOnly then&lt;br /&gt;
            error(i18n:msg(&#039;error-write-permission&#039;, tostring(key)), 2)&lt;br /&gt;
        elseif options.noOverwrite and args[key] ~= nil then&lt;br /&gt;
            error(i18n:msg(&#039;error-overwrite-permission&#039;, tostring(key)), 2)&lt;br /&gt;
        elseif val == nil then&lt;br /&gt;
            -- If the argument is to be overwritten with nil, we need to erase&lt;br /&gt;
            -- the value in metaArgs, so that __index, __pairs and __ipairs do&lt;br /&gt;
            -- not use a previous existing value, if present; and we also need&lt;br /&gt;
            -- to memoize the nil in nilArgs, so that the value isn&#039;t looked&lt;br /&gt;
            -- up in the argument tables if it is accessed again.&lt;br /&gt;
            metaArgs[key] = nil&lt;br /&gt;
            nilArgs[key] = &#039;h&#039;&lt;br /&gt;
        else&lt;br /&gt;
            metaArgs[key] = val&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local function translatenext(invariant)&lt;br /&gt;
        local k, v = next(invariant.t, invariant.k)&lt;br /&gt;
        invariant.k = k&lt;br /&gt;
        if k == nil then&lt;br /&gt;
            return nil&lt;br /&gt;
        elseif type(k) ~= &#039;string&#039; or not options.backtranslate then&lt;br /&gt;
            return k, v&lt;br /&gt;
        else&lt;br /&gt;
            local backtranslate = options.backtranslate[k]&lt;br /&gt;
            if backtranslate == nil then&lt;br /&gt;
                -- Skip this one. This is a tail call, so this&lt;br /&gt;
                -- won&#039;t cause stack overflow.&lt;br /&gt;
                return translatenext(invariant)&lt;br /&gt;
            else&lt;br /&gt;
                return backtranslate, v&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- This metamethod is called when pairs is run on the args table.&lt;br /&gt;
    metatable.__pairs = function ()&lt;br /&gt;
        if not metatable.donePairs then&lt;br /&gt;
            mergeArgs(argTables)&lt;br /&gt;
            metatable.donePairs = true&lt;br /&gt;
        end&lt;br /&gt;
        return translatenext, { t = metaArgs }&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- This custom `ipairs`-style iterator uses our __index metamethod.&lt;br /&gt;
    local function inext(t, i)&lt;br /&gt;
        local v = t[i + 1]&lt;br /&gt;
        if v ~= nil then&lt;br /&gt;
            return i + 1, v&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- This metamethod is called when ipairs is run on the args table.&lt;br /&gt;
    metatable.__ipairs = function (t)&lt;br /&gt;
        return inext, t, 0&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return args&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return arguments&lt;/div&gt;</summary>
		<author><name>Cronos</name></author>
	</entry>
	<entry>
		<id>https://hna.wiki/Module:Quote?diff=8796</id>
		<title>Module:Quote</title>
		<link rel="alternate" type="text/html" href="https://hna.wiki/Module:Quote?diff=8796"/>
		<updated>2026-07-13T10:45:45Z</updated>

		<summary type="html">&lt;p&gt;Cronos: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- &amp;lt;nowiki&amp;gt;&lt;br /&gt;
local Quote = {}&lt;br /&gt;
local getArgs = require(&#039;Dev:Arguments&#039;).getArgs&lt;br /&gt;
local i18n = require(&#039;Dev:I18n&#039;).loadMessages(&#039;Quote&#039;)&lt;br /&gt;
&lt;br /&gt;
local function build(quote_contents, quote_source, options)&lt;br /&gt;
	local quote_container = mw.html.create(&#039;blockquote&#039;)&lt;br /&gt;
		:addClass(&#039;pull-quote&#039;)&lt;br /&gt;
		:addClass(options.align)&lt;br /&gt;
		:addClass(options.extraclasses)&lt;br /&gt;
		:css(options.styles)&lt;br /&gt;
		:cssText(options.extrastyles)&lt;br /&gt;
		:node(quote_contents)&lt;br /&gt;
&lt;br /&gt;
	if quote_source then&lt;br /&gt;
		quote_container:tag(&#039;div&#039;)&lt;br /&gt;
			:addClass(&#039;pull-quote__source&#039;)&lt;br /&gt;
				:tag(&#039;cite&#039;)&lt;br /&gt;
				:wikitext(quote_source)&lt;br /&gt;
				:done()&lt;br /&gt;
			:done()&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return quote_container&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function options(args)&lt;br /&gt;
	local options = {}&lt;br /&gt;
&lt;br /&gt;
	options.styles = {}&lt;br /&gt;
	options.extraclasses = i18n:parameter(&#039;class&#039;, args)&lt;br /&gt;
	options.extrastyles = i18n:parameter(&#039;style&#039;, args)&lt;br /&gt;
	options.align = &#039;&#039;&lt;br /&gt;
	local align = i18n:parameter(&#039;align&#039;, args)&lt;br /&gt;
	if align then&lt;br /&gt;
		options.align = &#039;pull-quote--&#039; .. align&lt;br /&gt;
		options.styles[&#039;width&#039;] = i18n:parameter(&#039;width&#039;, args) or&lt;br /&gt;
								  i18n:parameter(&#039;quotewidth&#039;, args) or&lt;br /&gt;
								  &#039;300px&#039;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return options&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- let MediaWiki parser auto-generate &amp;lt;p&amp;gt; tags&lt;br /&gt;
local function paragraph(quotetext)&lt;br /&gt;
	return &#039;\n&#039; .. quotetext .. &#039;\n&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Quote.quote(frame)&lt;br /&gt;
	local args = getArgs(frame)&lt;br /&gt;
&lt;br /&gt;
	local options = options(args)&lt;br /&gt;
&lt;br /&gt;
	local quotetext = args[1] or&lt;br /&gt;
					  i18n:parameter(&#039;quotetext&#039;, args) or&lt;br /&gt;
					  i18n:parameter(&#039;quote&#039;, args) or&lt;br /&gt;
					  i18n:parameter(&#039;text&#039;, args) or &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
	local person = args[2] or&lt;br /&gt;
				   i18n:parameter(&#039;person&#039;, args) or&lt;br /&gt;
				   i18n:parameter(&#039;speaker&#039;, args) or&lt;br /&gt;
				   i18n:parameter(&#039;personquoted&#039;, args) or nil&lt;br /&gt;
&lt;br /&gt;
	local source = args[3] or&lt;br /&gt;
				   i18n:parameter(&#039;source&#039;, args) or&lt;br /&gt;
				   i18n:parameter(&#039;quote_source&#039;, args) or nil&lt;br /&gt;
&lt;br /&gt;
	local quote_contents = mw.html.create(&#039;div&#039;)&lt;br /&gt;
		:addClass(&#039;pull-quote__text&#039;)&lt;br /&gt;
		:wikitext(paragraph(quotetext))&lt;br /&gt;
&lt;br /&gt;
	local quote_source = person&lt;br /&gt;
&lt;br /&gt;
	if person and source then&lt;br /&gt;
		quote_source = person .. &#039;, &#039; .. source&lt;br /&gt;
	elseif source then&lt;br /&gt;
		quote_source = source&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return build(quote_contents, quote_source, options)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Quote.dialogue(frame)&lt;br /&gt;
	local args = getArgs(frame)&lt;br /&gt;
&lt;br /&gt;
	local options = options(args)&lt;br /&gt;
&lt;br /&gt;
	local quote_contents = mw.html.create(&#039;div&#039;)&lt;br /&gt;
		:addClass(&#039;pull-quote__text&#039;)&lt;br /&gt;
&lt;br /&gt;
	local quote_source&lt;br /&gt;
&lt;br /&gt;
	for i, v in ipairs(args) do&lt;br /&gt;
		local next_param = i + 1&lt;br /&gt;
		&lt;br /&gt;
		if i % 2 ~= 0 then&lt;br /&gt;
			quote_contents:tag(&#039;div&#039;)&lt;br /&gt;
				:addClass(&#039;pull-quote__line&#039;)&lt;br /&gt;
				:tag(&#039;strong&#039;)&lt;br /&gt;
					:addClass(&#039;pull-quote__speaker&#039;)&lt;br /&gt;
					:wikitext(v .. &#039;:&#039;)&lt;br /&gt;
					:done()&lt;br /&gt;
				:wikitext(&#039; &#039; .. paragraph(args[next_param]))&lt;br /&gt;
				:done()&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local context = i18n:parameter(&#039;context&#039;, args)&lt;br /&gt;
	local source = i18n:parameter(&#039;source&#039;, args)&lt;br /&gt;
	if context and source then&lt;br /&gt;
		quote_source = context .. &#039;, &#039; .. source&lt;br /&gt;
	elseif context and not source then&lt;br /&gt;
		quote_source = context&lt;br /&gt;
	elseif source and not context then&lt;br /&gt;
		quote_source = source&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return build(quote_contents, quote_source, options)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return Quote&lt;br /&gt;
-- &amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Cronos</name></author>
	</entry>
	<entry>
		<id>https://hna.wiki/User:Shwll13?diff=8794</id>
		<title>User:Shwll13</title>
		<link rel="alternate" type="text/html" href="https://hna.wiki/User:Shwll13?diff=8794"/>
		<updated>2026-07-13T10:44:02Z</updated>

		<summary type="html">&lt;p&gt;Cronos: Cronos moved page User:Shirra to User:Shwll13: Automatically moved page while renaming the user &amp;quot;Shirra&amp;quot; to &amp;quot;Shwll13&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Custom Tabs|tab2=Sandbox}}&lt;br /&gt;
{{Quote|Moneytree, Moneytree, one shake to live in luxury&amp;lt;br&amp;gt;Moneytree, Moneytree, fast cash and that&#039;s a guarantee&amp;lt;br&amp;gt;Moneytree, Moneytree, the Nexus gonna make it rain&amp;lt;br&amp;gt;Moneytree, Moneytree, there&#039;s no brakes on the fortune train}}&lt;br /&gt;
==Checklist==&lt;br /&gt;
* Lumeridon&lt;br /&gt;
* Donubi&lt;br /&gt;
* Mushgloomini&lt;br /&gt;
* Faeflux (yes...)&lt;br /&gt;
* Nomnom (yes)&lt;br /&gt;
* Pupanoo (nah)&lt;br /&gt;
* Damasque (yes, cbt2 portrait is also outdated)&lt;br /&gt;
* Vixemourne (no)&lt;br /&gt;
* Camcorgi&lt;br /&gt;
* Hoppelon&lt;br /&gt;
* Pomparrot&lt;br /&gt;
* Lumehart&lt;br /&gt;
* Derpydile&lt;br /&gt;
* Blade (splash art defo got updated)&lt;br /&gt;
&lt;br /&gt;
==Gone==&lt;br /&gt;
* Scorscythe&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Others==&lt;br /&gt;
*Aspect Fracture -&amp;gt; Aspect contamination (Kiana&#039;s bio)&lt;br /&gt;
&lt;br /&gt;
==Media Products w/ Authors==&lt;br /&gt;
===Camcorgi Moments Photography Studio===&lt;br /&gt;
* [https://www.youtube.com/watch?v=KfPCXYYRBDc Planar Transmission: An Anima Fashion Brand, Modeled by Lumehart | #HonkaiNexusAnima]&lt;br /&gt;
* Iia Visitor&#039;s Guide: [https://www.hoyolab.com/article/45594251 Vol. 1], [https://www.hoyolab.com/article/45647745 Vol. 2]&lt;br /&gt;
&lt;br /&gt;
===ICM Finity Pictures Co., LTD. (Finity Plane)===&lt;br /&gt;
* [https://www.youtube.com/watch?v=SQgkvqLYuTE Planar Transmission: Trailer for a Psychological Thriller Starring Noctumbra | #HonkaiNexusAnima]&lt;/div&gt;</summary>
		<author><name>Cronos</name></author>
	</entry>
	<entry>
		<id>https://hna.wiki/MediaWiki:Weirdgloop-contact?diff=8792</id>
		<title>MediaWiki:Weirdgloop-contact</title>
		<link rel="alternate" type="text/html" href="https://hna.wiki/MediaWiki:Weirdgloop-contact?diff=8792"/>
		<updated>2026-07-13T09:33:52Z</updated>

		<summary type="html">&lt;p&gt;Cronos: Created page with &amp;quot;Contact&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Contact&lt;/div&gt;</summary>
		<author><name>Cronos</name></author>
	</entry>
	<entry>
		<id>https://hna.wiki/File:Favicon.ico?diff=8790</id>
		<title>File:Favicon.ico</title>
		<link rel="alternate" type="text/html" href="https://hna.wiki/File:Favicon.ico?diff=8790"/>
		<updated>2026-07-13T09:04:59Z</updated>

		<summary type="html">&lt;p&gt;Cronos: Cronos uploaded File:Favicon.ico&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Licensing ==&lt;br /&gt;
{{CC-BY-SA}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Wiki skin images]]&lt;/div&gt;</summary>
		<author><name>Cronos</name></author>
	</entry>
	<entry>
		<id>https://hna.wiki/Module:Namespace_detect/config?diff=8787</id>
		<title>Module:Namespace detect/config</title>
		<link rel="alternate" type="text/html" href="https://hna.wiki/Module:Namespace_detect/config?diff=8787"/>
		<updated>2026-07-13T07:27:24Z</updated>

		<summary type="html">&lt;p&gt;Cronos: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--------------------------------------------------------------------------------&lt;br /&gt;
--                    Namespace detect configuration data                     --&lt;br /&gt;
--                                                                            --&lt;br /&gt;
-- This module stores configuration data for Module:Namespace detect. Here    --&lt;br /&gt;
-- you can localise the module to your wiki&#039;s language.                       --&lt;br /&gt;
--                                                                            --&lt;br /&gt;
-- To activate a configuration item, you need to uncomment it. This means     --&lt;br /&gt;
-- that you need to remove the text &amp;quot;-- &amp;quot; at the start of the line.           --&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local cfg = {} -- Don&#039;t edit this line.&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
--                              Parameter names                               --&lt;br /&gt;
-- These configuration items specify custom parameter names. Values added     --&lt;br /&gt;
-- here will work in addition to the default English parameter names.         --&lt;br /&gt;
-- To add one extra name, you can use this format:                            --&lt;br /&gt;
--                                                                            --&lt;br /&gt;
-- cfg.foo = &#039;parameter name&#039;                                                 --&lt;br /&gt;
--                                                                            --&lt;br /&gt;
-- To add multiple names, you can use this format:                            --&lt;br /&gt;
--                                                                            --&lt;br /&gt;
-- cfg.foo = {&#039;parameter name 1&#039;, &#039;parameter name 2&#039;, &#039;parameter name 3&#039;}     --&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
---- This parameter displays content for the main namespace:&lt;br /&gt;
-- cfg.main = &#039;main&#039;&lt;br /&gt;
&lt;br /&gt;
---- This parameter displays in talk namespaces:&lt;br /&gt;
-- cfg.talk = &#039;talk&#039;&lt;br /&gt;
&lt;br /&gt;
---- This parameter displays content for &amp;quot;other&amp;quot; namespaces (namespaces for which&lt;br /&gt;
---- parameters have not been specified):&lt;br /&gt;
-- cfg.other = &#039;other&#039;&lt;br /&gt;
&lt;br /&gt;
---- This parameter makes talk pages behave as though they are the corresponding&lt;br /&gt;
---- subject namespace. Note that this parameter is used with [[Module:Yesno]].&lt;br /&gt;
---- Edit that module to change the default values of &amp;quot;yes&amp;quot;, &amp;quot;no&amp;quot;, etc.&lt;br /&gt;
-- cfg.subjectns = &#039;subjectns&#039;&lt;br /&gt;
&lt;br /&gt;
---- This parameter sets a demonstration namespace:&lt;br /&gt;
-- cfg.demospace = &#039;demospace&#039;&lt;br /&gt;
&lt;br /&gt;
---- This parameter sets a specific page to compare:&lt;br /&gt;
cfg.demopage = &#039;page&#039;&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
--                           Table configuration                              --&lt;br /&gt;
-- These configuration items allow customisation of the &amp;quot;table&amp;quot; function,     --&lt;br /&gt;
-- used to generate a table of possible parameters in the module              --&lt;br /&gt;
-- documentation.                                                             --&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
---- The header for the namespace column in the wikitable containing the list of&lt;br /&gt;
---- possible subject-space parameters.&lt;br /&gt;
-- cfg.wikitableNamespaceHeader = &#039;Namespace&#039;&lt;br /&gt;
&lt;br /&gt;
---- The header for the wikitable containing the list of possible subject-space&lt;br /&gt;
---- parameters.&lt;br /&gt;
-- cfg.wikitableAliasesHeader = &#039;Aliases&#039;&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
--                        End of configuration data                           --&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
return cfg -- Don&#039;t edit this line.&lt;/div&gt;</summary>
		<author><name>Cronos</name></author>
	</entry>
	<entry>
		<id>https://hna.wiki/Module:Namespace_detect?diff=8786</id>
		<title>Module:Namespace detect</title>
		<link rel="alternate" type="text/html" href="https://hna.wiki/Module:Namespace_detect?diff=8786"/>
		<updated>2026-07-13T07:27:12Z</updated>

		<summary type="html">&lt;p&gt;Cronos: Reverted edit by Cronos (talk) to last revision by 919dd&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- &amp;lt;nowiki&amp;gt;&lt;br /&gt;
--[[&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
--                            NAMESPACE DETECT                                --&lt;br /&gt;
--                                                                            --&lt;br /&gt;
-- This module implements the {{namespace detect}} template in Lua, with a    --&lt;br /&gt;
-- few improvements: all namespaces and all namespace aliases are supported,  --&lt;br /&gt;
-- and namespace names are detected automatically for the local wiki. The     --&lt;br /&gt;
-- module can also use the corresponding subject namespace value if it is     --&lt;br /&gt;
-- used on a talk page. Parameter names can be configured for different wikis --&lt;br /&gt;
-- by altering the values in the &amp;quot;cfg&amp;quot; table in                               --&lt;br /&gt;
-- Module:Namespace detect/config.                                            --&lt;br /&gt;
--                                                                            --&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
--]]&lt;br /&gt;
&lt;br /&gt;
local data = mw.title.new( &#039;Module:Namespace detect/data&#039;).exists and mw.loadData(&#039;Module:Namespace detect/data&#039;)&lt;br /&gt;
local argKeys = data.argKeys&lt;br /&gt;
local cfg = data.cfg&lt;br /&gt;
local mappings = data.mappings&lt;br /&gt;
&lt;br /&gt;
local yesno = require(&#039;Module:Yesno&#039;)&lt;br /&gt;
local mArguments -- Lazily initialise Module:Arguments&lt;br /&gt;
local mTableTools -- Lazily initilalise Module:TableTools&lt;br /&gt;
local ustringLower = mw.ustring.lower&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
local function fetchValue(t1, t2)&lt;br /&gt;
	-- Fetches a value from the table t1 for the first key in array t2 where&lt;br /&gt;
	-- a non-nil value of t1 exists.&lt;br /&gt;
	for i, key in ipairs(t2) do&lt;br /&gt;
		local value = t1[key]&lt;br /&gt;
		if value ~= nil then&lt;br /&gt;
			return value&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function equalsArrayValue(t, value)&lt;br /&gt;
	-- Returns true if value equals a value in the array t. Otherwise&lt;br /&gt;
	-- returns false.&lt;br /&gt;
	for i, arrayValue in ipairs(t) do&lt;br /&gt;
		if value == arrayValue then&lt;br /&gt;
			return true&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.getPageObject(page)&lt;br /&gt;
	-- Get the page object, passing the function through pcall in case of&lt;br /&gt;
	-- errors, e.g. being over the expensive function count limit.&lt;br /&gt;
	if page then&lt;br /&gt;
		local success, pageObject = pcall(mw.title.new, page)&lt;br /&gt;
		if success then&lt;br /&gt;
			return pageObject&lt;br /&gt;
		else&lt;br /&gt;
			return nil&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		return mw.title.getCurrentTitle()&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Provided for backward compatibility with other modules&lt;br /&gt;
function p.getParamMappings()&lt;br /&gt;
	return mappings&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getNamespace(args)&lt;br /&gt;
	-- This function gets the namespace name from the page object.&lt;br /&gt;
	local page = fetchValue(args, argKeys.demopage)&lt;br /&gt;
	if page == &#039;&#039; then&lt;br /&gt;
		page = nil&lt;br /&gt;
	end&lt;br /&gt;
	local demospace = fetchValue(args, argKeys.demospace)&lt;br /&gt;
	if demospace == &#039;&#039; then&lt;br /&gt;
		demospace = nil&lt;br /&gt;
	end&lt;br /&gt;
	local subjectns = fetchValue(args, argKeys.subjectns)&lt;br /&gt;
	local ret&lt;br /&gt;
	if demospace then&lt;br /&gt;
		-- Handle &amp;quot;demospace = main&amp;quot; properly.&lt;br /&gt;
		if equalsArrayValue(argKeys.main, ustringLower(demospace)) then&lt;br /&gt;
			ret = mw.site.namespaces[0].name&lt;br /&gt;
		else&lt;br /&gt;
			ret = demospace&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		local pageObject = p.getPageObject(page)&lt;br /&gt;
		if pageObject then&lt;br /&gt;
			if pageObject.isTalkPage then&lt;br /&gt;
				-- Get the subject namespace if the option is set,&lt;br /&gt;
				-- otherwise use &amp;quot;talk&amp;quot;.&lt;br /&gt;
				if yesno(subjectns) then&lt;br /&gt;
					ret = mw.site.namespaces[pageObject.namespace].subject.name&lt;br /&gt;
				else&lt;br /&gt;
					ret = &#039;talk&#039;&lt;br /&gt;
				end&lt;br /&gt;
			else&lt;br /&gt;
				ret = pageObject.nsText&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			return nil -- return nil if the page object doesn&#039;t exist.&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	ret = ret:gsub(&#039;_&#039;, &#039; &#039;)&lt;br /&gt;
	return ustringLower(ret)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p._main(args)&lt;br /&gt;
	-- Check the parameters stored in the mappings table for any matches.&lt;br /&gt;
	local namespace = getNamespace(args) or &#039;other&#039; -- &amp;quot;other&amp;quot; avoids nil table keys&lt;br /&gt;
	local params = mappings[namespace] or {}&lt;br /&gt;
	local ret = fetchValue(args, params)&lt;br /&gt;
	--[[&lt;br /&gt;
	-- If there were no matches, return parameters for other namespaces.&lt;br /&gt;
	-- This happens if there was no text specified for the namespace that&lt;br /&gt;
	-- was detected or if the demospace parameter is not a valid&lt;br /&gt;
	-- namespace. Note that the parameter for the detected namespace must be&lt;br /&gt;
	-- completely absent for this to happen, not merely blank.&lt;br /&gt;
	--]]&lt;br /&gt;
	if ret == nil then&lt;br /&gt;
		ret = fetchValue(args, argKeys.other)&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.main(frame)&lt;br /&gt;
	mArguments = require(&#039;Module:Arguments&#039;)&lt;br /&gt;
	local args = mArguments.getArgs(frame, {removeBlanks = false})&lt;br /&gt;
	local ret = p._main(args)&lt;br /&gt;
	return ret or &#039;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.table(frame)&lt;br /&gt;
	--[[&lt;br /&gt;
	-- Create a wikitable of all subject namespace parameters, for&lt;br /&gt;
	-- documentation purposes. The talk parameter is optional, in case it&lt;br /&gt;
	-- needs to be excluded in the documentation.&lt;br /&gt;
	--]]&lt;br /&gt;
	&lt;br /&gt;
	-- Load modules and initialise variables.&lt;br /&gt;
	mTableTools = require(&#039;Module:TableTools&#039;)&lt;br /&gt;
	local namespaces = mw.site.namespaces&lt;br /&gt;
	local cfg = data.cfg&lt;br /&gt;
	local useTalk = type(frame) == &#039;table&#039; &lt;br /&gt;
		and type(frame.args) == &#039;table&#039; &lt;br /&gt;
		and yesno(frame.args.talk) -- Whether to use the talk parameter.&lt;br /&gt;
	&lt;br /&gt;
	-- Get the header names.&lt;br /&gt;
	local function checkValue(value, default)&lt;br /&gt;
		if type(value) == &#039;string&#039; then&lt;br /&gt;
			return value&lt;br /&gt;
		else&lt;br /&gt;
			return default&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	local nsHeader = checkValue(cfg.wikitableNamespaceHeader, &#039;Namespace&#039;)&lt;br /&gt;
	local aliasesHeader = checkValue(cfg.wikitableAliasesHeader, &#039;Aliases&#039;)&lt;br /&gt;
&lt;br /&gt;
	-- Put the namespaces in order.&lt;br /&gt;
	local mappingsOrdered = {}&lt;br /&gt;
	for nsname, params in pairs(mappings) do&lt;br /&gt;
		if useTalk or nsname ~= &#039;talk&#039; then&lt;br /&gt;
			local nsid = namespaces[nsname].id&lt;br /&gt;
			-- Add 1, as the array must start with 1; nsid 0 would be lost otherwise.&lt;br /&gt;
			nsid = nsid + 1 &lt;br /&gt;
			mappingsOrdered[nsid] = params&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	mappingsOrdered = mTableTools.compressSparseArray(mappingsOrdered)&lt;br /&gt;
&lt;br /&gt;
	-- Build the table.&lt;br /&gt;
	local ret = &#039;{| class=&amp;quot;wikitable&amp;quot;&#039;&lt;br /&gt;
		.. &#039;\n|-&#039;&lt;br /&gt;
		.. &#039;\n! &#039; .. nsHeader&lt;br /&gt;
		.. &#039;\n! &#039; .. aliasesHeader&lt;br /&gt;
	for i, params in ipairs(mappingsOrdered) do&lt;br /&gt;
		for j, param in ipairs(params) do&lt;br /&gt;
			if j == 1 then&lt;br /&gt;
				ret = ret .. &#039;\n|-&#039;&lt;br /&gt;
					.. &#039;\n| &amp;lt;code&amp;gt;&#039; .. param .. &#039;&amp;lt;/code&amp;gt;&#039;&lt;br /&gt;
					.. &#039;\n| &#039;&lt;br /&gt;
			elseif j == 2 then&lt;br /&gt;
				ret = ret .. &#039;&amp;lt;code&amp;gt;&#039; .. param .. &#039;&amp;lt;/code&amp;gt;&#039;&lt;br /&gt;
			else&lt;br /&gt;
				ret = ret .. &#039;, &amp;lt;code&amp;gt;&#039; .. param .. &#039;&amp;lt;/code&amp;gt;&#039;&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	ret = ret .. &#039;\n|-&#039;&lt;br /&gt;
		.. &#039;\n|}&#039;&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Cronos</name></author>
	</entry>
	<entry>
		<id>https://hna.wiki/Module:Namespace_detect?diff=8785</id>
		<title>Module:Namespace detect</title>
		<link rel="alternate" type="text/html" href="https://hna.wiki/Module:Namespace_detect?diff=8785"/>
		<updated>2026-07-13T07:26:37Z</updated>

		<summary type="html">&lt;p&gt;Cronos: Replace with Wikipedia version&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
--                                                                            --&lt;br /&gt;
--                            NAMESPACE DETECT                                --&lt;br /&gt;
--                                                                            --&lt;br /&gt;
-- This module implements the {{namespace detect}} template in Lua, with a    --&lt;br /&gt;
-- few improvements: all namespaces and all namespace aliases are supported,  --&lt;br /&gt;
-- and namespace names are detected automatically for the local wiki. The     --&lt;br /&gt;
-- module can also use the corresponding subject namespace value if it is     --&lt;br /&gt;
-- used on a talk page. Parameter names can be configured for different wikis --&lt;br /&gt;
-- by altering the values in the &amp;quot;cfg&amp;quot; table in                               --&lt;br /&gt;
-- Module:Namespace detect/config.                                            --&lt;br /&gt;
--                                                                            --&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
--]]&lt;br /&gt;
&lt;br /&gt;
local data = mw.loadData(&#039;Module:Namespace detect/data&#039;)&lt;br /&gt;
local argKeys = data.argKeys&lt;br /&gt;
local cfg = data.cfg&lt;br /&gt;
local mappings = data.mappings&lt;br /&gt;
&lt;br /&gt;
local yesno = require(&#039;Module:Yesno&#039;)&lt;br /&gt;
local mArguments -- Lazily initialise Module:Arguments&lt;br /&gt;
local mTableTools -- Lazily initilalise Module:TableTools&lt;br /&gt;
local ustringLower = mw.ustring.lower&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
local function fetchValue(t1, t2)&lt;br /&gt;
	-- Fetches a value from the table t1 for the first key in array t2 where&lt;br /&gt;
	-- a non-nil value of t1 exists.&lt;br /&gt;
	for i, key in ipairs(t2) do&lt;br /&gt;
		local value = t1[key]&lt;br /&gt;
		if value ~= nil then&lt;br /&gt;
			return value&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function equalsArrayValue(t, value)&lt;br /&gt;
	-- Returns true if value equals a value in the array t. Otherwise&lt;br /&gt;
	-- returns false.&lt;br /&gt;
	for i, arrayValue in ipairs(t) do&lt;br /&gt;
		if value == arrayValue then&lt;br /&gt;
			return true&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.getPageObject(page)&lt;br /&gt;
	-- Get the page object, passing the function through pcall in case of&lt;br /&gt;
	-- errors, e.g. being over the expensive function count limit.&lt;br /&gt;
	if page then&lt;br /&gt;
		local success, pageObject = pcall(mw.title.new, page)&lt;br /&gt;
		if success then&lt;br /&gt;
			return pageObject&lt;br /&gt;
		else&lt;br /&gt;
			return nil&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		return mw.title.getCurrentTitle()&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Provided for backward compatibility with other modules&lt;br /&gt;
function p.getParamMappings()&lt;br /&gt;
	return mappings&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getNamespace(args)&lt;br /&gt;
	-- This function gets the namespace name from the page object.&lt;br /&gt;
	local page = fetchValue(args, argKeys.demopage)&lt;br /&gt;
	if page == &#039;&#039; then&lt;br /&gt;
		page = nil&lt;br /&gt;
	end&lt;br /&gt;
	local demospace = fetchValue(args, argKeys.demospace)&lt;br /&gt;
	if demospace == &#039;&#039; then&lt;br /&gt;
		demospace = nil&lt;br /&gt;
	end&lt;br /&gt;
	local subjectns = fetchValue(args, argKeys.subjectns)&lt;br /&gt;
	local ret&lt;br /&gt;
	if demospace then&lt;br /&gt;
		-- Handle &amp;quot;demospace = main&amp;quot; properly.&lt;br /&gt;
		if equalsArrayValue(argKeys.main, ustringLower(demospace)) then&lt;br /&gt;
			ret = mw.site.namespaces[0].name&lt;br /&gt;
		else&lt;br /&gt;
			ret = demospace&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		local pageObject = p.getPageObject(page)&lt;br /&gt;
		if pageObject then&lt;br /&gt;
			if pageObject.isTalkPage then&lt;br /&gt;
				-- Get the subject namespace if the option is set,&lt;br /&gt;
				-- otherwise use &amp;quot;talk&amp;quot;.&lt;br /&gt;
				if yesno(subjectns) then&lt;br /&gt;
					ret = mw.site.namespaces[pageObject.namespace].subject.name&lt;br /&gt;
				else&lt;br /&gt;
					ret = &#039;talk&#039;&lt;br /&gt;
				end&lt;br /&gt;
			else&lt;br /&gt;
				ret = pageObject.nsText&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			return nil -- return nil if the page object doesn&#039;t exist.&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	ret = ret:gsub(&#039;_&#039;, &#039; &#039;)&lt;br /&gt;
	return ustringLower(ret)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p._main(args)&lt;br /&gt;
	-- Check the parameters stored in the mappings table for any matches.&lt;br /&gt;
	local namespace = getNamespace(args) or &#039;other&#039; -- &amp;quot;other&amp;quot; avoids nil table keys&lt;br /&gt;
	local params = mappings[namespace] or {}&lt;br /&gt;
	local ret = fetchValue(args, params)&lt;br /&gt;
	--[[&lt;br /&gt;
	-- If there were no matches, return parameters for other namespaces.&lt;br /&gt;
	-- This happens if there was no text specified for the namespace that&lt;br /&gt;
	-- was detected or if the demospace parameter is not a valid&lt;br /&gt;
	-- namespace. Note that the parameter for the detected namespace must be&lt;br /&gt;
	-- completely absent for this to happen, not merely blank.&lt;br /&gt;
	--]]&lt;br /&gt;
	if ret == nil then&lt;br /&gt;
		ret = fetchValue(args, argKeys.other)&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.main(frame)&lt;br /&gt;
	mArguments = require(&#039;Module:Arguments&#039;)&lt;br /&gt;
	local args = mArguments.getArgs(frame, {removeBlanks = false})&lt;br /&gt;
	local ret = p._main(args)&lt;br /&gt;
	return ret or &#039;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.table(frame)&lt;br /&gt;
	--[[&lt;br /&gt;
	-- Create a wikitable of all subject namespace parameters, for&lt;br /&gt;
	-- documentation purposes. The talk parameter is optional, in case it&lt;br /&gt;
	-- needs to be excluded in the documentation.&lt;br /&gt;
	--]]&lt;br /&gt;
	&lt;br /&gt;
	-- Load modules and initialise variables.&lt;br /&gt;
	mTableTools = require(&#039;Module:TableTools&#039;)&lt;br /&gt;
	local namespaces = mw.site.namespaces&lt;br /&gt;
	local cfg = data.cfg&lt;br /&gt;
	local useTalk = type(frame) == &#039;table&#039; &lt;br /&gt;
		and type(frame.args) == &#039;table&#039; &lt;br /&gt;
		and yesno(frame.args.talk) -- Whether to use the talk parameter.&lt;br /&gt;
	&lt;br /&gt;
	-- Get the header names.&lt;br /&gt;
	local function checkValue(value, default)&lt;br /&gt;
		if type(value) == &#039;string&#039; then&lt;br /&gt;
			return value&lt;br /&gt;
		else&lt;br /&gt;
			return default&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	local nsHeader = checkValue(cfg.wikitableNamespaceHeader, &#039;Namespace&#039;)&lt;br /&gt;
	local aliasesHeader = checkValue(cfg.wikitableAliasesHeader, &#039;Aliases&#039;)&lt;br /&gt;
&lt;br /&gt;
	-- Put the namespaces in order.&lt;br /&gt;
	local mappingsOrdered = {}&lt;br /&gt;
	for nsname, params in pairs(mappings) do&lt;br /&gt;
		if useTalk or nsname ~= &#039;talk&#039; then&lt;br /&gt;
			local nsid = namespaces[nsname].id&lt;br /&gt;
			-- Add 1, as the array must start with 1; nsid 0 would be lost otherwise.&lt;br /&gt;
			nsid = nsid + 1 &lt;br /&gt;
			mappingsOrdered[nsid] = params&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	mappingsOrdered = mTableTools.compressSparseArray(mappingsOrdered)&lt;br /&gt;
&lt;br /&gt;
	-- Build the table.&lt;br /&gt;
	local ret = &#039;{| class=&amp;quot;wikitable&amp;quot;&#039;&lt;br /&gt;
		.. &#039;\n|-&#039;&lt;br /&gt;
		.. &#039;\n! &#039; .. nsHeader&lt;br /&gt;
		.. &#039;\n! &#039; .. aliasesHeader&lt;br /&gt;
	for i, params in ipairs(mappingsOrdered) do&lt;br /&gt;
		for j, param in ipairs(params) do&lt;br /&gt;
			if j == 1 then&lt;br /&gt;
				ret = ret .. &#039;\n|-&#039;&lt;br /&gt;
					.. &#039;\n| &amp;lt;code&amp;gt;&#039; .. param .. &#039;&amp;lt;/code&amp;gt;&#039;&lt;br /&gt;
					.. &#039;\n| &#039;&lt;br /&gt;
			elseif j == 2 then&lt;br /&gt;
				ret = ret .. &#039;&amp;lt;code&amp;gt;&#039; .. param .. &#039;&amp;lt;/code&amp;gt;&#039;&lt;br /&gt;
			else&lt;br /&gt;
				ret = ret .. &#039;, &amp;lt;code&amp;gt;&#039; .. param .. &#039;&amp;lt;/code&amp;gt;&#039;&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	ret = ret .. &#039;\n|-&#039;&lt;br /&gt;
		.. &#039;\n|}&#039;&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Cronos</name></author>
	</entry>
	<entry>
		<id>https://hna.wiki/Module:Namespace_detect/data?diff=8784</id>
		<title>Module:Namespace detect/data</title>
		<link rel="alternate" type="text/html" href="https://hna.wiki/Module:Namespace_detect/data?diff=8784"/>
		<updated>2026-07-13T07:24:09Z</updated>

		<summary type="html">&lt;p&gt;Cronos: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--------------------------------------------------------------------------------&lt;br /&gt;
--                          Namespace detect data                             --&lt;br /&gt;
-- This module holds data for [[Module:Namespace detect]] to be loaded per    --&lt;br /&gt;
-- page, rather than per #invoke, for performance reasons.                    --&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local cfg = mw.title.new( &#039;Module:Namespace detect/config&#039;).exists and require(&#039;Module:Namespace detect/config&#039;) or require(&#039;Dev:Namespace detect/config&#039;)&lt;br /&gt;
&lt;br /&gt;
local function addKey(t, key, defaultKey)&lt;br /&gt;
	if key ~= defaultKey then&lt;br /&gt;
		t[#t + 1] = key&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Get a table of parameters to query for each default parameter name.&lt;br /&gt;
-- This allows wikis to customise parameter names in the cfg table while&lt;br /&gt;
-- ensuring that default parameter names will always work. The cfg table&lt;br /&gt;
-- values can be added as a string, or as an array of strings.&lt;br /&gt;
&lt;br /&gt;
local defaultKeys = {&lt;br /&gt;
	&#039;main&#039;,&lt;br /&gt;
	&#039;talk&#039;,&lt;br /&gt;
	&#039;other&#039;,&lt;br /&gt;
	&#039;subjectns&#039;,&lt;br /&gt;
	&#039;demospace&#039;,&lt;br /&gt;
	&#039;demopage&#039;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local argKeys = {}&lt;br /&gt;
for i, defaultKey in ipairs(defaultKeys) do&lt;br /&gt;
	argKeys[defaultKey] = {defaultKey}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
for defaultKey, t in pairs(argKeys) do&lt;br /&gt;
	local cfgValue = cfg[defaultKey]&lt;br /&gt;
	local cfgValueType = type(cfgValue)&lt;br /&gt;
	if cfgValueType == &#039;string&#039; then&lt;br /&gt;
		addKey(t, cfgValue, defaultKey)&lt;br /&gt;
	elseif cfgValueType == &#039;table&#039; then&lt;br /&gt;
		for i, key in ipairs(cfgValue) do&lt;br /&gt;
			addKey(t, key, defaultKey)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	cfg[defaultKey] = nil -- Free the cfg value as we don&#039;t need it any more.&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getParamMappings()&lt;br /&gt;
	--[[&lt;br /&gt;
	-- Returns a table of how parameter names map to namespace names. The keys&lt;br /&gt;
	-- are the actual namespace names, in lower case, and the values are the&lt;br /&gt;
	-- possible parameter names for that namespace, also in lower case. The&lt;br /&gt;
	-- table entries are structured like this:&lt;br /&gt;
	-- {&lt;br /&gt;
	--   [&#039;&#039;] = {&#039;main&#039;},&lt;br /&gt;
	--   [&#039;wikipedia&#039;] = {&#039;wikipedia&#039;, &#039;project&#039;, &#039;wp&#039;},&lt;br /&gt;
	--   ...&lt;br /&gt;
	-- }&lt;br /&gt;
	--]]&lt;br /&gt;
	local mappings = {}&lt;br /&gt;
	local mainNsName = mw.site.subjectNamespaces[0].name&lt;br /&gt;
	mainNsName = mw.ustring.lower(mainNsName)&lt;br /&gt;
	mappings[mainNsName] = mw.clone(argKeys.main)&lt;br /&gt;
	mappings[&#039;talk&#039;] = mw.clone(argKeys.talk)&lt;br /&gt;
	for nsid, ns in pairs(mw.site.subjectNamespaces) do&lt;br /&gt;
		if nsid ~= 0 then -- Exclude main namespace.&lt;br /&gt;
			local nsname = mw.ustring.lower(ns.name)&lt;br /&gt;
			local canonicalName = mw.ustring.lower(ns.canonicalName)&lt;br /&gt;
			mappings[nsname] = {nsname}&lt;br /&gt;
			if canonicalName ~= nsname then&lt;br /&gt;
				table.insert(mappings[nsname], canonicalName)&lt;br /&gt;
			end&lt;br /&gt;
			for _, alias in ipairs(ns.aliases) do&lt;br /&gt;
				table.insert(mappings[nsname], mw.ustring.lower(alias))&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return mappings&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return {&lt;br /&gt;
	argKeys = argKeys,&lt;br /&gt;
	cfg = cfg,&lt;br /&gt;
	mappings = getParamMappings()&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Cronos</name></author>
	</entry>
</feed>