Module:I18n/consolefriendly

-- -- I18n storage module for FANDOM. -- @module     i18n -- @version    1.3.3 -- @usage      require("Dev:I18n") -- @author     Speedit -- @author     KockaAdmiralac -- @release    alpha; untested -- @todo       Fallbacks and unit testing. local i18n = {}

-- Module variables & dependencies. local title = mw.title.getCurrentTitle local fallbacks = require('Dev:Fallbacklist')

-- I18n datastore class. -- @section    i18nd local i18nd = {}

-- Datastore language getter. -- @name       i18nd:getLang -- @return     {string} Default language (datastore messages). function i18nd:getLang return self.defaultLang end

-- Datastore language setter to user language. -- @name       i18nd:useUserLang -- @return     {self} Self instance. function i18nd:useUserLang self.defaultLang = i18n.getLang or self.defaultLang return self end

-- Datastore language setter to user language. -- @name       i18nd:useContentLang -- @return     {self} Self instance. function i18nd:useContentLang self.defaultLang = mw.language.getContentLanguage:getCode return self end

-- Datastore language setter to specificed language. -- @name       i18nd:useLang -- @param      {string} code Language code to use. -- @return     {self} Self instance. function i18nd:useLang(code) self.defaultLang = isValidCode(code) and code or self.defaultLang return self end

-- Datastore temporary language setter to user language. -- @name       i18nd:inUserLang -- @return     {self} Self instance. function i18nd:inUserLang self.tempLang = i18n.getLang or i18nd.tempLang return self end

-- Datastore temporary language setter to user language. -- @name       i18nd:inContentLang -- @return     {self} Self instance. function i18nd:inContentLang self.tempLang = mw.language.getContentLanguage:getCode return self end

-- Datastore temporary language setter to specificed language. -- @name       i18nd:inLang -- @param      {string} code Language code to use. -- @return     {self} Self instance. function i18nd:inLang(code) self.tempLang = isValidCode(code) and code or self.tempLang return self end

-- Datastore temporary source setter to specificed datastore. -- @name       i18nd:fromSource -- @param      {string} ... Source name(s) to use. -- @return     {self} Self instance. function i18nd:fromSource(...) local c = select('#', ...) if c ~= 0 then self.tempSources = {} for i = 1, c do           local n = select(i, ...) if type(n) == 'string' and type(self._sources[n]) == 'number' then self.tempSources[n] = self._sources[n] end end end return self end

-- Datastore message utility. -- @name       i18nd:msg -- @param      {string|table} opts Message configuration or key. -- @param[opt] {string} opts.key Message key to return. -- @param[opt] {table} opts.args Arguments to substitute. -- @param[opt] {table} opts.sources Source names to limit to. -- @param[opt] {table} opts.lang Temporary language to use. -- @param[opt] {string} ... Arguments to substitute. -- @return     {string} Message key or ' '. -- @todo       Better fallback system with Dev:Fallbacklist. function i18nd:msg(opts, ...) local frame = mw.getCurrentFrame -- Argument normalization. if not self or not opts then error('missing arguments in i18nd:msg') end local key = type(opts) == 'table' and opts.key or opts local args = opts.args or {...} -- Configuration parameters. if opts.sources then self:fromSources(unpack(opts.sources)) end if opts.lang then self:inLang(opts.lang) end -- Source handling. local source_n = self.tempSources or self._sources local source_i = {} for n, i in pairs(source_n) do       source_i[i] = n    end self.tempSources = nil -- Language handling. local lang = self.tempLang or self.defaultLang self.tempLang = nil -- Message fetching. local msg for i, messages in ipairs(self._messages) do       -- Message data. local msg = (messages[lang] or {})[key] -- Fallback support (experimental). for _, l in ipairs((fallbacks[lang] or {})) do           if msg == nil then msg = (messages[l] or {})[key] end end -- Internal fallback to 'en'. msg = msg ~= nil and msg or messages.en[key] -- Handling argument substitution from Lua. if msg and source_i[i] and #args > 0 then msg = handleArgs(msg, args) end if msg and source_i[i] and lang ~= 'qqx' then return frame and frame.preprocess and frame:preprocess(mw.text.trim(msg)) or mw.text.trim(msg) end end return mw.text.nowiki('<' .. key .. '>') end

-- Argument substitution as $n where n > 0. -- @param      {string} msg Message to substitute arguments into. -- @param      {table} args Arguments table to substitute. -- @return     {string} Resulting message. function handleArgs(msg, args) for i, a in ipairs(args) do       msg = (string.gsub(msg, '%$' .. i, a)) end return msg end

-- Checks whether a language code is valid. -- @param      {string} code Language code to check -- @return     {bool} Whether the language code is valid. function isValidCode(code) return type(code) == 'string' and #mw.language.fetchLanguageName(code) ~= 0 end

-- Language code function. -- @usage -- @return     {string} code Language code. function i18n.getLang local code = mw.language.getContentLanguage:getCode local frame = mw.getCurrentFrame or {} local subPage = title.subpageText local uselang -- Language argument test. if isValidCode( ( (frame or {}).args or {}).uselang or '') then code = frame.args.uselang elseif isValidCode( ( (frame and (frame.getParent and frame:getParent(frame) or {}) or {}).args or {}).uselang or '') then code = frame:getParent.args.uselang -- Subpage language test. elseif title.isSubpage then code = isValidCode(subPage) and subPage or code -- User language test. elseif frame then -- use en if no frame found uselang = frame.preprocess and frame:preprocess('') or 'en' code = mw.text.decode(uselang) == ' ' and code or uselang end return code end

-- I18n message datastore loader. -- @param      {string} source ROOTPAGENAME/path of target i18n submodule. -- @usage      require('Dev:Install').loadMessages(source) -- @raise      'no source supplied to i18n.loadMessages' -- @return     {table} i18n I18n datastore instance. function i18n.loadMessages(...) local ds   local i = 0 local s = {} for j = 1, select('#', ...) do       local source = select(j, ...) if type(source) == 'string' and source ~= '' then i = i + 1 s[source] = i           if not ds then -- Instantiate datastore. ds = {} i18nd.__index = i18nd setmetatable(ds, i18nd) -- Set default language. ds.defaultLang = i18n.getLang ds._messages = {} end source = string.gsub(source, '^.', mw.ustring.upper) source = mw.ustring.find(source, ':') and source or 'Dev:' .. source .. '/i18n' ds._messages[i] = mw.loadData(source) end end if not ds then error('no source supplied to i18n.loadMessages') else -- Attach source index map. ds._sources = s       -- Return datastore instance. return ds   end end

-- I18n message function. -- @param      {table} frame Frame table from invocation. -- @param      {string} frame.args[1] ROOTPAGENAME of i18n submodule. -- @param      {string} frame.args[2] Key of i18n message. -- @param      {string} frame.args.lang Default language of message (optional). -- @usage -- @raise      'missing arguments in i18n.getMsg' -- @return     {string} msg I18n message in localised language. function i18n.getMsg(frame) local args = frame and frame.args or frame if       not frame or        not args or        not args[1] or        not args[2] then error('missing arguments in i18n.getMsg') end local source = args[1] local key = args[2] -- Pass through extra arguments. local repl = {} for i, a in ipairs(args) do       if i >= 3 then repl[i-2] = a       end end -- Load message data. local i18nd = i18n.loadMessages(source) -- Pass through language argument. i18nd:inLang(args.lang) -- Return message. return i18nd:msg { key = key, args = repl } end

-- Template wrapper for Template:I18n. -- @param              {table} frameChild Frame invocation object. -- @usage function i18n.main(frameChild) -- anti-console defense local frame = frameChild.getParent and frameChild:getParent or (frameChild or {}) local args = {} -- there is no .parents and .args in the console local frameChildArgs = frameChild.args and frameChild.args or frameChild for p, v in pairs(frameChildArgs) do args[p] = v end -- Extract function name as first argument. local fn_name = args[1] and table.remove(args, 1) -- Check for function argument. if fn_name == nil then error((mw.ustring.gsub(mw.ustring.match(mw.message.new('scribunto-common-nofunction'):plain, ':%s(.*)%p$'), '^.', mw.ustring.lower))) end -- Check function exists. if i18n[fn_name] == nil then error((mw.ustring.gsub(mw.ustring.match(mw.message.new('scribunto-common-nosuchfunction'):plain, ':%s(.*)%p$'), '^.', mw.ustring.lower))) end -- Execute function if it does. frame.args = args return i18n[fn_name](frame) end

return i18n --