<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-GB">
	<id>https://austenasia.wiki/index.php?action=history&amp;feed=atom&amp;title=Module%3AI18n</id>
	<title>Module:I18n - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://austenasia.wiki/index.php?action=history&amp;feed=atom&amp;title=Module%3AI18n"/>
	<link rel="alternate" type="text/html" href="https://austenasia.wiki/index.php?title=Module:I18n&amp;action=history"/>
	<updated>2026-08-23T12:39:21Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.45.1</generator>
	<entry>
		<id>https://austenasia.wiki/index.php?title=Module:I18n&amp;diff=84&amp;oldid=prev</id>
		<title>Austenasia: Created page with &quot;--- I18n library for message storage in Lua datastores. --  The module is designed to enable message separation from modules &amp; --  templates. It has support for handling language fallbacks. This --  module is a Lua port of wikia:dev:I18n-js and i18n modules that can be loaded --  by it are editable through wikia:dev:I18nEdit. --   --  On Wikimedia projects, i18n messages are editable  --  through Data:i18n/ subpages on --  Wik...&quot;</title>
		<link rel="alternate" type="text/html" href="https://austenasia.wiki/index.php?title=Module:I18n&amp;diff=84&amp;oldid=prev"/>
		<updated>2026-06-22T02:06:56Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;--- I18n library for message storage in Lua datastores. --  The module is designed to enable message separation from modules &amp;amp; --  templates. It has support for handling language fallbacks. This --  module is a Lua port of &lt;a href=&quot;https://community.fandom.com/wiki/w:c:dev:I18n-js&quot; class=&quot;extiw&quot; title=&quot;wikia:dev:I18n-js&quot;&gt;wikia:dev:I18n-js&lt;/a&gt; and i18n modules that can be loaded --  by it are editable through &lt;a href=&quot;https://community.fandom.com/wiki/w:c:dev:I18nEdit&quot; class=&quot;extiw&quot; title=&quot;wikia:dev:I18nEdit&quot;&gt;wikia:dev:I18nEdit&lt;/a&gt;. --   --  On Wikimedia projects, i18n messages are editable  --  through &lt;a href=&quot;/index.php?title=C:Special:PrefixIndex/Data:i18n/&amp;amp;action=edit&amp;amp;redlink=1&quot; class=&quot;new&quot; title=&quot;C:Special:PrefixIndex/Data:i18n/ (page does not exist)&quot;&gt;Data:i18n/&lt;/a&gt; subpages on --  Wik...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&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 [[wikia:dev:I18n-js]] and i18n modules that can be loaded&lt;br /&gt;
--  by it are editable through [[wikia:dev:I18nEdit]].&lt;br /&gt;
--  &lt;br /&gt;
--  On Wikimedia projects, i18n messages are editable &lt;br /&gt;
--  through [[c:Special:PrefixIndex/Data:i18n/|Data:i18n/]] subpages on&lt;br /&gt;
--  Wikimedia Commons.&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		 [[wikia:dev:User:KockaAdmiralac|KockaAdmiralac]] (original Fandom implementation)&lt;br /&gt;
--  @author		 [[wikia:dev:User:Speedit|Speedit]] (original Fandom implementation)&lt;br /&gt;
--  @author			[[User:Awesome Aasim|Awesome Aasim]] (Wikimedia port)&lt;br /&gt;
--  @attribution	[[wikia:dev:User:Cqm|Cqm]]&lt;br /&gt;
--  @release		beta&lt;br /&gt;
--  @see			[[wikia:dev:I18n|I18n guide]]&lt;br /&gt;
--  @see			[[wikia:dev:I18n-js]]&lt;br /&gt;
--  @see			[[wikia:dev: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(&amp;#039;Module:Fallbacklist&amp;#039;)&lt;br /&gt;
local entrypoint = require(&amp;#039;Module:Entrypoint&amp;#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, &amp;#039;%$&amp;#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) == &amp;#039;string&amp;#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) == &amp;#039;string&amp;#039; and&lt;br /&gt;
		(&lt;br /&gt;
			msg:find(&amp;#039;%-%-%-%-&amp;#039;) or&lt;br /&gt;
			msg:find(&amp;#039;%f[^\n%z][;:*#] &amp;#039;) or&lt;br /&gt;
			msg:find(&amp;#039;%f[^\n%z]==* *[^\n|]+ =*=%f[\n]&amp;#039;) or&lt;br /&gt;
			msg:find(&amp;#039;%b&amp;lt;&amp;gt;&amp;#039;) or msg:find(&amp;#039;\&amp;#039;\&amp;#039;&amp;#039;) or&lt;br /&gt;
			msg:find(&amp;#039;%[%b[]%]&amp;#039;) or msg:find(&amp;#039;{%b{}}&amp;#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 [[mw:Extension:Scribunto/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 = &amp;#039;message-name&amp;#039;,&lt;br /&gt;
--		  lang = &amp;#039;&amp;#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(&amp;#039;message-name&amp;#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} &amp;#039;missing arguments in Data:msg&amp;#039;&lt;br /&gt;
--  @return			 {string} Localised datastore message or `&amp;#039;&amp;lt;key&amp;gt;&amp;#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(&amp;#039;missing arguments in Data:msg&amp;#039;)&lt;br /&gt;
	end&lt;br /&gt;
	local key = type(opts) == &amp;#039;table&amp;#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 &amp;#039;en&amp;#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 ~= &amp;#039;qqx&amp;#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 &amp;#039;&amp;amp;#x29FC;&amp;#039; .. mw.text.nowiki(key) .. &amp;#039;&amp;amp;#x29FD;&amp;#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&amp;#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&amp;#039;s content language.&lt;br /&gt;
--  @function		   Data:parameter&lt;br /&gt;
--  @param			  {string} parameter Parameter&amp;#039;s key in the datastore&lt;br /&gt;
--  @param			  {table} args Arguments to find the parameter in&lt;br /&gt;
--  @error[176]		 {string} &amp;#039;missing arguments in Data:parameter&amp;#039;&lt;br /&gt;
--  @return			 {string|nil} Parameter&amp;#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(&amp;#039;missing arguments in Data:parameter&amp;#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(&amp;#039;#&amp;#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) == &amp;#039;string&amp;#039; and type(self._sources[n]) == &amp;#039;number&amp;#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]		 {string} &amp;#039;missing arguments in i18n.getMsg&amp;#039;&lt;br /&gt;
--  @return			 {string} I18n message in localised language.&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(&amp;#039;missing arguments in i18n.getMsg&amp;#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} &amp;#039;no source supplied to i18n.loadMessages&amp;#039;&lt;br /&gt;
--  @return			 {table} I18n datastore instance.&lt;br /&gt;
--  @usage			  require(&amp;#039;Module:I18n&amp;#039;).loadMessages(&amp;#039;1&amp;#039;, &amp;#039;2&amp;#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(&amp;#039;#&amp;#039;, ...) do&lt;br /&gt;
		local source = select(j, ...)&lt;br /&gt;
		if type(source) == &amp;#039;string&amp;#039; and source ~= &amp;#039;&amp;#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, &amp;#039;^.&amp;#039;, mw.ustring.upper)&lt;br /&gt;
			local success, messages = pcall(mw.loadData, mw.ustring.find(source, &amp;#039;:&amp;#039;)&lt;br /&gt;
				and source&lt;br /&gt;
				or  &amp;#039;Module:&amp;#039; .. source .. &amp;#039;/i18n&amp;#039;)&lt;br /&gt;
			if success then&lt;br /&gt;
				local msgCopy = {}&lt;br /&gt;
				local langSecond = nil&lt;br /&gt;
				for lang_id, msgtbl in pairs(messages) do&lt;br /&gt;
					if langSecond == nil then&lt;br /&gt;
						if lang_id == &amp;quot;qqq&amp;quot; or fallbacks[lang_id] ~= nil then&lt;br /&gt;
							langSecond = false&lt;br /&gt;
						else&lt;br /&gt;
							langSecond = true&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
					for id_lang, msg in pairs(msgtbl) do&lt;br /&gt;
						if langSecond then&lt;br /&gt;
							msgCopy[id_lang] = msgCopy[id_lang] or {}&lt;br /&gt;
							msgCopy[id_lang][lang_id] = msg&lt;br /&gt;
						else&lt;br /&gt;
							msgCopy[lang_id] = msgCopy[lang_id] or {}&lt;br /&gt;
							msgCopy[lang_id][id_lang] = msg&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
				ds._messages[i] = msgCopy&lt;br /&gt;
			end&lt;br /&gt;
			local tab = mw.ext.data.get(&amp;#039;I18n/&amp;#039; .. source .. &amp;#039;.tab&amp;#039;, &amp;#039;_&amp;#039;)&lt;br /&gt;
			local T = {}&lt;br /&gt;
			if not success and not tab then error(&amp;quot;i18n for &amp;quot; .. source .. &amp;quot; is missing&amp;quot;) end&lt;br /&gt;
			for _, row in pairs(tab.data) do -- convert the output into a dictionary table&lt;br /&gt;
				local id, t = unpack(row)&lt;br /&gt;
				for lang, msg in pairs(t) do&lt;br /&gt;
					if not T[lang] then T[lang] = {} end&lt;br /&gt;
					T[lang][id] = msg&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			if not success then&lt;br /&gt;
				ds._messages[i] = T&lt;br /&gt;
			else&lt;br /&gt;
				for lang, msgTbl in pairs(T) do&lt;br /&gt;
					ds._messages[i][lang] = ds._messages[i][lang] or msgTbl&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if not ds then&lt;br /&gt;
		error(&amp;#039;no source supplied to i18n.loadMessages&amp;#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&amp;#039;s language code through `uselang` parameter.&lt;br /&gt;
--  @function		   i18n.getLang&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(&amp;#039;{{int:lang}}&amp;#039;)&lt;br /&gt;
				or  frame:preprocess(&amp;#039;{{int:lang}}&amp;#039;)&lt;br /&gt;
		local decodedLang = mw.text.decode(uselang) &lt;br /&gt;
		if decodedLang ~= &amp;#039;&amp;lt;lang&amp;gt;&amp;#039; and decodedLang ~= &amp;#039;⧼lang⧽&amp;#039; then&lt;br /&gt;
			code = decodedLang == &amp;#039;(lang)&amp;#039;&lt;br /&gt;
				and &amp;#039;qqx&amp;#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;
-- Credit to http://stackoverflow.com/a/1283608/2644759&lt;br /&gt;
-- cc-by-sa 3.0&lt;br /&gt;
local function tableMerge(t1, t2, overwrite)&lt;br /&gt;
	for k,v in pairs(t2) do&lt;br /&gt;
		if type(v) == &amp;quot;table&amp;quot; and type(t1[k]) == &amp;quot;table&amp;quot; then&lt;br /&gt;
			-- since type(t1[k]) == type(v) == &amp;quot;table&amp;quot;, so t1[k] and v is true&lt;br /&gt;
			tableMerge(t1[k], v, overwrite) -- t2[k] == v&lt;br /&gt;
		else&lt;br /&gt;
			if overwrite or t1[k] == nil then t1[k] = v end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return t1&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Given an i18n table instantiates the values (deprecated)&lt;br /&gt;
--  @function i18n.loadI18n&lt;br /&gt;
--  @param {string} name name of module with i18n&lt;br /&gt;
--  @param {table} i18n_arg existing i18n&lt;br /&gt;
function i18n.loadI18n(name, i18n_arg)&lt;br /&gt;
	local exist, res = pcall(require, name)&lt;br /&gt;
	if exist and next(res) ~= nil then&lt;br /&gt;
		if i18n_arg then&lt;br /&gt;
			tableMerge(i18n_arg, res.i18n, true)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Loads an i18n for a specific frame (deprecated)&lt;br /&gt;
--  @function i18n.loadI18nFrame&lt;br /&gt;
--  @param {string} name name of module with i18n&lt;br /&gt;
--  @param {table} i18n_arg existing i18n&lt;br /&gt;
function i18n.loadI18nFrame(frame, i18n_arg)&lt;br /&gt;
	return i18n.loadI18n(frame:getTitle()..&amp;quot;/i18n&amp;quot;, i18n_arg)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Wrapper for the module.&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 require(&amp;quot;Module:Deprecated&amp;quot;)(i18n, &lt;br /&gt;
	{&lt;br /&gt;
		[&amp;quot;loadI18n&amp;quot;] = {&lt;br /&gt;
			deprecated = true,&lt;br /&gt;
			replacement = &amp;quot;use &amp;lt;code&amp;gt;i18n.loadMessages&amp;lt;/code&amp;gt;&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		[&amp;quot;loadI18nFrame&amp;quot;] = {&lt;br /&gt;
			deprecated = true,&lt;br /&gt;
			replacement = &amp;quot;use &amp;lt;code&amp;gt;i18n.loadMessages&amp;lt;/code&amp;gt;&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
)&lt;br /&gt;
-- &amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Austenasia</name></author>
	</entry>
</feed>