<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://mbwiki.stairwaygames.work/w/index.php?action=history&amp;feed=atom&amp;title=Module%3AI18n%2Fconsolefriendly</id>
	<title>Module:I18n/consolefriendly - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://mbwiki.stairwaygames.work/w/index.php?action=history&amp;feed=atom&amp;title=Module%3AI18n%2Fconsolefriendly"/>
	<link rel="alternate" type="text/html" href="https://mbwiki.stairwaygames.work/w/index.php?title=Module:I18n/consolefriendly&amp;action=history"/>
	<updated>2026-04-07T00:05:20Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.39.3</generator>
	<entry>
		<id>https://mbwiki.stairwaygames.work/w/index.php?title=Module:I18n/consolefriendly&amp;diff=5694&amp;oldid=prev</id>
		<title>Admin coral island: Created page with &quot;-- &lt;nowiki&gt; -- I18n storage module for FANDOM. -- @module      i18n -- @version     1.3.3 -- @usage       require(&quot;Dev:I18n&quot;) -- @author      Speedit -- @author      KockaAdmiralac -- @release     alpha; untested -- @todo        Fallbacks and unit testing. local i18n = {}  -- local keeper of locality -- keeps local functions local and defends them against nasty globals local _i18n = {}  -- Module variables &amp; dependencies. local title = mw.title.getCurrentTitle() local fa...&quot;</title>
		<link rel="alternate" type="text/html" href="https://mbwiki.stairwaygames.work/w/index.php?title=Module:I18n/consolefriendly&amp;diff=5694&amp;oldid=prev"/>
		<updated>2023-08-04T02:50:04Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;-- &amp;lt;nowiki&amp;gt; -- I18n storage module for FANDOM. -- @module      i18n -- @version     1.3.3 -- @usage       require(&amp;quot;Dev:I18n&amp;quot;) -- @author      Speedit -- @author      KockaAdmiralac -- @release     alpha; untested -- @todo        Fallbacks and unit testing. local i18n = {}  -- local keeper of locality -- keeps local functions local and defends them against nasty globals local _i18n = {}  -- Module variables &amp;amp; dependencies. local title = mw.title.getCurrentTitle() local fa...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;-- &amp;lt;nowiki&amp;gt;&lt;br /&gt;
-- I18n storage module for FANDOM.&lt;br /&gt;
-- @module      i18n&lt;br /&gt;
-- @version     1.3.3&lt;br /&gt;
-- @usage       require(&amp;quot;Dev:I18n&amp;quot;)&lt;br /&gt;
-- @author      Speedit&lt;br /&gt;
-- @author      KockaAdmiralac&lt;br /&gt;
-- @release     alpha; untested&lt;br /&gt;
-- @todo        Fallbacks and unit testing.&lt;br /&gt;
local i18n = {}&lt;br /&gt;
&lt;br /&gt;
-- local keeper of locality&lt;br /&gt;
-- keeps local functions local and defends them against nasty globals&lt;br /&gt;
local _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;Dev:Fallbacklist&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
-- I18n datastore class.&lt;br /&gt;
-- @section     i18nd&lt;br /&gt;
local i18nd = {}&lt;br /&gt;
&lt;br /&gt;
-- converts things to human-frienldy strings&lt;br /&gt;
-- https://stackoverflow.com/a/27028488&lt;br /&gt;
-- @param           {object} o thing to dump&lt;br /&gt;
-- @returns         {string} dumped thing&lt;br /&gt;
function dump(o)&lt;br /&gt;
   if type(o) == &amp;#039;table&amp;#039; then&lt;br /&gt;
      local s = &amp;#039;{ &amp;#039;&lt;br /&gt;
      for k,v in pairs(o) do&lt;br /&gt;
         if type(k) ~= &amp;#039;number&amp;#039; then k = &amp;#039;&amp;quot;&amp;#039;..k..&amp;#039;&amp;quot;&amp;#039; end&lt;br /&gt;
         s = s .. &amp;#039;[&amp;#039;..k..&amp;#039;] = &amp;#039; .. dump(v) .. &amp;#039;,&amp;#039;&lt;br /&gt;
      end&lt;br /&gt;
      return s .. &amp;#039;} &amp;#039;&lt;br /&gt;
   else&lt;br /&gt;
      return tostring(o)&lt;br /&gt;
   end&lt;br /&gt;
end-- dump&lt;br /&gt;
&lt;br /&gt;
-- Datastore language getter.&lt;br /&gt;
-- @name        i18nd:getLang&lt;br /&gt;
-- @return      {string} Default language (datastore messages).&lt;br /&gt;
function i18nd:getLang()&lt;br /&gt;
    return self.defaultLang&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Datastore language setter to user language.&lt;br /&gt;
-- @name        i18nd:useUserLang&lt;br /&gt;
-- @return      {self} Self instance.&lt;br /&gt;
function i18nd: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 user language.&lt;br /&gt;
-- @name        i18nd:useContentLang&lt;br /&gt;
-- @return      {self} Self instance.&lt;br /&gt;
function i18nd: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;
-- @name        i18nd:useLang&lt;br /&gt;
-- @param       {string} code Language code to use.&lt;br /&gt;
-- @return      {self} Self instance.&lt;br /&gt;
function i18nd: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;
-- Datastore temporary language setter to user language.&lt;br /&gt;
-- @name        i18nd:inUserLang&lt;br /&gt;
-- @return      {self} Self instance.&lt;br /&gt;
function i18nd:inUserLang()&lt;br /&gt;
    self.tempLang = i18n.getLang() or i18nd.tempLang&lt;br /&gt;
    return self&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Datastore temporary language setter to user language.&lt;br /&gt;
-- @name        i18nd:inContentLang&lt;br /&gt;
-- @return      {self} Self instance.&lt;br /&gt;
function i18nd: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;
-- Datastore temporary language setter to specificed language.&lt;br /&gt;
-- @name        i18nd:inLang&lt;br /&gt;
-- @param       {string} code Language code to use.&lt;br /&gt;
-- @return      {self} Self instance.&lt;br /&gt;
function i18nd: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;
-- Datastore temporary source setter to specificed datastore.&lt;br /&gt;
-- @name        i18nd:fromSource&lt;br /&gt;
-- @param       {string} ... Source name(s) to use.&lt;br /&gt;
-- @return      {self} Self instance.&lt;br /&gt;
function i18nd: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 message utility.&lt;br /&gt;
-- @name        i18nd:msg&lt;br /&gt;
-- @param       {string|table} opts Message configuration or key.&lt;br /&gt;
-- @param[opt]  {string} opts.key Message key to return.&lt;br /&gt;
-- @param[opt]  {table} opts.args Arguments to substitute.&lt;br /&gt;
-- @param[opt]  {table} opts.sources Source names to limit to.&lt;br /&gt;
-- @param[opt]  {table} opts.lang Temporary language to use.&lt;br /&gt;
-- @param[opt]  {string} ... Arguments to substitute.&lt;br /&gt;
-- @return      {string} Message key or &amp;#039;&amp;lt;key&amp;gt;&amp;#039;.&lt;br /&gt;
-- @todo        Better fallback system with [[Dev:Fallbacklist]].&lt;br /&gt;
function i18nd: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 i18nd: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;
            -- debug&lt;br /&gt;
            --mw.log(&amp;#039;i18n.msg pre-handleargs msg&amp;amp;args&amp;#039;, dump(msg), dump(args))&lt;br /&gt;
            -- not debug&lt;br /&gt;
            msg = _i18n.handleArgs(msg, args)&lt;br /&gt;
            -- debug&lt;br /&gt;
            --mw.log(&amp;#039;i18n.msg post-handleargs msg&amp;amp;args&amp;#039;, dump(msg), dump(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;
            -- debug&lt;br /&gt;
            --mw.log(&amp;#039;i18n.msg&amp;#039;, msg, dump(msg))&lt;br /&gt;
            return frame&lt;br /&gt;
                and frame.preprocess 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(&amp;#039;&amp;lt;&amp;#039; .. key .. &amp;#039;&amp;gt;&amp;#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Argument substitution as $n where n &amp;gt; 0.&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;
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; .. i, 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;
-- @param       {string} code Language code to check&lt;br /&gt;
-- @return      {bool} Whether the language code is valid.&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;
-- Language code function.&lt;br /&gt;
-- @usage       {{#invoke:i18n|getLang}}&lt;br /&gt;
-- @return      {string} code Language code.&lt;br /&gt;
function i18n.getLang()&lt;br /&gt;
    local code = mw.language.getContentLanguage():getCode()&lt;br /&gt;
    local frame = mw.getCurrentFrame() or {}&lt;br /&gt;
    local subPage = title.subpageText&lt;br /&gt;
    local uselang&lt;br /&gt;
    -- Language argument test.&lt;br /&gt;
    if _i18n.isValidCode( ( (frame or {}).args or {}).uselang or &amp;#039;&amp;#039;) then&lt;br /&gt;
        code = frame.args.uselang&lt;br /&gt;
    elseif _i18n.isValidCode( ( (frame and (frame.getParent and frame:getParent(frame) or {}) or {}).args or {}).uselang or &amp;#039;&amp;#039;) then&lt;br /&gt;
        code = frame:getParent().args.uselang&lt;br /&gt;
    -- Subpage language test.&lt;br /&gt;
    elseif title.isSubpage then&lt;br /&gt;
        code = _i18n.isValidCode(subPage) and subPage or code&lt;br /&gt;
    -- User language test.&lt;br /&gt;
    elseif frame then&lt;br /&gt;
        -- use en if no frame found&lt;br /&gt;
        uselang = frame.preprocess and frame:preprocess(&amp;#039;{{int:lang}}&amp;#039;) or &amp;#039;en&amp;#039;&lt;br /&gt;
        code = mw.text.decode(uselang) == &amp;#039;&amp;lt;lang&amp;gt;&amp;#039;&lt;br /&gt;
            and code&lt;br /&gt;
            or  uselang&lt;br /&gt;
    end&lt;br /&gt;
    return code&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- I18n message datastore loader.&lt;br /&gt;
-- @param       {string} source ROOTPAGENAME/path of target i18n submodule.&lt;br /&gt;
-- @usage       require(&amp;#039;Dev:Install&amp;#039;).loadMessages(source)&lt;br /&gt;
-- @raise       &amp;#039;no source supplied to i18n.loadMessages&amp;#039;&lt;br /&gt;
-- @return      {table} i18n I18n datastore instance.&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;
                i18nd.__index = i18nd&lt;br /&gt;
                setmetatable(ds, i18nd)&lt;br /&gt;
                -- Set default language.&lt;br /&gt;
                ds.defaultLang = i18n.getLang()&lt;br /&gt;
                ds._messages = {}&lt;br /&gt;
            end&lt;br /&gt;
            source = string.gsub(source, &amp;#039;^.&amp;#039;, mw.ustring.upper)&lt;br /&gt;
            source = mw.ustring.find(source, &amp;#039;:&amp;#039;)&lt;br /&gt;
                and source&lt;br /&gt;
                or  &amp;#039;Dev:&amp;#039; .. source .. &amp;#039;/i18n&amp;#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(&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;
-- I18n message function.&lt;br /&gt;
-- @param       {table} frame Frame table from invocation.&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       {string} frame.args.lang Default language of message (optional).&lt;br /&gt;
-- @usage       {{#invoke:i18n|getMsg|source|key}}&lt;br /&gt;
-- @raise       &amp;#039;missing arguments in i18n.getMsg&amp;#039;&lt;br /&gt;
-- @return      {string} msg I18n message in localised language.&lt;br /&gt;
function i18n.getMsg(frame)&lt;br /&gt;
    local args = frame and frame.args or frame&lt;br /&gt;
    if&lt;br /&gt;
        not frame or&lt;br /&gt;
        not args or&lt;br /&gt;
        not args[1] or&lt;br /&gt;
        not 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 = args[1]&lt;br /&gt;
    local key = args[2]&lt;br /&gt;
    -- Pass through extra arguments.&lt;br /&gt;
    local repl = {}&lt;br /&gt;
    for i, a in ipairs(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 i18nd = i18n.loadMessages(source)&lt;br /&gt;
    -- Pass through language argument.&lt;br /&gt;
    i18nd:inLang(args.lang)&lt;br /&gt;
    -- Return message.&lt;br /&gt;
    return i18nd:msg { key = key, args = repl }&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Template wrapper for [[Template:I18n]].&lt;br /&gt;
-- @param               {table} frameChild Frame invocation object.&lt;br /&gt;
-- @usage               {{#invoke:i18n|main}}&lt;br /&gt;
function i18n.main(frameChild)&lt;br /&gt;
    -- anti-console defense&lt;br /&gt;
    local frame = frameChild.getParent and frameChild:getParent() or (frameChild or {})&lt;br /&gt;
    local args = {}&lt;br /&gt;
    -- there is no .parents and .args in the console&lt;br /&gt;
    local frameChildArgs = frameChild.args and frameChild.args or frameChild&lt;br /&gt;
    for p, v in pairs(frameChildArgs) do args[p] = v end&lt;br /&gt;
    -- Extract function name as first argument.&lt;br /&gt;
    local fn_name = args[1] and table.remove(args, 1)&lt;br /&gt;
    -- Check for function argument.&lt;br /&gt;
    if fn_name == nil then&lt;br /&gt;
        error((mw.ustring.gsub(mw.ustring.match(mw.message.new(&amp;#039;scribunto-common-nofunction&amp;#039;):plain(), &amp;#039;:%s(.*)%p$&amp;#039;), &amp;#039;^.&amp;#039;, mw.ustring.lower)))&lt;br /&gt;
    end&lt;br /&gt;
    -- Check function exists.&lt;br /&gt;
    if i18n[fn_name] == nil then&lt;br /&gt;
        error((mw.ustring.gsub(mw.ustring.match(mw.message.new(&amp;#039;scribunto-common-nosuchfunction&amp;#039;):plain(), &amp;#039;:%s(.*)%p$&amp;#039;), &amp;#039;^.&amp;#039;, mw.ustring.lower)))&lt;br /&gt;
    end&lt;br /&gt;
    -- Execute function if it does.&lt;br /&gt;
    frame.args = args&lt;br /&gt;
    return i18n[fn_name](frame)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return i18n&lt;br /&gt;
-- &amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Admin coral island</name></author>
	</entry>
</feed>